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. -osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate) +struct TreesBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback { - static OrthQuadMap orthQuadMap; - OrthQuadMap::iterator giter - = orthQuadMap.find(ForestTuple(w, h, varieties)); - if (giter != orthQuadMap.end()) - return giter->second.get(); + TreesBoundingBoxCallback() {} + TreesBoundingBoxCallback(const TreesBoundingBoxCallback&, const CopyOp&) {} + META_Object(simgear, TreesBoundingBoxCallback); + virtual BoundingBox computeBound(const Drawable&) const; +}; - //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f), - // 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; +BoundingBox +TreesBoundingBoxCallback::computeBound(const Drawable& drawable) const +{ + 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; +} - osg::Geometry *geom = new osg::Geometry; +Geometry* makeSharedTreeGeometry(int numQuads) +{ + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + // set up the coords + 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; +} - 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)); +ref_ptr sharedTreeGeometry; - orthQuadMap.insert(std::make_pair(ForestTuple(w, h, varieties), geom)); - return geom; +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; +} + +Geode* createTreeGeode(float width, float height, int varieties) +{ + Geode* result = new Geode; + result->addDrawable(createTreeGeometry(width, height, varieties)); + return result; +} + +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); } } }