std::shared_ptr<Mesh> FbxParser::CreateMesh(GameObjectPtr node, FbxNode * fbxNode) { Mesh::Ptr mesh = Mesh::Create(fbxNode->GetName()); FbxMesh* fbxMesh = static_cast<FbxMesh*>(fbxNode->GetNodeAttribute()); int polygonCount = fbxMesh->GetPolygonCount(); int indicesCount = polygonCount * 3; mesh->Positions.Data.reserve(indicesCount * 3); mesh->Indices.Data.reserve(indicesCount); mesh->Colors.Data.reserve(indicesCount * 3); for (int i = 0; i < polygonCount; ++i) { ASSERT(fbxMesh->GetPolygonSize(i) <= 3, "Error: triangulate %s", mesh->GetName()); for (int jj = 0; jj < 3; ++jj) { int ctrPointIdx = fbxMesh->GetPolygonVertex(i, jj); // TODO // Use Triangle Strip instead of triangle list auto position = fbxMesh->GetControlPointAt(ctrPointIdx); mesh->Positions.Data.push_back((double*)&position); int indices = i * 3 + jj; mesh->Indices.Data.push_back(indices); auto color = fbxMesh->GetElementVertexColor(ctrPointIdx); mesh->Colors.Data.push_back((double*)&color); } } mesh->UpdateBuffer(); return mesh; }
std::shared_ptr<Mesh> MeshUtil::CreateQuad(const std::string &name, Vector4 min, Vector4 max) { Mesh::Ptr mesh = Mesh::Create(name); // 1----0 // | | // 2----3 mesh->Positions.Data = { max.x, max.y, max.z, min.x, max.y, max.z, min.x, min.y, min.z, max.x, min.y, min.z }; mesh->Indices.Data = { 0, 1, 2, 2, 3, 0 }; mesh->UVs.Data = { 1, 1, 0, 1, 0, 0, 1, 0 }; LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; mesh->CalculateAABB(); return mesh; }
std::shared_ptr<Mesh> MeshUtil::CreateCube(const std::string &name, Vector4 min, Vector4 max) { Mesh::Ptr mesh = Mesh::Create(name); mesh->Positions.Data = { // FTR, FTL, FBL, FBR max.x, max.y, max.z, min.x, max.y, max.z, min.x, min.y, max.z, max.x, min.y, max.z, // BTR, BTL, BBL, BBR max.x, max.y, min.z, min.x, max.y, min.z, min.x, min.y, min.z, max.x, min.y, min.z }; mesh->Indices.Data = { // front //0, 1, 2, 2, 3, 0, 0, 3, 2, 2, 1, 0, // back //4, 7, 6, 6, 5, 4, 4, 5, 6, 6, 7, 4, // left //2, 1, 5, 5, 6, 2, 2, 6, 5, 5, 1, 2, // right //4, 0, 3, 3, 7, 4, 4, 7, 3, 3, 0, 4, // top //4, 5, 1, 1, 0, 4, 4, 0, 1, 1, 5, 4, // bottom //2, 6, 7, 7, 3, 2 2, 3, 7, 7, 6, 2 }; LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; mesh->CalculateAABB(); return mesh; }
std::shared_ptr<Mesh> FbxUtil::CreateMesh(FbxMesh *fbxMesh) { Mesh::Ptr mesh = Mesh::Create(fbxMesh->GetName()); // read physical data. int polygonCount = fbxMesh->GetPolygonCount(); int indicesCount = polygonCount * 3; mesh->Positions.Data.reserve(indicesCount * 3); mesh->Indices.Data.reserve(indicesCount); if ((m_Options & Options::UV) && fbxMesh->GetElementUVCount() > 0) mesh->UVs.Data.reserve(indicesCount * 2); if ((m_Options & Options::NORMAL) && fbxMesh->GetElementNormalCount() > 0) mesh->Normals.Data.reserve(indicesCount * 3); if ((m_Options & Options::TANGENT) && fbxMesh->GetElementTangent() > 0) mesh->Tangents.Data.reserve(indicesCount * 3); int normalCounter = 0, uvCounter = 0, tangentCounter = 0; for (int i = 0; i < polygonCount; i++) { for (int j = 0; j < 3; j++) { int ctrPtrIndex = fbxMesh->GetPolygonVertex(i, j); auto position = fbxMesh->GetControlPointAt(ctrPtrIndex); mesh->Positions.Data.push_back((float)position.mData[0]); mesh->Positions.Data.push_back((float)position.mData[1]); mesh->Positions.Data.push_back((float)position.mData[2]); // uv if ((m_Options & Options::UV) && fbxMesh->GetElementUVCount() > 0) { int uvIndex = 0; FbxGeometryElementUV* vertexUV = fbxMesh->GetElementUV(); if (vertexUV->GetMappingMode() == FbxGeometryElement::eByControlPoint) { if (vertexUV->GetReferenceMode() == FbxGeometryElement::eDirect) uvIndex = ctrPtrIndex; else if (vertexUV->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) uvIndex = vertexUV->GetIndexArray().GetAt(ctrPtrIndex); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); } else if (vertexUV->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) { if (vertexUV->GetReferenceMode() == FbxGeometryElement::eDirect) uvIndex = uvCounter; else if (vertexUV->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) uvIndex = vertexUV->GetIndexArray().GetAt(uvCounter); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); uvCounter++; } auto uv = vertexUV->GetDirectArray().GetAt(uvIndex); mesh->UVs.Data.push_back((float)uv.mData[0]); mesh->UVs.Data.push_back(1.0f - (float)uv.mData[1]); } // normal if ((m_Options & Options::NORMAL) && fbxMesh->GetElementNormalCount() > 0) { int normalIndex = 0; FbxGeometryElementNormal* vertexNormal = fbxMesh->GetElementNormal(); if (vertexNormal->GetMappingMode() == FbxGeometryElement::eByControlPoint) { if (vertexNormal->GetReferenceMode() == FbxGeometryElement::eDirect) normalIndex = ctrPtrIndex; else if (vertexNormal->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) normalIndex = vertexNormal->GetIndexArray().GetAt(ctrPtrIndex); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); } else if (vertexNormal->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) { if (vertexNormal->GetReferenceMode() == FbxGeometryElement::eDirect) normalIndex = normalCounter; else if (vertexNormal->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) normalIndex = vertexNormal->GetIndexArray().GetAt(normalCounter); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); normalCounter++; } auto normal = vertexNormal->GetDirectArray().GetAt(normalIndex); mesh->Normals.Data.push_back((float)normal.mData[0]); mesh->Normals.Data.push_back((float)normal.mData[1]); mesh->Normals.Data.push_back((float)normal.mData[2]); } // tangent if ((m_Options & Options::TANGENT) && fbxMesh->GetElementNormalCount() > 0) { int tangentIndex = 0; FbxGeometryElementTangent* vertexTangent = fbxMesh->GetElementTangent(); if (vertexTangent->GetMappingMode() == FbxGeometryElement::eByControlPoint) { if (vertexTangent->GetReferenceMode() == FbxGeometryElement::eDirect) tangentIndex = ctrPtrIndex; else if (vertexTangent->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) tangentIndex = vertexTangent->GetIndexArray().GetAt(ctrPtrIndex); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); } else if (vertexTangent->GetMappingMode() == FbxGeometryElement::eByPolygonVertex) { if (vertexTangent->GetReferenceMode() == FbxGeometryElement::eDirect) tangentIndex = tangentCounter; else if (vertexTangent->GetReferenceMode() == FbxGeometryElement::eIndexToDirect) tangentIndex = vertexTangent->GetIndexArray().GetAt(tangentCounter); else ASSERT_MSG(false, "Error: Invalid Reference Mode!"); tangentCounter++; } auto tangent = vertexTangent->GetDirectArray().GetAt(tangentIndex); mesh->Tangents.Data.push_back((float)tangent.mData[0]); mesh->Tangents.Data.push_back((float)tangent.mData[1]); mesh->Tangents.Data.push_back((float)tangent.mData[2]); } mesh->Indices.Data.push_back(i * 3 + j); } } LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; if(m_Options & Options::OPTIMIZE_MESH) MeshUtil::Instance()->OptimizeMesh(mesh); mesh->CalculateAABB(); return mesh; }
std::shared_ptr<Mesh> MeshUtil::CreateCylinder(const std::string &name, float topR, float bottomR, float height, int segH, int segV) { if (segH < 2 || segV < 3) { LOGW << "SegH or SegV tooooo small!"; return nullptr; } Mesh::Ptr mesh = Mesh::Create(name); // allocate some space. mesh->Positions.Data.reserve((segV * segH + 2) * 3); mesh->Indices.Data.reserve((segV * segH * 2) * 3); float avgRadian = Angle::PI * 2.0f / segV; /*if (inclusive) { topR = topR / std::cos(avgRadian * 0.5f); bottomR = bottomR / std::cos(avgRadian * 0.5f); }*/ float currentHeight = height / 2.0f; float currentRadius = topR; float heightStep = height / (segH - 1); float radiusSetp = (topR - bottomR) / (segH - 1); std::vector<unsigned int> previousRing; previousRing.reserve(segV * 3); std::vector<unsigned int> currentRing; currentRing.reserve(segV * 3); unsigned int index = 0; auto AddVertex = [¤tRing, &mesh, &index](float x, float y, float z) -> unsigned int { mesh->Positions.Data.insert(mesh->Positions.Data.end(), { x, y, z }); return index++; }; for (int h = 0; h < segH; h++) { // fill current ring for (int v = 0; v < segV; v++) { float radian = avgRadian * v; currentRing.push_back(AddVertex( cos(radian) * currentRadius, currentHeight, sin(radian) * currentRadius)); } if (previousRing.size() > 0) { // has previous ring, we connect them to triangles. for (unsigned int i = 0; i < currentRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { previousRing[i], previousRing[i + 1], currentRing[i + 1], currentRing[i + 1], currentRing[i], previousRing[i] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { previousRing.back(), previousRing.front(), currentRing.front(), currentRing.front(), currentRing.back(), previousRing.back() }); } else { // don't have previous ring, then we're on top. // close this ring. unsigned int center = AddVertex(0, currentHeight, 0); for (unsigned int i = 0; i < currentRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, currentRing[i + 1], currentRing[i] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, currentRing.front(), currentRing.back() }); } previousRing = currentRing; currentRing.erase(currentRing.begin(), currentRing.end()); currentHeight -= heightStep; currentRadius -= radiusSetp; } // close bottom ring unsigned int center = AddVertex(0, -height / 2.0f, 0); for (unsigned int i = 0; i < previousRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, previousRing[i], previousRing[i + 1] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, previousRing.back(), previousRing.front() }); LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; if (topR == 0 || bottomR == 0) OptimizeMesh(mesh); mesh->CalculateAABB(); return mesh; }
std::shared_ptr<Mesh> MeshUtil::CreateSphere(const std::string &name, float radius, int segH, int segV) { if (segH < 2 || segV < 3) { LOGW << "SegH or SegV tooooo small!"; return nullptr; } Mesh::Ptr mesh = Mesh::Create(name); float avgRadianH = Angle::PI / (segH - 1); float avgRadianV = Angle::PI * 2.0f / segV; float currentHeight = radius; float currentRadius = 0.0f; std::vector<unsigned int> previousRing; previousRing.reserve(segV * 3); std::vector<unsigned int> currentRing; currentRing.reserve(segV * 3); /*if (inclusive) radius = radius / std::cos(avgRadianH * 0.5f);*/ unsigned int index = 0; auto AddVertex = [¤tRing, &mesh, &index](float x, float y, float z) -> unsigned int { mesh->Positions.Data.insert(mesh->Positions.Data.end(), { x, y, z }); return index++; }; for (int h = 0; h < segH; h++) { currentRadius = std::sin(h * avgRadianH) * radius; currentHeight = std::cos(h * avgRadianH) * radius; // fill current ring for (int v = 0; v < segV; v++) { float radian = avgRadianV * v; currentRing.push_back(AddVertex( cos(radian) * currentRadius, currentHeight, sin(radian) * currentRadius)); } if (previousRing.size() > 0) { // has previous ring, we connect them to triangles. for (unsigned int i = 0; i < currentRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { previousRing[i], previousRing[i + 1], currentRing[i + 1], currentRing[i + 1], currentRing[i], previousRing[i] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { previousRing.back(), previousRing.front(), currentRing.front(), currentRing.front(), currentRing.back(), previousRing.back() }); } else { // don't have previous ring, then we're on top. // close this ring. unsigned int center = AddVertex(0, currentHeight, 0); for (unsigned int i = 0; i < currentRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, currentRing[i + 1], currentRing[i] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, currentRing.front(), currentRing.back() }); } previousRing = currentRing; currentRing.erase(currentRing.begin(), currentRing.end()); } // close bottom ring unsigned int center = AddVertex(0, -radius, 0); for (unsigned int i = 0; i < previousRing.size() - 1; i++) { mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, previousRing[i], previousRing[i + 1] }); } mesh->Indices.Data.insert(mesh->Indices.Data.end(), { center, previousRing.back(), previousRing.front() }); LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; OptimizeMesh(mesh); mesh->CalculateAABB(); return mesh; }
std::shared_ptr<Mesh> MeshUtil::CreateIcoSphere(const std::string &name, float radius, int level) { Mesh::Ptr mesh = Mesh::Create(name); std::unordered_map<long long, unsigned int> middlePointIndexCache; unsigned int index = 0; auto AddVertex = [&mesh, &index, &radius](float x, float y, float z) -> unsigned int { float inverse_length = radius / std::sqrt(x * x + y * y + z * z); mesh->Positions.Data.insert(mesh->Positions.Data.end(), { x * inverse_length, y * inverse_length, z * inverse_length }); return index++; }; auto GetMidPoint = [&mesh, &middlePointIndexCache, &AddVertex](int p1, int p2) -> unsigned int { // first check if we have it already bool firstIsSmaller = p1 < p2; long long smallerIndex = firstIsSmaller ? p1 : p2; long long greaterIndex = firstIsSmaller ? p2 : p1; long long key = (smallerIndex << 32) + greaterIndex; auto it = middlePointIndexCache.find(key); if (it != middlePointIndexCache.end()) return it->second; // not in cache, calculate it float p1x = mesh->Positions.Data[p1 * 3]; float p1y = mesh->Positions.Data[p1 * 3 + 1]; float p1z = mesh->Positions.Data[p1 * 3 + 2]; float p2x = mesh->Positions.Data[p2 * 3]; float p2y = mesh->Positions.Data[p2 * 3 + 1]; float p2z = mesh->Positions.Data[p2 * 3 + 2]; // add middle vertex makes sure point is on unit sphere unsigned int i = AddVertex((p1x + p2x) * 0.5f, (p1y + p2y) * 0.5f, (p1z + p2z) * 0.5f); // store it, return index middlePointIndexCache.emplace(key, i); return i; }; // create 12 vertices of a icosahedron float t = (1 + std::sqrt(5.0f)) * 0.5f; AddVertex(-1, t, 0); AddVertex(1, t, 0); AddVertex(-1, -t, 0); AddVertex(1, -t, 0); AddVertex(0, -1, t); AddVertex(0, 1, t); AddVertex(0, -1, -t); AddVertex(0, 1, -t); AddVertex(t, 0, -1); AddVertex(t, 0, 1); AddVertex(-t, 0, -1); AddVertex(-t, 0, 1); // create 20 triangles of the icosahedron mesh->Indices.Data = { // 5 faces around point 0 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, // 5 adjacent faces 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, // 5 faces around point 3 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, // 5 adjacent faces 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 }; // refine triangles std::vector<unsigned int> newIndices; unsigned int oldSize = mesh->Indices.Data.size(); for (int i = 0; i < level; i++) { // allocate memory newIndices.reserve(oldSize * 4); newIndices.erase(newIndices.begin(), newIndices.end()); unsigned int numIndices = mesh->Indices.Data.size() / 3; for (unsigned int j = 0; j < numIndices; j++) { unsigned int index1 = mesh->Indices.Data[j * 3]; unsigned int index2 = mesh->Indices.Data[j * 3 + 1]; unsigned int index3 = mesh->Indices.Data[j * 3 + 2]; unsigned int a = GetMidPoint(index1, index2); unsigned int b = GetMidPoint(index2, index3); unsigned int c = GetMidPoint(index3, index1); newIndices.insert(newIndices.end(), { index1, a, c, index2, b, a, index3, c, b, a, b, c }); } oldSize = newIndices.size(); mesh->Indices.Data = newIndices; } LOGD << mesh->GetName() << " [vtx: " << mesh->Positions.Data.size() / 3 << " tris: " << mesh->Indices.Data.size() / 3 << "]"; mesh->CalculateAABB(); return mesh; }