From 7cd7618e8c41e4bbb9497ef433e9d09b299b2d96 Mon Sep 17 00:00:00 2001 From: Marc Helbling Date: Fri, 20 Jan 2017 15:09:25 +0100 Subject: [PATCH] Updates gles from sketchfab * fixes bbox issue for animated scenes * fixes geometry split (perf + line/point primitive management) * removes degenerated faces --- src/osgPlugins/gles/AABBonBoneVisitor.cpp | 2 +- src/osgPlugins/gles/CMakeLists.txt | 6 +- src/osgPlugins/gles/GeometryCleaner | 138 +++++++++++ src/osgPlugins/gles/GeometryIndexSplitter | 92 +++++++- src/osgPlugins/gles/GeometryIndexSplitter.cpp | 222 +++++++----------- src/osgPlugins/gles/GeometryInspector | 4 +- src/osgPlugins/gles/GeometryMapper | 31 +++ src/osgPlugins/gles/OpenGLESGeometryOptimizer | 33 ++- .../gles/OpenGLESGeometryOptimizer.cpp | 6 + src/osgPlugins/gles/RemapGeometryVisitor | 51 ++++ src/osgPlugins/gles/RemapGeometryVisitor.cpp | 72 ++++++ src/osgPlugins/gles/SmoothNormalVisitor | 38 ++- src/osgPlugins/gles/TangentSpaceVisitor.cpp | 35 +-- src/osgPlugins/gles/TriangleMeshSmoother.cpp | 16 +- src/osgPlugins/gles/glesUtil | 76 +++++- 15 files changed, 608 insertions(+), 214 deletions(-) create mode 100644 src/osgPlugins/gles/GeometryCleaner create mode 100644 src/osgPlugins/gles/GeometryMapper create mode 100644 src/osgPlugins/gles/RemapGeometryVisitor create mode 100644 src/osgPlugins/gles/RemapGeometryVisitor.cpp diff --git a/src/osgPlugins/gles/AABBonBoneVisitor.cpp b/src/osgPlugins/gles/AABBonBoneVisitor.cpp index 14ff98f77..0ea5302f7 100644 --- a/src/osgPlugins/gles/AABBonBoneVisitor.cpp +++ b/src/osgPlugins/gles/AABBonBoneVisitor.cpp @@ -62,7 +62,7 @@ void ComputeAABBOnBoneVisitor::computeBoundingBoxOnBones() { } // Compare initial and actual boundingBox if (no change) => no box on bone - if(bb == osg::BoundingBox() || bb._min.x() == bb._max.x() || bb._min.y() == bb._max.y() || bb._min.z() == bb._max.z()) { + if(bb == osg::BoundingBox() || (bb._min.x() == bb._max.x() && bb._min.y() == bb._max.y() && bb._min.z() == bb._max.z())) { continue; } diff --git a/src/osgPlugins/gles/CMakeLists.txt b/src/osgPlugins/gles/CMakeLists.txt index 2f0e70829..559b460f0 100644 --- a/src/osgPlugins/gles/CMakeLists.txt +++ b/src/osgPlugins/gles/CMakeLists.txt @@ -5,9 +5,9 @@ SET(TARGET_SRC BindPerVertexVisitor.cpp DetachPrimitiveVisitor.cpp GeometryIndexSplitter.cpp - GeometrySplitterVisitor.cpp SubGeometry.cpp OpenGLESGeometryOptimizer.cpp + RemapGeometryVisitor.cpp RigAnimationVisitor.cpp RigAttributesVisitor.cpp TriangleMeshSmoother.cpp @@ -25,9 +25,11 @@ SET(TARGET_H DrawArrayVisitor EdgeIndexFunctor GeometryArray + GeometryCleaner GeometryIndexSplitter GeometryInspector - GeometrySplitterVisitor + GeometryMapper + RemapGeometryVisitor GeometryUniqueVisitor glesUtil IndexMeshVisitor diff --git a/src/osgPlugins/gles/GeometryCleaner b/src/osgPlugins/gles/GeometryCleaner new file mode 100644 index 000000000..41310281a --- /dev/null +++ b/src/osgPlugins/gles/GeometryCleaner @@ -0,0 +1,138 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * +*/ + +#ifndef GEOMETRY_CLEANER +#define GEOMETRY_CLEANER + +#include +#include +#include + +#include "GeometryMapper" +#include "SubGeometry" + + +class GeometryCleaner : public GeometryMapper +{ +public: + + const GeometryList& process(osg::Geometry& geometry) { + _clean.clear(); + + if(dynamic_cast(&geometry)) { + // a morph geometry may have a degenerated primitive in one target but not all targets + // let's leave them unmodified + _clean.push_back(&geometry); + } + else if(dynamic_cast(&geometry)) { + // skipping rigged geometry for now + _clean.push_back(&geometry); + } + else { + osg::Vec3Array* positions = dynamic_cast(geometry.getVertexArray()); + SubGeometry cleaned(geometry, + clean(*positions, getTriangles(geometry), 3), + clean(*positions, getLines(geometry), 2), + clean(*positions, getWireframe(geometry), 2), + clean(*positions, getPoints(geometry), 1)); + _clean.push_back(cleaned.geometry()); + } + + return _clean; + } + + osg::DrawElements* getTriangles(osg::Geometry& geometry) { + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements(); + if(primitive && primitive->getMode() == osg::PrimitiveSet::TRIANGLES) { + return primitive; + } + } + return 0; + } + + osg::DrawElements* getLines(osg::Geometry& geometry) { + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements(); + if(primitive && primitive->getMode() == osg::PrimitiveSet::LINES) { + bool wireframe(false); + if(!primitive->getUserValue("wireframe", wireframe) || !wireframe) + return primitive; + } + } + return 0; + } + + osg::DrawElements* getWireframe(osg::Geometry& geometry) { + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements(); + if(primitive && primitive->getMode() == osg::PrimitiveSet::LINES) { + bool wireframe(false); + if(primitive->getUserValue("wireframe", wireframe) && wireframe) + return primitive; + } + } + return 0; + } + + osg::DrawElements* getPoints(osg::Geometry& geometry) { + for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { + osg::DrawElements* primitive = geometry.getPrimitiveSet(i)->getDrawElements(); + if(primitive && primitive->getMode() == osg::PrimitiveSet::POINTS) { + return primitive; + } + } + return 0; + } + + std::vector clean(const osg::Vec3Array& positions, osg::DrawElements* elements, const unsigned int size) { + std::vector indices; + + if(!elements) return indices; + + for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += size) { + if(size == 3) { + unsigned int v1 = elements->index(i), + v2 = elements->index(i + 1), + v3 = elements->index(i + 2); + osg::Vec3 p1 = positions[v1], + p2 = positions[v2], + p3 = positions[v3]; + + osg::Vec3f cross = (p2 - p1) ^ (p3 - p1); + if(cross.length()) { + indices.push_back(v1); + indices.push_back(v2); + indices.push_back(v3); + } + } + else if (size == 2) { + unsigned int v1 = elements->index(i), + v2 = elements->index(i + 1); + if(!(positions[v1] == positions[v2])) { + indices.push_back(v1); + indices.push_back(v2); + } + } + else { + indices.push_back(elements->index(i)); + } + } + + return indices; + } + + + GeometryList _clean; +}; + +#endif diff --git a/src/osgPlugins/gles/GeometryIndexSplitter b/src/osgPlugins/gles/GeometryIndexSplitter index 0ce5d77a0..f43c7e8a9 100644 --- a/src/osgPlugins/gles/GeometryIndexSplitter +++ b/src/osgPlugins/gles/GeometryIndexSplitter @@ -25,15 +25,81 @@ #include #include "glesUtil" +#include "GeometryMapper" #include "GeometryArray" #include "TriangleMeshGraph" #include "SubGeometry" #include "Line" -class GeometryIndexSplitter +class GeometryIndexSplitter : public GeometryMapper { protected: + class Cluster { + public: + Cluster(unsigned int maxAllowedIndex): + maxIndex(maxAllowedIndex) + { + } + + bool full() const { + return subvertices.size() >= maxIndex; + } + + bool fullOfTriangles() const { + // consider worse case i.e. no triangle vertex is already in the cluster + // so need room for 3 indices + return subvertices.size() + 3 >= maxIndex; + } + + bool fullOfLines() const { + return subvertices.size() + 2 >= maxIndex; + } + + bool contains(unsigned int v1, unsigned int v2) const { + return contains(v1) && contains(v2); + } + + bool contains(unsigned int v1) const { + return subvertices.count(v1); + } + + void addTriangle(unsigned int v1, unsigned int v2, unsigned v3) { + subtriangles.push_back(v1); + subtriangles.push_back(v2); + subtriangles.push_back(v3); + + subvertices.insert(v1); + subvertices.insert(v2); + subvertices.insert(v3); + } + + void addLine(unsigned int v1, unsigned int v2) { + sublines.push_back(v1); + sublines.push_back(v2); + + subvertices.insert(v1); + subvertices.insert(v2); + } + + void addPoint(unsigned int v1) { + subpoints.push_back(v1); + + subvertices.insert(v1); + } + + void addWire(unsigned int v1, unsigned int v2) { + subwireframe.push_back(v1); + subwireframe.push_back(v2); + } + + public: + const unsigned int maxIndex; + IndexVector subtriangles, subwireframe, sublines, subpoints; + IndexSet subvertices; + }; + + class IndexCache : public IndexDeque { public: IndexCache(unsigned int size=64) : _size(size) @@ -48,18 +114,29 @@ protected: }; public: - typedef std::vector< osg::ref_ptr > GeometryList; - GeometryIndexSplitter(unsigned int maxAllowedIndex): _maxAllowedIndex(maxAllowedIndex) {} + const GeometryList& process(osg::Geometry& geometry) { + _geometryList.clear(); + split(geometry); + return _geometryList; + } + bool split(osg::Geometry&); - unsigned int findCandidate(const IndexVector&); - unsigned int findCandidate(const IndexVector&, const IndexVector&); - void setTriangleCluster(const TriangleMeshGraph&, unsigned int, unsigned int, IndexVector&, IndexSet&, unsigned int&); - void extract_primitives(const IndexSet&, const osg::DrawElements*, IndexVector&, unsigned int); + template + typename C::value_type getNext(C& primitives, typename C::value_type default_value) { + if(primitives.empty()) { + return default_value; + } + typename C::value_type next = *primitives.begin(); + primitives.erase(primitives.begin()); + return next; + } + + unsigned int findCandidate(IndexSet&, const IndexCache&, const TriangleMeshGraph&); protected: bool needToSplit(const osg::Geometry&) const; @@ -68,7 +145,6 @@ protected: template void setBufferBoundingBox(T*) const; - void setValidIndices(std::set&, const osg::DrawElements*) const; public: const unsigned int _maxAllowedIndex; diff --git a/src/osgPlugins/gles/GeometryIndexSplitter.cpp b/src/osgPlugins/gles/GeometryIndexSplitter.cpp index 5c3cecd1d..d2452ff0b 100644 --- a/src/osgPlugins/gles/GeometryIndexSplitter.cpp +++ b/src/osgPlugins/gles/GeometryIndexSplitter.cpp @@ -11,8 +11,8 @@ bool GeometryIndexSplitter::split(osg::Geometry& geometry) { attachBufferBoundingBox(geometry); osg::DrawElements *wire_primitive = 0, - *line_primitive = 0, - *point_primitive = 0; + *line_primitive = 0, + *point_primitive = 0; for(unsigned int i = 0 ; i < geometry.getNumPrimitiveSets() ; ++ i) { osg::DrawElements* primitive = (geometry.getPrimitiveSet(i) ? geometry.getPrimitiveSet(i)->getDrawElements() : 0); if(primitive) { @@ -31,29 +31,34 @@ bool GeometryIndexSplitter::split(osg::Geometry& geometry) { } } + TriangleMeshGraph graph(geometry, false); + // only wireframe can be processed directly as they simply "duplicate" triangle or edge data; // lines/points may reference points not used for triangles so we keep a set of primitives // that remain to process - LineSet source_lines; - IndexSet source_points; + IndexSet triangles; + LineSet lines, wires; + IndexSet points; + for(unsigned int i = 0 ; i < graph.getNumTriangles() ; ++ i) { + triangles.insert(i); + } if(line_primitive) { for(unsigned int i = 0 ; i < line_primitive->getNumIndices() ; i += 2) { - source_lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1))); + lines.insert(Line(line_primitive->index(i), line_primitive->index(i + 1))); + } + } + if(wire_primitive) { + for(unsigned int i = 0 ; i < wire_primitive->getNumIndices() ; i += 2) { + wires.insert(Line(wire_primitive->index(i), wire_primitive->index(i + 1))); } } if(point_primitive) { for(unsigned int i = 0 ; i < point_primitive->getNumIndices() ; ++ i) { - source_points.insert(point_primitive->index(i)); + points.insert(point_primitive->index(i)); } } - TriangleMeshGraph graph(geometry, false); - unsigned int remaining_triangles = graph.getNumTriangles(), - cluster = 0; - IndexVector clusters(remaining_triangles, 0); - IndexCache cache; - // assign a cluster id for each triangle // 1. bootstrap cluster by selecting first remaining triangle // 2. while cluster size is < max cluster size @@ -64,95 +69,75 @@ bool GeometryIndexSplitter::split(osg::Geometry& geometry) { // 3. insert wireframe edges corresponding to selected triangles // 4. extract subgeometry - while(remaining_triangles || !source_lines.empty() || !source_points.empty()) { - IndexVector subtriangles, subwireframe, sublines, subpoints; - IndexSet cluster_vertices; + while(triangles.size() || lines.size() || points.size()) { + Cluster cluster(_maxAllowedIndex); + IndexCache cache; + unsigned int candidate = std::numeric_limits::max(); - ++ cluster; + // let's consider that every insert needs the place for a full new primitive for simplicity + while(!cluster.fullOfTriangles() && + (candidate = findCandidate(triangles, cache, graph)) != std::numeric_limits::max()) { + cache.push_back(candidate); + Triangle t = graph.triangle(candidate); + cluster.addTriangle(t.v1(), t.v2(), t.v3()); + } - if(remaining_triangles) { - // find first unmarked triangle (as remaining_triangles > 0 there *must* be at least one) - cache.push_back(findCandidate(clusters)); - setTriangleCluster(graph, cache.back(), cluster, clusters, cluster_vertices, remaining_triangles); + while(!cluster.fullOfLines() && lines.size()) { + Line line = getNext(lines, Line(std::numeric_limits::max(), std::numeric_limits::max())); + cluster.addLine(line._a, line._b); + } - while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) { - unsigned int candidate = std::numeric_limits::max(); + while(!cluster.full() && points.size()) { + unsigned int point = getNext(points, std::numeric_limits::max()); + cluster.addPoint(point); + } - for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) { - candidate = findCandidate(graph.triangleNeighbors(*cached), clusters); - if(candidate != std::numeric_limits::max()) break; + // update lines/points: if all vertices referenced by a point/line primitive are + // already extracted, let's insert it in the subgeometry and update the set of + // primitives still remaining Lines may e.g. reference one vertex in cluster A and + // the other in cluster B hence need specific care + if(line_primitive) { + for(LineSet::iterator line = lines.begin() ; line != lines.end() ; ) { + if(cluster.contains(line->_a, line->_b)) { + cluster.addLine(line->_a, line->_b); + lines.erase(line ++); } - - if(candidate == std::numeric_limits::max()) { - // do we have room for a triangle having all vertices not in the cluster? - if(!(cluster_vertices.size() + 2 < _maxAllowedIndex)) { - break; - } - - candidate = findCandidate(clusters); - } - - cache.push_back(candidate); - setTriangleCluster(graph, candidate, cluster, clusters, cluster_vertices, remaining_triangles); - } - - // build list of cluster triangles - for(unsigned int triangle = 0 ; triangle < clusters.size() ; ++ triangle) { - if(clusters[triangle] == cluster) { - const Triangle& t = graph.triangle(triangle); - subtriangles.push_back(t.v1()); - subtriangles.push_back(t.v2()); - subtriangles.push_back(t.v3()); - } - } - - // update lines/points: if all vertices referenced by a point/line primitive are - // already extracted, let's insert it in the subgeometry and update the set of - // primitives still remaining Lines may e.g. reference one vertex in cluster A and - // the other in cluster B hence need specific care - if(line_primitive) { - extract_primitives(cluster_vertices, line_primitive, sublines, 2); - for(unsigned int i = 0 ; i < sublines.size() / 2 ; i += 2) { - source_lines.erase(Line(sublines[i], sublines[i + 1])); - } - } - - if(point_primitive) { - extract_primitives(cluster_vertices, point_primitive, subpoints, 1); - for(unsigned int i = 0 ; i < subpoints.size() ; ++ i) { - source_points.erase(subpoints[i]); + else { + ++ line; } } } - // let's consider that every new lines adds 2 vertices for simplicity - while(!source_lines.empty() && cluster_vertices.size() - 1 < _maxAllowedIndex) { - Line line = *source_lines.begin(); - source_lines.erase(source_lines.begin()); - cluster_vertices.insert(line._a); - cluster_vertices.insert(line._b); - sublines.push_back(line._a); - sublines.push_back(line._b); - } - - while(!source_points.empty() && cluster_vertices.size() < _maxAllowedIndex) { - unsigned int point = *source_points.begin(); - source_points.erase(source_points.begin()); - cluster_vertices.insert(point); - subpoints.push_back(point); + if(point_primitive) { + // find all cluster vertices that should also have a point primitive + for(IndexSet::iterator subvertex = cluster.subvertices.begin() ; subvertex != cluster.subvertices.end() ; ++ subvertex) { + unsigned int index = *subvertex; + if(points.find(index) != points.end()) { + cluster.addPoint(index); + points.erase(index); + } + } } // finally extract wireframe (may originate from triangles or lines but necessarily have // to reference vertices that are *all* in the geometry) if(wire_primitive) { - extract_primitives(cluster_vertices, wire_primitive, subwireframe, 2); + for(LineSet::iterator wire = wires.begin() ; wire != wires.end() ; ) { + if(cluster.contains(wire->_a, wire->_b)) { + cluster.addWire(wire->_a, wire->_b); + wires.erase(wire ++); + } + else { + ++ wire; + } + } } _geometryList.push_back(SubGeometry(geometry, - subtriangles, - sublines, - subwireframe, - subpoints).geometry()); + cluster.subtriangles, + cluster.sublines, + cluster.subwireframe, + cluster.subpoints).geometry()); } osg::notify(osg::NOTICE) << "geometry " << &geometry << " " << geometry.getName() @@ -164,60 +149,20 @@ bool GeometryIndexSplitter::split(osg::Geometry& geometry) { } -unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& clusters) { - for(unsigned int i = 0 ; i < clusters.size() ; ++ i) { - if(!clusters[i]) { - return i; - } - } - return std::numeric_limits::max(); -} - - -unsigned int GeometryIndexSplitter::findCandidate(const IndexVector& candidates, const IndexVector& clusters) { - for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) { - if(!clusters[*candidate]) { - return *candidate; - } - } - return std::numeric_limits::max(); -} - - -void GeometryIndexSplitter::setTriangleCluster(const TriangleMeshGraph& graph, - unsigned int triangle, - unsigned int cluster, - IndexVector& clusters, - IndexSet& cluster_vertices, - unsigned int& remaining) { - clusters[triangle] = cluster; - const Triangle& t = graph.triangle(triangle); - cluster_vertices.insert(t.v1()); - cluster_vertices.insert(t.v2()); - cluster_vertices.insert(t.v3()); - remaining --; -} - - -void GeometryIndexSplitter::extract_primitives(const IndexSet& vertices, - const osg::DrawElements* elements, - IndexVector& indices, - unsigned int primitive_size) { - for(unsigned int i = 0 ; i < elements->getNumIndices() ; i += primitive_size) { - bool is_included = true; - for(unsigned int j = 0 ; j < primitive_size ; ++ j) { - if(!vertices.count(elements->index(i + j))) { - is_included = false; - break; - } - } - - if(is_included) { - for(unsigned int j = 0 ; j < primitive_size ; ++ j) { - indices.push_back(elements->index(i + j)); +unsigned int GeometryIndexSplitter::findCandidate(IndexSet& triangles, const IndexCache& cache, const TriangleMeshGraph& graph) { + // look for unclustered neighboring triangles + for(IndexCache::const_reverse_iterator cached = cache.rbegin() ; cached != cache.rend() ; ++ cached) { + IndexVector candidates = graph.triangleNeighbors(*cached); + for(IndexVector::const_iterator candidate = candidates.begin() ; candidate != candidates.end() ; ++ candidate) { + if(triangles.count(*candidate)) { + triangles.erase(*candidate); + return *candidate; } } } + + //fallback on any unclustered triangle + return getNext(triangles, std::numeric_limits::max()); } @@ -276,10 +221,3 @@ void GeometryIndexSplitter::setBufferBoundingBox(T* buffer) const { buffer->setUserValue("ufr", ufr); } } - - -void GeometryIndexSplitter::setValidIndices(std::set& indices, const osg::DrawElements* primitive) const { - for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) { - indices.insert(primitive->index(j)); - } -} diff --git a/src/osgPlugins/gles/GeometryInspector b/src/osgPlugins/gles/GeometryInspector index beea59194..cbd9a248e 100644 --- a/src/osgPlugins/gles/GeometryInspector +++ b/src/osgPlugins/gles/GeometryInspector @@ -7,6 +7,7 @@ #include #include "GeometryUniqueVisitor" +#include "glesUtil" #include #include @@ -34,8 +35,7 @@ public: osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); unsigned int count = 0; for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target, ++ count) { - osg::ref_ptr geometry = new osg::Geometry(*target->getGeometry()); - geometry->setPrimitiveSetList(morphGeometry.getPrimitiveSetList()); + glesUtil::TargetGeometry geometry(*target, morphGeometry); std::ostringstream oss; if(geometry->getName().empty()) { oss << "/tmp/noname_" << _processed.size(); diff --git a/src/osgPlugins/gles/GeometryMapper b/src/osgPlugins/gles/GeometryMapper new file mode 100644 index 000000000..4bf8674ea --- /dev/null +++ b/src/osgPlugins/gles/GeometryMapper @@ -0,0 +1,31 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * +*/ + +#ifndef GEOMETRY_MAPPER +#define GEOMETRY_MAPPER + +#include + +#include +#include + +typedef std::vector< osg::ref_ptr > GeometryList; + +class GeometryMapper { +public: + + // map one geometry to a list of geometries + // to be used with RemapGeometryVisitor + virtual const GeometryList& process(osg::Geometry&) = 0; +}; + +#endif diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer b/src/osgPlugins/gles/OpenGLESGeometryOptimizer index 751a9f2bd..c8bdf58fc 100644 --- a/src/osgPlugins/gles/OpenGLESGeometryOptimizer +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer @@ -16,26 +16,32 @@ #include #include //std::max -#include "AnimationCleanerVisitor" -#include "RigAnimationVisitor" +//animation: #include "AABBonBoneVisitor" +#include "AnimationCleanerVisitor" #include "DisableAnimationVisitor" +#include "LimitMorphTargetCount" +#include "MostInfluencedGeometryByBone" +#include "RigAnimationVisitor" +#include "RigAttributesVisitor" + +// geometry: #include "BindPerVertexVisitor" #include "DetachPrimitiveVisitor" #include "DrawArrayVisitor" -#include "GeometrySplitterVisitor" #include "IndexMeshVisitor" #include "PreTransformVisitor" +#include "RemapGeometryVisitor" #include "SmoothNormalVisitor" #include "TangentSpaceVisitor" #include "TriangleStripVisitor" #include "UnIndexMeshVisitor" #include "WireframeVisitor" -#include "AABBonBoneVisitor" -#include "AnimationCleanerVisitor" -#include "MostInfluencedGeometryByBone" -#include "LimitMorphTargetCount" -#include "RigAttributesVisitor" + +#include "GeometryIndexSplitter" +#include "GeometryCleaner" + +// debug #include "GeometryInspector" @@ -165,6 +171,12 @@ protected: node->accept(indexer); } + void makeCleanGeometry(osg::Node* node) { + GeometryCleaner cleaner; + RemapGeometryVisitor remapper(cleaner, _exportNonGeometryDrawables); + node->accept(remapper); + } + void makeSmoothNormal(osg::Node* node) { SmoothNormalVisitor smoother(osg::PI / 4.f, true); node->accept(smoother); @@ -176,8 +188,9 @@ protected: } void makeSplit(osg::Node* node) { - GeometrySplitterVisitor splitter(_maxIndexValue, _exportNonGeometryDrawables ); - node->accept(splitter); + GeometryIndexSplitter splitter(_maxIndexValue); + RemapGeometryVisitor remapper(splitter, _exportNonGeometryDrawables); + node->accept(remapper); } void makeTriStrip(osg::Node* node) { diff --git a/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp index 5c10f69fb..82b0a2ca1 100644 --- a/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp +++ b/src/osgPlugins/gles/OpenGLESGeometryOptimizer.cpp @@ -26,6 +26,12 @@ osg::Node* OpenGLESGeometryOptimizer::optimize(osg::Node& node) { // index (merge exact duplicates + uses simple triangles & lines i.e. no strip/fan/loop) makeIndexMesh(model.get()); + // clean (remove degenerated data) + std::string authoringTool; + if(model->getUserValue("authoring_tool", authoringTool) && authoringTool == "Tilt Brush") { + makeCleanGeometry(model.get()); + } + // smooth vertex normals (if geometry has no normal compute smooth normals) makeSmoothNormal(model.get()); diff --git a/src/osgPlugins/gles/RemapGeometryVisitor b/src/osgPlugins/gles/RemapGeometryVisitor new file mode 100644 index 000000000..0a803b160 --- /dev/null +++ b/src/osgPlugins/gles/RemapGeometryVisitor @@ -0,0 +1,51 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * +*/ + +#ifndef REMAP_GEOMETRY_VISITOR +#define REMAP_GEOMETRY_VISITOR + +#include + +#include +#include +#include + +#include "GeometryUniqueVisitor" +#include "GeometryMapper" + + +class RemapGeometryVisitor : public GeometryUniqueVisitor { +public: + typedef std::vector< osg::ref_ptr > GeometryList; + typedef std::vector< osg::ref_ptr > DrawableList; + typedef std::map GeometryMap; + + RemapGeometryVisitor(GeometryMapper& mapper, bool exportNonGeometryDrawables=false): + GeometryUniqueVisitor("RemapGeometryVisitor"), + _mapper(mapper), + _exportNonGeometryDrawables(exportNonGeometryDrawables) + {} + + void apply(osg::Geode&); + void process(osg::Geometry&); + +protected: + bool isProcessed(osg::Geometry*); + void setProcessed(osg::Geometry*, const GeometryList&); + +protected: + GeometryMapper& _mapper; + std::map _remap; + bool _exportNonGeometryDrawables; +}; + +#endif diff --git a/src/osgPlugins/gles/RemapGeometryVisitor.cpp b/src/osgPlugins/gles/RemapGeometryVisitor.cpp new file mode 100644 index 000000000..e389394a0 --- /dev/null +++ b/src/osgPlugins/gles/RemapGeometryVisitor.cpp @@ -0,0 +1,72 @@ +#include "RemapGeometryVisitor" +#include "glesUtil" + + +void RemapGeometryVisitor::apply(osg::Geode& geode) { + GeometryUniqueVisitor::apply(geode); + GeometryList remappedGeometries; + DrawableList nonGeometryDrawables; + + for(unsigned int i = 0 ; i < geode.getNumDrawables() ; ++ i) { + osg::Geometry* geometry = geode.getDrawable(i)->asGeometry(); + if(geometry) { + if(osgAnimation::RigGeometry* rigGeometry = dynamic_cast(geometry)) { + GeometryMap::iterator lookup = _remap.find(rigGeometry->getSourceGeometry()); + if(lookup != _remap.end() && !lookup->second.empty()) { + + // convert now static rigGeometry into simple Geometry + for(GeometryList::iterator mapped = lookup->second.begin() ; mapped != lookup->second.end() ; ++ mapped) { + if(glesUtil::hasPositiveWeights(mapped->get())) { + osgAnimation::RigGeometry* mappedRig = new osgAnimation::RigGeometry(*rigGeometry); + mappedRig->setSourceGeometry(mapped->get()); + remappedGeometries.push_back(mappedRig); + } + else { + remappedGeometries.push_back(mapped->get()); + } + } + + } + } + else { + GeometryMap::iterator lookup = _remap.find(geometry); + if(lookup != _remap.end() && !lookup->second.empty()) { + remappedGeometries.insert(remappedGeometries.end(), lookup->second.begin(), lookup->second.end()); + } + } + } + else { + nonGeometryDrawables.push_back(geode.getDrawable(i)); + } + } + + // remove all drawables + geode.removeDrawables(0, geode.getNumDrawables()); + // insert splitted geometries + for(unsigned int i = 0 ; i < remappedGeometries.size() ; ++ i) { + geode.addDrawable(remappedGeometries[i].get()); + } + + if(_exportNonGeometryDrawables) { + // insert other drawables (e.g. osgText) + for(unsigned int i = 0 ; i < nonGeometryDrawables.size() ; ++ i) { + geode.addDrawable(nonGeometryDrawables[i].get()); + } + } +} + + +void RemapGeometryVisitor::process(osg::Geometry& geometry) { + const GeometryList& mapped = _mapper.process(geometry); + setProcessed(&geometry, mapped); +} + + +bool RemapGeometryVisitor::isProcessed(osg::Geometry* node) { + return _remap.find(node) != _remap.end(); +} + + +void RemapGeometryVisitor::setProcessed(osg::Geometry* node, const GeometryList& list) { + _remap.insert(std::pair(node, GeometryList(list))); +} diff --git a/src/osgPlugins/gles/SmoothNormalVisitor b/src/osgPlugins/gles/SmoothNormalVisitor index 086c28239..50596c2a6 100644 --- a/src/osgPlugins/gles/SmoothNormalVisitor +++ b/src/osgPlugins/gles/SmoothNormalVisitor @@ -5,6 +5,7 @@ #include "GeometryUniqueVisitor" #include "TriangleMeshSmoother" +#include "glesUtil" class SmoothNormalVisitor : public GeometryUniqueVisitor { @@ -25,20 +26,41 @@ public: } void process(osgAnimation::MorphGeometry& morphGeometry) { - TriangleMeshSmoother(morphGeometry, 0, true, TriangleMeshSmoother::smooth_all); - osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); - for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { - // check normal orientation using the same primitives as parent geometry - osg::Geometry::PrimitiveSetList& primitives = target->getGeometry()->getPrimitiveSetList(); - target->getGeometry()->setPrimitiveSetList(morphGeometry.getPrimitiveSetList()); + bool needSmoothing = needMorphGeometrySmoothing(morphGeometry); - TriangleMeshSmoother(*target->getGeometry(), 0, true, TriangleMeshSmoother::smooth_all); + if(needSmoothing) { + TriangleMeshSmoother(morphGeometry, 0, true, TriangleMeshSmoother::smooth_all); - target->getGeometry()->setPrimitiveSetList(primitives); + osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { + // check normal orientation using the same primitives as parent geometry + glesUtil::TargetGeometry geometry(*target, morphGeometry); + if(geometry && !geometry->getNormalArray()) { + TriangleMeshSmoother(*geometry, 0, true, TriangleMeshSmoother::smooth_all); + } + } } } protected: + bool needMorphGeometrySmoothing(osgAnimation::MorphGeometry& morphGeometry) { + if(!morphGeometry.getNormalArray()) { + return true; + } + + osgAnimation::MorphGeometry::MorphTargetList targets = morphGeometry.getMorphTargetList(); + + for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { + osg::Geometry* geometry = target->getGeometry(); + if(geometry && !geometry->getNormalArray()) { + return true; + } + } + + return false; + } + + float _creaseAngle; bool _comparePosition; }; diff --git a/src/osgPlugins/gles/TangentSpaceVisitor.cpp b/src/osgPlugins/gles/TangentSpaceVisitor.cpp index 874d85657..aa7264a63 100644 --- a/src/osgPlugins/gles/TangentSpaceVisitor.cpp +++ b/src/osgPlugins/gles/TangentSpaceVisitor.cpp @@ -1,26 +1,13 @@ #include "TangentSpaceVisitor" - +#include "glesUtil" void TangentSpaceVisitor::process(osgAnimation::MorphGeometry& morphGeometry) { process(static_cast(morphGeometry)); osgAnimation::MorphGeometry::MorphTargetList& targets = morphGeometry.getMorphTargetList(); for(osgAnimation::MorphGeometry::MorphTargetList::iterator target = targets.begin() ; target != targets.end() ; ++ target) { - osg::Geometry* geometry = target->getGeometry(); - bool useParentMorphTexCoord = geometry->getTexCoordArrayList().empty(); - - if(useParentMorphTexCoord) { - // tangent space require tex coords; in case a target has no tex coords, we try to - // bind the parent geometry tex coords - geometry->setTexCoordArrayList(morphGeometry.getTexCoordArrayList()); - } - + glesUtil::TargetGeometry geometry(*target, morphGeometry); process(*geometry); - - if(useParentMorphTexCoord) { - // drop parent tex coords after tangent space computation - geometry->setTexCoordArrayList(osg::Geometry::ArrayList()); - } } } @@ -36,6 +23,9 @@ void TangentSpaceVisitor::process(osg::Geometry& geometry) { geometry.getVertexAttribArray(tangentIndex)->setUserValue("tangent", true); return; } + else { + OSG_WARN << "Anomaly: [TangentSpaceVisitor] Missing tangent array at specificied index." << std::endl; + } } if (!geometry.getTexCoordArray(_textureUnit)){ @@ -56,21 +46,6 @@ void TangentSpaceVisitor::process(osg::Geometry& geometry) { osg::ref_ptr generator = new osgUtil::TangentSpaceGenerator; generator->generate(&geometry, _textureUnit); - // keep original normal array - if (!geometry.getNormalArray()) { - if (generator->getNormalArray()) { - osg::Vec3Array* vec3Normals = new osg::Vec3Array(); - osg::Vec4Array* vec4Normals = generator->getNormalArray(); - for (unsigned int i = 0; i < vec4Normals->size(); i++) { - osg::Vec3 n = osg::Vec3((*vec4Normals)[i][0], - (*vec4Normals)[i][1], - (*vec4Normals)[i][2]); - vec3Normals->push_back(n); - } - geometry.setNormalArray(vec3Normals, osg::Array::BIND_PER_VERTEX); - } - } - if (generator->getTangentArray()) { osg::Vec4Array* normal = generator->getNormalArray(); osg::Vec4Array* tangent = generator->getTangentArray(); diff --git a/src/osgPlugins/gles/TriangleMeshSmoother.cpp b/src/osgPlugins/gles/TriangleMeshSmoother.cpp index 7215544f7..536844758 100644 --- a/src/osgPlugins/gles/TriangleMeshSmoother.cpp +++ b/src/osgPlugins/gles/TriangleMeshSmoother.cpp @@ -244,14 +244,16 @@ void TriangleMeshSmoother::updateGeometryPrimitives() { } } - osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); - for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) { - const Triangle& triangle = _graph->triangle(i); - triangles->push_back(triangle.v1()); - triangles->push_back(triangle.v2()); - triangles->push_back(triangle.v3()); + if(_graph->getNumTriangles()) { + osg::DrawElementsUInt* triangles = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES); + for(unsigned int i = 0 ; i < _graph->getNumTriangles() ; ++ i) { + const Triangle& triangle = _graph->triangle(i); + triangles->push_back(triangle.v1()); + triangles->push_back(triangle.v2()); + triangles->push_back(triangle.v3()); + } + primitives.push_back(triangles); } - primitives.push_back(triangles); _geometry.setPrimitiveSetList(primitives); } diff --git a/src/osgPlugins/gles/glesUtil b/src/osgPlugins/gles/glesUtil index bf6faffef..746232bc0 100644 --- a/src/osgPlugins/gles/glesUtil +++ b/src/osgPlugins/gles/glesUtil @@ -25,7 +25,7 @@ namespace glesUtil { using namespace std; using namespace osg; typedef std::vector IndexList; - + typedef osgAnimation::MorphGeometry::MorphTargetList MorphTargetList; inline bool hasPositiveWeights(const osg::Geometry* geometry) { const osg::Vec4Array* weights = 0; @@ -53,6 +53,52 @@ namespace glesUtil { } + class TargetGeometry { + public: + TargetGeometry(osgAnimation::MorphGeometry::MorphTarget& target, + osgAnimation::MorphGeometry& morph): + _geometry(0) + { + _geometry = target.getGeometry(); + _geometry->setPrimitiveSetList(morph.getPrimitiveSetList()); + + _hasTexCoord = _geometry->getTexCoordArrayList().size(); + if(!_hasTexCoord) { + _geometry->setTexCoordArrayList(morph.getTexCoordArrayList()); + } + } + + osg::Geometry* operator->() { + return _geometry; + } + + operator osg::Geometry*() { + return _geometry; + } + + operator osg::Geometry&() { + return *_geometry; + } + + operator bool() { + return _geometry != 0; + } + + ~TargetGeometry() { + if(!_hasTexCoord) { + // drop parent tex coords after tangent space computation + _geometry->setTexCoordArrayList(osg::Geometry::ArrayList()); + } + _geometry->setPrimitiveSetList(osg::Geometry::PrimitiveSetList()); + } + + protected: + osg::Geometry* _geometry; + bool _hasTexCoord; + }; + + + // A helper class that gathers up all the attribute arrays of an // osg::Geometry object that are BIND_PER_VERTEX and runs an // ArrayVisitor on them. @@ -61,6 +107,20 @@ namespace glesUtil { typedef std::vector ArrayList; GeometryArrayGatherer(osg::Geometry& geometry) { + addGeometryVertexAttributes(geometry); + _targetAttributesIndex = _arrayList.size(); + + if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast(&geometry)) { + MorphTargetList targets = morphGeometry->getMorphTargetList(); + for (MorphTargetList::iterator iterator = targets.begin(); iterator != targets.end(); ++ iterator) { + if(iterator->getGeometry()) { + addTargetVertexAttributes(*iterator->getGeometry()); + } + } + } + } + + void addGeometryVertexAttributes(osg::Geometry& geometry) { add(geometry.getVertexArray()); add(geometry.getNormalArray()); add(geometry.getColorArray()); @@ -74,6 +134,10 @@ namespace glesUtil { } } + void addTargetVertexAttributes(osg::Geometry& target) { + add(target.getVertexArray()); + } + void add(osg::Array* array) { if (array) { _arrayList.push_back(array); @@ -81,12 +145,16 @@ namespace glesUtil { } void accept(osg::ArrayVisitor& av) { - for(ArrayList::iterator itr = _arrayList.begin() ; itr != _arrayList.end(); ++ itr) { + unsigned int geometryAttributesIndex = 0; + for(ArrayList::iterator itr = _arrayList.begin() ; + geometryAttributesIndex < _targetAttributesIndex && itr != _arrayList.end(); + ++ geometryAttributesIndex, ++ itr) { (*itr)->accept(av); } } ArrayList _arrayList; + unsigned int _targetAttributesIndex; }; @@ -349,8 +417,8 @@ namespace glesUtil { void remapTargetVertices(Remapper remapper, Geometry& geom) { if(osgAnimation::MorphGeometry *morphGeometry = dynamic_cast(&geom)) { - osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList(); - for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) { + MorphTargetList targetList = morphGeometry->getMorphTargetList(); + for (MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) { osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti); osg::Geometry *target = morphTarget->getGeometry(); GeometryArrayGatherer gatherer(*target);