// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" // #include "nel/3d/packed_world.h" // #include "nel/misc/grid_traversal.h" using namespace NLMISC; namespace NL3D { //************************************************************************************************* void CPackedWorld::build(std::vector &packedZones) { _ZoneGrid.clear(); _Zones.clear(); if (packedZones.empty()) return; CAABBox box; nlassert(packedZones[0]) box = packedZones[0]->Box; for(uint k = 1; k < packedZones.size(); ++k) { nlassert(packedZones[k]); box.extend(packedZones[k]->Box.getMin()); box.extend(packedZones[k]->Box.getMax()); } _ZoneMinX = (sint32) floorf(box.getMin().x / 160.f); _ZoneMinY = (sint32) floorf(box.getMin().y / 160.f); // sint32 zoneMaxX = (sint32) floorf(box.getMax().x / 160.f); sint32 zoneMaxY = (sint32) floorf(box.getMax().y / 160.f); // _ZoneGrid.init(zoneMaxX - _ZoneMinX + 1, zoneMaxY - _ZoneMinY + 1); _Zones.resize(packedZones.size()); // for(uint k = 0; k < packedZones.size(); ++k) { CZoneInfo zi; zi.Zone = packedZones[k]; zi.RaytraceCounter = 0; _Zones[k] = zi; sint zoneMinX = (sint) floorf(packedZones[k]->Box.getMin().x / 160.f) - (sint) _ZoneMinX; sint zoneMinY = (sint) floorf(packedZones[k]->Box.getMin().y / 160.f) - (sint) _ZoneMinY; sint zoneMaxX = (sint) floorf(packedZones[k]->Box.getMax().x / 160.f) - (sint) _ZoneMinX; sint zoneMaxY = (sint) floorf(packedZones[k]->Box.getMax().y / 160.f) - (sint) _ZoneMinY; for (sint y = zoneMinY; y <= zoneMaxY; ++y) { if (y < 0) continue; if (y >= (sint) _ZoneGrid.getHeight()) break; for (sint x = zoneMinX; x <= zoneMaxX; ++x) { if (x < 0) continue; if (x >= (sint) _ZoneGrid.getWidth()) break; _ZoneGrid(x, y).IDs.push_back((uint32) k); } } } _RaytraceCounter = ~0; } //************************************************************************************************* bool CPackedWorld::raytrace(const NLMISC::CVector &start, const NLMISC::CVector &end, CVector &inter, std::vector *testedTriangles /*= NULL*/, NLMISC::CVector *normal) { if (_ZoneGrid.empty()) return false; ++_RaytraceCounter; float bestDist = FLT_MAX; NLMISC::CVector bestNormal; CVector currEnd = end; CVector currInter; if (_RaytraceCounter == (uint32) ~0) { for(uint k = 0; k < _Zones.size(); ++k) { _Zones[k].RaytraceCounter = 0; } } sint currX, currY; CVector2f start2f(start.x / 160.f, start.y / 160.f); CVector2f dir2f((end.x - start.x) / 160.f, (end.y - start.y) / 160.f); CGridTraversal::startTraverse(start2f, currX, currY); do { sint x = currX - (sint) _ZoneMinX; sint y = currY - (sint) _ZoneMinY; if (x < 0) continue; if (y < 0) continue; if (x >= (sint) _ZoneGrid.getWidth()) continue; if (y >= (sint) _ZoneGrid.getHeight()) continue; std::vector &currZoneList = _ZoneGrid(x, y).IDs; for(uint k = 0; k < currZoneList.size(); ++k) { if (_Zones[currZoneList[k]].RaytraceCounter != _RaytraceCounter) // already visited { NLMISC::CVector normalTmp; if (_Zones[currZoneList[k]].Zone->raytrace(start, currEnd, currInter, testedTriangles, &normalTmp)) { float dist = (currInter - start).norm(); if (dist < bestDist) { bestNormal = normalTmp; bestDist = dist; inter = currInter; currEnd = currInter; // during search, just seek hit that are nearest } } _Zones[currZoneList[k]].RaytraceCounter = _RaytraceCounter; } } if (bestDist != FLT_MAX) { if (normal) { *normal = bestNormal; } return true; } } while (CGridTraversal::traverse(start2f, dir2f, currX, currY)); return false; } //************************************************************************************************* void CPackedWorld::getZones(std::vector &zones) { zones.clear(); for(uint k = 0; k < _Zones.size(); ++k) { zones.push_back(_Zones[k].Zone); } } //************************************************************************************************* void CPackedWorld::serialZoneNames(NLMISC::IStream &f) throw(NLMISC::EStream) { f.serialVersion(1); f.serialCheck((uint32) 'OWPA'); f.serialCont(ZoneNames); } //************************************************************************************************* void CPackedWorld::serial(NLMISC::IStream &f) throw(NLMISC::EStream) { serialZoneNames(f); f.serialCont(_Zones); f.serial(_ZoneGrid); f.serial(_ZoneMinX); f.serial(_ZoneMinY); } //************************************************************************************************* void CPackedWorld::select(const NLMISC::CPolygon2D &poly, std::vector &selectedTriangles) const { selectedTriangles.clear(); // compute covered zones NLMISC::CPolygon2D zonePoly = poly; for (uint k = 0; k < zonePoly.Vertices.size(); ++k) { zonePoly.Vertices[k].x = zonePoly.Vertices[k].x / 160.f - (float) _ZoneMinX; zonePoly.Vertices[k].y = zonePoly.Vertices[k].y / 160.f - (float) _ZoneMinY; } NLMISC::CPolygon2D::TRasterVect borders; sint minY; zonePoly.computeOuterBorders(borders, minY); for (sint y = minY; y < (sint) (minY + borders.size()); ++y) { if (y < 0 || y >= (sint) _ZoneGrid.getHeight()) continue; for (sint x = borders[y - minY].first; x <= borders[y - minY].second; ++x) { if (x < 0 || x >= (sint) _ZoneGrid.getWidth()) continue; { const CZoneIndexList &zil = _ZoneGrid(x, y); for (uint k = 0; k < zil.IDs.size(); ++k) { _Zones[zil.IDs[k]].Zone->appendSelection(poly, selectedTriangles); } } } } } } // Nl3D