void RebuildMeshTangentSpace(PolygonGroup *group, bool precomputeBinormal/*=true*/) { DVASSERT(group->GetPrimitiveType() == PRIMITIVETYPE_TRIANGLELIST); //only triangle lists for now DVASSERT(group->GetFormat()&EVF_TEXCOORD0); DVASSERT(group->GetFormat()&EVF_NORMAL); Vector<FaceWork> faces; uint32 faceCount = group->GetIndexCount()/3; faces.resize(faceCount); Vector<VertexWork> verticesOrigin; Vector<VertexWork> verticesFull; verticesOrigin.resize(group->GetVertexCount()); verticesFull.resize(group->GetIndexCount()); for (uint32 i=0, sz = group->GetVertexCount(); i<sz; ++i) verticesOrigin[i].refIndex = i; //compute tangent for faces for (uint32 f=0; f<faceCount; ++f) { Vector3 pos[3]; Vector2 texCoord[3]; for (uint32 i=0; i<3; ++i) { int32 workIndex = f*3+i; int32 originIndex; group->GetIndex(workIndex, originIndex); faces[f].indexOrigin[i] = originIndex; group->GetCoord(originIndex, pos[i]); group->GetTexcoord(0, originIndex, texCoord[i]); verticesOrigin[originIndex].refIndices.push_back(workIndex); verticesFull[f*3+i].refIndex = faces[f].indexOrigin[i]; } float32 x10 = pos[1].x - pos[0].x; float32 y10 = pos[1].y - pos[0].y; float32 z10 = pos[1].z - pos[0].z; float32 u10 = texCoord[1].x-texCoord[0].x; float32 v10 = texCoord[1].y-texCoord[0].y; float32 x20 = pos[2].x - pos[0].x; float32 y20 = pos[2].y - pos[0].y; float32 z20 = pos[2].z - pos[0].z; float32 u20 = texCoord[2].x-texCoord[0].x; float32 v20 = texCoord[2].y-texCoord[0].y; float32 d = u10 * v20 - u20 * v10; if(d == 0.0f) { d = 1.0f; // this may happen in case of degenerated triangle } d = 1.0f / d; Vector3 tangent = Vector3((v20 * x10 - v10 * x20) * d, (v20 * y10 - v10 * y20) * d, (v20 * z10 - v10 * z20) * d); Vector3 binormal = Vector3((x20 * u10 - x10 * u20) * d, (y20 * u10 - y10 * u20) * d, (z20 * u10 - z10 * u20) * d); //should we normalize it here or only final result? tangent.Normalize(); binormal.Normalize(); faces[f].tangent = tangent; faces[f].binormal = binormal; for (int32 i=0; i<3; ++i) { verticesFull[f*3+i].tangent = tangent; verticesFull[f*3+i].binormal = binormal; } } /*smooth tangent space preventing mirrored uv's smooth*/ for (uint32 v = 0, sz = verticesFull.size(); v<sz; ++v) { int32 faceId = v/3; VertexWork& originVert = verticesOrigin[verticesFull[v].refIndex]; verticesFull[v].tbRatio = 1; for (int32 iRef=0, refSz = originVert.refIndices.size(); iRef<refSz; ++iRef) { int32 refFaceId = originVert.refIndices[iRef]/3; if (refFaceId == faceId) continue; //check if uv's mirrored; //here we use handness to find mirrored UV's - still not sure if it is better then using dot product (upd: experiments show it is really better) Vector3 n1 = CrossProduct(verticesFull[v].tangent, verticesFull[v].binormal); Vector3 n2 = CrossProduct(faces[refFaceId].tangent, faces[refFaceId].binormal); if (DotProduct(n1, n2)>0.0f) { verticesFull[v].tangent+=faces[refFaceId].tangent; verticesFull[v].binormal+=faces[refFaceId].binormal; verticesFull[v].tbRatio++; } } //as we use normalized tangent space - we renormalize vertex TB instead of rescaling it - think later if it is ok verticesFull[v].tangent.Normalize(); verticesFull[v].binormal.Normalize(); /*float32 invScale = 1.0f/(float32)vertices_full[v].tbRatio; vertices_full[v].tangent*=invScale; vertices_full[v].binormal*=invScale;*/ } const float32 EPS = 0.00001f; //should be the same value as in exporter Vector<int32> groups; //unlock vertices that have different tangent/binormal but same ref for (uint32 i=0, sz=verticesOrigin.size(); i<sz; ++i) { DVASSERT(verticesOrigin[i].refIndices.size()); //vertex with no reference triangles found? verticesOrigin[i].tangent = verticesFull[verticesOrigin[i].refIndices[0]].tangent; verticesOrigin[i].binormal = verticesFull[verticesOrigin[i].refIndices[0]].binormal; if (verticesOrigin[i].refIndices.size()<=1) //1 and less references do not need unlock test continue; groups.clear(); groups.push_back(0); verticesFull[verticesOrigin[i].refIndices[0]].resultGroup = 0; //if has different refs, check different groups; for (int32 refId=1, refSz = verticesOrigin[i].refIndices.size(); refId<refSz; ++refId) { VertexWork& vertexRef = verticesFull[verticesOrigin[i].refIndices[refId]]; bool groupFound = false; for (int32 groupId = 0, groupSz = groups.size(); groupId<groupSz; ++groupId) { const VertexWork& groupRef = verticesFull[verticesOrigin[i].refIndices[groups[groupId]]]; bool groupEqual = FLOAT_EQUAL_EPS(vertexRef.tangent.x, groupRef.tangent.x, EPS) && FLOAT_EQUAL_EPS(vertexRef.tangent.y, groupRef.tangent.y, EPS) && FLOAT_EQUAL_EPS(vertexRef.tangent.z, groupRef.tangent.z, EPS); if (precomputeBinormal) groupEqual &= FLOAT_EQUAL_EPS(vertexRef.binormal.x, groupRef.binormal.x, EPS) && FLOAT_EQUAL_EPS(vertexRef.binormal.y, groupRef.binormal.y, EPS) && FLOAT_EQUAL_EPS(vertexRef.binormal.z, groupRef.binormal.z, EPS); if (groupEqual) { vertexRef.resultGroup = groupId; groupFound = true; break; } } if (!groupFound) //start new group { vertexRef.resultGroup = groups.size(); groups.push_back(refId); } } if (groups.size()>1) //different groups found - unlock vertices and update refs { groups[0] = i; for (int32 groupId = 1, groupSz = groups.size(); groupId<groupSz; ++groupId) { verticesOrigin.push_back(verticesOrigin[i]); groups[groupId] = verticesOrigin.size()-1; verticesOrigin[groups[groupId]].refIndex = i; } for (int32 refId=1, refSz = verticesOrigin[i].refIndices.size(); refId<refSz; ++refId) { VertexWork& vertexRef = verticesFull[verticesOrigin[i].refIndices[refId]]; vertexRef.refIndex = groups[vertexRef.resultGroup]; } } } //copy original polygon group data and fill new tangent/binormal values ScopedPtr<PolygonGroup> tmpGroup(new PolygonGroup()); tmpGroup->AllocateData(group->GetFormat(), group->GetVertexCount(), group->GetIndexCount()); Memcpy(tmpGroup->meshData, group->meshData, group->GetVertexCount()*group->vertexStride); Memcpy(tmpGroup->indexArray, group->indexArray, group->GetIndexCount()*sizeof(int16)); int32 vertexFormat = group->GetFormat() | EVF_TANGENT; if (precomputeBinormal) vertexFormat|=EVF_BINORMAL; group->ReleaseData(); group->AllocateData(vertexFormat, verticesOrigin.size(), verticesFull.size()); //copy vertices for (uint32 i=0, sz = verticesOrigin.size(); i<sz; ++i) { CopyVertex(tmpGroup, verticesOrigin[i].refIndex, group, i); Vector3 normal; group->GetNormal(i, normal); Vector3 tangent = verticesOrigin[i].tangent; tangent -=normal*DotProduct(tangent, normal); tangent.Normalize(); group->SetTangent(i, tangent); if (precomputeBinormal) { Vector3 binormal = -verticesOrigin[i].binormal; binormal -=normal*DotProduct(binormal, normal); binormal.Normalize(); group->SetBinormal(i, binormal); } } //copy indices for (int32 i = 0, sz = verticesFull.size(); i<sz; ++i) group->SetIndex(i, verticesFull[i].refIndex); group->BuildBuffers(); }
void Encoder::Encode(DWORD ID, DWORD Class, DWORD CTNumber, DWORD MType) { BOOL bInsertSurfaceNode = FALSE; BOOL bInsertTextureNode = FALSE; m_pCurrentNode = m_pRootNode; // COBRA - RED - Missing Last Texture Initialization to INVALID DWORD dwCurrentTexture=-1; DWORD dwCurrentzBias=0; NodeType LastType=ROOT; DXFlagsType LastFlags; LastFlags.w=0x00; bool ChangeSurface=false; while(m_pCurrentNode != NULL) { IdleMode(); NodeType Type=m_pCurrentNode->Type; DXFlagsType Flags; Flags.w=m_pCurrentNode->Flags.w; // Make all checks here ChangeSurface=false; if(Type!=LastType || Type==DOF || Type==CLOSEDOF || Type==SLOT ) ChangeSurface = true; if(Flags.b.Texture && m_pCurrentNode->Texture!=dwCurrentTexture) ChangeSurface = true; if(Flags.b.Texture != LastFlags.b.Texture) ChangeSurface=true; if(Flags.b.Alpha != LastFlags.b.Alpha) ChangeSurface=true; if(Flags.b.Gouraud != LastFlags.b.Gouraud ) ChangeSurface=true; if(Flags.b.Lite != LastFlags.b.Lite ) ChangeSurface=true; if((Flags.b.VColor) != (LastFlags.b.VColor)) ChangeSurface=true; if(Flags.b.zBias != LastFlags.b.zBias) ChangeSurface=true; if(Flags.b.zBias && m_pCurrentNode->dwzBias!=dwCurrentzBias) ChangeSurface=true; if(Flags.b.ChromaKey != LastFlags.b.ChromaKey) ChangeSurface=true; // if any change if(ChangeSurface){ // Patch the segments PatchDWORD(); switch (Type){ case DOT: case LINE: case TRIANGLE: // Check if a Textured new surace and last texture still valid if(Flags.b.Texture && m_pCurrentNode->Texture!=dwCurrentTexture){ // No moer used Texture node //WriteTextureNode(m_pCurrentNode); dwCurrentTexture=m_pCurrentNode->Texture; } dwCurrentzBias=m_pCurrentNode->dwzBias; // Write the new Surface WriteSurfaceNode(m_pCurrentNode, dwCurrentTexture); // New ID m_dwNodeID++; break; case DOF: // We've to insert a new DOF node {DXDof *pDof = (DXDof*)m_pCurrentNode; WriteDofNode(pDof);} // and reset surfaces properties Flags.w=0; dwCurrentTexture=-1; // New ID m_dwNodeID++; break; case SLOT: // We've to insert a new SLOT node WriteSlotNode((DXSlot*)m_pCurrentNode); // New ID m_dwNodeID++; break; case CLOSEDOF: // We've to insert a new END DOF node WriteDofEndNode(); // and reset surfaces properties Flags.w=0; dwCurrentTexture=-1; // New ID m_dwNodeID++; break; default : MessageBox(NULL, "Unknown SURFACE TYPE!!", "Encoder", 0); } ChangeSurface=false; LastFlags=Flags; LastType=Type; } switch(Type) { case DOT: { DXVertex *pVertex = (DXVertex*)m_pCurrentNode; D3DVERTEXEX dxVertex; InitializeVertex(&dxVertex); CopyVertex(&(pVertex->Vertex), pVertex->VColor, &dxVertex); /*memcpy((void*)&dxVertex, (const void*)&pVertex->Vertex, sizeof(pVertex->Vertex)); memcpy((void*)(&dxVertex + sizeof(pVertex->Vertex)), (const void*)&pVertex->VColor, sizeof(pVertex->VColor));*/ // Add vertex in NON INDEXED MODE Int16 iIndex = AddVertexToPool(&dxVertex, false); WriteBuffer((const void*)&iIndex, sizeof(iIndex)); } break; case LINE: { DXLine *pLine = (DXLine*)m_pCurrentNode; for(int i=0; i<2; i++) { D3DVERTEXEX dxVertex; InitializeVertex(&dxVertex); CopyVertex(&(pLine->Vertex[i]), pLine->VColor[i], &dxVertex); /*memcpy((void*)&dxVertex, (const void*)&pLine->Vertex[i], sizeof(pLine->Vertex[i])); memcpy((void*)(&dxVertex + sizeof(pLine->Vertex[i])), (const void*)&pLine->VColor[i], sizeof(pLine->VColor[0]));*/ Int16 iIndex = AddVertexToPool(&dxVertex); WriteBuffer((const void*)&iIndex, sizeof(iIndex)); } } break; case TRIANGLE: { DXTriangle *pTriangle = (DXTriangle*)m_pCurrentNode; for(int i=0; i<3; i++) { D3DVERTEXEX dxVertex; InitializeVertex(&dxVertex); CopyVertex(&(pTriangle->Vertex[i]), pTriangle->VColor[i], &dxVertex); /*BYTE *pVertex = (BYTE*)&dxVertex; memcpy((void*)&dxVertex, (const void*)&pTriangle->Vertex[i], sizeof(pTriangle->Vertex[i])); memcpy((void*)(pVertex + sizeof(pTriangle->Vertex[i])), (const void*)&pTriangle->VColor[i], sizeof(pTriangle->VColor[0]));*/ Int16 iIndex = AddVertexToPool(&dxVertex); WriteBuffer((const void*)&iIndex, sizeof(iIndex)); } } break; } // Update pointers //m_pPreviousNode = m_pCurrentNode; m_pCurrentNode = m_pCurrentNode->Next; //m_pNextNode = m_pCurrentNode != NULL ? m_pCurrentNode->Next : NULL; } //// Set last node total size (Node + Data (total indexes size)) //DWORD dwTotalNodeSize = (m_dwTotalSurfaceVertexes*sizeof(Int16) + sizeof(DxSurfaceType)); //memcpy((void*)m_pLastSurfaceNode, (const void*)&dwTotalNodeSize, sizeof(dwTotalNodeSize)); //// Add last node Vertex Counter to last surface node //memcpy(m_pLastSurfaceNode + 12, (const void*)&m_dwTotalSurfaceVertexes, sizeof(m_dwTotalSurfaceVertexes)); // Patch the segments PatchDWORD(); // Write ENDMODEL node DxEndModelType dxEndModelNode; dxEndModelNode.h.dwNodeSize = sizeof(DxEndModelType); dxEndModelNode.h.Type = DX_MODELEND; WriteBuffer((void*)&dxEndModelNode, sizeof(dxEndModelNode)); // Write total number of nodes ((DxDbHeader*)m_pHeader)->dwNodesNr=m_dwTotalNodeCount; // Write other header fields //DWORD dwVertexesPoolStart = (DWORD)(m_pBuffer + m_dwBufferOffset); ((DxDbHeader*)m_pHeader)->pVPool=m_dwBufferOffset; ((DxDbHeader*)m_pHeader)->dwPoolSize=m_dwVertexesPoolSize; ((DxDbHeader*)m_pHeader)->dwNVertices=m_dwVertexesNumber; // Copy vertexes pool after nodes section WriteBuffer((const void*)m_pVertexesPool, m_dwVertexesPoolSize); ((DxDbHeader*)m_pHeader)->Id=ID; ((DxDbHeader*)m_pHeader)->ModelSize= m_dwBufferOffset; ((DxDbHeader*)m_pHeader)->VBClass=Class; ((DxDbHeader*)m_pHeader)->Version=MODEL_VERSION|((MODEL_VERSION^0xffff)<<16); // Assign Materials Features m_dwBufferOffset=AssignSurfaceFeatures(m_pBuffer, CTNumber, MType); #ifdef CRYPTED_MODELS TheVbManager.Encrypt((DWORD*)m_pBuffer); #endif // Write file //WriteFile(); }
bool FX_CSkinRenderer::TransformMesh ( Fx_CHARACTER_t *in_char, const FX_CMesh *pMesh, const KEYFRAME *pKeyFrames, const int in_sysVBNum, const int in_skinPartID ) { int index; UINT subindex; int globalBoneID; TEXTUREVERTEX *sysVB; float numToMultiply; sysVB = m_vertexBuffer; for ( index = 0; \ index < in_sysVBNum; \ ++index, ++sysVB ) { sysVB ->vecPos [0] = sysVB ->vecPos [1] = sysVB ->vecPos [2] = 0.0f; sysVB ->vecNormal [0] = sysVB ->vecNormal [1] = sysVB ->vecNormal [2] = 0.0f; m_pVertexMap = &pMesh->m_meshes[ in_char->stat_LODLevel ].meshchunk->vertexMaps[ index ]; m_pInfluence = &pMesh->m_meshes[ in_char->stat_LODLevel ].influences[ m_pVertexMap->iVertexID ]; if ( 1 == m_pInfluence->iNumBoneInfluences ) { m_pBoneInfluence = &m_pInfluence->boneInfluences [0]; globalBoneID = m_pSkelManager->m_iBoneLookUp[ in_char->attr_skelID ][ m_pBoneInfluence->iBoneID ]; D3DXQuaternionConjugate ( (D3DXQUATERNION *)g_quatConjugate, (D3DXQUATERNION *)pKeyFrames [globalBoneID].quatCoordinate ); Fx_Quat3Multiply( g_quatAfterTransform, g_quatBuffer, pKeyFrames [globalBoneID].quatCoordinate, m_pBoneInfluence ->quatPos, g_quatConjugate ); sysVB ->vecPos [0] = g_quatAfterTransform [0] + pKeyFrames [globalBoneID].vecOffset [0]; sysVB ->vecPos [1] = g_quatAfterTransform [1] + pKeyFrames [globalBoneID].vecOffset [1]; sysVB ->vecPos [2] = g_quatAfterTransform [2] + pKeyFrames [globalBoneID].vecOffset [2]; Fx_Quat3Multiply( g_quatAfterTransform, g_quatBuffer, pKeyFrames [globalBoneID].quatCoordinate, m_pBoneInfluence ->quatNormal, g_quatConjugate ); VectorCopy ( sysVB ->vecNormal, g_quatAfterTransform ); } else { for ( subindex = 0; \ subindex < m_pInfluence ->iNumBoneInfluences; \ ++subindex ) { m_pBoneInfluence = &m_pInfluence ->boneInfluences [subindex]; globalBoneID = m_pSkelManager->m_iBoneLookUp[ in_char->attr_skelID ][ m_pBoneInfluence->iBoneID ]; D3DXQuaternionConjugate ( (D3DXQUATERNION *)g_quatConjugate, (D3DXQUATERNION *)pKeyFrames [globalBoneID].quatCoordinate ); Fx_Quat3Multiply( g_quatAfterTransform, g_quatBuffer, pKeyFrames [globalBoneID].quatCoordinate, m_pBoneInfluence ->quatPos, g_quatConjugate ); sysVB ->vecPos [0] += m_pBoneInfluence ->fWeight * ( g_quatAfterTransform [0] + pKeyFrames [globalBoneID].vecOffset [0] ); sysVB ->vecPos [1] += m_pBoneInfluence ->fWeight * ( g_quatAfterTransform [1] + pKeyFrames [globalBoneID].vecOffset [1] ); sysVB ->vecPos [2] += m_pBoneInfluence ->fWeight * ( g_quatAfterTransform [2] + pKeyFrames [globalBoneID].vecOffset [2] ); Fx_Quat3Multiply( g_quatAfterTransform, g_quatBuffer, pKeyFrames [globalBoneID].quatCoordinate, m_pBoneInfluence ->quatNormal, g_quatConjugate ); sysVB ->vecNormal [0] += m_pBoneInfluence->fWeight * g_quatAfterTransform [0]; sysVB ->vecNormal [1] += m_pBoneInfluence->fWeight * g_quatAfterTransform [1]; sysVB ->vecNormal [2] += m_pBoneInfluence->fWeight * g_quatAfterTransform [2]; } switch ( m_pInfluence ->iNumBoneInfluences ) { case 2: numToMultiply = 0.2f; break; case 3: numToMultiply = 0.333333f; break; case 4: numToMultiply = 0.25f; break; case 5: numToMultiply = 0.2f; break; default: ShowError ( "Bone Influence 개수가 6개 이상입니다!", __FILE__, __LINE__ ); exit (0); } sysVB ->vecNormal [0] *= numToMultiply; sysVB ->vecNormal [1] *= numToMultiply; sysVB ->vecNormal [2] *= numToMultiply; } sysVB ->tu = m_pVertexMap ->VertexTC [0].tu; sysVB ->tv = 1.0f - m_pVertexMap ->VertexTC [0].tv; } CopyVertex( in_char, m_vertexBuffer, in_sysVBNum, in_skinPartID ); return true; }