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