khanat-opennel-code/code/nel/src/3d/tess_face_priority_list.cpp
acemtp@users.sourceforge.net d5c601ffa5 initial version
2010-05-06 02:08:41 +02:00

487 lines
14 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/tess_face_priority_list.h"
#include "nel/misc/debug.h"
#include <cmath>
#include "nel/3d/tessellation.h"
#include "nel/misc/fast_floor.h"
using namespace NLMISC;
using namespace std;
namespace NL3D
{
// ***************************************************************************
// ***************************************************************************
// CTessFacePListNode
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
void CTessFacePListNode::linkInPList(CTessFacePListNode &root)
{
// unlink old list from me.
_PrecTessFaceInPList->_NextTessFaceInPList= _NextTessFaceInPList;
_NextTessFaceInPList->_PrecTessFaceInPList= _PrecTessFaceInPList;
// link me to the list.
_PrecTessFaceInPList= &root;
_NextTessFaceInPList= root._NextTessFaceInPList;
// link the list to me.
root._NextTessFaceInPList->_PrecTessFaceInPList= this;
root._NextTessFaceInPList= this;
/*
NB if list was empty (this, this), then
_PrecTessFaceInPList= &root
_NextTessFaceInPList= root._NextTessFaceInPList= &root !
root._NextTessFaceInPList->_PrecTessFaceInPList= this; => root._PrecTessFaceInPList= this
root._NextTessFaceInPList= this
*/
}
// ***************************************************************************
void CTessFacePListNode::unlinkInPList()
{
/*
NB: if this node was empty (this, this), this is a No-Op.
If this node was the last of a list, then the root correctly get (&root, &root) after this.
*/
// unlink old list from me.
_PrecTessFaceInPList->_NextTessFaceInPList= _NextTessFaceInPList;
_NextTessFaceInPList->_PrecTessFaceInPList= _PrecTessFaceInPList;
// reset to empty node.
_PrecTessFaceInPList= this;
_NextTessFaceInPList= this;
}
// ***************************************************************************
void CTessFacePListNode::appendPList(CTessFacePListNode &root)
{
// If list to append is not empty.
if( root._NextTessFaceInPList != &root )
{
// If we are empty.
if( _NextTessFaceInPList==this )
{
// link the appendList to the root.
_PrecTessFaceInPList= root._PrecTessFaceInPList;
_NextTessFaceInPList= root._NextTessFaceInPList;
// link the root to the appendList.
_PrecTessFaceInPList->_NextTessFaceInPList= this;
_NextTessFaceInPList->_PrecTessFaceInPList= this;
}
// else bind first-last in the interval prec-next.
else
{
CTessFacePListNode *first= root._NextTessFaceInPList;
CTessFacePListNode *last= root._PrecTessFaceInPList;
CTessFacePListNode *prec= this;
CTessFacePListNode *next= _NextTessFaceInPList;
// insert the appendList in our list.
next->_PrecTessFaceInPList= last;
prec->_NextTessFaceInPList= first;
// insert our list in the appendList.
last->_NextTessFaceInPList= next;
first->_PrecTessFaceInPList= prec;
}
// clear input list.
root._PrecTessFaceInPList= &root;
root._NextTessFaceInPList= &root;
}
}
// ***************************************************************************
// ***************************************************************************
// CTessFacePriorityList
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CTessFacePriorityList::CTessFacePriorityList()
{
_OODistStep= 1;
_NEntries= 0;
_MaskEntries= 0;
_EntryModStart= 0;
_NumQuadrant= 0;
}
// ***************************************************************************
CTessFacePriorityList::~CTessFacePriorityList()
{
clear();
}
// ***************************************************************************
void CTessFacePriorityList::init(float distStep, uint numEntries, float distMaxMod, uint numQuadrant)
{
uint i;
nlassert(distStep>0);
nlassert(numEntries>0 && isPowerOf2(numEntries));
nlassert(distMaxMod<1);
nlassert(numQuadrant==0 || (numQuadrant>=4 && isPowerOf2(numQuadrant)) );
// clear the prioriy list before.
clear();
// setup
_OODistStep= 1.0f / distStep;
_NEntries= numEntries;
_MaskEntries= _NEntries-1;
_EntryModStart= (uint)ceil(distMaxMod * _NEntries);
NLMISC::clamp(_EntryModStart, 0U, _NEntries-1);
_NumQuadrant= numQuadrant;
// Build the Rolling tables.
_RollingTables.resize(1+_NumQuadrant);
for(i=0;i<_RollingTables.size();i++)
{
_RollingTables[i].init(_NEntries);
// setup the quadrant direction. NB: 0 not used since "Direction less rolling table"
if(i>=1)
{
uint idx= i-1;
// split evenly the plane with direction.
float angle= float(2*Pi*idx)/_NumQuadrant;
CMatrix mat;
mat.rotateZ(angle);
// setup the vector
_RollingTables[i].QuadrantDirection= mat.getJ();
}
}
// Build Quarter Selection. In is in CCW order. Out: see selectQuadrant()
const uint quarterLut[4]= {1,3,2,0};
_MaskQuadrant= _NumQuadrant-1;
for(i=0;i<4;i++)
{
// Re-Order for faster ASM acces. see selectQuadrant()
uint idx= quarterLut[i];
_QuarterQuadrantStart[idx]= i*_NumQuadrant/4;
_QuarterQuadrantEnd[idx]= 1+ (i+1)*_NumQuadrant/4;
}
}
// ***************************************************************************
void CTessFacePriorityList::clear()
{
// just clear all the rolling tables.
_RollingTables.clear();
}
// ***************************************************************************
uint CTessFacePriorityList::selectQuadrant(const CVector &direction)
{
// if numQuadrants=0, ret 0.
if(_NumQuadrant==0)
return 0;
// select the quarter.
uint quarterId;
#if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
__asm
{
mov esi, direction
mov eax, [esi]direction.y
mov ebx, [esi]direction.x
// read the sign
and eax, 0x80000000
and ebx, 0x80000000
// set the bit
shr eax, 30
shr ebx, 31
// assemble
or eax, ebx
mov quarterId, eax
}
#else
if(direction.y>0)
{
if(direction.x>0) quarterId= 0;
else quarterId= 1;
}
else
{
if(direction.x>0) quarterId= 2;
else quarterId= 3;
}
#endif
// Run only the quadrants of the associated quarter.
uint quadrantStart= _QuarterQuadrantStart[quarterId];
uint quadrantEnd= _QuarterQuadrantEnd[quarterId];
// For all quadrants of the quarter, return the best direction
float bestDirPs= -FLT_MAX;
uint bestQuadrant= 0;
for(uint i=quadrantStart;i<quadrantEnd;i++)
{
// get the quadrant Id. Must mask for last (0). And Start at 1 in the _RollingTables.
// uint quadrantId= (i&_MaskQuadrant)+1;
// Test with this quadrant
float ps= direction*_RollingTables[i].QuadrantDirection;
if(ps>bestDirPs)
{
bestDirPs= ps;
bestQuadrant= i;
}
}
return bestQuadrant;
}
// ***************************************************************************
void CTessFacePriorityList::insert(uint quadrantId, float distance, CTessFace *value)
{
// plist must be inited.
nlassert(_NEntries>0 && quadrantId<_RollingTables.size());
// First, setup in our basis.
distance*= _OODistStep;
// Insert int the good quadrant
CRollingTable &rollTable= _RollingTables[quadrantId];
// Then, look where we must insert it.
sint idInsert;
if(distance<=0)
idInsert= 0;
else
{
// Must insert so we can't miss it when a shift occurs (=> floor).
idInsert= NLMISC::OptFastFloor(distance + rollTable.Remainder);
}
idInsert= std::max(0, idInsert);
// Manage Mod.
// If the element to insert must be inserted at distance > distMax.
if(idInsert>(sint)_MaskEntries)
{
// Compute number of entries to apply the mod.
uint nMod= _NEntries - _EntryModStart;
// Of how many entries are we too far.
idInsert= idInsert - _MaskEntries;
// Then loop in the interval [_EntryModStart, _NEntries[.
idInsert= idInsert % nMod;
idInsert= _EntryModStart + idInsert;
}
// insert in the Roll Table.
rollTable.insertInRollTable(idInsert, value);
}
// ***************************************************************************
void CTessFacePriorityList::shift(const CVector &direction, CTessFacePListNode &pulledElements)
{
// plist must be inited.
nlassert(_NEntries>0);
pulledElements.unlinkInPList();
// Shift all the rolling tables
for(uint i=0;i<_RollingTables.size();i++)
{
CRollingTable &rollTable= _RollingTables[i];
float shiftDistance;
// if quadrant 0, Direction less => get distance with Euler norm.
if(i==0)
{
shiftDistance= direction.norm();
}
// Else compute the effective distance we run to the plane.
else
{
shiftDistance= direction*rollTable.QuadrantDirection;
// For now, just clamp, but may be interesting to shift Back the rolling table !!
shiftDistance= max(0.f, shiftDistance);
}
// First, setup in our basis.
shiftDistance*= _OODistStep;
// at least, fill OUT with elements of entry 0.
// For all elements of the entry 0 of , pull them, and insert in result list.
pulledElements.appendPList(rollTable.getRollTableEntry(0));
// shift.
rollTable.Remainder+= shiftDistance;
// If Remainder>=1, it means that we must shift the rolling table, and get elements deleted.
uint entryShift= (uint)floor(rollTable.Remainder);
rollTable.Remainder= rollTable.Remainder - entryShift;
// shift full array??
if( entryShift >= _NEntries)
{
entryShift= _NEntries;
// The entire array is pulled, _Remainder should get a good value.
rollTable.Remainder= 0;
}
// If some real shift, do it.
rollTable.shiftEntries(entryShift, pulledElements);
}
}
// ***************************************************************************
void CTessFacePriorityList::shiftAll(CTessFacePListNode &pulledElements)
{
// plist must be inited.
nlassert(_NEntries>0);
pulledElements.unlinkInPList();
// Do it for all rolling tables.
for(uint i=0;i<_RollingTables.size();i++)
{
// The entire array is pulled, _Remainder should get a good value.
uint entryShift= _NEntries;
_RollingTables[i].Remainder= 0;
// shift the entire array.
_RollingTables[i].shiftEntries(entryShift, pulledElements);
}
}
// ***************************************************************************
// ***************************************************************************
// Rolling table.
// ***************************************************************************
// ***************************************************************************
// ***************************************************************************
CTessFacePriorityList::CRollingTable::CRollingTable()
{
_EntryStart= 0;
_NEntries= 0;
_MaskEntries= 0;
Remainder= 0;
}
// ***************************************************************************
CTessFacePriorityList::CRollingTable::~CRollingTable()
{
clearRollTable();
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::init(uint numEntries)
{
_EntryStart= 0;
_NEntries= numEntries;
_MaskEntries= _NEntries-1;
Remainder= 0;
_Entries.resize(numEntries);
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::insertInRollTable(uint entry, CTessFace *value)
{
CTessFacePListNode &root= _Entries[ (entry + _EntryStart) & _MaskEntries ];
// Insert into list.
value->linkInPList(root);
}
// ***************************************************************************
CTessFacePListNode &CTessFacePriorityList::CRollingTable::getRollTableEntry(uint entry)
{
CTessFacePListNode &root= _Entries[ (entry + _EntryStart) & _MaskEntries ];
return root;
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::clearRollTableEntry(uint entry)
{
CTessFacePListNode &root= _Entries[ (entry + _EntryStart) & _MaskEntries ];
// clear all the list.
while( root.nextInPList() != &root )
{
// unlink from list
CTessFacePListNode *node= root.nextInPList();
node->unlinkInPList();
}
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::shiftRollTable(uint shiftEntry)
{
// delete all elements shifted.
for(uint i=0; i<shiftEntry; i++)
{
clearRollTableEntry(i);
}
// shift to right the ptr of entries.
_EntryStart+= shiftEntry;
_EntryStart= _EntryStart & _MaskEntries;
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::clearRollTable()
{
for(uint i=0; i<_NEntries; i++)
{
clearRollTableEntry(i);
}
_EntryStart= 0;
// For convenience only (not really useful).
Remainder= 0;
}
// ***************************************************************************
void CTessFacePriorityList::CRollingTable::shiftEntries(uint entryShift, CTessFacePListNode &pulledElements)
{
if(entryShift>0)
{
// before shifting the roll Table, fill pulledElements.
for(uint i=0; i<entryShift; i++)
{
// For all elements of the ith entry, pull them and isnert in result list.
pulledElements.appendPList(getRollTableEntry(i));
}
// shift the roll Table. lists are already empty.
shiftRollTable(entryShift);
}
}
} // NL3D