void SkinInfo::buildBabylonSkeleton(BabylonSkeleton& skel){ if (!hasSkin()){ return; } skel.name = getNodeId(_node) + L"_skeleton"; for (auto& b : _bones){ BabylonBone babbone; babbone.index = static_cast<int>(skel.bones.size()); //babbone.matrix = ConvertToBabylonCoordinateSystem( b.matrixLocalBindPose); babbone.matrix = b.matrixLocalBindPose; babbone.name = b.name; babbone.parentBoneIndex = b.parentBoneIndex; auto animStack = _node->GetScene()->GetCurrentAnimationStack(); FbxString animStackName = animStack->GetName(); //FbxTakeInfo* takeInfo = node->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = animStack->GetLocalTimeSpan().GetStart().GetFrameCount(animTimeMode); auto endFrame = animStack->GetLocalTimeSpan().GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; auto matrixAnim = std::make_shared<BabylonAnimation<FbxMatrix>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"_matrix", L"_matrix", true, 0, static_cast<int>(animLengthInFrame), true); for (auto& kf : b.keyFrames){ babylon_animation_key<FbxMatrix> key; key.frame = kf.frame; //key.values = ConvertToBabylonCoordinateSystem(kf.matrixLocal); key.values = kf.matrixLocal; matrixAnim->appendKey(key); } babbone.animation = matrixAnim; skel.bones.push_back(babbone); } }
int _tmain(int argc, _TCHAR* argv[]) { std::wcout << L"version : 2015.09.14" << std::endl; std::wcout << L"Usage : FbxExporter <path to fbx file> <outdir> [/fps:60|30|24] [/skipemptynodes] [/animstack:\"animstack name\"]" << std::endl; if (argc < 3) { std::wcerr << L"Invalid argument count" << std::endl; return -1; } std::wstring wInputPath(argv[1]); std::wstring wInputDir(argv[1]); std::wstring wInputFileName(argv[1]); auto lastDirSeparator = wInputDir.find_last_of(L'\\'); if (lastDirSeparator == wInputDir.npos) { wInputDir = L"."; } else { wInputDir.erase(lastDirSeparator); wInputFileName.erase(0, lastDirSeparator + 1); } std::wstring wOutputPath(argv[2]); CreateDirectory(wOutputPath.c_str(), nullptr); bool skipEmptyNodes = false; std::wstring animStackName; for (int i = 3; i < argc; ++i) { std::wstring warg = argv[i]; if (warg == L"/skipemptynodes") { skipEmptyNodes = true; } else if (warg.find(L"/fps:") == 0) { if (warg == L"/fps:60") { GlobalSettings::Current().AnimationsTimeMode = FbxTime::EMode::eFrames60; } else if (warg == L"/fps:30") { GlobalSettings::Current().AnimationsTimeMode = FbxTime::EMode::eFrames30; } else if (warg == L"/fps:24") { GlobalSettings::Current().AnimationsTimeMode = FbxTime::EMode::eFrames24; } else { std::wcerr << L"Unrecognized fps parameter" << std::endl; return -2; } } else if (warg.find(L"/animstack:") == 0) { animStackName = warg.substr(11); if (animStackName.size()>0 && animStackName[0] == L'\"') { animStackName.erase(0, 1); } if (animStackName.size() > 0 && animStackName[animStackName.size() - 1] == L'\"') { animStackName.erase(animStackName.size() - 1, 1); } } } FbxSceneLoader sceneLoader(wstringToUtf8(wInputPath)); auto animStackCount = sceneLoader.getScene()->GetSrcObjectCount<FbxAnimStack>(); if (animStackName.size() == 0) { GlobalSettings::Current().AnimStackIndex = 0; } else { for (auto ix = 0; ix < animStackCount; ++ix) { auto animStack = sceneLoader.getScene()->GetSrcObject<FbxAnimStack>(ix); if (utf8ToWstring(animStack->GetName()) == animStackName) { GlobalSettings::Current().AnimStackIndex = ix; } } } std::wcout << L"Animation stacks : " << std::endl; for (auto ix = 0; ix < animStackCount; ++ix) { auto animStack = sceneLoader.getScene()->GetSrcObject<FbxAnimStack>(ix); if (ix == GlobalSettings::Current().AnimStackIndex) { std::wcout << L"[X] "; sceneLoader.getScene()->SetCurrentAnimationStack(animStack); } else { std::wcout << L"[ ] "; } std::wcout << utf8ToWstring(animStack->GetName()); auto ts=animStack->GetLocalTimeSpan(); auto start = ts.GetStart(); auto stop = ts.GetStop(); std::wcout << L"(" << start.GetMilliSeconds() << L" - " << stop.GetMilliSeconds() << L")" << std::endl; } auto root = sceneLoader.rootNode(); BabylonScene babScene(*root, skipEmptyNodes); for (auto& mat : babScene.materials()) { exportTexture(mat.ambientTexture, wOutputPath); exportTexture(mat.diffuseTexture, wOutputPath); exportTexture(mat.specularTexture, wOutputPath); exportTexture(mat.emissiveTexture, wOutputPath); exportTexture(mat.reflectionTexture, wOutputPath); exportTexture(mat.bumpTexture, wOutputPath); } auto json = babScene.toJson(); if (L'\\' != *wOutputPath.crbegin()) { wOutputPath.append(L"\\"); } wOutputPath.append(wInputFileName); auto lastDot = wOutputPath.find_last_of(L'.'); wOutputPath.erase(lastDot); wOutputPath.append(L".babylon"); DeleteFile(wOutputPath.c_str()); std::ofstream stream(wOutputPath); json.serialize(stream); stream.flush(); return 0; }
//FbxAMatrix ComputeTotalMatrix(FbxNode* node, FbxTime time = FBXSDK_TIME_INFINITE){ // //} SkinInfo::SkinInfo(FbxNode* meshNode) : _node(meshNode), _mesh(meshNode->GetMesh()), _skin(nullptr) { int deformerCount = _mesh->GetDeformerCount(); for (auto ix = 0; ix < deformerCount; ++ix){ auto skin = reinterpret_cast<FbxSkin*>(_mesh->GetDeformer(ix, FbxDeformer::eSkin)); if (skin){ _skin = skin; break; } } if (!_skin){ return; } std::vector<FbxPose*> bindPoses; auto poseCount = _node->GetScene()->GetPoseCount(); for (auto ix = 0; ix < poseCount; ++ix){ auto pose = _node->GetScene()->GetPose(ix); if (pose->IsBindPose()){ bindPoses.push_back(pose); } } std::vector<FbxNode*> unsortedFlatListOfNodes; std::vector<FbxCluster*> unsortedFlatListOfClusters; auto clusterCount = _skin->GetClusterCount(); for (auto ix = 0; ix < clusterCount; ++ix){ auto cluster = _skin->GetCluster(ix); if (!cluster) { std::cout << "Invalid skin" << std::endl; _skin = nullptr; return; } auto linkNode = cluster->GetLink(); if (!linkNode){ std::cout << "Invalid skin" << std::endl; _skin = nullptr; return; } unsortedFlatListOfClusters.push_back(cluster); unsortedFlatListOfNodes.push_back(linkNode); } ComputeBoneHierarchy(unsortedFlatListOfNodes, unsortedFlatListOfClusters, _bones, _fbxClusterIndexToBoneIndex, _controlPointToBoneIndicesAndWeights); auto deformType = _skin->GetDeformerType(); auto geometryTransform = GetGeometryTransformation(meshNode); // compute all bones global inverse and global matrix for (auto& bone : _bones){ FbxAMatrix transformMatrix; FbxAMatrix transformLinkMatrix; FbxMatrix globalBindposeInverseMatrix; bone.cluster->GetTransformMatrix(transformMatrix); // The transformation of the mesh at binding time bone.cluster->GetTransformLinkMatrix(transformLinkMatrix); // The transformation of the cluster(joint) at binding time from joint space to world space /*for (auto pose : bindPoses){ auto inPoseIndex = pose->Find(bone.linkNode); if (inPoseIndex >= 0){ auto tempMat = pose->GetMatrix(inPoseIndex); transformLinkMatrix = *(FbxAMatrix*) (double*) &tempMat; break; } }*/ globalBindposeInverseMatrix = FbxMatrix(transformLinkMatrix.Inverse()) * FbxMatrix(transformMatrix) * geometryTransform; bone.matrixGlobalBindPose = ConvertToBabylonCoordinateSystem(globalBindposeInverseMatrix.Inverse()); if (bone.parentBoneIndex == -1){ bone.matrixLocalBindPose = bone.matrixGlobalBindPose; } else{ bone.matrixLocalBindPose = _bones[bone.parentBoneIndex].matrixGlobalBindPose.Inverse()* bone.matrixGlobalBindPose; } } // compute anim auto animStack = _node->GetScene()->GetCurrentAnimationStack(); FbxString animStackName = animStack->GetName(); //FbxTakeInfo* takeInfo = node->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = animStack->GetLocalTimeSpan().GetStart().GetFrameCount(animTimeMode); auto endFrame = animStack->GetLocalTimeSpan().GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; for (auto ix = 0; ix < animLengthInFrame; ix++){ FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); auto currTransformOffset = FbxMatrix(meshNode->EvaluateGlobalTransform(currTime)) * geometryTransform; auto currTransformOffsetInverse = currTransformOffset.Inverse(); // compute global transform and local for (auto& bone : _bones){ BoneAnimKeyFrame kf; kf.frame = ix; kf.matrixGlobal = ConvertToBabylonCoordinateSystem(currTransformOffsetInverse*bone.linkNode->EvaluateGlobalTransform(currTime)); if (bone.parentBoneIndex == -1){ kf.matrixLocal = kf.matrixGlobal; } else{ auto& parentBone = _bones[bone.parentBoneIndex]; kf.matrixLocal = //bone.matrixLocalBindPose; parentBone.keyFrames[parentBone.keyFrames.size() - 1].matrixGlobal.Inverse()* kf.matrixGlobal; } bone.keyFrames.push_back(kf); } } }
BabylonMesh::BabylonMesh(BabylonNode* node) : BabylonAbstractMesh(node), _isEnabled(true), _isVisible(true), _billboardMode(0), _visibility(1), _skeletonId(-1), _pickable(true), _hasVertexAlpha(false), _checkCollision(false), _receiveShadows(false), _infiniteDistance(false), _autoAnimate(false), _autoAnimateFrom(0), _autoAnimateTo(0), _autoAnimateLoop(false), _showBoundingBox(false), _showSubMeshesBoundingBox(false), _applyFog(false), _alphaIndex(0) { pivotMatrix.SetIdentity(); auto fbxNode = node->fbxNode(); std::string ansiName = fbxNode->GetName(); name(std::wstring(ansiName.begin(), ansiName.end())); id(getNodeId(fbxNode)); auto parent = fbxNode->GetParent(); if (parent) { parentId(getNodeId(parent)); } pivotMatrix = ConvertToBabylonCoordinateSystem( GetGeometryTransformation(fbxNode)); auto animStack = fbxNode->GetScene()->GetCurrentAnimationStack(); FbxString animStackName = animStack->GetName(); //FbxTakeInfo* takeInfo = node->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = animStack->GetLocalTimeSpan().GetStart().GetFrameCount(animTimeMode); auto endFrame = animStack->GetLocalTimeSpan().GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; _visibility = static_cast<float>(node->fbxNode()->Visibility.Get()); auto posAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"position", L"position", true, 0, static_cast<int>(animLengthInFrame), true); auto rotAnim = std::make_shared<BabylonAnimation<babylon_vector4>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"rotationQuaternion", L"rotationQuaternion", true, 0, static_cast<int>(animLengthInFrame), true); auto scaleAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"scaling", L"scaling", true, 0, static_cast<int>(animLengthInFrame), true); auto visibilityAnim = std::make_shared<BabylonAnimation<float>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"visibility", L"visibility", true, 0, static_cast<int>(animLengthInFrame), true); auto mesh = fbxNode->GetMesh(); _isVisible = fbxNode->Show.Get(); auto rotCurveNode = fbxNode->LclRotation.GetCurveNode(); auto translateCurveNode = fbxNode->LclTranslation.GetCurveNode(); auto scalingCurveNode = fbxNode->LclScaling.GetCurveNode(); auto visibilityCurveNode = fbxNode->Visibility.GetCurveNode(); if (rotCurveNode || translateCurveNode || scalingCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<babylon_vector3> poskey; babylon_animation_key<babylon_vector4> rotkey; babylon_animation_key<babylon_vector3> scalekey; poskey.frame = ix; rotkey.frame = ix; scalekey.frame = ix; auto currTransform = node->GetLocal(currTime); poskey.values = currTransform.translation(); rotkey.values = currTransform.rotationQuaternion(); scalekey.values = currTransform.scaling(); posAnim->appendKey(poskey); rotAnim->appendKey(rotkey); scaleAnim->appendKey(scalekey); } } if (visibilityCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<float> visibilityKey; visibilityKey.frame = ix; visibilityKey.values = static_cast<float>(node->fbxNode()->Visibility.EvaluateValue(currTime)); visibilityAnim->appendKey(visibilityKey); } } if (!posAnim->isConstant()){ animations.push_back(posAnim); } if (!rotAnim->isConstant()){ animations.push_back(rotAnim); } if (!scaleAnim->isConstant()){ animations.push_back(scaleAnim); } if (!visibilityAnim->isConstant()) { animations.push_back(visibilityAnim); } if (!mesh) { return; } if (mesh->GetPolygonCount() == 0){ return; } _receiveShadows = mesh->ReceiveShadow.Get(); FbxGeometryConverter conv(mesh->GetFbxManager()); conv.ComputePolygonSmoothingFromEdgeSmoothing(mesh); if (!mesh->IsTriangleMesh()) { mesh = (FbxMesh*) conv.Triangulate(mesh, true); } mesh->RemoveBadPolygons(); mesh->GenerateNormals(); FbxStringList uvSetNameList; mesh->GetUVSetNames(uvSetNameList); std::vector<std::string> uniqueUVSets; int uvCount = uvSetNameList.GetCount(); for (int i = 0; i < uvCount; ++i) { std::string value = uvSetNameList.GetStringAt(i); if (std::find(uniqueUVSets.begin(), uniqueUVSets.end(), value) == uniqueUVSets.end()) { uniqueUVSets.push_back(value); } } uvsets = uniqueUVSets; bool hasUv = uniqueUVSets.size() > 0; bool hasUv2 = uniqueUVSets.size() > 1; bool hasUv3 = uniqueUVSets.size() > 2; bool hasUv4 = uniqueUVSets.size() > 3; bool hasUv5 = uniqueUVSets.size() > 4; bool hasUv6 = uniqueUVSets.size() > 5; std::string uvSetName; std::string uv2SetName; std::string uv3SetName; std::string uv4SetName; std::string uv5SetName; std::string uv6SetName; if (hasUv) { uvSetName = uniqueUVSets[0]; } if (hasUv2) { uv2SetName = uniqueUVSets[1]; } if (hasUv3) { uv3SetName = uniqueUVSets[2]; } if (hasUv4) { uv4SetName = uniqueUVSets[3]; } if (hasUv5) { uv5SetName = uniqueUVSets[4]; } if (hasUv6) { uv6SetName = uniqueUVSets[5]; } auto colors = mesh->GetElementVertexColor(); FbxLayerElement::EMappingMode colorMappingMode; FbxLayerElement::EReferenceMode colorReferenceMode; if (colors) { colorMappingMode = colors->GetMappingMode(); colorReferenceMode = colors->GetReferenceMode(); } auto normals = mesh->GetElementNormal(); FbxGeometryElementUV* uvs = nullptr; FbxGeometryElementUV* uvs2 = nullptr; FbxGeometryElementUV* uvs3 = nullptr; FbxGeometryElementUV* uvs4 = nullptr; FbxGeometryElementUV* uvs5 = nullptr; FbxGeometryElementUV* uvs6 = nullptr; FbxLayerElement::EMappingMode uvsMappingMode; FbxLayerElement::EReferenceMode uvsReferenceMode; FbxLayerElement::EMappingMode uvs2MappingMode; FbxLayerElement::EReferenceMode uvs2ReferenceMode; FbxLayerElement::EMappingMode uvs3MappingMode; FbxLayerElement::EReferenceMode uvs3ReferenceMode; FbxLayerElement::EMappingMode uvs4MappingMode; FbxLayerElement::EReferenceMode uvs4ReferenceMode; FbxLayerElement::EMappingMode uvs5MappingMode; FbxLayerElement::EReferenceMode uvs5ReferenceMode; FbxLayerElement::EMappingMode uvs6MappingMode; FbxLayerElement::EReferenceMode uvs6ReferenceMode; if (hasUv) { uvs = mesh->GetElementUV(uvSetName.c_str()); uvsMappingMode = uvs->GetMappingMode(); uvsReferenceMode = uvs->GetReferenceMode(); } if (hasUv2) { uvs2 = mesh->GetElementUV(uv2SetName.c_str()); uvs2MappingMode = uvs2->GetMappingMode(); uvs2ReferenceMode = uvs2->GetReferenceMode(); } if (hasUv3) { uvs3 = mesh->GetElementUV(uv3SetName.c_str()); uvs3MappingMode = uvs3->GetMappingMode(); uvs3ReferenceMode = uvs3->GetReferenceMode(); } if (hasUv4) { uvs4 = mesh->GetElementUV(uv4SetName.c_str()); uvs4MappingMode = uvs4->GetMappingMode(); uvs4ReferenceMode = uvs4->GetReferenceMode(); } if (hasUv5) { uvs5 = mesh->GetElementUV(uv5SetName.c_str()); uvs5MappingMode = uvs5->GetMappingMode(); uvs5ReferenceMode = uvs5->GetReferenceMode(); } if (hasUv6) { uvs6 = mesh->GetElementUV(uv6SetName.c_str()); uvs6MappingMode = uvs6->GetMappingMode(); uvs6ReferenceMode = uvs6->GetReferenceMode(); } auto normalMappingMode = normals->GetMappingMode(); auto normalReferenceMode = normals->GetReferenceMode(); std::vector<SubmeshData> submeshes; auto materialCount = node->fbxNode()->GetMaterialCount(); if (materialCount == 0) { materialCount = 1; } submeshes.resize(materialCount); auto baseLayer = mesh->GetLayer(0); auto materials = baseLayer->GetMaterials(); FbxLayerElement::EMappingMode materialMappingMode = materials ? materials->GetMappingMode() : FbxLayerElement::eByPolygon; // extract deformers SkinInfo skinInfo(fbxNode); if (skinInfo.hasSkin()){ associatedSkeleton = std::make_shared<BabylonSkeleton>(); skinInfo.buildBabylonSkeleton(*associatedSkeleton); } auto triangleCount = mesh->GetPolygonCount(); for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { int materialIndex = 0; if (materialCount > 0 && materials) { switch (materialMappingMode) { case FbxLayerElement::eAllSame: materialIndex = materials->GetIndexArray().GetAt(0); break; case FbxLayerElement::eByPolygon: materialIndex = materials->GetIndexArray().GetAt(triangleIndex); } } auto& submesh = submeshes[materialIndex]; triangle t; for (int cornerIndex = 0; cornerIndex < 3; ++cornerIndex) { auto controlPointIndex = mesh->GetPolygonVertex(triangleIndex, cornerIndex); auto vertexIndex = triangleIndex * 3 + cornerIndex; auto position = mesh->GetControlPoints()[controlPointIndex]; position[2] = -position[2]; BabylonVertex v; v.position = position; if (normals) { int normalMapIndex = (normalMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int normalValueIndex = (normalReferenceMode == FbxLayerElement::eDirect) ? normalMapIndex : normals->GetIndexArray().GetAt(normalMapIndex); v.normal = normals->GetDirectArray().GetAt(normalValueIndex); v.normal.z = -v.normal.z; } if (colors) { int mappingIndex = (colorMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (colorReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : colors->GetIndexArray().GetAt(mappingIndex); v.color = colors->GetDirectArray().GetAt(valueIndex); } if (uvs) { int mappingIndex = (uvsMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvsReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs->GetIndexArray().GetAt(mappingIndex); v.uv = uvs->GetDirectArray().GetAt(valueIndex); //v.uv.y = 1 - v.uv.y; } if (uvs2) { int mappingIndex = (uvs2MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs2ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs2->GetIndexArray().GetAt(mappingIndex); v.uv2 = uvs2->GetDirectArray().GetAt(valueIndex); } if (uvs3) { int mappingIndex = (uvs3MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs3ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs3->GetIndexArray().GetAt(mappingIndex); v.uv3 = uvs3->GetDirectArray().GetAt(valueIndex); } if (uvs4) { int mappingIndex = (uvs4MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs4ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs4->GetIndexArray().GetAt(mappingIndex); v.uv4 = uvs4->GetDirectArray().GetAt(valueIndex); } if (uvs5) { int mappingIndex = (uvs5MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs5ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs5->GetIndexArray().GetAt(mappingIndex); v.uv5 = uvs5->GetDirectArray().GetAt(valueIndex); } if (uvs6) { int mappingIndex = (uvs6MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs6ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs6->GetIndexArray().GetAt(mappingIndex); v.uv6 = uvs6->GetDirectArray().GetAt(valueIndex); } if (skinInfo.hasSkin()){ auto& skinData = skinInfo.controlPointBoneIndicesAndWeights(controlPointIndex); for (std::size_t boneix = 0; boneix < skinData.size()&&boneix< (size_t)4; ++boneix){ v.boneIndices[boneix] = skinData[boneix].index; v.boneWeights[boneix] = static_cast<float>(skinData[boneix].weight); } for (auto boneix = skinData.size(); boneix < 4; ++boneix){ v.boneIndices[boneix] = skinInfo.bonesCount(); v.boneWeights[boneix] = 0; } } auto foundVertex = submesh.knownVertices.find(v); if (foundVertex != submesh.knownVertices.end()) { //submesh.indices.push_back(foundVertex->second); t.indices[cornerIndex] = foundVertex->second; } else { auto index = static_cast<int>(submesh.vertices.size()); submesh.vertices.push_back(v); //submesh.indices.push_back(index); submesh.knownVertices[v] = index; t.indices[cornerIndex] = index; } } if (submesh.knownTriangles.insert(t).second) { submesh.indices.push_back(t.indices[0]); submesh.indices.push_back(t.indices[1]); submesh.indices.push_back(t.indices[2]); } else { std::cout << "duplicate triangle found (and eliminated) in " << fbxNode->GetName() << std::endl; } } std::uint32_t vertexOffset = 0; for (auto matIndex = 0u; matIndex < submeshes.size(); ++matIndex) { auto& submesh = submeshes[matIndex]; BabylonSubmesh babsubmesh; babsubmesh.indexCount = static_cast<int>(submesh.indices.size()); babsubmesh.indexStart = static_cast<int>(_indices.size()); babsubmesh.materialIndex = matIndex; babsubmesh.verticesCount = static_cast<int>(submesh.vertices.size()); babsubmesh.verticesStart = static_cast<int>(_positions.size()); for (auto& v : submesh.vertices) { _positions.push_back(v.position); if (normals) { _normals.push_back(v.normal); } if (colors) { _colors.push_back(v.color); } if (uvs) { _uvs.push_back(v.uv); } if (uvs2) { _uvs2.push_back(v.uv2); } if (uvs3) { _uvs3.push_back(v.uv3); } if (uvs4) { _uvs4.push_back(v.uv4); } if (uvs5) { _uvs5.push_back(v.uv5); } if (uvs6) { _uvs6.push_back(v.uv6); } if (skinInfo.hasSkin()){ float weight0 = v.boneWeights[0]; float weight1 = v.boneWeights[1]; float weight2 = v.boneWeights[2]; int bone0 = v.boneIndices[0]; int bone1 = v.boneIndices[1]; int bone2 = v.boneIndices[2]; int bone3 = v.boneIndices[3]; _boneWeights.push_back(babylon_vector4( weight0, weight1, weight2, 1.0f - weight0 - weight1 - weight2)); _boneIndices.push_back((bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0); } } for (auto i : submesh.indices) { _indices.push_back(i + vertexOffset); } vertexOffset = static_cast<int>(_positions.size()); _submeshes.push_back(babsubmesh); } }