/*------------------------------------------------------------------------------------- Copyright (c) 2006 John Judnich 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. 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: 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. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------------*/ #include "TreeLoader2D.h" #include "PagedGeometry.h" #include "PropertyMaps.h" #include #include #include #include #include #include using namespace Ogre; namespace Forests { TreeLoader2D::TreeLoader2D(PagedGeometry *geom, const TBounds &bounds) { //Calculate grid size TreeLoader2D::geom = geom; pageSize = geom->getPageSize(); //Reset height function heightFunction = NULL; heightFunctionUserData = NULL; //Make sure the bounds are aligned with PagedGeometry's grid, so the TreeLoader's grid tiles will have a 1:1 relationship actualBounds = bounds; gridBounds = bounds; gridBounds.left = pageSize * Math::Floor((gridBounds.left - geom->getBounds().left) / pageSize) + geom->getBounds().left; gridBounds.top = pageSize * Math::Floor((gridBounds.top - geom->getBounds().top) / pageSize) + geom->getBounds().top; gridBounds.right = pageSize * Math::Ceil((gridBounds.right - geom->getBounds().left) / pageSize) + geom->getBounds().left; gridBounds.bottom = pageSize * Math::Ceil((gridBounds.bottom - geom->getBounds().top) / pageSize) + geom->getBounds().top; //Calculate page grid size pageGridX = Math::Ceil(gridBounds.width() / pageSize) + 1; pageGridZ = Math::Ceil(gridBounds.height() / pageSize) + 1; //Reset color map colorMap = NULL; colorMapFilter = MAPFILTER_NONE; //Default scale range maximumScale = 2.0f; minimumScale = 0.0f; } TreeLoader2D::~TreeLoader2D() { //Delete all page grids PageGridListIterator i; for (i = pageGridList.begin(); i != pageGridList.end(); ++i){ delete[] i->second; } pageGridList.clear(); } void TreeLoader2D::addTree(Entity *entity, const Vector3 &position, Degree yaw, Real scale, void* userData) { //First convert the coordinate to PagedGeometry's local system #ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM Vector3 pos = geom->_convertToLocal(position); #else Vector3 pos = position; #endif //Check that the tree is within bounds (DEBUG) #ifdef _DEBUG const Real smallVal = 0.01f; if (pos.x < actualBounds.left-smallVal || pos.x > actualBounds.right+smallVal || pos.z < actualBounds.top-smallVal || pos.z > actualBounds.bottom+smallVal) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree position is out of bounds", "TreeLoader::addTree()"); if (scale < minimumScale || scale > maximumScale) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Tree scale out of range", "TreeLoader::addTree()"); #endif //If the tree is slightly out of bounds (due to imprecise coordinate conversion), fix it if (pos.x < actualBounds.left) pos.x = actualBounds.left; else if (pos.x > actualBounds.right) pos.x = actualBounds.right; if (pos.z < actualBounds.top) pos.z = actualBounds.top; else if (pos.z > actualBounds.bottom) pos.z = actualBounds.bottom; Real x = pos.x; Real z = pos.z; //Find the appropriate page grid for the entity PageGridListIterator i; i = pageGridList.find(entity); std::vector *pageGrid; if (i != pageGridList.end()){ //If it exists, get the page grid pageGrid = i->second; } else { //If it does not exist, create a new page grid pageGrid = new std::vector[pageGridX * pageGridZ]; //Register the new page grid in the pageGridList for later retrieval pageGridList.insert(PageGridListValue(entity, pageGrid)); } //Calculate the gridbounds-relative position of the tree Real xrel = x - gridBounds.left; Real zrel = z - gridBounds.top; //Get the appropriate grid element based on the new tree's position int pageX = Math::Floor(xrel / pageSize); int pageZ = Math::Floor(zrel / pageSize); std::vector &treeList = _getGridPage(pageGrid, pageX, pageZ); //Create the new tree TreeDef tree; tree.xPos = 65535 * (xrel - (pageX * pageSize)) / pageSize; tree.zPos = 65535 * (zrel - (pageZ * pageSize)) / pageSize; tree.rotation = 255 * (yaw.valueDegrees() / 360.0f); tree.scale = 255 * ((scale - minimumScale) / maximumScale); #ifdef PAGEDGEOMETRY_USER_DATA tree.userData = userData; #endif //Add it to the tree list treeList.push_back(tree); //Rebuild geometry if necessary geom->reloadGeometryPage(Vector3(x, 0, z)); } #ifdef PAGEDGEOMETRY_USER_DATA std::vector #else void #endif TreeLoader2D::deleteTrees(const Ogre::Vector3 &position, Ogre::Real radius, Entity *type) { //First convert the coordinate to PagedGeometry's local system #ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM Vector3 pos = geom->_convertToLocal(position); #else Vector3 pos = position; #endif #ifdef PAGEDGEOMETRY_USER_DATA //Keep a list of user-defined data associated with deleted trees std::vector deletedUserData; #endif //If the position is slightly out of bounds, fix it if (pos.x < actualBounds.left) pos.x = actualBounds.left; else if (pos.x > actualBounds.right) pos.x = actualBounds.right; if (pos.z < actualBounds.top) pos.z = actualBounds.top; else if (pos.z > actualBounds.bottom) pos.z = actualBounds.bottom; Real x = pos.x; Real z = pos.z; //Determine the grid blocks which might contain the requested trees int minPageX = Math::Floor(((x-radius) - gridBounds.left) / pageSize); int minPageZ = Math::Floor(((z-radius) - gridBounds.top) / pageSize); int maxPageX = Math::Floor(((x+radius) - gridBounds.left) / pageSize); int maxPageZ = Math::Floor(((z+radius) - gridBounds.top) / pageSize); Real radiusSq = radius * radius; if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1; if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1; if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1; if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1; PageGridListIterator it, end; if (type == NULL){ //Scan all entity types it = pageGridList.begin(); end = pageGridList.end(); } else { //Only scan entities of the given type it = pageGridList.find(type); assert(it != pageGridList.end()); end = it; ++end; } //Scan all the grid blocks while (it != end){ std::vector *pageGrid = it->second; for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){ for (int tileX = minPageX; tileX <= maxPageX; ++tileX){ bool modified = false; //Scan all trees in grid block std::vector &treeList = _getGridPage(pageGrid, tileX, tileZ); uint32 i = 0; while (i < treeList.size()){ //Get tree distance float distX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize) - pos.x; float distZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize) - pos.z; float distSq = distX * distX + distZ * distZ; if (distSq <= radiusSq){ #ifdef PAGEDGEOMETRY_USER_DATA deletedUserData.push_back(treeList[i].userData); #endif //If it's within the radius, delete it treeList[i] = treeList.back(); treeList.pop_back(); modified = true; } else ++i; } //Rebuild geometry if necessary if (modified){ Vector3 pos(gridBounds.left + ((0.5f + tileX) * pageSize), 0, gridBounds.top + ((0.5f + tileZ) * pageSize)); geom->reloadGeometryPage(pos); } } } ++it; } #ifdef PAGEDGEOMETRY_USER_DATA return deletedUserData; #endif } #ifdef PAGEDGEOMETRY_USER_DATA std::vector #else void #endif TreeLoader2D::deleteTrees(TBounds area, Ogre::Entity *type) { #ifdef PAGEDGEOMETRY_USER_DATA //Keep a list of user-defined data associated with deleted trees std::vector deletedUserData; #endif //If the area is slightly out of bounds, fix it if (area.left < actualBounds.left) area.left = actualBounds.left; else if (area.left > actualBounds.right) area.left = actualBounds.right; if (area.top < actualBounds.top) area.top = actualBounds.top; else if (area.top > actualBounds.bottom) area.top = actualBounds.bottom; if (area.right < actualBounds.left) area.right = actualBounds.left; else if (area.right > actualBounds.right) area.right = actualBounds.right; if (area.bottom < actualBounds.top) area.bottom = actualBounds.top; else if (area.bottom > actualBounds.bottom) area.bottom = actualBounds.bottom; //Determine the grid blocks which might contain the requested trees int minPageX = Math::Floor((area.left - gridBounds.left) / pageSize); int minPageZ = Math::Floor((area.top - gridBounds.top) / pageSize); int maxPageX = Math::Floor((area.right - gridBounds.left) / pageSize); int maxPageZ = Math::Floor((area.bottom - gridBounds.top) / pageSize); if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1; if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1; if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1; if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1; PageGridListIterator it, end; if (type == NULL){ //Scan all entity types it = pageGridList.begin(); end = pageGridList.end(); } else { //Only scan entities of the given type it = pageGridList.find(type); assert(it != pageGridList.end()); end = it; ++end; } //Scan all the grid blocks while (it != end){ std::vector *pageGrid = it->second; for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){ for (int tileX = minPageX; tileX <= maxPageX; ++tileX){ bool modified = false; //Scan all trees in grid block std::vector &treeList = _getGridPage(pageGrid, tileX, tileZ); uint32 i = 0; while (i < treeList.size()){ //Check if tree is in bounds float posX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize); float posZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize); if (posX >= area.left && posX <= area.right && posZ >= area.top && posZ <= area.bottom) { //If so, delete it #ifdef PAGEDGEOMETRY_USER_DATA deletedUserData.push_back(treeList[i].userData); #endif //If it's within the radius, delete it treeList[i] = treeList.back(); treeList.pop_back(); modified = true; } else ++i; } //Rebuild geometry if necessary if (modified){ Vector3 pos(gridBounds.left + ((0.5f + tileX) * pageSize), 0, gridBounds.top + ((0.5f + tileZ) * pageSize)); geom->reloadGeometryPage(pos); } } } ++it; } #ifdef PAGEDGEOMETRY_USER_DATA return deletedUserData; #endif } #ifdef PAGEDGEOMETRY_USER_DATA std::vector TreeLoader2D::findTrees(const Ogre::Vector3 &position, Real radius, Entity *type) { //First convert the coordinate to PagedGeometry's local system #ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM Vector3 pos = geom->_convertToLocal(position); #else Vector3 pos = position; #endif //Keep a list of user-defined data associated with deleted trees std::vector foundUserData; //If the position is slightly out of bounds, fix it if (pos.x < actualBounds.left) pos.x = actualBounds.left; else if (pos.x > actualBounds.right) pos.x = actualBounds.right; if (pos.z < actualBounds.top) pos.z = actualBounds.top; else if (pos.x > actualBounds.bottom) pos.z = actualBounds.bottom; //Determine the grid blocks which might contain the requested trees int minPageX = Math::Floor(((pos.x-radius) - gridBounds.left) / pageSize); int minPageZ = Math::Floor(((pos.z-radius) - gridBounds.top) / pageSize); int maxPageX = Math::Floor(((pos.x+radius) - gridBounds.left) / pageSize); int maxPageZ = Math::Floor(((pos.z+radius) - gridBounds.top) / pageSize); Real radiusSq = radius * radius; if (minPageX < 0) minPageX = 0; else if (minPageX >= pageGridX) minPageX = pageGridX-1; if (minPageZ < 0) minPageZ = 0; else if (minPageZ >= pageGridZ) minPageZ = pageGridZ-1; if (maxPageX < 0) maxPageX = 0; else if (maxPageX >= pageGridX) maxPageX = pageGridX-1; if (maxPageZ < 0) maxPageZ = 0; else if (maxPageZ >= pageGridZ) maxPageZ = pageGridZ-1; PageGridListIterator it, end; if (type == NULL){ //Scan all entity types it = pageGridList.begin(); end = pageGridList.end(); } else { //Only scan entities of the given type it = pageGridList.find(type); assert(it != pageGridList.end()); end = it; ++end; } //Scan all the grid blocks while (it != end){ std::vector *pageGrid = it->second; for (int tileZ = minPageZ; tileZ <= maxPageZ; ++tileZ){ for (int tileX = minPageX; tileX <= maxPageX; ++tileX){ bool modified = false; //Scan all trees in grid block std::vector &treeList = _getGridPage(pageGrid, tileX, tileZ); uint32 i = 0; while (i < treeList.size()){ //Get tree distance float distX = (gridBounds.left + (tileX * pageSize) + ((Real)treeList[i].xPos / 65535) * pageSize) - pos.x; float distZ = (gridBounds.top + (tileZ * pageSize) + ((Real)treeList[i].zPos / 65535) * pageSize) - pos.z; float distSq = distX * distX + distZ * distZ; if (distSq <= radiusSq){ foundUserData.push_back(treeList[i].userData); } else ++i; } } } ++it; } return foundUserData; } #endif void TreeLoader2D::setColorMap(const Ogre::String &mapFile, MapChannel channel) { if (colorMap){ colorMap->unload(); colorMap = NULL; } if (mapFile != ""){ colorMap = ColorMap::load(mapFile, channel); colorMap->setFilter(colorMapFilter); } } void TreeLoader2D::setColorMap(Ogre::TexturePtr map, MapChannel channel) { if (colorMap){ colorMap->unload(); colorMap = NULL; } if (map){ colorMap = ColorMap::load(map, channel); colorMap->setFilter(colorMapFilter); } } void TreeLoader2D::loadPage(PageInfo &page) { //Calculate x/z indexes for the grid array page.xIndex -= Math::Floor(gridBounds.left / pageSize); page.zIndex -= Math::Floor(gridBounds.top / pageSize); //Check if the requested page is in bounds if (page.xIndex < 0 || page.zIndex < 0 || page.xIndex >= pageGridX || page.zIndex >= pageGridZ) return; //For each tree type... PageGridListIterator i; for (i = pageGridList.begin(); i != pageGridList.end(); ++i){ //Get the appropriate tree list for the specified page std::vector *pageGrid = i->second; std::vector &treeList = _getGridPage(pageGrid, page.xIndex, page.zIndex); Entity *entity = i->first; //Load the listed trees into the page std::vector::iterator o; for (o = treeList.begin(); o != treeList.end(); ++o){ //Get position Vector3 pos; pos.x = page.bounds.left + ((Real)o->xPos / 65535) * pageSize; pos.z = page.bounds.top + ((Real)o->zPos / 65535) * pageSize; //Calculate terrain height at pos.x / pos.z to get pos.y if (heightFunction != NULL) pos.y = heightFunction(pos.x, pos.z, heightFunctionUserData); else pos.y = 0.0f; //Get rotation Degree angle((Real)o->rotation * (360.0f / 255)); Quaternion rot(angle, Vector3::UNIT_Y); //Get scale Vector3 scale; scale.y = (Real)o->scale * (maximumScale / 255) + minimumScale; scale.x = scale.y; scale.z = scale.y; //Get color ColourValue color; if (colorMap) color = colorMap->getColorAt_Unpacked(pos.x, pos.z, actualBounds); else color = ColourValue::White; addEntity(entity, pos, rot, scale, color); } } } TreeIterator2D TreeLoader2D::getTrees() { return TreeIterator2D(this); } TreeIterator2D::TreeIterator2D(TreeLoader2D *trees) { TreeIterator2D::trees = trees; //Test if the GridList has anything in it if (trees->pageGridList.empty()) { // If not, set hasMore to false and return. hasMore = false; return; } //Setup iterators currentGrid = trees->pageGridList.begin(); currentX = 0; currentZ = 0; currentTreeList = &trees->_getGridPage(currentGrid->second, currentX, currentZ); currentTree = currentTreeList->begin(); hasMore = true; //If there's not tree in the first page, keep looking if (currentTree == currentTreeList->end()) moveNext(); //Read the first tree's data _readTree(); //Read one more tree, so peekNext() will properly return the first item, while the system //actually is looking ahead one item (this way it can detect the end of the list before //it's too late) if (hasMore) moveNext(); } TreeRef TreeIterator2D::getNext() { TreeRef tree = peekNext(); moveNext(); return tree; } void TreeIterator2D::moveNext() { //Out of bounds check if (!hasMore) OGRE_EXCEPT(Exception::ERR_INVALID_STATE, "Cannot read past end of TreeIterator list", "TreeIterator::moveNext()"); //Preserve the last tree prevTreeDat = currentTreeDat; //Increment iterators to the next tree if (currentTree != currentTreeList->end()) ++currentTree; while (currentTree == currentTreeList->end()){ if (++currentX >= trees->pageGridX){ currentX = 0; if (++currentZ >= trees->pageGridZ){ ++currentGrid; if (currentGrid == trees->pageGridList.end()){ //No more trees left hasMore = false; return; } currentX = 0; currentZ = 0; } } currentTreeList = &trees->_getGridPage(currentGrid->second, currentX, currentZ); currentTree = currentTreeList->begin(); } //Read the current tree data _readTree(); } void TreeIterator2D::_readTree() { TreeLoader2D::TreeDef treeDef = *currentTree; //Get position Real boundsLeft = trees->gridBounds.left + trees->pageSize * currentX; Real boundsTop = trees->gridBounds.top + trees->pageSize * currentZ; currentTreeDat.position.x = boundsLeft + ((Real)treeDef.xPos / 65535) * trees->pageSize; currentTreeDat.position.z = boundsTop + ((Real)treeDef.zPos / 65535) * trees->pageSize; //Calculate terrain height at x / z to get y if (trees->heightFunction != NULL) currentTreeDat.position.y = trees->heightFunction(currentTreeDat.position.x, currentTreeDat.position.z, trees->heightFunctionUserData); else currentTreeDat.position.y = 0.0f; //Get rotation currentTreeDat.yaw = Degree((Real)treeDef.rotation * (360.0f / 255)); //Get scale currentTreeDat.scale = (Real)treeDef.scale * (trees->maximumScale / 255) + trees->minimumScale; //Get entity currentTreeDat.entity = currentGrid->first; } }