X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2FTreeBin.cxx;h=66c26addba0ea60ca68522cc6fd80dcccb095f1b;hb=2cc2a857a2cc2daff30601e0f8f697c768dd5b30;hp=dfad0dc4bcc65d80d66d1545950a0fa5961dd65e;hpb=4b63bc051e701835a12f6f1eca5437b6abd58593;p=simgear.git diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index dfad0dc4..66c26add 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -19,226 +19,308 @@ * */ -#include -#include -#include +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + #include #include -#include #include #include #include -#include -#include -#include #include #include +#include +#include #include +#include +#include +#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 + +using namespace osg; namespace simgear { -osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate) +// 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; +}; + +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; +} - //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 - osg::Vec3Array& v = *(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); - 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); - - t[0].set(0.0f,0.0f); - t[1].set(1.0f,0.0f); - t[2].set(1.0f,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[7].set(0.0f,1.0f); - - for (unsigned int i = 0; i < 8; i++) - { - v[i] = v[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; +} - osg::Geometry *geom = new osg::Geometry; +ref_ptr sharedTreeGeometry; - geom->setVertexArray( &v ); +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); + // The primitive sets render the same geometry, but the second + // will rotated 90 degrees by the vertex shader, which uses the + // fog coordinate as a rotation. + for (int i = 0; i < 2; ++i) + quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS)); + return quadGeom; +} - geom->setTexCoordArray( 0, &t ); +EffectGeode* createTreeGeode(float width, float height, int varieties) +{ + EffectGeode* result = new EffectGeode; + result->addDrawable(createTreeGeometry(width, height, varieties)); + return result; +} - geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) ); +void addTreeToLeafGeode(Geode* geode, const SGVec3f& p) +{ + Vec3 pos = toOsg(p); + 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); + } +} + +typedef std::map > EffectMap; - return geom; +static EffectMap treeEffectMap; + +// Helper classes for creating the quad tree +namespace +{ +struct MakeTreesLeaf +{ + MakeTreesLeaf(float range, int varieties, float width, float height, + Effect* effect) : + _range(range), _varieties(varieties), + _width(width), _height(height), _effect(effect) {} + + MakeTreesLeaf(const MakeTreesLeaf& rhs) : + _range(rhs._range), + _varieties(rhs._varieties), _width(rhs._width), _height(rhs._height), + _effect(rhs._effect) + {} + + LOD* operator() () const + { + LOD* result = new LOD; + EffectGeode* geode = createTreeGeode(_width, _height, _varieties); + geode->setEffect(_effect.get()); + result->addChild(geode, 0, _range); + return result; + } + float _range; + int _varieties; + float _width; + float _height; + ref_ptr _effect; +}; + +struct AddTreesLeafObject +{ + void operator() (LOD* lod, const TreeBin::Tree& tree) const + { + Geode* geode = static_cast(lod->getChild(0)); + addTreeToLeafGeode(geode, tree.position); + } +}; + +struct GetTreeCoord +{ + Vec3 operator() (const TreeBin::Tree& tree) const + { + return toOsg(tree.position); + } +}; + +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 + { + Vec3 pos = toOsg(tree.position); + return TreeBin::Tree(toSG(pos * mat)); } - - 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); - } + 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. + ref_ptr group; + + Effect* effect = 0; + EffectMap::iterator iter = treeEffectMap.find(forest.texture); + if (iter == treeEffectMap.end()) { + SGPropertyNode_ptr effectProp = new SGPropertyNode; + makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree"); + SGPropertyNode* params = makeChild(effectProp, "parameters"); + // emphasize n = 0 + params->getChild("texture", 0, true)->getChild("image", 0, true) + ->setStringValue(forest.texture); + effect = makeEffect(effectProp, true); + treeEffectMap.insert(EffectMap::value_type(forest.texture, effect)); + } else { + effect = 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, forest.texture_varieties, + forest.width, forest.height, effect)); + // 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 (size_t i = 0; i < group->getNumChildren(); ++i) + mt->addChild(group->getChild(i)); + return mt; } }