]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/TreeBin.cxx
Random trees from Stuart Buchanan
[simgear.git] / simgear / scene / tgdb / TreeBin.cxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2008 Stuart Buchanan
4  *
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.
9  *
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.
14  *
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,
18  * MA 02110-1301, USA.
19  *
20  */
21
22 #include <osg/AlphaFunc>
23 #include <osg/Billboard>
24 #include <osg/BlendFunc>
25 #include <osg/Geode>
26 #include <osg/Geometry>
27 #include <osg/Material>
28 #include <osg/Math>
29 #include <osg/MatrixTransform>
30 #include <osg/Matrix>
31 #include <osg/StateSet>
32 #include <osg/Texture2D>
33 #include <osg/TexEnv>
34
35 #include <osgDB/ReadFile>
36 #include <osgDB/FileUtils>
37
38 #include <simgear/misc/sg_path.hxx>
39
40 #include "ShaderGeometry.hxx"
41 #include "TreeBin.hxx"
42
43 #define SG_TREE_QUAD_TREE_SIZE 32
44
45 namespace simgear
46 {
47
48 osg::Geometry* createOrthQuads(float w, float h, const osg::Matrix& rotate)
49 {
50
51     //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
52     // set up the coords
53     osg::Vec3Array& v = *(new osg::Vec3Array(8));
54     osg::Vec2Array& t = *(new osg::Vec2Array(8));
55     
56     /*
57     float rotation = 0.0f;
58     float sw = sinf(rotation)*w*0.5f;
59     float cw = cosf(rotation)*w*0.5f;
60
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);
65
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);
70     */
71     float cw = w*0.5f;
72
73     v[0].set(0.0f,-cw,0.0f);
74     v[1].set(0.0f, cw,0.0f);
75     v[2].set(0.0f, cw,h);
76     v[3].set(0.0f,-cw,h);
77
78     v[4].set(-cw,0.0f,0.0f);
79     v[5].set( cw,0.0f,0.0f);
80     v[6].set( cw,0.0f,h);
81     v[7].set(-cw,0.0f,h);
82
83     t[0].set(0.0f,0.0f);
84     t[1].set(1.0f,0.0f);
85     t[2].set(1.0f,1.0f);
86     t[3].set(0.0f,1.0f);
87
88     t[4].set(0.0f,0.0f);
89     t[5].set(1.0f,0.0f);
90     t[6].set(1.0f,1.0f);
91     t[7].set(0.0f,1.0f);
92     
93     for (unsigned int i = 0; i < 8; i++)
94     {
95       v[i] = v[i] * rotate;      
96     }
97
98     osg::Geometry *geom = new osg::Geometry;
99
100     geom->setVertexArray( &v );
101
102     geom->setTexCoordArray( 0, &t );
103
104     geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8) );
105
106     return geom;
107 }
108
109 osg::Group* createForest(const TreeBin& forest, const osg::Matrix& transform)
110 {
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);
115     
116     osg::Geometry* shared_geometry = createOrthQuads(firstTree.width, 
117                                                      firstTree.height, 
118                                                      transform);
119     osg::Group* group = new osg::Group;
120
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));
125
126     osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
127     alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
128
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 );
136     
137     osg::StateSet* stateset = new osg::StateSet;
138     stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
139     stateset->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
140
141     osg::Program* program = new osg::Program;
142     stateset->setAttribute(program);
143     osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
144     stateset->addUniform(baseTextureSampler);
145
146     /*
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.
152      */
153     char vertexShaderSource[] = 
154 //        "varying vec3 N;\n"\r
155 //        "varying vec3 v;\n"
156         "varying vec2 texcoord;\n"
157         "varying float fogFactor;\n"
158         "\n"
159         "void main(void)\n"
160         "{\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"
170         "}\n";
171
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"
178         "\n"
179         "void main(void) \n"
180         "{ \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"
189         "}\n";
190
191     osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
192     program->addShader(vertex_shader);
193     
194     osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
195     program->addShader(fragment_shader);
196    
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];
200
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++)
204     {    
205         const osg::Vec3f center = forest.getTree(i).position.osg() * transform;
206         extents.expandBy(center);
207     }
208     
209     const osg::Vec2 quadMin(extents.xMin(), extents.yMin());
210     const osg::Vec2 quadMax(extents.xMax(), extents.yMax());
211
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);        
223       }
224     }
225     
226     // Now we've got our quadtree, add the trees based on location.
227     
228     for (unsigned int i = 0; i < forest.getNumTrees(); i++)
229     {    
230       TreeBin::Tree t = forest.getTree(i);
231       osg::Vec3 center = t.position.osg() * transform;
232
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));
237     
238       leaves[y][x]->addTree(t.position.osg(), t.height);
239     }
240     
241     return group;
242 }
243
244 }