Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / zone_symmetrisation.cpp
blob51b00b3f3c8efea375a28c3a1ff83e60ebcc9fe4
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
18 #include "nel/3d/zone_symmetrisation.h"
19 #include "nel/3d/zone.h"
20 #include "nel/3d/tile_bank.h"
22 using namespace std;
23 using namespace NLMISC;
25 #ifdef DEBUG_NEW
26 #define new DEBUG_NEW
27 #endif
29 namespace NL3D
32 // ***************************************************************************
34 CZoneSymmetrisation::CZoneSymmetrisation()
38 // ***************************************************************************
41 Bit field
42 1-0 : state layer 0
43 3-2 : state layer 1
44 5-4 : state layer 2
45 7-6 : border state
46 9-8 : oriented border state
47 10 : corner flag
50 // ***************************************************************************
52 CZoneSymmetrisation::TState CZoneSymmetrisation::getTileState (uint patch, uint tile, uint layer) const
54 nlassert (layer<5);
55 return (TState)((_TilesLayerStates[patch][tile]>>(layer*2))&0x3);
58 // ***************************************************************************
60 CZoneSymmetrisation::TState CZoneSymmetrisation::getTileBorderState (uint patch, uint tile) const
62 return getTileState (patch, tile, 3);
65 // ***************************************************************************
67 CZoneSymmetrisation::TState CZoneSymmetrisation::getOrientedTileBorderState (uint patch, uint tile) const
69 return getTileState (patch, tile, 4);
72 // ***************************************************************************
74 bool CZoneSymmetrisation::getOrientedTileCorner (uint patch, uint tile)
76 return ( ( _TilesLayerStates[patch][tile] >> 10 ) & 1 ) != 0;
79 // ***************************************************************************
81 void CZoneSymmetrisation::setTileState (uint patch, uint tile, uint layer, TState state)
83 uint16 &ref = _TilesLayerStates[patch][tile];
84 ref &= ~(3<<(layer*2));
85 ref |= ((uint16)state)<<(layer*2);
88 // ***************************************************************************
90 void CZoneSymmetrisation::setTileBorderState (uint patch, uint tile, TState state)
92 setTileState (patch, tile, 3, state);
95 // ***************************************************************************
97 void CZoneSymmetrisation::setOrientedTileBorderState (uint patch, uint tile, TState state)
99 setTileState (patch, tile, 4, state);
102 // ***************************************************************************
104 void CZoneSymmetrisation::setOrientedTileCorner (uint patch, uint tile, bool corner)
106 uint16 &ref = _TilesLayerStates[patch][tile];
107 ref &= ~(1<<10);
108 ref |= ((uint16)corner)<<(10);
111 // ***************************************************************************
115 REMARKS:
116 - I call "isotile" the set of connected tiles in the zone using the same tileset and a continus rotation value.
118 This method determines the state of each tile in the zone. This state will be used to transform
119 the tile during symmetrisation of the zone.
121 The state can be Regular, Goofy or Nothing.
123 If the state is Nothing, this tile is in an "isotile" that is not connected to a zone border. No matter the method
124 you use to tranform it, it will not influence the neighbor zones.
126 If the state is Regular, the rotation of this tile after symmetrisation will by given by this formula : tilerot = (4-tilerot)&3
127 If the state is Goofy, the rotation of this tile after symmetrisation will by given by this formula : tilerot = (4-tilerot+2)&3
129 - Getting the state of the tile:
130 - A) We flag all the zone patches as Nothing.
131 - B) We need to select patches having an open edge on the zone border. To do so, we use the snapCell and weldThreshold
132 parameters.
133 - C) Then, for each patches on the zone border, we need to know if they are Goofy or Regular.
139 | 0 3
140 | *****
141 | * * This patch is regular
142 | * *
143 | *****
144 | 1 2
146 | 2 1
147 | *****
148 | * * This patch is regular
149 | * *
150 | *****
151 | 3 0
153 | 3 2
154 | *****
155 | * * This patch is goofy
156 | * *
157 | *****
158 | 0 1
160 | 1 4
161 | *****
162 | * * This patch is goofy
163 | * *
164 | *****
165 | 2 3
166 -----------------------------------------> X
168 - D) We flag each tiles as Nothing
169 - E) We flag each tile on an opened edge using this formula:
170 - If the patch is not Nothing do
171 - tileIsGoofy = (patchIsGoofy) XOR ((tileRotation&1) != 0)
172 - F) Then, we propagate the tile A flag across the tiles in the zone following this rules:
173 - If A is not Nothing do
174 - For each neighbor B of A do
175 - If B is different from A do
176 - If A is Regular (res Goofy), and B is Nothing, B will be Regular (res Goofy)
177 - If A is Regular (res Goofy), and B is Goofy (res Regular), -> Error
178 - Propagate B
181 bool CZoneSymmetrisation::build (const std::vector<CPatchInfo> &patchInfo, float snapCell, float weldThreshold, const CTileBank &bank, CError &errorDesc, const NLMISC::CMatrix &toOriginalSpace)
183 // * Clear errors
184 errorDesc.Errors.clear ();
186 // * Build the patches state
188 // A) Resize arrays
189 _TilesLayerStates.resize (patchInfo.size ());
191 // D) Resize the tile array
192 uint i;
193 for (i=0; i<patchInfo.size (); i++)
195 // Ref on the patch
196 const CPatchInfo &patch = patchInfo[i];
197 _TilesLayerStates[i].resize (0);
198 _TilesLayerStates[i].resize (patch.OrderS * patch.OrderT, 0);
201 // B), C) and E) We need to select patches having an open edge on the zone border. To do so, we use the snapCell and weldThreshold parameters
202 for (i=0; i<patchInfo.size (); i++)
204 // Ref on the patch
205 const CPatchInfo &patch = patchInfo[i];
207 // Does this patch is over a border ?
208 TState patchState;
209 if (!setTileState (patch, i, snapCell, weldThreshold, patchState, toOriginalSpace, bank))
211 // Push an error
212 errorDesc.Errors.push_back ("Patch nb "+toString (i)+" is invalid");
215 // Set the oriented patch state
216 if (!setOrientedTileState (patch, i, snapCell, weldThreshold, patchState, toOriginalSpace, bank))
218 // Push an error
219 errorDesc.Errors.push_back ("Patch nb "+toString (i)+" is invalid");
223 // F) We flag each tile on an opened edge using this formula
224 // - If the patch is not Nothing do
225 // - tileIsGoofy = (patchIsGoofy) XOR ((tileRotation&1) != 0)
226 for (i=0; i<patchInfo.size (); i++)
228 // Ref on the patch
229 const CPatchInfo &patch = patchInfo[i];
231 // For each tile
232 uint s,t;
233 for (t=0; t<patch.OrderT; t++)
235 for (s=0; s<patch.OrderS; s++)
237 if (!propagateTileState (i, s, t, patchInfo, bank, false))
239 // Push an error
240 errorDesc.Errors.push_back ("Error during propagation. Topology invalid.");
241 return false;
247 // G) Force all remaining Nothing tiles to Regular
248 for (i=0; i<patchInfo.size (); i++)
250 // Ref on the patch
251 const CPatchInfo &patch = patchInfo[i];
253 // For each tile
254 uint s,t;
255 for (t=0; t<patch.OrderT; t++)
257 for (s=0; s<patch.OrderS; s++)
259 if (!propagateTileState (i, s, t, patchInfo, bank, true))
261 // Push an error
262 errorDesc.Errors.push_back ("Error during propagation. Topology invalid.");
263 return false;
269 // Returns true if no error
270 return true; // errorDesc.Errors.size () == 0;
273 // ***************************************************************************
275 bool CZoneSymmetrisation::snapOnGrid (float& value, float resolution, float snap)
277 // Calc the floor
278 float _floor = (float) ( resolution * floor (value / resolution) );
279 nlassert (_floor<=value);
281 // Calc the remainder
282 float remainder = value - _floor;
283 //nlassert ( (remainder>=0) && (remainder<resolution) );
285 // Check the snape
286 if ( remainder <= snap )
288 // Flag it
289 value = _floor;
291 // Floor is good
292 return true;
294 else if ( (resolution - remainder) <= snap )
296 // Flag it
297 value = _floor + resolution;
299 // Floor + resolution is good
300 return true;
302 return false;
305 // ***************************************************************************
307 bool CZoneSymmetrisation::setTileState (const NL3D::CPatchInfo &patch, uint patchId, float snapCell, float weldThreshold, TState &state, const NLMISC::CMatrix &toOriginalSpace, const CTileBank &bank)
309 // Edges state
310 TState edgesState[4] = { Nothing, Nothing, Nothing, Nothing };
312 // Vertices position
313 sint32 vertPosU[4];
314 sint32 vertPosV[4];
316 // For each vertices
317 uint i;
318 for (i=0; i<4; i++)
320 // Snap the vertex
321 CVector original = toOriginalSpace * patch.Patch.Vertices[i];
322 float valueU = original.x;
323 float valueV = original.y;
325 // Snap on U
326 if (snapOnGrid (valueU, snapCell, weldThreshold))
327 vertPosU[i] = (sint32)((valueU+0.5f) / snapCell);
328 else
329 vertPosU[i] = 0x80000000;
331 // Snap on V
332 if (snapOnGrid (valueV, snapCell, weldThreshold))
333 vertPosV[i] = (sint32)((valueV+0.5f) / snapCell);
334 else
335 vertPosV[i] = 0x80000000;
338 // Patch flags
339 bool regular = false;
340 bool goofy = false;
341 bool EdgeSnaped[4] = { false, false, false, false };
343 // For each edges
344 for (i=0; i<4; i++)
346 // Vertex snapped and align on a common axis ?
347 if ( ((uint32) vertPosU[i] != 0x80000000) || ((uint32) vertPosV[i] != 0x80000000) )
349 // Snapped on U or V ?
350 bool snapU = (vertPosU[i] == vertPosU[(i+1)&3]) && ((uint32) vertPosU[i] != 0x80000000);
351 bool snapV = (vertPosV[i] == vertPosV[(i+1)&3]) && ((uint32) vertPosV[i] != 0x80000000);
353 // If snapped on one, continue
354 if (snapU || snapV)
356 // If snap on the both, error
357 if (snapU && snapV)
358 return false;
360 // Is this edge Regular or Goofy ?
361 if (snapU)
362 edgesState[i] = (i&1)?Goofy:Regular;
363 else // (snapV)
364 edgesState[i] = (i&1)?Regular:Goofy;
366 // Flag the patch
367 if (edgesState[i] == Regular)
368 regular = true;
369 else
370 goofy = true;
372 // Edge snaped
373 EdgeSnaped[i] = true;
378 // Goofy and regular ? Error
379 if (goofy && regular)
380 return false;
382 // Nothing ?
383 if ((!goofy) && (!regular))
384 state = Nothing;
385 else
387 // Not nothing ?
388 state = regular?Regular:Goofy;
390 // * Set the tiles
392 // For each edges
393 for (i=0; i<4; i++)
395 // Edge snapped ?
396 if (EdgeSnaped[i])
398 // For each tiles
399 uint tileCount = ((i&1)!=0)?patch.OrderS:patch.OrderT;
400 sint currentTile;
401 sint delta;
402 switch (i)
404 case 0:
405 currentTile = 0;
406 delta = patch.OrderS;
407 break;
408 case 1:
409 currentTile = patch.OrderS*(patch.OrderT-1);
410 delta = 1;
411 break;
412 case 2:
413 currentTile = patch.OrderS-1;
414 delta = patch.OrderS;
415 break;
416 case 3:
417 currentTile = 0;
418 delta = 1;
419 break;
420 default:
421 currentTile = 0;
422 delta = 1;
423 break;
425 uint j;
426 for (j=0; j<tileCount; j++)
428 // Set the border state
429 setTileBorderState (patchId, currentTile, state);
431 // For each layer
432 uint layer;
433 for (layer=0; layer<3; layer++)
435 // Get the tiles set used here
436 uint tile = patch.Tiles[currentTile].Tile[layer];
437 if (tile != NL_TILE_ELM_LAYER_EMPTY)
439 int tileSet;
440 int number;
441 CTileBank::TTileType type;
442 bank.getTileXRef (tile, tileSet, number, type);
444 if ((tileSet < 0) || (tileSet >= bank.getTileSetCount()))
446 nlwarning("CZoneSymmetrisation::setTileState : tile %d has an unknown tileSet (%d)", tile, tileSet);
447 return false;
450 // Set it only if not oriented
451 if (!bank.getTileSet (tileSet)->getOriented ())
453 // Set the tile state
454 setTileState (patchId, currentTile, layer, state);
459 // Next tile
460 currentTile += delta;
466 return true;
469 // ***************************************************************************
471 bool CZoneSymmetrisation::setOrientedTileState (const NL3D::CPatchInfo &patch, uint patchId, float snapCell, float weldThreshold, TState &state, const NLMISC::CMatrix &toOriginalSpace, const CTileBank &bank)
473 // Edges state
474 TState edgesState[4] = { Nothing, Nothing, Nothing, Nothing };
476 // Vertices position
477 sint32 vertPosU[4];
478 sint32 vertPosV[4];
480 // For each vertices
481 uint i;
482 for (i=0; i<4; i++)
484 // Snap the vertex
485 CVector original = toOriginalSpace * patch.Patch.Vertices[i];
486 float valueU = original.x;
487 float valueV = original.y;
489 // Snap on U
490 if (snapOnGrid (valueU, snapCell, weldThreshold))
491 vertPosU[i] = (sint32)((valueU+0.5f) / snapCell);
492 else
493 vertPosU[i] = 0x80000000;
495 // Snap on V
496 if (snapOnGrid (valueV, snapCell, weldThreshold))
497 vertPosV[i] = (sint32)((valueV+0.5f) / snapCell);
498 else
499 vertPosV[i] = 0x80000000;
502 // Patch flags
503 bool regular = false;
504 bool goofy = false;
505 bool EdgeSnaped[4] = { false, false, false, false };
507 // For each edges
508 for (i=0; i<4; i++)
510 // Vertex snapped and align on a common axis ?
511 if ( ((uint32) vertPosU[i] != 0x80000000) || ((uint32) vertPosV[i] != 0x80000000) )
513 // Snapped on U or V ?
514 bool snapU = (vertPosU[i] == vertPosU[(i+1)&3]) && ((uint32) vertPosU[i] != 0x80000000);
515 bool snapV = (vertPosV[i] == vertPosV[(i+1)&3]) && ((uint32) vertPosV[i] != 0x80000000);
517 // If snapped on one, continue
518 if (snapU || snapV)
520 // If snap on the both, error
521 if (snapU && snapV)
522 return false;
524 // Is this edge Regular or Goofy ?
525 edgesState[i] = (i&1)?Goofy:Regular;
527 // Flag the patch
528 if (edgesState[i] == Regular)
529 regular = true;
530 else
531 goofy = true;
533 // Edge snaped
534 EdgeSnaped[i] = true;
539 // * Set the tiles
541 // For each edges
542 for (i=0; i<4; i++)
544 // Edge snapped ?
545 if (EdgeSnaped[i])
547 // For each tiles
548 uint tileCount = ((i&1)!=0)?patch.OrderS:patch.OrderT;
549 sint currentTile;
550 sint delta;
551 switch (i)
553 case 0:
554 currentTile = 0;
555 delta = patch.OrderS;
556 break;
557 case 1:
558 currentTile = patch.OrderS*(patch.OrderT-1);
559 delta = 1;
560 break;
561 case 2:
562 currentTile = patch.OrderS-1;
563 delta = patch.OrderS;
564 break;
565 case 3:
566 currentTile = 0;
567 delta = 1;
568 break;
569 default:
570 currentTile = 0;
571 delta = 1;
572 break;
574 uint j;
575 for (j=0; j<tileCount; j++)
577 // Set the border state
578 setOrientedTileBorderState (patchId, currentTile, edgesState[i]);
580 // For each layer
581 uint layer;
582 for (layer=0; layer<3; layer++)
584 // Get the tiles set used here
585 uint tile = patch.Tiles[currentTile].Tile[layer];
586 if (tile != NL_TILE_ELM_LAYER_EMPTY)
588 int tileSet;
589 int number;
590 CTileBank::TTileType type;
592 bank.getTileXRef (tile, tileSet, number, type);
593 if ((tileSet < 0) || (tileSet >= bank.getTileSetCount()))
595 nlwarning("CZoneSymmetrisation::setOrientedTileState : tile %d has an unknown tileSet (%d)", tile, tileSet);
596 return false;
599 // Set it only if oriented
600 if (bank.getTileSet (tileSet)->getOriented ())
602 setTileState (patchId, currentTile, layer, edgesState[i]);
607 // Next tile
608 currentTile += delta;
613 // For each corners
614 for (i=0; i<4; i++)
616 // Corner snapped ?
617 uint next = (i+1)&3;
618 if (EdgeSnaped[i] && EdgeSnaped[next])
620 // Flag tile as corner
621 switch (i)
623 case 0:
624 setOrientedTileCorner (patchId, patch.OrderS*(patch.OrderT-1), true);
625 break;
626 case 1:
627 setOrientedTileCorner (patchId, patch.OrderS*patch.OrderT-1, true);
628 break;
629 case 2:
630 setOrientedTileCorner (patchId, patch.OrderS-1, true);
631 break;
632 case 3:
633 setOrientedTileCorner (patchId, 0, true);
634 break;
639 return true;
642 // ***************************************************************************
644 CVector2f st2uv (sint s, sint t, const CPatchInfo &patch)
646 return CVector2f ((((float)s)+0.5f)/(float)patch.OrderS, (((float)t)+0.5f)/(float)patch.OrderT);
649 // ***************************************************************************
651 void uv2st (const CVector2f &in, sint &s, sint &t, const CPatchInfo &patch)
653 s = (sint)(in.x*(float)patch.OrderS);
654 t = (sint)(in.y*(float)patch.OrderT);
657 // ***************************************************************************
659 class CFillStackNode
661 public:
662 CFillStackNode (uint16 patch, uint16 s, uint16 t, uint8 rotate, CZoneSymmetrisation::TState state) { Patch = patch; S = s; T = t; Edge = 0; Rotate = rotate; State = state; };
663 uint16 S;
664 uint16 T;
665 uint16 Patch;
666 uint8 Edge;
667 uint8 Rotate;
668 CZoneSymmetrisation::TState State;
671 // ***************************************************************************
673 bool CZoneSymmetrisation::propagateTileState (uint patch, uint s, uint t, const std::vector<CPatchInfo> &patchInfo, const CTileBank &bank, bool forceRegular)
675 // For each layer
676 uint layer;
677 for (layer=0; layer<3; layer++)
679 // Get the patch ptr
680 const CPatchInfo *currentPatchPtr = &(patchInfo[patch]);
682 // Get the tile index
683 uint tile = s+t*currentPatchPtr->OrderS;
685 // Get the tiles set used here
686 uint tileIndex = currentPatchPtr->Tiles[tile].Tile[layer];
687 if (tileIndex != NL_TILE_ELM_LAYER_EMPTY)
689 // Valid tile number ?
690 if (tileIndex >= (uint)bank.getTileCount ())
692 nlwarning ("CZoneSymmetrisation::propagateTileState: Invalid tile index");
693 return false;
696 // Get the tile set used by this layer
697 int tileSetToPropagate;
698 int number;
699 CTileBank::TTileType type;
700 bank.getTileXRef (tileIndex, tileSetToPropagate, number, type);
702 if ((tileSetToPropagate < 0) || (tileSetToPropagate >= bank.getTileSetCount()))
704 nlwarning("CZoneSymmetrisation::propagateTileState: tile %d has an unknown tileSet (%d)", tileIndex, tileSetToPropagate);
706 else
708 // Oriented ?
709 bool oriented = bank.getTileSet (tileSetToPropagate)->getOriented ();
711 // If oriented, must not be a corner
712 if (!(oriented && getOrientedTileCorner (patch, tile)))
714 // Current node
715 CFillStackNode currentNode (patch, s, t, currentPatchPtr->Tiles[tile].getTileOrient(layer), getTileState (patch, tile, layer));
717 // Propagate non-Nothing tiles
718 if ( (!forceRegular && (currentNode.State != Nothing)) || (forceRegular && (currentNode.State == Nothing)) )
720 // Force to Regular ?
721 if (forceRegular)
723 setTileState (patch, tile, layer, Regular);
724 currentNode.State = Regular;
727 // Fill stack
728 vector<CFillStackNode> stack;
729 stack.push_back (currentNode);
731 // While people in the stack
732 while (!stack.empty ())
734 // Pop last element
735 currentNode = stack.back ();
736 stack.pop_back ();
740 // Set current patch pointer
741 currentPatchPtr = &(patchInfo[currentNode.Patch]);
743 // Get neighbor
744 CFillStackNode neighborNode (currentNode.Patch, currentNode.S, currentNode.T, currentNode.Rotate, currentNode.State);
745 switch (currentNode.Edge)
747 case 0:
748 neighborNode.S--;
749 break;
750 case 1:
751 neighborNode.T++;
752 break;
753 case 2:
754 neighborNode.S++;
755 break;
756 case 3:
757 neighborNode.T--;
758 break;
761 // Is still in patch ?
762 if ( (neighborNode.S>=patchInfo[currentNode.Patch].OrderS) || (neighborNode.T>=patchInfo[currentNode.Patch].OrderT) )
764 // No, found new patch
765 uint position;
766 switch (currentNode.Edge)
768 case 0:
769 position = neighborNode.T;
770 break;
771 case 1:
772 position = neighborNode.S;
773 break;
774 case 2:
775 position = patchInfo[currentNode.Patch].OrderT - neighborNode.T - 1;
776 break;
777 case 3:
778 position = patchInfo[currentNode.Patch].OrderS - neighborNode.S - 1;
779 break;
780 default:
781 position = 0;
782 break;
785 // Get next patch
786 uint patchOut;
787 sint sOut;
788 sint tOut;
789 if (patchInfo[currentNode.Patch].getNeighborTile (currentNode.Patch, currentNode.Edge, position,
790 patchOut, sOut, tOut, patchInfo))
792 // Should be another patch
793 nlassert (patchOut != currentNode.Patch);
795 // Get patch id
796 neighborNode.Patch = patchOut;
798 // Coordinate must be IN the patch
799 nlassert (sOut >= 0);
800 nlassert (tOut >= 0);
801 nlassert (sOut < patchInfo[neighborNode.Patch].OrderS);
802 nlassert (tOut < patchInfo[neighborNode.Patch].OrderT);
804 // Copy it
805 neighborNode.S = sOut;
806 neighborNode.T = tOut;
808 // Find neighbor
809 const CPatchInfo::CBindInfo &neighborBindInfo = patchInfo[currentNode.Patch].BindEdges[currentNode.Edge];
810 uint edgePatch;
811 for (edgePatch=0; edgePatch<(uint)neighborBindInfo.NPatchs; edgePatch++)
813 if (neighborBindInfo.Next[edgePatch] == neighborNode.Patch)
814 break;
817 // Must find one patch
818 nlassert (edgePatch<(uint)neighborBindInfo.NPatchs);
820 // Rotation
821 neighborNode.Rotate = (currentNode.Rotate + 2 + neighborBindInfo.Edge[edgePatch] - currentNode.Edge) & 3;
823 // Toggle the state ?
824 if ((neighborNode.Rotate ^ currentNode.Rotate) & 1)
826 // Yes
827 neighborNode.State = (neighborNode.State == Regular) ? Goofy : Regular;
830 else
832 // No propagation, continue
833 currentNode.Edge++;
834 continue;
838 // Neighbor patch
839 const CPatchInfo *neighborPatchPtr = &(patchInfo[neighborNode.Patch]);
841 // Get the tile index
842 uint neighborTile = neighborNode.S+neighborNode.T*neighborPatchPtr->OrderS;
844 // Look for the same tile set in the new tile
845 uint neighborLayer;
846 for (neighborLayer=0; neighborLayer<3; neighborLayer++)
848 // Get the tile index
849 uint neighborTileIndex = neighborPatchPtr->Tiles[neighborTile].Tile[neighborLayer];
851 if (neighborTileIndex != NL_TILE_ELM_LAYER_EMPTY)
853 // Valid tile number ?
854 if (neighborTileIndex >= (uint)bank.getTileCount ())
856 nlwarning ("CZoneSymmetrisation::propagateTileState: Invalid tile index");
857 return false;
860 // Get tileset
861 int neighborTileSet;
862 int neighborNumber;
863 CTileBank::TTileType neighborType;
864 bank.getTileXRef (neighborTileIndex, neighborTileSet, neighborNumber, neighborType);
866 // Same tileset ? Stop!
867 if ( (neighborTileSet == tileSetToPropagate) &&
868 (neighborNode.Rotate == neighborPatchPtr->Tiles[neighborTile].getTileOrient(neighborLayer)) )
869 break;
873 // Found ?
874 if (neighborLayer<3)
876 // If oriented, must not be a corner
877 if (!(oriented && getOrientedTileCorner (neighborNode.Patch, neighborTile)))
879 // Propagate in the new node ?
880 TState neighborState = getTileState (neighborNode.Patch, neighborTile, neighborLayer);
881 if (neighborState == Nothing)
883 // Set the state
884 setTileState (neighborNode.Patch, neighborTile, neighborLayer, neighborNode.State);
886 // Stack current node if some neighbor left to visit
887 if (currentNode.Edge < 3)
889 currentNode.Edge++;
890 stack.push_back (currentNode);
893 // Continue with the new node
894 currentNode = neighborNode;
896 else if (neighborState != neighborNode.State)
898 // Error, same tile but not same state
899 // nlwarning ("CZoneSymmetrisation::propagateTileState: error, find same iso surfaces with different state.");
901 // No propagation, continue
902 currentNode.Edge++;
904 else
906 // No propagation, continue
907 currentNode.Edge++;
910 else
912 // No propagation, continue
913 currentNode.Edge++;
916 else
917 // No propagation, continue
918 currentNode.Edge++;
920 while (currentNode.Edge<4);
928 return true;
931 // ***************************************************************************
933 } // NL3D