khanat-opennel-code/code/nel/src/pacs/move_container.cpp
2010-08-20 13:27:17 +02:00

2062 lines
58 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 "stdpacs.h"
#include "move_primitive.h"
#include "move_element.h"
#include "primitive_block.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/misc/i_xml.h"
using namespace NLMISC;
#define NELPACS_ALLOC_DYNAMIC_INFO 100
#define NELPACS_ALLOC_STATIC_INFO 100
H_AUTO_DECL ( NLPACS_Eval_Collision )
#define NLPACS_HAUTO_EVAL_COLLISION H_AUTO_USE ( NLPACS_Eval_Collision )
/****************************************************************************
Doc:
// Non collisionnable primitive
Their moves are evaluate one by one with evalNCPrimitiveCollision().
If a collision is found, reaction() is called.
// Collisionnable primitives
Each primitive must be moved first with the move() method.
Their moves are evaluate all at once. All the collisions found are time sorted in a time orderin table (_TimeOT).
While the table is not empty, the first collision occured in time is solved and
If a collision is found, reaction() is called.
****************************************************************************/
namespace NLPACS
{
// ***************************************************************************
CMoveContainer::~CMoveContainer ()
{
clear ();
}
// ***************************************************************************
void CMoveContainer::clear ()
{
// Clear all primitives
std::set<CMovePrimitive*>::iterator ite=_PrimitiveSet.begin();
while (ite!=_PrimitiveSet.end ())
{
freePrimitive (*ite);
ite++;
}
// Clear primitive set
_PrimitiveSet.clear ();
// Clear root changed
_ChangedRoot.clear ();
// Clear static world image set
_StaticWorldImage.clear ();
// Clear cell array
_VectorCell.clear ();
// Clear time ot
_TimeOT.clear ();
}
// ***************************************************************************
void CMoveContainer::init (double xmin, double ymin, double xmax, double ymax, uint widthCellCount, uint heightCellCount,
double primitiveMaxSize, uint8 numWorldImage, uint maxIteration, uint otSize)
{
// Clear arrays
clear ();
// Create world images
_ChangedRoot.resize (numWorldImage);
for (uint i=0; i<numWorldImage; i++)
_ChangedRoot[i]=NULL;
// Not in test mode
_Retriever=NULL;
// Element size
_PrimitiveMaxSize=primitiveMaxSize;
// BB
_Xmin=xmin;
_Ymin=ymin;
_Xmax=xmax;
_Ymax=ymax;
// Cells count
_CellCountWidth=widthCellCount;
_CellCountHeight=heightCellCount;
// Cells size
_CellWidth=(_Xmax - _Xmin)/(double)_CellCountWidth;
_CellHeight=(_Ymax - _Ymin)/(double)_CellCountHeight;
// Cell array
_VectorCell.resize (numWorldImage);
for (uint j=0; j<numWorldImage; j++)
_VectorCell[j].resize (_CellCountWidth * _CellCountHeight);
// resize OT
_OtSize=otSize;
_TimeOT.resize (otSize);
// Clear the OT
clearOT ();
// Clear test time
_TestTime=0xffffffff;
_MaxTestIteration=maxIteration;
// Resize trigger array
_Triggers.resize (NELPACS_CONTAINER_TRIGGER_DEFAULT_SIZE);
}
// ***************************************************************************
void CMoveContainer::init (CGlobalRetriever* retriever, uint widthCellCount, uint heightCellCount, double primitiveMaxSize,
uint8 numWorldImage, uint maxIteration, uint otSize)
{
// Get min max of the global retriever BB
CVector min=retriever->getBBox().getMin();
CVector max=retriever->getBBox().getMax();
// Setup min max
double xmin=min.x;
double ymin=min.y;
double xmax=max.x;
double ymax=max.y;
// Init
init (xmin, ymin, xmax, ymax, widthCellCount, heightCellCount, primitiveMaxSize, numWorldImage, maxIteration, otSize);
// Init the retriever
_Retriever=retriever;
}
// ***************************************************************************
void CMoveContainer::evalCollision (double deltaTime, uint8 worldImage)
{
NLPACS_HAUTO_EVAL_COLLISION
// H_AUTO(PACS_MC_evalCollision);
// New test time
_TestTime++;
// Delta time
_DeltaTime=deltaTime;
// Clear triggers
_Triggers.clear ();
// Update the bounding box and position of modified primitives
updatePrimitives (0.f, worldImage);
#ifdef NL_DEBUG
// Check list integrity
//checkSortedList ();
#endif // NL_DEBUG
// Get first collision
_PreviousCollisionNode = &_TimeOT[0];
if(_PreviousCollisionNode == NULL)
return;
// Eval all collisions
evalAllCollisions (0.f, worldImage);
// Clear modified list
clearModifiedList (worldImage);
// Modified list is empty at this point
nlassert (_ChangedRoot[worldImage]==NULL);
// Previous node is a 'hard' OT node
nlassert (!_PreviousCollisionNode->isInfo());
// Get next collision
CCollisionOTInfo *nextCollision;
{
H_AUTO (NLPACS_Get_Next_Info);
nextCollision=_PreviousCollisionNode->getNextInfo ();
}
// Collision ?
while (nextCollision)
{
// Get new previous OT hard node
_PreviousCollisionNode=nextCollision->getPrevious ();
// Previous node is a 'hard' OT node
nlassert (!_PreviousCollisionNode->isInfo());
// Keep this collision
reaction (*nextCollision);
// Remove this collision from ot
if (!nextCollision->isCollisionAgainstStatic ())
{
// Remove the primitive from OT
nextCollision->unlink();
CCollisionOTDynamicInfo *info = static_cast<CCollisionOTDynamicInfo*>(nextCollision);
if (info->getFirstPrimitive())
info->getFirstPrimitive()->removeCollisionOTInfo(info);
if (info->getSecondPrimitive())
info->getSecondPrimitive()->removeCollisionOTInfo(info);
}
// Last time
double newTime=nextCollision->getCollisionTime ();
// Remove modified objects from the OT
removeModifiedFromOT (worldImage);
// Must have been removed
nlassert (nextCollision->getPrevious ()==NULL);
nlassert (nextCollision->CCollisionOT::getNext ()==NULL);
// Update the bounding box and position of modified primitives
updatePrimitives (newTime, worldImage);
// Eval all collisions of modified objects for the new delta t
evalAllCollisions (newTime, worldImage);
// Clear modified list
clearModifiedList (worldImage);
// Get next collision
nextCollision=_PreviousCollisionNode->getNextInfo ();
}
#ifdef NL_DEBUG
// OT must be cleared
checkOT ();
#endif // NL_DEBUG
// Free ordered table info
freeAllOTInfo ();
// Some init
_PreviousCollisionNode=NULL;
}
// ***************************************************************************
bool CMoveContainer::testMove (UMovePrimitive* primitive, const CVectorD& speed, double deltaTime, uint8 worldImage, CVectorD *contactNormal)
{
// H_AUTO(PACS_MC_testMove);
if (contactNormal)
*contactNormal = CVectorD::Null;
// Cast
nlassert (dynamic_cast<CMovePrimitive*>(primitive));
CMovePrimitive* prim=static_cast<CMovePrimitive*>(primitive);
// New test time
_TestTime++;
// Delta time
_DeltaTime=deltaTime;
// Get the world image primitive
uint8 primitiveWorldImage;
CPrimitiveWorldImage *wI;
if (prim->isNonCollisionable ())
{
wI=prim->getWorldImage (0);
primitiveWorldImage=worldImage;
}
else
{
wI=prim->getWorldImage (worldImage);
primitiveWorldImage=worldImage;
}
// Backup speed
CVectorD oldSpeed=wI->getSpeed ();
// Set speed
wI->move (speed, *this, *prim, primitiveWorldImage);
// Update the bounding box and position of the primitive
wI->update (0, _DeltaTime, *prim);
// Compute cells overlaped by the primitive
if (!prim->isNonCollisionable ())
updateCells (prim, worldImage);
#ifdef NL_DEBUG
// Check list integrity
// checkSortedList ();
#endif // NL_DEBUG
// Result
bool result=false;
bool testMoveValid;
// Eval first each static world images
result=evalOneTerrainCollision (0, prim, primitiveWorldImage, true, testMoveValid, NULL, contactNormal);
// Eval first each static world images
if (!result)
{
std::set<uint8>::iterator ite=_StaticWorldImage.begin();
while (ite!=_StaticWorldImage.end())
{
// Eval in this world image
result=evalOnePrimitiveCollision (0, prim, *ite, primitiveWorldImage, true, true, testMoveValid, NULL, contactNormal);
// If found, abort
if (result)
break;
// Next world image
ite++;
}
}
// Eval collisions if not found and not tested
if ((!result) && (_StaticWorldImage.find (worldImage)==_StaticWorldImage.end()))
result=evalOnePrimitiveCollision (0, prim, worldImage, primitiveWorldImage, true, false, testMoveValid, NULL, contactNormal);
// Backup speed only if the primitive is inserted in the world image
if (prim->isInserted (primitiveWorldImage))
wI->move (oldSpeed, *this, *prim, primitiveWorldImage);
#ifdef NL_DEBUG
// OT must be cleared
checkOT ();
#endif // NL_DEBUG
// Free ordered table info
freeAllOTInfo ();
// Some init
_PreviousCollisionNode=NULL;
// Return result
return !result;
}
// ***************************************************************************
void CMoveContainer::updatePrimitives (double beginTime, uint8 worldImage)
{
H_AUTO (NLPACS_Update_Primitives);
// For each changed primitives
CMovePrimitive *changed=_ChangedRoot[worldImage];
while (changed)
{
// Get the primitive world image
CPrimitiveWorldImage *wI;
if (changed->isNonCollisionable())
wI=changed->getWorldImage (0);
else
wI=changed->getWorldImage (worldImage);
// Force the build of the bounding box
wI->update (beginTime, _DeltaTime, *changed);
// Is inserted in this world image ?
if (changed->isInserted (worldImage))
{
// Compute cells overlaped by the primitive
updateCells (changed, worldImage);
}
// Next primitive
changed=wI->getNextModified ();
}
}
// ***************************************************************************
void CMoveContainer::updateCells (CMovePrimitive *primitive, uint8 worldImage)
{
// H_AUTO(PACS_MC_updateCells);
// Get the primitive world image
CPrimitiveWorldImage *wI=primitive->getWorldImage (worldImage);
#if !FINAL_VERSION
// Check BB width not too large
if (wI->getBBXMax() - wI->getBBXMin() > _CellWidth)
{
nlwarning ("Primitives have moved more than a cell.");
}
// Check BB height not too large
if (wI->getBBYMax() - wI->getBBYMin() > _CellHeight)
{
nlwarning ("Primitives have moved more than a cell.");
}
#endif
// Get coordinate in the cell array
sint minx=(int)floor ((wI->getBBXMin() - _Xmin) / _CellWidth);
sint miny=(int)floor ((wI->getBBYMin() - _Ymin) / _CellHeight);
sint maxx=(int)floor ((wI->getBBXMax() - _Xmin) / _CellWidth);
sint maxy=(int)floor ((wI->getBBYMax() - _Ymin) / _CellHeight);
// Born
if (minx<0)
minx=0;
if (miny<0)
miny=0;
if (maxx>=(int)_CellCountWidth)
maxx=(int)_CellCountWidth-1;
if (maxy>=(int)_CellCountHeight)
maxy=(int)_CellCountHeight-1;
maxx=std::min (minx+1, maxx);
maxy=std::min (miny+1, maxy);
// flags founded
bool found[4]={false, false, false, false};
// For each old cells
uint i;
for (i=0; i<4; i++)
{
// Element
CMoveElement *elm = wI->getMoveElement (i);
// Old element in this cell ?
if ( elm )
{
// Check
nlassert (elm->X<_CellCountWidth);
nlassert (elm->Y<_CellCountHeight);
// Must remove it ?
if ( (elm->X < minx) || (elm->X > maxx) || (elm->Y < miny) || (elm->Y > maxy) )
{
// Yes remove it
wI->removeMoveElement (i, *this, worldImage);
}
else
{
// Checks
nlassert (((elm->X - minx)==0)||((elm->X - minx)==1));
nlassert (((elm->Y - miny)==0)||((elm->Y - miny)==1));
// Update position
#ifndef TEST_CELL
_VectorCell[worldImage][elm->X+elm->Y*_CellCountWidth].updateSortedLists (elm, worldImage);
#endif
// Check found cells
found[ elm->X - minx + ((elm->Y - miny) << (maxx-minx)) ]=true;
}
}
}
// For each case selected
int x, y;
i=0;
for (y=miny; y<=(int)maxy; y++)
for (x=minx; x<=(int)maxx; x++)
{
// Check the formula
nlassert ((int)i == (x - minx + ((y - miny) << (maxx-minx)) ));
// If the cell is not found
if (!found[i])
{
// Center of the cell
double cx=((double)x+0.5f)*_CellWidth+_Xmin;
double cy=((double)y+0.5f)*_CellHeight+_Ymin;
// Add it in the list
wI->addMoveElement (_VectorCell[worldImage][x+y*_CellCountWidth], (uint16)x, (uint16)y, cx, cy, primitive, *this, worldImage);
}
// Next cell
i++;
}
}
// ***************************************************************************
void CMoveContainer::getCells (CMovePrimitive *primitive, uint8 worldImage, uint8 primitiveWorldImage, CMoveElement **elementArray)
{
// H_AUTO(PACS_MC_getCells);
// Get the primitive world image
CPrimitiveWorldImage *wI;
if (primitive->isNonCollisionable())
wI=primitive->getWorldImage (0);
else
wI=primitive->getWorldImage (primitiveWorldImage);
#if !FINAL_VERSION
// Check BB width not too large
if (wI->getBBXMax() - wI->getBBXMin() > _CellWidth)
{
//nlwarning ("Primitives have moved more than a cell.");
}
// Check BB height not too large
if (wI->getBBYMax() - wI->getBBYMin() > _CellHeight)
{
//nlwarning ("Primitives have moved more than a cell.");
}
#endif
// Get coordinate in the cell array
int minx=(int)floor ((wI->getBBXMin() - _Xmin) / _CellWidth);
int miny=(int)floor ((wI->getBBYMin() - _Ymin) / _CellHeight);
int maxx=(int)floor ((wI->getBBXMax() - _Xmin) / _CellWidth);
int maxy=(int)floor ((wI->getBBYMax() - _Ymin) / _CellHeight);
// Born
if (minx<0)
minx=0;
if (miny<0)
miny=0;
if (maxx>=(int)_CellCountWidth)
maxx=(int)_CellCountWidth-1;
if (maxy>=(int)_CellCountHeight)
maxy=(int)_CellCountHeight-1;
maxx=std::min (minx+1, maxx);
maxy=std::min (miny+1, maxy);
// For each case selected
int x, y;
int i=0;
for (y=miny; y<=(int)maxy; y++)
for (x=minx; x<=(int)maxx; x++)
{
// Check the formula
nlassert ((int)i == (x - minx + ((y - miny) << (maxx-minx)) ));
// Center of the cell
double cx=((double)x+0.5f)*_CellWidth+_Xmin;
// Primitive center
double pcx=(wI->getBBXMin()+wI->getBBXMax())/2.f;
elementArray[i]->Primitive=primitive;
elementArray[i]->X=uint16(x);
elementArray[i]->Y=uint16(y);
// Insert in left or right ?
if (pcx<cx)
{
// In the left
elementArray[i]->NextX=_VectorCell[worldImage][x+y*_CellCountWidth].getFirstX ();
elementArray[i]->PreviousX=NULL;
}
else
{
// In the right
elementArray[i]->PreviousX=_VectorCell[worldImage][x+y*_CellCountWidth].getLastX ();
elementArray[i]->NextX=NULL;
}
// Next cell
i++;
}
// Erase last array element
for (; i<4; i++)
{
elementArray[i]=NULL;
}
}
// ***************************************************************************
void CMoveContainer::clearModifiedList (uint8 worldImage)
{
H_AUTO (NLPACS_Clear_Modified_List);
// For each changed primitives
CMovePrimitive *changed=_ChangedRoot[worldImage];
while (changed)
{
// Get the world image primitive
CPrimitiveWorldImage *wI;
if (changed->isNonCollisionable())
wI=changed->getWorldImage (0);
else
wI=changed->getWorldImage (worldImage);
// Next primitive
changed=wI->getNextModified ();
// Remove it from the list
wI->setInModifiedListFlag (false);
}
// Empty list
_ChangedRoot[worldImage]=NULL;
}
// ***************************************************************************
void CMoveContainer::checkSortedList ()
{
// Check each primitives in the set
std::set<CMovePrimitive*>::iterator ite=_PrimitiveSet.begin();
while (ite!=_PrimitiveSet.end())
{
// Check
(*ite)->checkSortedList ();
ite++;
}
}
// ***************************************************************************
bool CMoveContainer::evalOneTerrainCollision (double beginTime, CMovePrimitive *primitive, uint8 primitiveWorldImage,
bool testMove, bool &testMoveValid, CCollisionOTStaticInfo *staticColInfo, CVectorD *contactNormal)
{
// H_AUTO(PACS_MC_evalOneCollision);
H_AUTO(NLPACS_Eval_One_Terrain_Collision);
// Find its collisions
bool found=false;
// Get the primitive world image
CPrimitiveWorldImage *wI;
if (primitive->isNonCollisionable())
wI=primitive->getWorldImage (0);
else
wI=primitive->getWorldImage (primitiveWorldImage);
// Begin time must be the same as beginTime
//nlassert (wI->getInitTime()==beginTime);
if (wI->getInitTime() != beginTime)
{
nlwarning("PACS: evalOneTerrainCollision() failure, wI->getInitTime() [%f] != beginTime [%f]", wI->getInitTime(), beginTime);
return false;
}
// Test its static collision
if (_Retriever)
{
// Delta pos..
// Test retriever with the primitive
const TCollisionSurfaceDescVector *result=wI->evalCollision (*_Retriever, _SurfaceTemp, _TestTime, _MaxTestIteration, *primitive);
if (result)
{
// TEST MOVE MUST BE OK !!
testMoveValid=true;
// Size of the array
uint size=(uint)result->size();
// For each detected collisions
for (uint c=0; c<size; c++)
{
// Ref on the collision
CCollisionSurfaceDesc desc=(*result)[c];
double contactTime = (_DeltaTime-beginTime)*desc.ContactTime+beginTime;
/*
* If beginTime is 0.999999999 and desc.ContactTime<1.0, contactTime will be 1.0.
* In this case, we force contactTime to be beginTime to avoid collision at time == 1.0.
**/
if ((contactTime >= 1.0) && (beginTime < 1.0) && (desc.ContactTime < 1.0))
contactTime = beginTime;
// Set the container's time space contact time
desc.ContactTime = contactTime;
// ptr on the surface
const CRetrievableSurface *surf= _Retriever->getSurfaceById (desc.ContactSurface);
// TODO: check surface flags against primitive flags HERE:
// Is a wall ?
bool isWall;
if(!surf)
isWall= true;
else
isWall= !(surf->isFloor() || surf->isCeiling());
// stop on a wall.
if(isWall)
{
// Test move ?
if (testMove)
{
// return contact normal only when testmove and vector provided
if (contactNormal)
*contactNormal = desc.ContactNormal;
return true;
}
else
{
// OK, collision if we are a collisionable primitive
newCollision (primitive, desc, primitiveWorldImage, beginTime, staticColInfo);
// One collision found
found=true;
break;
}
}
}
}
else
// More than maxtest made, exit
return false;
}
return found;
}
// ***************************************************************************
bool CMoveContainer::evalOnePrimitiveCollision (double beginTime, CMovePrimitive *primitive, uint8 worldImage, uint8 primitiveWorldImage,
bool testMove, bool secondIsStatic, bool &/* testMoveValid */, CCollisionOTDynamicInfo *dynamicColInfo,
CVectorD *contactNormal)
{
// H_AUTO(PACS_MC_evalOneCollision);
H_AUTO(NLPACS_Eval_One_Primitive_Collision);
// Find its collisions
bool found=false;
// Get the primitive world image
CPrimitiveWorldImage *wI;
if (primitive->isNonCollisionable())
wI=primitive->getWorldImage (0);
else
wI=primitive->getWorldImage (primitiveWorldImage);
// Begin time must be the same as beginTime
//nlassert (wI->getInitTime()==beginTime);
if (wI->getInitTime() != beginTime)
{
nlwarning("PACS: evalOnePrimitiveCollision() failure, wI->getInitTime() [%f] != beginTime [%f]", wI->getInitTime(), beginTime);
return false;
}
// Element table
CMoveElement tableNotInserted[4];
CMoveElement *table[4];
// Single test ?
bool singleTest=testMove;
// Is in world image
if ((worldImage==primitiveWorldImage) && wI->isInWorldImageFlag())
{
// Get move element table from the primitive
table[0]=wI->getMoveElement (0);
table[1]=wI->getMoveElement (1);
table[2]=wI->getMoveElement (2);
table[3]=wI->getMoveElement (3);
}
else
{
// Set table pointers
table[0]=tableNotInserted+0;
table[1]=tableNotInserted+1;
table[2]=tableNotInserted+2;
table[3]=tableNotInserted+3;
// Get cells
getCells (primitive, worldImage, primitiveWorldImage, table);
// Force the test
singleTest=true;
}
// For each move element
for (uint i=0; i<4; i++)
{
// Get the element
CMoveElement *elm=table[i];
// Element valid ?
if (elm)
{
// Check
nlassert (elm->Primitive==primitive);
// Primitive to the left
// Lookup in X sorted list on the left
CMoveElement *other=elm->PreviousX;
nlassert (other!=elm);
while (other && (wI->getBBXMin() - other->Primitive->getWorldImage(worldImage)->getBBXMin() < _PrimitiveMaxSize) )
{
// Other primitive
CMovePrimitive *otherPrimitive=other->Primitive;
CPrimitiveWorldImage *otherWI=otherPrimitive->getWorldImage (worldImage);
nlassert (otherPrimitive!=primitive);
// Continue the check if the other primitive is not int the modified list or if its pointer is higher than primitive
if ( singleTest || ( (!otherWI->isInModifiedListFlag ()) || (primitive<otherPrimitive) ) )
{
// Look if valid in X
if (wI->getBBXMin() < otherWI->getBBXMax())
{
// Look if valid in Y
if ( (wI->getBBYMin() < otherWI->getBBYMax()) && (otherWI->getBBYMin() < wI->getBBYMax()) )
{
// If not already in collision with this primitive
if (!primitive->isInCollision (otherPrimitive))
{
if (evalPrimAgainstPrimCollision (beginTime, primitive, otherPrimitive, wI, otherWI, testMove,
primitiveWorldImage, worldImage, secondIsStatic, dynamicColInfo, contactNormal))
{
if (testMove)
return true;
found=true;
}
}
}
}
}
// Next primitive to the left
other = other->PreviousX;
}
// Lookup in X sorted list on the right
other=elm->NextX;
// Primitive to the right
while (other && (other->Primitive->getWorldImage(worldImage)->getBBXMin() < wI->getBBXMax()) )
{
// Other primitive
CMovePrimitive *otherPrimitive=other->Primitive;
CPrimitiveWorldImage *otherWI=otherPrimitive->getWorldImage (worldImage);
nlassert (otherPrimitive!=primitive);
// Continue the check if the other primitive is not in the modified list or if its pointer is higher than primitive
if ( singleTest || ( (!otherWI->isInModifiedListFlag ()) || (primitive<otherPrimitive) ) )
{
// Look if valid in Y
if ( (wI->getBBYMin() < otherWI->getBBYMax()) && (otherWI->getBBYMin() < wI->getBBYMax()) )
{
// If not already in collision with this primitive
if (!primitive->isInCollision (otherPrimitive))
{
if (evalPrimAgainstPrimCollision (beginTime, primitive, otherPrimitive, wI, otherWI, testMove,
primitiveWorldImage, worldImage, secondIsStatic, dynamicColInfo, contactNormal))
{
if (testMove)
return true;
found=true;
}
}
}
}
// Next primitive to the left
other = other->NextX;
}
}
}
return found;
}
// ***************************************************************************
bool CMoveContainer::evalPrimAgainstPrimCollision (double beginTime, CMovePrimitive *primitive, CMovePrimitive *otherPrimitive,
CPrimitiveWorldImage *wI, CPrimitiveWorldImage *otherWI, bool testMove,
uint8 firstWorldImage, uint8 secondWorldImage, bool secondIsStatic, CCollisionOTDynamicInfo *dynamicColInfo,
CVectorD * /* contactNormal */)
{
// H_AUTO(PACS_MC_evalPrimAgainstPrimCollision);
// Test the primitive
double firstTime, lastTime;
// Collision
CCollisionDesc desc;
if (wI->evalCollision (*otherWI, desc, beginTime, _DeltaTime, _TestTime, _MaxTestIteration,
firstTime, lastTime, *primitive, *otherPrimitive))
{
// Enter or exit
bool enter = (beginTime<=firstTime) && (firstTime<_DeltaTime) && ((primitive->getTriggerType()&UMovePrimitive::EnterTrigger)
|| (otherPrimitive->getTriggerType()&UMovePrimitive::EnterTrigger));
bool exit = (beginTime<=lastTime) && (lastTime<_DeltaTime) && ((primitive->getTriggerType()&UMovePrimitive::ExitTrigger)
|| (otherPrimitive->getTriggerType()&UMovePrimitive::ExitTrigger));
bool overlap = (firstTime<=beginTime) && (lastTime>_DeltaTime) && ((primitive->getTriggerType()&UMovePrimitive::OverlapTrigger)
|| (otherPrimitive->getTriggerType()&UMovePrimitive::OverlapTrigger));
bool contact = ( beginTime<((firstTime+lastTime)/2) ) && (firstTime<=_DeltaTime);
bool collision = contact && (primitive->isObstacle() && otherPrimitive->isObstacle ());
// Return collision time
if (testMove)
return contact;
/**
* Raise Trigger !
* For collisionnable primitives, trigger are raised here (in reaction) because
* this is the moment we are sure the collision happened.
*
* For non collisionable primitves, the trigger is raised at collision time because without OT,
* we can't stop evaluating collision on triggers.
*/
if (primitive->isNonCollisionable () && (enter || exit || overlap))
{
if (primitive->isTriggered (*otherPrimitive, enter, exit))
{
// Add a trigger
if (enter)
newTrigger (primitive, otherPrimitive, desc, UTriggerInfo::In);
if (exit)
newTrigger (primitive, otherPrimitive, desc, UTriggerInfo::Out);
if (overlap)
newTrigger (primitive, otherPrimitive, desc, UTriggerInfo::Inside);
}
// If the other primitive is not an obstacle, skip it because it will re-generate collisions.
if (!collision)
return false;
}
// OK, collision
if (contact || enter || exit || overlap)
newCollision (primitive, otherPrimitive, desc, contact, enter, exit, overlap, firstWorldImage, secondWorldImage, secondIsStatic,
dynamicColInfo);
// Collision
return collision;
}
return false;
}
// ***************************************************************************
void CMoveContainer::evalAllCollisions (double beginTime, uint8 worldImage)
{
H_AUTO(NLPACS_Eval_All_Collisions);
// First primitive
CMovePrimitive *primitive=_ChangedRoot[worldImage];
// For each modified primitive
while (primitive)
{
// Get the primitive world image
uint8 primitiveWorldImage;
CPrimitiveWorldImage *wI;
if (primitive->isNonCollisionable ())
{
wI=primitive->getWorldImage (0);
primitiveWorldImage=worldImage;
}
else
{
wI=primitive->getWorldImage (worldImage);
primitiveWorldImage=worldImage;
}
CVectorD d0=wI->getDeltaPosition();
// Find a collision
bool found=false;
bool testMoveValid=false;
// Eval collision on the terrain
found|=evalOneTerrainCollision (beginTime, primitive, primitiveWorldImage, false, testMoveValid, NULL, NULL);
// If the primitive can collid other primitive..
if (primitive->getCollisionMask())
{
// Eval collision in each static world image
std::set<uint8>::iterator ite=_StaticWorldImage.begin();
while (ite!=_StaticWorldImage.end())
{
// Eval in this world image
found|=evalOnePrimitiveCollision (beginTime, primitive, *ite, primitiveWorldImage, false, true, testMoveValid, NULL, NULL);
// Next world image
ite++;
}
}
CVectorD d1=wI->getDeltaPosition();
// If the primitive can collid other primitive..
if (primitive->getCollisionMask())
{
// Eval collision in the world image if not already tested
if (_StaticWorldImage.find (worldImage)==_StaticWorldImage.end())
found|=evalOnePrimitiveCollision (beginTime, primitive, worldImage, primitiveWorldImage, false, false, testMoveValid, NULL, NULL);
}
CVectorD d2=wI->getDeltaPosition();
// No collision ?
if (!found)
{
//nlassert ((d0==d1)&&(d0==d2));
//nlassert (f1==f2);
if (_Retriever&&testMoveValid)
{
// Do move
wI->doMove (*_Retriever, _SurfaceTemp, _DeltaTime, _DeltaTime, primitive->getDontSnapToGround());
}
else
{
// Do move
wI->doMove (_DeltaTime);
}
}
// Next primitive
primitive=wI->getNextModified ();
}
}
// ***************************************************************************
void CMoveContainer::newCollision (CMovePrimitive* first, CMovePrimitive* second, const CCollisionDesc& desc, bool collision, bool enter, bool exit, bool inside,
uint firstWorldImage, uint secondWorldImage, bool secondIsStatic, CCollisionOTDynamicInfo *dynamicColInfo)
{
// H_AUTO(PACS_MC_newCollision_short);
nlassert ((dynamicColInfo && first->isNonCollisionable ()) || (!dynamicColInfo && first->isCollisionable ()));
if (dynamicColInfo)
{
dynamicColInfo->init (first, second, desc, collision, enter, exit, inside, uint8(firstWorldImage), uint8(secondWorldImage), secondIsStatic);
}
else
{
// Get an ordered time index. Always round to the future.
int index=(int)(ceil (desc.ContactTime*(double)_OtSize/_DeltaTime) );
// Clamp left.
if (index<0)
index=0;
// If in time
if (index<(int)_OtSize)
{
// Build info
CCollisionOTDynamicInfo *info = allocateOTDynamicInfo ();
info->init (first, second, desc, collision, enter, exit, inside, uint8(firstWorldImage), uint8(secondWorldImage), secondIsStatic);
// Add in the primitive list
first->addCollisionOTInfo (info);
second->addCollisionOTInfo (info);
// Insert in the time ordered table
//nlassert (index<(int)_TimeOT.size());
if (index >= (int)_TimeOT.size())
{
nlwarning("PACS: newCollision() failure, index [%d] >= (int)_TimeOT.size() [%d], clamped to max", index, (int)_TimeOT.size());
index = (int)_TimeOT.size()-1;
}
_TimeOT[index].link (info);
// Check it is after the last hard collision
nlassert (_PreviousCollisionNode<=&_TimeOT[index]);
}
}
}
// ***************************************************************************
void CMoveContainer::newCollision (CMovePrimitive* first, const CCollisionSurfaceDesc& desc, uint8 worldImage, double beginTime, CCollisionOTStaticInfo *staticColInfo)
{
// H_AUTO(PACS_MC_newCollision_long);
// Check
nlassert (_Retriever);
nlassert ((staticColInfo && first->isNonCollisionable ()) || (!staticColInfo && first->isCollisionable ()));
// Get the world image
CPrimitiveWorldImage *wI;
if (first->isNonCollisionable())
wI=first->getWorldImage (0);
else
wI=first->getWorldImage (worldImage);
// Time
double time=desc.ContactTime;
/*
if (time == _DeltaTime)
time -= _DeltaTime*FLT_EPSILON;
*/
// Check time interval
//nlassertex (beginTime<=time, ("beginTime=%f, time=%f", beginTime, time));
//nlassertex (time<_DeltaTime, ("time=%f, _DeltaTime=%f", time, _DeltaTime));
if (beginTime > time)
{
nlwarning("PACS: beginTime=%f > time=%f", beginTime, time);
}
if (time >= _DeltaTime)
{
nlinfo("PACS: time=%f >= _DeltaTime=%f", time, _DeltaTime);
}
// Time of the collision.
time-=NELPACS_DIST_BACK/wI->getSpeed().norm();
time=std::max(time, beginTime);
double ratio=(time-beginTime)/(_DeltaTime-beginTime);
/*
nlassert (ratio>=0);
nlassert (ratio<=1);
*/
if (ratio < 0.0)
{
nlwarning("PACS: ratio=%f < 0.0", ratio);
ratio = 0.0;
}
if (ratio > 1.0)
{
nlwarning("PACS: ratio=%f > 1.0", ratio);
ratio = 1.0;
}
if (staticColInfo)
{
// Make a new globalposition
UGlobalPosition endPosition=_Retriever->doMove (wI->getGlobalPosition(), wI->getDeltaPosition(),
(float)ratio, _SurfaceTemp, false);
// Init the info descriptor
staticColInfo->init (first, desc, endPosition, ratio, worldImage);
}
else
{
// Get an ordered time index. Always round to the future.
int index=(int)(ceil (time*(double)_OtSize/_DeltaTime) );
// Clamp left.
if (index<0)
index=0;
// If in time
if (index<(int)_OtSize)
{
// Build info
CCollisionOTStaticInfo *info = allocateOTStaticInfo ();
// Make a new globalposition
UGlobalPosition endPosition=_Retriever->doMove (wI->getGlobalPosition(), wI->getDeltaPosition(),
(float)ratio, _SurfaceTemp, false);
// Init the info descriptor
info->init (first, desc, endPosition, ratio, worldImage);
// Add in the primitive list
first->addCollisionOTInfo (info);
// Insert in the time ordered table
//nlassert (index<(int)_TimeOT.size());
if (index >= (int)_TimeOT.size())
{
nlwarning("PACS: newCollision() failure, index [%d] >= (int)_TimeOT.size() [%d], clamped to max", index, (int)_TimeOT.size());
index = (int)_TimeOT.size()-1;
}
_TimeOT[index].link (info);
// Check it is after the last hard collision
nlassert (_PreviousCollisionNode<=&_TimeOT[index]);
}
}
}
// ***************************************************************************
void CMoveContainer::newTrigger (CMovePrimitive* first, CMovePrimitive* second, const CCollisionDesc& desc, uint triggerType)
{
// Element index
uint index=(uint)_Triggers.size();
// Add one element
_Triggers.resize (index+1);
// Fill info
_Triggers[index].Object0=first->UserData;
_Triggers[index].Object1=second->UserData;
_Triggers[index].CollisionDesc=desc;
_Triggers[index].CollisionType = uint8(triggerType);
}
// ***************************************************************************
void CMoveContainer::checkOT ()
{
// Check
nlassert (_OtSize==_TimeOT.size());
// Check linked list
for (uint i=0; i<_OtSize-1; i++)
{
// Check link
nlassert ( _TimeOT[i].getNext() == (&(_TimeOT[i+1])) );
nlassert ( _TimeOT[i+1].getPrevious() == (&(_TimeOT[i])) );
}
// Check first and last
nlassert ( _TimeOT[0].getPrevious() == NULL );
nlassert ( _TimeOT[_OtSize-1].getNext() == NULL );
}
// ***************************************************************************
void CMoveContainer::clearOT ()
{
// Check
nlassert (_OtSize==_TimeOT.size());
// clear the list
uint i;
for (i=0; i<_OtSize; i++)
_TimeOT[i].clear ();
// Relink the list
for (i=0; i<_OtSize-1; i++)
// Link the two cells
_TimeOT[i].link (&(_TimeOT[i+1]));
}
// ***************************************************************************
void CMoveContainer::removeModifiedFromOT (uint8 worldImage)
{
// For each changed primitives
CMovePrimitive *changed=_ChangedRoot[worldImage];
while (changed)
{
// Remove from ot list
changed->removeCollisionOTInfo ();
// Get the primitive world image
CPrimitiveWorldImage *wI;
if (changed->isNonCollisionable())
wI=changed->getWorldImage (0);
else
wI=changed->getWorldImage (worldImage);
// Next primitive
changed=wI->getNextModified ();
}
}
// ***************************************************************************
CCollisionOTDynamicInfo *CMoveContainer::allocateOTDynamicInfo ()
{
return _AllocOTDynamicInfo.allocate ();
}
// ***************************************************************************
CCollisionOTStaticInfo *CMoveContainer::allocateOTStaticInfo ()
{
return _AllocOTStaticInfo.allocate ();
}
// ***************************************************************************
// Free all ordered table info
void CMoveContainer::freeAllOTInfo ()
{
H_AUTO (NLPACS_Free_All_OT_Info);
_AllocOTDynamicInfo.free ();
_AllocOTStaticInfo.free ();
}
// ***************************************************************************
CMovePrimitive *CMoveContainer::allocatePrimitive (uint8 firstWorldImage, uint8 numWorldImage)
{
// Simply allocate
return new CMovePrimitive (this, firstWorldImage, numWorldImage);
}
// ***************************************************************************
void CMoveContainer::freePrimitive (CMovePrimitive *primitive)
{
// Simply deallocate
delete primitive;
}
// ***************************************************************************
CPrimitiveWorldImage **CMoveContainer::allocateWorldImagesPtrs (uint numPtrs)
{
return new CPrimitiveWorldImage*[numPtrs];
}
// ***************************************************************************
void CMoveContainer::freeWorldImagesPtrs (CPrimitiveWorldImage **ptrs)
{
delete [] ptrs;
}
// ***************************************************************************
CPrimitiveWorldImage *CMoveContainer::allocateWorldImage ()
{
return new CPrimitiveWorldImage;
}
// ***************************************************************************
void CMoveContainer::freeWorldImage (CPrimitiveWorldImage *worldImage)
{
delete worldImage;
}
// ***************************************************************************
CMoveElement *CMoveContainer::allocateMoveElement ()
{
// Simply allocate
return new CMoveElement;
}
// ***************************************************************************
void CMoveContainer::freeMoveElement (CMoveElement *element)
{
// Simply deallocate
delete element;
}
// ***************************************************************************
void UMoveContainer::deleteMoveContainer (UMoveContainer *container)
{
delete (CMoveContainer*)container;
}
// ***************************************************************************
UMovePrimitive *CMoveContainer::addCollisionablePrimitive (uint8 firstWorldImage, uint8 numWorldImage, const UMovePrimitive *copyFrom)
{
// Allocate primitive
CMovePrimitive *primitive=allocatePrimitive (firstWorldImage, numWorldImage);
// Add into the set
_PrimitiveSet.insert (primitive);
// if copy from primitive is not null, copy attributes
if (copyFrom != NULL)
{
primitive->setPrimitiveType(copyFrom->getPrimitiveType());
primitive->setReactionType(copyFrom->getReactionType());
primitive->setTriggerType(copyFrom->getTriggerType());
primitive->setCollisionMask(copyFrom->getCollisionMask());
primitive->setOcclusionMask(copyFrom->getOcclusionMask());
primitive->setObstacle(copyFrom->getObstacle());
primitive->setAbsorbtion(copyFrom->getAbsorbtion());
primitive->setHeight(copyFrom->getHeight());
if (primitive->getPrimitiveType() == UMovePrimitive::_2DOrientedBox)
{
float width=0.0f, height=0.0f;
copyFrom->getSize(width, height);
primitive->setSize(width, height);
}
else
{
primitive->setRadius(copyFrom->getRadius());
}
}
// Return it
return primitive;
}
// ***************************************************************************
UMovePrimitive *CMoveContainer::addNonCollisionablePrimitive (const UMovePrimitive *copyFrom)
{
// Allocate primitive
CMovePrimitive *primitive=allocatePrimitive (0, 1);
// Set as noncollisionable
primitive->setNonCollisionable (true);
// Add into the set
_PrimitiveSet.insert (primitive);
// if copy from primitive is not null, copy attributes
if (copyFrom != NULL)
{
primitive->setPrimitiveType(copyFrom->getPrimitiveType());
primitive->setReactionType(copyFrom->getReactionType());
primitive->setTriggerType(copyFrom->getTriggerType());
primitive->setCollisionMask(copyFrom->getCollisionMask());
primitive->setOcclusionMask(copyFrom->getOcclusionMask());
primitive->setObstacle(copyFrom->getObstacle());
primitive->setAbsorbtion(copyFrom->getAbsorbtion());
primitive->setHeight(copyFrom->getHeight());
if (primitive->getPrimitiveType() == UMovePrimitive::_2DOrientedBox)
{
float width=0.0f, height=0.0f;
copyFrom->getSize(width, height);
primitive->setSize(width, height);
}
else
{
primitive->setRadius(copyFrom->getRadius());
}
}
// Return it
return primitive;
}
// ***************************************************************************
void CMoveContainer::removePrimitive (UMovePrimitive* primitive)
{
// CMovePrimitive pointer
CMovePrimitive *prim=(CMovePrimitive*)primitive;
// Get the primitive world image
for (uint8 i=0; i<prim->getNumWorldImage (); i++)
{
// World image
uint8 worldImage=prim->getFirstWorldImage ()+i;
// Get primitive world image
CPrimitiveWorldImage *wI=prim->getWorldImage (worldImage);
// In modified list ?
if (wI->isInModifiedListFlag ())
{
// Non collisionable primitive ?
if (prim->isNonCollisionable())
{
// Remove from all world image
removeNCFromModifiedList (prim, worldImage);
}
else
{
// Remove from modified list
removeFromModifiedList (prim, worldImage);
}
}
}
// Remove from the set
_PrimitiveSet.erase (prim);
// Erase it
freePrimitive (prim);
}
// ***************************************************************************
void CMoveContainer::removeNCFromModifiedList (CMovePrimitive* primitive, uint8 worldImage)
{
// For each world image
uint i;
uint worldImageCount = (uint)_ChangedRoot.size();
for (i=0; i<worldImageCount; i++)
{
// For each changed primitives
CMovePrimitive *changed=_ChangedRoot[i];
CPrimitiveWorldImage *previous=NULL;
CPrimitiveWorldImage *wI=primitive->getWorldImage (worldImage);
while (changed)
{
// Get the primitive world image
CPrimitiveWorldImage *changedWI=changed->getWorldImage (worldImage);
// Remove from ot list
if (changed==primitive)
{
// There is a previous primitive ?
if (previous)
previous->linkInModifiedList (wI->getNextModified ());
else
_ChangedRoot[i]=wI->getNextModified ();
// Unlink
wI->linkInModifiedList (NULL);
wI->setInModifiedListFlag (false);
break;
}
// Next primitive
previous=changedWI;
changed=changedWI->getNextModified ();
}
// Breaked ?
if (changed==primitive)
break;
}
}
// ***************************************************************************
void CMoveContainer::removeFromModifiedList (CMovePrimitive* primitive, uint8 worldImage)
{
// For each changed primitives
CMovePrimitive *changed=_ChangedRoot[worldImage];
CPrimitiveWorldImage *previous=NULL;
CPrimitiveWorldImage *wI=primitive->getWorldImage (worldImage);
while (changed)
{
// Get the primitive world image
CPrimitiveWorldImage *changedWI=changed->getWorldImage (worldImage);
// Remove from ot list
if (changed==primitive)
{
// There is a previous primitive ?
if (previous)
previous->linkInModifiedList (wI->getNextModified ());
else
_ChangedRoot[worldImage]=wI->getNextModified ();
// Unlink
wI->linkInModifiedList (NULL);
wI->setInModifiedListFlag (false);
break;
}
// Next primitive
previous=changedWI;
changed=changedWI->getNextModified ();
}
}
// ***************************************************************************
void CMoveContainer::unlinkMoveElement (CMoveElement *element, uint8 worldImage)
{
// Some checks
nlassert (element->X<_CellCountWidth);
nlassert (element->Y<_CellCountHeight);
// Unlink it
CMoveCell &cell=_VectorCell[worldImage][element->X+element->Y*_CellCountWidth];
cell.unlinkX (element);
//cell.unlinkY (element);
}
// ***************************************************************************
void CMoveContainer::reaction (const CCollisionOTInfo& first)
{
// H_AUTO(PACS_MC_reaction);
// Static collision ?
if (first.isCollisionAgainstStatic())
{
// Check mode
nlassert (_Retriever);
// Cast
const CCollisionOTStaticInfo *staticInfo=safe_cast<const CCollisionOTStaticInfo*> (&first);
// Get the primitive world image
CMovePrimitive *movePrimitive=staticInfo->getPrimitive ();
CPrimitiveWorldImage *wI;
if (movePrimitive->isNonCollisionable ())
wI=movePrimitive->getWorldImage (0);
else
wI=movePrimitive->getWorldImage (staticInfo->getWorldImage());
// Dynamic collision
wI->reaction ( staticInfo->getCollisionDesc (), staticInfo->getGlobalPosition (),
*_Retriever, staticInfo->getDeltaTime(), _DeltaTime, *staticInfo->getPrimitive (), *this, staticInfo->getWorldImage());
}
else
{
// Cast
const CCollisionOTDynamicInfo *dynInfo=safe_cast<const CCollisionOTDynamicInfo*> (&first);
// Get the primitives world image
CPrimitiveWorldImage *firstWI;
if (dynInfo->getFirstPrimitive ()->isNonCollisionable ())
firstWI=dynInfo->getFirstPrimitive ()->getWorldImage (0);
else
firstWI=dynInfo->getFirstPrimitive ()->getWorldImage (dynInfo->getFirstWorldImage());
CPrimitiveWorldImage *secondWI;
if (dynInfo->getSecondPrimitive ()->isNonCollisionable ())
secondWI=dynInfo->getSecondPrimitive ()->getWorldImage (0);
else
secondWI=dynInfo->getSecondPrimitive ()->getWorldImage (dynInfo->getSecondWorldImage());
// Dynamic collision
firstWI->reaction ( *secondWI, dynInfo->getCollisionDesc (), _Retriever, _SurfaceTemp, dynInfo->isCollision(),
*dynInfo->getFirstPrimitive (), *dynInfo->getSecondPrimitive (), this, dynInfo->getFirstWorldImage(),
dynInfo->getSecondWorldImage(), dynInfo->isSecondStatic());
/**
* Raise Trigger !
* For collisionnable primitives, trigger are raised here (in reaction) because
* this is the moment we are sure the collision happened.
*
* For non collisionable primitves, the trigger is raised at collision time because without OT,
* we can't stop evaluating collision on triggers.
*/
if (dynInfo->getFirstPrimitive ()->isCollisionable ())
{
if (dynInfo->getFirstPrimitive ()->isTriggered (*dynInfo->getSecondPrimitive (), dynInfo->isEnter(), dynInfo->isExit()))
{
if (dynInfo->isEnter())
newTrigger (dynInfo->getFirstPrimitive (), dynInfo->getSecondPrimitive (), dynInfo->getCollisionDesc (), UTriggerInfo::In);
if (dynInfo->isExit())
newTrigger (dynInfo->getFirstPrimitive (), dynInfo->getSecondPrimitive (), dynInfo->getCollisionDesc (), UTriggerInfo::Out);
if (dynInfo->isInside())
newTrigger (dynInfo->getFirstPrimitive (), dynInfo->getSecondPrimitive (), dynInfo->getCollisionDesc (), UTriggerInfo::Inside);
}
}
}
}
// ***************************************************************************
void CMoveContainer::setAsStatic (uint8 worldImage)
{
// Add this world image in the static set of world image
_StaticWorldImage.insert (worldImage);
}
// ***************************************************************************
void CMoveContainer::duplicateWorldImage (uint8 source, uint8 dest)
{
// Cell count
uint cellCount=_CellCountWidth*_CellCountHeight;
// Clear dest modified list
clearModifiedList (dest);
// Clear destination cells
uint i;
for (i=0; i<cellCount; i++)
{
// Get first X
CMoveElement *elm;
while ((elm=_VectorCell[dest][i].getFirstX ()))
{
// Get primitive world image
CPrimitiveWorldImage *wI=elm->Primitive->getWorldImage (dest);
// Remove the primitive
int i;
for (i=0; i<4; i++)
{
if (wI->getMoveElement(i))
wI->removeMoveElement (i, *this, dest);
}
}
}
// Duplicate destination cells
for (i=0; i<cellCount; i++)
{
// Get first X
CMoveElement *elm=_VectorCell[source][i].getFirstX ();
while (elm)
{
// Get primitive world image
CPrimitiveWorldImage *wISource=elm->Primitive->getWorldImage (source);
CPrimitiveWorldImage *wIDest=elm->Primitive->getWorldImage (dest);
// First time the primitive is visited ?
if (wIDest->getMoveElement (0)==NULL)
{
wIDest->copy (*wISource);
}
// Add at the end of the list
wIDest->addMoveElementendOfList (_VectorCell[dest][i], elm->X, elm->Y, elm->Primitive, *this);
// Added ?
nlassert (wIDest->getMoveElement (0)!=NULL);
// Next primitive
elm=elm->NextX;
}
}
}
// ***************************************************************************
UMoveContainer *UMoveContainer::createMoveContainer (double xmin, double ymin, double xmax, double ymax,
uint widthCellCount, uint heightCellCount, double primitiveMaxSize, uint8 numWorldImage,
uint maxIteration, uint otSize)
{
// Create a CMoveContainer
return new CMoveContainer (xmin, ymin, xmax, ymax, widthCellCount, heightCellCount, primitiveMaxSize, numWorldImage, maxIteration, otSize);
}
// ***************************************************************************
UMoveContainer *UMoveContainer::createMoveContainer (UGlobalRetriever* retriever, uint widthCellCount,
uint heightCellCount, double primitiveMaxSize, uint8 numWorldImage, uint maxIteration, uint otSize)
{
// Cast
nlassert (dynamic_cast<CGlobalRetriever*>(retriever));
CGlobalRetriever* r=static_cast<CGlobalRetriever*>(retriever);
// Create a CMoveContainer
return new CMoveContainer (r, widthCellCount, heightCellCount, primitiveMaxSize, numWorldImage, maxIteration, otSize);
}
// ***************************************************************************
void UCollisionDesc::serial (NLMISC::IStream& stream)
{
stream.serial (ContactPosition);
stream.serial (ContactNormal0);
stream.serial (ContactNormal1);
stream.serial (ContactTime);
};
// ***************************************************************************
void UTriggerInfo::serial (NLMISC::IStream& stream)
{
stream.serial (Object0);
stream.serial (Object1);
stream.serial (CollisionDesc);
}
// ***************************************************************************
void CMoveContainer::addCollisionnablePrimitiveBlock(UPrimitiveBlock *pb,uint8 firstWorldImage,uint8 numWorldImage,std::vector<UMovePrimitive*> *primitives,float orientation,const NLMISC::CVector &position, bool dontSnapToGround /* = false*/, const NLMISC::CVector &scale /* = NLMISC::CVector(1.0f, 1.0f, 1.0f)*/)
{
CPrimitiveBlock *block = NLMISC::safe_cast<CPrimitiveBlock *>(pb);
// Reserve the pointer array
if (primitives)
primitives->reserve (block->Primitives.size());
// For each primitive
uint prim;
for (prim=0; prim<block->Primitives.size(); prim++)
{
// Create a collisionable primitive
UMovePrimitive *primitive = addCollisionablePrimitive (firstWorldImage, numWorldImage);
// Ref on the block descriptor
CPrimitiveDesc &desc = block->Primitives[prim];
// Set its properties
primitive->setPrimitiveType (desc.Type);
primitive->setReactionType (desc.Reaction);
primitive->setTriggerType (desc.Trigger);
primitive->setCollisionMask (desc.CollisionMask);
primitive->setOcclusionMask (desc.OcclusionMask);
primitive->setObstacle (desc.Obstacle);
primitive->setAbsorbtion (desc.Attenuation);
primitive->setDontSnapToGround(dontSnapToGround);
primitive->UserData = desc.UserData;
if (desc.Type == UMovePrimitive::_2DOrientedBox)
{
// ONLY ASSUME UNIFORM SCALE ON X/Y
primitive->setSize (desc.Length[0]*scale.x, desc.Length[1]*scale.x);
}
else
{
// ONLY ASSUME UNIFORM SCALE ON X/Y
nlassert (desc.Type == UMovePrimitive::_2DOrientedCylinder);
primitive->setRadius (desc.Length[0]*scale.x);
}
primitive->setHeight (desc.Height*scale.z);
// Insert the primitives
// For each world image
uint wI;
for (wI=firstWorldImage; wI<(uint)(firstWorldImage+numWorldImage); wI++)
{
// Insert the primitive
primitive->insertInWorldImage (uint8(wI));
// Final position&
float cosa = (float) cos (orientation);
float sina = (float) sin (orientation);
CVector finalPos;
finalPos.x = cosa * desc.Position.x * scale.x - sina * desc.Position.y * scale.y + position.x;
finalPos.y = sina * desc.Position.x * scale.x + cosa * desc.Position.y * scale.y + position.y;
finalPos.z = desc.Position.z *scale.z + position.z;
// Set the primtive orientation
if (desc.Type == UMovePrimitive::_2DOrientedBox)
primitive->setOrientation ((float)fmod ((float)(desc.Orientation + orientation), (float)(2.0f*Pi)), uint8(wI));
// Set the primitive global position
primitive->setGlobalPosition (finalPos, uint8(wI));
}
// Feedback asked ?
if (primitives)
{
// Add the pointer
primitives->push_back (primitive);
}
}
}
// ***************************************************************************
bool CMoveContainer::loadCollisionablePrimitiveBlock (const char *filename, uint8 firstWorldImage, uint8 numWorldImage, std::vector<UMovePrimitive*> *primitives, float orientation, const NLMISC::CVector &position, bool dontSnapToGround /*= false*/)
{
// Check world image
if ( (uint)(firstWorldImage+numWorldImage) > _ChangedRoot.size() )
{
nlwarning ("Invalid world image number.");
return false;
}
// Try to load the file
CIFile file;
if (file.open (filename))
{
// Create the XML stream
CIXml input;
// Init
if (input.init (file))
{
// The primitive block
CPrimitiveBlock block;
// Serial it
file.serial (block);
// add primitives
addCollisionnablePrimitiveBlock(&block, firstWorldImage, numWorldImage, primitives, orientation, position, dontSnapToGround);
return true;
}
else
{
// Warning
nlwarning ("Can't init XML stream with file %s.", filename);
return false;
}
}
else
{
// Warning
nlwarning ("Can't load primitive block %s.", filename);
return false;
}
}
// ***************************************************************************
void CMoveContainer::getPrimitives(std::vector<const UMovePrimitive *> &dest) const
{
dest.resize(_PrimitiveSet.size());
std::copy(_PrimitiveSet.begin(), _PrimitiveSet.end(), dest.begin());
}
// ***************************************************************************
void UMoveContainer::getPACSCoordsFromMatrix(NLMISC::CVector &pos,float &angle,const NLMISC::CMatrix &mat)
{
pos = mat.getPos();
CVector orient = mat.mulVector(NLMISC::CVector::I);
orient.z = 0.f;
orient.normalize();
angle = orient.y >= 0.f ? ::acosf(orient.x)
: 2.f * (float) NLMISC::Pi - ::acosf(orient.x);
}
// ***************************************************************************
bool CMoveContainer::evalNCPrimitiveCollision (double deltaTime, UMovePrimitive *primitive, uint8 worldImage)
{
// New test time
_TestTime++;
// Clear triggers
_Triggers.clear ();
// Only non-collisionable primitives
if (!primitive->isCollisionable())
{
// Delta time
_DeltaTime=deltaTime;
// Begin of the time slice to compute
double beginTime = 0;
double collisionTime = deltaTime;
// Get the world image
CPrimitiveWorldImage *wI = ((CMovePrimitive*)primitive)->getWorldImage (0);
CCollisionOTInfo *firstCollision = NULL;
do
{
//nlassert (beginTime < 1.0);
if (beginTime >= 1.0)
{
nlwarning("PACS: evalNCPrimitiveCollision() failure, beginTime [%f] >= 1.0", beginTime);
return false;
}
// Update the primitive
wI->update (beginTime, deltaTime, *(CMovePrimitive*)primitive);
CVectorD d0=wI->getDeltaPosition();
// Eval collision again the terrain
bool testMoveValid = false;
CCollisionOTStaticInfo staticColInfo;
CCollisionOTDynamicInfo dynamicColInfoWI0;
CCollisionOTDynamicInfo dynamicColInfoWI;
firstCollision = NULL;
// If collision found, note it is on the landscape
if (evalOneTerrainCollision (beginTime, (CMovePrimitive*)primitive, worldImage, false, testMoveValid, &staticColInfo, NULL))
{
firstCollision = &staticColInfo;
}
// Eval collision again the static primitives
std::set<uint8>::iterator ite=_StaticWorldImage.begin();
while (ite!=_StaticWorldImage.end())
{
// Eval in this world image
if (evalOnePrimitiveCollision (beginTime, (CMovePrimitive*)primitive, *ite, worldImage, false, true, testMoveValid, &dynamicColInfoWI0, NULL))
{
// First collision..
if (!firstCollision || (firstCollision->getCollisionTime () > dynamicColInfoWI0.getCollisionTime ()))
{
firstCollision = &dynamicColInfoWI0;
}
}
// Next world image
ite++;
}
// Checks
CVectorD d1=wI->getDeltaPosition();
// Eval collision again the world image
if (_StaticWorldImage.find (worldImage)==_StaticWorldImage.end())
{
if (evalOnePrimitiveCollision (beginTime, (CMovePrimitive*)primitive, worldImage, worldImage, false, false, testMoveValid, &dynamicColInfoWI, NULL))
{
// First collision..
if (!firstCollision || (firstCollision->getCollisionTime () > dynamicColInfoWI.getCollisionTime ()))
{
firstCollision = &dynamicColInfoWI;
}
}
}
// Checks
CVectorD d2=wI->getDeltaPosition();
nlassert ((d0==d1)&&(d0==d2));
// if (found)
// nlstop;
// Reaction
if (firstCollision)
{
collisionTime = firstCollision->getCollisionTime ();
reaction (*firstCollision);
//nlassert (collisionTime != 1);
if (collisionTime == 1)
{
nlinfo("PACS: evalNCPrimitiveCollision() failure, collisionTime [%f] == 1", collisionTime);
return false;
}
}
else
{
// Retriever mode ?
if (_Retriever&&testMoveValid)
{
// Do move
wI->doMove (*_Retriever, _SurfaceTemp, deltaTime, collisionTime, ((CMovePrimitive*)primitive)->getDontSnapToGround());
}
else
{
// Do move
wI->doMove (_DeltaTime);
}
}
beginTime = collisionTime;
}
while (firstCollision);
}
else
return false;
return true;
}
} // NLPACS