Updates gles from sketchfab

* fixes bbox issue for animated scenes
* fixes geometry split (perf + line/point primitive management)
* removes degenerated faces
This commit is contained in:
Marc Helbling
2017-01-20 15:09:25 +01:00
parent 255a86cbe2
commit 7cd7618e8c
15 changed files with 608 additions and 214 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 <osg/Geometry>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/MorphGeometry>
#include "GeometryMapper"
#include "SubGeometry"
class GeometryCleaner : public GeometryMapper
{
public:
const GeometryList& process(osg::Geometry& geometry) {
_clean.clear();
if(dynamic_cast<osgAnimation::MorphGeometry*>(&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<osgAnimation::RigGeometry*>(&geometry)) {
// skipping rigged geometry for now
_clean.push_back(&geometry);
}
else {
osg::Vec3Array* positions = dynamic_cast<osg::Vec3Array*>(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<unsigned int> clean(const osg::Vec3Array& positions, osg::DrawElements* elements, const unsigned int size) {
std::vector<unsigned int> 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

View File

@@ -25,15 +25,81 @@
#include <osgAnimation/RigGeometry>
#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<osg::Geometry> > 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>
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<typename T>
void setBufferBoundingBox(T*) const;
void setValidIndices(std::set<unsigned int>&, const osg::DrawElements*) const;
public:
const unsigned int _maxAllowedIndex;

View File

@@ -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<unsigned int>::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<unsigned int>::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<unsigned int>::max(), std::numeric_limits<unsigned int>::max()));
cluster.addLine(line._a, line._b);
}
while(remaining_triangles && cluster_vertices.size() < _maxAllowedIndex) {
unsigned int candidate = std::numeric_limits<unsigned int>::max();
while(!cluster.full() && points.size()) {
unsigned int point = getNext(points, std::numeric_limits<unsigned int>::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<unsigned int>::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<unsigned int>::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<unsigned int>::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<unsigned int>::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<unsigned int>::max());
}
@@ -276,10 +221,3 @@ void GeometryIndexSplitter::setBufferBoundingBox(T* buffer) const {
buffer->setUserValue("ufr", ufr);
}
}
void GeometryIndexSplitter::setValidIndices(std::set<unsigned int>& indices, const osg::DrawElements* primitive) const {
for(unsigned int j = 0 ; j < primitive->getNumIndices() ; ++ j) {
indices.insert(primitive->index(j));
}
}

View File

@@ -7,6 +7,7 @@
#include <sstream>
#include "GeometryUniqueVisitor"
#include "glesUtil"
#include <osg/Geode>
#include <osgDB/ReaderWriter>
@@ -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<osg::Geometry> 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();

View File

@@ -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 <vector>
#include <osg/ref_ptr>
#include <osg/Geometry>
typedef std::vector< osg::ref_ptr<osg::Geometry> > 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

View File

@@ -16,26 +16,32 @@
#include <osg/Node>
#include <algorithm> //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) {

View File

@@ -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());

View File

@@ -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 <map>
#include <osg/ref_ptr>
#include <osg/Geometry>
#include <osgAnimation/RigGeometry>
#include "GeometryUniqueVisitor"
#include "GeometryMapper"
class RemapGeometryVisitor : public GeometryUniqueVisitor {
public:
typedef std::vector< osg::ref_ptr<osg::Geometry> > GeometryList;
typedef std::vector< osg::ref_ptr<osg::Drawable> > DrawableList;
typedef std::map<osg::Geometry*, GeometryList> 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<osg::Geometry*, GeometryList> _remap;
bool _exportNonGeometryDrawables;
};
#endif

View File

@@ -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<osgAnimation::RigGeometry*>(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<osg::Geometry*, GeometryList>(node, GeometryList(list)));
}

View File

@@ -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;
};

View File

@@ -1,26 +1,13 @@
#include "TangentSpaceVisitor"
#include "glesUtil"
void TangentSpaceVisitor::process(osgAnimation::MorphGeometry& morphGeometry) {
process(static_cast<osg::Geometry&>(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<osgUtil::TangentSpaceGenerator> 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();

View File

@@ -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);
}

View File

@@ -25,7 +25,7 @@ namespace glesUtil {
using namespace std;
using namespace osg;
typedef std::vector<unsigned int> 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<osg::Array*> ArrayList;
GeometryArrayGatherer(osg::Geometry& geometry) {
addGeometryVertexAttributes(geometry);
_targetAttributesIndex = _arrayList.size();
if(osgAnimation::MorphGeometry* morphGeometry = dynamic_cast<osgAnimation::MorphGeometry*>(&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<osgAnimation::MorphGeometry*>(&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);