3 * Copyright (C) 2012 Stuart Buchanan
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 # include <simgear_config.h>
31 #include <boost/foreach.hpp>
32 #include <boost/tuple/tuple_comparison.hpp>
35 #include <osg/Geometry>
37 #include <osg/MatrixTransform>
39 #include <osg/ShadeModel>
40 #include <osg/Material>
41 #include <osg/CullFace>
43 #include <osgDB/ReadFile>
44 #include <osgDB/FileUtils>
46 #include <simgear/debug/logstream.hxx>
47 #include <simgear/math/SGLimits.hxx>
48 #include <simgear/math/SGMisc.hxx>
49 #include <simgear/math/sg_random.h>
50 #include <simgear/misc/sg_path.hxx>
51 #include <simgear/scene/material/Effect.hxx>
52 #include <simgear/scene/material/EffectGeode.hxx>
53 #include <simgear/scene/model/model.hxx>
54 #include <simgear/props/props.hxx>
56 #include "ShaderGeometry.hxx"
57 #include "SGBuildingBin.hxx"
65 typedef std::map<std::string, osg::observer_ptr<osg::StateSet> > BuildingStateSetMap;
66 static BuildingStateSetMap statesetmap;
68 typedef std::map<std::string, osg::observer_ptr<Effect> > EffectMap;
69 static EffectMap buildingEffectMap;
71 // Building instance scheme:
72 // vertex - local position of vertices, with 0,0,0 being the center front.
73 // fog coord - rotation
74 // color - xyz of tree quad origin, replicated 4 times.
76 struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
78 BuildingBoundingBoxCallback() {}
79 BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {}
80 META_Object(simgear, BuildingBoundingBoxCallback);
81 virtual BoundingBox computeBound(const Drawable&) const;
85 BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
88 const Geometry* geom = static_cast<const Geometry*>(&drawable);
89 const Vec3Array* v = static_cast<const Vec3Array*>(geom->getVertexArray());
90 const Vec4Array* pos = static_cast<const Vec4Array*>(geom->getColorArray());
92 Geometry::PrimitiveSetList primSets = geom->getPrimitiveSetList();
93 for (Geometry::PrimitiveSetList::const_iterator psitr = primSets.begin(), psend = primSets.end();
96 DrawArrays* da = static_cast<DrawArrays*>(psitr->get());
97 GLint psFirst = da->getFirst();
98 GLint psEndVert = psFirst + da->getCount();
99 for (GLint i = psFirst;i < psEndVert; ++i) {
101 Matrixd trnsfrm = Matrixd::rotate(- M_PI * 2 * (*pos)[i].a(), Vec3(0.0f, 0.0f, 1.0f));
103 pt += Vec3((*pos)[i].x(), (*pos)[i].y(), (*pos)[i].z());
110 // Set up the building set based on the material definitions
111 SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) {
113 material_name = new std::string(mat->get_names()[0]);
114 SG_LOG(SG_TERRAIN, SG_DEBUG, "Building material " << material_name);
115 texture = new std::string(mat->get_building_texture());
116 lightMap = new std::string(mat->get_building_lightmap());
117 SG_LOG(SG_TERRAIN, SG_DEBUG, "Building texture " << texture);
119 // Generate a random seed for the building generation.
121 mt_init(&seed, unsigned(123));
123 smallSharedGeometry = new osg::Geometry();
124 mediumSharedGeometry = new osg::Geometry();
125 largeSharedGeometry = new osg::Geometry();
127 smallBuildingMaxRadius = std::max(mat->get_building_small_max_depth() * 0.5, mat->get_building_small_max_width() * 0.5);
128 mediumBuildingMaxRadius = std::max(mat->get_building_medium_max_depth() * 0.5, mat->get_building_medium_max_width() * 0.5);
129 largeBuildingMaxRadius = std::max(mat->get_building_large_max_depth() * 0.5, mat->get_building_large_max_width() * 0.5);
131 smallBuildingMaxDepth = mat->get_building_small_max_depth();
132 mediumBuildingMaxDepth = mat->get_building_medium_max_depth();
133 largeBuildingMaxDepth = mat->get_building_large_max_depth();
135 smallBuildingFraction = mat->get_building_small_fraction();
136 mediumBuildingFraction = mat->get_building_medium_fraction();
138 buildingRange = mat->get_building_range();
140 SG_LOG(SG_TERRAIN, SG_DEBUG, "Building fractions " << smallBuildingFraction << " " << mediumBuildingFraction);
143 // TODO: Reverse this - otherwise we never get any large buildings!
144 BuildingType types[] = { SGBuildingBin::SMALL, SGBuildingBin::MEDIUM, SGBuildingBin::LARGE };
145 BuildingList lists[] = { SGBuildingBin::smallBuildings, SGBuildingBin::mediumBuildings, SGBuildingBin::largeBuildings };
146 ref_ptr<Geometry> geometries[] = { smallSharedGeometry, mediumSharedGeometry, largeSharedGeometry };
148 for (int bt=0; bt < 3; bt++) {
149 SGBuildingBin::BuildingType buildingtype = types[bt];
150 ref_ptr<Geometry> sharedGeometry = geometries[bt];
151 BuildingList buildings = lists[bt];
153 osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
154 osg::ref_ptr<osg::Vec2Array> t = new osg::Vec2Array;
155 osg::ref_ptr<osg::Vec3Array> n = new osg::Vec3Array;
157 v->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
158 t->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
159 n->reserve(BUILDING_SET_SIZE * VERTICES_PER_BUILDING);
161 sharedGeometry->setFogCoordBinding(osg::Geometry::BIND_PER_VERTEX);
162 sharedGeometry->setComputeBoundingBoxCallback(new BuildingBoundingBoxCallback);
163 sharedGeometry->setUseDisplayList(false);
164 sharedGeometry->setDataVariance(osg::Object::STATIC);
166 sharedGeometry->setUseVertexBufferObjects(true);
169 for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {
176 if (buildingtype == SGBuildingBin::SMALL) {
178 width = mat->get_building_small_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_width() - mat->get_building_small_min_width());
179 depth = mat->get_building_small_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_small_max_depth() - mat->get_building_small_min_depth());
180 floors = SGMisc<double>::round(mat->get_building_small_min_floors() + mt_rand(&seed) * (mat->get_building_small_max_floors() - mat->get_building_small_min_floors()));
181 height = floors * (2.8 + mt_rand(&seed));
183 // Small buildings are never deeper than they are wide.
184 if (depth > width) { depth = width; }
186 pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
187 } else if (buildingtype == SGBuildingBin::MEDIUM) {
188 width = mat->get_building_medium_min_width() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_width() - mat->get_building_medium_min_width());
189 depth = mat->get_building_medium_min_depth() + mt_rand(&seed) * mt_rand(&seed) * (mat->get_building_medium_max_depth() - mat->get_building_medium_min_depth());
190 floors = SGMisc<double>::round(mat->get_building_medium_min_floors() + mt_rand(&seed) * (mat->get_building_medium_max_floors() - mat->get_building_medium_min_floors()));
191 height = floors * (2.8 + mt_rand(&seed));
193 while ((height > width) && (floors > mat->get_building_medium_min_floors())) {
194 // Ensure that medium buildings aren't taller than they are wide
196 height = floors * (2.8 + mt_rand(&seed));
199 pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());
201 width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width());
202 depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth());
203 floors = SGMisc<double>::round(mat->get_building_large_min_floors() + mt_rand(&seed) * (mat->get_building_large_max_floors() - mat->get_building_large_min_floors()));
204 height = floors * (2.8 + mt_rand(&seed));
205 pitched = (mt_rand(&seed) < mat->get_building_large_pitch());
208 Building building = Building(buildingtype,
215 buildings.push_back(building);
217 // Now create an OSG Geometry based on the Building
218 float cw = 0.5f * building.width;
219 float cd = building.depth;
220 float ch = building.height;
222 // 0,0,0 is the bottom center of the front
223 // face, e.g. where the front door would be
226 // This exteds 10m below the main section
228 v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom right
229 v->push_back( osg::Vec3( 0, cw, -10) ); // bottom left
230 v->push_back( osg::Vec3( 0, cw, 0) ); // top left
231 v->push_back( osg::Vec3( 0, -cw, 0) ); // top right
233 for (int i=0; i<4; ++i)
234 n->push_back( osg::Vec3(1, 0, 0) ); // normal
237 v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom right
238 v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom left
239 v->push_back( osg::Vec3( 0, -cw, 0) ); // top left
240 v->push_back( osg::Vec3( -cd, -cw, 0) ); // top right
242 for (int i=0; i<4; ++i)
243 n->push_back( osg::Vec3(0, -1, 0) ); // normal
246 v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom right
247 v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom left
248 v->push_back( osg::Vec3( -cd, -cw, 0) ); // top left
249 v->push_back( osg::Vec3( -cd, cw, 0) ); // top right
251 for (int i=0; i<4; ++i)
252 n->push_back( osg::Vec3(-1, 0, 0) ); // normal
255 v->push_back( osg::Vec3( 0, cw, -10) ); // bottom right
256 v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom left
257 v->push_back( osg::Vec3( -cd, cw, 0) ); // top left
258 v->push_back( osg::Vec3( 0, cw, 0) ); // top right
260 for (int i=0; i<4; ++i)
261 n->push_back( osg::Vec3(0, 1, 0) ); // normal
265 v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom right
266 v->push_back( osg::Vec3( 0, cw, 0) ); // bottom left
267 v->push_back( osg::Vec3( 0, cw, ch) ); // top left
268 v->push_back( osg::Vec3( 0, -cw, ch) ); // top right
270 for (int i=0; i<4; ++i)
271 n->push_back( osg::Vec3(1, 0, 0) ); // normal
274 v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom right
275 v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom left
276 v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
277 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
279 for (int i=0; i<4; ++i)
280 n->push_back( osg::Vec3(0, -1, 0) ); // normal
283 v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom right
284 v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom left
285 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top left
286 v->push_back( osg::Vec3( -cd, cw, ch) ); // top right
288 for (int i=0; i<4; ++i)
289 n->push_back( osg::Vec3(-1, 0, 0) ); // normal
292 v->push_back( osg::Vec3( 0, cw, 0) ); // bottom right
293 v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom left
294 v->push_back( osg::Vec3( -cd, cw, ch) ); // top left
295 v->push_back( osg::Vec3( 0, cw, ch) ); // top right
297 for (int i=0; i<4; ++i)
298 n->push_back( osg::Vec3(0, 1, 0) ); // normal
301 if (building.pitched) {
303 // Front pitched roof
304 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
305 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
306 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
307 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
309 for (int i=0; i<4; ++i)
310 n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal
313 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
314 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
315 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
316 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
318 for (int i=0; i<4; ++i)
319 n->push_back( osg::Vec3(0, -1, 0) ); // normal
322 v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom right
323 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom left
324 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
325 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
327 for (int i=0; i<4; ++i)
328 n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal
330 // Right pitched roof
331 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
332 v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom left
333 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
334 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
336 for (int i=0; i<4; ++i)
337 n->push_back( osg::Vec3(0, 1, 0) ); // normal
339 // If the roof isn't pitched, we still generate the
340 // vertices for simplicity later.
343 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
344 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
345 v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
346 v->push_back( osg::Vec3(-cd, -cw, ch) ); // top right
348 for (int i=0; i<4; ++i)
349 n->push_back( osg::Vec3(0, 0, 1) ); // normal
351 // Left non-pitched roof
352 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
353 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
354 v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
355 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
357 for (int i=0; i<4; ++i)
358 n->push_back( osg::Vec3(0, -1, 0) ); // normal
361 v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom right
362 v->push_back( osg::Vec3(-cd, -cw, ch) ); // bottom left
363 v->push_back( osg::Vec3(-cd, -cw, ch) ); // top left
364 v->push_back( osg::Vec3(-cd, cw, ch) ); // top right
366 for (int i=0; i<4; ++i)
367 n->push_back( osg::Vec3(1, 0, 0) ); // normal
369 // Right pitched roof
370 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
371 v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom left
372 v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
373 v->push_back( osg::Vec3( 0, cw, ch) ); // top right
375 for (int i=0; i<4; ++i)
376 n->push_back( osg::Vec3(0, 1, 0) ); // normal
379 // The 1024x1024 texture is split into 32x16 blocks.
380 // For a small building, each block is 6m wide and 3m high.
381 // For a medium building, each block is 10m wide and 3m high.
382 // For a large building, each block is 20m wide and 3m high
384 if (building.type == SGBuildingBin::SMALL) {
385 // Small buildings are represented on the bottom 5 rows of 3 floors
386 int row = ((int) (mt_rand(&seed) * 1000)) % 5;
387 float base_y = (float) row * 16.0 * 3.0 / 1024.0;
388 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
389 float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
390 float right_x = 0.0f;
391 float front_x = 384.0/1024.0;
392 float back_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f);
394 // BASEMENT - uses the baseline texture
395 for (unsigned int i = 0; i < 16; i++) {
396 t->push_back( osg::Vec2( left_x, base_y) );
400 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
401 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
402 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
403 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
406 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
407 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
408 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
409 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
411 // Back (same as front for the moment)
412 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
413 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
414 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
415 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
417 // Right (same as left for the moment)
418 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
419 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
420 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
421 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
424 if (building.pitched) {
425 // Use the entire height of the roof texture
426 top_y = base_y + 16.0 * 3.0 / 1024.0;
427 left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
428 right_x = 512/1024.0;
429 front_x = 480.0/1024.0;
430 back_x = 512.0/1024.0;
433 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
434 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
435 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
436 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
439 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
440 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
441 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
442 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
444 // Back (same as front for the moment)
445 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
446 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
447 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
448 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
450 // Right (same as left for the moment)
451 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
452 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
453 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
454 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
457 left_x = 640.0/1024.0;
458 right_x = 512.0/1024.0;
459 // Use the entire height of the roof texture
460 top_y = base_y + 16.0 * 3.0 / 1024.0;
462 // Flat roofs still have 4 surfaces, so we need to set the textures
463 for (int i=0; i<4; ++i) {
464 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
465 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
466 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
467 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
473 if (building.type == SGBuildingBin::MEDIUM)
475 int column = ((int) (mt_rand(&seed) * 1000)) % 5;
476 float base_y = 288 / 1024.0;
477 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
478 float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
479 float right_x = column * 192.0 /1024.0;
481 // BASEMENT - uses the baseline texture
482 for (unsigned int i = 0; i < 16; i++) {
483 t->push_back( osg::Vec2( left_x, base_y) );
488 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
489 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
490 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
491 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
494 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
495 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
496 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
497 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
499 // Back (same as front for the moment)
500 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
501 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
502 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
503 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
505 // Right (same as left for the moment)
506 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
507 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
508 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
509 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
512 if (building.pitched) {
513 base_y = 288.0/1024.0;
514 top_y = 576.0/1024.0;
515 left_x = 960.0/1024.0;
519 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
520 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
521 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
522 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
525 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
526 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
527 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
528 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
530 // Back (same as front for the moment)
531 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
532 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
533 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
534 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
536 // Right (same as left for the moment)
537 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
538 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
539 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
540 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
544 top_y = 576.0/1024.0;
545 left_x = column * 192.0 /1024.0;
546 right_x = (column + 1)* 192.0 /1024.0;
548 // Flat roofs still have 4 surfaces
549 for (int i=0; i<4; ++i) {
550 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
551 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
552 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
553 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
558 if (building.type == SGBuildingBin::LARGE)
560 int column = ((int) (mt_rand(&seed) * 1000)) % 8;
561 float base_y = 576 / 1024.0;
562 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
563 float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
564 float right_x = column * 128.0 /1024.0;
566 // BASEMENT - uses the baseline texture
567 for (unsigned int i = 0; i < 16; i++) {
568 t->push_back( osg::Vec2( left_x, base_y) );
573 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
574 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
575 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
576 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
579 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
580 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
581 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
582 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
584 // Back (same as front for the moment)
585 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
586 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
587 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
588 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
590 // Right (same as left for the moment)
591 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
592 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
593 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
594 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
597 if (building.pitched) {
601 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
602 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
603 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
604 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
607 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
608 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
609 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
610 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
612 // Back (same as front for the moment)
613 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
614 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
615 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
616 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
618 // Right (same as left for the moment)
619 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
620 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
621 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
622 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
628 // Flat roofs still have 4 surfaces
629 for (int i=0; i<4; ++i) {
630 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
631 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
632 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
633 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
639 // Set the vertex, texture and normals. Colors will be set per-instance
641 sharedGeometry->setVertexArray(v);
642 sharedGeometry->setTexCoordArray(0, t, Array::BIND_PER_VERTEX);
643 sharedGeometry->setNormalArray(n, Array::BIND_PER_VERTEX);
647 void SGBuildingBin::insert(SGVec3f p, float r, BuildingType type) {
649 if (type == SGBuildingBin::SMALL) {
650 smallBuildingLocations.push_back(BuildingInstance(p, r, &smallBuildings, smallSharedGeometry));
653 if (type == SGBuildingBin::MEDIUM) {
654 mediumBuildingLocations.push_back(BuildingInstance(p, r, &mediumBuildings, mediumSharedGeometry));
657 if (type == SGBuildingBin::LARGE) {
658 largeBuildingLocations.push_back(BuildingInstance(p, r, &largeBuildings, largeSharedGeometry));
662 int SGBuildingBin::getNumBuildings() {
663 return smallBuildingLocations.size() + mediumBuildingLocations.size() + largeBuildingLocations.size();
666 bool SGBuildingBin::checkMinDist (SGVec3f p, float radius) {
667 BuildingInstanceList::iterator iter;
669 float r = (radius + smallBuildingMaxRadius) * (radius + smallBuildingMaxRadius);
670 for (iter = smallBuildingLocations.begin(); iter != smallBuildingLocations.end(); ++iter) {
671 if (iter->getDistSqr(p) < r) {
676 r = (radius + mediumBuildingMaxRadius) * (radius + mediumBuildingMaxRadius);
677 for (iter = mediumBuildingLocations.begin(); iter != mediumBuildingLocations.end(); ++iter) {
678 if (iter->getDistSqr(p) < r) {
683 r = (radius + largeBuildingMaxRadius) * (radius + largeBuildingMaxRadius);
684 for (iter = largeBuildingLocations.begin(); iter != largeBuildingLocations.end(); ++iter) {
685 if (iter->getDistSqr(p) < r) {
693 SGBuildingBin::BuildingType SGBuildingBin::getBuildingType(float roll) {
695 if (roll < smallBuildingFraction) {
696 return SGBuildingBin::SMALL;
699 if (roll < (smallBuildingFraction + mediumBuildingFraction)) {
700 return SGBuildingBin::MEDIUM;
703 return SGBuildingBin::LARGE;
706 float SGBuildingBin::getBuildingMaxRadius(BuildingType type) {
708 if (type == SGBuildingBin::SMALL) return smallBuildingMaxRadius;
709 if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxRadius;
710 if (type == SGBuildingBin::LARGE) return largeBuildingMaxRadius;
715 float SGBuildingBin::getBuildingMaxDepth(BuildingType type) {
717 if (type == SGBuildingBin::SMALL) return smallBuildingMaxDepth;
718 if (type == SGBuildingBin::MEDIUM) return mediumBuildingMaxDepth;
719 if (type == SGBuildingBin::LARGE) return largeBuildingMaxDepth;
724 ref_ptr<Group> SGBuildingBin::createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options)
726 ref_ptr<Effect> effect;
727 EffectMap::iterator iter = buildingEffectMap.find(*texture);
729 if ((iter == buildingEffectMap.end())||
730 (!iter->second.lock(effect)))
732 SGPropertyNode_ptr effectProp = new SGPropertyNode;
733 makeChild(effectProp, "inherits-from")->setStringValue("Effects/building");
734 SGPropertyNode* params = makeChild(effectProp, "parameters");
735 // Main texture - n=0
736 params->getChild("texture", 0, true)->getChild("image", 0, true)
737 ->setStringValue(*texture);
740 params->getChild("texture", 3, true)->getChild("image", 0, true)
741 ->setStringValue(*lightMap);
743 effect = makeEffect(effectProp, true, options);
744 if (iter == buildingEffectMap.end())
745 buildingEffectMap.insert(EffectMap::value_type(*texture, effect));
747 iter->second = effect; // update existing, but empty observer
750 ref_ptr<Group> group = new osg::Group();
752 // Now, create a quadbuilding for the buildings.
754 BuildingInstanceList locs[] = { smallBuildingLocations,
755 SGBuildingBin::mediumBuildingLocations,
756 SGBuildingBin::largeBuildingLocations };
758 for (int i = 0; i < 3; i++)
760 // Create a quad tree. Only small and medium buildings are faded out.
761 BuildingGeometryQuadtree
762 quadbuilding(GetBuildingCoord(), AddBuildingLeafObject(),
763 SG_BUILDING_QUAD_TREE_DEPTH,
764 MakeBuildingLeaf(buildingRange, effect, (i != 2)));
766 // Transform building positions from the "geocentric" positions we
767 // get from the scenery polys into the local Z-up coordinate
769 std::vector<BuildingInstance> rotatedBuildings;
770 rotatedBuildings.reserve(locs[i].size());
771 std::transform(locs[i].begin(), locs[i].end(),
772 std::back_inserter(rotatedBuildings),
773 BuildingInstanceTransformer(transInv));
774 quadbuilding.buildQuadTree(rotatedBuildings.begin(), rotatedBuildings.end());
776 for (size_t j = 0; j < quadbuilding.getRoot()->getNumChildren(); ++j)
777 group->addChild(quadbuilding.getRoot()->getChild(j));
783 // We may end up with a quadtree with many empty leaves. One might say
784 // that we should avoid constructing the leaves in the first place,
785 // but this node visitor tries to clean up after the fact.
786 struct QuadTreeCleaner : public osg::NodeVisitor
788 QuadTreeCleaner() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)
793 for (int i = lod.getNumChildren() - 1; i >= 0; --i) {
794 EffectGeode* geode = dynamic_cast<EffectGeode*>(lod.getChild(i));
797 bool geodeEmpty = true;
798 if (geode->getNumDrawables() > 1) {
799 SG_LOG(SG_TERRAIN, SG_DEBUG, "Building LOD Drawables: " << geode->getNumDrawables());
802 for (unsigned j = 0; j < geode->getNumDrawables(); ++j) {
803 const Geometry* geom = dynamic_cast<Geometry*>(geode->getDrawable(j));
808 for (unsigned k = 0; k < geom->getNumPrimitiveSets(); k++) {
809 const PrimitiveSet* ps = geom->getPrimitiveSet(k);
810 if (ps->getNumIndices() > 0) {
817 lod.removeChildren(i, 1);
822 // This actually returns a MatrixTransform node. If we rotate the whole
823 // forest into the local Z-up coordinate system we can reuse the
824 // primitive building geometry for all the forests of the same type.
825 osg::Group* createRandomBuildings(SGBuildingBinList& buildings, const osg::Matrix& transform,
826 const SGReaderWriterOptions* options)
828 Matrix transInv = Matrix::inverse(transform);
830 // Set up some shared structures.
831 MatrixTransform* mt = new MatrixTransform(transform);
832 SGBuildingBinList::iterator i;
834 for (i = buildings.begin(); i != buildings.end(); ++i) {
835 SGBuildingBin* bin = *i;
836 ref_ptr<Group> group = bin->createBuildingsGroup(transInv, options);
838 for (size_t j = 0; j < group->getNumChildren(); ++j) {
839 mt->addChild(group->getChild(j));
847 QuadTreeCleaner cleaner;