void copy_face_attributes(Mesh::Ptr mesh, std::unique_ptr<draco::Mesh>& draco_mesh) { const auto num_faces = mesh->get_num_faces(); const auto& attribute_names = mesh->get_attribute_names(); for (const auto& name : attribute_names) { const auto& values = mesh->get_attribute(name); if (values.size() % num_faces != 0) continue; const auto num_rows = num_faces; const auto num_cols = values.size() / num_faces; draco::GeometryAttribute attr; if (name == "face_normal") { attr.Init(draco::GeometryAttribute::NORMAL, nullptr, num_cols, draco::DT_FLOAT64, false, sizeof(Float) * num_cols, 0); } else if (name.substr(0, 4) == "face"){ attr.Init(draco::GeometryAttribute::GENERIC, nullptr, num_cols, draco::DT_FLOAT64, false, sizeof(Float) * num_cols, 0); } else { // Not a face attribute. continue; } const auto id = draco_mesh->AddAttribute(attr, true, num_rows); draco_mesh->SetAttributeElementType(id, draco::MESH_FACE_ATTRIBUTE); for (size_t i=0; i<num_rows; i++) { draco_mesh->attribute(id)->SetAttributeValue( draco::AttributeValueIndex(i), values.data() + i*num_cols); } std::unique_ptr<draco::AttributeMetadata> metadata = std::make_unique<draco::AttributeMetadata>(); metadata->AddEntryString("name", name); draco_mesh->AddAttributeMetadata(id, std::move(metadata)); } }
Elements::Ptr Elements::adapt_boundary(Mesh::Ptr mesh) { if (mesh->get_num_voxels() > 0) { const size_t vertex_per_voxel = mesh->get_vertex_per_voxel(); switch (vertex_per_voxel) { case 4: return Ptr(new TriangleElements(mesh)); default: std::stringstream err_msg; err_msg << "Voxel with " << vertex_per_voxel << " vertices is not supported yet."; throw NotImplementedError(err_msg.str()); } } else { const size_t vertex_per_face = mesh->get_vertex_per_face(); switch (vertex_per_face) { case 3: return Ptr(new EdgeElements(mesh)); default: std::stringstream err_msg; err_msg << "Face with " << vertex_per_face << " vertices is not supported yet."; throw NotImplementedError(err_msg.str()); } } }
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; }
void copy_faces(Mesh::Ptr mesh, std::unique_ptr<DracoMesh>& draco_mesh) { const auto num_faces = mesh->get_num_faces(); const auto& faces = mesh->get_faces(); for (int i = 0; i < num_faces; ++i) { draco_mesh->AddFace({{ draco::PointIndex(faces[i*3]), draco::PointIndex(faces[i*3+1]), draco::PointIndex(faces[i*3+2]) }}); } }
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; }
void InflatorEngine::save_mesh(const std::string& filename, const MatrixFr& vertices, const MatrixIr& faces, VectorF debug) { VectorF flattened_vertices(vertices.rows() * vertices.cols()); std::copy(vertices.data(), vertices.data() + vertices.rows() * vertices.cols(), flattened_vertices.data()); VectorI flattened_faces(faces.rows() * faces.cols()); std::copy(faces.data(), faces.data() + faces.rows() * faces.cols(), flattened_faces.data()); VectorI voxels = VectorI::Zero(0); Mesh::Ptr mesh = MeshFactory().load_data( flattened_vertices, flattened_faces, voxels, vertices.cols(), faces.cols(), 0).create_shared(); mesh->add_attribute("debug"); mesh->set_attribute("debug", debug); MeshWriter::Ptr writer = MeshWriter::create(filename); writer->with_attribute("debug"); writer->write_mesh(*mesh); }
std::unique_ptr<draco::PointCloud> to_draco_point_cloud(Mesh::Ptr mesh, bool with_attributes=true) { std::unique_ptr<draco::PointCloud> draco_mesh(new draco::PointCloud()); assert(mesh->get_num_faces() == 0); copy_vertices(mesh, draco_mesh); if (with_attributes) { copy_vertex_attributes(mesh, draco_mesh); } return draco_mesh; }
GeoMeshPtr GeogramMeshUtils::mesh_to_geomesh(const Mesh::Ptr mesh) { const size_t dim = mesh->get_dim(); const size_t vertex_per_face = mesh->get_vertex_per_face(); const size_t num_vertices = mesh->get_num_vertices(); const size_t num_faces = mesh->get_num_faces(); const auto& vertices = mesh->get_vertices(); const auto& faces = mesh->get_faces(); if (vertex_per_face != 3) { throw NotImplementedError("Converting non-triangle mesh to " "Geogram mesh is not yet implemented"); } auto geo_mesh = std::make_shared<GeoMesh>(dim, false); geo_mesh->vertices.clear(); geo_mesh->vertices.create_vertices(num_vertices); geo_mesh->facets.clear(); geo_mesh->facets.create_triangles(num_faces); for (size_t i=0; i<num_vertices; i++) { auto& p = geo_mesh->vertices.point(i); for (size_t j=0; j<dim; j++) { p[j] = vertices[i*dim+j]; } } for (size_t i=0; i<num_faces; i++) { geo_mesh->facets.set_vertex(i, 0, faces[i*3]); geo_mesh->facets.set_vertex(i, 1, faces[i*3+1]); geo_mesh->facets.set_vertex(i, 2, faces[i*3+2]); } return geo_mesh; }
void copy_vertices(Mesh::Ptr mesh, std::unique_ptr<DracoMesh>& draco_mesh) { const auto dim = mesh->get_dim(); const auto num_vertices = mesh->get_num_vertices(); draco_mesh->set_num_points(num_vertices); draco::GeometryAttribute positions; positions.Init(draco::GeometryAttribute::POSITION, // Attribute type nullptr, // data buffer dim, // number of components draco::DT_FLOAT64, // data type false, // normalized sizeof(Float) * dim, // byte stride 0); // byte offset auto pos_att_id = draco_mesh->AddAttribute( positions, // attribute object true, // identity mapping num_vertices); // num attribute values for (int i = 0; i < num_vertices; ++i) { draco_mesh->attribute(pos_att_id)->SetAttributeValue( draco::AttributeValueIndex(i), mesh->get_vertex(i).data()); } }
CellPartition::Ptr CellPartition::create(const Mesh::Ptr& mesh) { const MatrixFr vertices = MatrixUtils::reshape<MatrixFr>( mesh->get_vertices(), mesh->get_num_vertices(), mesh->get_dim()); const MatrixIr faces = MatrixUtils::reshape<MatrixIr>( mesh->get_faces(), mesh->get_num_faces(), mesh->get_vertex_per_face()); return CellPartition::Ptr(new CellPartition(vertices, faces)); }
void copy_metadata(std::unique_ptr<DracoMesh>& draco_mesh, Mesh::Ptr mesh) { const auto metadata = draco_mesh->GetMetadata(); if (metadata == nullptr) return; const auto& attr_metadatas = metadata->attribute_metadatas(); for (const auto& attr_metadata : attr_metadatas) { std::string name=""; attr_metadata->GetEntryString("name", &name); if (name == "") continue; auto uid = attr_metadata->att_unique_id(); const auto attr = draco_mesh->GetAttributeByUniqueId(uid); const auto num_rows = attr->size(); const auto num_cols = attr->num_components(); VectorF data(num_rows * num_cols); for (size_t i=0; i<num_rows; i++) { attr->ConvertValue(draco::AttributeValueIndex(i), data.data() + i*num_cols); } mesh->add_empty_attribute(name); mesh->set_attribute(name, data); } }
std::unique_ptr<draco::Mesh> to_draco_mesh(Mesh::Ptr mesh, bool with_attributes=true) { std::unique_ptr<draco::Mesh> draco_mesh(new draco::Mesh()); const size_t vertex_per_face = mesh->get_vertex_per_face(); if (vertex_per_face != 3) { throw NotImplementedError( "Draco encoding only supports triangle mesh."); } copy_vertices(mesh, draco_mesh); copy_faces(mesh, draco_mesh); if (with_attributes) { copy_vertex_attributes(mesh, draco_mesh); //copy_face_attributes(mesh, draco_mesh); } return draco_mesh; }
std::string DracoCompressionEngine::compress(Mesh::Ptr mesh) const { const size_t num_faces = mesh->get_num_faces(); draco::EncoderBuffer buffer; draco::Encoder encoder; if (num_faces > 0) { auto draco_mesh = DracoCompressionEngineHelper::to_draco_mesh(mesh); const auto status = encoder.EncodeMeshToBuffer(*draco_mesh, &buffer); if (!status.ok()) { throw RuntimeError("Draco encoding error!"); } return std::string(buffer.data(), buffer.size()); } else { auto draco_mesh = DracoCompressionEngineHelper::to_draco_point_cloud(mesh); const auto status = encoder.EncodePointCloudToBuffer(*draco_mesh, &buffer); if (!status.ok()) { throw RuntimeError("Draco encoding error!"); } return std::string(buffer.data(), buffer.size()); } }
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::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; }
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::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; }