diff --git a/src/celmodel/mesh.cpp b/src/celmodel/mesh.cpp index a89d23ed4..579f7cc00 100644 --- a/src/celmodel/mesh.cpp +++ b/src/celmodel/mesh.cpp @@ -40,6 +40,13 @@ VertexDescription appendingAttributes(const VertexDescription& desc, It begin, I return VertexDescription(std::move(allAttributes)); } +bool +isOpaqueMaterial(const Material &material) +{ + return (!(material.opacity > 0.01f && material.opacity < 1.0f)) && + material.blend != BlendMode::AdditiveBlend; +} + } // end unnamed namespace @@ -322,13 +329,7 @@ Mesh::addGroup(PrimitiveGroupType prim, { PrimitiveGroup g; if (prim == PrimitiveGroupType::LineStrip || prim == PrimitiveGroupType::LineList) - { g = createLinePrimitiveGroup(prim == PrimitiveGroupType::LineStrip, indices); - } - else - { - g.primOverride = prim; - } g.indices = std::move(indices); g.prim = prim; @@ -721,4 +722,51 @@ Mesh::getPrimitiveCount() const return count; } +void +Mesh::merge(const Mesh &other) +{ + auto &ti = groups.front().indices; + const auto &oi = other.groups.front().indices; + + ti.reserve(ti.size() + oi.size()); + for (auto i : oi) + ti.push_back(i + nVertices); + + vertices.reserve(vertices.size() + other.vertices.size()); + vertices.insert(vertices.end(), other.vertices.begin(), other.vertices.end()); + + nVertices += other.nVertices; +} + +bool +Mesh::canMerge(const Mesh &other, const std::vector &materials) const +{ + if (getGroupCount() != 1 || other.getGroupCount() != 1) + return false; + + const auto &tg = groups.front(); + const auto &og = other.groups.front(); + + if (tg.vertexCountOverride != 0 || og.vertexCountOverride != 0 || tg.prim != PrimitiveGroupType::TriList) + return false; + + if (std::tie(tg.materialIndex, tg.prim, vertexDesc.strideBytes) != + std::tie(og.materialIndex, og.prim, other.vertexDesc.strideBytes)) + return false; + + if (!isOpaqueMaterial(materials[tg.materialIndex]) || !isOpaqueMaterial(materials[og.materialIndex])) + return false; + + for (auto i = VertexAttributeSemantic::Position; + i < VertexAttributeSemantic::SemanticMax; + i = static_cast(1 + static_cast(i))) + { + auto &ta = vertexDesc.getAttribute(i); + auto &oa = other.vertexDesc.getAttribute(i); + if (ta.format != oa.format || ta.offsetWords != oa.offsetWords) + return false; + } + + return true; +} } // end namespace cmod diff --git a/src/celmodel/mesh.h b/src/celmodel/mesh.h index fc6bcb58d..68dbb2945 100644 --- a/src/celmodel/mesh.h +++ b/src/celmodel/mesh.h @@ -237,6 +237,9 @@ class Mesh unsigned int getVertexStrideWords() const { return vertexDesc.strideBytes / sizeof(cmod::VWord); } unsigned int getPrimitiveCount() const; + void merge(const Mesh&); + bool canMerge(const Mesh&, const std::vector &materials) const; + private: PrimitiveGroup createLinePrimitiveGroup(bool lineStrip, const std::vector& indices); void mergePrimitiveGroups(); diff --git a/src/celmodel/model.cpp b/src/celmodel/model.cpp index ce3f77b66..def26e852 100644 --- a/src/celmodel/model.cpp +++ b/src/celmodel/model.cpp @@ -15,8 +15,11 @@ #include +#include + #include "model.h" +using celestia::util::GetLogger; namespace cmod { @@ -335,6 +338,22 @@ Model::sortMeshes(const MeshComparator& comparator) // Sort the meshes so that completely opaque ones are first std::sort(meshes.begin(), meshes.end(), std::ref(comparator)); + + std::vector newMeshes; + newMeshes.push_back(meshes[0].clone()); + + for (size_t i = 1; i < meshes.size(); i++) + { + auto &p = newMeshes.back(); + if (!p.canMerge(meshes[i], materials)) + { + newMeshes.push_back(meshes[i].clone()); + continue; + } + p.merge(meshes[i]); + } + GetLogger()->info("Merged similar meshes: {} -> {}.\n", meshes.size(), newMeshes.size()); + meshes = std::move(newMeshes); } } // end namespace cmod