]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/SGBuildingBin.cxx
Add support for double-sided taxiway signs and create 3D models for them
[simgear.git] / simgear / scene / tgdb / SGBuildingBin.cxx
1 /* -*-c++-*-
2  *
3  * Copyright (C) 2012 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 #ifdef HAVE_CONFIG_H
23 #  include <simgear_config.h>
24 #endif
25
26 #include <algorithm>
27 #include <vector>
28 #include <string>
29 #include <map>
30
31 #include <boost/foreach.hpp>
32 #include <boost/tuple/tuple_comparison.hpp>
33
34 #include <osg/Geode>
35 #include <osg/Geometry>
36 #include <osg/Math>
37 #include <osg/MatrixTransform>
38 #include <osg/Matrix>
39 #include <osg/ShadeModel>
40 #include <osg/Material>
41 #include <osg/CullFace>
42
43 #include <osgDB/ReadFile>
44 #include <osgDB/FileUtils>
45
46 #include <simgear/debug/logstream.hxx>
47 #include <simgear/math/sg_random.h>
48 #include <simgear/misc/sg_path.hxx>
49 #include <simgear/scene/material/Effect.hxx>
50 #include <simgear/scene/material/EffectGeode.hxx>
51 #include <simgear/scene/model/model.hxx>
52 #include <simgear/props/props.hxx>
53 #include <simgear/scene/util/QuadTreeBuilder.hxx>
54 #include <simgear/scene/util/RenderConstants.hxx>
55 #include <simgear/scene/util/StateAttributeFactory.hxx>
56 #include <simgear/structure/OSGUtils.hxx>
57
58
59 #include "ShaderGeometry.hxx"
60 #include "SGBuildingBin.hxx"
61
62 #define SG_BUILDING_QUAD_TREE_DEPTH 2
63 #define SG_BUILDING_FADE_OUT_LEVELS 4
64
65 using namespace osg;
66
67 namespace simgear
68 {
69   
70 typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSetMap;
71 static BuildingStateSetMap statesetmap;
72
73 void addBuildingToLeafGeode(Geode* geode, const SGBuildingBin::Building& building)
74 {
75       // Generate a repeatable random seed
76       mt seed;
77       mt_init(&seed, unsigned(building.position.x()));      
78       
79       // Get or create geometry.
80       osg::ref_ptr<osg::Geometry> geom;
81       osg::Vec3Array* v = new osg::Vec3Array;
82       osg::Vec2Array* t = new osg::Vec2Array;
83       osg::Vec4Array* c = new osg::Vec4Array; // single value
84       osg::Vec3Array* n = new osg::Vec3Array;            
85       
86       if (geode->getNumDrawables() == 0) {
87         geom = new osg::Geometry;        
88         v = new osg::Vec3Array;
89         t = new osg::Vec2Array;
90         c = new osg::Vec4Array;
91         n = new osg::Vec3Array;
92         
93         // Set the color, which is bound overall, and simply white
94         c->push_back( osg::Vec4( 1, 1, 1, 1) );
95         geom->setColorArray(c);
96         geom->setColorBinding(osg::Geometry::BIND_OVERALL);
97
98         geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
99         // Temporary primitive set. Will be over-written later.
100         geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,1));
101         geode->addDrawable(geom);
102       } else {
103         geom = (osg::Geometry*) geode->getDrawable(0);        
104         v = (osg::Vec3Array*) geom->getVertexArray();
105         t = (osg::Vec2Array*) geom->getTexCoordArray(0);
106         c = (osg::Vec4Array*) geom->getColorArray();
107         n = (osg::Vec3Array*) geom->getNormalArray();
108       }
109       
110       // For the moment we'll create a simple box with 5 sides (no need 
111       // for a base).
112       int num_quads = 5;    
113       
114       if (building.pitched) {
115         // If it's a pitched roof, we add another 3 quads (we'll be
116         // removing the flat top).
117         num_quads+=3;        
118       }          
119
120       // Set up the rotation and translation matrix, which we apply to
121       // vertices as they are created as we'll be adding buildings later.
122       osg::Matrix transformMat;
123       transformMat = osg::Matrix::translate(toOsg(building.position));
124       double hdg =  - building.rotation * M_PI * 2;
125       osg::Matrix rotationMat = osg::Matrix::rotate(hdg,
126                                                osg::Vec3d(0.0, 0.0, 1.0));
127       transformMat.preMult(rotationMat);                  
128
129       // Create the vertices
130       float cw = 0.5f * building.width;
131       float cd = building.depth;
132       float ch = building.height;
133       
134       // 0,0,0 is the bottom center of the front
135       // face, e.g. where the front door would be      
136       
137       // BASEMENT
138       // This exteds 10m below the main section
139       // Front face        
140       v->push_back( osg::Vec3( 0,  cw, -10) * transformMat ); // bottom right
141       v->push_back( osg::Vec3( 0, -cw, -10) * transformMat ); // bottom left
142       v->push_back( osg::Vec3( 0, -cw,   0) * transformMat ); // top left
143       v->push_back( osg::Vec3( 0,  cw,   0) * transformMat ); // top right
144       
145       for (int i=0; i<4; ++i)
146         n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
147       
148       // Left face
149       v->push_back( osg::Vec3(  0, -cw, -10) * transformMat ); // bottom right
150       v->push_back( osg::Vec3( cd, -cw, -10) * transformMat ); // bottom left
151       v->push_back( osg::Vec3( cd, -cw,   0) * transformMat ); // top left
152       v->push_back( osg::Vec3(  0, -cw,   0) * transformMat ); // top right
153
154       for (int i=0; i<4; ++i)
155         n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
156
157       // Back face
158       v->push_back( osg::Vec3( cd, -cw, -10) * transformMat ); // bottom right
159       v->push_back( osg::Vec3( cd,  cw, -10) * transformMat ); // bottom left
160       v->push_back( osg::Vec3( cd,  cw,   0) * transformMat ); // top left
161       v->push_back( osg::Vec3( cd, -cw,   0) * transformMat ); // top right
162       
163       for (int i=0; i<4; ++i)
164         n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
165       
166       // Right face
167       v->push_back( osg::Vec3( cd, cw, -10) * transformMat ); // bottom right
168       v->push_back( osg::Vec3(  0, cw, -10) * transformMat ); // bottom left
169       v->push_back( osg::Vec3(  0, cw,   0) * transformMat ); // top left
170       v->push_back( osg::Vec3( cd, cw,   0) * transformMat ); // top right
171
172       for (int i=0; i<4; ++i)
173         n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal      
174       
175       // MAIN BODY
176       // Front face        
177       v->push_back( osg::Vec3( 0,  cw,  0) * transformMat ); // bottom right
178       v->push_back( osg::Vec3( 0, -cw,  0) * transformMat ); // bottom left
179       v->push_back( osg::Vec3( 0, -cw, ch) * transformMat ); // top left
180       v->push_back( osg::Vec3( 0,  cw, ch) * transformMat ); // top right
181       
182       for (int i=0; i<4; ++i)
183         n->push_back( osg::Vec3(-1, 0, 0) * rotationMat ); // normal
184       
185       // Left face
186       v->push_back( osg::Vec3(  0, -cw,  0) * transformMat ); // bottom right
187       v->push_back( osg::Vec3( cd, -cw,  0) * transformMat ); // bottom left
188       v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top left
189       v->push_back( osg::Vec3(  0, -cw, ch) * transformMat ); // top right
190
191       for (int i=0; i<4; ++i)
192         n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
193
194       // Back face
195       v->push_back( osg::Vec3( cd, -cw,  0) * transformMat ); // bottom right
196       v->push_back( osg::Vec3( cd,  cw,  0) * transformMat ); // bottom left
197       v->push_back( osg::Vec3( cd,  cw, ch) * transformMat ); // top left
198       v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top right
199       
200       for (int i=0; i<4; ++i)
201         n->push_back( osg::Vec3(1, 0, 0) * rotationMat ); // normal
202       
203       // Right face
204       v->push_back( osg::Vec3( cd, cw,  0) * transformMat ); // bottom right
205       v->push_back( osg::Vec3(  0, cw,  0) * transformMat ); // bottom left
206       v->push_back( osg::Vec3(  0, cw, ch) * transformMat ); // top left
207       v->push_back( osg::Vec3( cd, cw, ch) * transformMat ); // top right
208
209       for (int i=0; i<4; ++i)
210         n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
211       
212       // ROOF
213       if (building.pitched) {      
214         
215         // Front pitched roof
216         v->push_back( osg::Vec3(     0,  cw,   ch) * transformMat ); // bottom right
217         v->push_back( osg::Vec3(     0, -cw,   ch) * transformMat ); // bottom left
218         v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top left
219         v->push_back( osg::Vec3(0.5*cd,  cw, ch+3) * transformMat ); // top right
220         
221         for (int i=0; i<4; ++i)
222           n->push_back( osg::Vec3(-0.707, 0, 0.707) * rotationMat ); // normal
223         
224         // Left pitched roof
225         v->push_back( osg::Vec3(     0, -cw,   ch) * transformMat ); // bottom right
226         v->push_back( osg::Vec3(    cd, -cw,   ch) * transformMat ); // bottom left
227         v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top left
228         v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top right
229         
230         for (int i=0; i<4; ++i)
231           n->push_back( osg::Vec3(0, -1, 0) * rotationMat ); // normal
232
233         // Back pitched roof
234         v->push_back( osg::Vec3(    cd, -cw,   ch) * transformMat ); // bottom right
235         v->push_back( osg::Vec3(    cd,  cw,   ch) * transformMat ); // bottom left
236         v->push_back( osg::Vec3(0.5*cd,  cw, ch+3) * transformMat ); // top left
237         v->push_back( osg::Vec3(0.5*cd, -cw, ch+3) * transformMat ); // top right
238         
239         for (int i=0; i<4; ++i)
240           n->push_back( osg::Vec3(0.707, 0, 0.707) * rotationMat ); // normal      
241
242         // Right pitched roof
243         v->push_back( osg::Vec3(    cd, cw,   ch) * transformMat ); // bottom right
244         v->push_back( osg::Vec3(     0, cw,   ch) * transformMat ); // bottom left
245         v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top left
246         v->push_back( osg::Vec3(0.5*cd, cw, ch+3) * transformMat ); // top right
247         
248         for (int i=0; i<4; ++i)
249           n->push_back( osg::Vec3(0, 1, 0) * rotationMat ); // normal
250       } else {      
251         // Top face
252         v->push_back( osg::Vec3(  0,  cw, ch) * transformMat ); // bottom right
253         v->push_back( osg::Vec3(  0, -cw, ch) * transformMat ); // bottom left
254         v->push_back( osg::Vec3( cd, -cw, ch) * transformMat ); // top left
255         v->push_back( osg::Vec3( cd,  cw, ch) * transformMat ); // top right
256         
257         for (int i=0; i<4; ++i)
258           n->push_back( osg::Vec3( 0, 0, 1) * rotationMat ); // normal
259       }
260       
261       // The 1024x1024 texture is split into 32x16 blocks.
262       // For a small building, each block is 6m wide and 3m high.
263       // For a medium building, each block is 10m wide and 3m high.
264       // For a large building, each block is 20m wide and 3m high
265       
266       if (building.type == SGBuildingBin::SMALL) {
267         // Small buildings are represented on the bottom 5 rows of 3 floors
268         int row = ((int) (mt_rand(&seed) * 1000)) % 5;
269         float base_y = (float) row * 16.0 * 3.0 / 1024.0;
270         float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
271         float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
272         float right_x = 0.0f;
273         float front_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f);
274         float back_x = 384.0/1024.0;
275
276         // BASEMENT - uses the baseline texture
277         for (unsigned int i = 0; i < 16; i++) {          
278           t->push_back( osg::Vec2( left_x, base_y) ); 
279         }
280         // MAIN BODY
281         // Front
282         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
283         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
284         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
285         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
286         
287         // Left
288         t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
289         t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
290         t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
291         t->push_back( osg::Vec2( front_x, top_y ) ); // top right
292         
293         // Back (same as front for the moment)
294         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
295         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
296         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
297         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
298         
299         // Right (same as left for the moment)
300         t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
301         t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
302         t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
303         t->push_back( osg::Vec2( front_x, top_y ) ); // top right
304
305         // ROOF
306         if (building.pitched) { 
307           // Use the entire height of the roof texture
308           top_y = base_y + 16.0 * 3.0 / 1024.0;     
309           left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
310           right_x = 512/1024.0;
311           front_x = 512.0/1024.0;
312           back_x = 480.0/1024.0;
313           
314           // Front
315           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
316           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
317           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
318           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
319           
320           // Left
321           t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
322           t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
323           t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
324           t->push_back( osg::Vec2( front_x, top_y ) ); // top right
325           
326           // Back (same as front for the moment)
327           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
328           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
329           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
330           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
331           
332           // Right (same as left for the moment)
333           t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
334           t->push_back( osg::Vec2( back_x,  base_y) ); // bottom left
335           t->push_back( osg::Vec2( back_x,  top_y ) ); // top left
336           t->push_back( osg::Vec2( front_x, top_y ) ); // top right
337         } else {
338           // Flat roof
339           left_x = 640.0/1024.0;
340           right_x = 512.0/1024.0;
341           // Use the entire height of the roof texture
342           top_y = base_y + 16.0 * 3.0 / 1024.0;    
343           
344           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
345           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
346           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
347           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
348         }
349         
350       }
351       
352       if (building.type == SGBuildingBin::MEDIUM) 
353       {
354         int column = ((int) (mt_rand(&seed) * 1000)) % 5;        
355         float base_y = 288 / 1024.0;
356         float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
357         float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
358         float right_x = column * 192.0 /1024.0;
359
360         // BASEMENT - uses the baseline texture
361         for (unsigned int i = 0; i < 16; i++) {          
362           t->push_back( osg::Vec2( left_x, base_y) ); 
363         }      
364
365         // MAIN BODY
366         // Front
367         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
368         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
369         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
370         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
371         
372         // Left
373         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
374         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
375         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
376         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
377         
378         // Back (same as front for the moment)
379         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
380         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
381         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
382         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
383         
384         // Right (same as left for the moment)
385         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
386         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
387         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
388         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
389
390         // ROOF
391         if (building.pitched) {      
392           base_y = 288.0/1024.0;
393           top_y = 576.0/1024.0;
394           left_x = 1.0;
395           right_x = 960.0/1024.0;
396           // Front
397           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
398           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
399           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
400           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
401           
402           // Left
403           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
404           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
405           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
406           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
407             
408           // Back (same as front for the moment)
409           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
410           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
411           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
412           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
413           
414           // Right (same as left for the moment)
415           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
416           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
417           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
418           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
419         } else {
420           // Flat roof
421           base_y = 416/1024.0;
422           top_y = 576.0/1024.0;
423           left_x = (column + 1)* 192.0 /1024.0;
424           right_x = column * 192.0 /1024.0;
425           //right_x = left_x + 32.0 / 1024.0 * 6.0;
426           
427           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
428           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
429           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
430           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
431         }
432       }
433
434       if (building.type == SGBuildingBin::LARGE)
435       {
436         int column = ((int) (mt_rand(&seed) * 1000)) % 8;        
437         float base_y = 576 / 1024.0;
438         float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
439         float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
440         float right_x = column * 128.0 /1024.0; 
441
442         // BASEMENT - uses the baseline texture
443         for (unsigned int i = 0; i < 16; i++) {          
444           t->push_back( osg::Vec2( left_x, base_y) ); 
445         }      
446
447         // MAIN BODY
448         // Front
449         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
450         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
451         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
452         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
453         
454         // Left
455         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
456         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
457         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
458         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
459         
460         // Back (same as front for the moment)
461         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
462         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
463         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
464         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
465         
466         // Right (same as left for the moment)
467         t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
468         t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
469         t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
470         t->push_back( osg::Vec2( right_x, top_y ) ); // top right
471
472         // ROOF
473         if (building.pitched) {      
474           base_y = 896/1024.0;
475           top_y = 1.0;
476           // Front
477           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
478           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
479           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
480           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
481           
482           // Left
483           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
484           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
485           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
486           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
487             
488           // Back (same as front for the moment)
489           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
490           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
491           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
492           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
493           
494           // Right (same as left for the moment)
495           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
496           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
497           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
498           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
499         } else {
500           // Flat roof
501           base_y = 896/1024.0;
502           top_y = 1.0;
503           
504           t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
505           t->push_back( osg::Vec2( left_x,  base_y) ); // bottom left
506           t->push_back( osg::Vec2( left_x,  top_y ) ); // top left
507           t->push_back( osg::Vec2( right_x, top_y ) ); // top right
508         }
509
510       }
511
512       // Set the vertex, texture and normals back.
513       geom->setVertexArray(v);
514       geom->setTexCoordArray(0, t);
515       geom->setNormalArray(n);
516       
517       geom->setPrimitiveSet(0, new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,v->size()));
518       geode->setDrawable(0, geom);      
519 }
520
521 typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
522
523 static EffectMap buildingEffectMap;
524
525 // Helper classes for creating the quad tree
526 namespace
527 {
528 struct MakeBuildingLeaf
529 {
530     MakeBuildingLeaf(float range, Effect* effect) :
531         _range(range), _effect(effect) {}
532     
533     MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
534         _range(rhs._range), _effect(rhs._effect)
535     {}
536
537     LOD* operator() () const
538     {
539         LOD* result = new LOD;
540         
541         // Create a series of LOD nodes so trees cover decreases slightly
542         // gradually with distance from _range to 2*_range
543         for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++)
544         {   
545             EffectGeode* geode = new EffectGeode;
546             geode->setEffect(_effect.get());
547             result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));               
548         }
549         return result;
550     }
551     
552     float _range;
553     ref_ptr<Effect> _effect;
554 };
555
556 struct AddBuildingLeafObject
557 {
558     void operator() (LOD* lod, const SGBuildingBin::Building& building) const
559     {
560         Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
561         addBuildingToLeafGeode(geode, building);
562     }
563 };
564
565 struct GetBuildingCoord
566 {
567     Vec3 operator() (const SGBuildingBin::Building& building) const
568     {
569         return toOsg(building.position);
570     }
571 };
572
573 typedef QuadTreeBuilder<LOD*, SGBuildingBin::Building, MakeBuildingLeaf, AddBuildingLeafObject,
574                         GetBuildingCoord> BuildingGeometryQuadtree;
575 }
576
577 struct BuildingTransformer
578 {
579     BuildingTransformer(Matrix& mat_) : mat(mat_) {}
580     SGBuildingBin::Building operator()(const SGBuildingBin::Building& building) const
581     {
582         Vec3 pos = toOsg(building.position);
583         return SGBuildingBin::Building(toSG(pos * mat), building);
584     }
585     Matrix mat;
586 };
587
588
589
590 // This actually returns a MatrixTransform node. If we rotate the whole
591 // forest into the local Z-up coordinate system we can reuse the
592 // primitive building geometry for all the forests of the same type.
593 osg::Group* createRandomBuildings(SGBuildingBinList buildings, const osg::Matrix& transform,
594                          const SGReaderWriterOptions* options)
595 {
596     Matrix transInv = Matrix::inverse(transform);
597     static Matrix ident;
598     // Set up some shared structures.
599     MatrixTransform* mt = new MatrixTransform(transform);
600
601     SGBuildingBin* bin = NULL;
602       
603     BOOST_FOREACH(bin, buildings)
604     {      
605       
606         ref_ptr<Effect> effect;
607         EffectMap::iterator iter = buildingEffectMap.find(bin->texture);
608
609         if ((iter == buildingEffectMap.end())||
610             (!iter->second.lock(effect)))
611         {
612             SGPropertyNode_ptr effectProp = new SGPropertyNode;
613             makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
614             SGPropertyNode* params = makeChild(effectProp, "parameters");
615             // Main texture - n=0
616             params->getChild("texture", 0, true)->getChild("image", 0, true)
617                 ->setStringValue(bin->texture);
618
619             // Light map - n=1
620             params->getChild("texture", 1, true)->getChild("image", 0, true)
621                 ->setStringValue(bin->lightMap);
622                 
623             effect = makeEffect(effectProp, true, options);
624             if (iter == buildingEffectMap.end())
625                 buildingEffectMap.insert(EffectMap::value_type(bin->texture, effect));
626             else
627                 iter->second = effect; // update existing, but empty observer
628         }
629       
630         // Now, create a quadbuilding for the buildings.            
631         BuildingGeometryQuadtree
632             quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
633                      SG_BUILDING_QUAD_TREE_DEPTH,
634                      MakeBuildingLeaf(20000.0f, effect)); // FIXME - tie to property
635                      
636         // Transform building positions from the "geocentric" positions we
637         // get from the scenery polys into the local Z-up coordinate
638         // system.
639         std::vector<SGBuildingBin::Building> rotatedBuildings;
640         rotatedBuildings.reserve(bin->buildings.size());
641         std::transform(bin->buildings.begin(), bin->buildings.end(),
642                        std::back_inserter(rotatedBuildings),
643                        BuildingTransformer(transInv));
644         quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
645         
646         ref_ptr<Group> group = quadbuilding.getRoot();
647         
648         mt->addChild(group);        
649     }
650     
651     return mt;
652 }
653
654 }