3 * Copyright (C) 2008 Stuart Buchanan
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include <osg/AlphaFunc>
23 #include <osg/Billboard>
24 #include <osg/BlendFunc>
26 #include <osg/Geometry>
27 #include <osg/Material>
29 #include <osg/MatrixTransform>
31 #include <osg/StateSet>
32 #include <osg/Texture2D>
35 #include <osgDB/ReadFile>
36 #include <osgDB/FileUtils>
38 #include <simgear/misc/sg_path.hxx>
40 #include "ShaderGeometry.hxx"
41 #include "TreeBin.hxx"
43 #define SG_TREE_QUAD_TREE_SIZE 32
48 osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
51 //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
53 osg::Vec3Array& v = *(new osg::Vec3Array(8));
54 osg::Vec2Array& t = *(new osg::Vec2Array(8));
57 float rotation = 0.0f;
58 float sw = sinf(rotation)*w*0.5f;
59 float cw = cosf(rotation)*w*0.5f;
61 v[0].set(pos.x()-sw,pos.y()-cw,pos.z()+0.0f);
62 v[1].set(pos.x()+sw,pos.y()+cw,pos.z()+0.0f);
63 v[2].set(pos.x()+sw,pos.y()+cw,pos.z()+h);
64 v[3].set(pos.x()-sw,pos.y()-cw,pos.z()+h);
66 v[4].set(pos.x()-cw,pos.y()+sw,pos.z()+0.0f);
67 v[5].set(pos.x()+cw,pos.y()-sw,pos.z()+0.0f);
68 v[6].set(pos.x()+cw,pos.y()-sw,pos.z()+h);
69 v[7].set(pos.x()-cw,pos.y()+sw,pos.z()+h);
73 v[0].set(0.0f,-cw,0.0f);
74 v[1].set(0.0f, cw,0.0f);
78 v[4].set(-cw,0.0f,0.0f);
79 v[5].set( cw,0.0f,0.0f);
93 for (unsigned int i = 0; i < 8; i++)
98 osg::Geometry *geom = new osg::Geometry;
100 geom->setVertexArray( &v );
102 geom->setTexCoordArray( 0, &t );
104 geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
109 osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform)
111 // Set up some shared structures.
112 // FIXME: Currently we only take the texture, height and width of the first tree in the forest. In the future
113 // we should be able to handle multiple textures etc.
114 TreeBin::Tree firstTree = forest.getTree(0);
116 osg::Geometry* shared_geometry = createOrthQuads(firstTree.width,
119 osg::Group* group = new osg::Group;
121 osg::Texture2D *tex = new osg::Texture2D;
122 tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
123 tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
124 tex->setImage(osgDB::readImageFile(firstTree.texture));
126 osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
127 alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
129 osg::StateSet *dstate = new osg::StateSet;
130 dstate->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
131 dstate->setTextureAttribute(0, new osg::TexEnv );
132 dstate->setAttributeAndModes( new osg::BlendFunc, osg::StateAttribute::ON );
133 dstate->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
134 dstate->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
135 dstate->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
137 osg::StateSet* stateset = new osg::StateSet;
138 stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
139 stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
141 osg::Program* program = new osg::Program;
142 stateset->setAttribute(program);
143 osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
144 stateset->addUniform(baseTextureSampler);
147 * FIXME: Currently, calculating the diffuse term results in a bad
148 * "flickering" and a tendency of the diffuse term be either
149 * 0.0 of 1.0. Hence, it has been commented out in the shader below.
150 * I (Stuart) suspect it may be because the light is so distant that
151 * we're seeing floating point representation issues.
153 char vertexShaderSource[] =
154 // "varying vec3 N;\n"
\r
155 // "varying vec3 v;\n"
156 "varying vec2 texcoord;\n"
157 "varying float fogFactor;\n"
161 // " v = vec3(gl_ModelViewMatrix * gl_Vertex);\n"
\r
162 // " N = normalize(gl_NormalMatrix * gl_Normal);\n"
\r
163 " texcoord = gl_MultiTexCoord0.st;\n"
164 " vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
165 " gl_Position = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
166 " const float LOG2 = 1.442695;\n"
\r
167 " gl_FogFragCoord = gl_Position.z;\n"
\r
168 " fogFactor = exp2( -gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord * LOG2 );\n"
\r
169 " fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
172 char fragmentShaderSource[] =
173 "uniform sampler2D baseTexture; \n"
174 // "varying vec3 N;\n"
\r
175 // "varying vec3 v;\n"
176 "varying vec2 texcoord;\n"
177 "varying float fogFactor;\n"
181 " vec4 base = texture2D( baseTexture, texcoord);\n"
182 // " vec3 L = normalize(gl_LightSource[0].position.xyz);\n"
183 // " vec4 vDiffuse = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);\n"
184 // " vDiffuse = sqrt(clamp(vDiffuse, 0.0, 1.0));\n"
185 // " vec4 vAmbient = gl_FrontLightProduct[0].ambient;\n"
186 // " vec4 finalColor = base * (vAmbient + vDiffuse);\n"
187 " vec4 finalColor = base * gl_FrontLightProduct[0].diffuse;\n"
188 " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
191 osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
192 program->addShader(vertex_shader);
194 osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
195 program->addShader(fragment_shader);
197 // Now, create a quadtree for the forest.
198 osg::ref_ptr<osg::Group> _root;
199 ShaderGeometry* leaves[SG_TREE_QUAD_TREE_SIZE][SG_TREE_QUAD_TREE_SIZE];
201 // Determine the extents of the tree, and a list of the required textures for later.
202 osg::BoundingBox extents;
203 for (unsigned int i = 0; i < forest.getNumTrees(); i++)
205 const osg::Vec3f center = forest.getTree(i).position.osg() * transform;
206 extents.expandBy(center);
209 const osg::Vec2 quadMin(extents.xMin(), extents.yMin());
210 const osg::Vec2 quadMax(extents.xMax(), extents.yMax());
212 for (int i = 0; i < SG_TREE_QUAD_TREE_SIZE; ++i) {
213 osg::LOD* interior = new osg::LOD;
214 //osg::Group* interior = new osg::Group;
215 group->addChild(interior);
216 for (int j = 0; j < SG_TREE_QUAD_TREE_SIZE; ++j) {
217 osg::Geode* geode = new osg::Geode;
218 leaves[i][j] = new ShaderGeometry();
219 leaves[i][j]->setGeometry(shared_geometry);
220 geode->setStateSet(stateset);
221 geode->addDrawable(leaves[i][j]);
222 interior->addChild(geode, 0, firstTree.range);
226 // Now we've got our quadtree, add the trees based on location.
228 for (unsigned int i = 0; i < forest.getNumTrees(); i++)
230 TreeBin::Tree t = forest.getTree(i);
231 osg::Vec3 center = t.position.osg() * transform;
233 int x = (int)(SG_TREE_QUAD_TREE_SIZE * (center.x() - quadMin.x()) / (quadMax.x() - quadMin.x()));
234 x = osg::clampTo(x, 0, (SG_TREE_QUAD_TREE_SIZE - 1));
235 int y = (int)(SG_TREE_QUAD_TREE_SIZE * (center.y() - quadMin.y()) / (quadMax.y() - quadMin.y()));
236 y = osg::clampTo(y, 0, (SG_TREE_QUAD_TREE_SIZE -1));
238 leaves[y][x]->addTree(t.position.osg(), t.height);