From 4b63bc051e701835a12f6f1eca5437b6abd58593 Mon Sep 17 00:00:00 2001 From: timoore Date: Sat, 2 Feb 2008 23:01:05 +0000 Subject: [PATCH] Random trees from Stuart Buchanan Stuart's new file SGTreeBin.hxx has been split into 4 files: TreeBin.[ch]xx and ShaderGeometry.[ch]xx. --- simgear/scene/material/mat.cxx | 12 ++ simgear/scene/material/mat.hxx | 55 +++++- simgear/scene/material/matmodel.cxx | 70 +++---- simgear/scene/material/matmodel.hxx | 14 ++ simgear/scene/tgdb/Makefile.am | 7 +- simgear/scene/tgdb/SGModelBin.hxx | 42 +---- simgear/scene/tgdb/ShaderGeometry.cxx | 32 ++++ simgear/scene/tgdb/ShaderGeometry.hxx | 79 ++++++++ simgear/scene/tgdb/TreeBin.cxx | 244 +++++++++++++++++++++++++ simgear/scene/tgdb/TreeBin.hxx | 66 +++++++ simgear/scene/tgdb/obj.cxx | 81 ++++++-- simgear/scene/util/QuadTreeBuilder.cxx | 36 ++-- simgear/scene/util/QuadTreeBuilder.hxx | 15 +- 13 files changed, 629 insertions(+), 124 deletions(-) create mode 100644 simgear/scene/tgdb/ShaderGeometry.cxx create mode 100644 simgear/scene/tgdb/ShaderGeometry.hxx create mode 100644 simgear/scene/tgdb/TreeBin.cxx create mode 100644 simgear/scene/tgdb/TreeBin.hxx diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index 7e590d63..4257ae2a 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -141,6 +141,10 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode * props wrapv = props->getBoolValue("wrapv", true); mipmap = props->getBoolValue("mipmap", true); light_coverage = props->getDoubleValue("light-coverage", 0.0); + tree_coverage = props->getDoubleValue("tree-coverage", 0.0); + tree_height = props->getDoubleValue("tree-height-m", 0.0); + tree_width = props->getDoubleValue("tree-width-m", 0.0); + tree_range = props->getDoubleValue("tree-range-m", 0.0); // surface values for use with ground reactions solid = props->getBoolValue("solid", true); @@ -177,6 +181,14 @@ SGMaterial::read_properties( const string &fg_root, const SGPropertyNode * props for (unsigned int i = 0; i < object_group_nodes.size(); i++) object_groups.push_back(new SGMatModelGroup(object_group_nodes[i])); + vector tree_texture_nodes = + ((SGPropertyNode *)props)->getChildren("tree-texture"); + for (unsigned int i = 0; i < tree_texture_nodes.size(); i++) { + SGPath tpath( fg_root ); + tpath.append(tree_texture_nodes[i]->getStringValue()); + tree_textures.push_back(tpath.str()); + } + // read glyph table for taxi-/runway-signs vector glyph_nodes = props->getChildren("glyph"); for (unsigned int i = 0; i < glyph_nodes.size(); i++) { diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index 0a0fbd13..38cc7c95 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -142,10 +142,47 @@ public: * * A smaller number means more generated night lighting. * - * @return The area (m^2?) covered by each light. + * @return The area (m^2) covered by each light. */ inline double get_light_coverage () const { return light_coverage; } + /** + * Get the forest coverage. + * + * A smaller number means more generated forest canopy. + * + * @return The area (m^2) covered by each canopy. + */ + inline double get_tree_coverage () const { return tree_coverage; } + + /** + * Get the forest height. + * + * @return The average height of the trees. + */ + inline double get_tree_height () const { return tree_height; } + + /** + * Get the forest width. + * + * @return The average width of the trees. + */ + inline double get_tree_width () const { return tree_width; } + + /** + * Get the forest LoD range. + * + * @return The LoD range for the trees. + */ + inline double get_tree_range () const { return tree_range; } + + /** + * Get the list of textures to use for trees in the forest + * + * @return the vector of forest textures to use. + */ + inline vector get_tree_textures () const { return tree_textures; } + /** * Return if the surface material is solid, if it is not solid, a fluid * can be assumed, that is usually water. @@ -259,6 +296,18 @@ private: // coverage of night lighting. double light_coverage; + + // coverage of trees + double tree_coverage; + + // Range at which trees become visible + double tree_range; + + // Height of the tree + double tree_height; + + // Width of the tree + double tree_width; // True if the material is solid, false if it is a fluid bool solid; @@ -286,7 +335,9 @@ private: // taxiway-/runway-sign texture elements map > glyphs; - + + // The list of forest textures, used when creating trees + vector tree_textures; //////////////////////////////////////////////////////////////////// // Internal constructors and methods. diff --git a/simgear/scene/material/matmodel.cxx b/simgear/scene/material/matmodel.cxx index 3d58d18e..7af83c75 100644 --- a/simgear/scene/material/matmodel.cxx +++ b/simgear/scene/material/matmodel.cxx @@ -142,33 +142,6 @@ SGMatModel::load_models ( SGModelLib *modellib, // the billboarding should be handled // there). - // Create multiple LoD nodes so instead of all objects - // of the same type appearing at once, some appear further - // away. - // - // Very basic hardcoded distribution: - // 4 at normal range - // 2 at 1.5 times normal range - // 1 at 2 time normal range. - // - // We achieve this by creating the three different LoD - // nodes and adding them to the _models list multiple times. - - osg::LOD * lod1 = new osg::LOD; - lod1->setName("Model LOD"); - lod1->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); - lod1->setRange(0, 0, _range_m); - - osg::LOD * lod15 = new osg::LOD; - lod15->setName("Model LOD - 1.5"); - lod15->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); - lod15->setRange(0, 0, 1.5 * _range_m); - - osg::LOD * lod2 = new osg::LOD; - lod2->setName("Model LOD - 2.0"); - lod2->setRangeMode(osg::LOD::DISTANCE_FROM_EYE_POINT); - lod2->setRange(0, 0, 2.0 * _range_m); - if (_heading_type == HEADING_BILLBOARD) { // if the model is a billboard, it is likely : // 1. a branch with only leaves, @@ -180,27 +153,10 @@ SGMatModel::load_models ( SGModelLib *modellib, stateSet->setAttributeAndModes(alphaFunc, osg::StateAttribute::OVERRIDE); stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); - - lod1->addChild(entity); - lod15->addChild(entity); - lod2->addChild(entity); - } else { - lod1->addChild(entity); - lod15->addChild(entity); - lod2->addChild(entity); - } - - // Vary the distribution of LoDs by adding multiple times. - _models.push_back(lod1); - _models.push_back(lod1); - _models.push_back(lod1); - _models.push_back(lod1); - - _models.push_back(lod15); - _models.push_back(lod15); - - _models.push_back(lod2); - + } + + _models.push_back(entity); + } else { SG_LOG(SG_INPUT, SG_ALERT, "Failed to load object " << _paths[i]); } @@ -240,6 +196,24 @@ SGMatModel::get_coverage_m2 () const return _coverage_m2; } +double SGMatModel::get_range_m() const +{ + return _range_m; +} + +double SGMatModel::get_randomized_range_m(mt* seed) const +{ + double lrand = mt_rand(seed); + + // Note that the LoD is not completely randomized. + // 10% at 2 * range_m + // 30% at 1.5 * range_m + // 60% at 1 * range_m + if (lrand < 0.1) return 2 * _range_m; + if (lrand < 0.4) return 1.5 * _range_m; + else return _range_m; +} + SGMatModel::HeadingType SGMatModel::get_heading_type () const { diff --git a/simgear/scene/material/matmodel.hxx b/simgear/scene/material/matmodel.hxx index f173f5c0..982e89a9 100644 --- a/simgear/scene/material/matmodel.hxx +++ b/simgear/scene/material/matmodel.hxx @@ -40,6 +40,7 @@ #include #include #include +#include SG_USING_STD(string); @@ -112,6 +113,19 @@ public: */ double get_coverage_m2 () const; + /** + * Get the visual range of the object in meters. + * + * @return The visual range. + */ + double get_range_m () const; + + /** + * Get a randomized visual range + * + * @return a randomized visual range + */ + double get_randomized_range_m(mt* seed) const; /** * Get the heading type for the object. diff --git a/simgear/scene/tgdb/Makefile.am b/simgear/scene/tgdb/Makefile.am index 6eba2549..7c0a7fa3 100644 --- a/simgear/scene/tgdb/Makefile.am +++ b/simgear/scene/tgdb/Makefile.am @@ -18,7 +18,8 @@ include_HEADERS = \ SGTexturedTriangleBin.hxx \ SGTriangleBin.hxx \ SGVertexArrayBin.hxx \ - GroundLightManager.hxx + GroundLightManager.hxx \ + ShaderGeometry.hxx libsgtgdb_a_SOURCES = \ apt_signs.cxx \ @@ -28,6 +29,8 @@ libsgtgdb_a_SOURCES = \ SGOceanTile.cxx \ SGReaderWriterBTG.cxx \ SGVasiDrawable.cxx \ - GroundLightManager.cxx + GroundLightManager.cxx \ + ShaderGeometry.cxx \ + TreeBin.cxx INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/tgdb/SGModelBin.hxx b/simgear/scene/tgdb/SGModelBin.hxx index 1c851418..bd61a948 100755 --- a/simgear/scene/tgdb/SGModelBin.hxx +++ b/simgear/scene/tgdb/SGModelBin.hxx @@ -27,48 +27,22 @@ class SGMatModelBin { public: struct MatModel { - MatModel(const SGVec3f& p, SGMatModel *m) : - position(p), model(m) + MatModel(const SGVec3f& p, SGMatModel *m, int l) : + position(p), model(m), lod(l) { } SGVec3f position; SGMatModel *model; + int lod; }; typedef std::vector MatModelList; void insert(const MatModel& model) { - float x = model.position.x(); - float y = model.position.y(); - float z = model.position.z(); - - if (_models.size() == 0) - { - min_x = x; - max_x = x; - - min_y = y; - max_y = y; - - min_z = z; - max_z = z; - } - else - { - min_x = SGMisc::min(min_x, x); - max_x = SGMisc::max(max_x, x); - - min_y = SGMisc::min(min_y, y); - max_y = SGMisc::max(max_y, y); - - min_z = SGMisc::min(min_z, z); - max_z = SGMisc::max(max_z, z); - } - _models.push_back(model); } - void insert(const SGVec3f& p, SGMatModel *m) - { insert(MatModel(p, m)); } + void insert(const SGVec3f& p, SGMatModel *m, int l) + { insert(MatModel(p, m, l)); } unsigned getNumModels() const { return _models.size(); } @@ -77,12 +51,6 @@ public: private: MatModelList _models; - float min_x; - float max_x; - float min_y; - float max_y; - float min_z; - float max_z; }; #endif diff --git a/simgear/scene/tgdb/ShaderGeometry.cxx b/simgear/scene/tgdb/ShaderGeometry.cxx new file mode 100644 index 00000000..d98b5b66 --- /dev/null +++ b/simgear/scene/tgdb/ShaderGeometry.cxx @@ -0,0 +1,32 @@ +#include "ShaderGeometry.hxx" + +using namespace osg; + +namespace simgear +{ +void ShaderGeometry::drawImplementation(RenderInfo& renderInfo) const +{ + for(PositionSizeList::const_iterator itr = _trees.begin(); + itr != _trees.end(); + ++itr) { + glColor4fv(itr->ptr()); + _geometry->draw(renderInfo); + } +} + +BoundingBox ShaderGeometry::computeBound() const +{ + BoundingBox geom_box = _geometry->getBound(); + BoundingBox bb; + for(PositionSizeList::const_iterator itr = _trees.begin(); + itr != _trees.end(); + ++itr) { + bb.expandBy(geom_box.corner(0)*(*itr)[3] + + Vec3((*itr)[0], (*itr)[1], (*itr)[2])); + bb.expandBy(geom_box.corner(7)*(*itr)[3] + + Vec3((*itr)[0], (*itr)[1], (*itr)[2])); + } + return bb; +} + +} diff --git a/simgear/scene/tgdb/ShaderGeometry.hxx b/simgear/scene/tgdb/ShaderGeometry.hxx new file mode 100644 index 00000000..78eb1d4f --- /dev/null +++ b/simgear/scene/tgdb/ShaderGeometry.hxx @@ -0,0 +1,79 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 Stuart Buchanan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifndef SHADER_GEOMETRY_HXX +#define SHADER_GEOMETRY_HXX 1 + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace simgear +{ + +class ShaderGeometry : public osg::Drawable +{ + public: + ShaderGeometry() + { + setUseDisplayList(false); + } + + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + ShaderGeometry(const ShaderGeometry& ShaderGeometry,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): + osg::Drawable(ShaderGeometry,copyop) {} + + META_Object(osg,ShaderGeometry); + + typedef std::vector PositionSizeList; + + virtual void drawImplementation(osg::RenderInfo& renderInfo) const; + virtual osg::BoundingBox computeBound() const; + + + void setGeometry(osg::Geometry* geometry) + { + _geometry = geometry; + } + + void addTree(const osg::Vec3& position, float scale) + { + _trees.push_back(osg::Vec4(position, scale)); + } + + osg::ref_ptr _geometry; + + PositionSizeList _trees; + + protected: + + virtual ~ShaderGeometry() {} + +}; + +} +#endif diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx new file mode 100644 index 00000000..dfad0dc4 --- /dev/null +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -0,0 +1,244 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 Stuart Buchanan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#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 + +namespace simgear +{ + +osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate) +{ + + //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f), + // 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::Geometry *geom = new osg::Geometry; + + geom->setVertexArray( &v ); + + geom->setTexCoordArray( 0, &t ); + + geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) ); + + return geom; +} + +osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform) +{ + // 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); + } + + 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); + } + } + + // 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); + } + + return group; +} + +} diff --git a/simgear/scene/tgdb/TreeBin.hxx b/simgear/scene/tgdb/TreeBin.hxx new file mode 100644 index 00000000..48a327fe --- /dev/null +++ b/simgear/scene/tgdb/TreeBin.hxx @@ -0,0 +1,66 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 Stuart Buchanan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifndef TREE_BIN_HXX +#define TREE_BIN_HXX + +#include + +#include +#include +#include + +#include + +namespace simgear +{ +class TreeBin { +public: + struct Tree { + Tree(const SGVec3f& p, string t, float h, float w, double r) : + position(p), texture(t), height(h), width(w), range(r) + { } + SGVec3f position; + string texture; + float height; + float width; + double range; + }; + typedef std::vector TreeList; + + void insert(const Tree& t) + { _trees.push_back(t); } + void insert(const SGVec3f& p, string t, float h, float w, double r) + { insert(Tree(p, t, h, w, r)); } + + unsigned getNumTrees() const + { return _trees.size(); } + const Tree& getTree(unsigned i) const + { return _trees[i]; } + +private: + TreeList _trees; +}; + +osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate); +osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform); +} +#endif diff --git a/simgear/scene/tgdb/obj.cxx b/simgear/scene/tgdb/obj.cxx index 88275137..5fd630d0 100644 --- a/simgear/scene/tgdb/obj.cxx +++ b/simgear/scene/tgdb/obj.cxx @@ -56,6 +56,7 @@ #include "SGTexturedTriangleBin.hxx" #include "SGLightBin.hxx" #include "SGModelBin.hxx" +#include "TreeBin.hxx" #include "SGDirectionalLightBin.hxx" #include "GroundLightManager.hxx" @@ -73,6 +74,7 @@ struct SGTileGeometryBin { SGMaterialTriangleMap materialTriangleMap; SGLightBin tileLights; SGLightBin randomTileLights; + TreeBin randomForest; SGDirectionalLightBin runwayLights; SGDirectionalLightBin taxiLights; SGDirectionalLightListBin vasiLights; @@ -80,7 +82,7 @@ struct SGTileGeometryBin { SGLightListBin odalLights; SGDirectionalLightListBin reilLights; SGMatModelBin randomModels; - + static SGVec4f getMaterialLightColor(const SGMaterial* material) { @@ -448,10 +450,45 @@ struct SGTileGeometryBin { } } } + + void computeRandomForest(SGMaterialLib* matlib) + { + SGMaterialTriangleMap::iterator i; + + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(586)); + + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { + SGMaterial *mat = matlib->find(i->first); + if (!mat) + continue; + + float coverage = mat->get_tree_coverage(); + if (coverage <= 0) + continue; + + vector textures = mat->get_tree_textures(); + + std::vector randomPoints; + i->second.addRandomSurfacePoints(coverage, 0, randomPoints); + std::vector::iterator j; + for (j = randomPoints.begin(); j != randomPoints.end(); ++j) { + int k = mt_rand(&seed) * textures.size(); + if (k == textures.size()) k--; + randomForest.insert(*j, textures[k], mat->get_tree_height(), mat->get_tree_width(), mat->get_tree_range()); + } + } + } void computeRandomObjects(SGMaterialLib* matlib) { SGMaterialTriangleMap::iterator i; + + // generate a repeatable random seed + mt seed; + mt_init(&seed, unsigned(123)); + for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) { SGMaterial *mat = matlib->find(i->first); if (!mat) @@ -478,7 +515,7 @@ struct SGTileGeometryBin { i->second.addRandomPoints(object->get_coverage_m2(), randomPoints); std::vector::iterator l; for (l = randomPoints.begin(); l != randomPoints.end(); ++l) { - randomModels.insert(*l, object); + randomModels.insert(*l, object, object->get_randomized_range_m(&seed)); } } } @@ -517,13 +554,22 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool osg::ref_ptr lightGroup = new SGOffsetTransform(0.94); osg::ref_ptr randomObjects; + osg::ref_ptr randomForest; osg::Group* terrainGroup = new osg::Group; - + osg::Node* node = tileGeometryBin.getSurfaceGeometry(matlib); if (node) terrainGroup->addChild(node); if (use_random_objects) { + + // Simple matrix for used for flipping models that have been oriented + // with the center of the tile but upside down. + static const osg::Matrix flip(1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1); + tileGeometryBin.computeRandomObjects(matlib); if (tileGeometryBin.randomModels.getNumModels() > 0) { @@ -536,12 +582,9 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool // based on the centre of the tile, as the small angular differences // between different points on the tile aren't worth worrying about // for random objects. We also need to flip the orientation 180 degrees - static const osg::Matrix flip(1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, -1, 0, - 0, 0, 0, 1); osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg()); - std::vector > models; + + LodMap models; for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) { SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i); @@ -566,12 +609,21 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool } position->addChild(node); - models.push_back(position); - // Add to the leaf of the quadtree based on object location. + models.insert(std::pair,int>(position, obj.lod)); } + randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile); randomObjects->setName("random objects"); } + + // Now add some random forest. + tileGeometryBin.computeRandomForest(matlib); + + if (tileGeometryBin.randomForest.getNumTrees() > 0) { + osg::Matrix forAtt = flip * world2Tile; + randomForest = createForest(tileGeometryBin.randomForest, forAtt); + randomForest->setName("random trees"); + } } if (calc_lights) { @@ -606,7 +658,7 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool lightGroup->addChild(groundLights2); } } - + if (!tileGeometryBin.vasiLights.empty()) { SGVec4f red(1, 0, 0, 1); SGMaterial* mat = matlib->find("RWY_RED_LIGHTS"); @@ -689,12 +741,15 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool transform->addChild(lightLOD); } - if (randomObjects.valid()) { + if (randomObjects.valid() || randomForest.valid()) { // Add a LoD node, so we don't try to display anything when the tile center // is more than 20km away. osg::LOD* objectLOD = new osg::LOD; - objectLOD->addChild(randomObjects.get(), 0, 20000); + + if (randomObjects.valid()) objectLOD->addChild(randomObjects.get(), 0, 20000); + if (randomForest.valid()) objectLOD->addChild(randomForest.get(), 0, 20000); + unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECIEVESHADOW_BIT; objectLOD->setNodeMask(nodeMask); transform->addChild(objectLOD); diff --git a/simgear/scene/util/QuadTreeBuilder.cxx b/simgear/scene/util/QuadTreeBuilder.cxx index 4fe230c5..b4dcf072 100644 --- a/simgear/scene/util/QuadTreeBuilder.cxx +++ b/simgear/scene/util/QuadTreeBuilder.cxx @@ -16,6 +16,7 @@ #include #include +#include #include "QuadTreeBuilder.hxx" @@ -27,35 +28,36 @@ namespace simgear QuadTreeBuilder::QuadTreeBuilder(const Vec2& min, const Vec2& max) : _root(new osg::Group), _min(min), _max(max) { - for (int i = 0; i < 4; ++i) { + for (int i = 0; i < QUAD_TREE_LEAVES; ++i) { Group* interior = new osg::Group; _root->addChild(interior); - for (int j = 0; j < 4; ++j) { - Group* leaf = new osg::Group; - interior->addChild(leaf); - _leaves[i][j] = leaf; + for (int j = 0; j < QUAD_TREE_LEAVES; ++j) { + LOD* lod = new osg::LOD; + interior->addChild(lod); + _leaves[i][j] = lod; } } } -void QuadTreeBuilder::addNode(Node* node, const Matrix& transform) +void QuadTreeBuilder::addNode(Node* node, int lod, const Matrix& transform) { Vec3 center = node->getBound().center() * transform; - int x = (int)(4.0 * (center.x() - _min.x()) / (_max.x() - _min.x())); - x = clampTo(x, 0, 3); - int y = (int)(4.0 * (center.y() - _min.y()) / (_max.y() - _min.y())); - y = clampTo(y, 0, 3); - _leaves[y][x]->addChild(node); + int x = (int)(QUAD_TREE_LEAVES * (center.x() - _min.x()) / (_max.x() - _min.x())); + x = clampTo(x, 0, (QUAD_TREE_LEAVES - 1)); + int y = (int)(QUAD_TREE_LEAVES * (center.y() - _min.y()) / (_max.y() - _min.y())); + y = clampTo(y, 0, (QUAD_TREE_LEAVES -1)); + + _leaves[y][x]->addChild(node, 0, lod); } -osg::Group* QuadTreeBuilder::makeQuadTree(vector >& nodes, +osg::Group* QuadTreeBuilder::makeQuadTree(LodMap& models, const Matrix& transform) { typedef vector > NodeList; BoundingBox extents; - for (NodeList::iterator iter = nodes.begin(); iter != nodes.end(); ++iter) { - const Vec3 center = (*iter)->getBound().center() * transform; + for (LodMap::iterator iter = models.begin(); iter != models.end(); ++iter) { + const Vec3 center = (*iter).first->getBound().center() * transform; extents.expandBy(center); } const Vec2 quadMin(extents.xMin(), extents.yMin()); @@ -63,10 +65,10 @@ osg::Group* QuadTreeBuilder::makeQuadTree(vector >& nodes, ref_ptr result; { QuadTreeBuilder quadTree(quadMin, quadMax); - for (NodeList::iterator iter = nodes.begin(); - iter != nodes.end(); + for (LodMap::iterator iter = models.begin(); + iter != models.end(); ++iter) { - quadTree.addNode(iter->get(), transform); + quadTree.addNode(iter->first.get(), iter->second, transform); } result = quadTree.getRoot(); } diff --git a/simgear/scene/util/QuadTreeBuilder.hxx b/simgear/scene/util/QuadTreeBuilder.hxx index 6a0e2dd1..ff16fb46 100644 --- a/simgear/scene/util/QuadTreeBuilder.hxx +++ b/simgear/scene/util/QuadTreeBuilder.hxx @@ -22,23 +22,28 @@ #include #include #include +#include + +#define QUAD_TREE_LEAVES 4 namespace simgear { +typedef std::map,int> LodMap; + // Create a quad tree based on x, y extents class QuadTreeBuilder { public: QuadTreeBuilder(const osg::Vec2& min, const osg::Vec2& max); ~QuadTreeBuilder() {} osg::Group* getRoot() { return _root.get(); } - // Add node to the quadtree using its x, y - void addNode(osg::Node* node, const osg::Matrix& transform); - // Make a quadtree of nodes from a vector of nodes - static osg::Group* makeQuadTree(std::vector >& nodes, + // Add node to the quadtree using its x, y and LoD + void addNode(osg::Node* node, int lod, const osg::Matrix& transform); + // Make a quadtree of nodes from a map of nodes and LOD values + static osg::Group* makeQuadTree(LodMap& nodes, const osg::Matrix& transform); protected: osg::ref_ptr _root; - osg::Group* _leaves[4][4]; + osg::LOD* _leaves[QUAD_TREE_LEAVES][QUAD_TREE_LEAVES]; osg::Vec2 _min; osg::Vec2 _max; }; -- 2.39.5