#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning( disable : 4505 ) #pragma warning( default : 4996 ) #endif #include #include "fbxReader.h" enum GeometryType { GEOMETRY_STATIC, GEOMETRY_RIG, GEOMETRY_MORPH }; osg::Vec3d convertVec3(const FbxVector4& v) { return osg::Vec3d( v[0], v[1], v[2]); } template bool layerElementValid(const FbxLayerElementTemplate* pLayerElement) { if (!pLayerElement) return false; switch (pLayerElement->GetMappingMode()) { case FbxLayerElement::eByControlPoint: case FbxLayerElement::eByPolygonVertex: case FbxLayerElement::eByPolygon: break; default: return false; } switch (pLayerElement->GetReferenceMode()) { case FbxLayerElement::eDirect: case FbxLayerElement::eIndexToDirect: return true; default: break; } return false; } template int getVertexIndex(const FbxLayerElementTemplate* pLayerElement, const FbxMesh* fbxMesh, int nPolygon, int nPolyVertex, int nMeshVertex) { int index = 0; switch (pLayerElement->GetMappingMode()) { case FbxLayerElement::eByControlPoint: index = fbxMesh->GetPolygonVertex(nPolygon, nPolyVertex); break; case FbxLayerElement::eByPolygonVertex: index = nMeshVertex; break; case FbxLayerElement::eByPolygon: index = nPolygon; break; default: OSG_WARN << "getVertexIndex: unsupported FBX mapping mode" << std::endl; } if (pLayerElement->GetReferenceMode() == FbxLayerElement::eDirect) { return index; } return pLayerElement->GetIndexArray().GetAt(index); } template int getPolygonIndex(const FbxLayerElementTemplate* pLayerElement, int nPolygon) { if (pLayerElement && pLayerElement->GetMappingMode() == FbxLayerElement::eByPolygon) { switch (pLayerElement->GetReferenceMode()) { case FbxLayerElement::eDirect: return nPolygon; case FbxLayerElement::eIndexToDirect: return pLayerElement->GetIndexArray().GetAt(nPolygon); default: break; } } return 0; } template FbxT getElement(const FbxLayerElementTemplate* pLayerElement, const FbxMesh* fbxMesh, int nPolygon, int nPolyVertex, int nMeshVertex) { return pLayerElement->GetDirectArray().GetAt(getVertexIndex( pLayerElement, fbxMesh, nPolygon, nPolyVertex, nMeshVertex)); } typedef std::map > GeometryMap; osg::Array* createVec2Array(bool doublePrecision) { if (doublePrecision) return new osg::Vec2dArray; else return new osg::Vec2Array; } osg::Array* createVec3Array(bool doublePrecision) { if (doublePrecision) return new osg::Vec3dArray; else return new osg::Vec3Array; } osg::Array* createVec4Array(bool doublePrecision) { if (doublePrecision) return new osg::Vec4dArray; else return new osg::Vec4Array; } osg::Geometry* getGeometry(osg::Geode* pGeode, GeometryMap& geometryMap, std::vector& stateSetList, GeometryType gt, unsigned int mti, bool bNormal, bool useDiffuseMap, bool useOpacityMap, bool useEmissiveMap, // more here... bool bColor, const osgDB::Options& options, bool lightmapTextures) { GeometryMap::iterator it = geometryMap.find(mti); if (it != geometryMap.end()) { return it->second.get(); } osg::ref_ptr pGeometry; if (gt == GEOMETRY_MORPH) { pGeometry = new osgAnimation::MorphGeometry; } else { pGeometry = new osg::Geometry; } osgDB::Options::PrecisionHint precision = options.getPrecisionHint(); pGeometry->setVertexArray(createVec3Array((precision & osgDB::Options::DOUBLE_PRECISION_VERTEX) != 0), osg::Array::BIND_PER_VERTEX); if (bNormal) pGeometry->setNormalArray(createVec3Array((precision & osgDB::Options::DOUBLE_PRECISION_NORMAL) != 0), osg::Array::BIND_PER_VERTEX); // create as much textures coordinates as needed... if (useDiffuseMap) pGeometry->setTexCoordArray(StateSetContent::DIFFUSE_TEXTURE_UNIT, createVec2Array((precision & osgDB::Options::DOUBLE_PRECISION_TEX_COORD) != 0), osg::Array::BIND_PER_VERTEX); if (useOpacityMap) pGeometry->setTexCoordArray(StateSetContent::OPACITY_TEXTURE_UNIT, createVec2Array((precision & osgDB::Options::DOUBLE_PRECISION_TEX_COORD) != 0), osg::Geometry::BIND_PER_VERTEX); if (useEmissiveMap) pGeometry->setTexCoordArray(StateSetContent::EMISSIVE_TEXTURE_UNIT, createVec2Array((precision & osgDB::Options::DOUBLE_PRECISION_TEX_COORD) != 0), osg::Geometry::BIND_PER_VERTEX); // create more textures coordinates here... if (bColor) pGeometry->setColorArray(createVec4Array((precision & osgDB::Options::DOUBLE_PRECISION_COLOR) != 0), osg::Geometry::BIND_PER_VERTEX); if (mti < stateSetList.size()) { osg::StateSet* stateSet = pGeometry->getOrCreateStateSet(); bool transparent = false; const StateSetContent& ssc = stateSetList[mti]; // set material... if (osg::Material* pMaterial = ssc.material.get()) { stateSet->setAttributeAndModes(pMaterial); transparent = pMaterial->getDiffuse(osg::Material::FRONT).w() < 1.0f; } // diffuse texture map... if (ssc.diffuseTexture) { stateSet->setTextureAttributeAndModes(StateSetContent::DIFFUSE_TEXTURE_UNIT, ssc.diffuseTexture.get()); if (ssc.diffuseScaleU != 1.0 || ssc.diffuseScaleV != 1.0) { // set UV scaling... osg::ref_ptr texmat = new osg::TexMat(); osg::Matrix uvScaling; uvScaling.makeScale(osg::Vec3(ssc.diffuseScaleU, ssc.diffuseScaleV, 1.0)); texmat->setMatrix(uvScaling); stateSet->setTextureAttributeAndModes(StateSetContent::DIFFUSE_TEXTURE_UNIT, texmat.get(), osg::StateAttribute::ON); } if (lightmapTextures) { double factor = ssc.diffuseFactor; osg::ref_ptr texenv = new osg::TexEnvCombine(); texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE); texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); texenv->setSource2_RGB(osg::TexEnvCombine::CONSTANT); texenv->setConstantColor(osg::Vec4(factor, factor, factor, factor)); stateSet->setTextureAttributeAndModes(StateSetContent::DIFFUSE_TEXTURE_UNIT, texenv.get(), osg::StateAttribute::ON); } // setup transparency if (!transparent && ssc.diffuseTexture->getImage()) transparent = ssc.diffuseTexture->getImage()->isImageTranslucent(); } // opacity texture map... if (ssc.opacityTexture) { stateSet->setTextureAttributeAndModes(StateSetContent::OPACITY_TEXTURE_UNIT, ssc.opacityTexture.get()); if (ssc.opacityScaleU != 1.0 || ssc.opacityScaleV != 1.0) { // set UV scaling... osg::ref_ptr texmat = new osg::TexMat(); osg::Matrix uvScaling; uvScaling.makeScale(osg::Vec3(ssc.opacityScaleU, ssc.opacityScaleV, 1.0)); texmat->setMatrix(uvScaling); stateSet->setTextureAttributeAndModes(StateSetContent::OPACITY_TEXTURE_UNIT, texmat.get(), osg::StateAttribute::ON); } // setup combiner to ignore RGB... osg::ref_ptr texenv = new osg::TexEnvCombine(); texenv->setCombine_RGB(osg::TexEnvCombine::REPLACE); texenv->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); stateSet->setTextureAttributeAndModes(StateSetContent::OPACITY_TEXTURE_UNIT, texenv.get(), osg::StateAttribute::ON); // setup transparency... if (!transparent && ssc.opacityTexture->getImage()) transparent = ssc.opacityTexture->getImage()->isImageTranslucent(); } // reflection texture map... if (ssc.reflectionTexture) { stateSet->setTextureAttributeAndModes(StateSetContent::REFLECTION_TEXTURE_UNIT, ssc.reflectionTexture.get()); // setup spherical map... osg::ref_ptr texgen = new osg::TexGen(); texgen->setMode(osg::TexGen::SPHERE_MAP); stateSet->setTextureAttributeAndModes(StateSetContent::REFLECTION_TEXTURE_UNIT, texgen.get(), osg::StateAttribute::ON); // setup combiner for factor... double factor = ssc.reflectionFactor; osg::ref_ptr texenv = new osg::TexEnvCombine(); texenv->setCombine_RGB(osg::TexEnvCombine::INTERPOLATE); texenv->setSource0_RGB(osg::TexEnvCombine::TEXTURE); texenv->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); texenv->setSource2_RGB(osg::TexEnvCombine::CONSTANT); texenv->setConstantColor(osg::Vec4(factor, factor, factor, factor)); stateSet->setTextureAttributeAndModes(StateSetContent::REFLECTION_TEXTURE_UNIT, texenv.get(), osg::StateAttribute::ON); } // emissive texture map if (ssc.emissiveTexture) { if (ssc.emissiveScaleU != 1.0 || ssc.emissiveScaleV != 1.0) { // set UV scaling... osg::ref_ptr texmat = new osg::TexMat(); osg::Matrix uvScaling; uvScaling.makeScale(osg::Vec3(ssc.emissiveScaleU, ssc.emissiveScaleV, 1.0)); texmat->setMatrix(uvScaling); stateSet->setTextureAttributeAndModes(StateSetContent::EMISSIVE_TEXTURE_UNIT, texmat.get(), osg::StateAttribute::ON); } stateSet->setTextureAttributeAndModes(StateSetContent::EMISSIVE_TEXTURE_UNIT, ssc.emissiveTexture.get()); } // add more texture maps here... if (transparent) { stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); stateSet->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); } } geometryMap.insert(std::pair >(mti, pGeometry)); pGeode->addDrawable(pGeometry.get()); return pGeometry.get(); } osgAnimation::VertexInfluence& getVertexInfluence( osgAnimation::VertexInfluenceMap& vim, const std::string& name) { osgAnimation::VertexInfluenceMap::iterator it = vim.lower_bound(name); if (it == vim.end() || name != it->first) { it = vim.insert(it, osgAnimation::VertexInfluenceMap::value_type( name, osgAnimation::VertexInfluence())); it->second.setName(name); } return it->second; } void addChannel( osgAnimation::Channel* pChannel, osg::ref_ptr& pAnimManager, const char* pTakeName) { if (!pChannel) { return; } if (!pAnimManager) pAnimManager = new osgAnimation::BasicAnimationManager; osgAnimation::Animation* pAnimation = 0; const osgAnimation::AnimationList& anims = pAnimManager->getAnimationList(); for (size_t i = 0; i < anims.size(); ++i) { if (anims[i]->getName() == pTakeName) { pAnimation = anims[i].get(); } } if (!pAnimation) { pAnimation = new osgAnimation::Animation; pAnimation->setName(pTakeName); pAnimManager->registerAnimation(pAnimation); } pAnimation->addChannel(pChannel); } void readAnimation(FbxNode* pNode, FbxScene& fbxScene, const std::string& targetName, osg::ref_ptr& pAnimationManager, FbxMesh* pMesh, int nBlendShape, int nBlendShapeChannel, int nShape) { for (int i = 0; i < fbxScene.GetSrcObjectCount(); ++i) { FbxAnimStack* pAnimStack = FbxCast(fbxScene.GetSrcObject(i)); int nbAnimLayers = pAnimStack->GetMemberCount(); const char* pTakeName = pAnimStack->GetName(); if (!pTakeName || !*pTakeName) continue; for (int j = 0; j < nbAnimLayers; j++) { FbxAnimLayer* pAnimLayer = pAnimStack->GetMember(j); FbxAnimCurve* pCurve = pMesh->GetShapeChannel(nBlendShape, nBlendShapeChannel, pAnimLayer, false); if (!pCurve) { continue; } int nKeys = pCurve->KeyGetCount(); if (!nKeys) { continue; } osgAnimation::FloatLinearChannel* pChannel = new osgAnimation::FloatLinearChannel; std::vector >& keyFrameCntr = *pChannel->getOrCreateSampler()->getOrCreateKeyframeContainer(); for (int k = 0; k < nKeys; ++k) { FbxAnimCurveKey key = pCurve->KeyGet(k); double fTime = key.GetTime().GetSecondDouble(); float fValue = static_cast(key.GetValue() * 0.01); keyFrameCntr.push_back(osgAnimation::FloatKeyframe(fTime,fValue)); } pChannel->setTargetName(targetName); std::stringstream ss; ss << nShape; pChannel->setName(ss.str()); addChannel(pChannel, pAnimationManager, pTakeName); } } } void addBindMatrix( BindMatrixMap& boneBindMatrices, FbxNode* pBone, const osg::Matrix& bindMatrix, osgAnimation::RigGeometry* pRigGeometry) { boneBindMatrices.insert(BindMatrixMap::value_type( BindMatrixMap::key_type(pBone, pRigGeometry), bindMatrix)); } void addVec2ArrayElement(osg::Array& a, const FbxVector2& v) { if (a.getType() == osg::Array::Vec2dArrayType) { static_cast(a).push_back(osg::Vec2d(v[0], v[1])); } else { static_cast(a).push_back(osg::Vec2( static_cast(v[0]), static_cast(v[1]))); } } void addVec3ArrayElement(osg::Array& a, const FbxVector4& v) { if (a.getType() == osg::Array::Vec3dArrayType) { static_cast(a).push_back(osg::Vec3d(v[0], v[1], v[2])); } else { static_cast(a).push_back(osg::Vec3( static_cast(v[0]), static_cast(v[1]), static_cast(v[2]))); } } void addColorArrayElement(osg::Array& a, const FbxColor& c) { if (a.getType() == osg::Array::Vec4dArrayType) { static_cast(a).push_back(osg::Vec4d(c.mRed, c.mGreen, c.mBlue, c.mAlpha)); } else { static_cast(a).push_back(osg::Vec4( static_cast(c.mRed), static_cast(c.mGreen), static_cast(c.mBlue), static_cast(c.mAlpha))); } } // scans StateSetList looking for the (first) channel name for the specified map type... std::string getUVChannelForTextureMap(std::vector& stateSetList, const char* pName) { // will return the first occurrence in the state set list... // TODO: what if more than one channel for the same map type? for (unsigned int i = 0; i < stateSetList.size(); i++) { if (0 == strcmp(pName, FbxSurfaceMaterial::sDiffuse)) return stateSetList[i].diffuseChannel; if (0 == strcmp(pName, FbxSurfaceMaterial::sTransparentColor)) return stateSetList[i].opacityChannel; if (0 == strcmp(pName, FbxSurfaceMaterial::sReflection)) return stateSetList[i].reflectionChannel; if (0 == strcmp(pName, FbxSurfaceMaterial::sEmissive)) return stateSetList[i].emissiveChannel; // more here... } return ""; } // scans mesh layers looking for the UV element corresponding to the specified channel name... const FbxLayerElementUV* getUVElementForChannel(std::string uvChannelName, FbxLayerElement::EType elementType, FbxMesh* pFbxMesh) { // scan layers for specified UV channel... for (int cLayerIndex = 0; cLayerIndex < pFbxMesh->GetLayerCount(); cLayerIndex++) { const FbxLayer* pFbxLayer = pFbxMesh->GetLayer(cLayerIndex); if (!pFbxLayer) continue; if (const FbxLayerElementUV* uv = pFbxLayer->GetUVs()) { if (0 == uvChannelName.compare(uv->GetName())) return uv; } } for (int cLayerIndex = 0; cLayerIndex < pFbxMesh->GetLayerCount(); cLayerIndex++) { const FbxLayer* pFbxLayer = pFbxMesh->GetLayer(cLayerIndex); if (!pFbxLayer) continue; if (const FbxLayerElementUV* uv = pFbxLayer->GetUVs(elementType)) { return uv; } } return 0; } typedef std::pair GIPair; typedef std::multimap FbxToOsgVertexMap; typedef std::map OsgToFbxNormalMap; void readMeshTriangle(const FbxMesh * fbxMesh, int i /*polygonIndex*/, int posInPoly0, int posInPoly1, int posInPoly2, int meshVertex0, int meshVertex1, int meshVertex2, FbxToOsgVertexMap& fbxToOsgVertMap, OsgToFbxNormalMap& osgToFbxNormMap, const FbxVector4* pFbxVertices, const FbxLayerElementNormal* pFbxNormals, const FbxLayerElementUV* pFbxUVs_diffuse, const FbxLayerElementUV* pFbxUVs_opacity, const FbxLayerElementUV* pFbxUVs_emissive, const FbxLayerElementVertexColor* pFbxColors, osg::Geometry* pGeometry, osg::Array* pVertices, osg::Array* pNormals, osg::Array* pTexCoords_diffuse, osg::Array* pTexCoords_opacity, osg::Array* pTexCoords_emissive, osg::Array* pColors) { int v0 = fbxMesh->GetPolygonVertex(i, posInPoly0), v1 = fbxMesh->GetPolygonVertex(i, posInPoly1), v2 = fbxMesh->GetPolygonVertex(i, posInPoly2); fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v0, GIPair(pGeometry, pVertices->getNumElements()))); fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v1, GIPair(pGeometry, pVertices->getNumElements() + 1))); fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v2, GIPair(pGeometry, pVertices->getNumElements() + 2))); addVec3ArrayElement(*pVertices, pFbxVertices[v0]); addVec3ArrayElement(*pVertices, pFbxVertices[v1]); addVec3ArrayElement(*pVertices, pFbxVertices[v2]); if (pNormals) { int n0 = getVertexIndex(pFbxNormals, fbxMesh, i, posInPoly0, meshVertex0); int n1 = getVertexIndex(pFbxNormals, fbxMesh, i, posInPoly1, meshVertex1); int n2 = getVertexIndex(pFbxNormals, fbxMesh, i, posInPoly2, meshVertex2); osgToFbxNormMap.insert(OsgToFbxNormalMap::value_type(GIPair(pGeometry, pNormals->getNumElements()), n0)); osgToFbxNormMap.insert(OsgToFbxNormalMap::value_type(GIPair(pGeometry, pNormals->getNumElements() + 1), n1)); osgToFbxNormMap.insert(OsgToFbxNormalMap::value_type(GIPair(pGeometry, pNormals->getNumElements() + 2), n2)); addVec3ArrayElement(*pNormals, pFbxNormals->GetDirectArray().GetAt(n0)); addVec3ArrayElement(*pNormals, pFbxNormals->GetDirectArray().GetAt(n1)); addVec3ArrayElement(*pNormals, pFbxNormals->GetDirectArray().GetAt(n2)); } // add texture maps data (avoid duplicates)... if (pTexCoords_diffuse) { addVec2ArrayElement(*pTexCoords_diffuse, getElement(pFbxUVs_diffuse, fbxMesh, i, posInPoly0, meshVertex0)); addVec2ArrayElement(*pTexCoords_diffuse, getElement(pFbxUVs_diffuse, fbxMesh, i, posInPoly1, meshVertex1)); addVec2ArrayElement(*pTexCoords_diffuse, getElement(pFbxUVs_diffuse, fbxMesh, i, posInPoly2, meshVertex2)); } if (pTexCoords_opacity && (pTexCoords_opacity != pTexCoords_diffuse)) { addVec2ArrayElement(*pTexCoords_opacity, getElement(pFbxUVs_opacity, fbxMesh, i, posInPoly0, meshVertex0)); addVec2ArrayElement(*pTexCoords_opacity, getElement(pFbxUVs_opacity, fbxMesh, i, posInPoly1, meshVertex1)); addVec2ArrayElement(*pTexCoords_opacity, getElement(pFbxUVs_opacity, fbxMesh, i, posInPoly2, meshVertex2)); } // Only spherical reflection maps are supported (so do not add coordinates for the reflection map) if (pTexCoords_emissive && (pTexCoords_emissive != pTexCoords_opacity) && (pTexCoords_emissive != pTexCoords_diffuse)) { addVec2ArrayElement(*pTexCoords_emissive, getElement(pFbxUVs_emissive, fbxMesh, i, posInPoly0, meshVertex0)); addVec2ArrayElement(*pTexCoords_emissive, getElement(pFbxUVs_emissive, fbxMesh, i, posInPoly1, meshVertex1)); addVec2ArrayElement(*pTexCoords_emissive, getElement(pFbxUVs_emissive, fbxMesh, i, posInPoly2, meshVertex2)); } // add more texture maps here... if (pColors) { addColorArrayElement(*pColors, getElement(pFbxColors, fbxMesh, i, posInPoly0, meshVertex0)); addColorArrayElement(*pColors, getElement(pFbxColors, fbxMesh, i, posInPoly1, meshVertex1)); addColorArrayElement(*pColors, getElement(pFbxColors, fbxMesh, i, posInPoly2, meshVertex2)); } } /// Says if a quad should be split using vertices 02 (or else 13) bool quadSplit02(const FbxMesh * fbxMesh, int i /*polygonIndex*/, int posInPoly0, int posInPoly1, int posInPoly2, int posInPoly3, const FbxVector4* pFbxVertices) { // Algorithm may be a bit dumb. If you got a faster one, feel free to change. // Here we test each of the 4 triangles and see if there is one in the opposite direction. // Triangles: 012, 023, 013, 123 // For this, we do a cross product to get normals. We say here the first triangle is the reference, and do a dot product to see the direction. // Normals: na (= (p1-p0)^(p2-p1)), nb, na, nd // Dot products: rb (na.nb), rc, rd // Results: // if r*>0 => convex (02 and 13 are ok, so choose 02) // if rb only <0, or r*<0 => concave, split on 13 // if rc only <0, or rd<0 => concave, split on 02 // else unhandled (crossed polygon?) => choose 02 // In short: // if rb only <0, or r*<0 => return false // else return true int v0 = fbxMesh->GetPolygonVertex(i, posInPoly0); int v1 = fbxMesh->GetPolygonVertex(i, posInPoly1); int v2 = fbxMesh->GetPolygonVertex(i, posInPoly2); int v3 = fbxMesh->GetPolygonVertex(i, posInPoly3); osg::Vec3d p0(pFbxVertices[v0][0], pFbxVertices[v0][1], pFbxVertices[v0][2]); osg::Vec3d p1(pFbxVertices[v1][0], pFbxVertices[v1][1], pFbxVertices[v1][2]); osg::Vec3d p2(pFbxVertices[v2][0], pFbxVertices[v2][1], pFbxVertices[v2][2]); osg::Vec3d p3(pFbxVertices[v3][0], pFbxVertices[v3][1], pFbxVertices[v3][2]); osg::Vec3d na((p1 - p0) ^ (p2 - p1)); osg::Vec3d nb((p2 - p0) ^ (p3 - p2)); double rb(na * nb); if (rb >= 0) return true; // Split at 02 osg::Vec3d nc((p1 - p0) ^ (p3 - p1)); osg::Vec3d nd((p2 - p1) ^ (p3 - p2)); double rc(na * nc); double rd(na * nd); return (rc >= 0 || rd >= 0); } struct PolygonRef { PolygonRef(osg::Geometry* pGeometry, int numPoly, int nVertex) : pGeometry(pGeometry), numPoly(numPoly), nVertex(nVertex) {} osg::Geometry* pGeometry; int numPoly; int nVertex; }; typedef std::vector PolygonRefList; osgDB::ReaderWriter::ReadResult OsgFbxReader::readMesh( FbxNode* pNode, FbxMesh* fbxMesh, std::vector& stateSetList, const char* szName) { GeometryMap geometryMap; osg::Geode* pGeode = new osg::Geode; pGeode->setName(szName); const FbxLayerElementNormal* pFbxNormals = 0; const FbxLayerElementVertexColor* pFbxColors = 0; const FbxLayerElementMaterial* pFbxMaterials = 0; const FbxVector4* pFbxVertices = fbxMesh->GetControlPoints(); // scan layers for Normals, Colors and Materials elements (this will get the first available elements)... for (int cLayerIndex = 0; cLayerIndex < fbxMesh->GetLayerCount(); cLayerIndex++) { const FbxLayer* pFbxLayer = fbxMesh->GetLayer(cLayerIndex); if (!pFbxLayer) continue; // get normals, colors and materials... if (!pFbxNormals) pFbxNormals = pFbxLayer->GetNormals(); if (!pFbxColors) pFbxColors = pFbxLayer->GetVertexColors(); if (!pFbxMaterials) pFbxMaterials = pFbxLayer->GetMaterials(); } // look for UV elements (diffuse, opacity, reflection, emissive, ...) and get their channels names... std::string diffuseChannel = getUVChannelForTextureMap(stateSetList, FbxSurfaceMaterial::sDiffuse); std::string opacityChannel = getUVChannelForTextureMap(stateSetList, FbxSurfaceMaterial::sTransparentColor); std::string emissiveChannel = getUVChannelForTextureMap(stateSetList, FbxSurfaceMaterial::sEmissive); // look for more UV elements here... // UV elements... const FbxLayerElementUV* pFbxUVs_diffuse = getUVElementForChannel(diffuseChannel, FbxLayerElement::eTextureDiffuse, fbxMesh); const FbxLayerElementUV* pFbxUVs_opacity = getUVElementForChannel(opacityChannel, FbxLayerElement::eTextureTransparency, fbxMesh); const FbxLayerElementUV* pFbxUVs_emissive = getUVElementForChannel(emissiveChannel, FbxLayerElement::eTextureEmissive, fbxMesh); // more UV elements here... // check elements validity... if (!layerElementValid(pFbxNormals)) pFbxNormals = 0; if (!layerElementValid(pFbxColors)) pFbxColors = 0; if (!layerElementValid(pFbxUVs_diffuse)) pFbxUVs_diffuse = 0; if (!layerElementValid(pFbxUVs_opacity)) pFbxUVs_opacity = 0; if (!layerElementValid(pFbxUVs_emissive)) pFbxUVs_emissive = 0; // more here... int nPolys = fbxMesh->GetPolygonCount(); int nDeformerCount = fbxMesh->GetDeformerCount(FbxDeformer::eSkin); int nDeformerBlendShapeCount = fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape); GeometryType geomType = GEOMETRY_STATIC; //determine the type of geometry if (nDeformerCount) { geomType = GEOMETRY_RIG; } else if (nDeformerBlendShapeCount) { geomType = GEOMETRY_MORPH; } FbxToOsgVertexMap fbxToOsgVertMap; OsgToFbxNormalMap osgToFbxNormMap; // First add only triangles and quads (easy to split into triangles without // more processing) // This is the reason we store polygons references: PolygonRefList polygonRefList; for (int i = 0, nVertex = 0; i < nPolys; ++i) { int lPolygonSize = fbxMesh->GetPolygonSize(i); int materialIndex = getPolygonIndex(pFbxMaterials, i); osg::Geometry* pGeometry = getGeometry(pGeode, geometryMap, stateSetList, geomType, materialIndex, pFbxNormals != 0, pFbxUVs_diffuse != 0, pFbxUVs_opacity != 0, pFbxUVs_emissive != 0, // more UV elements here... pFbxColors != 0, options, lightmapTextures); osg::Array* pVertices = pGeometry->getVertexArray(); osg::Array* pNormals = pGeometry->getNormalArray(); // get texture coordinates... osg::Array* pTexCoords_diffuse = pGeometry->getTexCoordArray(StateSetContent::DIFFUSE_TEXTURE_UNIT); osg::Array* pTexCoords_opacity = pGeometry->getTexCoordArray(StateSetContent::OPACITY_TEXTURE_UNIT); osg::Array* pTexCoords_emissive = pGeometry->getTexCoordArray(StateSetContent::EMISSIVE_TEXTURE_UNIT); // more texture coordinates here... osg::Array* pColors = pGeometry->getColorArray(); if (lPolygonSize == 3) { // Triangle readMeshTriangle(fbxMesh, i, 0, 1, 2, nVertex, nVertex+1, nVertex+2, fbxToOsgVertMap, osgToFbxNormMap, pFbxVertices, pFbxNormals, pFbxUVs_diffuse, pFbxUVs_opacity, pFbxUVs_emissive, pFbxColors, pGeometry, pVertices, pNormals, pTexCoords_diffuse, pTexCoords_opacity, pTexCoords_emissive, pColors); nVertex += 3; } else if (lPolygonSize == 4) { // Quad - Convert to triangles // Use some fast specialized code to see how the should be decomposed // Two cases : Split at '02' (012 and 023), or split at '13 (013 and 123) bool split02 = quadSplit02(fbxMesh, i, 0, 1, 2, 3, pFbxVertices); int p02 = split02 ? 2 : 3; // Triangle 0, point 2 int p10 = split02 ? 0 : 1; // Triangle 1, point 0 readMeshTriangle(fbxMesh, i, 0, 1, p02, nVertex, nVertex+1, nVertex+p02, fbxToOsgVertMap, osgToFbxNormMap, pFbxVertices, pFbxNormals, pFbxUVs_diffuse, pFbxUVs_opacity, pFbxUVs_emissive, pFbxColors, pGeometry, pVertices, pNormals, pTexCoords_diffuse, pTexCoords_opacity, pTexCoords_emissive, pColors); readMeshTriangle(fbxMesh, i, p10, 2, 3, nVertex+p10, nVertex+2, nVertex+3, fbxToOsgVertMap, osgToFbxNormMap, pFbxVertices, pFbxNormals, pFbxUVs_diffuse, pFbxUVs_opacity, pFbxUVs_emissive, pFbxColors, pGeometry, pVertices, pNormals, pTexCoords_diffuse, pTexCoords_opacity, pTexCoords_emissive, pColors); nVertex += 4; } else if (tessellatePolygons) { // Polygons - Store to add after triangles polygonRefList.push_back(PolygonRef(pGeometry, i, nVertex)); nVertex += lPolygonSize; } else { int nVertex0 = nVertex; nVertex += (std::min)(2, lPolygonSize); for (int j = 2; j < lPolygonSize; ++j, ++nVertex) { readMeshTriangle(fbxMesh, i, 0, j - 1, j, nVertex0, nVertex - 1, nVertex, fbxToOsgVertMap, osgToFbxNormMap, pFbxVertices, pFbxNormals, pFbxUVs_diffuse, pFbxUVs_opacity, pFbxUVs_emissive, pFbxColors, pGeometry, pVertices, pNormals, pTexCoords_diffuse, pTexCoords_opacity, pTexCoords_emissive, pColors); } } } for (unsigned i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); osg::DrawArrays* pDrawArrays = new osg::DrawArrays( GL_TRIANGLES, 0, pGeometry->getVertexArray()->getNumElements()); pGeometry->addPrimitiveSet(pDrawArrays); } // Now add polygons - Convert to triangles // We put vertices in their own PrimitiveSet with Mode=POLYGON; then run the // Tessellator on the Geometry which should tessellate the polygons // automagically. for (PolygonRefList::iterator it = polygonRefList.begin(), itEnd=polygonRefList.end(); it != itEnd; ++it) { int i = it->numPoly; int lPolygonSize = fbxMesh->GetPolygonSize(i); //int materialIndex = getPolygonIndex(pFbxMaterials, i); osg::Geometry* pGeometry = it->pGeometry; osg::Array* pVertices = pGeometry->getVertexArray(); osg::Array* pNormals = pGeometry->getNormalArray(); osg::Array* pTexCoords_diffuse = pGeometry->getTexCoordArray(StateSetContent::DIFFUSE_TEXTURE_UNIT); osg::Array* pTexCoords_opacity = pGeometry->getTexCoordArray(StateSetContent::OPACITY_TEXTURE_UNIT); osg::Array* pTexCoords_emissive = pGeometry->getTexCoordArray(StateSetContent::EMISSIVE_TEXTURE_UNIT); osg::Array* pColors = pGeometry->getColorArray(); // Index of the 1st vertex of the polygon in the geometry int osgVertex0 = pVertices->getNumElements(); for (int j = 0, nVertex = it->nVertex; jGetPolygonVertex(i, j); fbxToOsgVertMap.insert(FbxToOsgVertexMap::value_type(v0, GIPair(pGeometry, pVertices->getNumElements()))); addVec3ArrayElement(*pVertices, pFbxVertices[v0]); if (pNormals) { int n0 = getVertexIndex(pFbxNormals, fbxMesh, i, j, nVertex); osgToFbxNormMap.insert(OsgToFbxNormalMap::value_type(GIPair(pGeometry, pNormals->getNumElements()), n0)); addVec3ArrayElement(*pNormals, pFbxNormals->GetDirectArray().GetAt(n0)); } // add texture maps data (avoid duplicates)... if (pTexCoords_diffuse) { addVec2ArrayElement(*pTexCoords_diffuse, getElement(pFbxUVs_diffuse, fbxMesh, i, j, nVertex)); } if (pTexCoords_opacity && (pTexCoords_opacity != pTexCoords_diffuse)) { addVec2ArrayElement(*pTexCoords_opacity, getElement(pFbxUVs_opacity, fbxMesh, i, j, nVertex)); } // Only spherical reflection maps are supported (so do not add coordinates for the reflection map) if (pTexCoords_emissive && (pTexCoords_emissive != pTexCoords_opacity) && (pTexCoords_emissive != pTexCoords_diffuse)) { addVec2ArrayElement(*pTexCoords_emissive, getElement(pFbxUVs_emissive, fbxMesh, i, j, nVertex)); } // add more texture maps here... if (pColors) { addColorArrayElement(*pColors, getElement(pFbxColors, fbxMesh, i, j, nVertex)); } } osg::DrawArrays* pDrawArrays = new osg::DrawArrays( GL_POLYGON, osgVertex0, pGeometry->getVertexArray()->getNumElements() - osgVertex0); pGeometry->addPrimitiveSet(pDrawArrays); } for (unsigned i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); // Now split polygons if necessary osgUtil::Tessellator tessellator; tessellator.retessellatePolygons(*pGeometry); if (pGeode->getNumDrawables() > 1) { std::stringstream ss; ss << pGeode->getName() << " " << i + 1; pGeometry->setName(ss.str()); } else { pGeometry->setName(pGeode->getName()); } } if (geomType == GEOMETRY_RIG) { typedef std::map, osg::ref_ptr > GeometryRigGeometryMap; GeometryRigGeometryMap old2newGeometryMap; for (unsigned i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); osgAnimation::RigGeometry* pRig = new osgAnimation::RigGeometry; pRig->setSourceGeometry(pGeometry); pRig->copyFrom(*pGeometry); old2newGeometryMap.insert(GeometryRigGeometryMap::value_type( pGeometry, pRig)); pRig->setDataVariance(osg::Object::DYNAMIC); pRig->setUseDisplayList( false ); pGeode->setDrawable(i, pRig); pRig->setInfluenceMap(new osgAnimation::VertexInfluenceMap); pGeometry = pRig; } for (int i = 0; i < nDeformerCount; ++i) { FbxSkin* pSkin = (FbxSkin*)fbxMesh->GetDeformer(i, FbxDeformer::eSkin); int nClusters = pSkin->GetClusterCount(); for (int j = 0; j < nClusters; ++j) { FbxCluster* pCluster = pSkin->GetCluster(j); //assert(KFbxCluster::eNORMALIZE == pCluster->GetLinkMode()); FbxNode* pBone = pCluster->GetLink(); FbxAMatrix transformLink; pCluster->GetTransformLinkMatrix(transformLink); FbxAMatrix transformLinkInverse = transformLink.Inverse(); const double* pTransformLinkInverse = transformLinkInverse; osg::Matrix bindMatrix(pTransformLinkInverse); int nIndices = pCluster->GetControlPointIndicesCount(); int* pIndices = pCluster->GetControlPointIndices(); double* pWeights = pCluster->GetControlPointWeights(); for (int k = 0; k < nIndices; ++k) { int fbxIndex = pIndices[k]; float weight = static_cast(pWeights[k]); for (FbxToOsgVertexMap::const_iterator it = fbxToOsgVertMap.find(fbxIndex); it != fbxToOsgVertMap.end() && it->first == fbxIndex; ++it) { GIPair gi = it->second; osgAnimation::RigGeometry& rig = dynamic_cast( *old2newGeometryMap[gi.first]); addBindMatrix(boneBindMatrices, pBone, bindMatrix, &rig); osgAnimation::VertexInfluenceMap& vim = *rig.getInfluenceMap(); osgAnimation::VertexInfluence& vi = getVertexInfluence(vim, pBone->GetName()); vi.push_back(osgAnimation::VertexIndexWeight( gi.second, weight)); } } } } } else if (geomType == GEOMETRY_MORPH) { for (unsigned i = 0; i < pGeode->getNumDrawables(); ++i) { osg::Geometry* pGeometry = pGeode->getDrawable(i)->asGeometry(); osgAnimation::MorphGeometry& morph = dynamic_cast(*pGeometry); pGeode->addUpdateCallback(new osgAnimation::UpdateMorph(morph.getName())); //read morph geometry for (int nBlendShape = 0; nBlendShape < nDeformerBlendShapeCount; ++nBlendShape) { FbxBlendShape* pBlendShape = FbxCast(fbxMesh->GetDeformer(nBlendShape, FbxDeformer::eBlendShape)); const int nBlendShapeChannelCount = pBlendShape->GetBlendShapeChannelCount(); for (int nBlendShapeChannel = 0; nBlendShapeChannel < nBlendShapeChannelCount; ++nBlendShapeChannel) { FbxBlendShapeChannel* pBlendShapeChannel = pBlendShape->GetBlendShapeChannel(nBlendShapeChannel); if (!pBlendShapeChannel->GetTargetShapeCount()) continue; //Assume one shape if (pBlendShapeChannel->GetTargetShapeCount() > 1) { OSG_WARN << "Multiple FBX Target Shapes, only the first will be used" << std::endl; } const FbxGeometryBase* pMorphShape = pBlendShapeChannel->GetTargetShape(0); const FbxLayerElementNormal* pFbxShapeNormals = 0; if (const FbxLayer* pFbxShapeLayer = pMorphShape->GetLayer(0)) { pFbxShapeNormals = pFbxShapeLayer->GetNormals(); if (!layerElementValid(pFbxShapeNormals)) pFbxShapeNormals = 0; } osg::Geometry* pMorphTarget = new osg::Geometry(morph); pMorphTarget->setVertexArray(static_cast( pMorphTarget->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ARRAYS))); if (pFbxShapeNormals) { if (osg::Array* pNormals = pMorphTarget->getNormalArray()) { pMorphTarget->setNormalArray(static_cast( pNormals->clone(osg::CopyOp::DEEP_COPY_ARRAYS))); } } pMorphTarget->setName(pMorphShape->GetName()); morph.addMorphTarget(pMorphTarget, 0.0f); readAnimation(pNode, fbxScene, morph.getName(), pAnimationManager, fbxMesh, nBlendShape, nBlendShapeChannel, (int)morph.getMorphTargetList().size() - 1); } } } int nMorphTarget = 0; for (int nBlendShape = 0; nBlendShape < nDeformerBlendShapeCount; ++nBlendShape) { FbxBlendShape* pBlendShape = FbxCast(fbxMesh->GetDeformer(nBlendShape, FbxDeformer::eBlendShape)); const int nBlendShapeChannelCount = pBlendShape->GetBlendShapeChannelCount(); for (int nBlendShapeChannel = 0; nBlendShapeChannel < nBlendShapeChannelCount; ++nBlendShapeChannel) { FbxBlendShapeChannel* pBlendShapeChannel = pBlendShape->GetBlendShapeChannel(nBlendShapeChannel); if (!pBlendShapeChannel->GetTargetShapeCount()) continue; //Assume one shape again const FbxGeometryBase* pMorphShape = pBlendShapeChannel->GetTargetShape(0); const FbxLayerElementNormal* pFbxShapeNormals = 0; if (const FbxLayer* pFbxShapeLayer = pMorphShape->GetLayer(0)) { pFbxShapeNormals = pFbxShapeLayer->GetNormals(); if (!layerElementValid(pFbxShapeNormals)) pFbxShapeNormals = 0; } const FbxVector4* pControlPoints = pMorphShape->GetControlPoints(); int nControlPoints = pMorphShape->GetControlPointsCount(); for (int fbxIndex = 0; fbxIndex < nControlPoints; ++fbxIndex) { osg::Vec3d vPos = convertVec3(pControlPoints[fbxIndex]); for (FbxToOsgVertexMap::const_iterator it = fbxToOsgVertMap.find(fbxIndex); it != fbxToOsgVertMap.end() && it->first == fbxIndex; ++it) { GIPair gi = it->second; osgAnimation::MorphGeometry& morphGeom = dynamic_cast(*gi.first); osg::Geometry* pGeometry = morphGeom.getMorphTarget(nMorphTarget).getGeometry(); if (pGeometry->getVertexArray()->getType() == osg::Array::Vec3dArrayType) { osg::Vec3dArray* pVertices = static_cast(pGeometry->getVertexArray()); (*pVertices)[gi.second] = vPos; } else { osg::Vec3Array* pVertices = static_cast(pGeometry->getVertexArray()); (*pVertices)[gi.second] = vPos; } if (pFbxShapeNormals && pGeometry->getNormalArray()) { if (pGeometry->getNormalArray()->getType() == osg::Array::Vec3dArrayType) { osg::Vec3dArray* pNormals = static_cast(pGeometry->getNormalArray()); (*pNormals)[gi.second] = convertVec3( pFbxShapeNormals->GetDirectArray().GetAt(osgToFbxNormMap[gi])); } else { osg::Vec3Array* pNormals = static_cast(pGeometry->getNormalArray()); (*pNormals)[gi.second] = convertVec3( pFbxShapeNormals->GetDirectArray().GetAt(osgToFbxNormMap[gi])); } } } } //don't put this in the for loop as we don't want to do it if the loop continues early ++nMorphTarget; } } } FbxAMatrix fbxGeometricTransform; fbxGeometricTransform.SetTRS( pNode->GeometricTranslation.Get(), pNode->GeometricRotation.Get(), pNode->GeometricScaling.Get()); const double* pGeometricMat = fbxGeometricTransform; osg::Matrix osgGeometricTransform(pGeometricMat); if (geomType == GEOMETRY_RIG) { FbxSkin* pSkin = (FbxSkin*)fbxMesh->GetDeformer(0, FbxDeformer::eSkin); if (pSkin->GetClusterCount()) { FbxAMatrix fbxTransformMatrix; pSkin->GetCluster(0)->GetTransformMatrix(fbxTransformMatrix); const double* pTransformMatrix = fbxTransformMatrix; osgGeometricTransform.postMult(osg::Matrix(pTransformMatrix)); } } osg::Node* pResult = pGeode; if (!osgGeometricTransform.isIdentity()) { osg::MatrixTransform* pMatTrans = new osg::MatrixTransform(osgGeometricTransform); pMatTrans->addChild(pGeode); pResult = pMatTrans; } if (geomType == GEOMETRY_RIG) { //Add the geometry to the skeleton ancestor of one of the bones. FbxSkin* pSkin = (FbxSkin*)fbxMesh->GetDeformer(0, FbxDeformer::eSkin); if (pSkin->GetClusterCount()) { osgAnimation::Skeleton* pSkeleton = getSkeleton( pSkin->GetCluster(0)->GetLink(), fbxSkeletons, skeletonMap); pSkeleton->addChild(pResult); return osgDB::ReaderWriter::ReadResult::FILE_LOADED; } } return osgDB::ReaderWriter::ReadResult(pResult); } osgDB::ReaderWriter::ReadResult OsgFbxReader::readFbxMesh(FbxNode* pNode, std::vector& stateSetList) { FbxMesh* lMesh = FbxCast(pNode->GetNodeAttribute()); if (!lMesh) { return osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE; } return readMesh(pNode, lMesh, stateSetList, pNode->GetName()); }