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/>.
19 #include "nel/3d/zone.h"
20 #include "nel/3d/landscape.h"
21 #include "nel/3d/zone_symmetrisation.h"
22 #include "nel/misc/common.h"
23 #include "nel/misc/hierarchical_timer.h"
26 using namespace NLMISC
;
30 // define it only for debug bind.
31 //#define NL3D_DEBUG_DONT_BIND_PATCH
42 // ***************************************************************************
43 // ***************************************************************************
45 // ***************************************************************************
46 // ***************************************************************************
49 // ***************************************************************************
50 void CPatchInfo::setCornerSmoothFlag(uint corner
, bool smooth
)
55 _CornerSmoothFlag
|= mask
;
57 _CornerSmoothFlag
&= ~mask
;
60 // ***************************************************************************
61 bool CPatchInfo::getCornerSmoothFlag(uint corner
) const
65 return (_CornerSmoothFlag
& mask
)!=0;
69 // ***************************************************************************
70 // ***************************************************************************
72 // ***************************************************************************
73 // ***************************************************************************
76 // ***************************************************************************
84 // ***************************************************************************
87 // release() must have been called.
92 // ***************************************************************************
93 void CZone::computeBBScaleBias(const CAABBox
&bb
)
96 // Take a security for noise. (useful for zone clipping).
97 ZoneBB
.setHalfSize(ZoneBB
.getHalfSize()+CVector(NL3D_NOISE_MAX
, NL3D_NOISE_MAX
, NL3D_NOISE_MAX
));
98 CVector hs
= ZoneBB
.getHalfSize();
99 float rmax
= maxof(hs
.x
, hs
.y
, hs
.z
);
100 PatchScale
= rmax
/ 32760; // Prevent from float imprecision by taking 32760 and not 32767.
101 PatchBias
= ZoneBB
.getCenter();
105 // ***************************************************************************
106 void CZone::build(uint16 zoneId
, const std::vector
<CPatchInfo
> &patchs
, const std::vector
<CBorderVertex
> &borderVertices
, uint32 numVertices
)
109 zinfo
.ZoneId
= zoneId
;
110 zinfo
.Patchs
= patchs
;
111 zinfo
.BorderVertices
= borderVertices
;
113 build(zinfo
, numVertices
);
115 // ***************************************************************************
116 void CZone::build(const CZoneInfo
&zoneInfo
, uint32 numVertices
)
122 uint16 zoneId
= zoneInfo
.ZoneId
;
123 const std::vector
<CPatchInfo
> &patchs
= zoneInfo
.Patchs
;
124 const std::vector
<CBorderVertex
> &borderVertices
= zoneInfo
.BorderVertices
;
128 BorderVertices
= borderVertices
;
130 // Compute the bbox and the bias/scale.
131 //=====================================
134 bb
.setCenter(patchs
[0].Patch
.Vertices
[0]);
135 bb
.setHalfSize(CVector::Null
);
136 for(j
=0;j
<(sint
)patchs
.size();j
++)
138 const CBezierPatch
&p
= patchs
[j
].Patch
;
140 bb
.extend(p
.Vertices
[i
]);
142 bb
.extend(p
.Tangents
[i
]);
144 bb
.extend(p
.Interiors
[i
]);
146 // Compute BBox, and Patch Scale Bias, according to Noise.
147 computeBBScaleBias(bb
);
150 // Compute/compress Patchs.
151 //=========================
152 Patchs
.resize(patchs
.size());
153 PatchConnects
.resize(patchs
.size());
155 for(j
=0;j
<(sint
)patchs
.size();j
++)
157 const CPatchInfo
&pi
= patchs
[j
];
158 const CBezierPatch
&p
= pi
.Patch
;
159 CPatch
&pa
= Patchs
[j
];
160 CPatchConnect
&pc
= PatchConnects
[j
];
163 pa
.Flags
&=~NL_PATCH_SMOOTH_FLAG_MASK
;
164 pa
.Flags
|=NL_PATCH_SMOOTH_FLAG_MASK
&(pi
.Flags
<<NL_PATCH_SMOOTH_FLAG_SHIFT
);
168 // copy noise rotation.
169 pa
.NoiseRotation
= pi
.NoiseRotation
;
170 // copy all noise smoothing info.
173 pa
.setCornerSmoothFlag(i
, pi
.getCornerSmoothFlag(i
));
176 // Copy order of the patch
177 pa
.OrderS
= pi
.OrderS
;
178 pa
.OrderT
= pi
.OrderT
;
182 pa
.Vertices
[i
].pack(p
.Vertices
[i
], PatchBias
, PatchScale
);
184 pa
.Tangents
[i
].pack(p
.Tangents
[i
], PatchBias
, PatchScale
);
186 pa
.Interiors
[i
].pack(p
.Interiors
[i
], PatchBias
, PatchScale
);
188 pa
.TileColors
= pi
.TileColors
;
189 /* Copy TileLightInfluences. It is possible that pi.TileLightInfluences.size()!= 0
190 and pi.TileLightInfluences.size()!= (uint)(pi.OrderS/2+1)*(pi.OrderT/2+1)
191 Because of a preceding bug where pa.OrderS and pa.OrderT were not initialized before the
192 pa.resetTileLightInfluences();
194 if( pi
.TileLightInfluences
.size()!= (uint
)(pi
.OrderS
/2+1)*(pi
.OrderT
/2+1) )
196 pa
.resetTileLightInfluences();
200 pa
.TileLightInfluences
= pi
.TileLightInfluences
;
203 // Number of lumels in this patch
204 uint lumelCount
=(pi
.OrderS
*NL_LUMEL_BY_TILE
)*(pi
.OrderT
*NL_LUMEL_BY_TILE
);
207 if (pi
.Lumels
.size ()==lumelCount
)
209 // Pack the lumel map
210 pa
.packShadowMap (&pi
.Lumels
[0]);
215 pa
.resetCompressedLumels ();
218 nlassert(pa
.Tiles
.size()== (uint
)pi
.OrderS
*pi
.OrderT
);
219 nlassert(pa
.TileColors
.size()== (uint
)(pi
.OrderS
+1)*(pi
.OrderT
+1));
221 // Build the patchConnect.
222 pc
.ErrorSize
= pi
.ErrorSize
;
225 pc
.BaseVertices
[i
]= pi
.BaseVertices
[i
];
226 maxVertex
= max((sint
)pc
.BaseVertices
[i
], maxVertex
);
229 pc
.BindEdges
[i
]= pi
.BindEdges
[i
];
232 NumVertices
= maxVertex
+1;
233 NumVertices
= max((uint32
)NumVertices
, numVertices
);
235 // Init the Clip Arrays
236 _PatchRenderClipped
.resize((uint
)Patchs
.size());
237 _PatchOldRenderClipped
.resize((uint
)Patchs
.size());
238 _PatchRenderClipped
.setAll();
239 _PatchOldRenderClipped
.setAll();
242 //=========================
243 // build array, lights are sorted
244 std::vector
<uint
> plRemap
;
245 _PointLightArray
.build(zoneInfo
.PointLights
, plRemap
);
246 // Check TileLightInfluences integrity, and remap PointLight Indices.
247 for(j
=0;j
<(sint
)patchs
.size();j
++)
249 CPatch
&pa
= Patchs
[j
];
250 for(uint k
= 0; k
<pa
.TileLightInfluences
.size(); k
++)
252 CTileLightInfluence
&tli
= pa
.TileLightInfluences
[k
];
253 for(uint l
=0; l
<CTileLightInfluence::NumLightPerCorner
; l
++)
255 // If NULL light, break and continue to next TileLightInfluence.
256 if(tli
.Light
[l
]== 0xFF)
261 nlassert(tli
.Light
[l
] < _PointLightArray
.getPointLights().size());
262 // Remap index, because of light sorting.
263 tli
.Light
[l
]= plRemap
[tli
.Light
[l
]];
271 // ***************************************************************************
272 void CZone::retrieve(std::vector
<CPatchInfo
> &patchs
, std::vector
<CBorderVertex
> &borderVertices
)
278 patchs
= zinfo
.Patchs
;
279 borderVertices
= zinfo
.BorderVertices
;
282 // ***************************************************************************
283 void CZone::retrieve(CZoneInfo
&zoneInfo
)
288 std::vector
<CPatchInfo
> &patchs
= zoneInfo
.Patchs
;
289 std::vector
<CBorderVertex
> &borderVertices
= zoneInfo
.BorderVertices
;
291 zoneInfo
.ZoneId
= getZoneId();
294 // uncompress Patchs.
295 //=========================
296 patchs
.resize(Patchs
.size());
297 for(j
=0;j
<(sint
)patchs
.size();j
++)
299 CPatchInfo
&pi
= patchs
[j
];
300 CBezierPatch
&p
= pi
.Patch
;
301 CPatch
&pa
= Patchs
[j
];
302 CPatchConnect
&pc
= PatchConnects
[j
];
306 pi
.Flags
= (pa
.Flags
&NL_PATCH_SMOOTH_FLAG_MASK
)>>NL_PATCH_SMOOTH_FLAG_SHIFT
;
310 // copy noise rotation.
311 pi
.NoiseRotation
= pa
.NoiseRotation
;
312 // copy all noise smoothing info.
315 pi
.setCornerSmoothFlag(i
, pa
.getCornerSmoothFlag(i
));
319 // re-Build the uncompressed bezier patch.
321 pa
.Vertices
[i
].unpack(p
.Vertices
[i
], PatchBias
, PatchScale
);
323 pa
.Tangents
[i
].unpack(p
.Tangents
[i
], PatchBias
, PatchScale
);
325 pa
.Interiors
[i
].unpack(p
.Interiors
[i
], PatchBias
, PatchScale
);
327 pi
.TileColors
= pa
.TileColors
;
328 pi
.TileLightInfluences
= pa
.TileLightInfluences
;
329 pi
.Lumels
.resize ((pa
.OrderS
*4)*(pa
.OrderT
*4));
330 pi
.Flags
=(pa
.Flags
&NL_PATCH_SMOOTH_FLAG_MASK
)>>NL_PATCH_SMOOTH_FLAG_SHIFT
;
332 // Unpack the lumel map
333 pa
.unpackShadowMap (&pi
.Lumels
[0]);
335 // from the patchConnect.
336 pi
.OrderS
= pa
.OrderS
;
337 pi
.OrderT
= pa
.OrderT
;
338 pi
.ErrorSize
= pc
.ErrorSize
;
341 pi
.BaseVertices
[i
]= pc
.BaseVertices
[i
];
344 pi
.BindEdges
[i
]= pc
.BindEdges
[i
];
347 // retrieve bordervertices.
348 //=========================
349 borderVertices
= BorderVertices
;
351 // retrieve PointLights.
352 //=========================
353 zoneInfo
.PointLights
= _PointLightArray
.getPointLights();
358 // ***************************************************************************
359 void CZone::build(const CZone
&zone
)
364 BorderVertices
= zone
.BorderVertices
;
366 // Compute the bbox and the bias/scale.
367 //=====================================
369 PatchScale
= zone
.PatchScale
;
370 PatchBias
= zone
.PatchBias
;
373 // Compute/compress Patchs.
374 //=========================
376 PatchConnects
= zone
.PatchConnects
;
378 // Init the Clip Arrays
379 _PatchRenderClipped
.resize((uint
)Patchs
.size());
380 _PatchOldRenderClipped
.resize((uint
)Patchs
.size());
381 _PatchRenderClipped
.setAll();
382 _PatchOldRenderClipped
.setAll();
386 //=========================
387 _PointLightArray
= zone
._PointLightArray
;
390 NumVertices
= zone
.NumVertices
;
395 // ***************************************************************************
396 void CBorderVertex::serial(NLMISC::IStream
&f
)
398 /* ***********************************************
399 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
400 * It can be loaded/called through CAsyncFileManager for instance
401 * ***********************************************/
402 (void)f
.serialVersion(0);
404 f
.xmlSerial (CurrentVertex
, "CURRENT_VERTEX");
405 f
.xmlSerial (NeighborZoneId
, "NEIGHTBOR_ZONE_ID");
406 f
.xmlSerial (NeighborVertex
, "NEIGHTBOR_VERTEX");
408 void CZone::CPatchConnect::serial(NLMISC::IStream
&f
)
410 /* ***********************************************
411 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
412 * It can be loaded/called through CAsyncFileManager for instance
413 * ***********************************************/
414 uint ver
= f
.serialVersion(1);
417 f
.serial(OldOrderS
, OldOrderT
, ErrorSize
);
420 f
.xmlSerial (BaseVertices
[0], BaseVertices
[1], BaseVertices
[2], BaseVertices
[3], "BASE_VERTICES");
421 f
.xmlSerial (BindEdges
[0], BindEdges
[1], BindEdges
[2], BindEdges
[3], "BIND_EDGES");
423 void CPatchInfo::CBindInfo::serial(NLMISC::IStream
&f
)
425 /* ***********************************************
426 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
427 * It can be loaded/called through CAsyncFileManager for instance
428 * ***********************************************/
429 (void)f
.serialVersion(0);
430 f
.xmlSerial(NPatchs
, "NPATCH");
431 nlassert ( (NPatchs
==0) | (NPatchs
==1) | (NPatchs
==2) | (NPatchs
==4) | (NPatchs
==5) );
432 f
.xmlSerial (ZoneId
, "ZONE_ID");
433 f
.xmlSerial (Next
[0], Next
[1], Next
[2], Next
[3], "NEXT_PATCH");
434 f
.xmlSerial (Edge
[0], Edge
[1], Edge
[2], Edge
[3], "NEXT_EDGE");
437 // ***************************************************************************
438 void CZone::serial(NLMISC::IStream
&f
)
440 /* ***********************************************
441 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
442 * It can be loaded/called through CAsyncFileManager for instance
443 * ***********************************************/
448 - Lumels compression version 2.
456 uint ver
= f
.serialVersion(4);
458 // No more compatibility before version 3
461 throw EOlderStream(f
);
464 f
.serialCheck(NELID("ENOZ"));
466 f
.xmlSerial (ZoneId
, "ZONE_ID");
467 f
.xmlSerial (ZoneBB
, "BB");
468 f
.xmlSerial (PatchBias
, "PATCH_BIAS");
469 f
.xmlSerial (PatchScale
, "PATCH_SCALE");
470 f
.xmlSerial (NumVertices
, "NUM_VERTICES");
472 f
.xmlPush ("BORDER_VERTICES");
473 f
.serialCont(BorderVertices
);
476 f
.xmlPush ("PATCHES");
477 f
.serialCont(Patchs
);
480 f
.xmlPush ("PATCH_CONNECTS");
481 f
.serialCont(PatchConnects
);
486 f
.xmlPush ("POINT_LIGHTS");
487 f
.serial(_PointLightArray
);
491 // If read, must create and init Patch Clipped state to true (clipped even if not compiled)
494 _PatchRenderClipped
.resize((uint
)Patchs
.size());
495 _PatchOldRenderClipped
.resize((uint
)Patchs
.size());
496 _PatchRenderClipped
.setAll();
497 _PatchOldRenderClipped
.setAll();
500 // If read and version 0, must init default TileColors of patchs.
501 //===============================================================
502 // if(f.isReading() && ver<2) ...
503 // Deprecated, because ver<3 not supported
508 // ***************************************************************************
509 void CZone::compile(CLandscape
*landscape
, TZoneMap
&loadedZones
)
512 TZoneMap neighborZones
;
514 //nlinfo("Compile Zone: %d \n", (sint32)getZoneId());
516 // Can't compile if compiled.
518 Landscape
= landscape
;
520 // Attach this to loadedZones.
521 //============================
522 nlassert(loadedZones
.find(ZoneId
)==loadedZones
.end());
523 loadedZones
[ZoneId
]= this;
525 // Create/link the base vertices according to present neigbor zones.
526 //============================
527 BaseVertices
.clear();
528 BaseVertices
.resize(NumVertices
);
529 // First try to link vertices to other.
530 for(i
=0;i
<(sint
)BorderVertices
.size();i
++)
532 sint cur
= BorderVertices
[i
].CurrentVertex
;
533 sint vertto
= BorderVertices
[i
].NeighborVertex
;
534 sint zoneto
= BorderVertices
[i
].NeighborZoneId
;
535 nlassert(cur
<NumVertices
);
537 if(loadedZones
.find(zoneto
)!=loadedZones
.end())
540 zone
= (*loadedZones
.find(zoneto
)).second
;
541 nlassert(zone
!=this);
542 // insert the zone in the neigborood (if not done...).
543 neighborZones
[zoneto
]= zone
;
544 // Doesn't matter if BaseVertices is already linked to another zone...
545 // This should be the same pointer in this case...
546 BaseVertices
[cur
]= zone
->getBaseVertex(vertto
);
549 // Else, create unbounded vertices.
550 for(i
=0;i
<(sint
)BaseVertices
.size();i
++)
552 if(BaseVertices
[i
]==NULL
)
554 BaseVertices
[i
]= new CTessBaseVertex
;
559 // compile() the patchs.
560 //======================
561 for(j
=0;j
<(sint
)Patchs
.size();j
++)
563 CPatch
&pa
= Patchs
[j
];
564 CPatchConnect
&pc
= PatchConnects
[j
];
565 CTessVertex
*baseVertices
[4];
567 baseVertices
[0]= &(BaseVertices
[pc
.BaseVertices
[0]]->Vert
);
568 baseVertices
[1]= &(BaseVertices
[pc
.BaseVertices
[1]]->Vert
);
569 baseVertices
[2]= &(BaseVertices
[pc
.BaseVertices
[2]]->Vert
);
570 baseVertices
[3]= &(BaseVertices
[pc
.BaseVertices
[3]]->Vert
);
571 pa
.compile(this, j
, pa
.OrderS
, pa
.OrderT
, baseVertices
, pc
.ErrorSize
);
574 // compile() the Clip information for the patchs.
575 //======================
576 _PatchBSpheres
.resize(Patchs
.size());
577 for(j
=0;j
<(sint
)Patchs
.size();j
++)
579 CPatch
&pa
= Patchs
[j
];
581 // Buil the BSPhere of the patch.
582 CAABBox bb
= pa
.buildBBox();
583 _PatchBSpheres
[j
].Center
= bb
.getCenter();
584 _PatchBSpheres
[j
].Radius
= bb
.getRadius();
587 // bind() the patchs. (after all compiled).
588 //===================
589 for(j
=0;j
<(sint
)Patchs
.size();j
++)
591 CPatch
&pa
= Patchs
[j
];
592 CPatchConnect
&pc
= PatchConnects
[j
];
594 // bind the patch. This is the original bind, not a rebind.
595 bindPatch(loadedZones
, pa
, pc
, false);
599 // rebindBorder() on neighbor zones.
600 //==================================
602 // Traverse the neighborood.
603 for(zoneIt
= neighborZones
.begin(); zoneIt
!=neighborZones
.end(); zoneIt
++)
605 (*zoneIt
).second
->rebindBorder(loadedZones
);
612 // ***************************************************************************
613 void CZone::release(TZoneMap
&loadedZones
)
620 // detach this zone to loadedZones.
621 //=================================
622 nlassert(loadedZones
.find(ZoneId
)!=loadedZones
.end());
623 loadedZones
.erase(ZoneId
);
624 // It doesn't server to unbindPatch(), since patch is not binded to neigbors.
627 // unbind() the patchs.
628 //=====================
629 for(j
=0;j
<(sint
)Patchs
.size();j
++)
631 CPatch
&pa
= Patchs
[j
];
636 // rebindBorder() on neighbor zones.
637 //==================================
638 // Build the nieghborood.
639 TZoneMap neighborZones
;
640 for(i
=0;i
<(sint
)BorderVertices
.size();i
++)
642 sint cur
= BorderVertices
[i
].CurrentVertex
;
643 sint zoneto
= BorderVertices
[i
].NeighborZoneId
;
644 nlassert(cur
<NumVertices
);
646 if(loadedZones
.find(zoneto
)!=loadedZones
.end())
649 zone
= (*loadedZones
.find(zoneto
)).second
;
650 nlassert(zone
!=this);
651 // insert the zone in the neigborood (if not done...).
652 neighborZones
[zoneto
]= zone
;
657 // Traverse the neighborood.
658 for(zoneIt
= neighborZones
.begin(); zoneIt
!=neighborZones
.end(); zoneIt
++)
661 (*zoneIt
).second
->rebindBorder(loadedZones
);
665 // release() the patchs.
666 //======================
667 // unbind() need compiled neigbor patchs, so do the release after all unbind (so after rebindBorder() too...).
668 for(j
=0;j
<(sint
)Patchs
.size();j
++)
670 CPatch
&pa
= Patchs
[j
];
675 // destroy/unlink the base vertices (internal..), according to present neigbor zones.
676 //=================================
677 // Just release the smartptrs (easy!!). Do it after patchs released...
678 BaseVertices
.clear();
688 // ***************************************************************************
689 // ***************************************************************************
691 // ***************************************************************************
692 // ***************************************************************************
695 // ***************************************************************************
696 void CZone::rebindBorder(TZoneMap
&loadedZones
)
700 // rebind patchs which are on border.
701 for(j
=0;j
<(sint
)Patchs
.size();j
++)
703 CPatch
&pa
= Patchs
[j
];
704 CPatchConnect
&pc
= PatchConnects
[j
];
706 if(patchOnBorder(pc
))
708 // rebind the patch. This is a rebind.
709 bindPatch(loadedZones
, pa
, pc
, true);
714 // ***************************************************************************
715 CPatch
*CZone::getZonePatch(TZoneMap
&loadedZones
, sint zoneId
, sint patch
)
717 #ifdef NL3D_DEBUG_DONT_BIND_PATCH
720 if(loadedZones
.find(zoneId
)==loadedZones
.end())
723 return (loadedZones
[zoneId
])->getPatch(patch
);
727 // ***************************************************************************
728 void CZone::buildBindInfo(uint patchId
, uint edge
, CZone
*neighborZone
, CPatch::CBindInfo
&paBind
)
730 nlassert(patchId
< Patchs
.size());
731 nlassert(neighborZone
);
733 CPatchConnect
&pc
= PatchConnects
[patchId
];
736 // Get the bind info of this patch to his neighbor on "edge".
737 CPatchInfo::CBindInfo
&pcBind
= pc
.BindEdges
[edge
];
738 nlassert(pcBind
.NPatchs
==0 || pcBind
.NPatchs
==1 || pcBind
.NPatchs
==2 || pcBind
.NPatchs
==4 || pcBind
.NPatchs
==5);
742 paBind
.Zone
= neighborZone
;
745 // Special case of a small patch connected to a bigger.
746 if(pcBind
.NPatchs
==5)
749 paBind
.Next
[0]= neighborZone
->getPatch(pcBind
.Next
[0]);
750 paBind
.Edge
[0]= pcBind
.Edge
[0];
752 // Get the twin bindInfo of pcBind.
753 const CPatchInfo::CBindInfo
&pcBindNeighbor
=
754 neighborZone
->getPatchConnect(pcBind
.Next
[0])->BindEdges
[pcBind
.Edge
[0]];
755 // must have a multiple bind.
756 nlassert(pcBindNeighbor
.NPatchs
== 2 || pcBindNeighbor
.NPatchs
== 4);
758 // number of bind is stored on the twin bindInfo.
759 paBind
.MultipleBindNum
= pcBindNeighbor
.NPatchs
;
761 // Search our patchId on neighbor;
762 paBind
.MultipleBindId
= 255;
763 for(sint i
=0; i
<paBind
.MultipleBindNum
; i
++)
765 if(pcBindNeighbor
.Next
[i
]==patchId
)
766 paBind
.MultipleBindId
= i
;
768 nlassert(paBind
.MultipleBindId
!= 255);
772 paBind
.MultipleBindNum
= 0;
773 paBind
.NPatchs
= pcBind
.NPatchs
;
774 for(sint i
=0;i
<paBind
.NPatchs
; i
++)
776 paBind
.Next
[i
]= neighborZone
->getPatch(pcBind
.Next
[i
]);
777 paBind
.Edge
[i
]= pcBind
.Edge
[i
];
785 // ***************************************************************************
786 void CZone::bindPatch(TZoneMap
&loadedZones
, CPatch
&pa
, CPatchConnect
&pc
, bool rebind
)
788 CPatch::CBindInfo edges
[4];
791 for(sint i
=0;i
<4;i
++)
793 CPatchInfo::CBindInfo
&pcBind
= pc
.BindEdges
[i
];
794 CPatch::CBindInfo
&paBind
= edges
[i
];
796 nlassert(pcBind
.NPatchs
==0 || pcBind
.NPatchs
==1 || pcBind
.NPatchs
==2 || pcBind
.NPatchs
==4 || pcBind
.NPatchs
==5);
797 paBind
.NPatchs
= pcBind
.NPatchs
;
801 TZoneMap::iterator itZoneMap
;
802 // If no neighbor, or if zone neighbor not loaded.
803 if( paBind
.NPatchs
==0 || (itZoneMap
=loadedZones
.find(pcBind
.ZoneId
)) == loadedZones
.end() )
806 paBind
.Zone
= itZoneMap
->second
;
809 // Special case of a small patch connected to a bigger.
810 if(paBind
.NPatchs
==5)
812 paBind
.Edge
[0]= pcBind
.Edge
[0];
813 paBind
.Next
[0]= CZone::getZonePatch(loadedZones
, pcBind
.ZoneId
, pcBind
.Next
[0]);
814 // If not loaded, don't bind to this edge.
819 // pa.bind() will do the job.
820 // Leave it flagged with NPatchs==5.
826 // Bind 1/1 and 1/2,1/4
827 if(paBind
.NPatchs
>=1)
829 paBind
.Edge
[0]= pcBind
.Edge
[0];
830 paBind
.Next
[0]= CZone::getZonePatch(loadedZones
, pcBind
.ZoneId
, pcBind
.Next
[0]);
831 // If not loaded, don't bind to this edge.
835 if(paBind
.NPatchs
>=2)
837 paBind
.Edge
[1]= pcBind
.Edge
[1];
838 paBind
.Next
[1]= CZone::getZonePatch(loadedZones
, pcBind
.ZoneId
, pcBind
.Next
[1]);
839 // If not loaded, don't bind to this edge.
843 if(paBind
.NPatchs
>=4)
845 paBind
.Edge
[2]= pcBind
.Edge
[2];
846 paBind
.Edge
[3]= pcBind
.Edge
[3];
847 paBind
.Next
[2]= CZone::getZonePatch(loadedZones
, pcBind
.ZoneId
, pcBind
.Next
[2]);
848 paBind
.Next
[3]= CZone::getZonePatch(loadedZones
, pcBind
.ZoneId
, pcBind
.Next
[3]);
849 // If not loaded, don't bind to this edge.
850 if(!paBind
.Next
[2] || !paBind
.Next
[3])
859 pa
.bind(edges
, rebind
);
863 // ***************************************************************************
864 void CZone::unbindPatch(CPatch
&pa
)
867 Remind: the old version with CPatch::unbindFrom*() doesn't work because of CZone::release(). This function
868 first erase the zone from loadedZones...
869 Not matter here. We use CPatch::unbind() which should do all the good job correctly (unbind pa from ohters
870 , and unbind others from pa at same time).
877 // ***************************************************************************
878 bool CZone::patchOnBorder(const CPatchConnect
&pc
) const
880 // If only one of neighbor patch is not of this zone, we are on a border.
883 for(sint i
=0;i
<4;i
++)
885 const CPatchInfo::CBindInfo
&pcBind
= pc
.BindEdges
[i
];
887 nlassert(pcBind
.NPatchs
==0 || pcBind
.NPatchs
==1 || pcBind
.NPatchs
==2 || pcBind
.NPatchs
==4 || pcBind
.NPatchs
==5);
888 if(pcBind
.NPatchs
>=1)
890 if(pcBind
.ZoneId
!= ZoneId
)
899 // ***************************************************************************
900 // ***************************************************************************
902 // ***************************************************************************
903 // ***************************************************************************
906 // ***************************************************************************
907 const CBSphere
&CZone::getPatchBSphere(uint patch
) const
909 static CBSphere dummySphere
;
910 if(patch
<_PatchBSpheres
.size())
911 return _PatchBSpheres
[patch
];
917 // ***************************************************************************
918 void CZone::clip(const std::vector
<CPlane
> &pyramid
)
920 H_AUTO( NLMISC_ClipZone
);
924 // bkup old ClipResult. NB: by default, it is ClipOut (no VB created).
925 sint oldClipResult
= ClipResult
;
927 // Pyramid with only the planes that clip the zone
928 static std::vector
<CPlane
> patchPyramid(10);
929 static std::vector
<uint
> patchPyramidIndex(10);
930 patchPyramidIndex
.clear();
932 // Compute ClipResult.
933 //-------------------
935 for(sint i
=0;i
<(sint
)pyramid
.size();i
++)
938 if(!ZoneBB
.clipBack(pyramid
[i
]))
941 // If out of only one plane, out of all.
944 // If partially IN (ie not entirely out, and not entirely IN)
945 else if(ZoneBB
.clipFront(pyramid
[i
]))
947 // Force ClipResult to be ClipSide, and not ClipIn.
949 // Append the plane index to list to test
950 patchPyramidIndex
.push_back(i
);
959 // don't need to go below...
964 // Clip By Patch Pass.
965 //--------------------
966 if(ClipResult
==ClipOut
)
968 H_AUTO( NLMISC_ClipZone_Out
);
970 // Set All RenderClip flags to true.
971 _PatchRenderClipped
.setAll();
973 else if(ClipResult
==ClipIn
)
975 H_AUTO( NLMISC_ClipZone_In
);
977 // Set All RenderClip flags to false.
978 _PatchRenderClipped
.clearAll();
982 H_AUTO( NLMISC_ClipZone_Side
);
984 // Copy only the pyramid planes of interest
985 patchPyramid
.resize(patchPyramidIndex
.size());
987 for(i
=0;i
<patchPyramidIndex
.size();i
++)
989 patchPyramid
[i
]= pyramid
[patchPyramidIndex
[i
]];
992 // clip all patchs with the simplified pyramid
993 clipPatchs(patchPyramid
);
997 // delete / reallocate / fill VBuffers.
998 //-------------------
999 // If there is a change in the Clip of the zone, or if patchs may have change (ie ClipSide is undetermined).
1000 if(oldClipResult
!=ClipResult
|| oldClipResult
==ClipSide
)
1002 // get BitSet as Raw Array of uint32
1003 uint32
*oldRenderClip
= const_cast<uint32
*>(&_PatchOldRenderClipped
.getVector()[0]);
1004 const uint32
*newRenderClip
= &_PatchRenderClipped
.getVector()[0];
1005 uint numPatchs
= (uint
)Patchs
.size();
1006 // Then, we must test by patch.
1007 for(uint i
=0;i
<numPatchs
;oldRenderClip
++, newRenderClip
++)
1009 uint32 oldWord
= *oldRenderClip
;
1010 uint32 newWord
= *newRenderClip
;
1011 // process at max 32 patch
1012 uint maxNumBits
= min((numPatchs
-i
), 32U);
1014 for(;maxNumBits
>0;maxNumBits
--, mask
<<=1, i
++)
1016 // same as: if(_PatchOldRenderClipped[i] != _PatchRenderClipped[i])
1017 if( (oldWord
^newWord
)&mask
)
1020 *oldRenderClip
&= ~mask
;
1021 *oldRenderClip
|= newWord
&mask
;
1022 // update clip patch
1023 Patchs
[i
].updateClipPatchVB( (newWord
&mask
)!=0 );
1033 // ***************************************************************************
1034 void CZone::clipPatchs(const std::vector
<CPlane
> &pyramid
)
1036 // Init all to Not clipped
1037 _PatchRenderClipped
.clearAll();
1039 for(uint j
=0;j
<_PatchBSpheres
.size();j
++)
1041 CBSphere
&bSphere
= _PatchBSpheres
[j
];
1042 for(sint i
=0;i
<(sint
)pyramid
.size();i
++)
1045 if(!bSphere
.clipBack(pyramid
[i
]))
1047 _PatchRenderClipped
.set(j
, true);
1055 // ***************************************************************************
1057 // Code for Debug test Only.. Do not erase it, may be used later :)
1059 static void cleanTess(CTessFace *face)
1063 cleanTess(face->SonLeft);
1064 cleanTess(face->SonRight);
1066 // If has father, clean it.
1069 CTessFace *face1=face->Father;
1070 CTessFace *face2=face->Father->FBase;
1071 face1->FLeft= face1->SonLeft->FBase;
1072 face1->FRight= face1->SonRight->FBase;
1075 face2->FLeft= face2->SonLeft->FBase;
1076 face2->FRight= face2->SonRight->FBase;
1080 static void testTess(CTessFace *face)
1084 testTess(face->SonLeft);
1085 testTess(face->SonRight);
1088 nlassert(!face->FBase || face->FBase->Patch!=(CPatch*)0xdddddddd);
1089 nlassert(!face->FLeft || face->FLeft->Patch!=(CPatch*)0xdddddddd);
1090 nlassert(!face->FRight || face->FRight->Patch!=(CPatch*)0xdddddddd);
1092 static void checkTess()
1094 // This test should be inserted at begin of CZone::refine().
1095 // And it needs hacking public/private.
1098 pPatch= &(*Patchs.begin());
1099 for(n=(sint)Patchs.size();n>0;n--, pPatch++)
1101 cleanTess(pPatch->Son0);
1102 cleanTess(pPatch->Son1);
1104 pPatch= &(*Patchs.begin());
1105 for(n=(sint)Patchs.size();n>0;n--, pPatch++)
1107 testTess(pPatch->Son0);
1108 testTess(pPatch->Son1);
1114 // ***************************************************************************
1115 void CZone::excludePatchFromRefineAll(uint patch
, bool exclude
)
1118 nlassert(patch
<Patchs
.size());
1120 if(patch
>=Patchs
.size())
1123 Patchs
[patch
].ExcludeFromRefineAll
= exclude
;
1127 // ***************************************************************************
1128 void CZone::refineAll()
1135 // DO NOT do a forceNoRenderClip(), to avoid big allocation of Near/Far VB vertices in driver.
1136 // DO NOT modify ClipResult, to avoid big allocation of Near/Far VB vertices in driver.
1138 // refine ALL patchs (even those which may be invisible).
1139 CPatch
*pPatch
= &(*Patchs
.begin());
1141 for(n
=(sint
)Patchs
.size();n
>0;n
--, pPatch
++)
1143 // For Pacs construction: may exclude some patch from refineAll (for speed improvement).
1144 if(!pPatch
->ExcludeFromRefineAll
)
1145 pPatch
->refineAll();
1151 // ***************************************************************************
1152 void CZone::averageTesselationVertices()
1159 // averageTesselationVertices of ALL patchs.
1160 CPatch
*pPatch
= &(*Patchs
.begin());
1161 for(sint n
=(sint
)Patchs
.size();n
>0;n
--, pPatch
++)
1163 pPatch
->averageTesselationVertices();
1168 // ***************************************************************************
1169 void CZone::preRender()
1174 static const uint updateFarRefineFreq
= 15;
1175 // Take the renderDate here.
1176 uint curDateMod
= CLandscapeGlobals::CurrentRenderDate
& updateFarRefineFreq
;
1178 // If no patchs, do nothing.
1182 /* If patchs invisible, must still update their Far Textures,
1183 else, there may be slowdown when we turn the head.
1187 // If all the zone is invisible.
1188 if(ClipResult
==ClipOut
)
1190 // No patchs are visible, but maybe update the far textures.
1191 if( curDateMod
==(ZoneId
& updateFarRefineFreq
) )
1193 // updateTextureFarOnly for all patchs.
1194 for(uint i
=0;i
<Patchs
.size();i
++)
1196 Patchs
[i
].updateTextureFarOnly(_PatchBSpheres
[i
]);
1200 // else If some patchs only are visible.
1201 else if(ClipResult
==ClipSide
)
1203 // PreRender Pass, or updateTextureFarOnly(), according to _PatchRenderClipped state.
1204 for(uint i
=0;i
<Patchs
.size();i
++)
1206 // If the patch is visible
1207 if(!_PatchRenderClipped
[i
])
1209 // Then preRender it.
1210 Patchs
[i
].preRender(_PatchBSpheres
[i
]);
1214 // else maybe updateFar it.
1215 // ZoneId+i for better repartition.
1216 if( curDateMod
==((ZoneId
+i
) & updateFarRefineFreq
) )
1217 Patchs
[i
].updateTextureFarOnly(_PatchBSpheres
[i
]);
1221 else // ClipResult==ClipIn
1223 // PreRender Pass for All
1224 for(uint i
=0;i
<Patchs
.size();i
++)
1226 Patchs
[i
].preRender(_PatchBSpheres
[i
]);
1233 // ***************************************************************************
1234 void CZone::resetRenderFarAndDeleteVBFV()
1236 for(uint i
=0;i
<Patchs
.size();i
++)
1238 // If patch is visible
1239 if(!_PatchRenderClipped
[i
])
1241 // release VertexBuffer, and FaceBuffer
1242 Patchs
[i
].deleteVBAndFaceVector();
1244 _PatchRenderClipped
.set(i
, true);
1247 Patchs
[i
].resetRenderFar();
1252 // ***************************************************************************
1253 void CZone::forceMergeAtTileLevel()
1257 if (!Patchs
.empty())
1258 pPatch
= &(*Patchs
.begin());
1260 for(sint n
=(sint
)Patchs
.size();n
>0;n
--, pPatch
++)
1262 pPatch
->forceMergeAtTileLevel();
1267 // ***************************************************************************
1268 // ***************************************************************************
1270 // ***************************************************************************
1271 // ***************************************************************************
1274 // ***************************************************************************
1275 void CZone::changePatchTextureAndColor (sint numPatch
, const std::vector
<CTileElement
> *tiles
, const std::vector
<CTileColor
> *colors
)
1277 nlassert(numPatch
>=0);
1278 nlassert(numPatch
<getNumPatchs());
1281 // Update the patch texture.
1284 nlassert( Patchs
[numPatch
].Tiles
.size() == tiles
->size() );
1285 Patchs
[numPatch
].Tiles
= *tiles
;
1288 // Update the patch colors.
1291 nlassert( Patchs
[numPatch
].TileColors
.size() == colors
->size() );
1292 Patchs
[numPatch
].TileColors
= *colors
;
1297 // If the patch is visible, then we must LockBuffers, because new VertexVB may be created.
1298 if(!_PatchRenderClipped
[numPatch
])
1299 Landscape
->updateGlobalsAndLockBuffers(CVector::Null
);
1301 // Recompute UVs for new setup of Tiles.
1302 Patchs
[numPatch
].deleteTileUvs();
1303 Patchs
[numPatch
].recreateTileUvs();
1305 // unlockBuffers() if necessary.
1306 if(!_PatchRenderClipped
[numPatch
])
1308 Landscape
->unlockBuffers();
1309 // This patch is visible, and TileFaces have been deleted / added.
1310 // So must update TessBlock.
1311 Landscape
->updateTessBlocksFaceVector();
1317 // ***************************************************************************
1318 void CZone::refreshTesselationGeometry(sint numPatch
)
1320 nlassert(numPatch
>=0);
1321 nlassert(numPatch
<getNumPatchs());
1324 // At next render, we must re-fill the entire unclipped VB, so change are taken into account.
1325 Landscape
->_RenderMustRefillVB
= true;
1327 Patchs
[numPatch
].refreshTesselationGeometry();
1331 // ***************************************************************************
1332 const std::vector
<CTileElement
> &CZone::getPatchTexture(sint numPatch
) const
1334 nlassert(numPatch
>=0);
1335 nlassert(numPatch
<getNumPatchs());
1337 // Update the patch texture.
1338 return Patchs
[numPatch
].Tiles
;
1342 // ***************************************************************************
1343 const std::vector
<CTileColor
> &CZone::getPatchColor(sint numPatch
) const
1345 nlassert(numPatch
>=0);
1346 nlassert(numPatch
<getNumPatchs());
1348 // Update the patch texture.
1349 return Patchs
[numPatch
].TileColors
;
1352 // ***************************************************************************
1353 void CZone::setTileColor(bool monochrome
, float factor
)
1355 nlassert(factor
>= 0.0f
); // factor must not be negative as its a multiplier
1359 for (uint32 i
= 0; i
< Patchs
.size(); ++i
)
1361 vector
<CTileColor
> &rTC
= Patchs
[i
].TileColors
;
1362 for (uint32 j
= 0; j
< rTC
.size(); ++j
)
1364 float fR
= (rTC
[j
].Color565
& 31) / 32.0f
;
1365 float fG
= ((rTC
[j
].Color565
>> 5) & 63) / 64.0f
;
1366 float fB
= ((rTC
[j
].Color565
>> 11) & 31) / 32.0f
;
1368 fR
= 0.28f
* fR
+ 0.59f
* fG
+ 0.13f
* fB
;
1370 nlassert(fR
< 0.99f
);
1373 if (fR
> 0.99f
) fR
= 0.99f
; // Avoid reaching 1
1375 uint16 nR
= (uint16
)(fR
* 32.0f
);
1376 uint16 nG
= (uint16
)(fR
* 64.0f
);
1377 uint16 nB
= (uint16
)(fR
* 32.0f
);
1379 rTC
[j
].Color565
= nR
+ (nG
<< 5) + (nB
<< 11);
1387 for (uint32 i
= 0; i
< Patchs
.size(); ++i
)
1389 vector
<CTileColor
> &rTC
= Patchs
[i
].TileColors
;
1390 for (uint32 j
= 0; j
< rTC
.size(); ++j
)
1392 float fR
= (rTC
[j
].Color565
& 31) / 32.0f
;
1393 float fG
= ((rTC
[j
].Color565
>> 5) & 63) / 64.0f
;
1394 float fB
= ((rTC
[j
].Color565
>> 11) & 31) / 32.0f
;
1400 if (fR
> 0.99f
) fR
= 0.99f
;
1401 if (fG
> 0.99f
) fG
= 0.99f
;
1402 if (fB
> 0.99f
) fB
= 0.99f
;
1404 uint16 nR
= (uint16
)(fR
* 32.0f
);
1405 uint16 nG
= (uint16
)(fG
* 64.0f
);
1406 uint16 nB
= (uint16
)(fB
* 32.0f
);
1408 rTC
[j
].Color565
= nR
+ (nG
<< 5) + (nB
<< 11);
1415 // ***************************************************************************
1416 void CZone::debugBinds(FILE *f
)
1418 fprintf(f
, "*****************************\n");
1419 fprintf(f
, "ZoneId: %d. NPatchs:%u\n", ZoneId
, (uint
)PatchConnects
.size());
1421 for(i
=0;i
<(sint
)PatchConnects
.size();i
++)
1423 CPatchConnect
&pc
= PatchConnects
[i
];
1424 fprintf(f
, "patch%d:\n", i
);
1425 for(sint j
=0;j
<4;j
++)
1427 CPatchInfo::CBindInfo
&bd
= pc
.BindEdges
[j
];
1428 fprintf(f
, " edge%d: Zone:%u. NPatchs:%u. ", j
, (uint
)bd
.ZoneId
, (uint
)bd
.NPatchs
);
1429 for(sint k
=0;k
<bd
.NPatchs
;k
++)
1431 fprintf(f
, "p%ue%u - ", (uint
)bd
.Next
[k
], (uint
)bd
.Edge
[k
]);
1437 fprintf(f
,"Vertices :\n");
1438 for(i
=0;i
<(sint
)BorderVertices
.size();i
++)
1440 fprintf(f
,"current : %u -> (zone %u) vertex %u\n", (uint
)BorderVertices
[i
].CurrentVertex
,
1441 (uint
)BorderVertices
[i
].NeighborZoneId
,
1442 (uint
)BorderVertices
[i
].NeighborVertex
);
1447 // ***************************************************************************
1448 void CZone::applyHeightField(const CLandscape
&landScape
)
1451 vector
<CBezierPatch
> patchs
;
1453 // no patch, do nothing.
1457 // 0. Unpack patchs to Bezier Patchs.
1458 //===================================
1459 patchs
.resize(Patchs
.size());
1460 for(j
=0;j
<(sint
)patchs
.size();j
++)
1462 CBezierPatch
&p
= patchs
[j
];
1463 CPatch
&pa
= Patchs
[j
];
1465 // re-Build the uncompressed bezier patch.
1467 pa
.Vertices
[i
].unpack(p
.Vertices
[i
], PatchBias
, PatchScale
);
1469 pa
.Tangents
[i
].unpack(p
.Tangents
[i
], PatchBias
, PatchScale
);
1471 pa
.Interiors
[i
].unpack(p
.Interiors
[i
], PatchBias
, PatchScale
);
1474 // 1. apply heightfield on bezier patchs.
1475 //===================================
1476 for(j
=0;j
<(sint
)patchs
.size();j
++)
1478 CBezierPatch
&p
= patchs
[j
];
1482 p
.Vertices
[i
]+= landScape
.getHeightFieldDeltaZ(p
.Vertices
[i
].x
, p
.Vertices
[i
].y
);
1484 p
.Tangents
[i
]+= landScape
.getHeightFieldDeltaZ(p
.Tangents
[i
].x
, p
.Tangents
[i
].y
);
1486 p
.Interiors
[i
]+= landScape
.getHeightFieldDeltaZ(p
.Interiors
[i
].x
, p
.Interiors
[i
].y
);
1490 // 2. Re-compute Patch Scale/Bias, and Zone BBox.
1491 //===================================
1493 bb
.setCenter(patchs
[0].Vertices
[0]);
1494 bb
.setHalfSize(CVector::Null
);
1495 for(j
=0;j
<(sint
)patchs
.size();j
++)
1498 const CBezierPatch
&p
= patchs
[j
];
1500 bb
.extend(p
.Vertices
[i
]);
1502 bb
.extend(p
.Tangents
[i
]);
1504 bb
.extend(p
.Interiors
[i
]);
1506 // Compute BBox, and Patch Scale Bias, according to Noise.
1507 computeBBScaleBias(bb
);
1510 // 3. Re-pack patchs.
1511 //===================================
1512 for(j
=0;j
<(sint
)patchs
.size();j
++)
1514 CBezierPatch
&p
= patchs
[j
];
1515 CPatch
&pa
= Patchs
[j
];
1517 // Build the packed patch.
1519 pa
.Vertices
[i
].pack(p
.Vertices
[i
], PatchBias
, PatchScale
);
1521 pa
.Tangents
[i
].pack(p
.Tangents
[i
], PatchBias
, PatchScale
);
1523 pa
.Interiors
[i
].pack(p
.Interiors
[i
], PatchBias
, PatchScale
);
1527 // ***************************************************************************
1528 void CZone::setupColorsFromTileFlags(const NLMISC::CRGBA colors
[4])
1530 for (uint k
= 0; k
< Patchs
.size(); ++k
)
1532 Patchs
[k
].setupColorsFromTileFlags(colors
);
1537 // ***************************************************************************
1538 void CZone::copyTilesFlags(sint destPatchId
, const CPatch
*srcPatch
)
1540 CPatch
*destPatch
= getPatch(destPatchId
);
1542 destPatch
->copyTileFlagsFromPatch(srcPatch
);
1546 // ***************************************************************************
1547 bool CPatchInfo::getNeighborTile (uint patchId
, uint edge
, sint position
, uint
&patchOut
, sint
&sOut
, sint
&tOut
,
1548 const vector
<CPatchInfo
> &patchInfos
) const
1553 uint length
= (edge
&1) ? OrderS
: OrderT
;
1554 nlassert ((uint
)position
<length
);
1556 // What kind of case ?
1557 switch (BindEdges
[edge
].NPatchs
)
1563 // Get neighbor index and position in neighbor
1564 uint neighborLength
= (length
/ BindEdges
[edge
].NPatchs
);
1565 uint neighbor
= position
/ neighborLength
;
1566 uint neighborPosition
= neighborLength
- (position
% neighborLength
) - 1;
1567 uint neighborEdge
= BindEdges
[edge
].Edge
[neighbor
];
1570 patchOut
= BindEdges
[edge
].Next
[neighbor
];
1573 uint neighborRealLength
= (neighborEdge
&1) ? patchInfos
[patchOut
].OrderS
: patchInfos
[patchOut
].OrderT
;
1574 if (neighborRealLength
== neighborLength
)
1576 // Get final coordinate
1577 switch (neighborEdge
)
1581 tOut
= neighborPosition
;
1584 sOut
= neighborPosition
;
1585 tOut
= patchInfos
[patchOut
].OrderT
-1;
1588 sOut
= patchInfos
[patchOut
].OrderS
-1;
1589 tOut
= patchInfos
[patchOut
].OrderT
-neighborPosition
-1;
1592 sOut
= patchInfos
[patchOut
].OrderS
-neighborPosition
-1;
1605 // Find in the neighbor where we are
1606 patchOut
= BindEdges
[edge
].Next
[0];
1607 uint neighborEdge
= BindEdges
[edge
].Edge
[0];
1608 uint neighborEdgeCount
= patchInfos
[patchOut
].BindEdges
[neighborEdge
].NPatchs
;
1611 uint neighborRealLength
= (neighborEdge
&1) ? patchInfos
[patchOut
].OrderS
: patchInfos
[patchOut
].OrderT
;
1614 if ((neighborRealLength
/ neighborEdgeCount
) == length
)
1616 // Find us in the neighbor
1617 uint neighborPosition
;
1618 for (neighborPosition
=0; neighborPosition
<neighborEdgeCount
; neighborPosition
++)
1621 if (patchInfos
[patchOut
].BindEdges
[neighborEdge
].Next
[neighborPosition
] == patchId
)
1626 nlassert (neighborPosition
!=neighborEdgeCount
);
1627 neighborPosition
= (neighborPosition
+ 1) * (neighborRealLength
/ neighborEdgeCount
) - position
- 1;
1629 // Get final coordinate
1630 switch (neighborEdge
)
1634 tOut
= neighborPosition
;
1637 sOut
= neighborPosition
;
1638 tOut
= patchInfos
[patchOut
].OrderT
-1;
1641 sOut
= patchInfos
[patchOut
].OrderS
-1;
1642 tOut
= patchInfos
[patchOut
].OrderT
-neighborPosition
-1;
1645 sOut
= patchInfos
[patchOut
].OrderS
-neighborPosition
-1;
1661 // ***************************************************************************
1663 bool CPatchInfo::getTileSymmetryRotate (const CTileBank
&bank
, uint tile
, bool &symmetry
, uint
&rotate
)
1665 // Need check the tile ?
1666 if ( (symmetry
|| (rotate
!= 0)) && (tile
!= 0xffffffff) )
1669 if (tile
< (uint
)bank
.getTileCount())
1674 CTileBank::TTileType type
;
1677 bank
.getTileXRef ((int)tile
, tileSet
, number
, type
);
1679 if ((tileSet
< 0) || (tileSet
>= bank
.getTileSetCount()))
1681 nlwarning("tile %d has an unknown tileSet (%d)",tile
, tileSet
);
1685 // Is it an oriented tile ?
1686 if (bank
.getTileSet (tileSet
)->getOriented())
1688 // New rotation value
1702 // ***************************************************************************
1704 bool CPatchInfo::transformTile (const CTileBank
&bank
, uint
&tile
, uint
&tileRotation
, bool symmetry
, uint rotate
, bool goofy
)
1707 if ( (rotate
!=0) || symmetry
)
1709 if (tile
< (uint
)bank
.getTileCount())
1714 CTileBank::TTileType type
;
1717 bank
.getTileXRef ((int)tile
, tileSet
, number
, type
);
1720 if (type
== CTileBank::transition
)
1722 // Rotation for transition
1723 uint transRotate
= rotate
;
1725 // Number should be ok
1726 nlassert (number
>=0);
1727 nlassert (number
<CTileSet::count
);
1730 const CTileSet
*pTileSet
= bank
.getTileSet (tileSet
);
1733 CTileSet::TFlagBorder oriented
[4] =
1735 pTileSet
->getOrientedBorder (CTileSet::left
, CTileSet::getEdgeType ((CTileSet::TTransition
)number
, CTileSet::left
)),
1736 pTileSet
->getOrientedBorder (CTileSet::bottom
, CTileSet::getEdgeType ((CTileSet::TTransition
)number
, CTileSet::bottom
)),
1737 pTileSet
->getOrientedBorder (CTileSet::right
, CTileSet::getEdgeType ((CTileSet::TTransition
)number
, CTileSet::right
)),
1738 pTileSet
->getOrientedBorder (CTileSet::top
, CTileSet::getEdgeType ((CTileSet::TTransition
)number
, CTileSet::top
))
1744 if ( (tileRotation
& 1) ^ goofy
)
1746 CTileSet::TFlagBorder tmp
= oriented
[1];
1747 oriented
[1] = CTileSet::getInvertBorder (oriented
[3]);
1748 oriented
[3] = CTileSet::getInvertBorder (tmp
);
1749 oriented
[2] = CTileSet::getInvertBorder (oriented
[2]);
1750 oriented
[0] = CTileSet::getInvertBorder (oriented
[0]);
1754 CTileSet::TFlagBorder tmp
= oriented
[0];
1755 oriented
[0] = CTileSet::getInvertBorder (oriented
[2]);
1756 oriented
[2] = CTileSet::getInvertBorder (tmp
);
1757 oriented
[1] = CTileSet::getInvertBorder (oriented
[1]);
1758 oriented
[3] = CTileSet::getInvertBorder (oriented
[3]);
1763 CTileSet::TFlagBorder edges
[4];
1764 edges
[0] = pTileSet
->getOrientedBorder (CTileSet::left
, oriented
[(0 + transRotate
)&3]);
1765 edges
[1] = pTileSet
->getOrientedBorder (CTileSet::bottom
, oriented
[(1 + transRotate
)&3]);
1766 edges
[2] = pTileSet
->getOrientedBorder (CTileSet::right
, oriented
[(2 + transRotate
)&3]);
1767 edges
[3] = pTileSet
->getOrientedBorder (CTileSet::top
, oriented
[(3 + transRotate
)&3]);
1769 // Get the good tile number
1770 CTileSet::TTransition transition
= pTileSet
->getTransitionTile (edges
[3], edges
[1], edges
[0], edges
[2]);
1771 nlassert ((CTileSet::TTransition
)transition
!= CTileSet::notfound
);
1772 tile
= (uint
)(pTileSet
->getTransition (transition
)->getTile ());
1775 // Transform rotation: invert rotation
1776 tileRotation
+= rotate
;
1779 if (goofy
&& symmetry
)
1782 // Mask the rotation
1793 // ***************************************************************************
1795 void CPatchInfo::transform256Case (const CTileBank
&bank
, uint8
&case256
, uint tileRotation
, bool symmetry
, uint rotate
, bool goofy
)
1798 if ( (rotate
!=0) || symmetry
)
1803 // Take the symmetry
1804 uint symArray
[4] = {3, 2, 1, 0};
1805 case256
= symArray
[case256
];
1807 if (goofy
&& ((tileRotation
& 1) ==0))
1809 if ((!goofy
) && (tileRotation
& 1))
1819 // ***************************************************************************
1821 bool CPatchInfo::transform (std::vector
<CPatchInfo
> &patchInfo
, NL3D::CZoneSymmetrisation
&zoneSymmetry
, const NL3D::CTileBank
&bank
, bool symmetry
, uint rotate
, float snapCell
, float weldThreshold
, const NLMISC::CMatrix
&toOriginalSpace
)
1823 uint patchCount
= (uint
)patchInfo
.size ();
1826 // --- Export tile info Symmetry of the bind info.
1827 // --- Parse each patch and each edge
1830 NL3D::CZoneSymmetrisation::CError error
;
1832 // Build the structure
1833 if (!zoneSymmetry
.build (patchInfo
, snapCell
, weldThreshold
, bank
, error
, toOriginalSpace
))
1841 for(i
=0 ; i
<patchCount
; i
++)
1843 // Ref on the current patch
1844 CPatchInfo
&pi
= patchInfo
[i
];
1846 // --- Symmetry vertex indexes
1849 CVector tmp
= pi
.Patch
.Vertices
[0];
1850 pi
.Patch
.Vertices
[0] = pi
.Patch
.Vertices
[3];
1851 pi
.Patch
.Vertices
[3] = tmp
;
1852 tmp
= pi
.Patch
.Vertices
[1];
1853 pi
.Patch
.Vertices
[1] = pi
.Patch
.Vertices
[2];
1854 pi
.Patch
.Vertices
[2] = tmp
;
1857 tmp
= pi
.Patch
.Tangents
[0];
1858 pi
.Patch
.Tangents
[0] = pi
.Patch
.Tangents
[5];
1859 pi
.Patch
.Tangents
[5] = tmp
;
1860 tmp
= pi
.Patch
.Tangents
[1];
1861 pi
.Patch
.Tangents
[1] = pi
.Patch
.Tangents
[4];
1862 pi
.Patch
.Tangents
[4] = tmp
;
1863 tmp
= pi
.Patch
.Tangents
[2];
1864 pi
.Patch
.Tangents
[2] = pi
.Patch
.Tangents
[3];
1865 pi
.Patch
.Tangents
[3] = tmp
;
1866 tmp
= pi
.Patch
.Tangents
[6];
1867 pi
.Patch
.Tangents
[6] = pi
.Patch
.Tangents
[7];
1868 pi
.Patch
.Tangents
[7] = tmp
;
1871 tmp
= pi
.Patch
.Interiors
[0];
1872 pi
.Patch
.Interiors
[0] = pi
.Patch
.Interiors
[3];
1873 pi
.Patch
.Interiors
[3] = tmp
;
1874 tmp
= pi
.Patch
.Interiors
[1];
1875 pi
.Patch
.Interiors
[1] = pi
.Patch
.Interiors
[2];
1876 pi
.Patch
.Interiors
[2] = tmp
;
1878 // ** Symmetries tile colors
1881 uint countU
= pi
.OrderS
/2+1;
1882 uint countV
= pi
.OrderT
+1;
1883 for (v
=0; v
<countV
; v
++)
1884 for (u
=0; u
<countU
; u
++)
1886 // Store it in the tile info
1887 uint index0
= u
+v
*(pi
.OrderS
+1);
1888 uint index1
= (pi
.OrderS
-u
)+v
*(pi
.OrderS
+1);
1891 uint16 tmp
= pi
.TileColors
[index0
].Color565
;
1892 pi
.TileColors
[index0
].Color565
= pi
.TileColors
[index1
].Color565
;
1893 pi
.TileColors
[index1
].Color565
= tmp
;
1897 uint flags
= (uint
)(pi
.getSmoothFlag (0))<<2;
1898 flags
|= (uint
)(pi
.getSmoothFlag (2))<<0;
1899 flags
|= (uint
)(pi
.getSmoothFlag (1))<<1;
1900 flags
|= (uint
)(pi
.getSmoothFlag (3))<<3;
1905 // --- Symmetry of the bind info.
1906 // --- Parse each patch and each edge
1908 for (i
=0 ; i
<patchCount
; i
++)
1910 // Ref on the patch info
1911 CPatchInfo
&pi
= patchInfo
[i
];
1913 // Xchg left and right
1914 swap (pi
.BindEdges
[0], pi
.BindEdges
[2]);
1915 swap (pi
.BaseVertices
[0], pi
.BaseVertices
[3]);
1916 swap (pi
.BaseVertices
[1], pi
.BaseVertices
[2]);
1919 for (uint edge
=0; edge
<4; edge
++)
1921 // Ref on the patch info
1922 CPatchInfo::CBindInfo
&bindEdge
= pi
.BindEdges
[edge
];
1925 // Look if it is a bind ?
1926 if ( (bindEdge
.NPatchs
>1) && (bindEdge
.NPatchs
!=5) )
1928 for (next
=0; next
<(uint
)bindEdge
.NPatchs
/2; next
++)
1930 swap (bindEdge
.Next
[bindEdge
.NPatchs
- next
- 1], bindEdge
.Next
[next
]);
1931 swap (bindEdge
.Edge
[bindEdge
.NPatchs
- next
- 1], bindEdge
.Edge
[next
]);
1935 // Look if we are binded on a reversed edge
1936 uint bindCount
= (bindEdge
.NPatchs
==5) ? 1 : bindEdge
.NPatchs
;
1937 for (next
=0; next
<bindCount
; next
++)
1940 if ( (bindEdge
.Edge
[next
] & 1) == 0)
1943 bindEdge
.Edge
[next
] += 2;
1944 bindEdge
.Edge
[next
] &= 3;
1952 for (i
=0 ; i
<patchCount
; i
++)
1955 CPatchInfo
&pi
= patchInfo
[i
];
1958 std::vector
<CTileElement
> tiles
= pi
.Tiles
;
1961 for (v
=0; v
<pi
.OrderT
; v
++)
1962 for (u
=0; u
<pi
.OrderS
; u
++)
1965 int uSymmetry
= symmetry
? (pi
.OrderS
-u
-1) : u
;
1968 CTileElement
&element
= pi
.Tiles
[u
+v
*pi
.OrderS
];
1970 // Copy the orginal symmetrical element
1971 element
= tiles
[uSymmetry
+v
*pi
.OrderS
];
1974 for (int l
=0; l
<3; l
++)
1977 if (element
.Tile
[l
] != 0xffff)
1979 // Get the tile index
1980 uint tile
= element
.Tile
[l
];
1981 uint tileRotation
= element
.getTileOrient (l
);
1983 // Get rot and symmetry for this tile
1984 uint tileRotate
= rotate
;
1985 bool tileSymmetry
= symmetry
;
1986 bool goofy
= symmetry
&& (zoneSymmetry
.getTileState (i
, uSymmetry
+v
*pi
.OrderS
, l
) == CZoneSymmetrisation::Goofy
);
1988 // Transform the transfo
1989 if (getTileSymmetryRotate (bank
, tile
, tileSymmetry
, tileRotate
))
1991 // Transform the tile
1992 if (!transformTile (bank
, tile
, tileRotation
, tileSymmetry
, (4-tileRotate
)&3, goofy
))
1995 nlwarning ("Error getting symmetrical / rotated zone tile.");
2002 nlwarning ("Error getting symmetrical / rotated zone tile.");
2007 element
.Tile
[l
] = tile
;
2008 element
.setTileOrient (l
, (uint8
)tileRotation
);
2013 if (element
.Tile
[0]!=0xffff)
2018 element
.getTile256Info (is256x256
, uvOff
);
2023 // Get rot and symmetry for this tile
2024 uint tileRotate
= rotate
;
2025 bool tileSymmetry
= symmetry
;
2026 uint tileRotation
= tiles
[uSymmetry
+v
*pi
.OrderS
].getTileOrient (0);
2027 bool goofy
= symmetry
&& (zoneSymmetry
.getTileState (i
, uSymmetry
+v
*pi
.OrderS
, 0) == CZoneSymmetrisation::Goofy
);
2029 // Transform the transfo
2030 getTileSymmetryRotate (bank
, element
.Tile
[0], tileSymmetry
, tileRotate
);
2032 // Transform the case
2033 transform256Case (bank
, uvOff
, tileRotation
, tileSymmetry
, (4-tileRotate
)&3, goofy
);
2035 element
.setTile256Info (true, uvOff
);
2045 // ***************************************************************************