khanat-opennel-code/code/nel/src/3d/patchuv_locator.cpp

211 lines
6.1 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/patchuv_locator.h"
using namespace std;
using namespace NLMISC;
namespace NL3D
{
// ***************************************************************************
void CPatchUVLocator::build(const CPatch *patchCenter, sint edgeCenter, CPatch::CBindInfo &bindInfo)
{
nlassert(bindInfo.Zone);
// copy basic. NB: NPatchs==0 means patchCenter is binded on a 1/X patch.
_CenterPatch= const_cast<CPatch*>(patchCenter);
_CenterPatchEdge= edgeCenter;
_NPatchs= bindInfo.NPatchs;
// set it to true. false-d if one of the neighbor patch does not have same number of tile.
_SameEdgeOrder= true;
// For all patchs binded to me.
for(sint i=0; i<_NPatchs; i++)
{
// The edge of the neihbor on which we are binded.
sint edgeNeighbor= bindInfo.Edge[i];
CPatch *paNeighbor= bindInfo.Next[i];
_NeighborPatch[i]= paNeighbor;
// Find uvI, uvJ, uvP such that:
// uvOut= uvIn.x * uvI + uvIn.y * uvJ + uvP.
CVector2f &uvI= _NeighborBasis[i].UvI;
CVector2f &uvJ= _NeighborBasis[i].UvJ;
CVector2f &uvP= _NeighborBasis[i].UvP;
// Find the basis MeToNeighbor.
//=============================
sint rotation= (edgeCenter - edgeNeighbor + 4) & 3;
// Find scale to apply.
float scX, scY;
// If our neighbor edge is a vertical edge
if( (edgeNeighbor&1)==0 )
{
scX= 1;
// Manage difference of Order at the edge.
scY= (float)paNeighbor->getOrderForEdge(edgeNeighbor) / (float)patchCenter->getOrderForEdge(edgeCenter);
// Manage bind on the edge.
// If patchCenter is binded on a bigger
if(bindInfo.MultipleBindNum!=0)
scY/= bindInfo.MultipleBindNum;
if(_NPatchs>1)
scY*= _NPatchs;
// same TileOrder on the edge??
if(scY!=1)
_SameEdgeOrder= false;
}
else
{
scY= 1;
// Manage difference of Order at the edge.
scX= (float)paNeighbor->getOrderForEdge(edgeNeighbor) / (float)patchCenter->getOrderForEdge(edgeCenter);
// Manage bind on the edge.
// If patchCenter is binded on a bigger
if(bindInfo.MultipleBindNum!=0)
scX/= bindInfo.MultipleBindNum;
if(_NPatchs>1)
scX*= _NPatchs;
// same TileOrder on the edge??
if(scX!=1)
_SameEdgeOrder= false;
}
// Find rotation to apply.
switch(rotation)
{
case 0: uvI.set(-scX, 0); uvJ.set(0, -scY); break;
case 1: uvI.set(0, -scY); uvJ.set(scX, 0); break;
case 2: uvI.set(scX, 0); uvJ.set(0, scY); break;
case 3: uvI.set(0, scY); uvJ.set(-scX, 0); break;
}
// Find the position.
//=============================
// Find the uv coord at start of the edge, for 2 patchs.
CVector2f uvCenter(0.f, 0.f);
CVector2f uvNeighbor(0.f, 0.f);
float decal;
// find the uv at start of edgeCenter, + decal due to bind 1/X.
float ocS= patchCenter->getOrderS();
float ocT= patchCenter->getOrderT();
// Manage Bind 1/X.
if(_NPatchs>1)
{
// Move uvCenter, so it is near the position at start of edgeNeighbor.
decal= (float)i / _NPatchs;
}
else
decal= 0;
// Manage rotation.
switch(edgeCenter)
{
case 0: uvCenter.set(0, decal*ocT); break;
case 1: uvCenter.set(decal*ocS, ocT); break;
case 2: uvCenter.set(ocS, (1-decal)*ocT); break;
case 3: uvCenter.set((1-decal)*ocS, 0); break;
};
// find the uv at start of edgeNeighbor, + decal due to bind X/1.
float onS= paNeighbor->getOrderS();
float onT= paNeighbor->getOrderT();
// Manage Bind X/1.
if(bindInfo.MultipleBindNum!=0)
{
// Must invert the id, because of mirror.... (make a draw).
sint id= (bindInfo.MultipleBindNum-1) - bindInfo.MultipleBindId;
// Move uvNeighbor, so it is near the position at start of edgeCenter.
decal= (float)id / bindInfo.MultipleBindNum;
}
else
decal= 0;
// Manage rotation.
switch(edgeNeighbor)
{
case 0: uvNeighbor.set(0, (1-decal)*onT); break;
case 1: uvNeighbor.set((1-decal)*onS, onT); break;
case 2: uvNeighbor.set(onS, decal*onT); break;
case 3: uvNeighbor.set(decal*onS, 0); break;
};
// uvOut= uvIn.x * uvI + uvIn.y * uvJ + uvP.
// So uvP = uvOut - uvIn.x * uvI - uvIn.y * uvJ
uvP= uvNeighbor - uvCenter.x * uvI - uvCenter.y * uvJ;
}
}
// ***************************************************************************
uint CPatchUVLocator::selectPatch(const CVector2f &uvIn)
{
if(_NPatchs==1)
return 0;
else
{
// Choice before on which patch we must go.
float selection=0.0;
uint os= _CenterPatch->getOrderS();
uint ot= _CenterPatch->getOrderT();
switch(_CenterPatchEdge)
{
case 0: selection= uvIn.y / ot; break;
case 1: selection= uvIn.x / os; break;
case 2: selection= (ot-uvIn.y) / ot; break;
case 3: selection= (os-uvIn.x) / os; break;
}
sint sel= (sint)floor(selection*_NPatchs);
clamp(sel, 0, _NPatchs-1);
return sel;
}
}
// ***************************************************************************
void CPatchUVLocator::locateUV(const CVector2f &uvIn, uint patch, CPatch *&patchOut, CVector2f &uvOut)
{
if(_NPatchs==1)
{
// Change basis and select good patch.
_NeighborBasis[0].mulPoint(uvIn, uvOut);
patchOut= _NeighborPatch[0];
}
else
{
// Change basis and select good patch.
_NeighborBasis[patch].mulPoint(uvIn, uvOut);
patchOut= _NeighborPatch[patch];
}
}
} // NL3D