Sign in to follow this  

[MDX] Problems with computing tangent frame

This topic is 4301 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, Trying to get tangent frames computed for -all- meshs I load in my game engine but having a bit of trouble. I made a little soft cube in Blender, and exported it to .x. Then, I loaded it in the engine - that's all fine, so I thought - time to do some more playing with shaders! Seems that has to be more complex, as things always are. I wanted to give normal mapping a try, but that needs tangents and binormals, so I tried this code to get a tangent frame computed:
        public StaticMesh()
        {
            GraphicsBuffer adj = new GraphicsBuffer();

            using (D3D.Mesh input = new D3D.Mesh(VideoTask.GetInstance().Device, GeneralMediaManager.FindFile("cube.x", false),
                D3D.MeshFlags.Managed, adj, null, null))
            {
                using (D3D.Mesh clean = input.Optimize(D3D.MeshFlags.OptimizeCompact | D3D.MeshFlags.OptimizeAttributeSort | D3D.MeshFlags.OptimizeDeviceIndependent |
                    D3D.MeshFlags.OptimizeStripeReorder, adj))
                {
                    D3D.VertexElement[] elements = new D3D.VertexElement[]
                        {
                        new D3D.VertexElement(0, 0, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default,
                                            D3D.DeclarationUsage.Position, 0),

                        new D3D.VertexElement(0, 12, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default,
                                            D3D.DeclarationUsage.Normal, 0),

                        new D3D.VertexElement(0, 24, D3D.DeclarationType.Float2, D3D.DeclarationMethod.Default,
                                            D3D.DeclarationUsage.TextureCoordinate, 0),

                        new D3D.VertexElement(0, 32, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default,
                                            D3D.DeclarationUsage.BiNormal, 0),

                        new D3D.VertexElement(0, 44, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default,
                                            D3D.DeclarationUsage.Tangent, 0),

                        D3D.VertexElement.VertexDeclarationEnd
                        };

                    this.mesh = clean.Clone(VideoTask.GetInstance().Device, D3D.MeshFlags.Managed, elements);

                    this.mesh.ComputeTangentFrame(D3D.TangentOptions.CalculateNormals);
                }
            }
        }

But, this throws an exception on mesh.ComputeTangentFrame and debug output shows:
[2312] D3DX: (INFO) Using 3DNow Instructions 
[2312] D3DX: Unicode support: 1
[2312]  
[2312] Miss rate before optimization: 1.000000
[2312] Miss rate after optimization: 1.000000
[2312] D3DX: D3DXComputeTangentFrame: Unable to create consistent tangent frames in this mesh without splitting vertices. 
Any ideas? How can I make it "split vertices"? Thanks,

Share this post


Link to post
Share on other sites
You have some minor adjustments that needs to be made to your code to work.
With your vertex declaration your last offset is incorrect.


//1 set of position coordinates is 3 floats, offset = 0
//1 set of normal coordinates is 3 floats, offset = 12
//1 set of texture coordinates is 2 floats, offset = 24
//1 set of tangent coordinates is 3 floats, offset = 32
//1 set of binormal coordinates is 3 floats, offset = 48
VertexElement[] elements = new VertexElement[]
{
new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
new VertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
new VertexElement(0, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
new VertexElement(0, 32, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Tangent, 0),
new VertexElement(0, 44, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.BiNormal, 0),
VertexElement.VertexDeclarationEnd,
};
//Create a temporary mesh and clone the original mesh with the new declaration
Mesh tempMesh = mesh.Clone(MeshFlags.Managed, elements, device);
//Dispose the original mesh
mesh.Dispose();
//Assign the original mesh to the temp one
mesh = tempMesh;
//Compute the tangent and binormal vectors.
mesh.ComputeTangentFrame(0);





This should work fine.
If you want some more information you can check out the tutorial I wrote on normal mapping using MDX at MDXInfo.com

EDIT: Sorry Acid2, the last offset is 44

I hope this helps.
Take care.

[Edited by - Armadon on March 7, 2006 9:51:19 PM]

Share this post


Link to post
Share on other sites
Why 48?
32 + 3 floats = 32 + 3x4bytes = 44 - no?

Also, same result - it needs to split vertices :( Anyone know what this means? Could it be a bad mesh?

Share this post


Link to post
Share on other sites
I spent ages a while ago with a similar problem. But I found some code on the internet that works. I edited it a bit, and here it is (using MDX 2.0 Beta - Feb SDK). The TangentVertex class is a very simple class to store all the vertex information and declaration. As you can see, I here use the D3D.Mesh.ComputeTangent, instead of D3D.Mesh.ComputeTangentFrame method.

Good luck!


private void PrepareMesh()
{
// If we already have the required delcaration, return
D3D.VertexElement[] declaration = m_Mesh.GetDeclaration();
if (TangentVertex.IsTangentVertexDeclaration(declaration))
return;

// Check if the mesh has normal declaration
bool bGenerateNormals = true;
for (int i = 0; (i < 5) && (i < declaration.Length); i++)
{
if (declaration[i].DeclarationUsage == D3D.DeclarationUsage.Normal)
{
bGenerateNormals = false;
break;
}
}

// Create new mesh and clone everything
D3D.Mesh tempMesh = m_Mesh.Clone(GraphicsDevice.Device, m_Mesh.Options.Value, TangentVertex.VertexDeclaration);
m_Mesh.Dispose();
m_Mesh = tempMesh;

// Check if the mesh has texture coordinates or normals
bool bGenerateTexCoords = true;

int nVertexCount = m_Mesh.VertexCount;
DXG.GraphicsBuffer<TangentVertex> vertices = m_Mesh.LockVertexBuffer<TangentVertex>(D3D.LockFlags.None);
for (int i = 0; i < nVertexCount; i++)
{
if ((vertices[i].Tu != 0.0f) || (vertices[i].Tv != 0.0f))
bGenerateTexCoords = false;
if (vertices[i].Normal.Length() > 0)
bGenerateNormals = false;
if (!bGenerateTexCoords && !bGenerateNormals)
break;
}

m_Mesh.UnlockVertexBuffer();

// Generate Texture Coordinates and Normal if needed
if (bGenerateTexCoords)
SetSphericalTextureCoordinates();
if (bGenerateNormals)
m_Mesh.ComputeNormals();

// Well, finally I found out why this was crashing all the time,
// 1. the Vertex elements have to be in the right order:
// position, normal, texture coords, tangent, binormal.
// 2. doesn't work without the binormals, we need them too.
// Update: 2. is now fixed with the -1 parameter!
// Update: Using now D3DX.Default for -1 for binormal parameter.
// Update: In MDX 2.0 there is no D3DX.Default, using -1 again.
m_Mesh.ComputeTangent(0, 0, -1, 0);

// Finally optimize the mesh for this graphics card's vertex cache.
DXG.GraphicsBuffer<int> adjacency = new DXG.GraphicsBuffer<int>(m_Mesh.FaceCount * 3);
m_Mesh.GenerateAdjacency(0.00001f, adjacency);
m_Mesh.OptimizeInPlace(D3D.MeshFlags.OptimizeVertexCache, adjacency);
}


Share this post


Link to post
Share on other sites
acid2: How'd you get on?

Did you find a solution.

I'm using MDX 2.0.0.0 (Feb SDK) and have no joy when trying to compute BiNormals & Tangents with mesh.ComputeTangent(0,0,-1,0);.

If I try to use mesh.ComputeTangentFrame(0) it crashes with an "invalid param" exception. I've also experimented with different TangentOptions for this function but all give the same result as when passing it 0.

I'm just using Mesh.Teapot(device) for the mesh at the moment.

Can anyone suggest why I can't compute the tangents and binormals?

(I've cloned it into a mesh with a decent VertexDeclaration similar to Armadon's.

Share this post


Link to post
Share on other sites
Oh SWEET! Thanks so much David Hart, that code worked a treat, I didnt use it all, just compute tangent. Here's the code, hopefully you have some luck axon:


public StaticMesh()
{
GraphicsBuffer adj = new GraphicsBuffer();

using (D3D.Mesh input = new D3D.Mesh(VideoTask.GetInstance().Device, GeneralMediaManager.FindFile("cube.x", false),
D3D.MeshFlags.Managed, adj, null, null))
{
using (D3D.Mesh clean = input.Optimize(D3D.MeshFlags.OptimizeCompact | D3D.MeshFlags.OptimizeAttributeSort | D3D.MeshFlags.OptimizeDeviceIndependent |
D3D.MeshFlags.OptimizeStripeReorder, adj))
{
D3D.VertexElement[] elements = new D3D.VertexElement[]
{
new D3D.VertexElement(0, 0, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default, D3D.DeclarationUsage.Position, 0),
new D3D.VertexElement(0, 12, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default, D3D.DeclarationUsage.Normal, 0),
new D3D.VertexElement(0, 24, D3D.DeclarationType.Float2, D3D.DeclarationMethod.Default, D3D.DeclarationUsage.TextureCoordinate, 0),
new D3D.VertexElement(0, 32, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default, D3D.DeclarationUsage.Tangent, 0),
new D3D.VertexElement(0, 44, D3D.DeclarationType.Float3, D3D.DeclarationMethod.Default, D3D.DeclarationUsage.BiNormal, 0),
D3D.VertexElement.VertexDeclarationEnd,
};

this.mesh = clean.Clone(VideoTask.GetInstance().Device, D3D.MeshFlags.Managed, elements);

this.mesh.ComputeTangent(0, 0, -1, 0);
//this.mesh.ComputeNormals();
}
}
}



Thanks everyone for your help!

Share this post


Link to post
Share on other sites

This topic is 4301 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this