1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010-2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "nel/misc/types_nl.h"
24 #include "nel/misc/debug.h"
25 #include "nel/misc/path.h"
26 #include "nel/misc/file.h"
28 #include "nel/misc/plane.h"
29 #include "nel/misc/triangle.h"
30 #include "nel/misc/polygon.h"
32 #include "nel/3d/landscape.h"
33 #include "nel/3d/zone.h"
34 #include "nel/3d/mesh.h"
35 #include "nel/3d/quad_grid.h"
37 #include "nel/pacs/vector_2s.h"
39 #include "build_surf.h"
44 using namespace NLMISC
;
50 uint16
getZoneIdByPos(CVector
&pos
)
53 const float zdim
= 160.0f
;
55 x
= (uint
)(pos
.x
/zdim
);
56 y
= (uint
)(-pos
.y
/zdim
);
61 string
getZoneNameById(uint16 id
)
67 sprintf(ych
,"%d_%c%c", y
+1, 'A'+x
/26, 'A'+x
%26);
71 uint16
getZoneIdByName(string
&name
)
73 string upperName
= strupr (name
);
75 const char *str
= upperName
.c_str();
78 y
= y
*10 + *(str
++)-'0';
82 x
= (str
[0]-'A')*26+(str
[1]-'A');
87 CAABBox
getZoneBBoxById(uint16 id
)
91 const float zdim
= 160.0f
;
95 bbox
.setMinMax(CVector(zdim
*x
, -zdim
*(y
+1), -10000.0f
),
96 CVector(zdim
*(x
+1), -zdim
*y
, +10000.0f
));
103 bool relativeEquals(CVector
&a
, CVector
&b
, float epsilon
)
105 float n
= (a
-b
).norm();
109 return (n
/r
<= epsilon
);
112 bool absoluteEquals(CVector
&a
, CVector
&b
, float epsilon
)
114 float n
= (a
-b
).norm();
118 inline CVector
vmin(const CVector
&a
, const CVector
&b
, const CVector
&c
)
120 return CVector(std::min(a
.x
, std::min(b
.x
, c
.x
)),
121 std::min(a
.y
, std::min(b
.y
, c
.y
)),
122 std::min(a
.z
, std::min(b
.z
, c
.z
)));
125 inline CVector
vmax(const CVector
&a
, const CVector
&b
, const CVector
&c
)
127 return CVector(std::max(a
.x
, std::max(b
.x
, c
.x
)),
128 std::max(a
.y
, std::max(b
.y
, c
.y
)),
129 std::max(a
.z
, std::max(b
.z
, c
.z
)));
132 inline CVector
vmin(const CVector
&a
, const CVector
&b
)
134 return CVector(std::min(a
.x
, b
.x
),
139 inline CVector
vmax(const CVector
&a
, const CVector
&b
)
141 return CVector(std::max(a
.x
, b
.x
),
146 static sint64
float2Fixed(float f
)
148 return (sint64
)(floor(f
*NLPACS::Vector2sAccuracy
));
151 static float fixed2Float(sint64 s
)
153 return ((float)s
)/NLPACS::Vector2sAccuracy
;
156 static void snapAccuracyBit(float &f
)
158 f
= fixed2Float(float2Fixed(f
));
161 static void snapAccuracyBit(CVector
&v
)
163 snapAccuracyBit(v
.x
);
164 snapAccuracyBit(v
.y
);
165 snapAccuracyBit(v
.z
);
168 static CAABBox
getSnappedBBox(CVector v0
, CVector v1
, CVector v2
, const CAABBox
&bbox
)
183 static float alg2dArea(const CVector
&v0
, const CVector
&v1
, const CVector
&v2
)
185 float ux
= v1
.x
-v0
.x
,
190 return ux
*vy
- uy
*vx
;
193 static double alg2dArea(const CVectorD
&v0
, const CVectorD
&v1
, const CVectorD
&v2
)
195 double ux
= v1
.x
-v0
.x
,
200 return ux
*vy
- uy
*vx
;
207 enum { bucket_size
= 4, min_buckets
= 8, };
210 size_t operator() (const ptrA
&a
) const
212 return ((uintptr_t)a
)/sizeof(A
);
215 bool operator() (const ptrA
&a
, const ptrA
&b
) const
217 return (uintptr_t)a
< (uintptr_t)b
;
221 bool isInside(const CPolygon
&poly
, const NLPACS::CSurfElement
&elem
)
225 if (poly
.getNumVertices() <= 2)
228 CVector pnorm
= (poly
.Vertices
[0]-poly
.Vertices
[1])^(poly
.Vertices
[2]-poly
.Vertices
[1]);
233 const CVector
&v
= (*elem
.Vertices
)[elem
.Tri
[i
]];
236 for (j
=0; j
<poly
.Vertices
.size(); ++j
)
238 const CVector
&v0
= poly
.Vertices
[j
],
239 &v1
= (j
== poly
.Vertices
.size()-1) ? poly
.Vertices
[0] : poly
.Vertices
[j
+1];
241 if ((pnorm
^(v1
-v0
)) * (v
-v1
) > 0.0f
)
260 bool operator () (const CAABBox
&a
, const CAABBox
&b
) const
262 return a
.getCenter().z
< b
.getCenter().z
;
274 * CSurfElement methods
279 void NLPACS::CSurfElement::computeQuantas(CZoneTessellation
*zoneTessel
)
281 CVector v0
= (*Vertices
)[Tri
[0]],
282 v1
= (*Vertices
)[Tri
[1]],
283 v2
= (*Vertices
)[Tri
[2]];
293 CVector n
= (nv1
-nv0
) ^ (nv2
-nv0
);
295 double hmin
= std::min(v0
.z
, std::min(v1
.z
, v2
.z
));
296 //QuantHeight = ((uint8)(floor((v0.z+v1.z+v2.z)/6.0f)))%255;
297 QuantHeight
= ((uint8
)floor(hmin
/2.0))%255;
299 Area
= 0.5f
*n
.norm();
301 IsValid
= (Normal
.z
> 0.707f
);
303 uint8 bits0
= PrimChecker
.get((uint
)v0
.x
, (uint
)v0
.y
);
304 uint8 bits1
= PrimChecker
.get((uint
)v1
.x
, (uint
)v1
.y
);
305 uint8 bits2
= PrimChecker
.get((uint
)v2
.x
, (uint
)v2
.y
);
307 uint16 ws0
= PrimChecker
.index((uint
)v0
.x
, (uint
)v0
.y
);
308 uint16 ws1
= PrimChecker
.index((uint
)v1
.x
, (uint
)v1
.y
);
309 uint16 ws2
= PrimChecker
.index((uint
)v2
.x
, (uint
)v2
.y
);
311 uint8 bits
= bits0
|bits1
|bits2
;
313 if (bits
& CPrimChecker::Include
)
318 if (bits
& CPrimChecker::Exclude
)
324 if (bits
& CPrimChecker::ClusterHint
&& IsValid
)
329 // if ((bits & CPrimChecker::Cliff) && (bits & CPrimChecker::Water))
332 if (Normal
.z
<= 0.30f
)
337 else if ((bits
& CPrimChecker::Water
) != 0 && !ForceInvalid
)
339 bool w0
= ((bits0
&CPrimChecker::Water
) != 0);
340 bool w1
= ((bits1
&CPrimChecker::Water
) != 0);
341 bool w2
= ((bits2
&CPrimChecker::Water
) != 0);
345 if ((w0
&& w1
&& ws0
== ws1
) || (w0
&& w2
&& ws0
== ws2
))
347 else if (w1
&& w2
&& ws1
== ws2
)
357 nlwarning("No WaterShape found for element %d, whereas water detected...", ElemId
);
363 float wh
= PrimChecker
.waterHeight(ws
, exists
);
364 if (exists
&& ((*Vertices
)[Tri
[0]].z
< wh
|| (*Vertices
)[Tri
[1]].z
< wh
|| (*Vertices
)[Tri
[2]].z
< wh
))
366 if (bits
& CPrimChecker::Cliff
)
390 CAABBox
NLPACS::CSurfElement::getBBox() const
393 box
.setCenter((*Vertices
)[Tri
[0]]);
394 box
.extend((*Vertices
)[Tri
[1]]);
395 box
.extend((*Vertices
)[Tri
[2]]);
400 * CComputableSurfaceBorder methods implementation
404 void NLPACS::CComputableSurfaceBorder::dump()
408 nldebug("dump border between %d and %d", Left
, Right
);
409 for (i
=0; i
<(sint
)Vertices
.size(); ++i
)
411 nldebug(" v[%d]={%g,%g,%g}", i
, Vertices
[i
].x
, Vertices
[i
].y
, Vertices
[i
].z
);
415 void NLPACS::CComputableSurfaceBorder::smooth(float val
)
418 vector
<CVector
>::iterator it
, minIt
;
423 bool allowMoveLeft
= (Left
!= -1);
424 bool allowMoveRight
= (Right
!= -1);
428 for (; numPass
>0 && Vertices
.size()>3; --numPass
)
430 CVector previous
= Vertices
[0];
431 for (i
=1; i
<Vertices
.size()-1; ++i
)
433 CVector newVert
= (Vertices
[i
]*2.0+previous
+Vertices
[i
+1])/4.0f
;
435 if (!allowMoveLeft
|| !allowMoveRight
)
437 float area1
= alg2dArea(previous
, Vertices
[i
], Vertices
[i
+1]);
438 float area2
= alg2dArea(previous
, newVert
, Vertices
[i
+1]);
440 previous
= Vertices
[i
];
442 if ((!allowMoveLeft
&& area2
> area1
) || (!allowMoveRight
&& area2
< area1
))
443 Vertices
[i
] = newVert
;
447 previous
= Vertices
[i
];
448 Vertices
[i
] = newVert
;
453 before
= (uint
)Vertices
.size();
454 while (Vertices
.size()>3) // don't smooth blow 3 vertices to avoid degenrated surfaces
457 minIt
= Vertices
.end();
458 it
= Vertices
.begin();
461 for (i
=1; i
<Vertices
.size()-1; ++i
, ++it
)
465 area
= 0.5f
*((Vertices
[i
+1]-Vertices
[i
])^(Vertices
[i
-1]-Vertices
[i
])).norm();
473 if (minIt
!= Vertices
.end())
475 Vertices
.erase(minIt
);
482 after
= (uint
)Vertices
.size();
485 nlinfo("smoothed border %d-%d: %d -> %d", Left
, Right
, before
, after
);
489 * CComputableSurface methods implementation
492 void NLPACS::CComputableSurface::followBorder(CZoneTessellation
*zoneTessel
, CSurfElement
*first
, uint edge
, uint sens
, vector
<CVector
> &vstore
, bool &loop
)
494 CSurfElement
*current
= first
;
495 CSurfElement
*next
= current
->EdgeLinks
[edge
];
496 current
->EdgeFlag
[edge
] = true;
498 const sint32 currentSurfId
= current
->SurfaceId
;
499 const sint32 oppositeSurfId
= (next
!= NULL
) ? next
->SurfaceId
: UnaffectedSurfaceId
;
500 const sint32 oppositeZid
= current
->getZoneIdOnEdge(edge
);
503 sint pivot
= (edge
+sens
)%3;
504 sint nextEdge
= edge
;
506 bool allowThis
= true;
508 // adds the pivot to the border and its normal
509 vstore
.push_back((*current
->Vertices
)[current
->Tri
[pivot
]]);
517 current
->IsBorder
= true;
519 if (((oppositeSurfId
!= UnaffectedSurfaceId
) && (next
== NULL
|| (next
->SurfaceId
!= oppositeSurfId
&& next
->SurfaceId
!= currentSurfId
))) ||
520 ((oppositeSurfId
== UnaffectedSurfaceId
) && ((next
!= NULL
&& next
->SurfaceId
!= currentSurfId
) || (next
== NULL
&& current
->getZoneIdOnEdge(nextEdge
) != oppositeZid
))) ||
521 ((current
->EdgeFlag
[nextEdge
] || (zoneTessel
->VerticesFlags
[current
->Tri
[pivot
]]!=0)) && !allowThis
))
523 // if reaches the end of the border, then quits.
524 loop
= (absoluteEquals(vstore
.front(), vstore
.back(), 1e-2f
) && loopCount
!= 1);
527 else if ((oppositeSurfId
!= UnaffectedSurfaceId
&& next
->SurfaceId
== oppositeSurfId
) ||
528 (oppositeSurfId
== UnaffectedSurfaceId
&& next
== NULL
))
530 // if the next edge belongs to the border, then go on the same element
531 current
->EdgeFlag
[nextEdge
] = true;
532 if (oppositeSurfId
!= UnaffectedSurfaceId
)
534 for (oedge
=0; oedge
<3 && next
->EdgeLinks
[oedge
]!=current
; ++oedge
)
536 nlassert(oedge
!= 3);
537 nlassert(allowThis
|| !next
->EdgeFlag
[oedge
]);
538 next
->EdgeFlag
[oedge
] = true;
540 pivot
= (pivot
+sens
)%3;
541 nextEdge
= (nextEdge
+sens
)%3;
542 next
= current
->EdgeLinks
[nextEdge
];
543 vstore
.push_back((*current
->Vertices
)[current
->Tri
[pivot
]]);
547 // if the next element is inside the surface, then go to the next element
548 nlassert(next
->SurfaceId
== currentSurfId
);
550 for (oedge
=0; oedge
<3 && next
->EdgeLinks
[oedge
]!=current
; ++oedge
)
552 nlassert(oedge
!= 3);
554 pivot
= (oedge
+3-sens
)%3;
555 nextEdge
= (oedge
+sens
)%3;
556 next
= current
->EdgeLinks
[nextEdge
];
563 void NLPACS::CComputableSurface::buildBorders(CZoneTessellation
*zoneTessel
)
568 nlinfo("generate borders for the surface %d (%d elements) - water=%d", SurfaceId
, Elements
.size(), (IsUnderWater
? 1 : 0));
570 for (elem
=0; elem
<(sint
)Elements
.size(); ++elem
)
573 // scan for a edge that points to a different surface
575 nlassert(Elements
[elem
]->SurfaceId
== SurfaceId
);
577 for (edge
=0; edge
<3; ++edge
)
580 if ((Elements
[elem
]->EdgeLinks
[edge
] == NULL
|| Elements
[elem
]->EdgeLinks
[edge
]->SurfaceId
!= SurfaceId
) &&
581 !Elements
[elem
]->EdgeFlag
[edge
])
583 BorderIds
.push_back((uint16
)BorderKeeper
->size());
585 BorderKeeper
->resize(BorderKeeper
->size()+1);
586 CComputableSurfaceBorder
&border
= BorderKeeper
->back();
588 border
.Left
= Elements
[elem
]->SurfaceId
;
591 //border.DontSmooth = (Elements[elem]->EdgeLinks[edge] != NULL && Elements[elem]->NoLevelSurfaceId == Elements[elem]->EdgeLinks[edge]->SurfaceId);
593 if (Elements
[elem
]->EdgeLinks
[edge
] != NULL
&& Elements
[elem
]->EdgeLinks
[edge
]->ZoneId
!= Elements
[elem
]->ZoneId
)
595 // link on a neighbor zone
598 if (Elements
[elem
]->EdgeLinks
[edge
] == NULL
|| Elements
[elem
]->EdgeLinks
[edge
]->SurfaceId
== UnaffectedSurfaceId
)
605 border
.Right
= Elements
[elem
]->EdgeLinks
[edge
]->SurfaceId
;
609 nlinfo("generate border %d (%d-%d)", BorderKeeper
->size()-1, border
.Left
, border
.Right
);
612 vector
<CVector
> bwdVerts
;
613 vector
<CVector
> &fwdVerts
= border
.Vertices
;
615 followBorder(zoneTessel
, Elements
[elem
], edge
, 2, bwdVerts
, loop
);
619 fwdVerts
.reserve(bwdVerts
.size());
622 for (i
=(sint
)(bwdVerts
.size()-1); i
>=0; --i
)
624 fwdVerts
.push_back(bwdVerts
[i
]);
629 fwdVerts
.push_back(fwdVerts
.front());
633 fwdVerts
.resize(fwdVerts
.size()-2);
634 followBorder(zoneTessel
, Elements
[elem
], edge
, 1, fwdVerts
, loop
);
641 bool intersect(const CVectorD
& a0
, const CVectorD
& a1
, const CVectorD
& b0
, const CVectorD
& b1
, double& pa
, double& pb
)
643 double ax
= +(a1
.x
-a0
.x
);
644 double ay
= +(a1
.y
-a0
.y
);
645 double bx
= +(b1
.x
-b0
.x
);
646 double by
= +(b1
.y
-b0
.y
);
647 double cx
= +(b0
.x
-a0
.x
);
648 double cy
= +(b0
.y
-a0
.y
);
650 double d
= -ax
*by
+ ay
*bx
;
655 double a
= (bx
*cy
- by
*cx
) / d
;
656 double b
= (ax
*cy
- ay
*cx
) / d
;
661 return d
!= 0.0 && a
>= 0.0 && a
<= 1.0 && b
>= 0.0 && b
<= 1.0;
664 // Check Surface Consistency
665 bool NLPACS::CComputableSurface::checkConsistency()
669 std::vector
<std::pair
<NLMISC::CVectorD
, NLMISC::CVectorD
> > edges
;
672 for (i
=0; i
<BorderIds
.size(); ++i
)
674 CComputableSurfaceBorder
& border
= (*BorderKeeper
)[BorderIds
[i
]];
676 for (j
=0; j
+1<border
.Vertices
.size(); ++j
)
677 edges
.push_back(std::make_pair(border
.Vertices
[j
], border
.Vertices
[j
+1]));
680 for (i
=0; i
+1<edges
.size(); ++i
)
682 for (j
=i
+1; j
<edges
.size(); ++j
)
684 CVectorD a0
= edges
[i
].first
,
685 a1
= edges
[i
].second
;
686 CVectorD b0
= edges
[j
].first
,
687 b1
= edges
[j
].second
;
690 bool inters
= intersect(a0
, a1
, b0
, b1
, a
, b
);
695 double da
= (a1
-a0
).norm();
696 double db
= (b1
-b0
).norm();
698 bool tipa
= (fabs(a
)*da
< 4.0e-2 || fabs(1.0-a
)*da
< 4.0e-2);
699 bool tipb
= (fabs(b
)*db
< 4.0e-2 || fabs(1.0-b
)*db
< 4.0e-2);
723 * CZoneTessellation constructors and methods implementation
736 bool NLPACS::CZoneTessellation::setup(uint16 zoneId
, sint16 refinement
, const CVector
&translation
)
738 CentralZoneId
= zoneId
;
739 Refinement
= refinement
;
740 // Translation = translation;
742 // the zone bbox is hard coded for accuracy improvement...
743 OriginalBBox
= getZoneBBoxById(zoneId
);
745 Translation
= -BBox
.getCenter();
746 Translation
.x
= (float)floor(Translation
.x
+0.5f
);
747 Translation
.y
= (float)floor(Translation
.y
+0.5f
);
748 Translation
.z
= (float)floor(Translation
.z
+0.5f
);
749 BBox
.setCenter(CVector::Null
);
750 BBox
.setHalfSize(CVector(80.0f
, 80.0f
, BBox
.getHalfSize().z
));
752 // if zone doesn't exist, don't even setup tessellation
755 if (CPath::lookup(getZoneNameById(zoneId
)+ZoneExt
, false, false) == "")
758 catch (const EPathNotFound
&)
763 // setup the square of 9 zones...
765 nlinfo("setup zone tessellation %d %s", zoneId
, getZoneNameById(zoneId
).c_str());
767 sint zx
= zoneId
%256, zy
= zoneId
/256;
769 for (zy
=(zoneId
/256)-1; zy
<=(zoneId
/256)+1; ++zy
)
771 for (zx
=(zoneId
%256)-1; zx
<=(zoneId
%256)+1; ++zx
)
773 if (zx
>= 0 && zx
<= 255 && zy
>= 0 && zy
<= 255)
775 uint zid
= (zy
<<8) + (zx
);
776 string filename
= getZoneNameById(zid
)+ZoneExt
;
777 if (CPath::lookup(filename
, false, false) != "")
779 _ZoneIds
.push_back(zid
);
787 sort(_ZoneIds
.begin(), _ZoneIds
.end());
794 // ***************************************************************************
795 void NLPACS::CZoneTessellation::checkSameLandscapeHmBinds(const NL3D::CLandscape
&landscape
, const NL3D::CLandscape
&landscapeNoHm
)
798 for (uint i
=0; i
<_ZoneIds
.size(); ++i
)
800 uint16 zoneId
= _ZoneIds
[i
];
802 CLandscape::buildZoneName(zoneId
, zoneName
);
805 const CZone
*zone
= landscape
.getZone(zoneId
);
806 const CZone
*zoneNoHm
= landscapeNoHm
.getZone(zoneId
);
807 // Check that both are valid, or both are not
808 if( (zone
==NULL
) != (zoneNoHm
==NULL
) )
810 nlwarning("ERROR: The zone %s is %s in the landscape while it is %s in the landscape_with_No_Heightmap",
811 zoneName
.c_str(), zone
==NULL
?"not present":"present", zoneNoHm
==NULL
?"not present":"present");
814 // else if both are valid
815 else if(zone
&& zoneNoHm
)
817 // check same number of patches
818 uint numPatchs
= zone
->getNumPatchs();
819 uint numPatchsNoHm
= zoneNoHm
->getNumPatchs();
820 if(numPatchs
!=numPatchsNoHm
)
822 nlwarning("ERROR: The zone %s has %d Patchs in the landscape while it has %d Patchs in the landscape_with_No_Heightmap",
823 zoneName
.c_str(), numPatchs
, numPatchsNoHm
);
828 // Check all binds of all patches
829 std::vector
<string
> errors
;
831 for(uint j
=0;j
<numPatchs
;j
++)
833 const CZone::CPatchConnect
*patch
= zone
->getPatchConnect(j
);
834 const CZone::CPatchConnect
*patchNoHm
= zoneNoHm
->getPatchConnect(j
);
836 // Check BaseVertices
837 for(uint v
=0;v
<4;v
++)
839 if(patch
->BaseVertices
[v
] != patchNoHm
->BaseVertices
[v
])
841 errors
.push_back(toString(" The Patch %d has not the same %dth vertex: %d/%d",
842 j
, v
, patch
->BaseVertices
[v
], patchNoHm
->BaseVertices
[v
]));
847 for(uint b
=0;b
<4;b
++)
849 CPatchInfo::CBindInfo bind
= patch
->BindEdges
[b
];
850 CPatchInfo::CBindInfo bindNoHm
= patchNoHm
->BindEdges
[b
];
852 // verify all valid properties only
853 ok
= ok
&& bind
.NPatchs
== bindNoHm
.NPatchs
;
854 ok
= ok
&& bind
.ZoneId
== bindNoHm
.ZoneId
;
855 uint validNbBinds
= min(bind
.NPatchs
,uint8(4));
856 for(uint nb
=0;nb
<validNbBinds
;nb
++)
858 ok
= ok
&& bind
.Next
[nb
] == bindNoHm
.Next
[nb
];
859 ok
= ok
&& bind
.Edge
[nb
] == bindNoHm
.Edge
[nb
];
866 error
= toString(" The Patch %d that has not the same %dth bindEdge: NumPatchs: %d/%d; ZoneId: %d/%d",
867 j
, b
, bind
.NPatchs
, bindNoHm
.NPatchs
, bind
.ZoneId
, bindNoHm
.ZoneId
);
868 for(uint nb
=0;nb
<validNbBinds
;nb
++)
870 error
+= toString("; Edge (%d,%d)/(%d,%d)", bind
.Next
[nb
], bind
.Edge
[nb
], bindNoHm
.Next
[nb
], bindNoHm
.Edge
[nb
]);
873 errors
.push_back(error
);
879 // if some error found
882 // todo: change the way the landscape with heightmap is applied: it should be applied after welding!
883 // or at least the welding of zones should just keep the same welding as the non heightmapped one
884 nlwarning("ERROR: The zone %s has a different bind strucutre in the landscape and in the landscape_with_No_Heightmap", zoneName
.c_str());
885 nlwarning("ERROR: Hint: Check your heightmap: it may be too precise or has too much noise, causing the zonewelder to behav differently...");
886 nlwarning("TIP: Use the 'zone_elevation' tool on the welded no-heightmap zones to resolve this!");
887 nlwarning("More Details (information landscape / information landscape_with_No_Heightmap):");
888 for(uint j
=0;j
<errors
.size();j
++)
890 nlwarning("%s", errors
[j
].c_str());
900 // ***************************************************************************
901 void NLPACS::CZoneTessellation::build()
906 NL3D::CLandscape landscape
;
909 vector
<CVector
> normals
;
911 vector
<CVector
> vectorCheck
;
912 bool useNoHmZones
= true;
915 NL3D::CLandscape landscapeNoHm
;
916 landscapeNoHm
.init();
919 // load the 9 landscape zones
921 for (i
=0; i
<_ZoneIds
.size(); ++i
)
923 string filename
= getZoneNameById(_ZoneIds
[i
])+ZoneExt
;
924 CIFile
file(CPath::lookup(filename
));
930 nlinfo("use zone %s %d", filename
.c_str(), zone
.getZoneId());
932 if (zone
.getZoneId() != _ZoneIds
[i
])
934 nlwarning ("Zone %s ID is wrong. Abort.", filename
.c_str());
937 landscape
.addZone(zone
);
941 string filenameNH
= getZoneNameById(_ZoneIds
[i
])+ZoneNHExt
;
942 string loadZ
= CPath::lookup(filenameNH
, false, false);
945 CIFile
fileNH(loadZ
);
947 zoneNH
.serial(fileNH
);
949 if (zoneNH
.getZoneId() != _ZoneIds
[i
])
951 nlwarning ("Zone %s ID is wrong. Abort.", filenameNH
.c_str());
954 landscapeNoHm
.addZone(zoneNH
);
958 useNoHmZones
= false;
962 _ZonePtrs
.push_back(landscape
.getZone(_ZoneIds
[i
]));
965 landscape
.setNoiseMode(false);
966 landscape
.checkBinds();
970 landscapeNoHm
.setNoiseMode(false);
971 landscapeNoHm
.checkBinds();
974 BestFittingBBox
.setCenter(CVector::Null
);
975 BestFittingBBox
.setHalfSize(CVector::Null
);
976 BestFittingBBoxSetuped
= false;
978 // Compute best fitting bbox
979 for (i
=0; i
<_ZoneIds
.size(); ++i
)
981 if (_ZoneIds
[i
] == CentralZoneId
)
983 if(_ZonePtrs
[i
]->getNumPatchs()>0)
985 BestFittingBBox
= _ZonePtrs
[i
]->getZoneBB().getAABBox();
986 BestFittingBBoxSetuped
= true;
991 CAABBox enlBBox
= BestFittingBBox
;
992 enlBBox
.setHalfSize(enlBBox
.getHalfSize()+CVector(8.0f
, 8.0f
, 1000.0f
));
994 // Add neighbor patch
995 for (i
=0; i
<_ZoneIds
.size(); ++i
)
997 if (_ZoneIds
[i
] == CentralZoneId
)
999 for (j
=0; (sint
)j
<_ZonePtrs
[i
]->getNumPatchs(); ++j
)
1001 landscape
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, false);
1003 landscapeNoHm
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, false);
1006 nlinfo(" - selected %d/%d patches for zone %d", _ZonePtrs
[i
]->getNumPatchs(), _ZonePtrs
[i
]->getNumPatchs(), _ZoneIds
[i
]);
1011 for (j
=0; (sint
)j
<_ZonePtrs
[i
]->getNumPatchs(); ++j
)
1013 CAABBox pbox
= _ZonePtrs
[i
]->getPatch(j
)->buildBBox();
1014 bool inters
= enlBBox
.intersect(pbox
);
1018 landscape
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, false);
1020 landscapeNoHm
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, false);
1025 landscape
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, true);
1027 landscapeNoHm
.excludePatchFromRefineAll(_ZoneIds
[i
], j
, true);
1031 nlinfo(" - selected %d/%d patches for zone %d", nump
, _ZonePtrs
[i
]->getNumPatchs(), _ZoneIds
[i
]);
1035 // tessellate the landscape, get the leaves (the tessellation faces), and convert them
1036 // into surf elements
1038 nlinfo("Compute landscape tessellation");
1041 nlinfo(" - tessellate landscape");
1045 // Before tesselate, verify that the 2 landscape zones have at least the same binds!
1046 // Else there will be errors because of not the same tesselation
1047 checkSameLandscapeHmBinds(landscape
, landscapeNoHm
);
1050 landscapeNoHm
.setThreshold(0.0f
);
1051 landscapeNoHm
.setTileMaxSubdivision(TessellateLevel
);
1052 landscapeNoHm
.refineAll(CVector::Null
);
1053 landscapeNoHm
.averageTesselationVertices();
1056 vector
<const CTessFace
*> leavesNoHm
;
1057 landscapeNoHm
.getTessellationLeaves(leavesNoHm
);
1059 for (el
=0; el
<(sint
)leavesNoHm
.size(); ++el
)
1061 const CTessFace
*face
= leavesNoHm
[el
];
1062 const CVector
*v
[3];
1064 // get the vertices of the face
1065 v
[0] = &(face
->VBase
->EndPos
);
1066 v
[1] = &(face
->VLeft
->EndPos
);
1067 v
[2] = &(face
->VRight
->EndPos
);
1069 normals
.push_back( ((*(v
[1])-*(v
[0])) ^ (*(v
[2])-*(v
[0]))).normed() );
1071 vectorCheck
.push_back(*(v
[0]));
1072 vectorCheck
.push_back(*(v
[1]));
1073 vectorCheck
.push_back(*(v
[2]));
1078 // Build the lanscape with heightmap
1079 landscape
.setThreshold(0.0f
);
1080 landscape
.setTileMaxSubdivision(TessellateLevel
);
1081 landscape
.refineAll(CVector::Null
);
1082 landscape
.averageTesselationVertices();
1084 vector
<const CTessFace
*> leaves
;
1085 landscape
.getTessellationLeaves(leaves
);
1089 nlinfo(" - used no height map zones");
1090 nlinfo(" - generated %d leaves", leaves
.size());
1093 // If don't use NoHm zones, build normals and vectorCheck directly from std landscape
1096 for (el
=0; el
<(sint
)leaves
.size(); ++el
)
1098 const CTessFace
*face
= leaves
[el
];
1099 const CVector
*v
[3];
1101 // get the vertices of the face
1102 v
[0] = &(face
->VBase
->EndPos
);
1103 v
[1] = &(face
->VLeft
->EndPos
);
1104 v
[2] = &(face
->VRight
->EndPos
);
1106 normals
.push_back( ((*(v
[1])-*(v
[0])) ^ (*(v
[2])-*(v
[0]))).normed() );
1108 vectorCheck
.push_back(*(v
[0]));
1109 vectorCheck
.push_back(*(v
[1]));
1110 vectorCheck
.push_back(*(v
[2]));
1114 // check that there is the same number of faces from landscape with and without heightmap
1115 if (normals
.size() != leaves
.size())
1117 nlwarning ("ERROR : The heightmaped landscape has not the same number of polygon than the nonheightmaped landscape: %d/%d.",
1118 normals
.size(), leaves
.size());
1122 // generate a vector of vertices and of surf element
1123 CHashMap
<const CVector
*, uint32
, CHashPtr
<const CVector
> > vremap
;
1124 CHashMap
<const CVector
*, uint32
, CHashPtr
<const CVector
> >::iterator vremapit
;
1125 CHashMap
<const CTessFace
*, CSurfElement
*, CHashPtr
<const CTessFace
> > fremap
;
1126 CHashMap
<const CTessFace
*, CSurfElement
*, CHashPtr
<const CTessFace
> >::iterator fremapit
;
1128 _Tessellation
.resize(leaves
.size());
1131 nlinfo(" - make and remap surface elements");
1133 for (el
=0; el
<(sint
)leaves
.size(); ++el
)
1134 fremap
[leaves
[el
]] = &(_Tessellation
[el
]);
1138 float dist
, maxdist
= 0.0f
;
1140 for (el
=0; el
<(sint
)leaves
.size(); ++el
)
1142 const CTessFace
*face
= leaves
[el
];
1143 const CVector
*v
[3];
1145 CSurfElement
&element
= _Tessellation
[el
];
1148 element
.ZoneId
= face
->Patch
->getZone()->getZoneId();
1150 // get the vertices of the face
1151 v
[0] = &(face
->VBase
->EndPos
);
1152 v
[1] = &(face
->VLeft
->EndPos
);
1153 v
[2] = &(face
->VRight
->EndPos
);
1158 vcheck
= vectorCheck
[check
++] - *(v
[0]);
1160 dist
= vcheck
.norm();
1161 if (dist
> maxdist
) maxdist
= dist
;
1162 //nlassert(vcheck.norm() < 0.1f);
1164 vcheck
= vectorCheck
[check
++] - *(v
[1]);
1166 dist
= vcheck
.norm();
1167 if (dist
> maxdist
) maxdist
= dist
;
1168 //nlassert(vcheck.norm() < 0.1f);
1170 vcheck
= vectorCheck
[check
++] - *(v
[2]);
1172 dist
= vcheck
.norm();
1173 if (dist
> maxdist
) maxdist
= dist
;
1174 //nlassert(vcheck.norm() < 0.1f);
1177 //element.Normal = ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed();
1178 element
.Normal
= normals
[el
];
1181 // search the vertices in the map
1184 // if doesn't exist, create a new vertex
1185 if ((vremapit
= vremap
.find(v
[i
])) == vremap
.end())
1187 element
.Tri
[i
] = (uint32
)_Vertices
.size();
1188 _Vertices
.push_back(*(v
[i
]));
1189 vremap
.insert(make_pair(v
[i
], element
.Tri
[i
]));
1191 // else use previous
1194 element
.Tri
[i
] = vremapit
->second
;
1198 // setup the vertices pointer
1199 element
.Vertices
= &_Vertices
;
1203 edge
[0] = face
->FBase
;
1204 edge
[1] = face
->FRight
;
1205 edge
[2] = face
->FLeft
;
1209 fremapit
= fremap
.find(edge
[i
]);
1210 element
.EdgeLinks
[i
] = (fremapit
!= fremap
.end() ? fremapit
->second
: NULL
);
1214 for (el
=0; el
<(sint
)_Tessellation
.size(); ++el
)
1216 // add the element to the list of valid elements
1217 Elements
.push_back(&(_Tessellation
[el
]));
1227 void NLPACS::CZoneTessellation::compile()
1232 CAABBox tbox
= computeBBox();
1234 bool HasInvertedUnderWater
= false;
1237 for (el
=0; el
<(sint
)Elements
.size(); ++el
)
1239 CSurfElement
&element
= *(Elements
[el
]);
1242 if (element
.Normal
.z
< 0.0)
1244 CVector
&v0
= _Vertices
[element
.Tri
[0]],
1245 &v1
= _Vertices
[element
.Tri
[1]],
1246 &v2
= _Vertices
[element
.Tri
[2]];
1248 uint8 bits0
= PrimChecker
.get((uint
)v0
.x
, (uint
)v0
.y
);
1249 uint8 bits1
= PrimChecker
.get((uint
)v1
.x
, (uint
)v1
.y
);
1250 uint8 bits2
= PrimChecker
.get((uint
)v2
.x
, (uint
)v2
.y
);
1252 bool w0
= ((bits0
&CPrimChecker::Water
) != 0);
1253 bool w1
= ((bits1
&CPrimChecker::Water
) != 0);
1254 bool w2
= ((bits2
&CPrimChecker::Water
) != 0);
1256 if ((bits0
& CPrimChecker::Water
)!=0 || (bits1
& CPrimChecker::Water
)!=0 || (bits2
& CPrimChecker::Water
)!=0)
1260 uint16 ws0
= PrimChecker
.index((uint
)v0
.x
, (uint
)v0
.y
);
1261 uint16 ws1
= PrimChecker
.index((uint
)v1
.x
, (uint
)v1
.y
);
1262 uint16 ws2
= PrimChecker
.index((uint
)v2
.x
, (uint
)v2
.y
);
1264 if ((w0
&& w1
&& ws0
== ws1
) || (w0
&& w2
&& ws0
== ws2
))
1266 else if (w1
&& w2
&& ws1
== ws2
)
1275 float minz
= std::min(_Vertices
[element
.Tri
[0]].z
,
1276 std::min(_Vertices
[element
.Tri
[1]].z
,
1277 _Vertices
[element
.Tri
[2]].z
));
1280 float wh
= PrimChecker
.waterHeight(ws
, exists
)+WaterThreshold
;
1285 CPolygon
p(v0
, v1
, v2
);
1286 PrimChecker
.renderBits(p
, CPrimChecker::Cliff
);
1288 HasInvertedUnderWater
= true;
1294 if (HasInvertedUnderWater
)
1296 nlwarning("zone '%s' has reversed landscape under water", (getZoneNameById((uint16
)CentralZoneId
)+ZoneExt
).c_str());
1300 // compute elements features
1302 nlinfo("compute elements quantas");
1303 for (el
=0; el
<(sint
)Elements
.size(); ++el
)
1305 CSurfElement
&element
= *(Elements
[el
]);
1307 element
.ElemId
= el
;
1308 element
.computeQuantas(this);
1313 // optimizes the number of generated segments
1314 // it also smoothes a bit the surface border
1315 // it seems that 3 consecutive passes are optimal to reduce
1316 // nasty granularity
1318 nlinfo("reduce surfaces");
1324 for (p
=0; p
<(sint
)Elements
.size(); ++p
)
1326 CSurfElement
&e
= *(Elements
[p
]);
1327 CSurfElement
&e0
= *e
.EdgeLinks
[0],
1328 &e1
= *e
.EdgeLinks
[1],
1329 &e2
= *e
.EdgeLinks
[2];
1331 if (e
.IsMergable
&& &e0
!= NULL
&& &e1
!= NULL
&& &e2
!= NULL
&&
1332 e
.ZoneId
== e0
.ZoneId
&&
1333 e
.ZoneId
== e1
.ZoneId
&&
1334 e
.ZoneId
== e2
.ZoneId
&&
1337 // Strong optimization
1338 // merge the element quantas to the neighbors' quantas which are the most numerous
1339 // quantas are evaluated individually
1340 if (e0
.IsValid
&& e1
.IsValid
) e
.IsValid
= true;
1341 if (e1
.IsValid
&& e2
.IsValid
) e
.IsValid
= true;
1342 if (e0
.IsValid
&& e2
.IsValid
) e
.IsValid
= true;
1344 if (e0
.QuantHeight
== e1
.QuantHeight
) e
.QuantHeight
= e0
.QuantHeight
;
1345 if (e1
.QuantHeight
== e2
.QuantHeight
) e
.QuantHeight
= e1
.QuantHeight
;
1346 if (e0
.QuantHeight
== e2
.QuantHeight
) e
.QuantHeight
= e2
.QuantHeight
;
1348 if (e0
.IsValid
&& e1
.IsValid
&& e0
.WaterShape
== e1
.WaterShape
&& e0
.IsUnderWater
== e1
.IsUnderWater
)
1350 e
.WaterShape
= e0
.WaterShape
;
1351 e
.IsUnderWater
= e0
.IsUnderWater
;
1353 if (e1
.IsValid
&& e2
.IsValid
&& e1
.WaterShape
== e2
.WaterShape
&& e1
.IsUnderWater
== e2
.IsUnderWater
)
1355 e
.WaterShape
= e1
.WaterShape
;
1356 e
.IsUnderWater
= e1
.IsUnderWater
;
1358 if (e0
.IsValid
&& e2
.IsValid
&& e0
.WaterShape
== e2
.WaterShape
&& e0
.IsUnderWater
== e2
.IsUnderWater
)
1360 e
.WaterShape
= e2
.WaterShape
;
1361 e
.IsUnderWater
= e2
.IsUnderWater
;
1367 for (p
=0; p
<(sint
)Elements
.size(); ++p
)
1369 CSurfElement
&e
= *(Elements
[p
]);
1370 CSurfElement
&e0
= *e
.EdgeLinks
[0],
1371 &e1
= *e
.EdgeLinks
[1],
1372 &e2
= *e
.EdgeLinks
[2];
1374 if (&e
!= NULL
&& &e0
!= NULL
&& &e1
!= NULL
&& &e2
!= NULL
&&
1375 e
.IsValid
&& e0
.IsValid
&& e1
.IsValid
&& e2
.IsValid
&&
1376 !e
.IsUnderWater
&& e0
.IsUnderWater
&& e1
.IsUnderWater
&& e2
.IsUnderWater
)
1378 nlwarning("isolated submerged element '%d' !", p
);
1383 // translates vertices to the local axis
1384 sint64 vx
, vy
, vz
, tx
, ty
, tz
;
1385 tx
= float2Fixed(Translation
.x
);
1386 ty
= float2Fixed(Translation
.y
);
1387 tz
= float2Fixed(Translation
.z
);
1390 for (i
=0; i
<_Vertices
.size(); ++i
)
1392 vx
= float2Fixed(_Vertices
[i
].x
) + tx
;
1393 vy
= float2Fixed(_Vertices
[i
].y
) + ty
;
1394 vz
= float2Fixed(_Vertices
[i
].z
) + tz
;
1395 _Vertices
[i
] = CVector(fixed2Float(vx
), fixed2Float(vy
), fixed2Float(vz
));
1398 if(BestFittingBBoxSetuped
)
1399 BestFittingBBox
.setCenter(BestFittingBBox
.getCenter()+Translation
);
1406 // first pass of flood fill
1407 // allow detecting landscape irregularities
1411 nlinfo("build and flood fill surfaces -- pass 1");
1412 uint32 surfId
= 0; // + (ZoneId<<16);
1414 for (p
=0; p
<Elements
.size(); ++p
)
1416 if (Elements
[p
]->SurfaceId
== UnaffectedSurfaceId
)
1418 Surfaces
.push_back(CComputableSurface());
1419 CComputableSurface
&surf
= Surfaces
.back();
1421 surf
.BorderKeeper
= &Borders
;
1422 surf
.floodFill(Elements
[p
], surfId
++, CSurfElemCompareSimple(), this);
1423 surf
.BBox
= BestFittingBBox
;
1427 if (surf
.Area
< 30.0f
&& !surf
.Elements
.empty())
1432 aabbox
.setCenter((*surf
.Elements
[0]->Vertices
)[surf
.Elements
[0]->Tri
[0]]);
1434 for (i
=0; i
<surf
.Elements
.size(); ++i
)
1436 aabbox
.extend((*surf
.Elements
[i
]->Vertices
)[surf
.Elements
[i
]->Tri
[0]]);
1437 aabbox
.extend((*surf
.Elements
[i
]->Vertices
)[surf
.Elements
[i
]->Tri
[1]]);
1438 aabbox
.extend((*surf
.Elements
[i
]->Vertices
)[surf
.Elements
[i
]->Tri
[2]]);
1441 // swap all suface elements validity
1442 if (!surf
.Elements
[0]->ForceInvalid
&& aabbox
.getHalfSize().z
< 1.5f
)
1444 for (i
=0; i
<surf
.Elements
.size(); ++i
)
1446 surf
.Elements
[i
]->IsValid
= !surf
.Elements
[i
]->IsValid
;
1449 nlinfo("Reverted surface %d (%d elements, water=%d)", surfId
-1, surf
.Elements
.size(), (surf
.IsUnderWater
? 1 : 0));
1456 ExtSurfaces
.clear();
1459 vector
<CSurfElement
*> elDup
;
1460 for (el
=0; el
<(sint
)Elements
.size(); ++el
)
1461 if (Elements
[el
]->IsValid
)
1462 elDup
.push_back(Elements
[el
]);
1466 for (el
=0; el
<(sint
)Elements
.size(); ++el
)
1468 CSurfElement
&element
= *(Elements
[el
]);
1469 element
.SurfaceId
= UnaffectedSurfaceId
;
1472 if (element
.EdgeLinks
[i
] != NULL
&& !element
.EdgeLinks
[i
]->IsValid
)
1473 element
.EdgeLinks
[i
] = NULL
;
1479 nlinfo("build and flood fill surfaces");
1480 uint32 surfId
= 0; // + (ZoneId<<16);
1482 sint32 extSurf
= -1024;
1484 for (p
=0; p
<Elements
.size(); ++p
)
1486 if (Elements
[p
]->SurfaceId
== UnaffectedSurfaceId
)
1488 bool elInCentral
= (Elements
[p
]->ZoneId
== CentralZoneId
);
1491 sint32 thisSurfId
= (elInCentral
) ? surfId
++ : extSurf
--;
1493 Surfaces
.push_back(CComputableSurface());
1495 ExtSurfaces
.push_back(CComputableSurface());
1497 CComputableSurface
&surf
= (elInCentral
) ? Surfaces
.back() : ExtSurfaces
.back();
1499 surf
.BorderKeeper
= &Borders
;
1500 surf
.floodFill(Elements
[p
], thisSurfId
, CSurfElemCompareNormal(), this);
1501 surf
.BBox
= BestFittingBBox
;
1507 nlinfo("%d surfaces generated", totalSurf
);
1509 for (p
=0; p
<Surfaces
.size(); ++p
)
1511 nlinfo("surf %d: %d elements", p
, Surfaces
[p
].Elements
.size());
1513 if (Surfaces
[p
].Elements
.size() == 1)
1515 nlinfo("elm: %d", Surfaces
[p
].Elements
[0]->ElemId
);
1521 // flag vertices that are pointed by more than 2 surfaces
1522 VerticesFlags
.resize(_Vertices
.size(), 0);
1524 for (p
=0; p
<Elements
.size(); ++p
)
1526 CSurfElement
*elem
= Elements
[p
];
1528 sint32 s
= elem
->SurfaceId
;
1529 sint32 s0
= (elem
->EdgeLinks
[0] != NULL
? elem
->EdgeLinks
[0]->SurfaceId
: UnaffectedSurfaceId
);
1530 sint32 s1
= (elem
->EdgeLinks
[1] != NULL
? elem
->EdgeLinks
[1]->SurfaceId
: UnaffectedSurfaceId
);
1531 sint32 s2
= (elem
->EdgeLinks
[2] != NULL
? elem
->EdgeLinks
[2]->SurfaceId
: UnaffectedSurfaceId
);
1533 if (s
!= s0
&& s
!= s1
&& s0
!= s1
)
1535 VerticesFlags
[elem
->Tri
[2]] = 1;
1538 if (s
!= s1
&& s
!= s2
&& s1
!= s2
)
1540 VerticesFlags
[elem
->Tri
[0]] = 1;
1543 if (s
!= s2
&& s
!= s0
&& s2
!= s0
)
1545 VerticesFlags
[elem
->Tri
[1]] = 1;
1553 void NLPACS::CZoneTessellation::generateBorders(float smooth
)
1558 nlinfo("generate tessellation borders");
1559 // for each surface, build its border
1560 for (surf
=0; surf
<(sint
)Surfaces
.size(); ++surf
)
1561 Surfaces
[surf
].buildBorders(this);
1563 // then, for each border, link the related surfaces...
1565 nlinfo("smooth borders");
1567 sint totalBefore
= 0,
1569 for (border
=0; border
<(sint
)Borders
.size(); ++border
)
1571 CComputableSurfaceBorder
& cborder
= Borders
[border
];
1573 sint lsurf
= cborder
.Left
;
1574 sint rsurf
= cborder
.Right
;
1576 if (CheckConsistency
)
1580 CComputableSurface
& surf
= Surfaces
[lsurf
];
1581 if (!surf
.checkConsistency())
1583 nlwarning("Before smooth of border '%d', surface '%d' not consistent", border
, lsurf
);
1588 CComputableSurface
& surf
= Surfaces
[rsurf
];
1589 if (!surf
.checkConsistency())
1591 nlwarning("Before smooth of border '%d', surface '%d' not consistent", border
, rsurf
);
1596 float smScale
= (Borders
[border
].Right
< 0) ? 0.2f
: 1.0f
;
1597 uint before
= (uint
)Borders
[border
].Vertices
.size();
1598 if (SmoothBorders
&& !Borders
[border
].DontSmooth
)
1600 Borders
[border
].smooth(smooth
*smScale
);
1602 Borders
[border
].computeLength();
1603 uint after
= (uint
)Borders
[border
].Vertices
.size();
1604 totalBefore
+= before
;
1605 totalAfter
+= after
;
1607 if (CheckConsistency
)
1611 CComputableSurface
& surf
= Surfaces
[lsurf
];
1612 if (!surf
.checkConsistency())
1614 nlwarning("After smooth of border '%d', surface '%d' not consistent", border
, lsurf
);
1619 CComputableSurface
& surf
= Surfaces
[rsurf
];
1620 if (!surf
.checkConsistency())
1622 nlwarning("After smooth of border '%d', surface '%d' not consistent", border
, rsurf
);
1628 nlinfo("smooth process: %d -> %d (%.1f percent reduction)", totalBefore
, totalAfter
, 100.0*(1.0-(double)totalAfter
/(double)totalBefore
));
1635 void NLPACS::CZoneTessellation::saveTessellation(COFile
&output
)
1637 output
.serialCont(_Vertices
);
1641 for (i
=0; i
<_Tessellation
.size(); ++i
)
1642 _Tessellation
[i
].ElemId
= i
;
1644 uint32 numTessel
= (uint32
)_Tessellation
.size();
1645 output
.serial(numTessel
);
1647 for (i
=0; i
<_Tessellation
.size(); ++i
)
1649 _Tessellation
[i
].serial(output
, _Tessellation
);
1656 void NLPACS::CZoneTessellation::loadTessellation(CIFile
&input
)
1658 input
.serialCont(_Vertices
);
1663 input
.serial(numTessel
);
1664 _Tessellation
.resize(numTessel
);
1666 for (i
=0; i
<_Tessellation
.size(); ++i
)
1668 _Tessellation
[i
].serial(input
, _Tessellation
);
1671 Elements
.resize(_Tessellation
.size());
1672 for (i
=0; i
<_Tessellation
.size(); ++i
)
1674 Elements
[i
] = &_Tessellation
[i
];
1678 void NLPACS::CZoneTessellation::clear()
1682 _Tessellation
.clear();
1689 CAABBox
NLPACS::CZoneTessellation::computeBBox() const
1695 if (_Vertices
.empty())
1698 zbox
.setCenter(_Vertices
[0]);
1700 for (i
=1; i
<_Vertices
.size(); ++i
)
1701 zbox
.extend(_Vertices
[i
]);