X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2FTreeBin.cxx;h=a530ba32d3216489a8d703f055fcdcb069c5a1e7;hb=7e7ce2f38e87d6244e05730fa4382da088bb25f1;hp=dfad0dc4bcc65d80d66d1545950a0fa5961dd65e;hpb=4b63bc051e701835a12f6f1eca5437b6abd58593;p=simgear.git diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index dfad0dc4..a530ba32 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -19,6 +19,13 @@ * */ +#include +#include +#include +#include + +#include + #include #include #include @@ -36,38 +43,50 @@ #include #include +#include +#include +#include #include "ShaderGeometry.hxx" #include "TreeBin.hxx" -#define SG_TREE_QUAD_TREE_SIZE 32 +#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 { -osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate) +// memoize geometry +typedef boost::tuple ForestTuple; +typedef std::map > OrthQuadMap; + +osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate) { + static OrthQuadMap orthQuadMap; + OrthQuadMap::iterator giter + = orthQuadMap.find(ForestTuple(w, h, varieties)); + if (giter != orthQuadMap.end()) + return giter->second.get(); //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 rotation = 0.0f; - float sw = sinf(rotation)*w*0.5f; - float cw = cosf(rotation)*w*0.5f; - - v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f); - v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f); - v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h); - v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h); - - v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f); - v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f); - v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h); - v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h); - */ float cw = w*0.5f; v[0].set(0.0f,-cw,0.0f); @@ -80,165 +99,228 @@ osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate) 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(1.0f,0.0f); - t[2].set(1.0f,1.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(1.0f,0.0f); - t[6].set(1.0f,1.0f); + t[5].set( tx,0.0f); + t[6].set( tx,1.0f); t[7].set(0.0f,1.0f); - - for (unsigned int i = 0; i < 8; i++) - { - v[i] = v[i] * rotate; + + // 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::Geometry *geom = new osg::Geometry; - geom->setVertexArray( &v ); + 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)); + + orthQuadMap.insert(std::make_pair(ForestTuple(w, h, varieties), geom)); + return geom; +} - geom->setTexCoordArray( 0, &t ); + 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" + " 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" + " vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.1, n);\n" + " vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n" + " gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n" + " gl_BackColor = gl_FrontColor;\n" + " float fogCoord = abs(ecPosition.z);\n" + " fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n" + " fogFactor = clamp(fogFactor, 0.0, 1.0);\n" + "}\n"; - geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) ); +static char fragmentShaderSource[] = + "uniform sampler2D baseTexture; \n" + "varying float fogFactor;\n" + "\n" + "void main(void) \n" + "{ \n" + " vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n" + " vec4 finalColor = base * gl_Color;\n" + " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n" + "}\n"; - return geom; +typedef std::map > StateSetMap; + +static StateSetMap treeTextureMap; + +// Helper classes for creating the quad tree +namespace +{ +struct MakeTreesLeaf +{ + MakeTreesLeaf(float range, Geometry* geometry, int varieties) : + _range(range), _geometry(geometry), _varieties(varieties) + {} + MakeTreesLeaf(const MakeTreesLeaf& rhs) : + _range(rhs._range), _geometry(rhs._geometry), _varieties(rhs._varieties) {} + LOD* operator() () const + { + LOD* result = new LOD; + Geode* geode = new Geode; + ShaderGeometry* sg = new ShaderGeometry(_varieties); + sg->setGeometry(_geometry); + geode->addDrawable(sg); + result->addChild(geode, 0, _range); + return result; + } + float _range; + int _varieties; + Geometry* _geometry; +}; + +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); + } +}; + +struct GetTreeCoord +{ + Vec3 operator() (const TreeBin::Tree& tree) const + { + return tree.position.osg(); + } +}; + +typedef QuadTreeBuilder ShaderGeometryQuadtree; } -osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform) +struct TreeTransformer { - // Set up some shared structures. - // FIXME: Currently we only take the texture, height and width of the first tree in the forest. In the future - // we should be able to handle multiple textures etc. - TreeBin::Tree firstTree = forest.getTree(0); - - osg::Geometry* shared_geometry = createOrthQuads(firstTree.width, - firstTree.height, - transform); - osg::Group* group = new osg::Group; - - osg::Texture2D *tex = new osg::Texture2D; - tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP ); - tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP ); - tex->setImage(osgDB::readImageFile(firstTree.texture)); - - osg::AlphaFunc* alphaFunc = new osg::AlphaFunc; - alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f); - - osg::StateSet *dstate = new osg::StateSet; - dstate->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); - dstate->setTextureAttribute(0, new osg::TexEnv ); - dstate->setAttributeAndModes( new osg::BlendFunc, osg::StateAttribute::ON ); - dstate->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON ); - dstate->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); - dstate->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); - - osg::StateSet* stateset = new osg::StateSet; - stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); - stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); - - osg::Program* program = new osg::Program; - stateset->setAttribute(program); - osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0); - stateset->addUniform(baseTextureSampler); - - /* - * FIXME: Currently, calculating the diffuse term results in a bad - * "flickering" and a tendency of the diffuse term be either - * 0.0 of 1.0. Hence, it has been commented out in the shader below. - * I (Stuart) suspect it may be because the light is so distant that - * we're seeing floating point representation issues. - */ - char vertexShaderSource[] = -// "varying vec3 N;\n" -// "varying vec3 v;\n" - "varying vec2 texcoord;\n" - "varying float fogFactor;\n" - "\n" - "void main(void)\n" - "{\n" -// " v = vec3(gl_ModelViewMatrix * gl_Vertex);\n" -// " N = normalize(gl_NormalMatrix * gl_Normal);\n" - " texcoord = gl_MultiTexCoord0.st;\n" - " vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n" - " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n" - " const float LOG2 = 1.442695;\n" - " gl_FogFragCoord = gl_Position.z;\n" - " fogFactor = exp2( -gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord * LOG2 );\n" - " fogFactor = clamp(fogFactor, 0.0, 1.0);\n" - "}\n"; - - char fragmentShaderSource[] = - "uniform sampler2D baseTexture; \n" -// "varying vec3 N;\n" -// "varying vec3 v;\n" - "varying vec2 texcoord;\n" - "varying float fogFactor;\n" - "\n" - "void main(void) \n" - "{ \n" - " vec4 base = texture2D( baseTexture, texcoord);\n" -// " vec3 L = normalize(gl_LightSource[0].position.xyz);\n" -// " vec4 vDiffuse = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);\n" -// " vDiffuse = sqrt(clamp(vDiffuse, 0.0, 1.0));\n" -// " vec4 vAmbient = gl_FrontLightProduct[0].ambient;\n" -// " vec4 finalColor = base * (vAmbient + vDiffuse);\n" - " vec4 finalColor = base * gl_FrontLightProduct[0].diffuse;\n" - " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n" - "}\n"; - - osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource); - program->addShader(vertex_shader); - - osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource); - program->addShader(fragment_shader); - - // Now, create a quadtree for the forest. - osg::ref_ptr _root; - ShaderGeometry* leaves[SG_TREE_QUAD_TREE_SIZE][SG_TREE_QUAD_TREE_SIZE]; - - // Determine the extents of the tree, and a list of the required textures for later. - osg::BoundingBox extents; - for (unsigned int i = 0; i < forest.getNumTrees(); i++) - { - const osg::Vec3f center = forest.getTree(i).position.osg() * transform; - extents.expandBy(center); + TreeTransformer(Matrix& mat_) : mat(mat_) {} + 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); } + Matrix mat; +}; + +// This actually returns a MatrixTransform node. If we rotate the whole +// forest into the local Z-up coordinate system we can reuse the +// primitive tree geometry for all the forests of the same type. + +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); + + ref_ptr group; + + osg::StateSet* stateset = 0; + StateSetMap::iterator iter = treeTextureMap.find(forest.texture); + if (iter == treeTextureMap.end()) { + osg::Texture2D *tex = new osg::Texture2D; + tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP ); + tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP ); + tex->setImage(osgDB::readImageFile(forest.texture)); + + static ref_ptr alphaFunc; + static ref_ptr program; + static ref_ptr baseTextureSampler; + static ref_ptr material; - const osg::Vec2 quadMin(extents.xMin(), extents.yMin()); - const osg::Vec2 quadMax(extents.xMax(), extents.yMax()); - - for (int i = 0; i < SG_TREE_QUAD_TREE_SIZE; ++i) { - osg::LOD* interior = new osg::LOD; - //osg::Group* interior = new osg::Group; - group->addChild(interior); - for (int j = 0; j < SG_TREE_QUAD_TREE_SIZE; ++j) { - osg::Geode* geode = new osg::Geode; - leaves[i][j] = new ShaderGeometry(); - leaves[i][j]->setGeometry(shared_geometry); - geode->setStateSet(stateset); - geode->addDrawable(leaves[i][j]); - interior->addChild(geode, 0, firstTree.range); - } + stateset = new osg::StateSet; + stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); + stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin"); + if (!program.valid()) { + alphaFunc = new AlphaFunc; + alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f); + program = new Program; + baseTextureSampler = new osg::Uniform("baseTexture", 0); + 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); + material = new Material; + // Don´t track vertex color + material->setColorMode(Material::OFF); + material->setAmbient(Material::FRONT_AND_BACK, + Vec4(1.0f, 1.0f, 1.0f, 1.0f)); + material->setDiffuse(Material::FRONT_AND_BACK, + Vec4(1.0f, 1.0f, 1.0f, 1.0f)); + } + stateset->setAttributeAndModes(alphaFunc.get()); + stateset->setAttribute(program.get()); + stateset->addUniform(baseTextureSampler.get()); + stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON); + stateset->setAttribute(material.get()); + + treeTextureMap.insert(StateSetMap::value_type(forest.texture, + stateset)); + } else { + stateset = iter->second.get(); } - - // Now we've got our quadtree, add the trees based on location. - - for (unsigned int i = 0; i < forest.getNumTrees(); i++) - { - TreeBin::Tree t = forest.getTree(i); - osg::Vec3 center = t.position.osg() * transform; - - int x = (int)(SG_TREE_QUAD_TREE_SIZE * (center.x() - quadMin.x()) / (quadMax.x() - quadMin.x())); - x = osg::clampTo(x, 0, (SG_TREE_QUAD_TREE_SIZE - 1)); - int y = (int)(SG_TREE_QUAD_TREE_SIZE * (center.y() - quadMin.y()) / (quadMax.y() - quadMin.y())); - y = osg::clampTo(y, 0, (SG_TREE_QUAD_TREE_SIZE -1)); - - leaves[y][x]->addTree(t.position.osg(), t.height); + // Now, create a quadtree for the forest. + { + ShaderGeometryQuadtree quadtree(GetTreeCoord(), + AddTreesLeafObject(), + SG_TREE_QUAD_TREE_DEPTH, + MakeTreesLeaf(forest.range, + shared_geometry, + forest.texture_varieties)); + // Transform tree positions from the "geocentric" positions we + // get from the scenery polys into the local Z-up coordinate + // system. + std::vector rotatedTrees; + rotatedTrees.reserve(forest._trees.size()); + std::transform(forest._trees.begin(), forest._trees.end(), + std::back_inserter(rotatedTrees), + TreeTransformer(transInv)); + quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end()); + group = quadtree.getRoot(); } - - return group; + MatrixTransform* mt = new MatrixTransform(transform); + for (int i = 0; i < group->getNumChildren(); ++i) + mt->addChild(group->getChild(i)); + mt->setStateSet(stateset); + return mt; } }