+/* -*-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 <osgDB/Registry>
+#include <osgDB/Input>
+#include <osgDB/ParameterOutput>
+
#include "ShaderGeometry.hxx"
using namespace osg;
+using namespace osgDB;
namespace simgear
{
return bb;
}
+bool ShaderGeometry_readLocalData(Object& obj, Input& fr)
+{
+ bool iteratorAdvanced = false;
+
+ ShaderGeometry& geom = static_cast<ShaderGeometry&>(obj);
+
+ if ((fr[0].matchWord("geometry"))) {
+ ++fr;
+ iteratorAdvanced = true;
+ Drawable* drawable = fr.readDrawable();
+ if (drawable) {
+ geom._geometry = drawable;
+ }
+ }
+ if ((fr.matchSequence("instances %i"))) {
+ int entry = fr[0].getNoNestedBrackets();
+ int capacity;
+ fr[1].getInt(capacity);
+ geom._trees.reserve(capacity);
+ fr += 3;
+ iteratorAdvanced = true;
+ // skip {
+ while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) {
+ Vec4 v;
+ if (fr[0].getFloat(v.x()) && fr[1].getFloat(v.y())
+ && fr[2].getFloat(v.z()) && fr[3].getFloat(v.w())) {
+ fr += 4;
+ geom._trees.push_back(v);
+ } else {
+ ++fr;
+ }
+ }
+ }
+ return iteratorAdvanced;
+}
+
+bool ShaderGeometry_writeLocalData(const Object& obj, Output& fw)
+{
+ const ShaderGeometry& geom = static_cast<const ShaderGeometry&>(obj);
+
+ fw.indent() << "geometry" << std::endl;
+ fw.writeObject(*geom._geometry);
+ fw.indent() << "instances " << geom._trees.size() << std::endl;
+ fw.indent() << "{" << std::endl;
+ fw.moveIn();
+ for (ShaderGeometry::PositionSizeList::const_iterator iter
+ = geom._trees.begin();
+ iter != geom._trees.end();
+ ++iter) {
+ fw.indent() << iter->x() << " " << iter->y() << " " << iter->z() << " "
+ << iter->w() << std::endl;
+ }
+ fw.moveOut();
+ fw.indent() << "}" << std::endl;
+ return true;
+}
+
+osgDB::RegisterDotOsgWrapperProxy shaderGeometryProxy
+(
+ new ShaderGeometry,
+ "ShaderGeometry",
+ "Object Drawable ShaderGeometry",
+ &ShaderGeometry_readLocalData,
+ &ShaderGeometry_writeLocalData
+ );
}
ShaderGeometry(const ShaderGeometry& ShaderGeometry,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
osg::Drawable(ShaderGeometry,copyop) {}
- META_Object(osg,ShaderGeometry);
+ META_Object(flightgear, ShaderGeometry);
typedef std::vector<osg::Vec4> PositionSizeList;
virtual osg::BoundingBox computeBound() const;
- void setGeometry(osg::Geometry* geometry)
+ void setGeometry(osg::Drawable* geometry)
{
_geometry = geometry;
}
_trees.push_back(osg::Vec4(position, scale));
}
- osg::ref_ptr<osg::Geometry> _geometry;
+ osg::ref_ptr<osg::Drawable> _geometry;
PositionSizeList _trees;
*
*/
+#include <algorithm>
+#include <string>
+#include <map>
+
#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osgDB/FileUtils>
#include <simgear/misc/sg_path.hxx>
+#include <simgear/scene/util/QuadTreeBuilder.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+#include <simgear/scene/util/StateAttributeFactory.hxx>
#include "ShaderGeometry.hxx"
#include "TreeBin.hxx"
-#define SG_TREE_QUAD_TREE_SIZE 32
+#define SG_TREE_QUAD_TREE_DEPTH 3
+
+// Comments from Tim Moore:
+// Some work remains for this code. Stuart's enhancement for multiple
+// textures per forest should be integrated. We should try to use one
+// ShaderGeometry for *all* the trees in the scene graph and do the
+// rotation and scale with a MatrixTransform above the trees quad
+// tree. The positions would of course have to be transformed by the
+// inverse of that transform. Also, we should investigate whether it
+// would be better to instantiate trees as polygons in a osg::Geometry
+// object instead of using the ShaderGeometry instancing technique.
+
+using namespace osg;
namespace simgear
{
//const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
// set up the coords
+ // Create front and back polygons so we don't need to screw around
+ // with two-sided lighting in the shader.
osg::Vec3Array& v = *(new osg::Vec3Array(8));
+ osg::Vec3Array& n = *(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);
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;
+
+ // For now the normal is normal to the quad. If we want to get
+ // fancier and approximate a cylindrical tree or something, then
+ // we would really want more geometry.
+ std::fill(n.begin(), n.begin() + 4, Vec3f(1.0f, 0.0f, 0.0f));
+ std::fill(n.begin() + 4, n.end(), Vec3f(0.0f, -1.0f, 0.0f));
+ for (unsigned int i = 0; i < 8; i++) {
+ v[i] = v[i] * rotate;
+ // Should be the inverse transpose, but assume that rotate is
+ // orthonormal.
+ n[i] = n[i] * rotate;
}
osg::Geometry *geom = new osg::Geometry;
- geom->setVertexArray( &v );
+ geom->setVertexArray(&v);
+ geom->setTexCoordArray(0, &t);
+ geom->setNormalArray(&n);
+ geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
+ // No color for now; that's used to pass the position.
+ geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8));
- geom->setTexCoordArray( 0, &t );
+ return geom;
+}
- geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
+ static char vertexShaderSource[] =
+ "varying float fogFactor;\n"
+ "\n"
+ "void main(void)\n"
+ "{\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"
+ " fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
+ " fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
+ "}\n";
- return geom;
+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";
+
+typedef std::map<std::string, osg::ref_ptr<StateSet> > StateSetMap;
+
+static StateSetMap treeTextureMap;
+
+// Helper classes for creating the quad tree
+namespace
+{
+struct MakeTreesLeaf
+{
+ MakeTreesLeaf(float range, Geometry* geometry) :
+ _range(range), _geometry(geometry)
+ {}
+ MakeTreesLeaf(const MakeTreesLeaf& rhs) :
+ _range(rhs._range), _geometry(rhs._geometry) {}
+ LOD* operator() () const
+ {
+ LOD* result = new LOD;
+ Geode* geode = new Geode;
+ ShaderGeometry* sg = new ShaderGeometry;
+ sg->setGeometry(_geometry);
+ geode->addDrawable(sg);
+ result->addChild(geode, 0, _range);
+ return result;
+ }
+ float _range;
+ Geometry* _geometry;
+};
+
+struct AddTreesLeafObject
+{
+ void operator() (LOD* lod, const TreeBin::Tree& tree) const
+ {
+ Geode* geode = static_cast<Geode*>(lod->getChild(0));
+ ShaderGeometry* sg
+ = static_cast<ShaderGeometry*>(geode->getDrawable(0));
+ sg->addTree(tree.position.osg(), tree.height);
+ }
+};
+
+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;
+ }
+ Matrix _transform;
+};
+
+typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
+ GetTreeCoord> ShaderGeometryQuadtree;
}
-osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform)
+osg::Group* createForest(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
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);
- }
+ ref_ptr<Group> group;
+
+ osg::StateSet* stateset = 0;
+ StateSetMap::iterator iter = treeTextureMap.find(firstTree.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));
+
+ static ref_ptr<AlphaFunc> alphaFunc;
+ static ref_ptr<Program> program;
+ static ref_ptr<Uniform> baseTextureSampler;
+ static ref_ptr<Material> material;
- 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);
- }
+ stateset = new osg::StateSet;
+ stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
+ stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin");
+ if (!program.valid()) {
+ alphaFunc = new AlphaFunc;
+ alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f);
+ program = new Program;
+ baseTextureSampler = new osg::Uniform("baseTexture", 0);
+ Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
+ program->addShader(vertex_shader);
+ Shader* fragment_shader = new Shader(Shader::FRAGMENT,
+ fragmentShaderSource);
+ program->addShader(fragment_shader);
+ material = new Material;
+ // Don´t track vertex color
+ material->setColorMode(Material::OFF);
+ material->setAmbient(Material::FRONT_AND_BACK,
+ Vec4(.6f, .6f, .6f, 1.0f));
+ material->setDiffuse(Material::FRONT_AND_BACK,
+ Vec4(.4f, .4f, .4f, 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,
+ stateset));
+ } else {
+ stateset = iter->second.get();
}
-
- // Now we've got our quadtree, add the trees based on location.
-
- for (unsigned int i = 0; i < forest.getNumTrees(); i++)
- {
- TreeBin::Tree t = forest.getTree(i);
- osg::Vec3 center = t.position.osg() * transform;
-
- int x = (int)(SG_TREE_QUAD_TREE_SIZE * (center.x() - quadMin.x()) / (quadMax.x() - quadMin.x()));
- x = osg::clampTo(x, 0, (SG_TREE_QUAD_TREE_SIZE - 1));
- int y = (int)(SG_TREE_QUAD_TREE_SIZE * (center.y() - quadMin.y()) / (quadMax.y() - quadMin.y()));
- y = osg::clampTo(y, 0, (SG_TREE_QUAD_TREE_SIZE -1));
-
- leaves[y][x]->addTree(t.position.osg(), t.height);
+ // Now, create a quadtree for the forest.
+ {
+ ShaderGeometryQuadtree quadtree(GetTreeCoord(Matrix::inverse(transform)),
+ AddTreesLeafObject(),
+ SG_TREE_QUAD_TREE_DEPTH,
+ MakeTreesLeaf(firstTree.range,
+ shared_geometry));
+ quadtree.buildQuadTree(forest._trees.begin(), forest._trees.end());
+ group = quadtree.getRoot();
}
-
- return group;
+ group->setStateSet(stateset);
+ return group.release();
}
}
{ 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);
+osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform);
}
#endif
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();
+ int k = (int)(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());
}
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, object->get_randomized_range_m(&seed));
+ randomModels.insert(*l, object, (int)object->get_randomized_range_m(&seed));
}
}
}
}
};
+typedef std::pair<osg::Node*, int> ModelLOD;
+struct MakeQuadLeaf {
+ osg::LOD* operator() () const { return new osg::LOD; }
+};
+struct AddModelLOD {
+ void operator() (osg::LOD* leaf, ModelLOD& mlod) const
+ {
+ leaf->addChild(mlod.first, 0, mlod.second);
+ }
+};
+struct GetModelLODCoord {
+ GetModelLODCoord(const osg::Matrix& transform) : _transform(transform) {}
+ GetModelLODCoord(const GetModelLODCoord& rhs) : _transform(rhs._transform)
+ {}
+ osg::Vec3 operator() (const ModelLOD& mlod) const
+ {
+ return mlod.first->getBound().center() * _transform;
+ }
+ osg::Matrix _transform;
+};
+
+typedef QuadTreeBuilder<osg::LOD*, ModelLOD, MakeQuadLeaf, AddModelLOD,
+ GetModelLODCoord> RandomObjectsQuadtree;
+
osg::Node*
SGLoadBTG(const std::string& path, SGMaterialLib *matlib, bool calc_lights, bool use_random_objects)
{
SGGeod geodPos = SGGeod::fromCart(center);
SGQuatd hlOr = SGQuatd::fromLonLat(geodPos);
SGVec3f up = toVec3f(hlOr.backTransform(SGVec3d(0, 0, -1)));
- osg::Matrix world2Tile(-hlOr.osg());
GroundLightManager* lightManager = GroundLightManager::instance();
osg::ref_ptr<osg::Group> lightGroup = new SGOffsetTransform(0.94);
0, -1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1);
+ // Determine an rotation matrix for the models to place them
+ // perpendicular to the earth's surface. We use the same matrix,
+ // 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
+ osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
+ // The inverse goes from world coordinates to Z up tile coordinates.
+ osg::Matrix world2Tile(osg::Matrix(hlOr.osg().conj()) * flip);
tileGeometryBin.computeRandomObjects(matlib);
// Generate a repeatable random seed
mt seed;
mt_init(&seed, unsigned(123));
-
- // Determine an rotation matrix for the models to place them
- // perpendicular to the earth's surface. We use the same matrix,
- // 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
- osg::Matrix mAtt = flip * osg::Matrix::rotate(hlOr.osg());
-
- LodMap models;
-
+
+ std::vector<ModelLOD> models;
for (unsigned int i = 0; i < tileGeometryBin.randomModels.getNumModels(); i++) {
SGMatModelBin::MatModel obj = tileGeometryBin.randomModels.getMatModel(i);
osg::Node* node = sgGetRandomModel(obj.model);
}
position->addChild(node);
- models.insert(std::pair<osg::ref_ptr<osg::Node>,int>(position, obj.lod));
+ models.push_back(ModelLOD(position, obj.lod));
}
-
- randomObjects = QuadTreeBuilder::makeQuadTree(models, world2Tile);
+ RandomObjectsQuadtree quadtree((GetModelLODCoord(world2Tile)),
+ (AddModelLOD()));
+ quadtree.buildQuadTree(models.begin(), models.end());
+ randomObjects = quadtree.getRoot();
randomObjects->setName("random objects");
}
tileGeometryBin.computeRandomForest(matlib);
if (tileGeometryBin.randomForest.getNumTrees() > 0) {
- osg::Matrix forAtt = flip * world2Tile;
- randomForest = createForest(tileGeometryBin.randomForest, forAtt);
+ randomForest = createForest(tileGeometryBin.randomForest, mAtt);
randomForest->setName("random trees");
}
}
namespace simgear
{
-QuadTreeBuilder::QuadTreeBuilder(const Vec2& min, const Vec2& max) :
- _root(new osg::Group), _min(min), _max(max)
+#if 0
+QuadTreeBuilder::QuadTreeBuilder(const Vec2& min, const Vec2& max, int depth) :
+ _root(new osg::Group), _min(min), _max(max), _depth(depth),
+ _dimension(1 << depth), _leafStorage(_dimension * _dimension),
+ _leaves(_leafStorage, _dimension)
{
- for (int i = 0; i < QUAD_TREE_LEAVES; ++i) {
- Group* interior = new osg::Group;
- _root->addChild(interior);
- for (int j = 0; j < QUAD_TREE_LEAVES; ++j) {
- LOD* lod = new osg::LOD;
- interior->addChild(lod);
- _leaves[i][j] = lod;
+ for (LeafVector::iterator iter = _leafStorage.begin();
+ iter != _leafStorage.end();
+ ++iter)
+ *iter = new LOD;
+ vector<Group*> parentNodes(1);
+ parentNodes[0] = _root.get();
+ unsigned leafDim = 2;
+ for (int i = 0; i < depth - 1; ++i, leafDim *= 2) {
+ VectorArrayAdapter<vector<Group*> > parents(parentNodes, leafDim / 2);
+ vector<Group*> interiorNodes(leafDim * leafDim);
+ VectorArrayAdapter<vector<Group*> > interiors(interiorNodes, leafDim);
+ for (unsigned j = 0; j < leafDim; ++j) {
+ for (unsigned k = 0; k < leafDim; ++k) {
+ interiors(j, k) = new Group;
+ parents(j / 2, k / 2)->addChild(interiors(j, k));
+ }
}
+ parentNodes.swap(interiorNodes);
}
+ VectorArrayAdapter<vector<Group*> > leafParents(parentNodes,
+ _dimension / 2);
+ for (int j = 0; j < _dimension; ++j)
+ for (int k =0; k < _dimension; ++k)
+ leafParents(j / 2, k / 2)->addChild(_leaves(j, k));
}
void QuadTreeBuilder::addNode(Node* node, int lod, const Matrix& transform)
{
Vec3 center = node->getBound().center() * transform;
- 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));
+ int x = (int)(_dimension * (center.x() - _min.x()) / (_max.x() - _min.x()));
+ x = clampTo(x, 0, (_dimension - 1));
+ int y = (int)(_dimension * (center.y() - _min.y()) / (_max.y() - _min.y()));
+ y = clampTo(y, 0, (_dimension -1));
- _leaves[y][x]->addChild(node, 0, lod);
+ _leaves(y, x)->addChild(node, 0, lod);
}
osg::Group* QuadTreeBuilder::makeQuadTree(LodMap& models,
}
return result.release();
}
-
+#endif
}
#ifndef SIMGEAR_QUADTREEBUILDER_HXX
#define SIMGEAR_QUADTREEBUILDER_HXX 1
+#include <algorithm>
+#include <functional>
#include <vector>
+#include <osg/BoundingBox>
#include <osg/ref_ptr>
#include <osg/Node>
#include <osg/Group>
#include <osg/Matrix>
#include <osg/Vec2>
#include <osg/LOD>
-
-#define QUAD_TREE_LEAVES 4
+#include "VectorArrayAdapter.hxx"
namespace simgear
{
typedef std::map<osg::ref_ptr<osg::Node>,int> LodMap;
// Create a quad tree based on x, y extents
+template <typename LeafType, typename ObjectType, typename MakeLeaf,
+ typename AddLeafObject, typename GetObjectLocalCoords>
class QuadTreeBuilder {
public:
- QuadTreeBuilder(const osg::Vec2& min, const osg::Vec2& max);
+ QuadTreeBuilder(const GetObjectLocalCoords& getLocalCoords,
+ const AddLeafObject& addLeafObject, int depth = 2,
+ const MakeLeaf& makeLeaf = MakeLeaf()) :
+ _root(new osg::Group), _depth(depth),
+ _dimension(1 << depth), _leafStorage(_dimension * _dimension),
+ _leaves(_leafStorage, _dimension), _getLocalCoords(getLocalCoords),
+ _addLeafObject(addLeafObject), _makeLeaf(makeLeaf)
+ {
+ using namespace std;
+ using namespace osg;
+ generate(_leafStorage.begin(), _leafStorage.end(), _makeLeaf);
+ vector<Group*> parentNodes(1);
+ parentNodes[0] = _root.get();
+ unsigned leafDim = 2;
+ for (int i = 0; i < depth - 1; ++i, leafDim *= 2) {
+ VectorArrayAdapter<vector<Group*> > parents(parentNodes, leafDim / 2);
+ vector<Group*> interiorNodes(leafDim * leafDim);
+ VectorArrayAdapter<vector<Group*> > interiors(interiorNodes, leafDim);
+ for (unsigned j = 0; j < leafDim; ++j) {
+ for (unsigned k = 0; k < leafDim; ++k) {
+ interiors(j, k) = new Group;
+ parents(j / 2, k / 2)->addChild(interiors(j, k));
+ }
+ }
+ parentNodes.swap(interiorNodes);
+ }
+ VectorArrayAdapter<vector<Group*> > leafParents(parentNodes,
+ _dimension / 2);
+ for (int j = 0; j < _dimension; ++j)
+ for (int k =0; k < _dimension; ++k)
+ leafParents(j / 2, k / 2)->addChild(_leaves(j, k));
+ }
+ osg::Vec2 getMin() { return _min; }
+ void setMin(const osg::Vec2& min) { _min = min; }
+ osg::Vec2 getMax() { return _max; }
+ void setMax(const osg::Vec2& max) { _max = max; }
~QuadTreeBuilder() {}
osg::Group* getRoot() { return _root.get(); }
- // Add node to the quadtree using its x, y and LoD
- void addNode(osg::Node* node, int lod, const osg::Matrix& transform);
+
+ void addNode(ObjectType& obj)
+ {
+ using namespace osg;
+ const Vec3 center(_getLocalCoords(obj));
+ int x = (int)(_dimension * (center.x() - _min.x())
+ / (_max.x() - _min.x()));
+ x = clampTo(x, 0, (_dimension - 1));
+ int y = (int)(_dimension * (center.y() - _min.y())
+ / (_max.y() - _min.y()));
+ y = clampTo(y, 0, (_dimension -1));
+ _addLeafObject(_leaves(y, x), obj);
+ }
+ // STL craziness
+ struct AddNode
+ {
+ AddNode(QuadTreeBuilder* qt) : _qt(qt) {}
+ AddNode(const AddNode& rhs) : _qt(rhs._qt) {}
+ void operator() (ObjectType& obj) const { _qt->addNode(obj); }
+ QuadTreeBuilder *_qt;
+ };
// Make a quadtree of nodes from a map of nodes and LOD values
- static osg::Group* makeQuadTree(LodMap& nodes,
- const osg::Matrix& transform);
+ template <typename ForwardIterator>
+ void buildQuadTree(const ForwardIterator& begin,
+ const ForwardIterator& end)
+ {
+ using namespace osg;
+ BoundingBox extents;
+ for (ForwardIterator iter = begin; iter != end; ++iter) {
+ const Vec3 center = _getLocalCoords(*iter);
+ extents.expandBy(center);
+ }
+ _min = Vec2(extents.xMin(), extents.yMin());
+ _max = Vec2(extents.xMax(), extents.yMax());
+ std::for_each(begin, end, AddNode(this));
+ }
+
protected:
+ typedef std::vector<LeafType> LeafVector;
osg::ref_ptr<osg::Group> _root;
- osg::LOD* _leaves[QUAD_TREE_LEAVES][QUAD_TREE_LEAVES];
osg::Vec2 _min;
osg::Vec2 _max;
+ int _depth;
+ int _dimension;
+ LeafVector _leafStorage;
+ VectorArrayAdapter<LeafVector> _leaves;
+ const GetObjectLocalCoords _getLocalCoords;
+ const AddLeafObject _addLeafObject;
+ const MakeLeaf _makeLeaf;
};
+
}
#endif
//
// Normal opaque objects are assigned bin 0.
//
+// Random objects like trees may have transparency, but there are too
+// many to depth sort. By drawing them after the terrain we can at
+// least keep the sky under the ground from poking through.
+//
// Point lights blend with the terrain to simulate attenuation but
// should completely obscure any transparent geometry behind
// them. Also, they should be visible through semi-transparent cloud
// OSG and its file loaders throw all transparent objects into bin 10.
enum RenderBin {
+ RANDOM_OBJECTS_BIN = 2,
POINT_LIGHTS_BIN = 8,
CLOUDS_BIN = 9,
TRANSPARENT_BIN = 10 // assigned by OSG
_whiteTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_whiteTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_whiteTexture->setDataVariance(osg::Object::STATIC);
+ _white = new Vec4Array(1);
+ (*_white)[0].set(1.0f, 1.0f, 1.0f, 1.0f);
+ _white->setDataVariance(Object::STATIC);
+ _cullFaceBack = new CullFace(CullFace::BACK);
+ _cullFaceBack->setDataVariance(Object::STATIC);
}
osg::ref_ptr<StateAttributeFactory> StateAttributeFactory::_theInstance;
#include <OpenThreads/Mutex>
#include <osg/ref_ptr>
#include <osg/AlphaFunc>
+#include <osg/Array>
#include <osg/BlendFunc>
+#include <osg/CullFace>
#include <osg/ShadeModel>
#include <osg/Texture2D>
#include <osg/TexEnv>
osg::ShadeModel* getFlatShadeModel() { return _flat.get(); }
// White, repeating texture
osg::Texture2D* getWhiteTexture() { return _whiteTexture.get(); }
+ // White color
+ osg::Vec4Array* getWhiteColor() {return _white.get(); }
+ // cull back facing polygons
+ osg::CullFace* getCullFaceBack() { return _cullFaceBack.get(); }
+
static StateAttributeFactory* instance();
protected:
StateAttributeFactory();
osg::ref_ptr<osg::BlendFunc> _standardBlendFunc;
osg::ref_ptr<osg::TexEnv> _standardTexEnv;
osg::ref_ptr<osg::Texture2D> _whiteTexture;
+ osg::ref_ptr<osg::Vec4Array> _white;
+ osg::ref_ptr<osg::CullFace> _cullFaceBack;
static osg::ref_ptr<StateAttributeFactory> _theInstance;
static OpenThreads::Mutex _instanceMutex;
};
#ifndef VECTORARRAYADAPTERHXX
#define VECTORARRAYADAPTERHXX 1
+// #define SG_CHECK_VECTOR_ACCESS 1
namespace simgear
{
template <typename Vector>
_rowOffset(rowOffset)
{
}
-
+#ifdef SG_CHECK_VECTOR_ACCESS
+ typename Vector::value_type& operator() (int i, int j)
+ {
+ return _v.at(_baseOffset + i * _rowStride + _rowOffset + j);
+ }
+ const typename Vector::value_type& operator() (int i, int j) const
+ {
+ return _v.at(_baseOffset + i * _rowStride + _rowOffset + j);
+ }
+#else
typename Vector::value_type& operator() (int i, int j)
{
return _v[_baseOffset + i * _rowStride + _rowOffset + j];
{
return _v[_baseOffset + i * _rowStride + _rowOffset + j];
}
+#endif
private:
Vector& _v;
const int _rowStride;