]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/tgdb/TreeBin.cxx
Merge branch 'maint' into next
[simgear.git] / simgear / scene / tgdb / TreeBin.cxx
index a97774443545abec937f7b0e69c519c1ec332617..a530ba32d3216489a8d703f055fcdcb069c5a1e7 100644 (file)
  */
 
 #include <algorithm>
+#include <vector>
 #include <string>
 #include <map>
 
+#include <boost/tuple/tuple_comparison.hpp>
+
 #include <osg/AlphaFunc>
 #include <osg/Billboard>
 #include <osg/BlendFunc>
@@ -64,8 +67,17 @@ using namespace osg;
 namespace simgear
 {
 
-osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
+// memoize geometry
+typedef boost::tuple<float, float, int> ForestTuple;
+typedef std::map<ForestTuple, ref_ptr<Geometry> > 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
@@ -87,14 +99,19 @@ 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 now the normal is normal to the quad. If we want to get
@@ -118,39 +135,37 @@ osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
     // 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;
 }
 
  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"
-    "  vec3 N = normalize(gl_NormalMatrix * gl_Normal);\n"
-    "  vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(N, gl_LightSource[0].position.xyz));\n"
-    "  vec3 backDiffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(-N, gl_LightSource[0].position.xyz));\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 = ambientColor + gl_LightSource[0].diffuse * vec4(backDiffuse, 1.0)\n;"
-    "  gl_TexCoord[0] = gl_MultiTexCoord0;\n"
-    " float fogCoord = abs(ecPosition.z);\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";
 
 static char fragmentShaderSource[] = 
     "uniform sampler2D baseTexture; \n"
-//        "varying vec3 N;\n"\r
-//        "varying vec3 v;\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";
@@ -164,22 +179,23 @@ namespace
 {
 struct MakeTreesLeaf
 {
-    MakeTreesLeaf(float range, Geometry* geometry) :
-        _range(range), _geometry(geometry)
+    MakeTreesLeaf(float range, Geometry* geometry, int varieties) :
+        _range(range), _geometry(geometry), _varieties(varieties)
     {}
     MakeTreesLeaf(const MakeTreesLeaf& rhs) :
-        _range(rhs._range), _geometry(rhs._geometry) {}
+        _range(rhs._range), _geometry(rhs._geometry), _varieties(rhs._varieties) {}
     LOD* operator() () const
     {
         LOD* result = new LOD;
         Geode* geode = new Geode;
-        ShaderGeometry* sg = new ShaderGeometry;
+        ShaderGeometry* sg = new ShaderGeometry(_varieties);
         sg->setGeometry(_geometry);
         geode->addDrawable(sg);
         result->addChild(geode, 0, _range);
         return result;
     }
     float _range;
+    int _varieties;
     Geometry* _geometry;
 };
 
@@ -190,44 +206,57 @@ struct AddTreesLeafObject
         Geode* geode = static_cast<Geode*>(lod->getChild(0));
         ShaderGeometry* sg
             = static_cast<ShaderGeometry*>(geode->getDrawable(0));
-        sg->addTree(tree.position.osg(), tree.height);
+        sg->addTree(tree);
     }
 };
 
 struct GetTreeCoord
 {
-    GetTreeCoord(const Matrix& transform) : _transform(transform) {}
-    GetTreeCoord(const GetTreeCoord& rhs) : _transform(rhs._transform) {}
     Vec3 operator() (const TreeBin::Tree& tree) const
     {
-        return tree.position.osg() * _transform;
+        return tree.position.osg();
     }
-    Matrix _transform;
 };
 
 typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
                         GetTreeCoord> ShaderGeometryQuadtree;
 }
 
+struct TreeTransformer
+{
+    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. 
-    // 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::Geometry* shared_geometry = createOrthQuads(forest.width, 
+                                                     forest.height, 
+                                                     forest.texture_varieties,
+                                                     ident);
+
     ref_ptr<Group> group;
 
     osg::StateSet* stateset = 0;
-    StateSetMap::iterator iter = treeTextureMap.find(firstTree.texture);
+    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(firstTree.texture));
+        tex->setImage(osgDB::readImageFile(forest.texture));
 
         static ref_ptr<AlphaFunc> alphaFunc;
         static ref_ptr<Program> program;
@@ -244,6 +273,8 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
             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);
@@ -251,34 +282,45 @@ osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
             // DonĀ“t track vertex color
             material->setColorMode(Material::OFF);
             material->setAmbient(Material::FRONT_AND_BACK,
-                                 Vec4(.6f, .6f, .6f, 1.0f));
+                                 Vec4(1.0f, 1.0f, 1.0f, 1.0f));
             material->setDiffuse(Material::FRONT_AND_BACK,
-                                 Vec4(.4f, .4f, .4f, 1.0f));
+                                 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());
-        // XXX This should really come from a material definition
-        // instead of being hard-coded.
-        treeTextureMap.insert(StateSetMap::value_type(firstTree.texture,
+
+        treeTextureMap.insert(StateSetMap::value_type(forest.texture,
                                                       stateset));
     } else {
         stateset = iter->second.get();
     }
     // Now, create a quadtree for the forest.
     {
-        ShaderGeometryQuadtree quadtree(GetTreeCoord(Matrix::inverse(transform)),
+        ShaderGeometryQuadtree quadtree(GetTreeCoord(),
                                         AddTreesLeafObject(),
                                         SG_TREE_QUAD_TREE_DEPTH,
-                                        MakeTreesLeaf(firstTree.range,
-                                                      shared_geometry));
-        quadtree.buildQuadTree(forest._trees.begin(), forest._trees.end());
+                                        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<TreeBin::Tree> 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();
     }
-    group->setStateSet(stateset);
-    return group.release();    
+    MatrixTransform* mt = new MatrixTransform(transform);
+    for (int i = 0; i < group->getNumChildren(); ++i)
+        mt->addChild(group->getChild(i));
+    mt->setStateSet(stateset);
+    return mt;
 }
 
 }