Random trees from Stuart Buchanan

Stuart's new file SGTreeBin.hxx has been split into 4 files:
TreeBin.[ch]xx and ShaderGeometry.[ch]xx.
This commit is contained in:
timoore
2008-02-02 23:01:05 +00:00
parent a098ba5e79
commit 4b63bc051e
13 changed files with 629 additions and 124 deletions

View File

@@ -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<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++) {

View File

@@ -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<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.
@@ -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<string, SGSharedPtr<SGMaterialGlyph> > glyphs;
// The list of forest textures, used when creating trees
vector<string> tree_textures;
////////////////////////////////////////////////////////////////////
// Internal constructors and methods.

View File

@@ -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
{

View File

@@ -40,6 +40,7 @@
#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);
@@ -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.

View File

@@ -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)

View File

@@ -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<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(); }
@@ -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

View File

@@ -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;
}
}

View File

@@ -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 <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

View File

@@ -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 <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"
// "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<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;
}
}

View File

@@ -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 <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

View File

@@ -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<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)
@@ -478,7 +515,7 @@ struct SGTileGeometryBin {
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));
}
}
}
@@ -517,13 +554,22 @@ SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool
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) {
@@ -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<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);
@@ -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<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) {
@@ -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);

View File

@@ -16,6 +16,7 @@
#include <osg/BoundingBox>
#include <osg/Math>
#include <simgear/scene/util/SGNodeMasks.hxx>
#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<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());
@@ -63,10 +65,10 @@ osg::Group* QuadTreeBuilder::makeQuadTree(vector<ref_ptr<Node> >& nodes,
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();
}

View File

@@ -22,23 +22,28 @@
#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;
};