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) {
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);
165 for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {
172 if (buildingtype == SGBuildingBin::SMALL) {
174 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());
175 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());
176 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()));
177 height = floors * (2.8 + mt_rand(&seed));
179 // Small buildings are never deeper than they are wide.
180 if (depth > width) { depth = width; }
182 pitched = (mt_rand(&seed) < mat->get_building_small_pitch());
183 } else if (buildingtype == SGBuildingBin::MEDIUM) {
184 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());
185 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());
186 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()));
187 height = floors * (2.8 + mt_rand(&seed));
189 while ((height > width) && (floors > mat->get_building_medium_min_floors())) {
190 // Ensure that medium buildings aren't taller than they are wide
192 height = floors * (2.8 + mt_rand(&seed));
195 pitched = (mt_rand(&seed) < mat->get_building_medium_pitch());
197 width = mat->get_building_large_min_width() + mt_rand(&seed) * (mat->get_building_large_max_width() - mat->get_building_large_min_width());
198 depth = mat->get_building_large_min_depth() + mt_rand(&seed) * (mat->get_building_large_max_depth() - mat->get_building_large_min_depth());
199 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()));
200 height = floors * (2.8 + mt_rand(&seed));
201 pitched = (mt_rand(&seed) < mat->get_building_large_pitch());
204 Building building = Building(buildingtype,
211 buildings.push_back(building);
213 // Now create an OSG Geometry based on the Building
214 float cw = 0.5f * building.width;
215 float cd = building.depth;
216 float ch = building.height;
218 // 0,0,0 is the bottom center of the front
219 // face, e.g. where the front door would be
222 // This exteds 10m below the main section
224 v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom right
225 v->push_back( osg::Vec3( 0, cw, -10) ); // bottom left
226 v->push_back( osg::Vec3( 0, cw, 0) ); // top left
227 v->push_back( osg::Vec3( 0, -cw, 0) ); // top right
229 for (int i=0; i<4; ++i)
230 n->push_back( osg::Vec3(1, 0, 0) ); // normal
233 v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom right
234 v->push_back( osg::Vec3( 0, -cw, -10) ); // bottom left
235 v->push_back( osg::Vec3( 0, -cw, 0) ); // top left
236 v->push_back( osg::Vec3( -cd, -cw, 0) ); // top right
238 for (int i=0; i<4; ++i)
239 n->push_back( osg::Vec3(0, -1, 0) ); // normal
242 v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom right
243 v->push_back( osg::Vec3( -cd, -cw, -10) ); // bottom left
244 v->push_back( osg::Vec3( -cd, -cw, 0) ); // top left
245 v->push_back( osg::Vec3( -cd, cw, 0) ); // top right
247 for (int i=0; i<4; ++i)
248 n->push_back( osg::Vec3(-1, 0, 0) ); // normal
251 v->push_back( osg::Vec3( 0, cw, -10) ); // bottom right
252 v->push_back( osg::Vec3( -cd, cw, -10) ); // bottom left
253 v->push_back( osg::Vec3( -cd, cw, 0) ); // top left
254 v->push_back( osg::Vec3( 0, cw, 0) ); // top right
256 for (int i=0; i<4; ++i)
257 n->push_back( osg::Vec3(0, 1, 0) ); // normal
261 v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom right
262 v->push_back( osg::Vec3( 0, cw, 0) ); // bottom left
263 v->push_back( osg::Vec3( 0, cw, ch) ); // top left
264 v->push_back( osg::Vec3( 0, -cw, ch) ); // top right
266 for (int i=0; i<4; ++i)
267 n->push_back( osg::Vec3(1, 0, 0) ); // normal
270 v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom right
271 v->push_back( osg::Vec3( 0, -cw, 0) ); // bottom left
272 v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
273 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
275 for (int i=0; i<4; ++i)
276 n->push_back( osg::Vec3(0, -1, 0) ); // normal
279 v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom right
280 v->push_back( osg::Vec3( -cd, -cw, 0) ); // bottom left
281 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top left
282 v->push_back( osg::Vec3( -cd, cw, ch) ); // top right
284 for (int i=0; i<4; ++i)
285 n->push_back( osg::Vec3(-1, 0, 0) ); // normal
288 v->push_back( osg::Vec3( 0, cw, 0) ); // bottom right
289 v->push_back( osg::Vec3( -cd, cw, 0) ); // bottom left
290 v->push_back( osg::Vec3( -cd, cw, ch) ); // top left
291 v->push_back( osg::Vec3( 0, cw, ch) ); // top right
293 for (int i=0; i<4; ++i)
294 n->push_back( osg::Vec3(0, 1, 0) ); // normal
297 if (building.pitched) {
299 // Front pitched roof
300 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
301 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
302 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
303 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
305 for (int i=0; i<4; ++i)
306 n->push_back( osg::Vec3(0.707, 0, 0.707) ); // normal
309 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
310 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
311 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
312 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top right
314 for (int i=0; i<4; ++i)
315 n->push_back( osg::Vec3(0, -1, 0) ); // normal
318 v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom right
319 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom left
320 v->push_back( osg::Vec3(-0.5*cd, -cw, ch+3) ); // top left
321 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
323 for (int i=0; i<4; ++i)
324 n->push_back( osg::Vec3(-0.707, 0, 0.707) ); // normal
326 // Right pitched roof
327 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
328 v->push_back( osg::Vec3( -cd, cw, ch) ); // bottom left
329 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top left
330 v->push_back( osg::Vec3(-0.5*cd, cw, ch+3) ); // top right
332 for (int i=0; i<4; ++i)
333 n->push_back( osg::Vec3(0, 1, 0) ); // normal
335 // If the roof isn't pitched, we still generate the
336 // vertices for simplicity later.
339 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom right
340 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom left
341 v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
342 v->push_back( osg::Vec3(-cd, -cw, ch) ); // top right
344 for (int i=0; i<4; ++i)
345 n->push_back( osg::Vec3(0, 0, 1) ); // normal
347 // Left non-pitched roof
348 v->push_back( osg::Vec3( -cd, -cw, ch) ); // bottom right
349 v->push_back( osg::Vec3( 0, -cw, ch) ); // bottom left
350 v->push_back( osg::Vec3( 0, -cw, ch) ); // top left
351 v->push_back( osg::Vec3( -cd, -cw, ch) ); // top right
353 for (int i=0; i<4; ++i)
354 n->push_back( osg::Vec3(0, -1, 0) ); // normal
357 v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom right
358 v->push_back( osg::Vec3(-cd, -cw, ch) ); // bottom left
359 v->push_back( osg::Vec3(-cd, -cw, ch) ); // top left
360 v->push_back( osg::Vec3(-cd, cw, ch) ); // top right
362 for (int i=0; i<4; ++i)
363 n->push_back( osg::Vec3(1, 0, 0) ); // normal
365 // Right pitched roof
366 v->push_back( osg::Vec3( 0, cw, ch) ); // bottom right
367 v->push_back( osg::Vec3(-cd, cw, ch) ); // bottom left
368 v->push_back( osg::Vec3(-cd, cw, ch) ); // top left
369 v->push_back( osg::Vec3( 0, cw, ch) ); // top right
371 for (int i=0; i<4; ++i)
372 n->push_back( osg::Vec3(0, 1, 0) ); // normal
375 // The 1024x1024 texture is split into 32x16 blocks.
376 // For a small building, each block is 6m wide and 3m high.
377 // For a medium building, each block is 10m wide and 3m high.
378 // For a large building, each block is 20m wide and 3m high
380 if (building.type == SGBuildingBin::SMALL) {
381 // Small buildings are represented on the bottom 5 rows of 3 floors
382 int row = ((int) (mt_rand(&seed) * 1000)) % 5;
383 float base_y = (float) row * 16.0 * 3.0 / 1024.0;
384 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
385 float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
386 float right_x = 0.0f;
387 float front_x = 384.0/1024.0;
388 float back_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f);
390 // BASEMENT - uses the baseline texture
391 for (unsigned int i = 0; i < 16; i++) {
392 t->push_back( osg::Vec2( left_x, base_y) );
396 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
397 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
398 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
399 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
402 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
403 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
404 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
405 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
407 // Back (same as front for the moment)
408 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
409 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
410 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
411 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
413 // Right (same as left for the moment)
414 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
415 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
416 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
417 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
420 if (building.pitched) {
421 // Use the entire height of the roof texture
422 top_y = base_y + 16.0 * 3.0 / 1024.0;
423 left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
424 right_x = 512/1024.0;
425 front_x = 480.0/1024.0;
426 back_x = 512.0/1024.0;
429 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
430 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
431 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
432 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
435 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
436 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
437 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
438 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
440 // Back (same as front for the moment)
441 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
442 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
443 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
444 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
446 // Right (same as left for the moment)
447 t->push_back( osg::Vec2( front_x, base_y) ); // bottom right
448 t->push_back( osg::Vec2( back_x, base_y) ); // bottom left
449 t->push_back( osg::Vec2( back_x, top_y ) ); // top left
450 t->push_back( osg::Vec2( front_x, top_y ) ); // top right
453 left_x = 640.0/1024.0;
454 right_x = 512.0/1024.0;
455 // Use the entire height of the roof texture
456 top_y = base_y + 16.0 * 3.0 / 1024.0;
458 // Flat roofs still have 4 surfaces, so we need to set the textures
459 for (int i=0; i<4; ++i) {
460 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
461 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
462 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
463 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
469 if (building.type == SGBuildingBin::MEDIUM)
471 int column = ((int) (mt_rand(&seed) * 1000)) % 5;
472 float base_y = 288 / 1024.0;
473 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
474 float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
475 float right_x = column * 192.0 /1024.0;
477 // BASEMENT - uses the baseline texture
478 for (unsigned int i = 0; i < 16; i++) {
479 t->push_back( osg::Vec2( left_x, base_y) );
484 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
485 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
486 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
487 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
490 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
491 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
492 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
493 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
495 // Back (same as front for the moment)
496 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
497 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
498 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
499 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
501 // Right (same as left for the moment)
502 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
503 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
504 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
505 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
508 if (building.pitched) {
509 base_y = 288.0/1024.0;
510 top_y = 576.0/1024.0;
511 left_x = 960.0/1024.0;
515 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
516 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
517 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
518 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
521 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
522 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
523 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
524 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
526 // Back (same as front for the moment)
527 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
528 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
529 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
530 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
532 // Right (same as left for the moment)
533 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
534 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
535 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
536 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
540 top_y = 576.0/1024.0;
541 left_x = column * 192.0 /1024.0;
542 right_x = (column + 1)* 192.0 /1024.0;
544 // Flat roofs still have 4 surfaces
545 for (int i=0; i<4; ++i) {
546 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
547 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
548 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
549 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
554 if (building.type == SGBuildingBin::LARGE)
556 int column = ((int) (mt_rand(&seed) * 1000)) % 8;
557 float base_y = 576 / 1024.0;
558 float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
559 float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
560 float right_x = column * 128.0 /1024.0;
562 // BASEMENT - uses the baseline texture
563 for (unsigned int i = 0; i < 16; i++) {
564 t->push_back( osg::Vec2( left_x, base_y) );
569 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
570 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
571 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
572 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
575 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
576 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
577 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
578 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
580 // Back (same as front for the moment)
581 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
582 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
583 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
584 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
586 // Right (same as left for the moment)
587 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
588 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
589 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
590 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
593 if (building.pitched) {
597 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
598 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
599 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
600 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
603 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
604 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
605 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
606 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
608 // Back (same as front for the moment)
609 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
610 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
611 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
612 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
614 // Right (same as left for the moment)
615 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
616 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
617 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
618 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
624 // Flat roofs still have 4 surfaces
625 for (int i=0; i<4; ++i) {
626 t->push_back( osg::Vec2( right_x, base_y) ); // bottom right
627 t->push_back( osg::Vec2( left_x, base_y) ); // bottom left
628 t->push_back( osg::Vec2( left_x, top_y ) ); // top left
629 t->push_back( osg::Vec2( right_x, top_y ) ); // top right
635 // Set the vertex, texture and normals. Colors will be set per-instance
637 sharedGeometry->setVertexArray(v);
638 sharedGeometry->setTexCoordArray(0, t);
639 sharedGeometry->setNormalArray(n);
641 // Work around a bug in OSG 3.2.0 where BIND_PER_VERTEX appears
642 // not to take effect if the normal array is set subsequently.
643 sharedGeometry->setNormalBinding(osg::Geometry::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;