Do Bowties in my mesh matter when computing tangents?

Started by
9 comments, last by sipickles 18 years, 1 month ago
hi I am trying to apply reflections to my meshes with HLSL. To do so I need to add TANGENT information to the mesh, right? I am getting this output, and wondered if it mattered: D3DX: D3DXValidMesh: A bowtie was found. Bowties can be fixed by call D3DXCleanMesh D3DX: D3DXValidMesh: A bowtie is the usage of a single vertex by two separate fans of triangles. D3DX: D3DXValidMesh: The fix is to duplicate the vertex so that each fan has its own vertex. D3DX: D3DXValidMesh: Bowtie found around vertex 122 shared by faces 49 and 24 D3DX: D3DXValidMesh: Bowtie found around vertex 124 shared by faces 52 and 25 D3DX: D3DXValidMesh: Bowtie found around vertex 9 shared by faces 55 and 82 D3DX: D3DXValidMesh: Bowtie found around vertex 149 shared by faces 122 and 97 D3DX: D3DXValidMesh: Bowtie found around vertex 151 shared by faces 125 and 98 D3DX: D3DXValidMesh: Bowtie found around vertex 136 shared by faces 138 and 65 D3DX: D3DXValidMesh: Bowtie found around vertex 138 shared by faces 141 and 67 D3DX: D3DXComputeTangentFrame: Mesh is not valid D3DX: ID3DXMesh::GetFVF: The declaration cannot be converted to an FVF D3DX: ID3DXMesh::GetFVF: The declaration cannot be converted to an FVF Here's my code, its a bit rough, patched together from various examples :) Assume I;ve just loaded the mesh and materials in the usual way

	// TANGENT BUILDER--------------------------------------------------------------------------
	HRESULT hr;

	//CLEAN THE MESH
	DWORD* pAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];
	m_pMesh->GenerateAdjacency(0.0, pAdjacency);
	ID3DXMesh* cleanedMesh;
	hr = D3DXCleanMesh( D3DXCLEAN_BOWTIES, m_pMesh, pAdjacency, &cleanedMesh, pAdjacency, NULL ); 
	m_pMesh->Release();
	m_pMesh = cleanedMesh;


	// Useful for reading the mesh declaration
	//D3DVERTEXELEMENT9 declaration[MAX_FVF_DECL_SIZE];
	//m_pMesh->GetDeclaration(declaration);

	if ( !(m_pMesh->GetFVF() & D3DFVF_NORMAL) )
    {
        hr = m_pMesh->CloneMeshFVF( dw32BitFlag | D3DXMESH_MANAGED, 
			m_pMesh->GetFVF() | D3DFVF_NORMAL, 
			g_device, &l_pTempMesh );
        if (FAILED(hr))
		{
			StringCchPrintf( temp, 100, L"cSimpleMesh::CloneMeshFVF() - FAILED - '%s'\n", m_name );
			OutputDebugString( temp );
			SAFE_RELEASE( l_pTempMesh );
		}

        D3DXComputeNormals( l_pTempMesh, NULL );

        m_pMesh->Release();
        m_pMesh = l_pTempMesh;
    }

	// Expand the mesh to hold tangent data
    D3DVERTEXELEMENT9 decl[] =
    {
        { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, 
        { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },  
        { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
        { 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 }, 
        D3DDECL_END()
    };

    hr = m_pMesh->CloneMesh( dw32BitFlag | D3DXMESH_MANAGED, decl, g_device, 
		&l_pTempMesh );
    if (FAILED(hr))
	{
		StringCchPrintf( temp, 100, L"cSimpleMesh::CloneMesh() - FAILED - '%s'\n", m_name );
		OutputDebugString( temp );
		SAFE_RELEASE(l_pTempMesh);	
		return;
	}

    hr = D3DXComputeTangent( l_pTempMesh, // input mesh 
		0, // TexStageIndex 
		0, // TangentIndex 
		0, // BinormIndex 
		0, // Wrap 
		NULL // Adjacency 
		);

    m_pMesh->Release();
    m_pMesh = l_pTempMesh;


	// END OF TANGENT BUILDER ----------------------------------------------------------------------

    // Done with the material buffer
    m_pD3DXMtrlBuffer->Release();
But I still get the same output, even with the D3DXCleanMesh bit added.... Should I worry? I think its works with the HLSL effect, but then I dont know what thats supposed to look like! Many thanks Si
Advertisement
Are you trying to do environment mapping for your reflections?

Dave
Yes.
I have got this working and i didn't pass tangents in, only needed normals and texcoords. Post a picture of what it looks like.

Dave
Thats interesting....

I have a seperate problem with my environment mapping. Its not reflecting the environment map ( a cube map), instead its reflecting a 2d texture (the one the vehicle uses without shaders )... Not sure how I've achieved that yet!

Post your shader or email it and i'll have a look.

Dave
Heres the effect:
// cubeMapFX.fx  - reflects cubic texture// Light direction (view space)float3 LightDir < string UIDirectional = "Light Direction"; > =     normalize(float3(0.5, -0.5, 0)); // Light intensityfloat4 I_a = { 0.0f, 0.0f, 0.0f, 1.0f };    // ambientfloat4 I_d = { 1.0f, 1.0f, 1.0f, 1.0f };    // diffuse// Material reflectivityfloat4 k_a : MATERIALAMBIENT = { 1.0f, 1.0f, 1.0f, 1.0f };    // ambientfloat4 k_d : MATERIALDIFFUSE = { 1.0f, 1.0f, 1.0f, 1.0f };    // diffuse// Texturetexture Environment;			// cubemap// Transformationsfloat4x4 WorldView  : WORLD;float4x4 Projection : PROJECTION;struct VS_OUTPUT{    float4 Position		: POSITION;    float4 Diffuse		: COLOR0;    float3 Reflection	: TEXCOORD0;		    float3 Glossiness	: TEXCOORD1;		    float3 HalfVector	: TEXCOORD2;		};        // Vertex shaderVS_OUTPUT VS(        float3 Position : POSITION,    float3 Normal   : NORMAL,    float3 Tangent  : TANGENT){    VS_OUTPUT Out = (VS_OUTPUT)0;    LightDir = -LightDir;    float3 P = mul(float4(Position, 1), (float4x3)WorldView);   // position (view space)    float3 N = normalize(mul(Normal, (float3x3)WorldView));     // normal (view space)    float3 T = normalize(mul(Tangent, (float3x3)WorldView));    // tangent (view space)    float3 B = cross(N, T);                                     // binormal (view space)    float3 R = normalize(2 * dot(N, LightDir) * N - LightDir);  // reflection vector (view space)    float3 V = -normalize(P);                                   // view direction (view space)    float3 G = normalize(2 * dot(N, V) * N - V);                // glance vector (view space)    float3 H = normalize(LightDir + V);                         // half vector (view space)    float  f = 0.5 - dot(V, N); f = 1 - 4 * f * f;              // fresnel term    // Position     Out.Position = mul(float4(P, 1), Projection);                 // Diffuse + ambient     //Out.Diffuse = I_a * k_a + I_d * k_d * max(0, dot(N, L));     Out.Diffuse = I_a * k_a + I_d * k_d * max(0, dot(N, LightDir));      // Glossiness    Out.Glossiness = f * float4( 0.7,0.7,0.7,0.0 );    // Transform half vector into vertex space    Out.HalfVector = float3(dot(H, N), dot(H, B), dot(H, T));       Out.HalfVector = (1 + Out.HalfVector) / 2;  // bias    // Environment cube map coordinates    Out.Reflection = float3(-G.x, G.y, -G.z);                       return Out;}sampler Sampler0 = sampler_state {    texture = (Environment);    AddressU  = CLAMP;            AddressV  = CLAMP;    AddressW  = CLAMP;    MIPFILTER = LINEAR;    MINFILTER = LINEAR;    MAGFILTER = LINEAR;};float4 PS(VS_OUTPUT In) : COLOR{       float4 Color = (float4)0;    float3 Diffuse, Gloss;    Diffuse = In.Diffuse;// * Noise.a;    Gloss = texCUBE(Sampler0, In.Reflection) * saturate(In.Glossiness);                Color.rgb = Diffuse + Gloss;    Color.w   = 1;    return Color;}  technique tCubeMap{    pass P0    {		CullMode     = CCW;                // Shaders        VertexShader = compile vs_1_1 VS();        PixelShader  = compile ps_1_1 PS();    }  }


Heres the render call:

	if ( m_effect && g_useShaders  )	{		D3DXVECTOR4 tempVec = D3DXVECTOR4( g_lightDirection, 1.0f );			m_effect->SetTexture("Environment", m_environment );	//	m_effect->SetTexture("Tex1", m_backgroundTexture); 	//	m_effect->SetTexture("Tex2", m_detailTexture );		D3DXMATRIX worldView;		D3DXMatrixMultiply( &worldView, &m_worldMatrix, g_camera->GetViewMatrixPtr() ); 		m_effect->SetMatrix("WorldView", &worldView); // world matrix is Identity so no calc required.		m_effect->SetMatrix("Projection", g_simulation->GetProjMatrixPtr() ); 		m_effect->SetVector( "LightDir", &tempVec);		m_effect->SetFloatArray( "I_a", g_ambient, 4 );				m_effect->SetTechnique("tCubeMap"); 						UINT numPasses;//, iPass;		hr = m_effect->Begin( &numPasses, 0 );	//	for( iPass = 0; iPass < numPasses; iPass ++ )		hr = m_effect->BeginPass( 0 );		// Render the object's mesh.		m_mesh->Render( selected );		m_effect->EndPass();		m_effect->End();	}
That looks very complicated, here's mine:

float4x4	matWorld			:	M_W;float4x4	matViewProj			:	M_VP;float4x4	matWorldViewProj	:	M_WVP;float3		cameraPosition		:	F_CP;textureCUBE	cubemap				:	T_0;sampler	CMSampler = sampler_state{	texture	=	( cubemap );};struct VS_INPUT {	float4	Position		: POSITION;	float3	Normal		: NORMAL;};struct VS_OUTPUT {	float4	Position			: POSITION;	float3	Normal			: TEXCOORD0;	float3	EyeToVert		: TEXCOORD1;};	VS_OUTPUT	vs_main(VS_INPUT input){    VS_OUTPUT output = (VS_OUTPUT) 0;    output.Position		= mul( input.Position, matWorldViewProj );    output.Normal		= mul( input.Normal, matWorld );	output.EyeToVert				=	normalize( mul( input.Position, matWorld ) - cameraPosition );	output.EyeToVert				=	reflect( output.EyeToVert, output.Normal );	return output;}float4			ps_main( VS_OUTPUT input ) : COLOR{	return texCUBE( CMSampler, input.EyeToVert );}technique cel{	pass P0	{		VertexShader		= compile vs_2_0 vs_main();		PixelShader		= compile ps_2_0 ps_main();	}}


I would try making sure that the look up does everything right, before you have glossiness factors and the like. Other than that the only difference i can see isthat i have used textureCUBE instead of just texture for passing it in.


Hope that helps,

Dave
Thanks, I'll give it a try... Strangley, the wrong texture on the reflection was caused by a device->SetTexture call within the mesh rendering function.

Didnt think that device textures changed effect textures. There you go.

So, dave, just ignore the bowtie warnings?

Si
Well i would fix them if validate mesh throws that warning, especially if there is a D3DX method to do it. Chances are that the shared vertex won't effect your environment map, but validate mesh doesn't know your implementation so it is prolly warning you because some effects would need indpendant vertices.

Dave

This topic is closed to new replies.

Advertisement