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 #include <math.h>
31 
32 #include <osg/Geode>
33 #include <osg/Geometry>
34 #include <osg/Math>
35 #include <osg/MatrixTransform>
36 #include <osg/Matrix>
37 #include <osg/ShadeModel>
38 #include <osg/Material>
39 #include <osg/CullFace>
40 
41 #include <osgDB/ReadFile>
42 #include <osgDB/FileUtils>
43 
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/io/iostreams/sgstream.hxx>
46 #include <simgear/math/SGLimits.hxx>
47 #include <simgear/math/SGMisc.hxx>
48 #include <simgear/math/sg_random.h>
49 #include <simgear/misc/sg_path.hxx>
50 #include <simgear/scene/model/model.hxx>
51 #include <simgear/props/props.hxx>
52 
53 #include "ShaderGeometry.hxx"
54 #include "SGBuildingBin.hxx"
55 
56 using namespace osg;
57 
58 namespace simgear
59 {
60 
61 typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSetMap;
62 static BuildingStateSetMap statesetmap;
63 
64 typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
65 static EffectMap buildingEffectMap;
66 
67 // Helper classes for creating the quad tree
68 struct MakeBuildingLeaf
69 {
MakeBuildingLeafsimgear::MakeBuildingLeaf70     MakeBuildingLeaf(float range, Effect* effect, bool fade) :
71         _range(range), _effect(effect), _fade_out(fade) {}
72 
MakeBuildingLeafsimgear::MakeBuildingLeaf73     MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
74         _range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out)
75     {}
76 
operator ()simgear::MakeBuildingLeaf77     LOD* operator() () const
78     {
79         osg::Vec3Array* v = new osg::Vec3Array;
80         osg::Vec2Array* t = new osg::Vec2Array;
81         osg::Vec3Array* n = new osg::Vec3Array;
82         osg::Vec4Array* c = new osg::Vec4Array;
83         // Color array is used to identify the different building faces by the
84         // vertex shader for texture mapping:
85         // (front, roof, roof top vertex, side)
86 
87         v->reserve(52);
88         t->reserve(52);
89         n->reserve(52);
90         c->reserve(52);
91 
92         // Now create an OSG Geometry based on the Building
93         // 0,0,0 is the bottom center of the front
94         // face, e.g. where the front door would be
95 
96         // BASEMENT
97         // This extends 10m below the main section
98         // Front face
99         v->push_back( osg::Vec3( 0.0, -0.5, -1.0) ); // bottom right
100         v->push_back( osg::Vec3( 0.0,  0.5, -1.0) ); // bottom left
101         v->push_back( osg::Vec3( 0.0,  0.5,  0.0) ); // top left
102         v->push_back( osg::Vec3( 0.0, -0.5,  0.0) ); // top right
103 
104         for (int i=0; i<4; ++i) {
105           n->push_back( osg::Vec3(1, 0, 0) );    // normal
106           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
107           t->push_back( osg::Vec2( 0.0, 0.0) );
108         }
109 
110         // Left face
111         v->push_back( osg::Vec3( -1.0, -0.5, -1.0) ); // bottom right
112         v->push_back( osg::Vec3(  0.0, -0.5, -1.0) ); // bottom left
113         v->push_back( osg::Vec3(  0.0, -0.5,  0.0) ); // top left
114         v->push_back( osg::Vec3( -1.0, -0.5,  0.0) ); // top right
115 
116         for (int i=0; i<4; ++i) {
117           n->push_back( osg::Vec3(0, -1, 0) );   // normal
118           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
119           t->push_back( osg::Vec2( 0.0, 0.0) );
120         }
121 
122         // Back face
123         v->push_back( osg::Vec3( -1.0,  0.5, -1.0) ); // bottom right
124         v->push_back( osg::Vec3( -1.0, -0.5, -1.0) ); // bottom left
125         v->push_back( osg::Vec3( -1.0, -0.5,  0.0) ); // top left
126         v->push_back( osg::Vec3( -1.0,  0.5,  0.0) ); // top right
127 
128         for (int i=0; i<4; ++i) {
129           n->push_back( osg::Vec3(-1, 0, 0) );   // normal
130           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
131           t->push_back( osg::Vec2( 0.0, 0.0) );
132         }
133 
134         // Right face
135         v->push_back( osg::Vec3(  0.0, 0.5, -1.0) ); // bottom right
136         v->push_back( osg::Vec3( -1.0, 0.5, -1.0) ); // bottom left
137         v->push_back( osg::Vec3( -1.0, 0.5,  0.0) ); // top left
138         v->push_back( osg::Vec3(  0.0, 0.5,  0.0) ); // top right
139 
140         for (int i=0; i<4; ++i) {
141           n->push_back( osg::Vec3(0, 1, 0) );    // normal
142           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
143           t->push_back( osg::Vec2( 0.0, 0.0) );
144         }
145 
146         // MAIN BODY
147         // Front face
148         v->push_back( osg::Vec3( 0.0, -0.5, 0.0) ); // bottom right
149         v->push_back( osg::Vec3( 0.0,  0.5, 0.0) ); // bottom left
150         v->push_back( osg::Vec3( 0.0,  0.5, 1.0) ); // top left
151         v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // top right
152 
153         t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom right
154         t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left
155         t->push_back( osg::Vec2( 0.0, 1.0) ); // top left
156         t->push_back( osg::Vec2( 1.0, 1.0) ); // top right
157 
158         for (int i=0; i<4; ++i) {
159           n->push_back( osg::Vec3(1, 0, 0) );    // normal
160           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
161         }
162 
163         // Left face
164         v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // bottom right
165         v->push_back( osg::Vec3(  0.0, -0.5, 0.0) ); // bottom left
166         v->push_back( osg::Vec3(  0.0, -0.5, 1.0) ); // top left
167         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right
168 
169         t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom right
170         t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left
171         t->push_back( osg::Vec2( 1.0, 1.0) ); // top left
172         t->push_back( osg::Vec2( 0.0, 1.0) ); // top right
173 
174         for (int i=0; i<4; ++i) {
175           n->push_back( osg::Vec3(0, -1, 0) );    // normal
176           c->push_back( osg::Vec4(0, 0, 0, 1) ); // color - used to differentiate wall from roof
177         }
178 
179         // Back face
180         v->push_back( osg::Vec3( -1.0,  0.5, 0.0) ); // bottom right
181         v->push_back( osg::Vec3( -1.0, -0.5, 0.0) ); // bottom left
182         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top left
183         v->push_back( osg::Vec3( -1.0,  0.5, 1.0) ); // top right
184 
185         t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom right
186         t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom left
187         t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top left
188         t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top right
189 
190         for (int i=0; i<4; ++i) {
191           n->push_back( osg::Vec3(-1, 0, 0) );    // normal
192           c->push_back( osg::Vec4(1, 0, 0, 0) ); // color - used to differentiate wall from roof
193         }
194 
195         // Right face
196         v->push_back( osg::Vec3(  0.0, 0.5, 0.0) ); // bottom right
197         v->push_back( osg::Vec3( -1.0, 0.5, 0.0) ); // bottom left
198         v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // top left
199         v->push_back( osg::Vec3(  0.0, 0.5, 1.0) ); // top right
200 
201         t->push_back( osg::Vec2( 0.0, 0.0) ); // bottom right
202         t->push_back( osg::Vec2( 1.0, 0.0) ); // bottom left
203         t->push_back( osg::Vec2( 1.0, 1.0 ) ); // top left
204         t->push_back( osg::Vec2( 0.0, 1.0 ) ); // top right
205 
206         for (int i=0; i<4; ++i) {
207           n->push_back( osg::Vec3(0, 1, 0) );    // normal
208           c->push_back( osg::Vec4(0, 0, 0, 1) ); // color - used to differentiate wall from roof
209         }
210 
211         // ROOF 1 - built as a block.  The shader will deform it to the correct shape.
212         // Front face
213         v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // bottom right
214         v->push_back( osg::Vec3( 0.0,  0.5, 1.0) ); // bottom left
215         v->push_back( osg::Vec3( 0.0,  0.5, 1.0) ); // top left
216         v->push_back( osg::Vec3( 0.0, -0.5, 1.0) ); // top right
217 
218         t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom right
219         t->push_back( osg::Vec2(  0.0, 0.0) ); // bottom left
220         t->push_back( osg::Vec2(  0.0, 1.0) ); // top left
221         t->push_back( osg::Vec2( -1.0, 1.0) ); // top right
222 
223         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
224         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
225         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
226         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
227 
228         for (int i=0; i<4; ++i) {
229           n->push_back( osg::Vec3(0.707, 0, 0.707) );    // normal
230         }
231 
232         // Left face
233         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // bottom right
234         v->push_back( osg::Vec3(  0.0, -0.5, 1.0) ); // bottom left
235         v->push_back( osg::Vec3(  0.0, -0.5, 1.0) ); // top left
236         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right
237 
238         t->push_back( osg::Vec2(  0.0, 0.0) ); // bottom right
239         t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom left
240         t->push_back( osg::Vec2( -1.0, 1.0) ); // top left
241         t->push_back( osg::Vec2(  0.0, 1.0) ); // top right
242 
243         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
244         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
245         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
246         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
247 
248         for (int i=0; i<4; ++i) {
249           n->push_back( osg::Vec3(0, -0.707, 0.707) );    // normal
250         }
251 
252         // Back face
253         v->push_back( osg::Vec3( -1.0,  0.5, 1.0) ); // bottom right
254         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // bottom left
255         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top left
256         v->push_back( osg::Vec3( -1.0,  0.5, 1.0) ); // top right
257 
258         t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom right
259         t->push_back( osg::Vec2(  0.0, 0.0) ); // bottom left
260         t->push_back( osg::Vec2(  0.0, 1.0 ) ); // top left
261         t->push_back( osg::Vec2( -1.0, 1.0 ) ); // top right
262 
263         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
264         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
265         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
266         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
267 
268         for (int i=0; i<4; ++i) {
269           n->push_back( osg::Vec3(-0.707, 0, 0.707) );    // normal
270         }
271 
272         // Right face
273         v->push_back( osg::Vec3(  0.0, 0.5, 1.0) ); // bottom right
274         v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // bottom left
275         v->push_back( osg::Vec3( -1.0, 0.5, 1.0) ); // top left
276         v->push_back( osg::Vec3(  0.0, 0.5, 1.0) ); // top right
277 
278         t->push_back( osg::Vec2(  0.0, 0.0) ); // bottom right
279         t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom left
280         t->push_back( osg::Vec2( -1.0, 1.0 ) ); // top left
281         t->push_back( osg::Vec2(  0.0, 1.0 ) ); // top right
282 
283         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
284         c->push_back( osg::Vec4(0, 1, 0, 0) ); // color - used to differentiate wall from roof
285         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
286         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
287 
288         for (int i=0; i<4; ++i) {
289           n->push_back( osg::Vec3(0, 0.707, 0.707) );    // normal
290         }
291 
292         // Top face
293         v->push_back( osg::Vec3(  0.0, -0.5, 1.0) ); // bottom right
294         v->push_back( osg::Vec3(  0.0,  0.5, 1.0) ); // bottom left
295         v->push_back( osg::Vec3( -1.0,  0.5, 1.0) ); // top left
296         v->push_back( osg::Vec3( -1.0, -0.5, 1.0) ); // top right
297 
298         t->push_back( osg::Vec2( -1.0, 0.0) ); // bottom right
299         t->push_back( osg::Vec2(  0.0, 0.0) ); // bottom left
300         t->push_back( osg::Vec2(  0.0, 1.0) ); // top left
301         t->push_back( osg::Vec2( -1.0, 1.0) ); // top right
302 
303         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
304         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
305         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
306         c->push_back( osg::Vec4(0, 1, 1, 0) ); // color - used to differentiate wall from roof
307 
308         for (int i=0; i<4; ++i) {
309           n->push_back( osg::Vec3(0, 0, -1.0) );    // normal
310         }
311 
312         assert(v->size() == 52);
313         assert(t->size() == 52);
314         assert(c->size() == 52);
315         assert(n->size() == 52);
316 
317         Geometry* geom = new Geometry;
318         static int buildingCounter = 0;
319         geom->setName("BuildingGeometry_" + std::to_string(buildingCounter++));
320         geom->setVertexArray(v);
321         geom->setTexCoordArray(0, t, Array::BIND_PER_VERTEX);
322         geom->setNormalArray(n, Array::BIND_PER_VERTEX);
323         geom->setColorArray(c, Array::BIND_PER_VERTEX);
324         geom->setUseDisplayList( false );
325         geom->setUseVertexBufferObjects( true );
326         geom->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback);
327 
328         geom->setVertexAttribArray(BUILDING_POSITION_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX);
329         geom->setVertexAttribArray(BUILDING_SCALE_ATTR, new osg::Vec3Array, Array::BIND_PER_VERTEX);
330         geom->setVertexAttribArray(BUILDING_ATTR1, new osg::Vec3Array, Array::BIND_PER_VERTEX);
331         geom->setVertexAttribArray(BUILDING_ATTR2, new osg::Vec3Array, Array::BIND_PER_VERTEX);
332 
333         //geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 48, 0) );
334         geom->addPrimitiveSet( new osg::DrawArrays( GL_QUADS, 0, 52, 0) );
335 
336         EffectGeode* geode = new EffectGeode;
337         geode->addDrawable(geom);
338         geode->setEffect(_effect.get());
339 
340         StateSet* ss = geode->getOrCreateStateSet();
341         ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_POSITION_ATTR, 1));
342         ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_SCALE_ATTR, 1));
343         ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_ATTR1, 1));
344         ss->setAttributeAndModes(new osg::VertexAttribDivisor(BUILDING_ATTR2, 1));
345 
346         LOD* result = new LOD;
347         result->addChild(geode, 0, _range);
348         return result;
349     }
350 
351     float _range;
352     ref_ptr<Effect> _effect;
353     bool _fade_out;
354 };
355 
356 const float pack_precision = 128.0;
357 const float pack_precision1 = pack_precision+1.0;
358 
pack8bit(float a,float b,float c)359 float pack8bit(float a, float b, float c) {
360 	return floor(a * pack_precision + 0.5)
361 		   + floor(b * pack_precision + 0.5) * pack_precision1
362 		   + floor(c * pack_precision + 0.5) * pack_precision1 * pack_precision1;
363 };
364 
365 struct AddBuildingLeafObject
366 {
operator ()simgear::AddBuildingLeafObject367     void operator() (LOD* lod, const SGBuildingBin::BuildingInstance building) const
368     {
369         //Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
370         Geode* geode = static_cast<Geode*>(lod->getChild(0));
371 
372         Geometry* geom = static_cast<Geometry*>(geode->getDrawable(0));
373 
374         osg::Vec3Array* positions =  static_cast<osg::Vec3Array*> (geom->getVertexAttribArray(BUILDING_POSITION_ATTR));    // (x,y,z)
375         osg::Vec3Array* scale =  static_cast<osg::Vec3Array*> (geom->getVertexAttribArray(BUILDING_SCALE_ATTR)); // (width, depth, height)
376         osg::Vec3Array* attrib1 = static_cast<osg::Vec3Array*> (geom->getVertexAttribArray(BUILDING_ATTR1));
377         osg::Vec3Array* attrib2 = static_cast<osg::Vec3Array*> (geom->getVertexAttribArray(BUILDING_ATTR2));
378 
379         positions->push_back(building.position);
380         // Depth is the x-axis, width is the y-axis
381         scale->push_back(osg::Vec3f(building.depth, building.width, building.height));
382         attrib1->push_back(osg::Vec3d(
383           pack8bit(building.rotation,         // attr1 in shader
384                       building.walltex0.x(),
385                       building.walltex0.y()),
386           building.pitch_height,
387           pack8bit(building.tex1.x(),    // attr2 in shader
388                       building.tex1.y(),
389                       building.rooftex0.x())
390         ));
391         attrib2->push_back(osg::Vec3f(
392           pack8bit(    // attr3 in shader
393             building.rooftex0.y(),
394             building.tex1.z(),
395             building.rooftop_scale.x()),
396           building.rooftop_scale.y(),
397           0.0f
398         ));
399 
400         DrawArrays* primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
401         primSet->setNumInstances(positions->size());
402     }
403 };
404 
405 struct GetBuildingCoord
406 {
operator ()simgear::GetBuildingCoord407     Vec3 operator() (const SGBuildingBin::BuildingInstance& building) const
408     {
409         return building.position;
410     }
411 };
412 
413 typedef QuadTreeBuilder<LOD*, SGBuildingBin::BuildingInstance, MakeBuildingLeaf, AddBuildingLeafObject,
414                         GetBuildingCoord> BuildingGeometryQuadtree;
415 
416 
417   // Set up a BuildingBin from a file containing a list of individual building
418   // positions.
SGBuildingBin(const SGPath & absoluteFileName,const SGMaterial * mat,bool useVBOs)419   SGBuildingBin::SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs) :
420     SGBuildingBin::SGBuildingBin(mat, useVBOs)
421   {
422     if (!absoluteFileName.exists()) {
423       SG_LOG(SG_TERRAIN, SG_ALERT, "Building list file " << absoluteFileName << " does not exist.");
424       return;
425     }
426 
427     sg_gzifstream stream(absoluteFileName);
428     if (!stream.is_open()) {
429       SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName);
430       return;
431     }
432 
433     while (!stream.eof()) {
434       // read a line.  Each line defines a single building position, and may have
435       // a comment, starting with #
436       std::string line;
437       std::getline(stream, line);
438 
439       // strip comments
440       std::string::size_type hash_pos = line.find('#');
441       if (hash_pos != std::string::npos)
442           line.resize(hash_pos);
443 
444       if (line.empty()) {
445           continue; // skip blank lines
446       }
447 
448       // and process further
449       std::stringstream in(line);
450 
451       // Line format is X Y Z R B W D H P S O F WT RT
452       // where:
453       // X,Y,Z are the cartesian coordinates of the center of the front face. +X is East, +Y is North
454       // R is the building rotation in degrees centered on the middle of the front face.
455       // B is the building type [0, 1, 2] for SMALL, MEDIUM, LARGE
456       // W is the building width in meters
457       // D is the building depth in meters
458       // H is the building height in meters, excluding any pitched roof
459       // P is the pitch height in meters. 0 for a flat roof
460       // S is the roof shape (Currently the following values are valid: 0, 2, 4, 5) :
461       //   0=flat 1=skillion 2=gabled 3=half-hipped 4=hipped 5=pyramidal 6=gambrel
462       //   7=mansard 8=dome 9=onion 10=round 11=saltbox
463       // O is the roof ridge orientation :
464       //   0 = parallel to the front face of the building
465       //   1 = orthogonal to the front face of the building
466       // F is the number of floors (integer)
467       // WT is the texture index to use (integer) for walls. Buildings with the same WT value will have the same wall texture assigned.  There are 6 small, 6 medium and 4 large textures.
468       // RT is the texture index to use (integer) for roofs. Buildings with the same RT value will have the same roof texture assigned.  There are 6 small, 6 medium and 4 large textures.
469       float x = 0.0f, y = 0.0f, z = 0.0f, r = 0.0f, w = 0.0f, d = 0.0f, h = 0.0f, p = 0.0f;
470       int b = 0, s = 0, o = 0, f = 0, wt = 0, rt = 0;
471       in >> x >> y >> z >> r >> b;
472 
473       if (in.bad() || in.fail()) {
474           SG_LOG(SG_TERRAIN, SG_WARN, "Error parsing build entry in: " << absoluteFileName << " line: \"" << line << "\"");
475           continue;
476       }
477 
478       // these might fail, so check them after we look at failbit
479       in >> w >> d >> h >> p >> s >> o >> f >> wt >> rt;
480 
481       //SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << b );
482       SGVec3f loc = SGVec3f(x,y,z);
483       BuildingType type = BuildingType::SMALL;
484       if (b == 1)  type = BuildingType::MEDIUM;
485       if (b == 2)  type = BuildingType::LARGE;
486 
487       // Rotation is in the file as degrees, but in the datastructure normalized
488       // to 0.0 - 1.0
489       float rot = (float) (r / 360.0f);
490 
491       if (w == 0.0f) {
492         // If width is not defined, then we assume we don't have a full set of
493         // data for the buildings so just use the random building definitions
494         insert(loc, rot, type);
495       } else {
496         insert(loc, rot, type, w, d, h, p, f, s, o, wt, rt);
497       }
498     }
499 
500     stream.close();
501   };
502 
503   // Set up the building set based on the material definitions
SGBuildingBin(const SGMaterial * mat,bool useVBOs)504   SGBuildingBin::SGBuildingBin(const SGMaterial* mat, bool useVBOs) : _material(const_cast<SGMaterial*>(mat))
505   {
506       const auto& materialNames = mat->get_names();
507       if (materialNames.empty()) {
508           SG_LOG(SG_TERRAIN, SG_DEV_ALERT, "SGBuildingBin: material has zero names defined");
509       } else {
510           _materialName = materialNames.front();
511           SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << _materialName);
512       }
513 
514       _textureName = mat->get_building_texture();
515       _lightMapName = mat->get_building_lightmap();
516       buildingRange = mat->get_building_range();
517       SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << _textureName);
518   }
519 
~SGBuildingBin()520   SGBuildingBin::~SGBuildingBin() {
521     buildingLocations.clear();
522   }
523 
524   // Generate a building specifying the exact position, dimensions and texture index.
insert(SGVec3f p,float r,BuildingType buildingtype,float width,float depth,float height,float pitch_height,int floors,int roof_shape,int roof_orientation,int wall_tex_index,int roof_tex_index)525   void SGBuildingBin::insert(SGVec3f p, float r, BuildingType buildingtype, float width, float depth, float height, float pitch_height, int floors, int roof_shape, int roof_orientation, int wall_tex_index, int roof_tex_index) {
526 
527     // The 2048x2048 texture is split into 64x32 blocks.  So there are 64 on
528     // the x-axis and 128 on the y-axis.
529     // The leftmost 32 are used for the sides of the building, and the rightmost
530     // 32 for the roof.
531     const float BUILDING_TEXTURE_BLOCK_HEIGHT = 32.0f / 2048.0f; // The height of a single block within the random building texture
532     const float BUILDING_TEXTURE_BLOCK_WIDTH  = 64.0f / 2048.0f;  // The width of a single block within the random building texture
533     Vec2f wall_tex0, roof_tex0;
534     Vec3f tex1;
535 
536     if (buildingtype == SGBuildingBin::SMALL) {
537       // SMALL BUILDINGS
538       // Maximum texture height is 3 stories.
539       // Small buildings are represented on the bottom 18 rows
540       // Each block is 5m wide and 3m high.
541       int wall_row = wall_tex_index % 6;
542       int roof_row = roof_tex_index % 6;
543       float wall_x0 = 0.0f;
544       float wall_y0 = (float) wall_row  * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT;
545       float roof_x0 = 0.0f;
546       float roof_y0 = (float) roof_row  * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT;
547       float wall_roof_x1 = min(0.5f, std::round(width / 5.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
548       float side_x1 = min(0.5f, std::round(depth / 5.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
549       float y1 =  (float) (min(3, floors)) * BUILDING_TEXTURE_BLOCK_HEIGHT;
550 
551       // Checks
552       if ((wall_x0 + wall_roof_x1 > 0.5f) ||
553           (wall_y0 + y1 > (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT))) {
554         SG_LOG(SG_TERRAIN, SG_ALERT, "Small building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")");
555       }
556 
557 
558       wall_tex0 = Vec2f(wall_x0, wall_y0);
559       roof_tex0 = Vec2f(roof_x0, roof_y0);
560       tex1 = Vec3f(wall_roof_x1, y1, side_x1);
561     } else if (buildingtype == SGBuildingBin::MEDIUM) {
562       // MEDIUM BUILDING
563       // Maximum texture height is 8 stories.
564       // Medium buildings are arranged on the texture in a 2x3 pattern.
565       // For a medium building, each block is 10m wide and 3m high.
566       int wall_column = wall_tex_index % 2;
567       int wall_row = wall_tex_index % 3;
568       int roof_column = roof_tex_index % 2;
569       int roof_row = roof_tex_index % 3;
570 
571       float wall_x0 = wall_column * 0.25f;
572       // Counting from the bottom, we have 6 rows of small buildings, each 3 blocks high
573       float wall_y0 = (6.0f * 3.0f + wall_row * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT;
574 
575       float roof_x0 = roof_column * 0.25f;
576       // Counting from the bottom, we have 6 rows of small buildings, each 3 blocks high
577       float roof_y0 = (6.0f * 3.0f + roof_row * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT;
578 
579       float wall_roof_x1 = min(0.25f, std::ceil(width / 10.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
580       float side_x1 = min(0.5f, std::round(depth / 10.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
581       float y1 = (float) (min(8, floors)) * BUILDING_TEXTURE_BLOCK_HEIGHT;
582 
583       if ((wall_x0 + wall_roof_x1 > 0.5f) ||
584           (wall_y0 + y1 <  (6.0f * 3.0f * BUILDING_TEXTURE_BLOCK_HEIGHT))  ||
585           (wall_y0 + y1 > ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT))) {
586         SG_LOG(SG_TERRAIN, SG_ALERT, "Medium building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")");
587       }
588 
589 
590       wall_tex0 = Vec2f(wall_x0, wall_y0);
591       roof_tex0 = Vec2f(roof_x0, roof_y0);
592       tex1 = Vec3f(wall_roof_x1, y1, side_x1);
593     } else {
594       // LARGE BUILDING
595       // Maximum texture height is 22 stories.
596       // Large buildings are arranged in a 4x1 pattern
597       // Each block is 20m wide and 3m high.
598       int wall_column = wall_tex_index % 4;
599       int roof_column = roof_tex_index % 4;
600       // Counting from the bottom we have 6 rows of small buildings (3 blocks high),
601       // then 3 rows of medium buildings (8 blocks high).  Then the large building texture
602       float wall_x0 = wall_column * 0.125f;
603       float wall_y0 = (6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT;
604       float roof_x0 = roof_column * 0.125f;
605       float roof_y0 = (6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT;
606       float wall_roof_x1 = min(0.125f, std::ceil(width / 20.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
607       float side_x1 = min(0.5f, std::round(depth / 20.0f) * BUILDING_TEXTURE_BLOCK_WIDTH);
608       float y1 = (float) min(22, floors) * BUILDING_TEXTURE_BLOCK_HEIGHT;
609 
610       if ((wall_x0 + wall_roof_x1 > 0.5f) ||
611           (wall_y0 + y1 < ((6.0f * 3.0f + 3.0f * 8.0f) * BUILDING_TEXTURE_BLOCK_HEIGHT)) ||
612           (wall_y0 + y1 > 1.0)     ) {
613         SG_LOG(SG_TERRAIN, SG_ALERT, "Large building texture coordinates out of bounds offset (" << wall_x0 << ", " << wall_y0 << ") gain (" << wall_roof_x1 << ", " << y1 << ")");
614       }
615 
616       wall_tex0 = Vec2f(wall_x0, wall_y0);
617       roof_tex0 = Vec2f(roof_x0, roof_y0);
618       tex1 = Vec3f(wall_roof_x1, y1, side_x1);
619     }
620 
621     // Build a scaling factor in the x,y axes for the top of the roof. This allows us to create gabled, hipped, pyramidal roofs.
622     Vec2f rooftop_scale = Vec2f(1.0f, 1.0f); // Default of a flat roof.
623 
624     if (pitch_height > 0.0f)
625     {
626       // Roof with some pitch height
627 
628       if ((roof_shape == 2) || (roof_shape == 6) || (roof_shape == 10)  || (roof_shape == 11)) {
629         // Gabled, gambrel, round, saltbox
630         if (roof_orientation == 0) rooftop_scale = Vec2f(0.0f, 1.0f);
631         if (roof_orientation == 1) rooftop_scale = Vec2f(1.0f, 0.0f);
632       }
633 
634       if ((roof_shape == 3) || (roof_shape == 4) || (roof_shape == 7)) {
635         // Hipped, half-hipped, mansard
636         // The pitch height expressed as a fraction of the building width/depth such that the hipped
637         // roof has a pitch of around 45 degrees.  A minimum of 0.5 so that they have at least some ridge.
638         if (roof_orientation == 0) rooftop_scale = Vec2f(0.0f, max(0.5f,(depth -  2*pitch_height) / width));
639         if (roof_orientation == 1) rooftop_scale = Vec2f(max(0.5f,(width -  2*pitch_height) / width), 0.0f);
640       }
641 
642       // Pyramidal, dome, onion
643       if ((roof_shape == 5) || (roof_shape == 8) || (roof_shape == 9)) rooftop_scale = Vec2f(0.0f, 0.0f);
644     }
645 
646     buildingLocations.push_back(BuildingInstance(toOsg(p), width, depth, height, pitch_height, r, wall_tex0, roof_tex0, tex1, rooftop_scale));
647   }
648 
649 
650   // Generate a building of a given type at a specified position, using the random building material definition to determine the dimensions and texture index.
insert(SGVec3f p,float r,BuildingType buildingtype)651   void SGBuildingBin::insert(SGVec3f p, float r, BuildingType buildingtype) {
652 
653     float width, depth, height, pitch_height;
654     int floors;
655     int roof_shape;
656     int roof_orientation;
657 
658     // Generate a random seed for the building generation.
659     mt seed;
660     mt_init(&seed, unsigned(p.x() + p.y() + p.z()));
661 
662     if (buildingtype == SGBuildingBin::SMALL) {
663       // Small building
664       // Maximum number of floors is 3, and maximum width/depth is 192m.
665       width = _material->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_width() - _material->get_building_small_min_width());
666       depth = _material->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_small_max_depth() - _material->get_building_small_min_depth());
667       floors = SGMisc<double>::round(_material->get_building_small_min_floors() + mt_rand(&seed) * (_material->get_building_small_max_floors() - _material->get_building_small_min_floors()));
668       height = floors * (2.8 + mt_rand(&seed));
669 
670       // Small buildings are never deeper than they are wide.
671       if (depth > width) { depth = width; }
672 
673       pitch_height = (mt_rand(&seed) < _material->get_building_small_pitch()) ? 3.0 : 0.0;
674 
675       if (pitch_height == 0.0) {
676         roof_shape = 0;
677         roof_orientation = 0;
678       } else {
679         roof_shape = (int) (mt_rand(&seed) * 10.0);
680         roof_orientation = (int) std::round((float) mt_rand(&seed));
681       }
682     } else if (buildingtype == SGBuildingBin::MEDIUM) {
683       // MEDIUM BUILDING
684       width = _material->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_width() - _material->get_building_medium_min_width());
685       depth = _material->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (_material->get_building_medium_max_depth() - _material->get_building_medium_min_depth());
686       floors = SGMisc<double>::round(_material->get_building_medium_min_floors() + mt_rand(&seed) * (_material->get_building_medium_max_floors() - _material->get_building_medium_min_floors()));
687       height = floors * (2.8 + mt_rand(&seed));
688 
689       while ((height > width) && (floors > _material->get_building_medium_min_floors())) {
690           // Ensure that medium buildings aren't taller than they are wide
691           floors--;
692           height = floors * (2.8 + mt_rand(&seed));
693       }
694 
695       pitch_height = (mt_rand(&seed) < _material->get_building_medium_pitch()) ? 3.0 : 0.0;
696 
697       if (pitch_height == 0.0) {
698         roof_shape = 0;
699         roof_orientation = 0;
700       } else {
701         roof_shape = (int) (mt_rand(&seed) * 10.0);
702         roof_orientation = (int) std::round((float) mt_rand(&seed));
703       }
704     } else {
705       // LARGE BUILDING
706       width = _material->get_building_large_min_width() + mt_rand(&seed) * (_material->get_building_large_max_width() - _material->get_building_large_min_width());
707       depth = _material->get_building_large_min_depth() + mt_rand(&seed) * (_material->get_building_large_max_depth() - _material->get_building_large_min_depth());
708       floors = SGMisc<double>::round(_material->get_building_large_min_floors() + mt_rand(&seed) * (_material->get_building_large_max_floors() - _material->get_building_large_min_floors()));
709       height = floors * (2.8 + mt_rand(&seed));
710       pitch_height = (mt_rand(&seed) < _material->get_building_large_pitch()) ? 3.0 : 0.0;
711 
712       if (pitch_height == 0.0) {
713         roof_shape = 0;
714         roof_orientation = 0;
715       } else {
716         roof_shape = (int) (mt_rand(&seed) * 10.0);
717         roof_orientation = (int) std::round((float) mt_rand(&seed));
718       }
719     }
720 
721     insert(p, r, buildingtype, width, depth, height, pitch_height, floors, roof_shape, roof_orientation, (int) (mt_rand(&seed) * 1000.0), (int) (mt_rand(&seed) * 1000.0));
722   }
723 
getNumBuildings()724   int SGBuildingBin::getNumBuildings() {
725     return buildingLocations.size();
726   }
727 
checkMinDist(SGVec3f p,float radius)728   bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {
729     BuildingInstanceList::iterator iter;
730     for (iter = buildingLocations.begin(); iter != buildingLocations.end(); ++iter) {
731       if (iter->getDistSqr(toOsg(p)) < radius) {
732         return false;
733       }
734     }
735     return true;
736   }
737 
getBuildingType(float roll)738   SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
739       if (roll < _material->get_building_small_fraction()) {
740           return SGBuildingBin::SMALL;
741       }
742 
743       if (roll < (_material->get_building_small_fraction() + _material->get_building_medium_fraction())) {
744           return SGBuildingBin::MEDIUM;
745       }
746 
747     return SGBuildingBin::LARGE;
748   }
749 
getBuildingMaxRadius(BuildingType type)750   float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
751       if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_width();
752       if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_width();
753       if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_width();
754       return 0;
755   }
756 
getBuildingMaxDepth(BuildingType type)757   float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
758       if (type == SGBuildingBin::SMALL) return _material->get_building_small_max_depth();
759       if (type == SGBuildingBin::MEDIUM) return _material->get_building_medium_max_depth();
760       if (type == SGBuildingBin::LARGE) return _material->get_building_large_max_depth();
761       return 0;
762   }
763 
createBuildingsGroup(Matrix transInv,const SGReaderWriterOptions * options)764   ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
765   {
766     ref_ptr<Effect> effect;
767     auto iter = buildingEffectMap.find(_textureName);
768 
769     if ((iter == buildingEffectMap.end())||
770         (!iter->second.lock(effect)))
771     {
772       SGPropertyNode_ptr effectProp = new SGPropertyNode;
773       makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
774       SGPropertyNode* params = makeChild(effectProp, "parameters");
775       // Main texture - n=0
776       params->getChild("texture", 0, true)->getChild("image", 0, true)->setStringValue(_textureName);
777 
778       // Light map - n=3
779       params->getChild("texture", 3, true)->getChild("image", 0, true)->setStringValue(_lightMapName);
780 
781       effect = makeEffect(effectProp, true, options);
782       if (iter == buildingEffectMap.end())
783           buildingEffectMap.insert(EffectMap::value_type(_textureName, effect));
784       else
785           iter->second = effect; // update existing, but empty observer
786     }
787 
788     // Transform building positions from the "geocentric" positions we
789     // get from the scenery polys into the local Z-up coordinate
790     // system.
791     std::vector<BuildingInstance> rotatedBuildings;
792     rotatedBuildings.reserve(buildingLocations.size());
793     for (const auto &b : buildingLocations) {
794         rotatedBuildings.emplace_back(BuildingInstance(
795             b.position * transInv,
796             b
797         ));
798     }
799 
800     // Now, create a quadbuilding for the buildings.
801     BuildingGeometryQuadtree
802         quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
803                  SG_BUILDING_QUAD_TREE_DEPTH,
804                  MakeBuildingLeaf(buildingRange, effect, false));
805 
806     quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
807 
808     ref_ptr<Group> group = new osg::Group();
809 
810     static int buildingGroupCounter = 0;
811     group->setName("BuildingsGroup_" + std::to_string(buildingGroupCounter++));
812 
813     for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j)
814             group->addChild(quadbuilding.getRoot()->getChild(j));
815 
816     return group;
817   }
818 
819   // We may end up with a quadtree with many empty leaves. One might say
820   // that we should avoid constructing the leaves in the first place,
821   // but this node visitor tries to clean up after the fact.
822   struct QuadTreeCleaner : public osg::NodeVisitor
823   {
QuadTreeCleanersimgear::QuadTreeCleaner824       QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
825       {
826       }
applysimgear::QuadTreeCleaner827       void apply(LOD& lod)
828       {
829           for (int i  = lod.getNumChildren() - 1; i >= 0; --i) {
830               EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
831               if (!geode)
832                   continue;
833               bool geodeEmpty = true;
834               for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
835                   const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
836                   if (!geom) {
837                       geodeEmpty = false;
838                       break;
839                   }
840                   for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
841                       const PrimitiveSet* ps = geom->getPrimitiveSet(k);
842                       if (ps->getNumIndices() > 0) {
843                           geodeEmpty = false;
844                           break;
845                       }
846                   }
847               }
848               if (geodeEmpty)
849                   lod.removeChildren(i, 1);
850           }
851       }
852   };
853 
854   // This actually returns a MatrixTransform node. If we rotate the whole
855   // set of buildings into the local Z-up coordinate system we can reuse the
856   // primitive building geometry for all the buildings of the same type.
createRandomBuildings(SGBuildingBinList & buildings,const osg::Matrix & transform,const SGReaderWriterOptions * options)857   osg::Group* createRandomBuildings(SGBuildingBinList& buildings, const osg::Matrix& transform,
858                            const SGReaderWriterOptions* options)
859   {
860       Matrix transInv = Matrix::inverse(transform);
861       // Set up some shared structures.
862       MatrixTransform* mt = new MatrixTransform(transform);
863       SGBuildingBinList::iterator i;
864 
865       for (i = buildings.begin(); i != buildings.end(); ++i) {
866           SGBuildingBin* bin = *i;
867           ref_ptr<Group> group = bin->createBuildingsGroup(transInv, options);
868 
869           for (size_t j = 0; j < group->getNumChildren(); ++j) {
870             mt->addChild(group->getChild(j));
871           }
872       }
873 
874       QuadTreeCleaner cleaner;
875       mt->accept(cleaner);
876       return mt;
877   }
878 }
879