123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- /* Copyright (c) 2002-2012 Croteam Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as published by
- the Free Software Foundation
- 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 General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
- #include "stdh.h"
- #include <Engine/Entities/Entity.h>
- #include <Engine/Entities/EntityCollision.h>
- #include <Engine/Base/ListIterator.inl>
- #include <Engine/Math/Geometry.inl>
- #include <Engine/Math/Clipping.inl>
- #include <Engine/Brushes/Brush.h>
- #include <Engine/Templates/DynamicArray.cpp>
- #include <Engine/Templates/StaticArray.cpp>
- #include <Engine/Network/Network.h>
- #include <Engine/Network/SessionState.h>
- class CClipTest {
- public:
- CEntity *ct_penEntity; // the entity
- CEntity *ct_penObstacle; // obstacle entity (if cannot change)
- INDEX ct_iNewCollisionBox; // index of new collision box to set
- CCollisionInfo ct_ciNew; // collision info with new box
- FLOATaabbox3D ct_boxTotal; // union box for old and new in absolute coordinates
- CListHead ct_lhActiveSectors; // sectors that may be of interest
- BOOL PointTouchesSphere(
- const FLOAT3D &vPoint,
- const FLOAT3D &vSphereCenter,
- const FLOAT fSphereRadius);
- BOOL PointTouchesCylinder(
- const FLOAT3D &vPoint,
- const FLOAT3D &vCylinderBottomCenter,
- const FLOAT3D &vCylinderTopCenter,
- const FLOAT fCylinderRadius);
- // project spheres of a collision info to given placement
- void ProjectSpheresToPlacement(CCollisionInfo &ci,
- FLOAT3D &vPosition, FLOATmatrix3D &mRotation);
- // test if a sphere touches brush polygon
- BOOL SphereTouchesBrushPolygon(const CMovingSphere &msMoving,
- CBrushPolygon *pbpoPolygon);
- // test if entity touches brush polygon
- BOOL EntityTouchesBrushPolygon(CBrushPolygon *pbpoPolygon);
- // test if an entity can change to a new collision box without intersecting anything
- BOOL CanChange(CEntity *pen, INDEX iNewCollisionBox);
- ~CClipTest(void);
- };
- // test if an entity can change to a new collision box without intersecting anything
- BOOL CanEntityChangeCollisionBox(CEntity *pen, INDEX iNewCollisionBox, CEntity **ppenObstacle)
- {
- // if the entity is not linked to any sectors
- if (pen->en_rdSectors.IsEmpty()) {
- // make sure that the classification is ok
- pen->FindSectorsAroundEntity();
- }
- CClipTest ct;
- BOOL bCan = ct.CanChange(pen, iNewCollisionBox);
- *ppenObstacle = ct.ct_penObstacle;
- return bCan;
- }
- // project spheres of a collision info to given placement
- void CClipTest::ProjectSpheresToPlacement(CCollisionInfo &ci,
- FLOAT3D &vPosition, FLOATmatrix3D &mRotation)
- {
- // for each sphere
- FOREACHINSTATICARRAY(ci.ci_absSpheres, CMovingSphere, itms) {
- // project it in start point
- itms->ms_vRelativeCenter0 = itms->ms_vCenter*mRotation+vPosition;
- }
- }
- // test point to a sphere
- BOOL CClipTest::PointTouchesSphere(
- const FLOAT3D &vPoint,
- const FLOAT3D &vSphereCenter,
- const FLOAT fSphereRadius)
- {
- FLOAT fD = (vSphereCenter-vPoint).Length();
- return fD<fSphereRadius;
- }
- // test sphere to the edge (point to the edge cylinder)
- BOOL CClipTest::PointTouchesCylinder(
- const FLOAT3D &vPoint,
- const FLOAT3D &vCylinderBottomCenter,
- const FLOAT3D &vCylinderTopCenter,
- const FLOAT fCylinderRadius)
- {
- const FLOAT3D vCylinderBottomToTop = vCylinderTopCenter - vCylinderBottomCenter;
- const FLOAT fCylinderBottomToTopLength = vCylinderBottomToTop.Length();
- const FLOAT3D vCylinderDirection = vCylinderBottomToTop/fCylinderBottomToTopLength;
-
- FLOAT3D vBottomToPoint = vPoint-vCylinderBottomCenter;
- FLOAT fPointL = vBottomToPoint%vCylinderDirection;
- // if not between top and bottom
- if (fPointL<0 || fPointL>fCylinderBottomToTopLength) {
- // doesn't touch
- return FALSE;
- }
- // find distance from point to cylinder axis
- FLOAT fD = (vBottomToPoint-vCylinderDirection*fPointL).Length();
- return fD<fCylinderRadius;
- }
- // test if a sphere touches brush polygon
- BOOL CClipTest::SphereTouchesBrushPolygon(const CMovingSphere &msMoving,
- CBrushPolygon *pbpoPolygon)
- {
- const FLOATplane3D &plPolygon = pbpoPolygon->bpo_pbplPlane->bpl_plAbsolute;
- // calculate point distance from polygon plane
- FLOAT fDistance = plPolygon.PointDistance(msMoving.ms_vRelativeCenter0);
- // if is further away than sphere radius
- if (fDistance>msMoving.ms_fR || fDistance<-msMoving.ms_fR) {
- // no collision
- return FALSE;
- }
- // calculate coordinate projected to the polygon plane
- FLOAT3D vPosMid = msMoving.ms_vRelativeCenter0;
- FLOAT3D vHitPoint = plPolygon.ProjectPoint(vPosMid);
- // find major axes of the polygon plane
- INDEX iMajorAxis1, iMajorAxis2;
- GetMajorAxesForPlane(plPolygon, iMajorAxis1, iMajorAxis2);
- // create an intersector
- CIntersector isIntersector(vHitPoint(iMajorAxis1), vHitPoint(iMajorAxis2));
- // for all edges in the polygon
- FOREACHINSTATICARRAY(pbpoPolygon->bpo_abpePolygonEdges, CBrushPolygonEdge,
- itbpePolygonEdge) {
- // get edge vertices (edge direction is irrelevant here!)
- const FLOAT3D &vVertex0 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute;
- const FLOAT3D &vVertex1 = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute;
- // pass the edge to the intersector
- isIntersector.AddEdge(
- vVertex0(iMajorAxis1), vVertex0(iMajorAxis2),
- vVertex1(iMajorAxis1), vVertex1(iMajorAxis2));
- }
- // if the polygon is intersected by the ray
- if (isIntersector.IsIntersecting()) {
- return TRUE;
- }
- // for each edge in polygon
- FOREACHINSTATICARRAY(pbpoPolygon->bpo_abpePolygonEdges, CBrushPolygonEdge, itbpe) {
- // get edge vertices (edge direction is important here!)
- FLOAT3D vVertex0, vVertex1;
- itbpe->GetVertexCoordinatesAbsolute(vVertex0, vVertex1);
- // test sphere to the edge (point to the edge cylinder)
- if (PointTouchesCylinder(
- msMoving.ms_vRelativeCenter0, // point,
- vVertex0, // cylinder bottom center,
- vVertex1, // cylinder top center,
- msMoving.ms_fR // cylinder radius
- )) {
- return TRUE;
- }
- // test sphere to the first vertex
- // NOTE: using point to sphere collision
- if (PointTouchesSphere(
- msMoving.ms_vRelativeCenter0, // pount
- vVertex0, // sphere center
- msMoving.ms_fR // sphere radius
- )) {
- return TRUE;
- }
- }
- return FALSE;
- }
- // test if entity touches brush polygon
- BOOL CClipTest::EntityTouchesBrushPolygon(CBrushPolygon *pbpoPolygon)
- {
- // for each sphere
- FOREACHINSTATICARRAY(ct_ciNew.ci_absSpheres, CMovingSphere, itms) {
- // if it touches
- if (SphereTouchesBrushPolygon(*itms, pbpoPolygon)) {
- return TRUE;
- }
- }
- return FALSE;
- }
- // test if an entity can change to a new collision box without intersecting anything
- BOOL CClipTest::CanChange(CEntity *pen, INDEX iNewCollisionBox)
- {
- // can be used only for models
- ASSERT(
- pen->en_RenderType==CEntity::RT_MODEL ||
- pen->en_RenderType==CEntity::RT_EDITORMODEL ||
- pen->en_RenderType==CEntity::RT_SKAMODEL ||
- pen->en_RenderType==CEntity::RT_SKAEDITORMODEL);
- // safety check
- if (pen->en_pciCollisionInfo==NULL) {
- return FALSE;
- }
- // remember parameters
- ct_penEntity = pen;
- ct_iNewCollisionBox = iNewCollisionBox;
- ct_penObstacle = NULL;
- // create new temporary collision info
- ct_ciNew.FromModel(pen, iNewCollisionBox);
- // project it to entity placement
- ProjectSpheresToPlacement(ct_ciNew,
- pen->en_plPlacement.pl_PositionVector, pen->en_mRotation);
- // get total bounding box encompassing both old and new collision boxes
- FLOATaabbox3D boxOld, boxNew;
- ASSERT(ct_penEntity->en_pciCollisionInfo!=NULL);
- CCollisionInfo &ciOld = *ct_penEntity->en_pciCollisionInfo;
- ciOld.MakeBoxAtPlacement(ct_penEntity->en_plPlacement.pl_PositionVector,
- ct_penEntity->en_mRotation, boxOld);
- ct_ciNew.MakeBoxAtPlacement(ct_penEntity->en_plPlacement.pl_PositionVector,
- ct_penEntity->en_mRotation, boxNew);
- ct_boxTotal = boxOld;
- ct_boxTotal |= boxNew;
- // for each zoning sector that this entity is in
- {FOREACHSRCOFDST(ct_penEntity->en_rdSectors, CBrushSector, bsc_rsEntities, pbsc)
- // add it to list of active sectors
- ct_lhActiveSectors.AddTail(pbsc->bsc_lnInActiveSectors);
- ENDFOR}
- // for each active sector
- FOREACHINLIST(CBrushSector, bsc_lnInActiveSectors, ct_lhActiveSectors, itbsc) {
- // for non-zoning brush entities in the sector
- {FOREACHDSTOFSRC(itbsc->bsc_rsEntities, CEntity, en_rdSectors, pen)
- if (pen->en_RenderType!=CEntity::RT_BRUSH&&
- (_pNetwork->ga_ulDemoMinorVersion<=4 || pen->en_RenderType!=CEntity::RT_FIELDBRUSH)) {
- break; // brushes are sorted first in list
- }
- // get first mip
- CBrushMip *pbm = pen->en_pbrBrush->GetFirstMip();
- // if brush mip exists for that mip factor
- if (pbm!=NULL) {
- // for each sector in the mip
- {FOREACHINDYNAMICARRAY(pbm->bm_abscSectors, CBrushSector, itbscNonZoning) {
- CBrushSector &bscNonZoning = *itbscNonZoning;
- // add it to list of active sectors
- if(!bscNonZoning.bsc_lnInActiveSectors.IsLinked()) {
- ct_lhActiveSectors.AddTail(bscNonZoning.bsc_lnInActiveSectors);
- }
- }}
- }
- ENDFOR}
- // if the sector's brush doesn't have collision
- CEntity *penSectorBrush = itbsc->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
- if (penSectorBrush->en_ulCollisionFlags==0 ||
- (_pNetwork->ga_ulDemoMinorVersion>2 && penSectorBrush->en_RenderType!=CEntity::RT_BRUSH) ) {
- // skip it
- continue;
- }
- // for each polygon in the sector
- FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) {
- CBrushPolygon *pbpo = itbpo;
- // if its bbox has no contact with bbox to test
- if (!pbpo->bpo_boxBoundingBox.HasContactWith(ct_boxTotal) ) {
- // skip it
- continue;
- }
- // if it is passable
- if (pbpo->bpo_ulFlags&BPOF_PASSABLE) {
- // for each sector related to the portal
- {FOREACHDSTOFSRC(pbpo->bpo_rsOtherSideSectors, CBrushSector, bsc_rdOtherSidePortals, pbscRelated)
- // if the sector is not active
- if (!pbscRelated->bsc_lnInActiveSectors.IsLinked()) {
- // add it to active list
- ct_lhActiveSectors.AddTail(pbscRelated->bsc_lnInActiveSectors);
- }
- ENDFOR}
- // if it is not passable
- } else {
- // if entity touches it
- if (EntityTouchesBrushPolygon(pbpo)) {
- // test fails
- ct_penObstacle = pbpo->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
- return FALSE;
- }
- }
- }
- }
- return TRUE;
- }
- CClipTest::~CClipTest(void)
- {
- // clear list of active sectors
- {FORDELETELIST(CBrushSector, bsc_lnInActiveSectors, ct_lhActiveSectors, itbsc) {
- itbsc->bsc_lnInActiveSectors.Remove();
- }}
- }
|