Scene::Scene() : m_pTerrainMesh(new Framework::Mesh("Ground.xml")) , m_pCubeMesh(new Framework::Mesh("UnitCube.xml")) , m_pTetraMesh(new Framework::Mesh("UnitTetrahedron.xml")) , m_pCylMesh(new Framework::Mesh("UnitCylinder.xml")) , m_pSphereMesh(new Framework::Mesh("UnitSphere.xml")) { //Align the size of each MaterialBlock to the uniform buffer alignment. int uniformBufferAlignSize = 0; glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniformBufferAlignSize); m_sizeMaterialBlock = sizeof(MaterialBlock); m_sizeMaterialBlock += uniformBufferAlignSize - (m_sizeMaterialBlock % uniformBufferAlignSize); int sizeMaterialUniformBuffer = m_sizeMaterialBlock * MATERIAL_COUNT; std::vector<MaterialBlock> materials; GetMaterials(materials); assert(materials.size() == MATERIAL_COUNT); std::vector<GLubyte> mtlBuffer; mtlBuffer.resize(sizeMaterialUniformBuffer, 0); GLubyte *bufferPtr = &mtlBuffer[0]; for(size_t mtl = 0; mtl < materials.size(); ++mtl) memcpy(bufferPtr + (mtl * m_sizeMaterialBlock), &materials[mtl], sizeof(MaterialBlock)); glGenBuffers(1, &m_materialUniformBuffer); glBindBuffer(GL_UNIFORM_BUFFER, m_materialUniformBuffer); glBufferData(GL_UNIFORM_BUFFER, sizeMaterialUniformBuffer, bufferPtr, GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); }
std::shared_ptr<Scene> SceneFactory::CreateFromFile(const std::string& filename) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_ImproveCacheLocality | aiProcess_JoinIdenticalVertices | aiProcess_PreTransformVertices); if (scene == NULL) { std::printf("\nImport failed:\n\t"); auto errorString = importer.GetErrorString(); std::printf(errorString); std::printf("\n"); return nullptr; } Scene::vertexList vertices = GetVertices(scene); Scene::triangleList faces = GetFaces(scene); Scene::materialList materials = GetMaterials(scene); return std::shared_ptr<Scene>(new Scene(vertices, faces, materials)); }
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()->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); FbxTakeInfo* takeInfo = fbxNode->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = takeInfo->mLocalTimeSpan.GetStart().GetFrameCount(animTimeMode); auto endFrame = takeInfo->mLocalTimeSpan.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 (auto boneix = 0; boneix < skinData.size()&&boneix<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); } }
void ResourceModel::AddTMFObject(FILE* fp) { TMFObject* pObject = new TMFObject(); pObject->Initialise( fp, GetMaterials() ); mObjectList.push_back( pObject ); }
std::shared_ptr<Mesh> FBXConverter::LoadMesh(FbxMesh* fbxMesh) { assert(fbxMesh->GetLayerCount() > 0); auto node = fbxMesh->GetNode(); auto layer = fbxMesh->GetLayer(0); if (layer->GetNormals() == nullptr) { fbxMesh->GenerateNormals(true); } auto uvs = layer->GetUVs(); auto vcolors = layer->GetVertexColors(); auto normals = layer->GetNormals(); auto binormals = layer->GetBinormals(); auto materials = layer->GetMaterials(); auto controlPoints = fbxMesh->GetControlPoints(); auto controlPointsCount = fbxMesh->GetControlPointsCount(); auto polygonCount = fbxMesh->GetPolygonCount(); std::vector<FbxFace> faces; // Load weights std::vector<BoneConnector> bcs_temp; std::vector<Vertex> vs_temp; LoadSkin(fbxMesh, bcs_temp, vs_temp); // generate face vertex int32_t vertexID = 0; for (int32_t polygonIndex = 0; polygonIndex < polygonCount; polygonIndex++) { int polygonPointCount = fbxMesh->GetPolygonSize(polygonIndex); FbxFace face; for (int32_t polygonPointIndex = 0; polygonPointIndex < polygonPointCount; polygonPointIndex++) { auto ctrlPointIndex = fbxMesh->GetPolygonVertex(polygonIndex, polygonPointIndex); Vertex v; v.Position = LoadPosition(fbxMesh, ctrlPointIndex); v.Weights = vs_temp[ctrlPointIndex].Weights; if (normals != nullptr) { v.Normal = LoadNormal(normals, vertexID, ctrlPointIndex); } if (uvs != nullptr) { v.UV = LoadUV(fbxMesh, uvs, vertexID, ctrlPointIndex, polygonIndex, polygonPointIndex); } else { // Auto generated v.UV[0] = v.Position[0] + v.Position[2]; v.UV[1] = v.Position[1]; } if (vcolors != nullptr) { v.VertexColor = LoadVertexColor(fbxMesh, vcolors, vertexID, ctrlPointIndex, polygonIndex, polygonPointIndex); } face.Vertecies.push_back(v); vertexID++; } faces.push_back(face); } // 面の表裏入れ替え for (auto& face : faces) { std::reverse(face.Vertecies.begin(), face.Vertecies.end()); } // メッシュで使用可能な形式に変換 // 頂点変換テーブル作成 int32_t vInd = 0; std::map<Vertex, int32_t> v2ind; std::map<int32_t, Vertex> ind2v; for (auto& face : faces) { for (int32_t vi = 0; vi < (int32_t)face.Vertecies.size(); vi++) { auto vertex = face.Vertecies[vi]; auto it = v2ind.find(vertex); if (it == v2ind.end()) { v2ind[vertex] = vInd; ind2v[vInd] = vertex; vInd++; } } } // 設定 auto mesh = std::make_shared<Mesh>(); mesh->Name = node->GetName(); mesh->BoneConnectors = bcs_temp; mesh->Vertexes.resize(vInd); for (auto& iv : ind2v) { mesh->Vertexes[iv.first] = iv.second; } for (auto& face : faces) { if (face.Vertecies.size() < 3) continue; if (face.Vertecies.size() == 3) { Face f; f.Index[0] = v2ind[face.Vertecies[0]]; f.Index[1] = v2ind[face.Vertecies[1]]; f.Index[2] = v2ind[face.Vertecies[2]]; mesh->Faces.push_back(f); } if (face.Vertecies.size() == 4) { Face f0; f0.Index[0] = v2ind[face.Vertecies[0]]; f0.Index[1] = v2ind[face.Vertecies[1]]; f0.Index[2] = v2ind[face.Vertecies[2]]; mesh->Faces.push_back(f0); Face f1; f1.Index[0] = v2ind[face.Vertecies[0]]; f1.Index[1] = v2ind[face.Vertecies[2]]; f1.Index[2] = v2ind[face.Vertecies[3]]; mesh->Faces.push_back(f1); } } // Binormal,Tangent計算 std::map<int32_t, VertexNormals> vInd2Normals; for (const auto& face : mesh->Faces) { FbxVector4 binormal, tangent; CalcTangentSpace( mesh->Vertexes[face.Index[0]], mesh->Vertexes[face.Index[1]], mesh->Vertexes[face.Index[2]], binormal, tangent); for (auto i = 0; i < 3; i++) { vInd2Normals[face.Index[i]].Binormal += binormal; vInd2Normals[face.Index[i]].Tangent += tangent; vInd2Normals[face.Index[i]].Count += 1; } } for (auto& vn : vInd2Normals) { vn.second.Binormal /= vn.second.Count; vn.second.Tangent /= vn.second.Count; } for (auto& vn : vInd2Normals) { mesh->Vertexes[vn.first].Binormal = vn.second.Binormal; mesh->Vertexes[vn.first].Tangent = vn.second.Tangent; // 適当な値を代入する if (mesh->Vertexes[vn.first].Binormal.Length() == 0.0f) { if (mesh->Vertexes[vn.first].Normal != FbxVector4(1, 0, 0)) { mesh->Vertexes[vn.first].Binormal = FbxVector4(1, 0, 0); mesh->Vertexes[vn.first].Tangent = FbxVector4(0, 1, 0); } else { mesh->Vertexes[vn.first].Binormal = FbxVector4(0, 1, 0); mesh->Vertexes[vn.first].Tangent = FbxVector4(1, 0, 0); } } } return mesh; }