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);
for (unsigned int i = 0; i < object_group_nodes.size(); i++)
object_groups.push_back(new SGMatModelGroup(object_group_nodes[i]));
+ vector<SGPropertyNode_ptr> 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<SGPropertyNode_ptr> glyph_nodes = props->getChildren("glyph");
for (unsigned int i = 0; i < glyph_nodes.size(); i++) {
*
* 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<string> 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.
// 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;
// taxiway-/runway-sign texture elements
map<string, SGSharedPtr<SGMaterialGlyph> > glyphs;
-
+
+ // The list of forest textures, used when creating trees
+ vector<string> tree_textures;
\f
////////////////////////////////////////////////////////////////////
// Internal constructors and methods.
// 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,
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]);
}
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
{
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/props/props.hxx>
+#include <simgear/math/sg_random.h>
SG_USING_STD(string);
*/
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.
SGTexturedTriangleBin.hxx \
SGTriangleBin.hxx \
SGVertexArrayBin.hxx \
- GroundLightManager.hxx
+ GroundLightManager.hxx \
+ ShaderGeometry.hxx
libsgtgdb_a_SOURCES = \
apt_signs.cxx \
SGOceanTile.cxx \
SGReaderWriterBTG.cxx \
SGVasiDrawable.cxx \
- GroundLightManager.cxx
+ GroundLightManager.cxx \
+ ShaderGeometry.cxx \
+ TreeBin.cxx
INCLUDES = -I$(top_srcdir)
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<MatModel> 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<float>::min(min_x, x);
- max_x = SGMisc<float>::max(max_x, x);
-
- min_y = SGMisc<float>::min(min_y, y);
- max_y = SGMisc<float>::max(max_y, y);
-
- min_z = SGMisc<float>::min(min_z, z);
- max_z = SGMisc<float>::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(); }
private:
MatModelList _models;
- float min_x;
- float max_x;
- float min_y;
- float max_y;
- float min_z;
- float max_z;
};
#endif
--- /dev/null
+#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;
+}
+
+}
--- /dev/null
+/* -*-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 <vector>
+
+#include <osg/BoundingBox>
+#include <osg/CopyOp>
+#include <osg/Drawable>
+#include <osg/Geometry>
+#include <osg/RenderInfo>
+#include <osg/Vec3>
+#include <osg/Vec4>
+
+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<osg::Vec4> 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<osg::Geometry> _geometry;
+
+ PositionSizeList _trees;
+
+ protected:
+
+ virtual ~ShaderGeometry() {}
+
+};
+
+}
+#endif
--- /dev/null
+/* -*-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 <osg/AlphaFunc>
+#include <osg/Billboard>
+#include <osg/BlendFunc>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Material>
+#include <osg/Math>
+#include <osg/MatrixTransform>
+#include <osg/Matrix>
+#include <osg/StateSet>
+#include <osg/Texture2D>
+#include <osg/TexEnv>
+
+#include <osgDB/ReadFile>
+#include <osgDB/FileUtils>
+
+#include <simgear/misc/sg_path.hxx>
+
+#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"\r
+// "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"\r
+// " N = normalize(gl_NormalMatrix * gl_Normal);\n"\r
+ " 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"\r
+ " gl_FogFragCoord = gl_Position.z;\n"\r
+ " fogFactor = exp2( -gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord * LOG2 );\n"\r
+ " fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
+ "}\n";
+
+ char fragmentShaderSource[] =
+ "uniform sampler2D baseTexture; \n"
+// "varying vec3 N;\n"\r
+// "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<osg::Group> _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;
+}
+
+}
--- /dev/null
+/* -*-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 <vector>
+
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/Matrix>
+
+#include <simgear/math/SGVec3.hxx>
+
+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<Tree> 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
#include "SGTexturedTriangleBin.hxx"
#include "SGLightBin.hxx"
#include "SGModelBin.hxx"
+#include "TreeBin.hxx"
#include "SGDirectionalLightBin.hxx"
#include "GroundLightManager.hxx"
SGMaterialTriangleMap materialTriangleMap;
SGLightBin tileLights;
SGLightBin randomTileLights;
+ TreeBin randomForest;
SGDirectionalLightBin runwayLights;
SGDirectionalLightBin taxiLights;
SGDirectionalLightListBin vasiLights;
SGLightListBin odalLights;
SGDirectionalLightListBin reilLights;
SGMatModelBin randomModels;
-
+
static SGVec4f
getMaterialLightColor(const SGMaterial* material)
{
}
}
}
+
+ 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<string> textures = mat->get_tree_textures();
+
+ std::vector<SGVec3f> randomPoints;
+ i->second.addRandomSurfacePoints(coverage, 0, randomPoints);
+ std::vector<SGVec3f>::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)
i->second.addRandomPoints(object->get_coverage_m2(), randomPoints);
std::vector<SGVec3f>::iterator l;
for (l = randomPoints.begin(); l != randomPoints.end(); ++l) {
- randomModels.insert(*l, object);
+ randomModels.insert(*l, object, object->get_randomized_range_m(&seed));
}
}
}
osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
osg::ref_ptr<osg::Group> randomObjects;
+ osg::ref_ptr<osg::Group> 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) {
// 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<osg::ref_ptr<osg::Node> > models;
+
+ LodMap models;
for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) {
SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i);
}
position->addChild(node);
- models.push_back(position);
- // Add to the leaf of the quadtree based on object location.
+ models.insert(std::pair<osg::ref_ptr<osg::Node>,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) {
lightGroup->addChild(groundLights2);
}
}
-
+
if (!tileGeometryBin.vasiLights.empty()) {
SGVec4f red(1, 0, 0, 1);
SGMaterial* mat = matlib->find("RWY_RED_LIGHTS");
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);
#include <osg/BoundingBox>
#include <osg/Math>
+#include <simgear/scene/util/SGNodeMasks.hxx>
#include "QuadTreeBuilder.hxx"
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<ref_ptr<Node> >& nodes,
+osg::Group* QuadTreeBuilder::makeQuadTree(LodMap& models,
const Matrix& transform)
{
typedef vector<ref_ptr<Node> > 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());
ref_ptr<Group> 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();
}
#include <osg/Group>
#include <osg/Matrix>
#include <osg/Vec2>
+#include <osg/LOD>
+
+#define QUAD_TREE_LEAVES 4
namespace simgear
{
+typedef std::map<osg::ref_ptr<osg::Node>,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<osg::ref_ptr<osg::Node> >& 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<osg::Group> _root;
- osg::Group* _leaves[4][4];
+ osg::LOD* _leaves[QUAD_TREE_LEAVES][QUAD_TREE_LEAVES];
osg::Vec2 _min;
osg::Vec2 _max;
};