1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
18 #include "nel/3d/zone_symmetrisation.h"
19 #include "nel/3d/zone.h"
20 #include "nel/3d/tile_bank.h"
23 using namespace NLMISC
;
32 // ***************************************************************************
34 CZoneSymmetrisation::CZoneSymmetrisation()
38 // ***************************************************************************
46 9-8 : oriented border state
50 // ***************************************************************************
52 CZoneSymmetrisation::TState
CZoneSymmetrisation::getTileState (uint patch
, uint tile
, uint layer
) const
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
];
108 ref
|= ((uint16
)corner
)<<(10);
111 // ***************************************************************************
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
133 - C) Then, for each patches on the zone border, we need to know if they are Goofy or Regular.
141 | * * This patch is regular
148 | * * This patch is regular
155 | * * This patch is goofy
162 | * * This patch is goofy
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
181 bool CZoneSymmetrisation::build (const std::vector
<CPatchInfo
> &patchInfo
, float snapCell
, float weldThreshold
, const CTileBank
&bank
, CError
&errorDesc
, const NLMISC::CMatrix
&toOriginalSpace
)
184 errorDesc
.Errors
.clear ();
186 // * Build the patches state
189 _TilesLayerStates
.resize (patchInfo
.size ());
191 // D) Resize the tile array
193 for (i
=0; i
<patchInfo
.size (); i
++)
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
++)
205 const CPatchInfo
&patch
= patchInfo
[i
];
207 // Does this patch is over a border ?
209 if (!setTileState (patch
, i
, snapCell
, weldThreshold
, patchState
, toOriginalSpace
, bank
))
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
))
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
++)
229 const CPatchInfo
&patch
= patchInfo
[i
];
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))
240 errorDesc
.Errors
.push_back ("Error during propagation. Topology invalid.");
247 // G) Force all remaining Nothing tiles to Regular
248 for (i
=0; i
<patchInfo
.size (); i
++)
251 const CPatchInfo
&patch
= patchInfo
[i
];
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))
262 errorDesc
.Errors
.push_back ("Error during propagation. Topology invalid.");
269 // Returns true if no error
270 return true; // errorDesc.Errors.size () == 0;
273 // ***************************************************************************
275 bool CZoneSymmetrisation::snapOnGrid (float& value
, float resolution
, float snap
)
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) );
286 if ( remainder
<= snap
)
294 else if ( (resolution
- remainder
) <= snap
)
297 value
= _floor
+ resolution
;
299 // Floor + resolution is good
305 // ***************************************************************************
307 bool CZoneSymmetrisation::setTileState (const NL3D::CPatchInfo
&patch
, uint patchId
, float snapCell
, float weldThreshold
, TState
&state
, const NLMISC::CMatrix
&toOriginalSpace
, const CTileBank
&bank
)
310 TState edgesState
[4] = { Nothing
, Nothing
, Nothing
, Nothing
};
321 CVector original
= toOriginalSpace
* patch
.Patch
.Vertices
[i
];
322 float valueU
= original
.x
;
323 float valueV
= original
.y
;
326 if (snapOnGrid (valueU
, snapCell
, weldThreshold
))
327 vertPosU
[i
] = (sint32
)((valueU
+0.5f
) / snapCell
);
329 vertPosU
[i
] = 0x80000000;
332 if (snapOnGrid (valueV
, snapCell
, weldThreshold
))
333 vertPosV
[i
] = (sint32
)((valueV
+0.5f
) / snapCell
);
335 vertPosV
[i
] = 0x80000000;
339 bool regular
= false;
341 bool EdgeSnaped
[4] = { false, false, false, false };
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
356 // If snap on the both, error
360 // Is this edge Regular or Goofy ?
362 edgesState
[i
] = (i
&1)?Goofy
:Regular
;
364 edgesState
[i
] = (i
&1)?Regular
:Goofy
;
367 if (edgesState
[i
] == Regular
)
373 EdgeSnaped
[i
] = true;
378 // Goofy and regular ? Error
379 if (goofy
&& regular
)
383 if ((!goofy
) && (!regular
))
388 state
= regular
?Regular
:Goofy
;
399 uint tileCount
= ((i
&1)!=0)?patch
.OrderS
:patch
.OrderT
;
406 delta
= patch
.OrderS
;
409 currentTile
= patch
.OrderS
*(patch
.OrderT
-1);
413 currentTile
= patch
.OrderS
-1;
414 delta
= patch
.OrderS
;
426 for (j
=0; j
<tileCount
; j
++)
428 // Set the border state
429 setTileBorderState (patchId
, currentTile
, state
);
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
)
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
);
450 // Set it only if not oriented
451 if (!bank
.getTileSet (tileSet
)->getOriented ())
453 // Set the tile state
454 setTileState (patchId
, currentTile
, layer
, state
);
460 currentTile
+= delta
;
469 // ***************************************************************************
471 bool CZoneSymmetrisation::setOrientedTileState (const NL3D::CPatchInfo
&patch
, uint patchId
, float snapCell
, float weldThreshold
, TState
&state
, const NLMISC::CMatrix
&toOriginalSpace
, const CTileBank
&bank
)
474 TState edgesState
[4] = { Nothing
, Nothing
, Nothing
, Nothing
};
485 CVector original
= toOriginalSpace
* patch
.Patch
.Vertices
[i
];
486 float valueU
= original
.x
;
487 float valueV
= original
.y
;
490 if (snapOnGrid (valueU
, snapCell
, weldThreshold
))
491 vertPosU
[i
] = (sint32
)((valueU
+0.5f
) / snapCell
);
493 vertPosU
[i
] = 0x80000000;
496 if (snapOnGrid (valueV
, snapCell
, weldThreshold
))
497 vertPosV
[i
] = (sint32
)((valueV
+0.5f
) / snapCell
);
499 vertPosV
[i
] = 0x80000000;
503 bool regular
= false;
505 bool EdgeSnaped
[4] = { false, false, false, false };
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
520 // If snap on the both, error
524 // Is this edge Regular or Goofy ?
525 edgesState
[i
] = (i
&1)?Goofy
:Regular
;
528 if (edgesState
[i
] == Regular
)
534 EdgeSnaped
[i
] = true;
548 uint tileCount
= ((i
&1)!=0)?patch
.OrderS
:patch
.OrderT
;
555 delta
= patch
.OrderS
;
558 currentTile
= patch
.OrderS
*(patch
.OrderT
-1);
562 currentTile
= patch
.OrderS
-1;
563 delta
= patch
.OrderS
;
575 for (j
=0; j
<tileCount
; j
++)
577 // Set the border state
578 setOrientedTileBorderState (patchId
, currentTile
, edgesState
[i
]);
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
)
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
);
599 // Set it only if oriented
600 if (bank
.getTileSet (tileSet
)->getOriented ())
602 setTileState (patchId
, currentTile
, layer
, edgesState
[i
]);
608 currentTile
+= delta
;
618 if (EdgeSnaped
[i
] && EdgeSnaped
[next
])
620 // Flag tile as corner
624 setOrientedTileCorner (patchId
, patch
.OrderS
*(patch
.OrderT
-1), true);
627 setOrientedTileCorner (patchId
, patch
.OrderS
*patch
.OrderT
-1, true);
630 setOrientedTileCorner (patchId
, patch
.OrderS
-1, true);
633 setOrientedTileCorner (patchId
, 0, 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 // ***************************************************************************
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
; };
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
)
677 for (layer
=0; layer
<3; layer
++)
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");
696 // Get the tile set used by this layer
697 int tileSetToPropagate
;
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
);
709 bool oriented
= bank
.getTileSet (tileSetToPropagate
)->getOriented ();
711 // If oriented, must not be a corner
712 if (!(oriented
&& getOrientedTileCorner (patch
, tile
)))
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 ?
723 setTileState (patch
, tile
, layer
, Regular
);
724 currentNode
.State
= Regular
;
728 vector
<CFillStackNode
> stack
;
729 stack
.push_back (currentNode
);
731 // While people in the stack
732 while (!stack
.empty ())
735 currentNode
= stack
.back ();
740 // Set current patch pointer
741 currentPatchPtr
= &(patchInfo
[currentNode
.Patch
]);
744 CFillStackNode
neighborNode (currentNode
.Patch
, currentNode
.S
, currentNode
.T
, currentNode
.Rotate
, currentNode
.State
);
745 switch (currentNode
.Edge
)
761 // Is still in patch ?
762 if ( (neighborNode
.S
>=patchInfo
[currentNode
.Patch
].OrderS
) || (neighborNode
.T
>=patchInfo
[currentNode
.Patch
].OrderT
) )
764 // No, found new patch
766 switch (currentNode
.Edge
)
769 position
= neighborNode
.T
;
772 position
= neighborNode
.S
;
775 position
= patchInfo
[currentNode
.Patch
].OrderT
- neighborNode
.T
- 1;
778 position
= patchInfo
[currentNode
.Patch
].OrderS
- neighborNode
.S
- 1;
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
);
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
);
805 neighborNode
.S
= sOut
;
806 neighborNode
.T
= tOut
;
809 const CPatchInfo::CBindInfo
&neighborBindInfo
= patchInfo
[currentNode
.Patch
].BindEdges
[currentNode
.Edge
];
811 for (edgePatch
=0; edgePatch
<(uint
)neighborBindInfo
.NPatchs
; edgePatch
++)
813 if (neighborBindInfo
.Next
[edgePatch
] == neighborNode
.Patch
)
817 // Must find one patch
818 nlassert (edgePatch
<(uint
)neighborBindInfo
.NPatchs
);
821 neighborNode
.Rotate
= (currentNode
.Rotate
+ 2 + neighborBindInfo
.Edge
[edgePatch
] - currentNode
.Edge
) & 3;
823 // Toggle the state ?
824 if ((neighborNode
.Rotate
^ currentNode
.Rotate
) & 1)
827 neighborNode
.State
= (neighborNode
.State
== Regular
) ? Goofy
: Regular
;
832 // No propagation, continue
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
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");
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
)) )
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
)
884 setTileState (neighborNode
.Patch
, neighborTile
, neighborLayer
, neighborNode
.State
);
886 // Stack current node if some neighbor left to visit
887 if (currentNode
.Edge
< 3)
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
906 // No propagation, continue
912 // No propagation, continue
917 // No propagation, continue
920 while (currentNode
.Edge
<4);
931 // ***************************************************************************