*/
#include <algorithm>
+#include <vector>
#include <string>
#include <map>
+#include <boost/tuple/tuple_comparison.hpp>
+
#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
namespace simgear
{
-osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
+// memoize geometry
+typedef boost::tuple<float, float, int> ForestTuple;
+typedef std::map<ForestTuple, ref_ptr<Geometry> > OrthQuadMap;
+
+osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate)
{
+ static OrthQuadMap orthQuadMap;
+ OrthQuadMap::iterator giter
+ = orthQuadMap.find(ForestTuple(w, h, varieties));
+ if (giter != orthQuadMap.end())
+ return giter->second.get();
//const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
// set up the coords
v[6].set( cw,0.0f,h);
v[7].set(-cw,0.0f,h);
+ // The texture coordinate range is not the
+ // entire coordinate space - as the texture
+ // has a number of different trees on it.
+ float tx = 1.0f/varieties;
+
t[0].set(0.0f,0.0f);
- t[1].set(1.0f,0.0f);
- t[2].set(1.0f,1.0f);
+ t[1].set( tx,0.0f);
+ t[2].set( tx,1.0f);
t[3].set(0.0f,1.0f);
t[4].set(0.0f,0.0f);
- t[5].set(1.0f,0.0f);
- t[6].set(1.0f,1.0f);
+ t[5].set( tx,0.0f);
+ t[6].set( tx,1.0f);
t[7].set(0.0f,1.0f);
// For now the normal is normal to the quad. If we want to get
// No color for now; that's used to pass the position.
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8));
+ orthQuadMap.insert(std::make_pair(ForestTuple(w, h, varieties), geom));
return geom;
}
static char vertexShaderSource[] =
"varying float fogFactor;\n"
+ "attribute float textureIndex;\n"
"\n"
"void main(void)\n"
"{\n"
+ " gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndex, 0.0, 0.0, 0.0);\n"
" vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
" gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
" vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n"
- " vec3 N = normalize(gl_NormalMatrix * gl_Normal);\n"
- " vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(N, gl_LightSource[0].position.xyz));\n"
- " vec3 backDiffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(-N, gl_LightSource[0].position.xyz));\n"
- " vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
- " gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
- " gl_BackColor = ambientColor + gl_LightSource[0].diffuse * vec4(backDiffuse, 1.0)\n;"
- " gl_TexCoord[0] = gl_MultiTexCoord0;\n"
- " float fogCoord = abs(ecPosition.z);\n"
+ " float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(-ecPosition));\n"
+ " vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.1, n);\n"
+ " vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
+ " gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
+ " gl_BackColor = gl_FrontColor;\n"
+ " float fogCoord = abs(ecPosition.z);\n"
" fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
" fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
"}\n";
static char fragmentShaderSource[] =
"uniform sampler2D baseTexture; \n"
-// "varying vec3 N;\n"\r
-// "varying vec3 v;\n"
"varying float fogFactor;\n"
"\n"
"void main(void) \n"
"{ \n"
" vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
-
" vec4 finalColor = base * gl_Color;\n"
" gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
"}\n";
{
struct MakeTreesLeaf
{
- MakeTreesLeaf(float range, Geometry* geometry) :
- _range(range), _geometry(geometry)
+ MakeTreesLeaf(float range, Geometry* geometry, int varieties) :
+ _range(range), _geometry(geometry), _varieties(varieties)
{}
MakeTreesLeaf(const MakeTreesLeaf& rhs) :
- _range(rhs._range), _geometry(rhs._geometry) {}
+ _range(rhs._range), _geometry(rhs._geometry), _varieties(rhs._varieties) {}
LOD* operator() () const
{
LOD* result = new LOD;
Geode* geode = new Geode;
- ShaderGeometry* sg = new ShaderGeometry;
+ ShaderGeometry* sg = new ShaderGeometry(_varieties);
sg->setGeometry(_geometry);
geode->addDrawable(sg);
result->addChild(geode, 0, _range);
return result;
}
float _range;
+ int _varieties;
Geometry* _geometry;
};
Geode* geode = static_cast<Geode*>(lod->getChild(0));
ShaderGeometry* sg
= static_cast<ShaderGeometry*>(geode->getDrawable(0));
- sg->addTree(tree.position.osg(), tree.height);
+ sg->addTree(tree);
}
};
struct GetTreeCoord
{
- GetTreeCoord(const Matrix& transform) : _transform(transform) {}
- GetTreeCoord(const GetTreeCoord& rhs) : _transform(rhs._transform) {}
Vec3 operator() (const TreeBin::Tree& tree) const
{
- return tree.position.osg() * _transform;
+ return tree.position.osg();
}
- Matrix _transform;
};
typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
GetTreeCoord> ShaderGeometryQuadtree;
}
+struct TreeTransformer
+{
+ TreeTransformer(Matrix& mat_) : mat(mat_) {}
+ TreeBin::Tree operator()(const TreeBin::Tree& tree) const
+ {
+ const Vec3& pos = tree.position.osg();
+ return TreeBin::Tree(SGVec3f(pos * mat), tree.texture_index,
+ tree.scale);
+ }
+ Matrix mat;
+};
+
+// This actually returns a MatrixTransform node. If we rotate the whole
+// forest into the local Z-up coordinate system we can reuse the
+// primitive tree geometry for all the forests of the same type.
+
osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
{
+ Matrix transInv = Matrix::inverse(transform);
+ static Matrix ident;
// Set up some shared structures.
- // FIXME: Currently we only take the texture, height and width of the first tree in the forest. In the future
- // we should be able to handle multiple textures etc.
- TreeBin::Tree firstTree = forest.getTree(0);
-
- osg::Geometry* shared_geometry = createOrthQuads(firstTree.width,
- firstTree.height,
- transform);
+ osg::Geometry* shared_geometry = createOrthQuads(forest.width,
+ forest.height,
+ forest.texture_varieties,
+ ident);
+
ref_ptr<Group> group;
osg::StateSet* stateset = 0;
- StateSetMap::iterator iter = treeTextureMap.find(firstTree.texture);
+ StateSetMap::iterator iter = treeTextureMap.find(forest.texture);
if (iter == treeTextureMap.end()) {
osg::Texture2D *tex = new osg::Texture2D;
tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
- tex->setImage(osgDB::readImageFile(firstTree.texture));
+ tex->setImage(osgDB::readImageFile(forest.texture));
static ref_ptr<AlphaFunc> alphaFunc;
static ref_ptr<Program> program;
baseTextureSampler = new osg::Uniform("baseTexture", 0);
Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
program->addShader(vertex_shader);
+ program->addBindAttribLocation("textureIndex", 1);
+
Shader* fragment_shader = new Shader(Shader::FRAGMENT,
fragmentShaderSource);
program->addShader(fragment_shader);
// DonĀ“t track vertex color
material->setColorMode(Material::OFF);
material->setAmbient(Material::FRONT_AND_BACK,
- Vec4(.6f, .6f, .6f, 1.0f));
+ Vec4(1.0f, 1.0f, 1.0f, 1.0f));
material->setDiffuse(Material::FRONT_AND_BACK,
- Vec4(.4f, .4f, .4f, 1.0f));
+ Vec4(1.0f, 1.0f, 1.0f, 1.0f));
}
stateset->setAttributeAndModes(alphaFunc.get());
stateset->setAttribute(program.get());
stateset->addUniform(baseTextureSampler.get());
stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
stateset->setAttribute(material.get());
- // XXX This should really come from a material definition
- // instead of being hard-coded.
- treeTextureMap.insert(StateSetMap::value_type(firstTree.texture,
+
+ treeTextureMap.insert(StateSetMap::value_type(forest.texture,
stateset));
} else {
stateset = iter->second.get();
}
// Now, create a quadtree for the forest.
{
- ShaderGeometryQuadtree quadtree(GetTreeCoord(Matrix::inverse(transform)),
+ ShaderGeometryQuadtree quadtree(GetTreeCoord(),
AddTreesLeafObject(),
SG_TREE_QUAD_TREE_DEPTH,
- MakeTreesLeaf(firstTree.range,
- shared_geometry));
- quadtree.buildQuadTree(forest._trees.begin(), forest._trees.end());
+ MakeTreesLeaf(forest.range,
+ shared_geometry,
+ forest.texture_varieties));
+ // Transform tree positions from the "geocentric" positions we
+ // get from the scenery polys into the local Z-up coordinate
+ // system.
+ std::vector<TreeBin::Tree> rotatedTrees;
+ rotatedTrees.reserve(forest._trees.size());
+ std::transform(forest._trees.begin(), forest._trees.end(),
+ std::back_inserter(rotatedTrees),
+ TreeTransformer(transInv));
+ quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
group = quadtree.getRoot();
}
- group->setStateSet(stateset);
- return group.release();
+ MatrixTransform* mt = new MatrixTransform(transform);
+ for (int i = 0; i < group->getNumChildren(); ++i)
+ mt->addChild(group->getChild(i));
+ mt->setStateSet(stateset);
+ return mt;
}
}