Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
TreeLoader2D.cpp
Go to the documentation of this file.
1/*-------------------------------------------------------------------------------------
2Copyright (c) 2006 John Judnich
3
4This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
5Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
6 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
7 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
8 3. This notice may not be removed or altered from any source distribution.
9-------------------------------------------------------------------------------------*/
10
11#include <OgreRoot.h>
12#include <OgreException.h>
13#include <OgreVector3.h>
14#include <OgreQuaternion.h>
15#include <OgreLogManager.h>
16#include <OgreStringConverter.h>
17
18#include "TreeLoader2D.h"
19#include "PagedGeometry.h"
20#include "PropertyMaps.h"
21
22using namespace Ogre;
23
24namespace Forests {
25
27{
28 //Calculate grid size
31
32 //Reset height function
33 heightFunction = NULL;
35
36 //Make sure the bounds are aligned with PagedGeometry's grid, so the TreeLoader's grid tiles will have a 1:1 relationship
37 actualBounds = bounds;
38 gridBounds = bounds;
39 gridBounds.left = pageSize * Math::Floor((gridBounds.left - geom->getBounds().left) / pageSize) + geom->getBounds().left;
40 gridBounds.top = pageSize * Math::Floor((gridBounds.top - geom->getBounds().top) / pageSize) + geom->getBounds().top;
41 gridBounds.right = pageSize * Math::Ceil((gridBounds.right - geom->getBounds().left) / pageSize) + geom->getBounds().left;
42 gridBounds.bottom = pageSize * Math::Ceil((gridBounds.bottom - geom->getBounds().top) / pageSize) + geom->getBounds().top;
43
44 //Calculate page grid size
45 pageGridX = (int)Math::Ceil(gridBounds.width() / pageSize) + 1;
46 pageGridZ = (int)Math::Ceil(gridBounds.height() / pageSize) + 1;
47
48 //Reset color map
49 colorMap = NULL;
51
52 //Default scale range
53 maximumScale = 2.0f;
54 minimumScale = 0.0f;
55}
56
58{
59 //Delete all page grids
61 for (i = pageGridList.begin(); i != pageGridList.end(); ++i){
62 delete[] i->second;
63 }
64 pageGridList.clear();
65}
66
67void TreeLoader2D::addTree(Entity *entity, const Vector3 &position, Degree yaw, Real scale, void* userData)
68{
69 Vector3 pos = position;
70
71 //Check that the tree is within bounds (DEBUG)
72 #ifdef _DEBUG
73 const Real smallVal = 0.01f;
74 if (pos.x < actualBounds.left-smallVal || pos.x > actualBounds.right+smallVal || pos.z < actualBounds.top-smallVal || pos.z > actualBounds.bottom+smallVal)
75 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree position is out of bounds", "TreeLoader::addTree()");
76 if (scale < minimumScale || scale > maximumScale)
77 OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree scale out of range", "TreeLoader::addTree()");
78 #endif
79
80 //If the tree is slightly out of bounds (due to imprecise coordinate conversion), fix it
81 if (pos.x < actualBounds.left)
82 pos.x = actualBounds.left;
83 else if (pos.x > actualBounds.right)
84 pos.x = actualBounds.right;
85
86 if (pos.z < actualBounds.top)
87 pos.z = actualBounds.top;
88 else if (pos.z > actualBounds.bottom)
89 pos.z = actualBounds.bottom;
90
91 //Real x = pos.x;
92 //Real z = pos.z;
93
94 std::vector<TreeDef> *pageGrid = 0;
95 //Find the appropriate page grid for the entity
96 PageGridListIterator i = pageGridList.find(entity);
97 if (i != pageGridList.end())
98 pageGrid = i->second; // If it exists, get the page grid
99 else
100 {
101 // If it does not exist, create a new page grid
102 pageGrid = new std::vector<TreeDef>[pageGridX * pageGridZ];
103 // Register the new page grid in the pageGridList for later retrieval
104 pageGridList.insert(PageGridListValue(entity, pageGrid));
105 }
106
107 //Calculate the gridbounds-relative position of the tree
108 Real xrel = pos.x - gridBounds.left;
109 Real zrel = pos.z - gridBounds.top;
110
111 //Get the appropriate grid element based on the new tree's position
112 int pageX = (int)Math::Floor(xrel / pageSize); // bad perfomance float --> int
113 int pageZ = (int)Math::Floor(zrel / pageSize); // bad perfomance float --> int
114 std::vector<TreeDef> &treeList = _getGridPage(pageGrid, pageX, pageZ);
115
116 //Create the new tree
117 TreeDef tree;
118 tree.xPos = static_cast<uint16>(0xFFFF * (xrel - (pageX * pageSize)) / pageSize);
119 tree.zPos = static_cast<uint16>(0xFFFF * (zrel - (pageZ * pageSize)) / pageSize);
120 tree.rotation = static_cast<uint8>(Ogre::Real(0.708) * yaw.valueDegrees()); // 0.708 ~= 255 / 360
121 tree.scale = static_cast<uint8>(Ogre::Real(255.) * ((scale - minimumScale) / maximumScale));
122
123 //Add it to the tree list
124 treeList.push_back(tree);
125
126 //Rebuild geometry if necessary
127 geom->reloadGeometryPage(Vector3(pos.x, 0, pos.z));
128}
129
130void TreeLoader2D::deleteTrees(const Ogre::Vector3 &position, Ogre::Real radius, Entity *type)
131{
132 //First convert the coordinate to PagedGeometry's local system
133 Vector3 pos = position;
134
135 //If the position is slightly out of bounds, fix it
136 if (pos.x < actualBounds.left)
137 pos.x = actualBounds.left;
138 else if (pos.x > actualBounds.right)
139 pos.x = actualBounds.right;
140
141 if (pos.z < actualBounds.top)
142 pos.z = actualBounds.top;
143 else if (pos.z > actualBounds.bottom)
144 pos.z = actualBounds.bottom;
145
146 Real x = pos.x;
147 Real z = pos.z;
148
149 //Determine the grid blocks which might contain the requested trees
150 int minPageX = (int)Math::Floor(((x-radius) - gridBounds.left) / pageSize);
151 int minPageZ = (int)Math::Floor(((z-radius) - gridBounds.top) / pageSize);
152 int maxPageX = (int)Math::Floor(((x+radius) - gridBounds.left) / pageSize);
153 int maxPageZ = (int)Math::Floor(((z+radius) - gridBounds.top) / pageSize);
154 Real radiusSq = radius * radius;
155
156 if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1;
157 if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1;
158 if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1;
159 if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1;
160
161 PageGridListIterator it, end;
162 if (type == NULL){
163 //Scan all entity types
164 it = pageGridList.begin();
165 end = pageGridList.end();
166 } else {
167 //Only scan entities of the given type
168 it = pageGridList.find(type);
169 assert(it != pageGridList.end());
170 end = it; ++end;
171 }
172
173 //Scan all the grid blocks
174 while (it != end){
175 std::vector<TreeDef> *pageGrid = it->second;
176
177 for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){
178 for (int tileX = minPageX; tileX <= maxPageX; ++tileX){
179 bool modified = false;
180
181 //Scan all trees in grid block
182 std::vector<TreeDef> &treeList = _getGridPage(pageGrid, tileX, tileZ);
183 uint32 i = 0;
184 while (i < treeList.size())
185 {
186 //Get tree distance
187 Ogre::Real distX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize) - pos.x;
188 Ogre::Real distZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize) - pos.z;
189 Ogre::Real distSq = distX * distX + distZ * distZ;
190
191 if (distSq <= radiusSq){
192 //If it's within the radius, delete it
193 treeList[i] = treeList.back();
194 treeList.pop_back();
195 modified = true;
196 }
197 else
198 ++i;
199 }
200
201 //Rebuild geometry if necessary
202 if (modified){
203 Vector3 pos(gridBounds.left + ((0.5f + tileX) * pageSize), 0, gridBounds.top + ((0.5f + tileZ) * pageSize));
205 }
206 }
207 }
208
209 ++it;
210 }
211}
212
213void TreeLoader2D::deleteTrees(TBounds area, Ogre::Entity *type)
214{
215 //If the area is slightly out of bounds, fix it
216 if (area.left < actualBounds.left) area.left = actualBounds.left;
217 else if (area.left > actualBounds.right) area.left = actualBounds.right;
218 if (area.top < actualBounds.top) area.top = actualBounds.top;
219 else if (area.top > actualBounds.bottom) area.top = actualBounds.bottom;
220 if (area.right < actualBounds.left) area.right = actualBounds.left;
221 else if (area.right > actualBounds.right) area.right = actualBounds.right;
222 if (area.bottom < actualBounds.top) area.bottom = actualBounds.top;
223 else if (area.bottom > actualBounds.bottom) area.bottom = actualBounds.bottom;
224
225 //Determine the grid blocks which might contain the requested trees
226 int minPageX = (int)Math::Floor((area.left - gridBounds.left) / pageSize);
227 int minPageZ = (int)Math::Floor((area.top - gridBounds.top) / pageSize);
228 int maxPageX = (int)Math::Floor((area.right - gridBounds.left) / pageSize);
229 int maxPageZ = (int)Math::Floor((area.bottom - gridBounds.top) / pageSize);
230
231 if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1;
232 if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1;
233 if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1;
234 if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1;
235
236 PageGridListIterator it, end;
237 if (type == NULL){
238 //Scan all entity types
239 it = pageGridList.begin();
240 end = pageGridList.end();
241 } else {
242 //Only scan entities of the given type
243 it = pageGridList.find(type);
244 assert(it != pageGridList.end());
245 end = it; ++end;
246 }
247
248 //Scan all the grid blocks
249 while (it != end){
250 std::vector<TreeDef> *pageGrid = it->second;
251
252 for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){
253 for (int tileX = minPageX; tileX <= maxPageX; ++tileX){
254 bool modified = false;
255
256 //Scan all trees in grid block
257 std::vector<TreeDef> &treeList = _getGridPage(pageGrid, tileX, tileZ);
258 uint32 i = 0;
259 while (i < treeList.size()){
260 //Check if tree is in bounds
261 Ogre::Real posX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize);
262 Ogre::Real posZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize);
263 if (posX >= area.left && posX <= area.right && posZ >= area.top && posZ <= area.bottom) {
264 //If it's within the radius, delete it
265 treeList[i] = treeList.back();
266 treeList.pop_back();
267 modified = true;
268 }
269 else
270 ++i;
271 }
272
273 //Rebuild geometry if necessary
274 if (modified){
275 Vector3 pos(gridBounds.left + ((0.5f + tileX) * pageSize), 0, gridBounds.top + ((0.5f + tileZ) * pageSize));
277 }
278 }
279 }
280
281 ++it;
282 }
283}
284
285void TreeLoader2D::setColorMap(const Ogre::String &mapFile, MapChannel channel)
286{
287 if (colorMap){
288 colorMap->unload();
289 colorMap = NULL;
290 }
291 if (mapFile != ""){
292 colorMap = ColorMap::load(mapFile, channel);
294 }
295}
296
297void TreeLoader2D::setColorMap(Ogre::TexturePtr map, MapChannel channel)
298{
299 if (colorMap){
300 colorMap->unload();
301 colorMap = NULL;
302 }
303 if (map){
304 colorMap = ColorMap::load(map, channel);
306 }
307}
308
310{
311 //Calculate x/z indexes for the grid array
312 page.xIndex -= (int)Math::Floor(gridBounds.left / pageSize);
313 page.zIndex -= (int)Math::Floor(gridBounds.top / pageSize);
314
315 //Check if the requested page is in bounds
316 if (page.xIndex < 0 || page.zIndex < 0 || page.xIndex >= pageGridX || page.zIndex >= pageGridZ)
317 return;
318
319 //For each tree type...
321 for (i = pageGridList.begin(); i != pageGridList.end(); ++i){
322 //Get the appropriate tree list for the specified page
323 std::vector<TreeDef> *pageGrid = i->second;
324 std::vector<TreeDef> &treeList = _getGridPage(pageGrid, page.xIndex, page.zIndex);
325 Entity *entity = i->first;
326
327 //Load the listed trees into the page
328 std::vector<TreeDef>::iterator o;
329 for (o = treeList.begin(); o != treeList.end(); ++o){
330 //Get position
331 Vector3 pos;
332 pos.x = page.bounds.left + ((Real)o->xPos / 65535) * pageSize;
333 pos.z = page.bounds.top + ((Real)o->zPos / 65535) * pageSize;
334
335 //Calculate terrain height at pos.x / pos.z to get pos.y
336 if (heightFunction != NULL)
337 pos.y = heightFunction(pos.x, pos.z, heightFunctionUserData);
338 else
339 pos.y = 0.0f;
340
341 //Get rotation
342 Degree angle((Real)o->rotation * (360.0f / 255));
343 Quaternion rot(angle, Vector3::UNIT_Y);
344
345 //Get scale
346 Vector3 scale;
347 scale.y = (Real)o->scale * (maximumScale / 255) + minimumScale;
348 scale.x = scale.y;
349 scale.z = scale.y;
350
351 //Get color
352 ColourValue color;
353 if (colorMap)
354 color = colorMap->getColorAt_Unpacked(pos.x, pos.z, actualBounds);
355 else
356 color = ColourValue::White;
357
358 addEntity(entity, pos, rot, scale, color);
359 }
360 }
361}
362
363} // namespace Forests
static ColorMap * load(const Ogre::String &fileName, MapChannel channel=CHANNEL_COLOR)
void setFilter(MapFilter filter)
Sets the filtering mode used for this color map.
Ogre::ColourValue getColorAt_Unpacked(Ogre::Real x, Ogre::Real z, const Ogre::TRect< Ogre::Real > &mapBounds)
Gets the color value at the specified position.
void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale=Ogre::Vector3::UNIT_SCALE, const Ogre::ColourValue &color=Ogre::ColourValue::White)
Call this from loadPage() to add an entity to the page being loaded.
A class providing highly optimized methods for rendering massive amounts of geometry.
Ogre::Real getPageSize()
Gets the current page size.
const TBounds & getBounds() const
Gets the current geometry boundary.
void reloadGeometryPage(const Ogre::Vector3 &point)
Reloads geometry at the given location.
TreeLoader2D(PagedGeometry *geom, const TBounds &bounds)
Creates a new TreeLoader2D object.
void deleteTrees(const Ogre::Vector3 &position, Ogre::Real radius=0.001f, Ogre::Entity *type=NULL)
Deletes trees within a certain radius of the given coordinates.
std::pair< Ogre::Entity *, std::vector< TreeDef > * > PageGridListValue
void setColorMap(const Ogre::String &mapFile, MapChannel channel=CHANNEL_COLOR)
Sets the color map used to color trees.
std::vector< TreeDef > & _getGridPage(std::vector< TreeDef > *grid, int x, int z)
PagedGeometry * geom
void loadPage(PageInfo &page)
This should be overridden to load a specified region of entities.
std::map< Ogre::Entity *, std::vector< TreeDef > * > pageGridList
void addTree(Ogre::Entity *entity, const Ogre::Vector3 &position, Ogre::Degree yaw=Ogre::Degree(0), Ogre::Real scale=1.0f, void *userData=NULL)
Adds an entity to the scene with the specified location, rotation, and scale.
Ogre::Real(* heightFunction)(Ogre::Real x, Ogre::Real z, void *userData)
std::map< Ogre::Entity *, std::vector< TreeDef > * >::iterator PageGridListIterator
@ MAPFILTER_NONE
Use no filtering - fast, but may appear blocky.
Ogre::TRect< Ogre::Real > TBounds
Define TBounds as a TRect using Real numeric units.
MapChannel
Specifies which color channel(s) to extract from an image.
Useful page information supplied to a pageloader.
int zIndex
The Z index of the page tile.
TBounds bounds
The page boundaries in which all entities should be placed.
int xIndex
The X index of the page tile.