FbxVector4 FBXScene::GetTangent(FbxMesh* pFBXMesh, int nLayerIndex, int nPolygonIndex, int nPolygonVertexIndex, int nVertexIndex) { FbxVector4 vTangent(0,0,0,0); int nLayerCount = pFBXMesh->GetLayerCount(); if( nLayerIndex < nLayerCount )//for( int i = 0; i < nLayerCount; ++i ) { FbxLayer* pFBXLayer = pFBXMesh->GetLayer(nLayerIndex); if( pFBXLayer ) { FbxLayerElementTangent* pTangents = pFBXLayer->GetTangents(); if( pTangents ) { const FbxLayerElementArrayTemplate<FbxVector4>& pTangentArray = pTangents->GetDirectArray(); if( nVertexIndex < pTangentArray.GetCount() ) { vTangent = pTangentArray.GetAt(nVertexIndex); vTangent.Normalize(); } } } } return vTangent; }
MMatrix ropeGenerator::getMatrixFromParamCurve( MFnNurbsCurve &curveFn, float param, float twist, MAngle divTwist ) { MPoint pDivPos; //Here we control the tangent of the rope curveFn.getPointAtParam( param, pDivPos, MSpace::kWorld ); MVector vTangent( curveFn.tangent( param, MSpace::kWorld ).normal() ); MVector vNormal( curveFn.normal( param, MSpace::kWorld ).normal() ); if ( MAngle( PrevNormal.angle( vNormal ) ).asDegrees() > 90 ) //fprintf(stderr, "Angle = %g\n",MAngle( PrevNormal.angle( vNormal )).asDegrees()); vNormal = vNormal * -1; PrevNormal = vNormal; //if ( vNormal.angle( ) ) MQuaternion qTwist( twist * divTwist.asRadians(), vTangent ); vNormal = vNormal.rotateBy( qTwist ); MVector vExtra( vNormal ^ vTangent ); vNormal.normalize(); vTangent.normalize(); vExtra.normalize(); double dTrans[4][4] ={ {vNormal.x, vNormal.y, vNormal.z, 0.0f}, {vTangent.x, vTangent.y, vTangent.z, 0.0f}, {vExtra.x, vExtra.y, vExtra.z, 0.0f}, {pDivPos.x,pDivPos.y,pDivPos.z, 1.0f}}; MMatrix mTrans( dTrans ); return mTrans; }
bool VCablePathRenderer::RebuildModel() { m_spChainMesh = NULL; IVPathRenderingData* pData = m_spPathRenderingData; if (pData == NULL || !pData->IsValid()) return false; int iNumLinks = pData->GetNumLinks(); m_iLastKnownNumLinks = iNumLinks; if (iNumLinks < 1) { hkvLog::Warning("VCablePathRenderer::RebuildModel: Can't create cable rendering - constraint chain has no links."); return false; } else if (iNumLinks > MAX_NUM_LINKS) { hkvLog::Warning("VCablePathRenderer::RebuildModel: Path chain contains too many links; clamping to %d.", MAX_NUM_LINKS); iNumLinks = MAX_NUM_LINKS; } int iVerticesPerRing = hkvMath::Max(6, VerticesPerRing); int iRingsPerLink = hkvMath::Max(1, RingsPerLink); //#define ENDS_ONLY // Vertices for each ring (final vertex is at the position of the first // vertex), times the number of rings #ifdef ENDS_ONLY int iNumVertices = 0; #else int iNumVertices = ((iNumLinks * iRingsPerLink) + 1) * (iVerticesPerRing + 1); #endif // Extra vertices for the end surfaces iNumVertices += (iVerticesPerRing + 1) * 2; if (iNumVertices > 65535) { hkvLog::Warning("VCablePathRenderer::RebuildModel: Can't create cable rendering - too many verticess."); return false; } #ifdef ENDS_ONLY int iNumTriangles = 0; #else int iNumTriangles = iRingsPerLink * iNumLinks * iVerticesPerRing * 2; #endif // Extra triangles for the end surfaces iNumTriangles += iVerticesPerRing * 2; float fRadius = pData->GetDiameter() / 2.0f; float fCircumference = fRadius * 2.0f * hkvMath::pi(); float fRingHeight = pData->GetLinkLength() / iRingsPerLink; float fTextureVPerRing = fRingHeight / fCircumference; // Load the model that provides the surface material of the cable VDynamicMeshPtr spTemplateMesh; if (!ModelFile.IsEmpty()) spTemplateMesh = VDynamicMesh::LoadDynamicMesh(ModelFile); // Pre-calculate the coordinates, normals and texture offsets of the ring // and end cap vertices VScopedArray<hkvVec3> rgvVertexTemplates(new hkvVec3[iVerticesPerRing + 1]); VScopedArray<hkvVec3> rgvNormalTemplates(new hkvVec3[iVerticesPerRing + 1]); VScopedArray<float> rgfTextureU(new float[iVerticesPerRing + 1]); VScopedArray<hkvVec2> rgvEndTexCoords(new hkvVec2[iVerticesPerRing]); { float fMaxEndTexOffset = 1.0f / (2.0f * hkvMath::pi()); for (int i = 0; i < iVerticesPerRing; ++i) { float fRatio = (float)i / (float)iVerticesPerRing; float fAngle = fRatio * 2.0f * hkvMath::pi(); rgvNormalTemplates[i].set(0.0f, hkvMath::cosRad(fAngle), hkvMath::sinRad(fAngle)); rgvVertexTemplates[i] = rgvNormalTemplates[i] * fRadius; rgfTextureU[i] = fRatio; rgvEndTexCoords[i].set( 0.5f + (hkvMath::cosRad(fAngle) * fMaxEndTexOffset), 0.5f + (hkvMath::sinRad(fAngle) * fMaxEndTexOffset)); } rgvNormalTemplates[iVerticesPerRing] = rgvNormalTemplates[0]; rgvVertexTemplates[iVerticesPerRing] = rgvVertexTemplates[0]; rgfTextureU[iVerticesPerRing] = 1.0f; } VColorRef cableColor(V_RGBA_YELLOW); hkvVec3 vTangent(1.f, 0.f, 0.f); int usageFlags = VIS_MEMUSAGE_STREAM; int bindFlags = VIS_BIND_SHADER_RESOURCE; #ifdef _VR_DX11 // Only specify this flag if this is a true DX11 card, since the engine currently // just passes it along to the device. This fails if the feature is not supported. // It is needed for Compute Shader Skinning if (Vision::Video.GetDXFeatureLevel() >= D3D_FEATURE_LEVEL_11_0) { usageFlags |= VIS_MEMUSAGE_UAV_BYTEADDRESS; bindFlags |= VIS_BIND_UNORDERED_ACCESS; } #endif VDynamicMeshBuilder meshBuilder(iNumVertices, iNumTriangles, iNumLinks + 2, 1, usageFlags, bindFlags); if (spTemplateMesh && (spTemplateMesh->GetSurfaceCount() > 0)) meshBuilder.CopySurfaceFrom(0, *spTemplateMesh->GetSurface(0)); #ifndef ENDS_ONLY for (int iCurrentLink = 0; iCurrentLink < iNumLinks; ++iCurrentLink) { /// The vertices of a chain link are influenced by three bones. int iPrevBone = iCurrentLink; int iThisBone = iCurrentLink + 1; int iNextBone = iCurrentLink + 2; /// The last link has an additional ring of vertices at its end int iLocalNumRings = iCurrentLink == (iNumLinks - 1) ? iRingsPerLink + 1 : iRingsPerLink; /// Compute the vertices for each ring of the current chain link: for (int iCurrentRing = 0; iCurrentRing < iLocalNumRings; ++iCurrentRing) { /// Normalized offset of this ring from the start of the chain link (range 0..1) float fNormOffset = (float)iCurrentRing / (float)iRingsPerLink; /// Calculate the influence factors of the three bones that might affect /// the current ring float fPrevBoneWeight = hkvMath::Max(0.0f, 0.5f - fNormOffset); float fThisBoneWeight = 1.0f - hkvMath::Abs(fNormOffset - 0.5f); float fNextBoneWeight = hkvMath::Max(0.0f, fNormOffset - 0.5f); /// Compute the vertices of one ring. The start/end vertices are at the same /// position, since two different texture coordinates are needed here. int iStartVertex = meshBuilder.GetNextVertexIndex(); for (int iLocalVertex = 0; iLocalVertex <= iVerticesPerRing; ++iLocalVertex) { hkvVec3 vPos(rgvVertexTemplates[iLocalVertex]); // Texture v coordinate increases throughout the chain float fTextureV = ((iCurrentLink * iRingsPerLink) + iCurrentRing) * fTextureVPerRing; hkvVec2 vTexCoords(rgfTextureU[iLocalVertex], fTextureV); // Add vertex data... int iCurrentVertex = meshBuilder.AddVertex(vPos, rgvNormalTemplates[iLocalVertex], vTangent, vTexCoords, cableColor); VASSERT(iCurrentVertex >= 0); // ...and bone weights. if (fPrevBoneWeight > 0.f) meshBuilder.AddBoneWeight(iPrevBone, fPrevBoneWeight); meshBuilder.AddBoneWeight(iThisBone, fThisBoneWeight); if (fNextBoneWeight > 0.f) meshBuilder.AddBoneWeight(iNextBone, fNextBoneWeight); // Unless we are at the last vertex in a ring or at the last ring of the // last link, compute the vertex indices that make up the triangles for // the cable. if ((iCurrentRing < iRingsPerLink) && (iLocalVertex < iVerticesPerRing)) { unsigned short i1, i2, i3; int iNextRing = iStartVertex + iVerticesPerRing + 1; i1 = iStartVertex + iLocalVertex; i2 = iStartVertex + iLocalVertex + 1; i3 = iNextRing + iLocalVertex; meshBuilder.AddTriangle(i1, i2, i3); i1 = iStartVertex + iLocalVertex + 1; i2 = iNextRing + iLocalVertex + 1; i3 = iNextRing + iLocalVertex; meshBuilder.AddTriangle(i1, i2, i3); } //iCurrentVertex++; } // End of vertex loop } // End of rings per chain link loop } // End of chain link loop #endif // Add the end surfaces { hkvVec3 vEndNormal = hkvVec3(-1.0f, 0.0f, 0.0f); hkvVec3 vEndTangent = hkvVec3(0.0f, 1.0f, 0.0f); // Center vertex (Top) int iCenterVertexIndex = meshBuilder.GetNextVertexIndex(); meshBuilder.AddVertex(hkvVec3::ZeroVector(), vEndNormal, vEndTangent, hkvVec2(0.5f, 0.5f), cableColor); meshBuilder.AddBoneWeight(0, 0.5f); meshBuilder.AddBoneWeight(1, 0.5f); // Outer vertices (Top) int iStartVertexIndex = meshBuilder.GetNextVertexIndex(); for (int iLocalVertex = 0; iLocalVertex < iVerticesPerRing; ++iLocalVertex) { meshBuilder.AddVertex(rgvVertexTemplates[iLocalVertex], vEndNormal, vEndTangent, rgvEndTexCoords[iLocalVertex], cableColor); meshBuilder.AddBoneWeight(0, 0.5f); meshBuilder.AddBoneWeight(1, 0.5f); int iNextLocalVertex = iLocalVertex + 1; if (iNextLocalVertex == iVerticesPerRing) iNextLocalVertex = 0; meshBuilder.AddTriangle(iCenterVertexIndex, iStartVertexIndex + iLocalVertex, iStartVertexIndex + iNextLocalVertex); } // Bottom cap: normal points the other way (tangent remains) vEndNormal.set(1.0f, 0.0f, 0.0f); // Center vertex (Bottom) iCenterVertexIndex = meshBuilder.GetNextVertexIndex(); meshBuilder.AddVertex(hkvVec3::ZeroVector(), vEndNormal, vEndTangent, hkvVec2(0.5f, 0.5f), cableColor); meshBuilder.AddBoneWeight(iNumLinks, 0.5f); meshBuilder.AddBoneWeight(iNumLinks + 1, 0.5f); // Outer vertices (Top) iStartVertexIndex = meshBuilder.GetNextVertexIndex(); for (int iLocalVertex = 0; iLocalVertex < iVerticesPerRing; ++iLocalVertex) { meshBuilder.AddVertex(rgvVertexTemplates[iLocalVertex], vEndNormal, vEndTangent, rgvEndTexCoords[iLocalVertex], cableColor); meshBuilder.AddBoneWeight(iNumLinks, 0.5f); meshBuilder.AddBoneWeight(iNumLinks + 1, 0.5f); int iNextLocalVertex = iLocalVertex + 1; if (iNextLocalVertex == iVerticesPerRing) iNextLocalVertex = 0; meshBuilder.AddTriangle(iCenterVertexIndex, iStartVertexIndex + iLocalVertex, iStartVertexIndex + iNextLocalVertex); } } VASSERT(meshBuilder.GetNextVertexIndex() == iNumVertices); VASSERT(meshBuilder.GetNextIndexIndex() == (iNumTriangles * 3)); m_spChainMesh = meshBuilder.Finalize(); VASSERT(m_spChainMesh); m_spChainMesh->SetResourceFlag(VRESOURCEFLAG_AUTODELETE); // Create the entity and the animation state if (!m_spChainEntity) { m_spChainEntity = static_cast<VCableChainEntity*>(Vision::Game.CreateEntity("VCableChainEntity", hkvVec3::ZeroVector())); } m_spChainEntity->SetRenderingData(pData); m_spChainEntity->SetMesh(m_spChainMesh); m_spChainEntity->SetCastShadows(CastDynamicShadows); VisAnimFinalSkeletalResult_cl* pFinalSkeletalResult; VisAnimConfig_cl* pAnimConfig = VisAnimConfig_cl::CreateSkeletalConfig(m_spChainMesh, &pFinalSkeletalResult); m_spChainEntity->SetAnimConfig(pAnimConfig); return true; }