Hi ~
D3DXComputeTangentFrameEx() is hard to use.
I use Full Vertex Model.
Let's see source...
D3DXCleanMesh()... hr = S_OK
D3DXValidMesh()... hr = S_OK
HRESULT hr =
hr is S_FALSE;
So...
Hi ~
D3DXComputeTangentFrameEx() is hard to use.
I use Full Vertex Model.
Let's see source...
D3DXCleanMesh()... hr = S_OK
D3DXValidMesh()... hr = S_OK
HRESULT hr =
Just noting the hr != S_OK isn't enough information. Are you compiling with DEBUG? What errors are you getting? In the dx control panel, set the error reporting to maximum.
If D3DXTANGENT_GENERATE_IN_PLACE doesn't work, it's likely that the data generated won't fit in the input mesh. So, don't use that option! Generate the new mesh, release the old mesh and use the new one.
while i don't use d3dx libs, I'll give this a shot based on this doc.
Your U and V are in the wrong order, and should be swapped. dwUPartialOutSemantic [in] should be tangent, while dwVPartialOutSemantic [in] should be binormal. Also push both tangent and normal and binormal to the same index.
if your vertex format is something like this position, normal, tangent, bitangent, texcoords. then your call to compute tangent frame ex should reflect that. the indices should reflect the offset from the start of the vertex to the semantic location. you are using 0, so everything is overwrighting the position data. I would assume that indices should be 8 for tangent, 12 for bitangent, and 16 for normal based on the vertex format i described above. Read the doc I linked above and try to understand the meaning of each of the functions inputs.
then your call to compute tangent frame ex should reflect that. the indices should reflect the offset from the start of the vertex to the semantic location.
That's not correct.
Be careful, Burnt_Fyr. That parameter is NOT the vertex declaration offset, it is the SEMANTIC index. If he has a single D3DDECLUSAGE_TANGENT, then the index is 0.
However, you're likely correct about the common usage order.
The examples I've seen (and used) are, in order in the D3DXComputeTangentFrameEx call:
D3DDECLUSAGE_TEXCOORD, 0,
D3DDECLUSAGE_TANGENT, 0,
D3DDECLUSAGE_BINORMAL, 0,
D3DDECLUSAGE_NORMAL, 0
EDIT: @AquaMacker - if you have the June 2010 SDK, take a look at the D3D9 examples LocalDeformablePRT and ParallaxOcclusionMapping for using D3DXComputeTangentFrameEx.
then your call to compute tangent frame ex should reflect that. the indices should reflect the offset from the start of the vertex to the semantic location.That's not correct.
Be careful, Burnt_Fyr. That parameter is NOT the vertex declaration offset, it is the SEMANTIC index. If he has a single D3DDECLUSAGE_TANGENT, then the index is 0.
However, you're likely correct about the common usage order.
Good call, this is why I should not attempt to help anyone before morning coffee.
Hi.
Not sure if you still have the problem, but here's my use case (Which works including tangents :))
1. load the mesh using D3DXLoadMeshFromX
2. create temporary mesh and optimize loaded mesh to it
3. swap meshes, release temporary mesh
4. check for existance of normals, binormals and tangents in the mesh
(by getting the vertexdeclaration from the loaded mesh)
5. if no binormals or tangents are there, generate them as followed:
A. create temporary mesh
B. clone loaded/ optimized mesh to temporary mesh
Important note: this is needed using your wanted vertex declaration, including binormals and tangents
(the declaration from the loaded mesh won't work because it has no declarations for binormals and tangents)
C. compute tangentframe using temporary/ cloned mesh
D. swap the temporary with the target mesh
E. release temporary mesh
Here's my code:
bool CD3dmesh::LoadXFile(const std::string pXfilename)
{
if(mD3ddev == NULL)
{
MessageBox(NULL, err_mesh_d3ddev.c_str(), err_windowtext.c_str(), MB_OK);
return false;
}
if(FAILED(D3DXLoadMeshFromXA(pXfilename.c_str(),
D3DXMESH_MANAGED, //SYSTEMMEM, // DYNAMIC??? SYSTEMMEM FOR DYNAMIC OBJECTS??
mD3ddev,
&mAdjacencyBuffer,
&mMaterialBuffer,
NULL,
&mNumMaterials,
&mMesh))) return false;
LPD3DXMESH tempMesh;
if(FAILED(mMesh->Optimize(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)mAdjacencyBuffer->GetBufferPointer(), (DWORD*)mAdjacencyBuffer->GetBufferPointer(), NULL, NULL, &tempMesh)))
return false;
mMesh = tempMesh;
tempMesh->Release();
/** check Mesh vertex declaration for normals, binormals & tangents **/
/**
/** Scenario 1: All there, proceed **/
/** Scenario 2: No normals, tangents and binormals **/
/** Scenario 3: No tangents and binormals **/
bool normals = false;
bool tangents = false;
bool binormals = false;
D3DVERTEXELEMENT9 meshDecl[MAX_FVF_DECL_SIZE];
mMesh->GetDeclaration(meshDecl);
for(unsigned int index=0;index<D3DXGetDeclLength(meshDecl);++index)
{
if(meshDecl[index].Usage == D3DDECLUSAGE_NORMAL) normals = true;
if(meshDecl[index].Usage == D3DDECLUSAGE_TANGENT) tangents = true;
if(meshDecl[index].Usage == D3DDECLUSAGE_BINORMAL) binormals = true;
}
if(!normals) {
if(D3D_OK != D3DXComputeNormals(mMesh, NULL)) return false; }
if(!tangents || !binormals) CalculateTangentFrame();
mTempMaterial = (D3DXMATERIAL*)mMaterialBuffer->GetBufferPointer();
return true;
}
/**************************************************************************************/
/*** CALCULATE TANGENT FRAME ***/
/*** ==> usage: when a mesh doesn't contain tangents and binormals ***/
/*** ==> creates the tangentframe, resulting in a new mesh, same D3DXMESH member ***/
/**************************************************************************************/
bool CD3dmesh::CalculateTangentFrame()
{
#ifdef _DEBUG
OutputDebugStringA("Warning: no tangents and/or binormals in mesh, calculating with D3DX!\n");
#endif
LPD3DXMESH tempMesh;
mMesh->CloneMesh(D3DXMESH_32BIT | D3DXMESH_MANAGED, Crealysm_dxmath::vtxdecl, mD3ddev, &tempMesh);
HRESULT hr = D3DXComputeTangentFrameEx(tempMesh,
D3DDECLUSAGE_TEXCOORD, 0,
D3DDECLUSAGE_BINORMAL, 0,
D3DDECLUSAGE_TANGENT, 0,
D3DDECLUSAGE_NORMAL, 0,
D3DXTANGENT_GENERATE_IN_PLACE,
(DWORD*)mAdjacencyBuffer->GetBufferPointer(),
// 0.01f, 0.25f, 0.01f,
-1.01f, -0.01f, -1.01f,
NULL,
NULL);
if(FAILED(hr))
{
#ifdef _DEBUG
OutputDebugString(DXGetErrorString(hr));
OutputDebugString(L", ");
OutputDebugString(DXGetErrorDescription(hr));
OutputDebugString(L"\n");
#endif
MessageBox(NULL, err_tangent_xfile.c_str(), err_windowtext.c_str(), MB_OK);
return false;
}
mMesh = tempMesh;
tempMesh->Release();
return true;
}
LPD3DXMESH tempMesh;
if(FAILED(mMesh->Optimize(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)mAdjacencyBuffer->GetBufferPointer(), (DWORD*)mAdjacencyBuffer->GetBufferPointer(), NULL, NULL, &tempMesh)))
return false;
mMesh = tempMesh;
tempMesh->Release();
@cozzie: Do you get memory leaks with that code? I'm curious because you keep releasing the temporary meshes you've just created and don't seem to release the original mMesh.
E.g.,
mMesh->Optimize(..., &tmpMesh) creates tmpMesh as the optimized mesh. The sequence after that call should be:
mMesh->Release(); // get rid of the old mesh
mMesh = tmpMesh; // keep the newly created mesh