From d4289c5d542465791567af4eecc313b456622017 Mon Sep 17 00:00:00 2001 From: timoore Date: Tue, 25 Aug 2009 07:19:39 +0000 Subject: [PATCH] Change trees code to use a faster OpenGL path The (random) dimensions of a large number of trees is stored in an array shared by all the tree geodes. The coordinates of the origin of each tree are replicated in an another array. This allows an entire block of trees to be rendered with a few OpenGL calls, instead of one function call per tree. --- simgear/scene/material/Effect.cxx | 1 - simgear/scene/tgdb/ShaderGeometry.cxx | 7 +- simgear/scene/tgdb/ShaderGeometry.hxx | 4 +- simgear/scene/tgdb/TreeBin.cxx | 273 ++++++++++++++++---------- simgear/scene/tgdb/TreeBin.hxx | 9 +- simgear/scene/tgdb/obj.cxx | 7 +- 6 files changed, 178 insertions(+), 123 deletions(-) diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 4415fe35..f812f57a 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -763,4 +763,3 @@ osgDB::RegisterDotOsgWrapperProxy effectProxy ); } } - diff --git a/simgear/scene/tgdb/ShaderGeometry.cxx b/simgear/scene/tgdb/ShaderGeometry.cxx index 089beb27..36a30934 100644 --- a/simgear/scene/tgdb/ShaderGeometry.cxx +++ b/simgear/scene/tgdb/ShaderGeometry.cxx @@ -32,14 +32,15 @@ using namespace osgDB; namespace simgear { -void ShaderGeometry::addTree(const TreeBin::Tree& t) +void ShaderGeometry::addObject(const Vec3& position, float scale, + int texture_index) { if (!_posScaleArray.valid()) { _posScaleArray = new Vec4Array(); _vertexAttribArray = new FloatArray(); } - _posScaleArray->push_back(Vec4(t.position.osg(), t.scale)); - _vertexAttribArray->push_back((float)t.texture_index / varieties); + _posScaleArray->push_back(Vec4(position, scale)); + _vertexAttribArray->push_back((float)texture_index / varieties); dirtyBound(); } diff --git a/simgear/scene/tgdb/ShaderGeometry.hxx b/simgear/scene/tgdb/ShaderGeometry.hxx index d13cd9d6..30c72fbe 100644 --- a/simgear/scene/tgdb/ShaderGeometry.hxx +++ b/simgear/scene/tgdb/ShaderGeometry.hxx @@ -33,8 +33,6 @@ #include #include -#include "TreeBin.hxx" - namespace simgear { @@ -67,7 +65,7 @@ class ShaderGeometry : public osg::Drawable _geometry = geometry; } - void addTree(const TreeBin::Tree& tree); + void addObject(const osg::Vec3& position, float scale, int texture_index); osg::ref_ptr _geometry; diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index c531d0ed..b02307db 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -42,111 +42,184 @@ #include #include +#include +#include #include #include #include #include +#include #include "ShaderGeometry.hxx" #include "TreeBin.hxx" #define SG_TREE_QUAD_TREE_DEPTH 3 -// Comments from Tim Moore: -// Some work remains for this code. Stuart's enhancement for multiple -// textures per forest should be integrated. We should try to use one -// ShaderGeometry for *all* the trees in the scene graph and do the -// rotation and scale with a MatrixTransform above the trees quad -// tree. The positions would of course have to be transformed by the -// inverse of that transform. Also, we should investigate whether it -// would be better to instantiate trees as polygons in a osg::Geometry -// object instead of using the ShaderGeometry instancing technique. - using namespace osg; namespace simgear { -// memoize geometry -typedef boost::tuple ForestTuple; -typedef std::map > OrthQuadMap; +// Tree instance scheme: +// vertex - local position of quad vertex. +// normal - x y scaling, z number of varieties +// fog coord - rotation +// color - xyz of tree quad origin, replicated 4 times. +// +// The tree quad is rendered twice, with different rotations, to +// create the crossed tree geometry. + +struct TreesBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback +{ + TreesBoundingBoxCallback() {} + TreesBoundingBoxCallback(const TreesBoundingBoxCallback&, const CopyOp&) {} + META_Object(simgear, TreesBoundingBoxCallback); + virtual BoundingBox computeBound(const Drawable&) const; +}; -osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate) +BoundingBox +TreesBoundingBoxCallback::computeBound(const Drawable& drawable) const { - static OrthQuadMap orthQuadMap; - OrthQuadMap::iterator giter - = orthQuadMap.find(ForestTuple(w, h, varieties)); - if (giter != orthQuadMap.end()) - return giter->second.get(); + BoundingBox bb; + const Geometry* geom = static_cast(&drawable); + const Vec3Array* v = static_cast(geom->getVertexArray()); + const Vec3Array* pos = static_cast(geom->getColorArray()); + const Vec3Array* params + = static_cast(geom->getNormalArray()); + const FloatArray* rot + = static_cast(geom->getFogCoordArray()); + float w = (*params)[0].x(); + float h = (*params)[0].y(); + Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList(); + FloatArray::const_iterator rotitr = rot->begin(); + for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), + psend = primSets.end(); + psitr != psend; + ++psitr, ++rotitr) { + Matrixd trnsfrm = (Matrixd::scale(w, w, h) + * Matrixd::rotate(*rotitr, Vec3(0.0f, 0.0f, 1.0f))); + DrawArrays* da = static_cast(psitr->get()); + GLint psFirst = da->getFirst(); + GLint psEndVert = psFirst + da->getCount(); + for (GLint i = psFirst;i < psEndVert; ++i) { + Vec3 pt = (*v)[i]; + pt = pt * trnsfrm; + pt += (*pos)[i]; + bb.expandBy(pt); + } + } + return bb; +} - //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f), +Geometry* makeSharedTreeGeometry(int numQuads) +{ + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); // set up the coords - // Create front and back polygons so we don't need to screw around - // with two-sided lighting in the shader. - osg::Vec3Array& v = *(new osg::Vec3Array(8)); - osg::Vec3Array& n = *(new osg::Vec3Array(8)); - osg::Vec2Array& t = *(new osg::Vec2Array(8)); - - float cw = w*0.5f; - - v[0].set(0.0f,-cw,0.0f); - v[1].set(0.0f, cw,0.0f); - v[2].set(0.0f, cw,h); - v[3].set(0.0f,-cw,h); - - v[4].set(-cw,0.0f,0.0f); - v[5].set( cw,0.0f,0.0f); - v[6].set( cw,0.0f,h); - v[7].set(-cw,0.0f,h); - - // The texture coordinate range is not the - // entire coordinate space - as the texture - // has a number of different trees on it. - float tx = 1.0f/varieties; - - t[0].set(0.0f,0.0f); - t[1].set( tx,0.0f); - t[2].set( tx,1.0f); - t[3].set(0.0f,1.0f); - - t[4].set(0.0f,0.0f); - t[5].set( tx,0.0f); - t[6].set( tx,1.0f); - t[7].set(0.0f,1.0f); - - // For now the normal is normal to the quad. If we want to get - // fancier and approximate a cylindrical tree or something, then - // we would really want more geometry. - std::fill(n.begin(), n.begin() + 4, Vec3f(1.0f, 0.0f, 0.0f)); - std::fill(n.begin() + 4, n.end(), Vec3f(0.0f, -1.0f, 0.0f)); - for (unsigned int i = 0; i < 8; i++) { - v[i] = v[i] * rotate; - // Should be the inverse transpose, but assume that rotate is - // orthonormal. - n[i] = n[i] * rotate; + osg::Vec3Array* v = new osg::Vec3Array; + osg::Vec2Array* t = new osg::Vec2Array; + v->reserve(numQuads * 4); + t->reserve(numQuads * 4); + for (int i = 0; i < numQuads; ++i) { + // Apply a random scaling factor and texture index. + float h = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f; + float cw = h * .5; + v->push_back(Vec3(0.0f, -cw, 0.0f)); + v->push_back(Vec3(0.0f, cw, 0.0f)); + v->push_back(Vec3(0.0f, cw, h)); + v->push_back(Vec3(0.0f,-cw, h)); + // The texture coordinate range is not the entire coordinate + // space, as the texture has a number of different trees on + // it. Here we assign random coordinates and let the shader + // choose the variety. + float variety = mt_rand(&seed); + t->push_back(Vec2(variety, 0.0f)); + t->push_back(Vec2(variety + 1.0f, 0.0f)); + t->push_back(Vec2(variety + 1.0f, 1.0f)); + t->push_back(Vec2(variety, 1.0f)); } + Geometry* result = new Geometry; + result->setVertexArray(v); + result->setTexCoordArray(0, t); + result->setComputeBoundingBoxCallback(new TreesBoundingBoxCallback); + result->setUseDisplayList(false); + return result; +} + +ref_ptr sharedTreeGeometry; - osg::Geometry *geom = new osg::Geometry; +Geometry* createTreeGeometry(float width, float height, int varieties) +{ + if (!sharedTreeGeometry) + sharedTreeGeometry = makeSharedTreeGeometry(1600); + Geometry* quadGeom = simgear::clone(sharedTreeGeometry.get(), + CopyOp::SHALLOW_COPY); + Vec3Array* params = new Vec3Array; + params->push_back(Vec3(width, height, (float)varieties)); + quadGeom->setNormalArray(params); + quadGeom->setNormalBinding(Geometry::BIND_OVERALL); + // Positions + quadGeom->setColorArray(new Vec3Array); + quadGeom->setColorBinding(Geometry::BIND_PER_VERTEX); + FloatArray* rotation = new FloatArray(2); + (*rotation)[0] = 0.0; + (*rotation)[1] = PI_2; + quadGeom->setFogCoordArray(rotation); + quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET); + for (int i = 0; i < 2; ++i) + quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS)); + return quadGeom; +} - geom->setVertexArray(&v); - geom->setTexCoordArray(0, &t); - geom->setNormalArray(&n); - geom->setNormalBinding(Geometry::BIND_PER_VERTEX); - // No color for now; that's used to pass the position. - geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8)); +Geode* createTreeGeode(float width, float height, int varieties) +{ + Geode* result = new Geode; + result->addDrawable(createTreeGeometry(width, height, varieties)); + return result; +} - orthQuadMap.insert(std::make_pair(ForestTuple(w, h, varieties), geom)); - return geom; +void addTreeToLeafGeode(Geode* geode, const SGVec3f& p) +{ + const Vec3& pos = p.osg(); + unsigned int numDrawables = geode->getNumDrawables(); + Geometry* geom + = static_cast(geode->getDrawable(numDrawables - 1)); + Vec3Array* posArray = static_cast(geom->getColorArray()); + if (posArray->size() + >= static_cast(geom->getVertexArray())->size()) { + Vec3Array* paramsArray + = static_cast(geom->getNormalArray()); + Vec3 params = (*paramsArray)[0]; + geom = createTreeGeometry(params.x(), params.y(), params.z()); + posArray = static_cast(geom->getColorArray()); + geode->addDrawable(geom); + } + posArray->insert(posArray->end(), 4, pos); + size_t numVerts = posArray->size(); + for (int i = 0; i < 2; ++i) { + DrawArrays* primSet + = static_cast(geom->getPrimitiveSet(i)); + primSet->setCount(numVerts); + } } static char vertexShaderSource[] = "varying float fogFactor;\n" - "attribute float textureIndex;\n" "\n" "void main(void)\n" "{\n" - " gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndex, 0.0, 0.0, 0.0);\n" - " vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n" + " float numVarieties = gl_Normal.z;\n" + " float texFract = floor(fract(gl_MultiTexCoord0.x) * numVarieties) / numVarieties;\n" + " texFract += floor(gl_MultiTexCoord0.x) / numVarieties;\n" + " float sr = sin(gl_FogCoord);\n" + " float cr = cos(gl_FogCoord);\n" + " gl_TexCoord[0] = vec4(texFract, gl_MultiTexCoord0.y, 0.0, 0.0);\n" + // scaling + " vec3 position = gl_Vertex.xyz * gl_Normal.xxy;\n" + // Rotation of the generic quad to specific one for the tree. + " position.xy = vec2(dot(position.xy, vec2(cr, sr)), dot(position.xy, vec2(-sr, cr)));\n" + " position = position + gl_Color.xyz;\n" " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n" " vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n" " float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(-ecPosition));\n" @@ -179,24 +252,26 @@ namespace { struct MakeTreesLeaf { - MakeTreesLeaf(float range, Geometry* geometry, int varieties) : - _range(range), _varieties(varieties), _geometry(geometry) - {} + MakeTreesLeaf(float range, int varieties, float width, float height) : + _range(range), _varieties(varieties), + _width(width), _height(height) {} + MakeTreesLeaf(const MakeTreesLeaf& rhs) : - _range(rhs._range), _varieties(rhs._varieties), _geometry(rhs._geometry) {} + _range(rhs._range), + _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height) + {} + LOD* operator() () const { LOD* result = new LOD; - Geode* geode = new Geode; - ShaderGeometry* sg = new ShaderGeometry(_varieties); - sg->setGeometry(_geometry); - geode->addDrawable(sg); + Geode* geode = createTreeGeode(_width, _height, _varieties); result->addChild(geode, 0, _range); return result; } float _range; int _varieties; - Geometry* _geometry; + float _width; + float _height; }; struct AddTreesLeafObject @@ -204,9 +279,7 @@ struct AddTreesLeafObject void operator() (LOD* lod, const TreeBin::Tree& tree) const { Geode* geode = static_cast(lod->getChild(0)); - ShaderGeometry* sg - = static_cast(geode->getDrawable(0)); - sg->addTree(tree); + addTreeToLeafGeode(geode, tree.position); } }; @@ -228,8 +301,7 @@ struct TreeTransformer TreeBin::Tree operator()(const TreeBin::Tree& tree) const { const Vec3& pos = tree.position.osg(); - return TreeBin::Tree(SGVec3f(pos * mat), tree.texture_index, - tree.scale); + return TreeBin::Tree(SGVec3f(pos * mat)); } Matrix mat; }; @@ -242,12 +314,7 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform) { Matrix transInv = Matrix::inverse(transform); static Matrix ident; - // Set up some shared structures. - osg::Geometry* shared_geometry = createOrthQuads(forest.width, - forest.height, - forest.texture_varieties, - ident); - + // Set up some shared structures. ref_ptr group; osg::StateSet* stateset = 0; @@ -271,10 +338,9 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform) alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f); program = new Program; baseTextureSampler = new osg::Uniform("baseTexture", 0); - Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource); + Shader* vertex_shader = new Shader(Shader::VERTEX, + vertexShaderSource); program->addShader(vertex_shader); - program->addBindAttribLocation("textureIndex", 1); - Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource); program->addShader(fragment_shader); @@ -299,12 +365,11 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform) } // Now, create a quadtree for the forest. { - ShaderGeometryQuadtree quadtree(GetTreeCoord(), - AddTreesLeafObject(), - SG_TREE_QUAD_TREE_DEPTH, - MakeTreesLeaf(forest.range, - shared_geometry, - forest.texture_varieties)); + ShaderGeometryQuadtree + quadtree(GetTreeCoord(), AddTreesLeafObject(), + SG_TREE_QUAD_TREE_DEPTH, + MakeTreesLeaf(forest.range, forest.texture_varieties, + forest.width, forest.height)); // Transform tree positions from the "geocentric" positions we // get from the scenery polys into the local Z-up coordinate // system. diff --git a/simgear/scene/tgdb/TreeBin.hxx b/simgear/scene/tgdb/TreeBin.hxx index 3c797028..bb542c23 100644 --- a/simgear/scene/tgdb/TreeBin.hxx +++ b/simgear/scene/tgdb/TreeBin.hxx @@ -36,12 +36,10 @@ namespace simgear class TreeBin { public: struct Tree { - Tree(const SGVec3f& p, int t, float s) : - position(p), texture_index(t), scale(s) + Tree(const SGVec3f& p) : + position(p) { } SGVec3f position; - int texture_index; - float scale; }; typedef std::vector TreeList; @@ -55,7 +53,7 @@ public: void insert(const Tree& t) { _trees.push_back(t); } void insert(const SGVec3f& p, int t, float s) - { insert(Tree(p, t, s)); } + { insert(Tree(p)); } unsigned getNumTrees() const { return _trees.size(); } @@ -64,7 +62,6 @@ public: TreeList _trees; }; -osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate); osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform); } #endif diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index cdfd64f6..54e3ef16 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -474,12 +474,7 @@ struct SGTileGeometryBin { std::vector::iterator j; for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { - - // Apply a random scaling factor and texture index. - float scale = (mt_rand(&seed) + mt_rand(&seed)) / 2.0f + 0.5f; - int v = (int) (mt_rand(&seed) * mat->get_tree_varieties()); - if (v == mat->get_tree_varieties()) v--; - randomForest.insert(*j, v, scale); + randomForest.insert(*j); } } } -- 2.39.5