// 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 "stdligo.h" #include "zone_template.h" #include "ligo_error.h" #include "nel/ligo/ligo_config.h" #include "nel/misc/stream.h" #include "nel/misc/matrix.h" using namespace std; using namespace NLMISC; namespace NLLIGO { const uint SnappedXFlag = 1; const uint SnappedYFlag = 2; // *************************************************************************** inline void CZoneTemplate::snap (float& value, float snap) { // Snap it value = snap * (float) floor ( (value / snap) + 0.5f ); } // *************************************************************************** inline bool CZoneTemplate::snapOnGrid (float& value, float resolution, float snap) { // Calc the floor float _floor = (float) ( resolution * floor (value / resolution) ); nlassert (_floor<=value); // Calc the remainder float remainder = value - _floor; //nlassert ( (remainder>=0) && (remainder &vertices, const std::vector< std::pair > &indexes, const CLigoConfig &config, CLigoError &errors) { // Clear the error message errors.clear (); // Make an boundary flag array vector boundaryFlags; // Vertices count uint vertexCount = (uint)vertices.size(); // Resize the array boundaryFlags.resize (vertexCount, 0); // *** Build the flag array and the snapped vertex array // For each vertices uint vertex; for (vertex = 0; vertex < vertexCount; vertex++) { // Snap the point on the X grid if (isSnapedOnGrid (vertices[vertex].x, config.CellSize, config.Snap)) // Flag on X boundaryFlags[vertex]|=SnappedXFlag; // Snap the point on the Y grid if (isSnapedOnGrid (vertices[vertex].y, config.CellSize, config.Snap)) // Flag on Y boundaryFlags[vertex]|=SnappedYFlag; } // *** Build the edge set multimap edgePair; multimap edgePairReverse; // Index count uint edgeCount = (uint)indexes.size(); // For each vertices uint edge; for (edge = 0; edge < edgeCount; edge++) { // Ref on the pair const pair &theEdge = indexes[edge]; // Vertex snapped ? if ( boundaryFlags[theEdge.first] && boundaryFlags[theEdge.second] ) { // Common coordinates uint common = boundaryFlags[theEdge.first] & boundaryFlags[theEdge.second]; // Snapped on the same kind of coordinates ? if ( common ) { // Keep this edge ? bool keep = false; // Snapped both on X ? if ( common & SnappedXFlag ) { // Keep it keep = true; } // Snapped both on X ? if ( common & SnappedYFlag ) { // Keep it keep = true; } // Keep this edge ? if (keep) { // Already inserted ? bool first = edgePair.find (theEdge.first) != edgePair.end(); bool second = edgePairReverse.find (theEdge.second) != edgePairReverse.end(); // First already inserted if (first || second) { // Error, two times the same vertex errors.MainError = CLigoError::VertexAlreadyUsed; if (first) errors.pushVertexError (CLigoError::VertexAlreadyUsed, theEdge.first, 0); if (second) errors.pushVertexError (CLigoError::VertexAlreadyUsed, theEdge.second, 0); return false; } if ((!first) && (!second)) { // Add to the map edgePair.insert (map::value_type(theEdge.first, theEdge.second)); edgePairReverse.insert (map::value_type(theEdge.second, theEdge.first)); } } } } } // *** Build the list of non included vertices // For each vertices for (uint i=0; i > segmentList; multimap::iterator currentVert = edgePair.begin(); // For each remaining segment while (currentVert != edgePair.end()) { // Get next vert uint first = currentVert->first; uint next = currentVert->second; // New list segmentList.push_front (list()); list &listVert = *segmentList.begin(); // Put the first vertices of the edge list listVert.push_back (first); listVert.push_back (next); // Erase it and edgePair.erase (currentVert); // Erase the reverse one currentVert = edgePairReverse.find (next); nlassert (currentVert != edgePairReverse.end()); edgePairReverse.erase (currentVert); // Look forward currentVert = edgePair.find (next); while (currentVert != edgePair.end()) { // Backup //uint current = currentVert->first; next = currentVert->second; // Push the next vertex listVert.push_back (next); // Erase it and edgePair.erase (currentVert); // Erase the reverse one currentVert = edgePairReverse.find (next); nlassert (currentVert != edgePairReverse.end()); edgePairReverse.erase (currentVert); // Look forward currentVert = edgePair.find (next); } // Edgelist ok ? if (next != first) { // No, look backward currentVert = edgePairReverse.find (first); while (currentVert != edgePairReverse.end()) { // Backup uint current = currentVert->second; next = currentVert->first; // Push the next vertex listVert.push_front (current); // Erase it edgePairReverse.erase (currentVert); // Erase the reverse one currentVert = edgePair.find (current); nlassert (currentVert != edgePair.end()); edgePair.erase (currentVert); // Look forward currentVert = edgePairReverse.find (current); } } // Next edge list currentVert = edgePair.begin(); } // ** Error traitment // Ok bool ok = true; // Edge index uint edgeIndex = 0; // List ok ? list >::iterator iteList = segmentList.begin (); while (iteList != segmentList.end()) { // Only one list list &listVert = *iteList; // First and last edge uint first = *listVert.begin(); uint last = *(--listVert.end()); // Opened edge ? if ( first != last ) { // Opened edge errors.pushVertexError (CLigoError::OpenedEdge, first, edgeIndex); errors.pushVertexError (CLigoError::OpenedEdge, last, edgeIndex); // Main error errors.MainError = CLigoError::OpenedEdge; // Not ko ok = false; } // Next edge list edgeIndex++; iteList++; } if (segmentList.size () > 1) { // Main error errors.MainError = CLigoError::MultipleEdge; // Not ok ok = false; } // Ok ? if (ok) { // Only one list list &listVert = *segmentList.begin (); // Test vertex enchainement list::iterator vertIte = listVert.begin(); // Current vertex id uint previous = *(--listVert.end()); vertIte++; // Error vertex set set errored; // For each vertices while (vertIte != listVert.end ()) { // Vertex id uint next = *vertIte; // Common flags uint commonFlags = boundaryFlags[previous]&boundaryFlags[next]; // The both on X ? if ( commonFlags & SnappedXFlag ) { // Get x index sint32 prevIndex = getSnappedIndex (vertices[previous].x, config.CellSize, config.Snap); sint32 nextIndex = getSnappedIndex (vertices[next].x, config.CellSize, config.Snap); // Not the same ? if (prevIndex != nextIndex) { // Vertex list error if (errored.insert (previous).second) errors.pushVertexError (CLigoError::VertexList, previous, 0); if (errored.insert (next).second) errors.pushVertexError (CLigoError::VertexList, next, 0); // Main error errors.MainError = CLigoError::VertexList; } } // Next vertex previous = next; vertIte++; } // No error ? if (errored.empty()) { // Only one list nlassert (segmentList.size()==1); // First of the list vertIte = listVert.begin(); // Remove first listVert.erase (vertIte); // Find a corner list::iterator firstIte = listVert.begin(); while (firstIte != listVert.end()) { // Corner ? if ( (boundaryFlags[*firstIte] & (SnappedXFlag|SnappedYFlag)) == (SnappedXFlag|SnappedYFlag) ) // Yes, exit break; // Next firstIte++; } // Can't be the last if (firstIte == listVert.end()) { // No corner found errors.MainError = CLigoError::NoCornerFound; return false; } // First of the segment vertIte = firstIte; // Current edge list std::vector edge; // Push the first edge.push_back (*vertIte); // Next vertIte++; // End ? if (vertIte == listVert.end()) // Start vertIte = listVert.begin(); // Edge index uint edgeIndex = 0; // Build the edges for(;;) { // Add it edge.push_back (*vertIte); // Corner ? if ( (boundaryFlags[*vertIte] & (SnappedXFlag|SnappedYFlag)) == (SnappedXFlag|SnappedYFlag) ) { // Get the index of start and end of the edge sint32 startX = getSnappedIndex (vertices[edge[0]].x, config.CellSize, config.Snap); sint32 startY = getSnappedIndex (vertices[edge[0]].y, config.CellSize, config.Snap); sint32 endX = getSnappedIndex (vertices[edge[edge.size()-1]].x, config.CellSize, config.Snap); sint32 endY = getSnappedIndex (vertices[edge[edge.size()-1]].y, config.CellSize, config.Snap); // Same point ? if ((startX==endX) && (startY==endY)) { // Error, two times the same vertex errors.MainError = CLigoError::TwoCornerVertices; errors.pushVertexError (CLigoError::TwoCornerVertices, edge[0], 0); errors.pushVertexError (CLigoError::TwoCornerVertices, edge[edge.size()-1], 0); return false; } // Same point ? if ((abs(startX-endX)>1) || (abs(startY-endY)>1)) { // Error, two times the same vertex errors.MainError = CLigoError::CornerIsMissing; errors.pushVertexError (CLigoError::CornerIsMissing, edge[0], 0); errors.pushVertexError (CLigoError::CornerIsMissing, edge[edge.size()-1], 0); return false; } // Get rotation uint rotation = 4; if ((endX-startX)==1) { if ((endY-startY)==0) rotation = 0; } else if ((endX-startX)==-1) { if ((endY-startY)==0) rotation = 2; } else if ((endX-startX)==0) { if ((endY-startY)==1) rotation = 1; else if ((endY-startY)==-1) rotation = 3; } // Checks nlassert (rotation != 4); // Build the vertex array vector vertexArray; vertexArray.resize (edge.size()); // Rotate matrix CMatrix mat; mat.identity(); mat.rotateZ ((float)rotation * (float)Pi / 2); mat.setPos (CVector (vertices[edge[0]].x, vertices[edge[0]].y, 0)); mat.invert (); // Rotate the array for (uint i=0; ibestY))) { // This edgeId is best bestX=x; bestY=y; bestEdge = edgeId; } } // Check nlassert (bestEdge!=0xffffffff); // Reoder std::vector newEdge (_Edges.size()); for (edgeId=0; edgeId<_Edges.size(); edgeId++) { // Copy the edge newEdge[edgeId]=_Edges[bestEdge++]; // Next if (bestEdge==_Edges.size()) bestEdge=0; } // Copy the final array _Edges=newEdge; // Return ok return true; } } // Errors. return false; } // *************************************************************************** void CZoneTemplate::serial (NLMISC::IStream& s) { // open an XML node s.xmlPush ("LIGO_ZONE_TEMPLATE"); // An header file s.serialCheck (string ("LigoZoneTemplate") ); // Node for the boundaries s.xmlPush ("EDGES"); // Serial the Vertices s.serialCont (_Edges); // Node for the boundaries s.xmlPop (); // Close the node s.xmlPop (); } // *************************************************************************** void CZoneTemplate::getMask (std::vector &mask, uint &width, uint &height) { // Some constantes static const sint32 addX[4] = { 1, 0, -1, 0 }; static const sint32 addY[4] = { 0, 1, 0, -1 }; static const sint32 cellX[4] = { 0, -1, -1, 0 }; static const sint32 cellY[4] = { 0, 0, -1, -1 }; static const sint32 moveX[4] = { 0, 1, 0, -1 }; static const sint32 moveY[4] = { -1, 0, 1, 0 }; // Max sint32 xMax = 0x80000000; sint32 yMax = 0x80000000; // For each edges uint edges; for (edges=0; edges<_Edges.size(); edges++) { // Get the rotation uint32 rot = _Edges[edges].getRotation (); nlassert (rot<4); // Get X and Y max coordinates sint32 x = _Edges[edges].getOffsetX () + addX[rot]; sint32 y = _Edges[edges].getOffsetY () + addY[rot]; // Greater ? if (x > xMax) xMax = x; if (y > yMax) yMax = y; } // Build the array width = (uint32) xMax; height = (uint32) yMax; // Bit array for each cell vector edgeArray (xMax*yMax, 0); // Resize it mask.resize (xMax*yMax, false); // Set of the cells in the mask set > setCell; // For each edge for (edges=0; edges<_Edges.size(); edges++) { // Get the rotation uint32 rot = _Edges[edges].getRotation (); nlassert (rot<4); // Get its x and y cell coordinate sint32 x = _Edges[edges].getOffsetX () + cellX[rot]; sint32 y = _Edges[edges].getOffsetY () + cellY[rot]; // Fill the edge array edgeArray[x+y*width] |= (1< (x, y) ); } // Second set set > setCell2; // For each element in the set set >::iterator ite = setCell.begin(); while (ite != setCell.end()) { // For each direction for (uint dir=0; dir<4; dir++) { // Get its x and y cell coordinate sint32 x = ite->first; sint32 y = ite->second; // Edge in this direction ? while ( (edgeArray[x+y*width] & (1< (x, y) ); // Some checks nlassert (x>=0); nlassert (x<(sint32)width); nlassert (y>=0); nlassert (y<(sint32)height); } } // Next one ite++; } // Merge the two set ite = setCell2.begin(); while (ite != setCell2.end()) { // Merge setCell.insert (*ite); // Next element ite++; } // Done, fill the array ite = setCell.begin(); while (ite != setCell.end()) { // Merge mask[ite->first+ite->second*width] = true; // Next element ite++; } } // *************************************************************************** }