2042 lines
56 KiB
C++
2042 lines
56 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "std3d.h"
|
|
|
|
#include "nel/3d/zone.h"
|
|
#include "nel/3d/landscape.h"
|
|
#include "nel/3d/zone_symmetrisation.h"
|
|
#include "nel/misc/common.h"
|
|
#include "nel/misc/hierarchical_timer.h"
|
|
|
|
|
|
using namespace NLMISC;
|
|
using namespace std;
|
|
|
|
|
|
// define it only for debug bind.
|
|
//#define NL3D_DEBUG_DONT_BIND_PATCH
|
|
|
|
|
|
namespace NL3D
|
|
{
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// CPatchInfo
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CPatchInfo::setCornerSmoothFlag(uint corner, bool smooth)
|
|
{
|
|
nlassert(corner<=3);
|
|
uint mask= 1<<corner;
|
|
if(smooth)
|
|
_CornerSmoothFlag|= mask;
|
|
else
|
|
_CornerSmoothFlag&= ~mask;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CPatchInfo::getCornerSmoothFlag(uint corner) const
|
|
{
|
|
nlassert(corner<=3);
|
|
uint mask= 1<<corner;
|
|
return (_CornerSmoothFlag & mask)!=0;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// CZone
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
CZone::CZone()
|
|
{
|
|
ZoneId= 0;
|
|
Compiled= false;
|
|
Landscape= NULL;
|
|
ClipResult= ClipOut;
|
|
}
|
|
// ***************************************************************************
|
|
CZone::~CZone()
|
|
{
|
|
// release() must have been called.
|
|
nlassert(!Compiled);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::computeBBScaleBias(const CAABBox &bb)
|
|
{
|
|
ZoneBB= bb;
|
|
// Take a security for noise. (useful for zone clipping).
|
|
ZoneBB.setHalfSize(ZoneBB.getHalfSize()+CVector(NL3D_NOISE_MAX, NL3D_NOISE_MAX, NL3D_NOISE_MAX));
|
|
CVector hs= ZoneBB.getHalfSize();
|
|
float rmax= maxof(hs.x, hs.y, hs.z);
|
|
PatchScale= rmax / 32760; // Prevent from float imprecision by taking 32760 and not 32767.
|
|
PatchBias= ZoneBB.getCenter();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::build(uint16 zoneId, const std::vector<CPatchInfo> &patchs, const std::vector<CBorderVertex> &borderVertices, uint32 numVertices)
|
|
{
|
|
CZoneInfo zinfo;
|
|
zinfo.ZoneId= zoneId;
|
|
zinfo.Patchs= patchs;
|
|
zinfo.BorderVertices= borderVertices;
|
|
|
|
build(zinfo, numVertices);
|
|
}
|
|
// ***************************************************************************
|
|
void CZone::build(const CZoneInfo &zoneInfo, uint32 numVertices)
|
|
{
|
|
sint i,j;
|
|
nlassert(!Compiled);
|
|
|
|
// Ref inupt
|
|
uint16 zoneId= zoneInfo.ZoneId;
|
|
const std::vector<CPatchInfo> &patchs= zoneInfo.Patchs;
|
|
const std::vector<CBorderVertex> &borderVertices= zoneInfo.BorderVertices;
|
|
|
|
|
|
ZoneId= zoneId;
|
|
BorderVertices= borderVertices;
|
|
|
|
// Compute the bbox and the bias/scale.
|
|
//=====================================
|
|
CAABBox bb;
|
|
if(patchs.size())
|
|
bb.setCenter(patchs[0].Patch.Vertices[0]);
|
|
bb.setHalfSize(CVector::Null);
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
const CBezierPatch &p= patchs[j].Patch;
|
|
for(i=0;i<4;i++)
|
|
bb.extend(p.Vertices[i]);
|
|
for(i=0;i<8;i++)
|
|
bb.extend(p.Tangents[i]);
|
|
for(i=0;i<4;i++)
|
|
bb.extend(p.Interiors[i]);
|
|
}
|
|
// Compute BBox, and Patch Scale Bias, according to Noise.
|
|
computeBBScaleBias(bb);
|
|
|
|
|
|
// Compute/compress Patchs.
|
|
//=========================
|
|
Patchs.resize(patchs.size());
|
|
PatchConnects.resize(patchs.size());
|
|
sint maxVertex=-1;
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
const CPatchInfo &pi= patchs[j];
|
|
const CBezierPatch &p= pi.Patch;
|
|
CPatch &pa= Patchs[j];
|
|
CPatchConnect &pc= PatchConnects[j];
|
|
|
|
// Smoothing flags
|
|
pa.Flags&=~NL_PATCH_SMOOTH_FLAG_MASK;
|
|
pa.Flags|=NL_PATCH_SMOOTH_FLAG_MASK&(pi.Flags<<NL_PATCH_SMOOTH_FLAG_SHIFT);
|
|
|
|
|
|
// Noise Data
|
|
// copy noise rotation.
|
|
pa.NoiseRotation= pi.NoiseRotation;
|
|
// copy all noise smoothing info.
|
|
for(i=0;i<4;i++)
|
|
{
|
|
pa.setCornerSmoothFlag(i, pi.getCornerSmoothFlag(i));
|
|
}
|
|
|
|
// Copy order of the patch
|
|
pa.OrderS= pi.OrderS;
|
|
pa.OrderT= pi.OrderT;
|
|
|
|
// Build the patch.
|
|
for(i=0;i<4;i++)
|
|
pa.Vertices[i].pack(p.Vertices[i], PatchBias, PatchScale);
|
|
for(i=0;i<8;i++)
|
|
pa.Tangents[i].pack(p.Tangents[i], PatchBias, PatchScale);
|
|
for(i=0;i<4;i++)
|
|
pa.Interiors[i].pack(p.Interiors[i], PatchBias, PatchScale);
|
|
pa.Tiles= pi.Tiles;
|
|
pa.TileColors= pi.TileColors;
|
|
/* Copy TileLightInfluences. It is possible that pi.TileLightInfluences.size()!= 0
|
|
and pi.TileLightInfluences.size()!= (uint)(pi.OrderS/2+1)*(pi.OrderT/2+1)
|
|
Because of a preceding bug where pa.OrderS and pa.OrderT were not initialized before the
|
|
pa.resetTileLightInfluences();
|
|
*/
|
|
if( pi.TileLightInfluences.size()!= (uint)(pi.OrderS/2+1)*(pi.OrderT/2+1) )
|
|
{
|
|
pa.resetTileLightInfluences();
|
|
}
|
|
else
|
|
{
|
|
pa.TileLightInfluences= pi.TileLightInfluences;
|
|
}
|
|
|
|
// Number of lumels in this patch
|
|
uint lumelCount=(pi.OrderS*NL_LUMEL_BY_TILE)*(pi.OrderT*NL_LUMEL_BY_TILE);
|
|
|
|
// Lumel empty ?
|
|
if (pi.Lumels.size ()==lumelCount)
|
|
{
|
|
// Pack the lumel map
|
|
pa.packShadowMap (&pi.Lumels[0]);
|
|
}
|
|
else
|
|
{
|
|
// Reset lightmap
|
|
pa.resetCompressedLumels ();
|
|
}
|
|
|
|
nlassert(pa.Tiles.size()== (uint)pi.OrderS*pi.OrderT);
|
|
nlassert(pa.TileColors.size()== (uint)(pi.OrderS+1)*(pi.OrderT+1));
|
|
|
|
// Build the patchConnect.
|
|
pc.ErrorSize= pi.ErrorSize;
|
|
for(i=0;i<4;i++)
|
|
{
|
|
pc.BaseVertices[i]= pi.BaseVertices[i];
|
|
maxVertex= max((sint)pc.BaseVertices[i], maxVertex);
|
|
}
|
|
for(i=0;i<4;i++)
|
|
pc.BindEdges[i]= pi.BindEdges[i];
|
|
}
|
|
|
|
NumVertices= maxVertex+1;
|
|
NumVertices= max((uint32)NumVertices, numVertices);
|
|
|
|
// Init the Clip Arrays
|
|
_PatchRenderClipped.resize((uint)Patchs.size());
|
|
_PatchOldRenderClipped.resize((uint)Patchs.size());
|
|
_PatchRenderClipped.setAll();
|
|
_PatchOldRenderClipped.setAll();
|
|
|
|
// Copy PointLights.
|
|
//=========================
|
|
// build array, lights are sorted
|
|
std::vector<uint> plRemap;
|
|
_PointLightArray.build(zoneInfo.PointLights, plRemap);
|
|
// Check TileLightInfluences integrity, and remap PointLight Indices.
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
for(uint k= 0; k<pa.TileLightInfluences.size(); k++)
|
|
{
|
|
CTileLightInfluence &tli= pa.TileLightInfluences[k];
|
|
for(uint l=0; l<CTileLightInfluence::NumLightPerCorner; l++)
|
|
{
|
|
// If NULL light, break and continue to next TileLightInfluence.
|
|
if(tli.Light[l]== 0xFF)
|
|
break;
|
|
else
|
|
{
|
|
// Check good index.
|
|
nlassert(tli.Light[l] < _PointLightArray.getPointLights().size());
|
|
// Remap index, because of light sorting.
|
|
tli.Light[l]= plRemap[tli.Light[l]];
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::retrieve(std::vector<CPatchInfo> &patchs, std::vector<CBorderVertex> &borderVertices)
|
|
{
|
|
CZoneInfo zinfo;
|
|
|
|
retrieve(zinfo);
|
|
|
|
patchs= zinfo.Patchs;
|
|
borderVertices= zinfo.BorderVertices;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::retrieve(CZoneInfo &zoneInfo)
|
|
{
|
|
sint i,j;
|
|
|
|
// Ref on input.
|
|
std::vector<CPatchInfo> &patchs= zoneInfo.Patchs;
|
|
std::vector<CBorderVertex> &borderVertices= zoneInfo.BorderVertices;
|
|
// Copy zoneId.
|
|
zoneInfo.ZoneId= getZoneId();
|
|
|
|
|
|
// uncompress Patchs.
|
|
//=========================
|
|
patchs.resize(Patchs.size());
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
CPatchInfo &pi= patchs[j];
|
|
CBezierPatch &p= pi.Patch;
|
|
CPatch &pa= Patchs[j];
|
|
CPatchConnect &pc= PatchConnects[j];
|
|
|
|
|
|
// Smoothing flags
|
|
pi.Flags= (pa.Flags&NL_PATCH_SMOOTH_FLAG_MASK)>>NL_PATCH_SMOOTH_FLAG_SHIFT;
|
|
|
|
|
|
// Noise Data
|
|
// copy noise rotation.
|
|
pi.NoiseRotation= pa.NoiseRotation;
|
|
// copy all noise smoothing info.
|
|
for(i=0;i<4;i++)
|
|
{
|
|
pi.setCornerSmoothFlag(i, pa.getCornerSmoothFlag(i));
|
|
}
|
|
|
|
|
|
// re-Build the uncompressed bezier patch.
|
|
for(i=0;i<4;i++)
|
|
pa.Vertices[i].unpack(p.Vertices[i], PatchBias, PatchScale);
|
|
for(i=0;i<8;i++)
|
|
pa.Tangents[i].unpack(p.Tangents[i], PatchBias, PatchScale);
|
|
for(i=0;i<4;i++)
|
|
pa.Interiors[i].unpack(p.Interiors[i], PatchBias, PatchScale);
|
|
pi.Tiles= pa.Tiles;
|
|
pi.TileColors= pa.TileColors;
|
|
pi.TileLightInfluences= pa.TileLightInfluences;
|
|
pi.Lumels.resize ((pa.OrderS*4)*(pa.OrderT*4));
|
|
pi.Flags=(pa.Flags&NL_PATCH_SMOOTH_FLAG_MASK)>>NL_PATCH_SMOOTH_FLAG_SHIFT;
|
|
|
|
// Unpack the lumel map
|
|
pa.unpackShadowMap (&pi.Lumels[0]);
|
|
|
|
// from the patchConnect.
|
|
pi.OrderS= pa.OrderS;
|
|
pi.OrderT= pa.OrderT;
|
|
pi.ErrorSize= pc.ErrorSize;
|
|
for(i=0;i<4;i++)
|
|
{
|
|
pi.BaseVertices[i]= pc.BaseVertices[i];
|
|
}
|
|
for(i=0;i<4;i++)
|
|
pi.BindEdges[i]= pc.BindEdges[i];
|
|
}
|
|
|
|
// retrieve bordervertices.
|
|
//=========================
|
|
borderVertices= BorderVertices;
|
|
|
|
// retrieve PointLights.
|
|
//=========================
|
|
zoneInfo.PointLights= _PointLightArray.getPointLights();
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::build(const CZone &zone)
|
|
{
|
|
nlassert(!Compiled);
|
|
|
|
ZoneId= zone.ZoneId;
|
|
BorderVertices= zone.BorderVertices;
|
|
|
|
// Compute the bbox and the bias/scale.
|
|
//=====================================
|
|
ZoneBB= zone.ZoneBB;
|
|
PatchScale= zone.PatchScale;
|
|
PatchBias= zone.PatchBias;
|
|
|
|
|
|
// Compute/compress Patchs.
|
|
//=========================
|
|
Patchs= zone.Patchs;
|
|
PatchConnects= zone.PatchConnects;
|
|
|
|
// Init the Clip Arrays
|
|
_PatchRenderClipped.resize((uint)Patchs.size());
|
|
_PatchOldRenderClipped.resize((uint)Patchs.size());
|
|
_PatchRenderClipped.setAll();
|
|
_PatchOldRenderClipped.setAll();
|
|
|
|
|
|
// copy pointLights.
|
|
//=========================
|
|
_PointLightArray= zone._PointLightArray;
|
|
|
|
|
|
NumVertices= zone.NumVertices;
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
void CBorderVertex::serial(NLMISC::IStream &f)
|
|
{
|
|
/* ***********************************************
|
|
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
|
|
* It can be loaded/called through CAsyncFileManager for instance
|
|
* ***********************************************/
|
|
(void)f.serialVersion(0);
|
|
|
|
f.xmlSerial (CurrentVertex, "CURRENT_VERTEX");
|
|
f.xmlSerial (NeighborZoneId, "NEIGHTBOR_ZONE_ID");
|
|
f.xmlSerial (NeighborVertex, "NEIGHTBOR_VERTEX");
|
|
}
|
|
void CZone::CPatchConnect::serial(NLMISC::IStream &f)
|
|
{
|
|
/* ***********************************************
|
|
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
|
|
* It can be loaded/called through CAsyncFileManager for instance
|
|
* ***********************************************/
|
|
uint ver= f.serialVersion(1);
|
|
|
|
if (ver<1)
|
|
f.serial(OldOrderS, OldOrderT, ErrorSize);
|
|
else
|
|
f.serial(ErrorSize);
|
|
f.xmlSerial (BaseVertices[0], BaseVertices[1], BaseVertices[2], BaseVertices[3], "BASE_VERTICES");
|
|
f.xmlSerial (BindEdges[0], BindEdges[1], BindEdges[2], BindEdges[3], "BIND_EDGES");
|
|
}
|
|
void CPatchInfo::CBindInfo::serial(NLMISC::IStream &f)
|
|
{
|
|
/* ***********************************************
|
|
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
|
|
* It can be loaded/called through CAsyncFileManager for instance
|
|
* ***********************************************/
|
|
(void)f.serialVersion(0);
|
|
f.xmlSerial(NPatchs, "NPATCH");
|
|
nlassert ( (NPatchs==0) | (NPatchs==1) | (NPatchs==2) | (NPatchs==4) | (NPatchs==5) );
|
|
f.xmlSerial (ZoneId, "ZONE_ID");
|
|
f.xmlSerial (Next[0], Next[1], Next[2], Next[3], "NEXT_PATCH");
|
|
f.xmlSerial (Edge[0], Edge[1], Edge[2], Edge[3], "NEXT_EDGE");
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::serial(NLMISC::IStream &f)
|
|
{
|
|
/* ***********************************************
|
|
* WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
|
|
* It can be loaded/called through CAsyncFileManager for instance
|
|
* ***********************************************/
|
|
/*
|
|
Version 4:
|
|
- PointLights
|
|
Version 3:
|
|
- Lumels compression version 2.
|
|
Version 2:
|
|
- Lumels.
|
|
Version 1:
|
|
- Tile color.
|
|
Version 0:
|
|
- base verison.
|
|
*/
|
|
uint ver= f.serialVersion(4);
|
|
|
|
// No more compatibility before version 3
|
|
if (ver<3)
|
|
{
|
|
throw EOlderStream(f);
|
|
}
|
|
|
|
f.serialCheck((uint32)'ENOZ');
|
|
|
|
f.xmlSerial (ZoneId, "ZONE_ID");
|
|
f.xmlSerial (ZoneBB, "BB");
|
|
f.xmlSerial (PatchBias, "PATCH_BIAS");
|
|
f.xmlSerial (PatchScale, "PATCH_SCALE");
|
|
f.xmlSerial (NumVertices, "NUM_VERTICES");
|
|
|
|
f.xmlPush ("BORDER_VERTICES");
|
|
f.serialCont(BorderVertices);
|
|
f.xmlPop ();
|
|
|
|
f.xmlPush ("PATCHES");
|
|
f.serialCont(Patchs);
|
|
f.xmlPop ();
|
|
|
|
f.xmlPush ("PATCH_CONNECTS");
|
|
f.serialCont(PatchConnects);
|
|
f.xmlPop ();
|
|
|
|
if (ver>=4)
|
|
{
|
|
f.xmlPush ("POINT_LIGHTS");
|
|
f.serial(_PointLightArray);
|
|
f.xmlPop ();
|
|
}
|
|
|
|
// If read, must create and init Patch Clipped state to true (clipped even if not compiled)
|
|
if(f.isReading())
|
|
{
|
|
_PatchRenderClipped.resize((uint)Patchs.size());
|
|
_PatchOldRenderClipped.resize((uint)Patchs.size());
|
|
_PatchRenderClipped.setAll();
|
|
_PatchOldRenderClipped.setAll();
|
|
}
|
|
|
|
// If read and version 0, must init default TileColors of patchs.
|
|
//===============================================================
|
|
// if(f.isReading() && ver<2) ...
|
|
// Deprecated, because ver<3 not supported
|
|
}
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::compile(CLandscape *landscape, TZoneMap &loadedZones)
|
|
{
|
|
sint i,j;
|
|
TZoneMap neighborZones;
|
|
|
|
//nlinfo("Compile Zone: %d \n", (sint32)getZoneId());
|
|
|
|
// Can't compile if compiled.
|
|
nlassert(!Compiled);
|
|
Landscape= landscape;
|
|
|
|
// Attach this to loadedZones.
|
|
//============================
|
|
nlassert(loadedZones.find(ZoneId)==loadedZones.end());
|
|
loadedZones[ZoneId]= this;
|
|
|
|
// Create/link the base vertices according to present neigbor zones.
|
|
//============================
|
|
BaseVertices.clear();
|
|
BaseVertices.resize(NumVertices);
|
|
// First try to link vertices to other.
|
|
for(i=0;i<(sint)BorderVertices.size();i++)
|
|
{
|
|
sint cur= BorderVertices[i].CurrentVertex;
|
|
sint vertto= BorderVertices[i].NeighborVertex;
|
|
sint zoneto= BorderVertices[i].NeighborZoneId;
|
|
nlassert(cur<NumVertices);
|
|
|
|
if(loadedZones.find(zoneto)!=loadedZones.end())
|
|
{
|
|
CZone *zone;
|
|
zone= (*loadedZones.find(zoneto)).second;
|
|
nlassert(zone!=this);
|
|
// insert the zone in the neigborood (if not done...).
|
|
neighborZones[zoneto]= zone;
|
|
// Doesn't matter if BaseVertices is already linked to another zone...
|
|
// This should be the same pointer in this case...
|
|
BaseVertices[cur]= zone->getBaseVertex(vertto);
|
|
}
|
|
}
|
|
// Else, create unbounded vertices.
|
|
for(i=0;i<(sint)BaseVertices.size();i++)
|
|
{
|
|
if(BaseVertices[i]==NULL)
|
|
{
|
|
BaseVertices[i]= new CTessBaseVertex;
|
|
}
|
|
}
|
|
|
|
|
|
// compile() the patchs.
|
|
//======================
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
CPatchConnect &pc= PatchConnects[j];
|
|
CTessVertex *baseVertices[4];
|
|
|
|
baseVertices[0]= &(BaseVertices[pc.BaseVertices[0]]->Vert);
|
|
baseVertices[1]= &(BaseVertices[pc.BaseVertices[1]]->Vert);
|
|
baseVertices[2]= &(BaseVertices[pc.BaseVertices[2]]->Vert);
|
|
baseVertices[3]= &(BaseVertices[pc.BaseVertices[3]]->Vert);
|
|
pa.compile(this, j, pa.OrderS, pa.OrderT, baseVertices, pc.ErrorSize);
|
|
};
|
|
|
|
// compile() the Clip information for the patchs.
|
|
//======================
|
|
_PatchBSpheres.resize(Patchs.size());
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
|
|
// Buil the BSPhere of the patch.
|
|
CAABBox bb= pa.buildBBox();
|
|
_PatchBSpheres[j].Center= bb.getCenter();
|
|
_PatchBSpheres[j].Radius= bb.getRadius();
|
|
}
|
|
|
|
// bind() the patchs. (after all compiled).
|
|
//===================
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
CPatchConnect &pc= PatchConnects[j];
|
|
|
|
// bind the patch. This is the original bind, not a rebind.
|
|
bindPatch(loadedZones, pa, pc, false);
|
|
}
|
|
|
|
|
|
// rebindBorder() on neighbor zones.
|
|
//==================================
|
|
ItZoneMap zoneIt;
|
|
// Traverse the neighborood.
|
|
for(zoneIt= neighborZones.begin(); zoneIt!=neighborZones.end(); zoneIt++)
|
|
{
|
|
(*zoneIt).second->rebindBorder(loadedZones);
|
|
}
|
|
|
|
// End!!
|
|
Compiled= true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::release(TZoneMap &loadedZones)
|
|
{
|
|
sint i,j;
|
|
|
|
if(!Compiled)
|
|
return;
|
|
|
|
// detach this zone to loadedZones.
|
|
//=================================
|
|
nlassert(loadedZones.find(ZoneId)!=loadedZones.end());
|
|
loadedZones.erase(ZoneId);
|
|
// It doesn't server to unbindPatch(), since patch is not binded to neigbors.
|
|
|
|
|
|
// unbind() the patchs.
|
|
//=====================
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
unbindPatch(pa);
|
|
}
|
|
|
|
|
|
// rebindBorder() on neighbor zones.
|
|
//==================================
|
|
// Build the nieghborood.
|
|
TZoneMap neighborZones;
|
|
for(i=0;i<(sint)BorderVertices.size();i++)
|
|
{
|
|
sint cur= BorderVertices[i].CurrentVertex;
|
|
sint zoneto= BorderVertices[i].NeighborZoneId;
|
|
nlassert(cur<NumVertices);
|
|
|
|
if(loadedZones.find(zoneto)!=loadedZones.end())
|
|
{
|
|
CZone *zone;
|
|
zone= (*loadedZones.find(zoneto)).second;
|
|
nlassert(zone!=this);
|
|
// insert the zone in the neigborood (if not done...).
|
|
neighborZones[zoneto]= zone;
|
|
}
|
|
}
|
|
// rebind borders.
|
|
ItZoneMap zoneIt;
|
|
// Traverse the neighborood.
|
|
for(zoneIt= neighborZones.begin(); zoneIt!=neighborZones.end(); zoneIt++)
|
|
{
|
|
// Since
|
|
(*zoneIt).second->rebindBorder(loadedZones);
|
|
}
|
|
|
|
|
|
// release() the patchs.
|
|
//======================
|
|
// unbind() need compiled neigbor patchs, so do the release after all unbind (so after rebindBorder() too...).
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
pa.release();
|
|
}
|
|
|
|
|
|
// destroy/unlink the base vertices (internal..), according to present neigbor zones.
|
|
//=================================
|
|
// Just release the smartptrs (easy!!). Do it after patchs released...
|
|
BaseVertices.clear();
|
|
|
|
|
|
// End!!
|
|
Compiled= false;
|
|
Landscape= NULL;
|
|
ClipResult= ClipOut;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Private part.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::rebindBorder(TZoneMap &loadedZones)
|
|
{
|
|
sint j;
|
|
|
|
// rebind patchs which are on border.
|
|
for(j=0;j<(sint)Patchs.size();j++)
|
|
{
|
|
CPatch &pa= Patchs[j];
|
|
CPatchConnect &pc= PatchConnects[j];
|
|
|
|
if(patchOnBorder(pc))
|
|
{
|
|
// rebind the patch. This is a rebind.
|
|
bindPatch(loadedZones, pa, pc, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CPatch *CZone::getZonePatch(TZoneMap &loadedZones, sint zoneId, sint patch)
|
|
{
|
|
#ifdef NL3D_DEBUG_DONT_BIND_PATCH
|
|
return NULL;
|
|
#endif
|
|
if(loadedZones.find(zoneId)==loadedZones.end())
|
|
return NULL;
|
|
else
|
|
return (loadedZones[zoneId])->getPatch(patch);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::buildBindInfo(uint patchId, uint edge, CZone *neighborZone, CPatch::CBindInfo &paBind)
|
|
{
|
|
nlassert(patchId < Patchs.size());
|
|
nlassert(neighborZone);
|
|
|
|
CPatchConnect &pc= PatchConnects[patchId];
|
|
|
|
|
|
// Get the bind info of this patch to his neighbor on "edge".
|
|
CPatchInfo::CBindInfo &pcBind= pc.BindEdges[edge];
|
|
nlassert(pcBind.NPatchs==0 || pcBind.NPatchs==1 || pcBind.NPatchs==2 || pcBind.NPatchs==4 || pcBind.NPatchs==5);
|
|
|
|
|
|
// copy zone ptr.
|
|
paBind.Zone= neighborZone;
|
|
|
|
|
|
// Special case of a small patch connected to a bigger.
|
|
if(pcBind.NPatchs==5)
|
|
{
|
|
paBind.NPatchs= 1;
|
|
paBind.Next[0]= neighborZone->getPatch(pcBind.Next[0]);
|
|
paBind.Edge[0]= pcBind.Edge[0];
|
|
|
|
// Get the twin bindInfo of pcBind.
|
|
const CPatchInfo::CBindInfo &pcBindNeighbor=
|
|
neighborZone->getPatchConnect(pcBind.Next[0])->BindEdges[pcBind.Edge[0]];
|
|
// must have a multiple bind.
|
|
nlassert(pcBindNeighbor.NPatchs == 2 || pcBindNeighbor.NPatchs == 4);
|
|
|
|
// number of bind is stored on the twin bindInfo.
|
|
paBind.MultipleBindNum= pcBindNeighbor.NPatchs;
|
|
|
|
// Search our patchId on neighbor;
|
|
paBind.MultipleBindId= 255;
|
|
for(sint i=0; i<paBind.MultipleBindNum; i++)
|
|
{
|
|
if(pcBindNeighbor.Next[i]==patchId)
|
|
paBind.MultipleBindId= i;
|
|
}
|
|
nlassert(paBind.MultipleBindId!= 255);
|
|
}
|
|
else
|
|
{
|
|
paBind.MultipleBindNum= 0;
|
|
paBind.NPatchs= pcBind.NPatchs;
|
|
for(sint i=0;i<paBind.NPatchs; i++)
|
|
{
|
|
paBind.Next[i]= neighborZone->getPatch(pcBind.Next[i]);
|
|
paBind.Edge[i]= pcBind.Edge[i];
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::bindPatch(TZoneMap &loadedZones, CPatch &pa, CPatchConnect &pc, bool rebind)
|
|
{
|
|
CPatch::CBindInfo edges[4];
|
|
|
|
// Fill all edges.
|
|
for(sint i=0;i<4;i++)
|
|
{
|
|
CPatchInfo::CBindInfo &pcBind= pc.BindEdges[i];
|
|
CPatch::CBindInfo &paBind= edges[i];
|
|
|
|
nlassert(pcBind.NPatchs==0 || pcBind.NPatchs==1 || pcBind.NPatchs==2 || pcBind.NPatchs==4 || pcBind.NPatchs==5);
|
|
paBind.NPatchs= pcBind.NPatchs;
|
|
|
|
|
|
// Find the zone.
|
|
TZoneMap::iterator itZoneMap;
|
|
// If no neighbor, or if zone neighbor not loaded.
|
|
if( paBind.NPatchs==0 || (itZoneMap=loadedZones.find(pcBind.ZoneId)) == loadedZones.end() )
|
|
paBind.Zone= NULL;
|
|
else
|
|
paBind.Zone= itZoneMap->second;
|
|
|
|
|
|
// Special case of a small patch connected to a bigger.
|
|
if(paBind.NPatchs==5)
|
|
{
|
|
paBind.Edge[0]= pcBind.Edge[0];
|
|
paBind.Next[0]= CZone::getZonePatch(loadedZones, pcBind.ZoneId, pcBind.Next[0]);
|
|
// If not loaded, don't bind to this edge.
|
|
if(!paBind.Next[0])
|
|
paBind.NPatchs=0;
|
|
else
|
|
{
|
|
// pa.bind() will do the job.
|
|
// Leave it flagged with NPatchs==5.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
// Bind 1/1 and 1/2,1/4
|
|
if(paBind.NPatchs>=1)
|
|
{
|
|
paBind.Edge[0]= pcBind.Edge[0];
|
|
paBind.Next[0]= CZone::getZonePatch(loadedZones, pcBind.ZoneId, pcBind.Next[0]);
|
|
// If not loaded, don't bind to this edge.
|
|
if(!paBind.Next[0])
|
|
paBind.NPatchs=0;
|
|
}
|
|
if(paBind.NPatchs>=2)
|
|
{
|
|
paBind.Edge[1]= pcBind.Edge[1];
|
|
paBind.Next[1]= CZone::getZonePatch(loadedZones, pcBind.ZoneId, pcBind.Next[1]);
|
|
// If not loaded, don't bind to this edge.
|
|
if(!paBind.Next[1])
|
|
paBind.NPatchs=0;
|
|
}
|
|
if(paBind.NPatchs>=4)
|
|
{
|
|
paBind.Edge[2]= pcBind.Edge[2];
|
|
paBind.Edge[3]= pcBind.Edge[3];
|
|
paBind.Next[2]= CZone::getZonePatch(loadedZones, pcBind.ZoneId, pcBind.Next[2]);
|
|
paBind.Next[3]= CZone::getZonePatch(loadedZones, pcBind.ZoneId, pcBind.Next[3]);
|
|
// If not loaded, don't bind to this edge.
|
|
if(!paBind.Next[2] || !paBind.Next[3])
|
|
paBind.NPatchs=0;
|
|
}
|
|
}
|
|
|
|
// First, unbind.
|
|
pa.unbind();
|
|
|
|
// Then bind.
|
|
pa.bind(edges, rebind);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::unbindPatch(CPatch &pa)
|
|
{
|
|
/*
|
|
Remind: the old version with CPatch::unbindFrom*() doesn't work because of CZone::release(). This function
|
|
first erase the zone from loadedZones...
|
|
Not matter here. We use CPatch::unbind() which should do all the good job correctly (unbind pa from ohters
|
|
, and unbind others from pa at same time).
|
|
*/
|
|
|
|
pa.unbind();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CZone::patchOnBorder(const CPatchConnect &pc) const
|
|
{
|
|
// If only one of neighbor patch is not of this zone, we are on a border.
|
|
|
|
// Test all edges.
|
|
for(sint i=0;i<4;i++)
|
|
{
|
|
const CPatchInfo::CBindInfo &pcBind= pc.BindEdges[i];
|
|
|
|
nlassert(pcBind.NPatchs==0 || pcBind.NPatchs==1 || pcBind.NPatchs==2 || pcBind.NPatchs==4 || pcBind.NPatchs==5);
|
|
if(pcBind.NPatchs>=1)
|
|
{
|
|
if(pcBind.ZoneId != ZoneId)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Render part.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
const CBSphere &CZone::getPatchBSphere(uint patch) const
|
|
{
|
|
static CBSphere dummySphere;
|
|
if(patch<_PatchBSpheres.size())
|
|
return _PatchBSpheres[patch];
|
|
else
|
|
return dummySphere;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::clip(const std::vector<CPlane> &pyramid)
|
|
{
|
|
H_AUTO( NLMISC_ClipZone );
|
|
|
|
nlassert(Compiled);
|
|
|
|
// bkup old ClipResult. NB: by default, it is ClipOut (no VB created).
|
|
sint oldClipResult= ClipResult;
|
|
|
|
// Pyramid with only the planes that clip the zone
|
|
static std::vector<CPlane> patchPyramid(10);
|
|
static std::vector<uint> patchPyramidIndex(10);
|
|
patchPyramidIndex.clear();
|
|
|
|
// Compute ClipResult.
|
|
//-------------------
|
|
ClipResult= ClipIn;
|
|
for(sint i=0;i<(sint)pyramid.size();i++)
|
|
{
|
|
// If entirely out.
|
|
if(!ZoneBB.clipBack(pyramid[i]))
|
|
{
|
|
ClipResult= ClipOut;
|
|
// If out of only one plane, out of all.
|
|
break;
|
|
}
|
|
// If partially IN (ie not entirely out, and not entirely IN)
|
|
else if(ZoneBB.clipFront(pyramid[i]))
|
|
{
|
|
// Force ClipResult to be ClipSide, and not ClipIn.
|
|
ClipResult=ClipSide;
|
|
// Append the plane index to list to test
|
|
patchPyramidIndex.push_back(i);
|
|
}
|
|
}
|
|
|
|
|
|
// Easy Clip :)
|
|
if(Patchs.empty())
|
|
{
|
|
ClipResult= ClipOut;
|
|
// don't need to go below...
|
|
return;
|
|
}
|
|
|
|
|
|
// Clip By Patch Pass.
|
|
//--------------------
|
|
if(ClipResult==ClipOut)
|
|
{
|
|
H_AUTO( NLMISC_ClipZone_Out );
|
|
|
|
// Set All RenderClip flags to true.
|
|
_PatchRenderClipped.setAll();
|
|
}
|
|
else if(ClipResult==ClipIn)
|
|
{
|
|
H_AUTO( NLMISC_ClipZone_In );
|
|
|
|
// Set All RenderClip flags to false.
|
|
_PatchRenderClipped.clearAll();
|
|
}
|
|
else
|
|
{
|
|
H_AUTO( NLMISC_ClipZone_Side );
|
|
|
|
// Copy only the pyramid planes of interest
|
|
patchPyramid.resize(patchPyramidIndex.size());
|
|
uint i;
|
|
for(i=0;i<patchPyramidIndex.size();i++)
|
|
{
|
|
patchPyramid[i]= pyramid[patchPyramidIndex[i]];
|
|
}
|
|
|
|
// clip all patchs with the simplified pyramid
|
|
clipPatchs(patchPyramid);
|
|
}
|
|
|
|
|
|
// delete / reallocate / fill VBuffers.
|
|
//-------------------
|
|
// If there is a change in the Clip of the zone, or if patchs may have change (ie ClipSide is undetermined).
|
|
if(oldClipResult!=ClipResult || oldClipResult==ClipSide)
|
|
{
|
|
// get BitSet as Raw Array of uint32
|
|
uint32 *oldRenderClip= const_cast<uint32*>(&_PatchOldRenderClipped.getVector()[0]);
|
|
const uint32 *newRenderClip= &_PatchRenderClipped.getVector()[0];
|
|
uint numPatchs= (uint)Patchs.size();
|
|
// Then, we must test by patch.
|
|
for(uint i=0;i<numPatchs;oldRenderClip++, newRenderClip++)
|
|
{
|
|
uint32 oldWord= *oldRenderClip;
|
|
uint32 newWord= *newRenderClip;
|
|
// process at max 32 patch
|
|
uint maxNumBits= min((numPatchs-i), 32U);
|
|
uint32 mask= 1;
|
|
for(;maxNumBits>0;maxNumBits--, mask<<=1, i++)
|
|
{
|
|
// same as: if(_PatchOldRenderClipped[i] != _PatchRenderClipped[i])
|
|
if( (oldWord^newWord)&mask )
|
|
{
|
|
// set the flag.
|
|
*oldRenderClip&= ~mask;
|
|
*oldRenderClip|= newWord&mask;
|
|
// update clip patch
|
|
Patchs[i].updateClipPatchVB( (newWord&mask)!=0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::clipPatchs(const std::vector<CPlane> &pyramid)
|
|
{
|
|
// Init all to Not clipped
|
|
_PatchRenderClipped.clearAll();
|
|
|
|
for(uint j=0;j<_PatchBSpheres.size();j++)
|
|
{
|
|
CBSphere &bSphere= _PatchBSpheres[j];
|
|
for(sint i=0;i<(sint)pyramid.size();i++)
|
|
{
|
|
// If entirely out.
|
|
if(!bSphere.clipBack(pyramid[i]))
|
|
{
|
|
_PatchRenderClipped.set(j, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// DebugYoyo.
|
|
// Code for Debug test Only.. Do not erase it, may be used later :)
|
|
/*
|
|
static void cleanTess(CTessFace *face)
|
|
{
|
|
if(!face->isLeaf())
|
|
{
|
|
cleanTess(face->SonLeft);
|
|
cleanTess(face->SonRight);
|
|
}
|
|
// If has father, clean it.
|
|
if(face->Father)
|
|
{
|
|
CTessFace *face1=face->Father;
|
|
CTessFace *face2=face->Father->FBase;
|
|
face1->FLeft= face1->SonLeft->FBase;
|
|
face1->FRight= face1->SonRight->FBase;
|
|
if(face2!=NULL)
|
|
{
|
|
face2->FLeft= face2->SonLeft->FBase;
|
|
face2->FRight= face2->SonRight->FBase;
|
|
}
|
|
}
|
|
}
|
|
static void testTess(CTessFace *face)
|
|
{
|
|
if(!face->isLeaf())
|
|
{
|
|
testTess(face->SonLeft);
|
|
testTess(face->SonRight);
|
|
}
|
|
// Test validity.
|
|
nlassert(!face->FBase || face->FBase->Patch!=(CPatch*)0xdddddddd);
|
|
nlassert(!face->FLeft || face->FLeft->Patch!=(CPatch*)0xdddddddd);
|
|
nlassert(!face->FRight || face->FRight->Patch!=(CPatch*)0xdddddddd);
|
|
}
|
|
static void checkTess()
|
|
{
|
|
// This test should be inserted at begin of CZone::refine().
|
|
// And it needs hacking public/private.
|
|
CPatch *pPatch;
|
|
sint n;
|
|
pPatch= &(*Patchs.begin());
|
|
for(n=(sint)Patchs.size();n>0;n--, pPatch++)
|
|
{
|
|
cleanTess(pPatch->Son0);
|
|
cleanTess(pPatch->Son1);
|
|
}
|
|
pPatch= &(*Patchs.begin());
|
|
for(n=(sint)Patchs.size();n>0;n--, pPatch++)
|
|
{
|
|
testTess(pPatch->Son0);
|
|
testTess(pPatch->Son1);
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::excludePatchFromRefineAll(uint patch, bool exclude)
|
|
{
|
|
nlassert(Compiled);
|
|
nlassert(patch<Patchs.size());
|
|
|
|
if(patch>=Patchs.size())
|
|
return;
|
|
|
|
Patchs[patch].ExcludeFromRefineAll= exclude;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::refineAll()
|
|
{
|
|
nlassert(Compiled);
|
|
|
|
if(Patchs.size()==0)
|
|
return;
|
|
|
|
// DO NOT do a forceNoRenderClip(), to avoid big allocation of Near/Far VB vertices in driver.
|
|
// DO NOT modify ClipResult, to avoid big allocation of Near/Far VB vertices in driver.
|
|
|
|
// refine ALL patchs (even those which may be invisible).
|
|
CPatch *pPatch= &(*Patchs.begin());
|
|
sint n;
|
|
for(n=(sint)Patchs.size();n>0;n--, pPatch++)
|
|
{
|
|
// For Pacs construction: may exclude some patch from refineAll (for speed improvement).
|
|
if(!pPatch->ExcludeFromRefineAll)
|
|
pPatch->refineAll();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::averageTesselationVertices()
|
|
{
|
|
nlassert(Compiled);
|
|
|
|
if(Patchs.size()==0)
|
|
return;
|
|
|
|
// averageTesselationVertices of ALL patchs.
|
|
CPatch *pPatch= &(*Patchs.begin());
|
|
for(sint n=(sint)Patchs.size();n>0;n--, pPatch++)
|
|
{
|
|
pPatch->averageTesselationVertices();
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::preRender()
|
|
{
|
|
nlassert(Compiled);
|
|
|
|
// Must be 2^X-1.
|
|
static const uint updateFarRefineFreq= 15;
|
|
// Take the renderDate here.
|
|
uint curDateMod= CLandscapeGlobals::CurrentRenderDate & updateFarRefineFreq;
|
|
|
|
// If no patchs, do nothing.
|
|
if(Patchs.empty())
|
|
return;
|
|
|
|
/* If patchs invisible, must still update their Far Textures,
|
|
else, there may be slowdown when we turn the head.
|
|
*/
|
|
|
|
|
|
// If all the zone is invisible.
|
|
if(ClipResult==ClipOut)
|
|
{
|
|
// No patchs are visible, but maybe update the far textures.
|
|
if( curDateMod==(ZoneId & updateFarRefineFreq) )
|
|
{
|
|
// updateTextureFarOnly for all patchs.
|
|
for(uint i=0;i<Patchs.size();i++)
|
|
{
|
|
Patchs[i].updateTextureFarOnly(_PatchBSpheres[i]);
|
|
}
|
|
}
|
|
}
|
|
// else If some patchs only are visible.
|
|
else if(ClipResult==ClipSide)
|
|
{
|
|
// PreRender Pass, or updateTextureFarOnly(), according to _PatchRenderClipped state.
|
|
for(uint i=0;i<Patchs.size();i++)
|
|
{
|
|
// If the patch is visible
|
|
if(!_PatchRenderClipped[i])
|
|
{
|
|
// Then preRender it.
|
|
Patchs[i].preRender(_PatchBSpheres[i]);
|
|
}
|
|
else
|
|
{
|
|
// else maybe updateFar it.
|
|
// ZoneId+i for better repartition.
|
|
if( curDateMod==((ZoneId+i) & updateFarRefineFreq) )
|
|
Patchs[i].updateTextureFarOnly(_PatchBSpheres[i]);
|
|
}
|
|
}
|
|
}
|
|
else // ClipResult==ClipIn
|
|
{
|
|
// PreRender Pass for All
|
|
for(uint i=0;i<Patchs.size();i++)
|
|
{
|
|
Patchs[i].preRender(_PatchBSpheres[i]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::resetRenderFarAndDeleteVBFV()
|
|
{
|
|
for(uint i=0;i<Patchs.size();i++)
|
|
{
|
|
// If patch is visible
|
|
if(!_PatchRenderClipped[i])
|
|
{
|
|
// release VertexBuffer, and FaceBuffer
|
|
Patchs[i].deleteVBAndFaceVector();
|
|
// Flag.
|
|
_PatchRenderClipped.set(i, true);
|
|
}
|
|
|
|
Patchs[i].resetRenderFar();
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::forceMergeAtTileLevel()
|
|
{
|
|
CPatch *pPatch=0;
|
|
if(Patchs.size()>0)
|
|
pPatch= &(*Patchs.begin());
|
|
for(sint n=(sint)Patchs.size();n>0;n--, pPatch++)
|
|
{
|
|
pPatch->forceMergeAtTileLevel();
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// Misc part.
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::changePatchTextureAndColor (sint numPatch, const std::vector<CTileElement> *tiles, const std::vector<CTileColor> *colors)
|
|
{
|
|
nlassert(numPatch>=0);
|
|
nlassert(numPatch<getNumPatchs());
|
|
|
|
|
|
// Update the patch texture.
|
|
if (tiles)
|
|
{
|
|
nlassert( Patchs[numPatch].Tiles.size() == tiles->size() );
|
|
Patchs[numPatch].Tiles = *tiles;
|
|
}
|
|
|
|
// Update the patch colors.
|
|
if (colors)
|
|
{
|
|
nlassert( Patchs[numPatch].TileColors.size() == colors->size() );
|
|
Patchs[numPatch].TileColors = *colors;
|
|
}
|
|
|
|
if (Compiled)
|
|
{
|
|
// If the patch is visible, then we must LockBuffers, because new VertexVB may be created.
|
|
if(!_PatchRenderClipped[numPatch])
|
|
Landscape->updateGlobalsAndLockBuffers(CVector::Null);
|
|
|
|
// Recompute UVs for new setup of Tiles.
|
|
Patchs[numPatch].deleteTileUvs();
|
|
Patchs[numPatch].recreateTileUvs();
|
|
|
|
// unlockBuffers() if necessary.
|
|
if(!_PatchRenderClipped[numPatch])
|
|
{
|
|
Landscape->unlockBuffers();
|
|
// This patch is visible, and TileFaces have been deleted / added.
|
|
// So must update TessBlock.
|
|
Landscape->updateTessBlocksFaceVector();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::refreshTesselationGeometry(sint numPatch)
|
|
{
|
|
nlassert(numPatch>=0);
|
|
nlassert(numPatch<getNumPatchs());
|
|
nlassert(Compiled);
|
|
|
|
// At next render, we must re-fill the entire unclipped VB, so change are taken into account.
|
|
Landscape->_RenderMustRefillVB= true;
|
|
|
|
Patchs[numPatch].refreshTesselationGeometry();
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const std::vector<CTileElement> &CZone::getPatchTexture(sint numPatch) const
|
|
{
|
|
nlassert(numPatch>=0);
|
|
nlassert(numPatch<getNumPatchs());
|
|
|
|
// Update the patch texture.
|
|
return Patchs[numPatch].Tiles;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
const std::vector<CTileColor> &CZone::getPatchColor(sint numPatch) const
|
|
{
|
|
nlassert(numPatch>=0);
|
|
nlassert(numPatch<getNumPatchs());
|
|
|
|
// Update the patch texture.
|
|
return Patchs[numPatch].TileColors;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::setTileColor(bool monochrome, float factor)
|
|
{
|
|
nlassert(factor >= 0.0f); // factor must not be negative as its a multiplier
|
|
|
|
if (monochrome)
|
|
{
|
|
for (uint32 i = 0; i < Patchs.size(); ++i)
|
|
{
|
|
vector<CTileColor> &rTC = Patchs[i].TileColors;
|
|
for (uint32 j = 0; j < rTC.size(); ++j)
|
|
{
|
|
float fR = (rTC[j].Color565 & 31) / 32.0f;
|
|
float fG = ((rTC[j].Color565 >> 5) & 63) / 64.0f;
|
|
float fB = ((rTC[j].Color565 >> 11) & 31) / 32.0f;
|
|
|
|
fR = 0.28f * fR + 0.59f * fG + 0.13f * fB;
|
|
|
|
nlassert(fR < 0.99f);
|
|
|
|
fR *= factor;
|
|
if (fR > 0.99f) fR = 0.99f; // Avoid reaching 1
|
|
|
|
uint16 nR = (uint16)(fR * 32.0f);
|
|
uint16 nG = (uint16)(fR * 64.0f);
|
|
uint16 nB = (uint16)(fR * 32.0f);
|
|
|
|
rTC[j].Color565 = nR + (nG << 5) + (nB << 11);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (factor != 1.0f)
|
|
{
|
|
for (uint32 i = 0; i < Patchs.size(); ++i)
|
|
{
|
|
vector<CTileColor> &rTC = Patchs[i].TileColors;
|
|
for (uint32 j = 0; j < rTC.size(); ++j)
|
|
{
|
|
float fR = (rTC[j].Color565 & 31) / 32.0f;
|
|
float fG = ((rTC[j].Color565 >> 5) & 63) / 64.0f;
|
|
float fB = ((rTC[j].Color565 >> 11) & 31) / 32.0f;
|
|
|
|
fR *= factor;
|
|
fG *= factor;
|
|
fB *= factor;
|
|
|
|
if (fR > 0.99f) fR = 0.99f;
|
|
if (fG > 0.99f) fG = 0.99f;
|
|
if (fB > 0.99f) fB = 0.99f;
|
|
|
|
uint16 nR = (uint16)(fR * 32.0f);
|
|
uint16 nG = (uint16)(fG * 64.0f);
|
|
uint16 nB = (uint16)(fB * 32.0f);
|
|
|
|
rTC[j].Color565 = nR + (nG << 5) + (nB << 11);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::debugBinds(FILE *f)
|
|
{
|
|
fprintf(f, "*****************************\n");
|
|
fprintf(f, "ZoneId: %d. NPatchs:%u\n", ZoneId, (uint)PatchConnects.size());
|
|
sint i;
|
|
for(i=0;i<(sint)PatchConnects.size();i++)
|
|
{
|
|
CPatchConnect &pc= PatchConnects[i];
|
|
fprintf(f, "patch%d:\n", i);
|
|
for(sint j=0;j<4;j++)
|
|
{
|
|
CPatchInfo::CBindInfo &bd= pc.BindEdges[j];
|
|
fprintf(f, " edge%d: Zone:%u. NPatchs:%u. ", j, (uint)bd.ZoneId, (uint)bd.NPatchs);
|
|
for(sint k=0;k<bd.NPatchs;k++)
|
|
{
|
|
fprintf(f, "p%ue%u - ", (uint)bd.Next[k], (uint)bd.Edge[k]);
|
|
}
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
|
|
fprintf(f,"Vertices :\n");
|
|
for(i=0;i<(sint)BorderVertices.size();i++)
|
|
{
|
|
fprintf(f,"current : %u -> (zone %u) vertex %u\n", (uint)BorderVertices[i].CurrentVertex,
|
|
(uint)BorderVertices[i].NeighborZoneId,
|
|
(uint)BorderVertices[i].NeighborVertex);
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::applyHeightField(const CLandscape &landScape)
|
|
{
|
|
sint i,j;
|
|
vector<CBezierPatch> patchs;
|
|
|
|
// no patch, do nothing.
|
|
if(Patchs.size()==0)
|
|
return;
|
|
|
|
// 0. Unpack patchs to Bezier Patchs.
|
|
//===================================
|
|
patchs.resize(Patchs.size());
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
CBezierPatch &p= patchs[j];
|
|
CPatch &pa= Patchs[j];
|
|
|
|
// re-Build the uncompressed bezier patch.
|
|
for(i=0;i<4;i++)
|
|
pa.Vertices[i].unpack(p.Vertices[i], PatchBias, PatchScale);
|
|
for(i=0;i<8;i++)
|
|
pa.Tangents[i].unpack(p.Tangents[i], PatchBias, PatchScale);
|
|
for(i=0;i<4;i++)
|
|
pa.Interiors[i].unpack(p.Interiors[i], PatchBias, PatchScale);
|
|
}
|
|
|
|
// 1. apply heightfield on bezier patchs.
|
|
//===================================
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
CBezierPatch &p= patchs[j];
|
|
|
|
// apply delta.
|
|
for(i=0;i<4;i++)
|
|
p.Vertices[i]+= landScape.getHeightFieldDeltaZ(p.Vertices[i].x, p.Vertices[i].y);
|
|
for(i=0;i<8;i++)
|
|
p.Tangents[i]+= landScape.getHeightFieldDeltaZ(p.Tangents[i].x, p.Tangents[i].y);
|
|
for(i=0;i<4;i++)
|
|
p.Interiors[i]+= landScape.getHeightFieldDeltaZ(p.Interiors[i].x, p.Interiors[i].y);
|
|
}
|
|
|
|
|
|
// 2. Re-compute Patch Scale/Bias, and Zone BBox.
|
|
//===================================
|
|
CAABBox bb;
|
|
bb.setCenter(patchs[0].Vertices[0]);
|
|
bb.setHalfSize(CVector::Null);
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
// extend bbox.
|
|
const CBezierPatch &p= patchs[j];
|
|
for(i=0;i<4;i++)
|
|
bb.extend(p.Vertices[i]);
|
|
for(i=0;i<8;i++)
|
|
bb.extend(p.Tangents[i]);
|
|
for(i=0;i<4;i++)
|
|
bb.extend(p.Interiors[i]);
|
|
}
|
|
// Compute BBox, and Patch Scale Bias, according to Noise.
|
|
computeBBScaleBias(bb);
|
|
|
|
|
|
// 3. Re-pack patchs.
|
|
//===================================
|
|
for(j=0;j<(sint)patchs.size();j++)
|
|
{
|
|
CBezierPatch &p= patchs[j];
|
|
CPatch &pa= Patchs[j];
|
|
|
|
// Build the packed patch.
|
|
for(i=0;i<4;i++)
|
|
pa.Vertices[i].pack(p.Vertices[i], PatchBias, PatchScale);
|
|
for(i=0;i<8;i++)
|
|
pa.Tangents[i].pack(p.Tangents[i], PatchBias, PatchScale);
|
|
for(i=0;i<4;i++)
|
|
pa.Interiors[i].pack(p.Interiors[i], PatchBias, PatchScale);
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CZone::setupColorsFromTileFlags(const NLMISC::CRGBA colors[4])
|
|
{
|
|
for (uint k = 0; k < Patchs.size(); ++k)
|
|
{
|
|
Patchs[k].setupColorsFromTileFlags(colors);
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void CZone::copyTilesFlags(sint destPatchId, const CPatch *srcPatch)
|
|
{
|
|
CPatch *destPatch = getPatch(destPatchId);
|
|
|
|
destPatch->copyTileFlagsFromPatch(srcPatch);
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
bool CPatchInfo::getNeighborTile (uint patchId, uint edge, sint position, uint &patchOut, sint &sOut, sint &tOut,
|
|
const vector<CPatchInfo> &patchInfos) const
|
|
{
|
|
nlassert (edge<4);
|
|
|
|
// S or T ?
|
|
uint length = (edge&1) ? OrderS : OrderT;
|
|
nlassert ((uint)position<length);
|
|
|
|
// What kind of case ?
|
|
switch (BindEdges[edge].NPatchs)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
{
|
|
// Get neighbor index and position in neighbor
|
|
uint neighborLength = (length / BindEdges[edge].NPatchs);
|
|
uint neighbor = position / neighborLength;
|
|
uint neighborPosition = neighborLength - (position % neighborLength) - 1;
|
|
uint neighborEdge = BindEdges[edge].Edge[neighbor];
|
|
|
|
// Patch id
|
|
patchOut = BindEdges[edge].Next[neighbor];
|
|
|
|
// Check neighbor
|
|
uint neighborRealLength = (neighborEdge&1) ? patchInfos[patchOut].OrderS : patchInfos[patchOut].OrderT;
|
|
if (neighborRealLength == neighborLength)
|
|
{
|
|
// Get final coordinate
|
|
switch (neighborEdge)
|
|
{
|
|
case 0:
|
|
sOut = 0;
|
|
tOut = neighborPosition;
|
|
break;
|
|
case 1:
|
|
sOut = neighborPosition;
|
|
tOut = patchInfos[patchOut].OrderT-1;
|
|
break;
|
|
case 2:
|
|
sOut = patchInfos[patchOut].OrderS-1;
|
|
tOut = patchInfos[patchOut].OrderT-neighborPosition-1;
|
|
break;
|
|
case 3:
|
|
sOut = patchInfos[patchOut].OrderS-neighborPosition-1;
|
|
tOut = 0;
|
|
break;
|
|
}
|
|
|
|
// Ok todo remove
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
{
|
|
// Find in the neighbor where we are
|
|
patchOut = BindEdges[edge].Next[0];
|
|
uint neighborEdge = BindEdges[edge].Edge[0];
|
|
uint neighborEdgeCount = patchInfos[patchOut].BindEdges[neighborEdge].NPatchs;
|
|
|
|
// Check neighbor
|
|
uint neighborRealLength = (neighborEdge&1) ? patchInfos[patchOut].OrderS : patchInfos[patchOut].OrderT;
|
|
|
|
// Good length ?
|
|
if ((neighborRealLength / neighborEdgeCount) == length)
|
|
{
|
|
// Find us in the neighbor
|
|
uint neighborPosition;
|
|
for (neighborPosition=0; neighborPosition<neighborEdgeCount; neighborPosition++)
|
|
{
|
|
// Found ?
|
|
if (patchInfos[patchOut].BindEdges[neighborEdge].Next[neighborPosition] == patchId)
|
|
break;
|
|
}
|
|
|
|
// Must be found
|
|
nlassert (neighborPosition!=neighborEdgeCount);
|
|
neighborPosition = (neighborPosition + 1) * (neighborRealLength / neighborEdgeCount) - position - 1;
|
|
|
|
// Get final coordinate
|
|
switch (neighborEdge)
|
|
{
|
|
case 0:
|
|
sOut = 0;
|
|
tOut = neighborPosition;
|
|
break;
|
|
case 1:
|
|
sOut = neighborPosition;
|
|
tOut = patchInfos[patchOut].OrderT-1;
|
|
break;
|
|
case 2:
|
|
sOut = patchInfos[patchOut].OrderS-1;
|
|
tOut = patchInfos[patchOut].OrderT-neighborPosition-1;
|
|
break;
|
|
case 3:
|
|
sOut = patchInfos[patchOut].OrderS-neighborPosition-1;
|
|
tOut = 0;
|
|
break;
|
|
}
|
|
|
|
// Ok
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CPatchInfo::getTileSymmetryRotate (const CTileBank &bank, uint tile, bool &symmetry, uint &rotate)
|
|
{
|
|
// Need check the tile ?
|
|
if ( (symmetry || (rotate != 0)) && (tile != 0xffffffff) )
|
|
{
|
|
// Tile exist ?
|
|
if (tile < (uint)bank.getTileCount())
|
|
{
|
|
// Get xref
|
|
int tileSet;
|
|
int number;
|
|
CTileBank::TTileType type;
|
|
|
|
// Get tile xref
|
|
bank.getTileXRef ((int)tile, tileSet, number, type);
|
|
|
|
if ((tileSet < 0) || (tileSet >= bank.getTileSetCount()))
|
|
{
|
|
nlwarning("tile %d has an unknown tileSet (%d)",tile, tileSet);
|
|
return false;
|
|
}
|
|
|
|
// Is it an oriented tile ?
|
|
if (bank.getTileSet (tileSet)->getOriented())
|
|
{
|
|
// New rotation value
|
|
rotate = 0;
|
|
}
|
|
|
|
// Ok
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
bool CPatchInfo::transformTile (const CTileBank &bank, uint &tile, uint &tileRotation, bool symmetry, uint rotate, bool goofy)
|
|
{
|
|
// Tile exist ?
|
|
if ( (rotate!=0) || symmetry )
|
|
{
|
|
if (tile < (uint)bank.getTileCount())
|
|
{
|
|
// Get xref
|
|
int tileSet;
|
|
int number;
|
|
CTileBank::TTileType type;
|
|
|
|
// Get tile xref
|
|
bank.getTileXRef ((int)tile, tileSet, number, type);
|
|
|
|
// Transition ?
|
|
if (type == CTileBank::transition)
|
|
{
|
|
// Rotation for transition
|
|
uint transRotate = rotate;
|
|
|
|
// Number should be ok
|
|
nlassert (number>=0);
|
|
nlassert (number<CTileSet::count);
|
|
|
|
// Tlie set number
|
|
const CTileSet *pTileSet = bank.getTileSet (tileSet);
|
|
|
|
// Get border desc
|
|
CTileSet::TFlagBorder oriented[4] =
|
|
{
|
|
pTileSet->getOrientedBorder (CTileSet::left, CTileSet::getEdgeType ((CTileSet::TTransition)number, CTileSet::left)),
|
|
pTileSet->getOrientedBorder (CTileSet::bottom, CTileSet::getEdgeType ((CTileSet::TTransition)number, CTileSet::bottom)),
|
|
pTileSet->getOrientedBorder (CTileSet::right, CTileSet::getEdgeType ((CTileSet::TTransition)number, CTileSet::right)),
|
|
pTileSet->getOrientedBorder (CTileSet::top, CTileSet::getEdgeType ((CTileSet::TTransition)number, CTileSet::top))
|
|
};
|
|
|
|
// Symmetry ?
|
|
if (symmetry)
|
|
{
|
|
if ( (tileRotation & 1) ^ goofy )
|
|
{
|
|
CTileSet::TFlagBorder tmp = oriented[1];
|
|
oriented[1] = CTileSet::getInvertBorder (oriented[3]);
|
|
oriented[3] = CTileSet::getInvertBorder (tmp);
|
|
oriented[2] = CTileSet::getInvertBorder (oriented[2]);
|
|
oriented[0] = CTileSet::getInvertBorder (oriented[0]);
|
|
}
|
|
else
|
|
{
|
|
CTileSet::TFlagBorder tmp = oriented[0];
|
|
oriented[0] = CTileSet::getInvertBorder (oriented[2]);
|
|
oriented[2] = CTileSet::getInvertBorder (tmp);
|
|
oriented[1] = CTileSet::getInvertBorder (oriented[1]);
|
|
oriented[3] = CTileSet::getInvertBorder (oriented[3]);
|
|
}
|
|
}
|
|
|
|
// Rotation
|
|
CTileSet::TFlagBorder edges[4];
|
|
edges[0] = pTileSet->getOrientedBorder (CTileSet::left, oriented[(0 + transRotate )&3]);
|
|
edges[1] = pTileSet->getOrientedBorder (CTileSet::bottom, oriented[(1 + transRotate )&3]);
|
|
edges[2] = pTileSet->getOrientedBorder (CTileSet::right, oriented[(2 + transRotate )&3]);
|
|
edges[3] = pTileSet->getOrientedBorder (CTileSet::top, oriented[(3 + transRotate )&3]);
|
|
|
|
// Get the good tile number
|
|
CTileSet::TTransition transition = pTileSet->getTransitionTile (edges[3], edges[1], edges[0], edges[2]);
|
|
nlassert ((CTileSet::TTransition)transition != CTileSet::notfound);
|
|
tile = (uint)(pTileSet->getTransition (transition)->getTile ());
|
|
}
|
|
|
|
// Transform rotation: invert rotation
|
|
tileRotation += rotate;
|
|
|
|
// If goofy, add +2
|
|
if (goofy && symmetry)
|
|
tileRotation += 2;
|
|
|
|
// Mask the rotation
|
|
tileRotation &= 3;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// Ok
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
void CPatchInfo::transform256Case (const CTileBank &bank, uint8 &case256, uint tileRotation, bool symmetry, uint rotate, bool goofy)
|
|
{
|
|
// Tile exist ?
|
|
if ( (rotate!=0) || symmetry )
|
|
{
|
|
// Symmetry ?
|
|
if (symmetry)
|
|
{
|
|
// Take the symmetry
|
|
uint symArray[4] = {3, 2, 1, 0};
|
|
case256 = symArray[case256];
|
|
|
|
if (goofy && ((tileRotation & 1) ==0))
|
|
case256 += 2;
|
|
if ((!goofy) && (tileRotation & 1))
|
|
case256 += 2;
|
|
}
|
|
|
|
// Rotation ?
|
|
case256 -= rotate;
|
|
case256 &= 3;
|
|
}
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
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)
|
|
{
|
|
uint patchCount = (uint)patchInfo.size ();
|
|
uint i;
|
|
|
|
// --- Export tile info Symmetry of the bind info.
|
|
// --- Parse each patch and each edge
|
|
|
|
// For each patches
|
|
NL3D::CZoneSymmetrisation::CError error;
|
|
|
|
// Build the structure
|
|
if (!zoneSymmetry.build (patchInfo, snapCell, weldThreshold, bank, error, toOriginalSpace))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Symmetry ?
|
|
if (symmetry)
|
|
{
|
|
for(i=0 ; i<patchCount; i++)
|
|
{
|
|
// Ref on the current patch
|
|
CPatchInfo &pi = patchInfo[i];
|
|
|
|
// --- Symmetry vertex indexes
|
|
|
|
// Vertices
|
|
CVector tmp = pi.Patch.Vertices[0];
|
|
pi.Patch.Vertices[0] = pi.Patch.Vertices[3];
|
|
pi.Patch.Vertices[3] = tmp;
|
|
tmp = pi.Patch.Vertices[1];
|
|
pi.Patch.Vertices[1] = pi.Patch.Vertices[2];
|
|
pi.Patch.Vertices[2] = tmp;
|
|
|
|
// Tangents
|
|
tmp = pi.Patch.Tangents[0];
|
|
pi.Patch.Tangents[0] = pi.Patch.Tangents[5];
|
|
pi.Patch.Tangents[5] = tmp;
|
|
tmp = pi.Patch.Tangents[1];
|
|
pi.Patch.Tangents[1] = pi.Patch.Tangents[4];
|
|
pi.Patch.Tangents[4] = tmp;
|
|
tmp = pi.Patch.Tangents[2];
|
|
pi.Patch.Tangents[2] = pi.Patch.Tangents[3];
|
|
pi.Patch.Tangents[3] = tmp;
|
|
tmp = pi.Patch.Tangents[6];
|
|
pi.Patch.Tangents[6] = pi.Patch.Tangents[7];
|
|
pi.Patch.Tangents[7] = tmp;
|
|
|
|
// Interior
|
|
tmp = pi.Patch.Interiors[0];
|
|
pi.Patch.Interiors[0] = pi.Patch.Interiors[3];
|
|
pi.Patch.Interiors[3] = tmp;
|
|
tmp = pi.Patch.Interiors[1];
|
|
pi.Patch.Interiors[1] = pi.Patch.Interiors[2];
|
|
pi.Patch.Interiors[2] = tmp;
|
|
|
|
// ** Symmetries tile colors
|
|
|
|
uint u,v;
|
|
uint countU = pi.OrderS/2+1;
|
|
uint countV = pi.OrderT+1;
|
|
for (v=0; v<countV; v++)
|
|
for (u=0; u<countU; u++)
|
|
{
|
|
// Store it in the tile info
|
|
uint index0 = u+v*(pi.OrderS+1);
|
|
uint index1 = (pi.OrderS-u)+v*(pi.OrderS+1);
|
|
|
|
// XChg
|
|
uint16 tmp = pi.TileColors[index0].Color565;
|
|
pi.TileColors[index0].Color565 = pi.TileColors[index1].Color565;
|
|
pi.TileColors[index1].Color565 = tmp;
|
|
}
|
|
|
|
// Smooth flags
|
|
uint flags = (uint)(pi.getSmoothFlag (0))<<2;
|
|
flags |= (uint)(pi.getSmoothFlag (2))<<0;
|
|
flags |= (uint)(pi.getSmoothFlag (1))<<1;
|
|
flags |= (uint)(pi.getSmoothFlag (3))<<3;
|
|
pi.Flags &= ~3;
|
|
pi.Flags |= flags;
|
|
}
|
|
|
|
// --- Symmetry of the bind info.
|
|
// --- Parse each patch and each edge
|
|
// For each patches
|
|
for (i=0 ; i<patchCount; i++)
|
|
{
|
|
// Ref on the patch info
|
|
CPatchInfo &pi = patchInfo[i];
|
|
|
|
// Xchg left and right
|
|
swap (pi.BindEdges[0], pi.BindEdges[2]);
|
|
swap (pi.BaseVertices[0], pi.BaseVertices[3]);
|
|
swap (pi.BaseVertices[1], pi.BaseVertices[2]);
|
|
|
|
// Flip edges
|
|
for (uint edge=0; edge<4; edge++)
|
|
{
|
|
// Ref on the patch info
|
|
CPatchInfo::CBindInfo &bindEdge = pi.BindEdges[edge];
|
|
|
|
uint next;
|
|
// Look if it is a bind ?
|
|
if ( (bindEdge.NPatchs>1) && (bindEdge.NPatchs!=5) )
|
|
{
|
|
for (next=0; next<(uint)bindEdge.NPatchs/2; next++)
|
|
{
|
|
swap (bindEdge.Next[bindEdge.NPatchs - next - 1], bindEdge.Next[next]);
|
|
swap (bindEdge.Edge[bindEdge.NPatchs - next - 1], bindEdge.Edge[next]);
|
|
}
|
|
}
|
|
|
|
// Look if we are binded on a reversed edge
|
|
uint bindCount = (bindEdge.NPatchs==5) ? 1 : bindEdge.NPatchs;
|
|
for (next=0; next<bindCount; next++)
|
|
{
|
|
// Left or right ?
|
|
if ( (bindEdge.Edge[next] & 1) == 0)
|
|
{
|
|
// Invert
|
|
bindEdge.Edge[next] += 2;
|
|
bindEdge.Edge[next] &= 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// For each patches
|
|
for (i=0 ; i<patchCount; i++)
|
|
{
|
|
// Tile infos
|
|
CPatchInfo &pi = patchInfo[i];
|
|
|
|
// Backup tiles
|
|
std::vector<CTileElement> tiles = pi.Tiles;
|
|
|
|
int u,v;
|
|
for (v=0; v<pi.OrderT; v++)
|
|
for (u=0; u<pi.OrderS; u++)
|
|
{
|
|
// U tile
|
|
int uSymmetry = symmetry ? (pi.OrderS-u-1) : u;
|
|
|
|
// Destination tile
|
|
CTileElement &element = pi.Tiles[u+v*pi.OrderS];
|
|
|
|
// Copy the orginal symmetrical element
|
|
element = tiles[uSymmetry+v*pi.OrderS];
|
|
|
|
// For each layer
|
|
for (int l=0; l<3; l++)
|
|
{
|
|
// Empty ?
|
|
if (element.Tile[l] != 0xffff)
|
|
{
|
|
// Get the tile index
|
|
uint tile = element.Tile[l];
|
|
uint tileRotation = element.getTileOrient (l);
|
|
|
|
// Get rot and symmetry for this tile
|
|
uint tileRotate = rotate;
|
|
bool tileSymmetry = symmetry;
|
|
bool goofy = symmetry && (zoneSymmetry.getTileState (i, uSymmetry+v*pi.OrderS, l) == CZoneSymmetrisation::Goofy);
|
|
|
|
// Transform the transfo
|
|
if (getTileSymmetryRotate (bank, tile, tileSymmetry, tileRotate))
|
|
{
|
|
// Transform the tile
|
|
if (!transformTile (bank, tile, tileRotation, tileSymmetry, (4-tileRotate)&3, goofy))
|
|
{
|
|
// Info
|
|
nlwarning ("Error getting symmetrical / rotated zone tile.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Info
|
|
nlwarning ("Error getting symmetrical / rotated zone tile.");
|
|
return false;
|
|
}
|
|
|
|
// Set the tile
|
|
element.Tile[l] = tile;
|
|
element.setTileOrient (l, (uint8)tileRotation);
|
|
}
|
|
}
|
|
|
|
// Empty ?
|
|
if (element.Tile[0]!=0xffff)
|
|
{
|
|
// Get 256 info
|
|
bool is256x256;
|
|
uint8 uvOff;
|
|
element.getTile256Info (is256x256, uvOff);
|
|
|
|
// 256 ?
|
|
if (is256x256)
|
|
{
|
|
// Get rot and symmetry for this tile
|
|
uint tileRotate = rotate;
|
|
bool tileSymmetry = symmetry;
|
|
uint tileRotation = tiles[uSymmetry+v*pi.OrderS].getTileOrient (0);
|
|
bool goofy = symmetry && (zoneSymmetry.getTileState (i, uSymmetry+v*pi.OrderS, 0) == CZoneSymmetrisation::Goofy);
|
|
|
|
// Transform the transfo
|
|
getTileSymmetryRotate (bank, element.Tile[0], tileSymmetry, tileRotate);
|
|
|
|
// Transform the case
|
|
transform256Case (bank, uvOff, tileRotation, tileSymmetry, (4-tileRotate)&3, goofy);
|
|
|
|
element.setTile256Info (true, uvOff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ok
|
|
return true;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
|
|
} // NL3D
|