Show kcollision.cpp syntax highlighted
/*
Copyright (C) 2003 by Luca Cappa
Written by Luca Cappa groton@users.sourceforge.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//
// I N C L U D E S
//
#include "cssysdef.h"
#include "csutil/sysfunc.h"
#include "csutil/cscolor.h"
#include "csutil/cmdhelp.h"
#include "csutil/cspmeter.h"
#include "csutil/csstring.h"
#include "csutil/scfstr.h"
#include "cstool/csview.h"
#include "cstool/initapp.h"
#include "cstool/collider.h"
#include "iutil/vfs.h"
#include "iutil/eventq.h"
#include "iutil/event.h"
#include "iutil/objreg.h"
#include "iutil/csinput.h"
#include "iutil/virtclk.h"
#include "iutil/plugin.h"
#include "iutil/string.h"
#include "iengine/sector.h"
#include "iengine/engine.h"
#include "iengine/camera.h"
#include "iengine/light.h"
#include "iengine/texture.h"
#include "iengine/mesh.h"
#include "iengine/movable.h"
#include "iengine/material.h"
#include "imesh/thing.h"
#include "imesh/object.h"
#include "imesh/sprite3d.h"
#include "ivideo/graph3d.h"
#include "ivideo/graph2d.h"
#include "ivideo/natwin.h"
#include "ivideo/txtmgr.h"
#include "ivideo/texture.h"
#include "ivideo/material.h"
#include "ivideo/fontserv.h"
#include "igraphic/imageio.h"
#include "imap/loader.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/conout.h"
#include "ivaria/reporter.h"
#include "ivaria/stdrep.h"
#include "ivaria/collider.h"
#include "csgeom/quaterni.h"
#include "csgeom/transfrm.h"
#include "csgeom/math3d_d.h"
#include "csgeom/math3d.h"
#include "igeom/polymesh.h"
#include "imap/loader.h"
#include "iaws/aws.h"
#include "iaws/awscnvs.h"
#include "korientation.h"
#include "kblock.h"
#include "kmap.h"
#include "kblockid.h"
#include "ksprite3d.h"
#include "kappstate.h"
#include "skybox.h"
#include "kcamera.h"
#include "kplayer.h"
#include "ske.h"
#include "kquaternion.h"
//?? Define this symbol to use the same code walktest use for collisions.
//???#define ALTERNATIVE_COLLISION
#ifdef ALTERNATIVE_COLLISION
/***********/
static bool s_onGround = false;//??added by me! :)
static int FindIntersection(csCollisionPair& cd,csVector3 line[2])
{
csVector3 tri1[3]; tri1[0]=cd.a1; tri1[1]=cd.b1; tri1[2]=cd.c1;
csVector3 tri2[3]; tri2[0]=cd.a2; tri2[1]=cd.b2; tri2[2]=cd.c2;
return csMath3::FindIntersection(tri1,tri2,line);
}
#define MAXSECTORSOCCUPIED 20
// No more than 1000 collisions ;)
//This array contains all the pairs of triangles colliding.
csCollisionPair l_ourCdContact[1000];
int l_numOurCd;
int FindSectors (csVector3 v, csVector3 d, iSector *s, iSector **sa)
{
int c = 0;
//??? Avoid this sqrt somehow? i.e. by having it in the objects.
float size = qsqrt (d.x * d.x + d.y * d.y + d.z * d.z);
csRef<iSectorIterator> it (g_ske->m_engine->GetNearbySectors (s, v, size));
iSector* sector;
while ((sector = it->Fetch ()) != NULL)
{
sa[c++] = sector;
if (c >= MAXSECTORSOCCUPIED) break;
}
return c;
}
//
//Returns the number of mesh objects collided with the collider passed
//as the argument.
int CollisionDetect (iEngine* Engine, csColliderWrapper *c, iSector* sp,
csReversibleTransform *cdt)
{
int l_hit = 0;
int j;
// Check collision with this sector.
csCollisionPair* CD_contact;
csRef<iObjectIterator> objit (g_ske->m_engine->GetNearbyObjects (sp,
cdt->GetOrigin (), 3)); // 3 should be enough for moving around.
while (!objit->IsFinished ())
{
iObject* mw_obj = objit->GetObject ();
//do not test with the player's colliders!//???
csColliderWrapper* l_cW = csColliderWrapper::GetColliderWrapper (mw_obj);
if (
(l_cW != NULL) && (l_cW->GetID () ==
g_ske->m_player->GetBodyColliderWrapper ()->GetID () )
||
(l_cW != NULL) && (l_cW->GetID () ==
g_ske->m_player->GetFeetsColliderWrapper ()->GetID () )
||
(strchr (mw_obj->GetName (), '@'))//???
)
{
objit->Next ();
continue;
}//if
csRef<iMeshWrapper> mw (SCF_QUERY_INTERFACE (mw_obj, iMeshWrapper));
if (mw)
{
g_ske->m_collisionDet->ResetCollisionPairs ();
if (c->Collide (mw_obj, cdt, &mw->GetMovable ()->GetTransform ()))
l_hit++;
CD_contact = g_ske->m_collisionDet->GetCollisionPairs ();
for (j=0 ; j<g_ske->m_collisionDet->GetCollisionPairCount () ; j++)
l_ourCdContact [l_numOurCd++] = CD_contact[j];
if (g_ske->m_collisionDet->GetOneHitOnly () && l_hit)
return 1;
// TODO, should test which one is the closest.
}
objit->Next ();
}
return l_hit;
}
SCF_VERSION (TerrainInfo, 0, 0, 1);
struct TerrainInfo : public csObject
{
iTerrFuncState* terrfunc;
SCF_DECLARE_IBASE_EXT (csObject);
};
SCF_IMPLEMENT_IBASE_EXT (TerrainInfo)
SCF_IMPLEMENTS_INTERFACE (TerrainInfo)
SCF_IMPLEMENT_IBASE_EXT_END
#else /////////////////////////////////////////////////////////////////////
struct KTriangle
{
csVector3 *m_a,*m_b,*m_c;
float m_distance;
csVector3 *m_normal;
KTriangle () : m_a(NULL), m_b(NULL), m_c(NULL), m_normal(NULL)
{
};
~KTriangle ()
{
delete m_a;delete m_b;delete m_c;delete m_normal;
};
};
/*float PointTriangleDistance (csVector3 p_point, KTriangle* p_tr)
{
csVector3 l_tr1[3];
l_tr1[0] = *p_tr->m_a;
l_tr1[1] = *p_tr->m_b;
l_tr1[2] = *p_tr->m_c;
csVector3 l_tr2[3];
l_tr2[0] = *p_tr->m_a;
l_tr2[1] = *p_tr->m_b;
l_tr2[2] = *p_tr->m_c;
csMath3::FindIntersection ());
}
//
//Order by distance (first the nearest).
static void OrderTriangle (KTriangle** p_triangles, int p_count)
{
qsort (p_triangles, p_count, sizeof (KTriangle), );
/*for(int i=0; i < p_count - 1; i++)
{
int l_max=i;
for(int j = i + 1; j < p_count; j++)
{
if(p_triangles[j]->m_distance < p_triangles[l_max]->m_distance)
{
l_max = j;
}//if
}//for
KTriangle* l_temp = p_triangles[i];
p_triangles[i] = p_triangles[l_max];
p_triangles[l_max] = l_temp;
}//for
}*/
#endif
//test for collision.
void SKE::DoGravity (const csVector3& p_pos, csVector3& p_vel,
const csMatrix3& l_mat)
{
#ifndef ALTERNATIVE_COLLISION
csMatrix3 l_identityMatrix;
csReversibleTransform l_bodyRT (l_identityMatrix, p_pos);
csVector3 l_newPos = p_pos + p_vel;//get the new pos.
csVector3 old = l_newPos;
csVector3 l_tempVel = p_vel;
//collects datas for CollidePath().
int l_count = -1; //start from -1!!!
//??UGLY!
#define MAX_OBJECTS_COLLECTED_FOR_COLLIDEPAT 60
static iCollider* l_coll[MAX_OBJECTS_COLLECTED_FOR_COLLIDEPAT];
static csReversibleTransform l_transByValue[MAX_OBJECTS_COLLECTED_FOR_COLLIDEPAT];
static csReversibleTransform* l_trans[MAX_OBJECTS_COLLECTED_FOR_COLLIDEPAT];
csRef<iObjectIterator> l_objIt =
m_engine->GetNearbyObjects (m_world, l_newPos, 3.0f);
if(l_objIt)
while (l_objIt->HasNext ())
{
iObject* l_o = l_objIt->Next ();
csColliderWrapper* l_cW = csColliderWrapper::GetColliderWrapper (l_o);
//Do not collect the player's colliders (nor meshes named using a @ char).
if (
(l_cW != NULL) &&
(l_cW->GetID () !=
m_player->GetBodyColliderWrapper()->GetID () ) &&
(l_cW->GetID () !=
m_player->GetFeetsColliderWrapper()->GetID () ) &&
(!strchr (l_cW->GetName (), '@'))
)
{
if (l_count++ > MAX_OBJECTS_COLLECTED_FOR_COLLIDEPAT)
break;
l_coll[l_count] = l_cW->GetCollider ();
csRef<iMeshWrapper> l_mW = SCF_QUERY_INTERFACE (l_o, iMeshWrapper);
l_transByValue[l_count] = (l_mW->GetMovable ()->GetFullTransform ());
l_trans[l_count] = & l_transByValue[l_count];
}//if
};//while
//Increment the counter of the collider:
//-1 means none, so -1+1 means 0 colliders.
++l_count;
int i = 0;
//
//Test along the path the collision.
m_collisionDet->ResetCollisionPairs ();
if(csColliderHelper::CollidePath (this->m_collisionDet, m_player->GetBodyColliderWrapper ()->
GetCollider (), &l_bodyRT, 0.1f,/*//??????*/ l_newPos, l_count, l_coll, l_trans) != 1)
{
csVector3 l_firstNewPos = l_newPos;
csCollisionPair *pair = m_collisionDet->GetCollisionPairs ();
int l_pairCount = m_collisionDet->GetCollisionPairCount ();
KTriangle** l_tr = new KTriangle* [l_pairCount];
for (i = 0; i < l_pairCount; i++)
{
l_tr[i] = new KTriangle;
l_tr[i]->m_a = new csVector3 (pair[i].a2);
l_tr[i]->m_b = new csVector3 (pair[i].b2);
l_tr[i]->m_c = new csVector3 (pair[i].c2);
//???l_tr[i]->m_distance = PointTriangleDistance (l_newPos, l_tr[i]);
csVector3 l_nV;
csMath3::CalcNormal(l_nV,pair[i].a2,pair[i].b2,pair[i].c2);
l_nV.Normalize();
l_tr[i]->m_normal = new csVector3 (l_nV);
}//for
for(i = 0; i < l_pairCount; i++)
{
if (*l_tr[i]->m_normal * p_vel > 0)
continue;//if the normal is in the other direction, disregard it.
l_tempVel = (l_firstNewPos + -(p_vel % *l_tr[i]->m_normal) % *l_tr[i]->m_normal) -
p_pos;
if (l_tempVel == 0)
continue;
l_newPos = csVector3 (p_pos + l_tempVel);
if (csColliderHelper::CollidePath (m_collisionDet, m_player->GetBodyColliderWrapper()->
GetCollider(), &l_bodyRT, 0.1f,/*//??????*/ l_newPos, l_count, l_coll, l_trans)
== 1)
{
p_vel = l_tempVel;
return;//No collision.
}//if
else
{
//
//Test if we are moving. If yes, just return the movement, if not, try another normal.
if( ((l_newPos - p_pos ).Norm() ) > SMALL_EPSILON )
{
p_vel = l_newPos - p_pos;
return;
}//if
}//else
}//for
if(i == l_pairCount)
{
p_vel = l_newPos - p_pos;
return;
}//if
}//if
#else
csVector3 l_newPos = p_pos + p_vel;
csMatrix3 l_identity;
csOrthoTransform l_test (l_identity, l_newPos);
iSector* l_sectors[MAXSECTORSOCCUPIED];
int l_numSectors = FindSectors (l_newPos, 4.0f * m_player->m_bodyRadius,
g_ske->m_kcam->GetCamera()->GetSector(), l_sectors);
l_numOurCd = 0;
g_ske->m_collisionDet->SetOneHitOnly (false);
int l_hits = 0;
// Check to see if there are any terrains, if so test against those.
// This routine will automatically adjust the transform to the highest
// terrain at this point.
// @@@@@@ The following code supports only one terrain in a sector!
int k = 0;
for (; k < l_numSectors ; k++)
{
iMeshList* l_mL = l_sectors[k]->GetMeshes ();
if (l_mL->GetCount () > 0)
{
csRef<TerrainInfo> l_tI (CS_GET_CHILD_OBJECT (l_sectors[k]->
QueryObject (), TerrainInfo));
if (l_tI)
{
if (l_tI->terrfunc)
{
l_hits += l_tI->terrfunc->CollisionDetect (&l_test);
}//if
}//if
else
{
l_tI.AttachNew (new TerrainInfo ());
l_tI->terrfunc = NULL; // No terrain found yet.
int i = 0;
for ( ; i < l_mL->GetCount () ; i++)
{
iMeshWrapper* l_terrainMW = l_mL->Get (i);
csRef<iTerrFuncState> l_state (SCF_QUERY_INTERFACE (l_terrainMW
->GetMeshObject (), iTerrFuncState));
if (l_state)
{
l_hits += l_state->CollisionDetect (&l_test);
l_tI->terrfunc = l_state;
break;
}//if
}//for
csRef<iObject> l_iObj (SCF_QUERY_INTERFACE (l_tI, iObject));
l_sectors[k]->QueryObject ()->ObjAdd (l_iObj);
}//else
}//if
}//for
// If there were hits with the terrain we update our new position
// here. Side note: this could moved outside the loop above because
// a compiler bug with gcc 2.7.2 prevented it from working when inside
// the loop.
if (l_hits)
l_newPos = l_test.GetOrigin ();
int j;
if (l_hits == 0)
{
g_ske->m_collisionDet->ResetCollisionPairs ();
csVector3 l_kkk(l_newPos); l_kkk += 0.5f;//???
l_test = csOrthoTransform (csMatrix3 (), l_kkk);
for ( ; l_numSectors-- ; )
l_hits += CollisionDetect (g_ske->m_engine, g_ske->
m_player->GetBodyColliderWrapper (), l_sectors[l_numSectors],
&l_test);
#if CS_DEBUG
if (l_numOurCd > 0)
printf ("body: hits=%d num_our_cd=%d\n", l_hits, l_numOurCd);
#endif // CS_DEBUG
for (j = 0 ; j < l_numOurCd; j++)
{
csCollisionPair& l_cP = l_ourCdContact[j];
csVector3 l_n = ((l_cP.c2 - l_cP.b2) % (l_cP.b2 - l_cP.a2)).Unit ();
if (l_n * p_vel < 0)
continue;
p_vel = -(p_vel % l_n) % l_n;
}
// We now know our (possible) velocity. Let's try to move up or down, if possible
l_newPos = p_pos + p_vel;
// Try again, and don't move if we're still in a wall
l_numSectors = FindSectors (l_newPos, 4.0f * g_ske->m_player->
m_bodyRadius,
g_ske->m_kcam->GetCamera()->GetSector(), l_sectors);
g_ske->m_collisionDet->SetOneHitOnly (false);
g_ske->m_collisionDet->ResetCollisionPairs ();
//????l_test = csOrthoTransform (csMatrix3(), l_newPos);
l_kkk = csVector3(l_newPos); l_kkk += 0.5f;//???
l_test = csOrthoTransform (csMatrix3 (), l_kkk);
int l_hit = 0;
for (; l_numSectors--;)
if (CollisionDetect (g_ske->m_engine, g_ske->m_player->
GetBodyColliderWrapper (), l_sectors[l_numSectors], &l_test) > 0)
{
l_newPos -= p_vel;
break;
}//if
l_test = csOrthoTransform (csMatrix3(), l_newPos);
l_numSectors = FindSectors (l_newPos, 4.0f * g_ske->m_player->
m_feetsRadius, g_ske->m_kcam->GetCamera()->GetSector(), l_sectors);
l_numOurCd = 0;
g_ske->m_collisionDet->SetOneHitOnly (false);
g_ske->m_collisionDet->ResetCollisionPairs ();
l_kkk = csVector3 (l_newPos); l_kkk += 0.0f;//???
l_test = csOrthoTransform (csMatrix3 (), l_kkk);
for ( ; l_numSectors-- ; )
l_hit += CollisionDetect (g_ske->m_engine, g_ske->m_player->
GetFeetsColliderWrapper (),
l_sectors[l_numSectors], &l_test);
if (!l_hit)
{
s_onGround = false;
if (g_ske->m_doGravity)
p_vel.y -= 0.002;//do the actual gravity force!
}//if
else
{
float max_y = -1e10;
for (j = 0 ; j < l_numOurCd ; j++)
{
csCollisionPair cd = l_ourCdContact[j];
csVector3 n = ((cd.c2 - cd.b2) % (cd.b2 - cd.a2)).Unit();
if (n * csVector3(0,-1,0) < 0.7)
continue;
csVector3 line[2];
cd.a1 += l_newPos;
cd.b1 += l_newPos;
cd.c1 += l_newPos;
if (FindIntersection (cd,line))
{
if (line[0].y>max_y)
max_y=line[0].y;
if (line[1].y>max_y)
max_y=line[1].y;
}//if
}//for
float p = l_newPos.y - max_y /*+ OYL*/ + 0.01;//??????????
if (ABS(p) < 0.5f /*DYL*/-0.01)//??? i added 0.5f, i.e. the legs offset!
{
if (max_y != -1e10)
l_newPos.y = max_y /*- OYL */- 0.01;
if (p_vel.y < 0)
p_vel.y = 0;
}//if
s_onGround = true;
}//else
}
//??l_newPos -= g_ske->m_kcam->GetCamera ()->GetTransform ().GetOrigin ();
//???g_ske->m_kcam->GetCamera ()->MoveWorld (l_newPos);
g_ske->m_player->m_mov3d.SetVelocity (
/*g_ske->m_kcam->GetCamera ()->GetTransform ().GetO2T () **/ p_vel);
p_vel = l_newPos - p_pos;//??? at this point, p_vel becomes the returned
//value of this function: infact it is the movement we can achieve in respect
//to the original position p_pos
/*//??????if(!g_ske->m_doGravity)
{
csVector3 l_vel = g_ske->m_player->m_mov3d.GetVelocity ();
l_vel.y -= SIGN (l_vel.y) * MIN (0.017, ABS (l_vel.y));
g_ske->m_player->m_mov3d.SetVelocity (l_vel);
}//if*/
#endif
}
/*
//??
/*char s_buffer[120];
static csVector3 lll;
if((lll.x!=l_newPos.x)||(lll.y!=l_newPos.y)||(lll.z!=l_newPos.z))
{
lll=csVector3(l_newPos);
sprintf(s_buffer,"testing for (%7.03f,%7.03f) \n",l_newPos.x,l_newPos.z);
m_consoleOutput->PutText(s_buffer);
}//if*/
See more files for this project here