3 * Copyright (C) 2011 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,
22 #ifndef SG_BUILDING_BIN_HXX
23 #define SG_BUILDING_BIN_HXX
31 #include <osg/Geometry>
33 #include <osg/MatrixTransform>
35 #include <osg/ShadeModel>
36 #include <osg/Material>
37 #include <osg/CullFace>
40 #include <simgear/scene/util/OsgMath.hxx>
41 #include <simgear/scene/material/mat.hxx>
43 #include <simgear/scene/util/QuadTreeBuilder.hxx>
44 #include <simgear/scene/util/RenderConstants.hxx>
45 #include <simgear/scene/util/StateAttributeFactory.hxx>
46 #include <simgear/structure/OSGUtils.hxx>
48 #define SG_BUILDING_QUAD_TREE_DEPTH 2
49 #define SG_BUILDING_FADE_OUT_LEVELS 4
58 // Number of buildings to auto-generate. Individual
59 // building instances are taken from this set.
60 static const unsigned int BUILDING_SET_SIZE = 200;
62 static const unsigned int QUADS_PER_BUILDING = 12;
63 static const unsigned int VERTICES_PER_BUILDING = 4 * QUADS_PER_BUILDING;
64 static const unsigned int VERTICES_PER_BUILDING_SET = BUILDING_SET_SIZE * VERTICES_PER_BUILDING;
74 Building(BuildingType t, float w, float d, float h, int f, bool pitch) :
81 radius(std::max(d, 0.5f*w))
92 float getFootprint() {
97 // The set of buildings that are instantiated
98 typedef std::vector<Building> BuildingList;
99 BuildingList smallBuildings;
100 BuildingList mediumBuildings;
101 BuildingList largeBuildings;
103 std::string material_name;
105 std::string lightMap;
107 // Fraction of buildings of this type
108 float smallBuildingFraction;
109 float mediumBuildingFraction;
111 // The maximum radius of each building type
112 float smallBuildingMaxRadius;
113 float mediumBuildingMaxRadius;
114 float largeBuildingMaxRadius;
116 // The maximum depth of each building type
117 float smallBuildingMaxDepth;
118 float mediumBuildingMaxDepth;
119 float largeBuildingMaxDepth;
121 // Shared geometries of the building set
122 ref_ptr<Geometry> smallSharedGeometry;
123 ref_ptr<Geometry> mediumSharedGeometry;
124 ref_ptr<Geometry> largeSharedGeometry;
126 struct BuildingInstance {
127 BuildingInstance(SGVec3f p, float r, const BuildingList* bl, ref_ptr<Geometry> sg) :
134 BuildingInstance(SGVec3f p, BuildingInstance b) :
136 rotation(b.rotation),
137 buildingList(b.buildingList),
138 sharedGeometry(b.sharedGeometry)
144 // References to allow the QuadTreeBuilder to work
145 const BuildingList* buildingList;
146 ref_ptr<Geometry> sharedGeometry;
148 SGVec3f getPosition() { return position; }
149 float getRotation() { return rotation; }
151 float getDistSqr(SGVec3f p) {
152 return distSqr(p, position);
155 const osg::Vec4f getColorValue() {
156 return osg::Vec4f(toOsg(position), rotation);
160 // Information for an instance of a building - position and orientation
161 typedef std::vector<BuildingInstance> BuildingInstanceList;
162 BuildingInstanceList smallBuildingLocations;
163 BuildingInstanceList mediumBuildingLocations;
164 BuildingInstanceList largeBuildingLocations;
168 SGBuildingBin(const SGMaterial *mat);
171 smallBuildings.clear();
172 mediumBuildings.clear();
173 largeBuildings.clear();
174 smallBuildingLocations.clear();
175 mediumBuildingLocations.clear();
176 largeBuildingLocations.clear();
179 void insert(SGVec3f p, float r, BuildingType type);
180 int getNumBuildings();
182 bool checkMinDist (SGVec3f p, float radius);
184 std::string getMaterialName() { return material_name; }
186 BuildingType getBuildingType(float roll);
188 float getBuildingMaxRadius(BuildingType);
189 float getBuildingMaxDepth(BuildingType);
191 // Helper classes for creating the quad tree
192 struct MakeBuildingLeaf
194 MakeBuildingLeaf(float range, Effect* effect) :
195 _range(range), _effect(effect) {}
197 MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
198 _range(rhs._range), _effect(rhs._effect)
201 LOD* operator() () const
203 LOD* result = new LOD;
205 // Create a series of LOD nodes so trees cover decreases slightly
206 // gradually with distance from _range to 2*_range
207 for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++)
209 EffectGeode* geode = new EffectGeode;
210 geode->setEffect(_effect.get());
211 result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));
217 ref_ptr<Effect> _effect;
220 struct AddBuildingLeafObject
222 Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const
224 Geometry* geom = simgear::clone(building.sharedGeometry.get(), CopyOp::SHALLOW_COPY);
225 geom->setColorArray(new Vec4Array);
226 geom->setColorBinding(Geometry::BIND_PER_VERTEX);
227 geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS));
231 void operator() (LOD* lod, const BuildingInstance& building) const
233 Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
234 unsigned int numDrawables = geode->getNumDrawables();
236 // Get the last geometry of to be added and check if there is space for
237 // another building instance within it. This is done by checking
238 // if the number of Color values matches the number of vertices.
239 // The color array is used to store the position of a particular
243 if (numDrawables == 0) {
244 // Create a new copy of the shared geometry to instantiate
245 geom = createNewBuildingGeometryInstance(building);
246 geode->addDrawable(geom);
248 geom = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
251 // Check if this building is too close to any other others.
252 DrawArrays* primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
253 Vec4Array* posArray = static_cast<Vec4Array*>(geom->getColorArray());
255 // Now check if this geometry is full.
256 if (posArray->size() >= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
257 // This particular geometry is full, so we generate another
258 // by taking a shallow copy of the shared Geomety.
259 geom = createNewBuildingGeometryInstance(building);
260 geode->addDrawable(geom);
261 posArray = static_cast<Vec4Array*>(geom->getColorArray());
262 SG_LOG(SG_TERRAIN, SG_DEBUG, "Added new geometry to building geod: " << geode->getNumDrawables());
265 // We now have a geometry with space for this new building.
266 // Set the position and rotation
267 osg::Vec4f c = osg::Vec4f(toOsg(building.position), building.rotation);
268 posArray->insert(posArray->end(), VERTICES_PER_BUILDING, c);
269 size_t numVerts = posArray->size();
270 primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
271 primSet->setCount(numVerts);
275 struct GetBuildingCoord
277 Vec3 operator() (const BuildingInstance& building) const
279 return toOsg(building.position);
283 typedef QuadTreeBuilder<LOD*, BuildingInstance, MakeBuildingLeaf, AddBuildingLeafObject,
284 GetBuildingCoord> BuildingGeometryQuadtree;
286 struct BuildingInstanceTransformer
288 BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {}
289 BuildingInstance operator()(const BuildingInstance& buildingInstance) const
291 Vec3 pos = toOsg(buildingInstance.position) * mat;
292 return BuildingInstance(toSG(pos), buildingInstance);
297 ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);
302 typedef std::list<SGBuildingBin*> SGBuildingBinList;
305 osg::Group* createRandomBuildings(SGBuildingBinList buildinglist, const osg::Matrix& transform,
306 const SGReaderWriterOptions* options);