1
/*-------------------------------------------------------------------------------------
2
Copyright (c) 2006 John Judnich
4
This 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.
5
Permission 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
-------------------------------------------------------------------------------------*/
11
#include "TreeLoader2D.h"
12
#include "PagedGeometry.h"
13
#include "PropertyMaps.h"
16
#include <OgreException.h>
17
#include <OgreVector3.h>
18
#include <OgreQuaternion.h>
19
#include <OgreLogManager.h>
20
#include <OgreStringConverter.h>
23
namespace PagedGeometry {
25
TreeLoader2D::TreeLoader2D(PagedGeometry *geom, const TRect<Real> &bounds)
28
TreeLoader2D::geom = geom;
29
pageSize = geom->getPageSize();
31
//Reset height function
32
heightFunction = NULL;
33
heightFunctionUserData = NULL;
35
//Make sure the bounds are aligned with PagedGeometry's grid, so the TreeLoader's grid tiles will have a 1:1 relationship
36
actualBounds = bounds;
38
gridBounds.left = pageSize * Math::Floor((gridBounds.left - geom->getBounds().left) / pageSize) + geom->getBounds().left;
39
gridBounds.top = pageSize * Math::Floor((gridBounds.top - geom->getBounds().top) / pageSize) + geom->getBounds().top;
40
gridBounds.right = pageSize * Math::Ceil((gridBounds.right - geom->getBounds().left) / pageSize) + geom->getBounds().left;
41
gridBounds.bottom = pageSize * Math::Ceil((gridBounds.bottom - geom->getBounds().top) / pageSize) + geom->getBounds().top;
43
//Calculate page grid size
44
pageGridX = Math::Ceil(gridBounds.width() / pageSize) + 1;
45
pageGridZ = Math::Ceil(gridBounds.height() / pageSize) + 1;
49
colorMapFilter = MAPFILTER_NONE;
56
TreeLoader2D::~TreeLoader2D()
58
//Delete all page grids
59
PageGridListIterator i;
60
for (i = pageGridList.begin(); i != pageGridList.end(); ++i){
66
void TreeLoader2D::addTree(Entity *entity, const Vector3 &position, Degree yaw, Real scale, void* userData)
68
//First convert the coordinate to PagedGeometry's local system
69
#ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM
70
Vector3 pos = geom->_convertToLocal(position);
72
Vector3 pos = position;
75
//If the tree is slightly out of bounds (due to imprecise coordinate conversion), fix it
76
if (pos.x < actualBounds.left)
77
pos.x = actualBounds.left;
78
else if (pos.x > actualBounds.right)
79
pos.x = actualBounds.right;
81
if (pos.z < actualBounds.top)
82
pos.z = actualBounds.top;
83
else if (pos.x > actualBounds.bottom)
84
pos.z = actualBounds.bottom;
89
//Check that the tree is within bounds (DEBUG)
91
const Real smallVal = 0.01f;
92
if (pos.x < actualBounds.left-smallVal || pos.x > actualBounds.right+smallVal || pos.z < actualBounds.top-smallVal || pos.z > actualBounds.bottom+smallVal)
93
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree position is out of bounds", "TreeLoader::addTree()");
94
if (scale < minimumScale || scale > maximumScale)
95
OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree scale out of range", "TreeLoader::addTree()");
98
//Find the appropriate page grid for the entity
99
PageGridListIterator i;
100
i = pageGridList.find(entity);
102
std::vector<TreeDef> *pageGrid;
103
if (i != pageGridList.end()){
104
//If it exists, get the page grid
105
pageGrid = i->second;
107
//If it does not exist, create a new page grid
108
pageGrid = new std::vector<TreeDef>[pageGridX * pageGridZ];
110
//Register the new page grid in the pageGridList for later retrieval
111
pageGridList.insert(PageGridListValue(entity, pageGrid));
114
//Calculate the gridbounds-relative position of the tree
115
Real xrel = x - gridBounds.left;
116
Real zrel = z - gridBounds.top;
118
//Get the appropriate grid element based on the new tree's position
119
int pageX = Math::Floor(xrel / pageSize);
120
int pageZ = Math::Floor(zrel / pageSize);
121
std::vector<TreeDef> &treeList = _getGridPage(pageGrid, pageX, pageZ);
123
//Create the new tree
125
tree.xPos = 65535 * (xrel - (pageX * pageSize)) / pageSize;
126
tree.zPos = 65535 * (zrel - (pageZ * pageSize)) / pageSize;
127
tree.rotation = 255 * (yaw.valueDegrees() / 360.0f);
128
tree.scale = 255 * ((scale - minimumScale) / maximumScale);
130
#ifdef PAGEDGEOMETRY_USER_DATA
131
tree.userData = userData;
134
//Add it to the tree list
135
treeList.push_back(tree);
137
//Rebuild geometry if necessary
138
geom->reloadGeometryPage(Vector3(x, 0, z));
141
#ifdef PAGEDGEOMETRY_USER_DATA
146
TreeLoader2D::deleteTrees(const Ogre::Vector3 &position, Real radius, Entity *type)
148
//First convert the coordinate to PagedGeometry's local system
149
#ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM
150
Vector3 pos = geom->_convertToLocal(position);
152
Vector3 pos = position;
155
#ifdef PAGEDGEOMETRY_USER_DATA
156
//Keep a list of user-defined data associated with deleted trees
157
std::vector<void*> deletedUserData;
160
//If the position is slightly out of bounds, fix it
161
if (pos.x < actualBounds.left)
162
pos.x = actualBounds.left;
163
else if (pos.x > actualBounds.right)
164
pos.x = actualBounds.right;
166
if (pos.z < actualBounds.top)
167
pos.z = actualBounds.top;
168
else if (pos.x > actualBounds.bottom)
169
pos.z = actualBounds.bottom;
174
//Determine the grid blocks which might contain the requested trees
175
int minPageX = Math::Floor(((x-radius) - gridBounds.left) / pageSize);
176
int minPageZ = Math::Floor(((z-radius) - gridBounds.top) / pageSize);
177
int maxPageX = Math::Floor(((x+radius) - gridBounds.left) / pageSize);
178
int maxPageZ = Math::Floor(((z+radius) - gridBounds.top) / pageSize);
179
Real radiusSq = radius * radius;
181
if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1;
182
if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1;
183
if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1;
184
if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1;
186
PageGridListIterator it, end;
188
//Scan all entity types
189
it = pageGridList.begin();
190
end = pageGridList.end();
192
//Only scan entities of the given type
193
it = pageGridList.find(type);
194
assert(it != pageGridList.end());
198
//Scan all the grid blocks
200
std::vector<TreeDef> *pageGrid = it->second;
202
for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){
203
for (int tileX = minPageX; tileX <= maxPageX; ++tileX){
204
bool modified = false;
206
//Scan all trees in grid block
207
std::vector<TreeDef> &treeList = _getGridPage(pageGrid, tileX, tileZ);
209
while (i < treeList.size()){
211
float distX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize) - pos.x;
212
float distZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize) - pos.z;
213
float distSq = distX * distX + distZ * distZ;
215
if (distSq <= radiusSq){
216
//If it's within the radius, delete it
217
treeList[i] = treeList.back();
218
#ifdef PAGEDGEOMETRY_USER_DATA
219
deletedUserData.push_back(treeList.back().userData);
228
//Rebuild geometry if necessary
230
Vector3 pos(gridBounds.left + ((0.5f + tileX) * pageSize), 0, gridBounds.top + ((0.5f + tileZ) * pageSize));
231
geom->reloadGeometryPage(pos);
239
#ifdef PAGEDGEOMETRY_USER_DATA
240
return deletedUserData;
244
void TreeLoader2D::setColorMap(const Ogre::String &mapFile, MapChannel channel)
251
colorMap = ColorMap::load(mapFile, channel);
252
colorMap->setMapBounds(actualBounds);
253
colorMap->setFilter(colorMapFilter);
257
void TreeLoader2D::setColorMap(Ogre::Texture *map, MapChannel channel)
264
colorMap = ColorMap::load(map, channel);
265
colorMap->setMapBounds(actualBounds);
266
colorMap->setFilter(colorMapFilter);
270
void TreeLoader2D::loadPage(PageInfo &page)
272
//Calculate x/z indexes for the grid array
273
page.xIndex -= Math::Floor(gridBounds.left / pageSize);
274
page.zIndex -= Math::Floor(gridBounds.top / pageSize);
276
//Check if the requested page is in bounds
277
if (page.xIndex < 0 || page.zIndex < 0 || page.xIndex >= pageGridX || page.zIndex >= pageGridZ)
280
//For each tree type...
281
PageGridListIterator i;
282
for (i = pageGridList.begin(); i != pageGridList.end(); ++i){
283
//Get the appropriate tree list for the specified page
284
std::vector<TreeDef> *pageGrid = i->second;
285
std::vector<TreeDef> &treeList = _getGridPage(pageGrid, page.xIndex, page.zIndex);
286
Entity *entity = i->first;
288
//Load the listed trees into the page
289
std::vector<TreeDef>::iterator o;
290
for (o = treeList.begin(); o != treeList.end(); ++o){
293
pos.x = page.bounds.left + ((Real)o->xPos / 65535) * pageSize;
294
pos.z = page.bounds.top + ((Real)o->zPos / 65535) * pageSize;
296
//Calculate terrain height at pos.x / pos.z to get pos.y
297
if (heightFunction != NULL)
298
pos.y = heightFunction(pos.x, pos.z, heightFunctionUserData);
303
Degree angle((Real)o->rotation * (360.0f / 255));
304
Quaternion rot(angle, Vector3::UNIT_Y);
308
scale.y = (Real)o->scale * (maximumScale / 255) + minimumScale;
315
color = colorMap->getColorAt_Unpacked(pos.x, pos.z);
317
color = ColourValue::White;
319
addEntity(entity, pos, rot, scale, color);
324
TreeIterator2D TreeLoader2D::getTrees()
326
return TreeIterator2D(this);
331
TreeIterator2D::TreeIterator2D(TreeLoader2D *trees)
333
TreeIterator2D::trees = trees;
336
currentGrid = trees->pageGridList.begin();
337
currentX = 0; currentZ = 0;
338
currentTreeList = &trees->_getGridPage(currentGrid->second, currentX, currentZ);
339
currentTree = currentTreeList->begin();
342
//If there's not tree in the first page, keep looking
343
if (currentTree == currentTreeList->end())
346
//Read the first tree's data
349
//Read one more tree, so peekNext() will properly return the first item, while the system
350
//actually is looking ahead one item (this way it can detect the end of the list before
356
TreeRef TreeIterator2D::getNext()
358
TreeRef tree = peekNext();
363
void TreeIterator2D::moveNext()
365
//Out of bounds check
367
OGRE_EXCEPT(1, "Cannot read past end of TreeIterator list", "TreeIterator::moveNext()");
369
//Preserve the last tree
370
prevTreeDat = currentTreeDat;
372
//Increment iterators to the next tree
373
if (currentTree != currentTreeList->end())
375
while (currentTree == currentTreeList->end()){
376
if (++currentX >= trees->pageGridX){
378
if (++currentZ >= trees->pageGridZ){
380
if (currentGrid == trees->pageGridList.end()){
385
currentX = 0; currentZ = 0;
389
currentTreeList = &trees->_getGridPage(currentGrid->second, currentX, currentZ);
390
currentTree = currentTreeList->begin();
393
//Read the current tree data
397
void TreeIterator2D::_readTree()
399
TreeLoader2D::TreeDef treeDef = *currentTree;
402
Real boundsLeft = trees->gridBounds.left + trees->pageSize * currentX;
403
Real boundsTop = trees->gridBounds.top + trees->pageSize * currentZ;
404
currentTreeDat.position.x = boundsLeft + ((Real)treeDef.xPos / 65535) * trees->pageSize;
405
currentTreeDat.position.z = boundsTop + ((Real)treeDef.zPos / 65535) * trees->pageSize;
407
//Calculate terrain height at x / z to get y
408
if (trees->heightFunction != NULL)
409
currentTreeDat.position.y = trees->heightFunction(currentTreeDat.position.x, currentTreeDat.position.z, trees->heightFunctionUserData);
411
currentTreeDat.position.y = 0.0f;
414
currentTreeDat.yaw = Degree((Real)treeDef.rotation * (360.0f / 255));
417
currentTreeDat.scale = (Real)treeDef.scale * (trees->maximumScale / 255) + trees->minimumScale;
420
currentTreeDat.entity = currentGrid->first;