123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- //
- // The graphics engine GLXEngine. The unit of GXScene for Delphi
- //
- unit GXS.Collision;
- (* Collision-detection management *)
- interface
- {$I Stage.Defines.inc}
- uses
- System.Classes,
- System.SysUtils,
- GXS.XCollection,
- Stage.VectorGeometry,
- Stage.Manager,
- Stage.VectorTypes,
- GXS.VectorLists,
- GXS.GeometryBB,
- GXS.Scene,
- GXS.VectorFileObjects;
- type
- TgxBCollision = class;
- TObjectCollisionEvent = procedure(Sender: TObject;
- object1, object2: TgxBaseSceneObject) of object;
- (* Defines how fine collision bounding is for a particular object.
- Possible values are :
- cbmPoint : the object is punctual and may only collide with volumes
- cbmSphere : the object is defined by its bounding sphere (sphere radius
- is the max of axis-aligned dimensions)
- cbmEllipsoid the object is defined by its bounding axis-aligned ellipsoid
- cbmCube : the object is defined by a bounding axis-aligned "cube"
- cbmFaces : the object is defined by its faces (needs object-level support,
- if unavalaible, uses cbmCube code) *)
- TCollisionBoundingMode = (cbmPoint, cbmSphere, cbmEllipsoid, cbmCube,
- cbmFaces);
- TFastCollisionChecker = function(obj1, obj2: TgxBaseSceneObject): Boolean;
- PFastCollisionChecker = ^TFastCollisionChecker;
- TgxCollisionManager = class(TComponent)
- private
- FClients: TList;
- FOnCollision: TObjectCollisionEvent;
- protected
- procedure RegisterClient(aClient: TgxBCollision);
- procedure DeRegisterClient(aClient: TgxBCollision);
- procedure DeRegisterAllClients;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure CheckCollisions;
- published
- property OnCollision: TObjectCollisionEvent read FOnCollision
- write FOnCollision;
- end;
- (* Collision detection behaviour.
- Allows an object to register to a TCollisionManager and be accounted for
- in collision-detection and distance calculation mechanisms.
- An object may have multiple TgxBCollision, registered to multiple collision
- managers, however if multiple behaviours share the same manager, only one
- of them will be accounted for, others will be ignored. *)
- TgxBCollision = class(TgxBehaviour)
- private
- FBoundingMode: TCollisionBoundingMode;
- FManager: TgxCollisionManager;
- FManagerName: String; // NOT persistent, temporarily used for persistence
- FGroupIndex: Integer;
- protected
- procedure SetGroupIndex(const value: Integer);
- procedure SetManager(const val: TgxCollisionManager);
- procedure WriteToFiler(writer: TWriter); override;
- procedure ReadFromFiler(reader: TReader); override;
- procedure Loaded; override;
- public
- constructor Create(AOwner: TXCollection); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- class function FriendlyName: String; override;
- class function FriendlyDescription: String; override;
- published
- // Refers the collision manager.
- property Manager: TgxCollisionManager read FManager write SetManager;
- property BoundingMode: TCollisionBoundingMode read FBoundingMode
- write FBoundingMode;
- property GroupIndex: Integer read FGroupIndex write SetGroupIndex;
- end;
- (* Fast Collision detection routines that are heavily
- specialized and just return a boolean *)
- function FastCheckPointVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckPointVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckPointVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckPointVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckSphereVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckSphereVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckSphereVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckSphereVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckEllipsoidVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckEllipsoidVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckEllipsoidVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckEllipsoidVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckCubeVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckCubeVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckCubeVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckCubeVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- function FastCheckCubeVsFace(obj1, obj2: TgxBaseSceneObject): Boolean;
- // experimental
- function FastCheckFaceVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- // experimental
- function FastCheckFaceVsFace(obj1, obj2: TgxBaseSceneObject): Boolean;
- (* Returns true when the bounding box cubes does intersect the other.
- Also true when the one cube does contain the other completely. *)
- function IntersectCubes(obj1, obj2: TgxBaseSceneObject): Boolean; overload;
- (* Returns or creates the TgxBCollision within the given behaviours.
- This helper function is convenient way to access a TgxBCollision. *)
- function GetOrCreateCollision(behaviours: TgxBehaviours)
- : TgxBCollision; overload;
- (* Returns or creates the TgxBCollision within the given object's behaviours.
- This helper function is convenient way to access a TgxBCollision. *)
- function GetOrCreateCollision(obj: TgxBaseSceneObject): TgxBCollision; overload;
- implementation // ------------------------------------------------------------
- const
- cEpsilon: Single = 1E-6;
- const
- cFastCollisionChecker: array [cbmPoint .. cbmFaces, cbmPoint .. cbmFaces]
- of TFastCollisionChecker = ((FastCheckPointVsPoint, FastCheckPointVsSphere,
- FastCheckPointVsEllipsoid, FastCheckPointVsCube, FastCheckPointVsCube),
- (FastCheckSphereVsPoint, FastCheckSphereVsSphere,
- FastCheckSphereVsEllipsoid, FastCheckSphereVsCube, FastCheckSphereVsCube),
- (FastCheckEllipsoidVsPoint, FastCheckEllipsoidVsSphere,
- FastCheckEllipsoidVsEllipsoid, FastCheckEllipsoidVsCube,
- FastCheckEllipsoidVsCube), (FastCheckCubeVsPoint, FastCheckCubeVsSphere,
- FastCheckCubeVsEllipsoid, FastCheckCubeVsCube, FastCheckCubeVsFace),
- (FastCheckCubeVsPoint, FastCheckCubeVsSphere, FastCheckCubeVsEllipsoid,
- FastCheckFaceVsCube, FastCheckFaceVsFace));
- // Collision utility routines
- function FastCheckPointVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := (obj2.SqrDistanceTo(obj1.AbsolutePosition) <= cEpsilon);
- end;
- function FastCheckPointVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := (obj2.SqrDistanceTo(obj1.AbsolutePosition) <=
- Sqr(obj2.BoundingSphereRadius));
- end;
- function FastCheckPointVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- v: TVector4f;
- begin
- // calc vector expressed in local coordinates (for obj2)
- v := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // rescale to unit dimensions
- // DivideVector(v, obj2.Scale.AsVector); //DanB - Scale removed in VectorTransform
- DivideVector(v, obj2.AxisAlignedDimensionsUnscaled);
- // ScaleVector(v,obj2.Scale.AsVector);
- // ScaleVector();
- v.W := 0;
- // if norm is below 1, collision
- Result := (VectorNorm(v) <= 1 { Sqr(obj2.BoundingSphereRadius) } );
- // since radius*radius = 1/2*1/2 = 1/4 for unit sphere
- end;
- function FastCheckPointVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- v: TVector4f;
- begin
- // calc vector expressed in local coordinates (for obj2)
- v := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // rescale to unit dimensions
- DivideVector(v, obj2.AxisAlignedDimensionsUnscaled);
- // if abs() of all components are below 1, collision
- Result := (MaxAbsXYZComponent(v) <= 1);
- end;
- function FastCheckSphereVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := (obj1.SqrDistanceTo(obj2.AbsolutePosition) <=
- Sqr(obj1.BoundingSphereRadius));
- end;
- function FastCheckSphereVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := (obj1.SqrDistanceTo(obj2.AbsolutePosition) <=
- Sqr(obj1.BoundingSphereRadius + obj2.BoundingSphereRadius));
- end;
- function FastCheckSphereVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- v: TVector4f;
- aad: TVector4f;
- begin
- // express in local coordinates (for obj2)
- v := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // calc local vector, and rescale to unit dimensions
- // VectorSubstract(pt1, obj2.AbsolutePosition, v);
- aad := VectorAdd(obj2.AxisAlignedDimensions, obj1.BoundingSphereRadius);
- DivideVector(v, aad);
- ScaleVector(v, obj2.Scale.AsVector); // by DanB
- v.W := 0;
- // if norm is below 1, collision
- Result := (VectorNorm(v) <= 1);
- end;
- function FastCheckSphereVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- v: TVector4f;
- aad: TVector4f;
- r, r2: Single;
- begin
- // express in local coordinates (for cube "obj2")
- // v gives the vector from obj2 to obj1 expressed in obj2's local system
- v := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // because of symmetry we can make abs(v)
- v.X := abs(v.X);
- v.Y := abs(v.Y);
- v.Z := abs(v.Z);
- ScaleVector(v, obj2.Scale.AsVector);
- aad := obj2.AxisAlignedDimensions; // should be abs at all!
- VectorSubtract(v, aad, v); // v holds the distance in each axis
- v.W := 0;
- r := obj1.BoundingSphereRadius { UnScaled };
- r2 := Sqr(r);
- if (v.X > 0) then
- begin
- if (v.Y > 0) then
- begin
- if (v.Z > 0) then
- begin
- // v is outside axis parallel projection, so use distance to edge point
- Result := (VectorNorm(v) <= r2);
- end
- else
- begin
- // v is inside z axis projection, but outside x-y projection
- Result := (VectorNorm(v.X, v.Y) <= r2);
- end
- end
- else
- begin
- if (v.Z > 0) then
- begin
- // v is inside y axis projection, but outside x-z projection
- Result := (VectorNorm(v.X, v.Z) <= r2);
- end
- else
- begin
- // v is inside y-z axis projection, but outside x projection
- Result := (v.X <= r);
- end
- end
- end
- else
- begin
- if (v.Y > 0) then
- begin
- if (v.Z > 0) then
- begin
- // v is inside x axis projection, but outside y-z projection
- Result := (VectorNorm(v.Y, v.Z) <= r2);
- end
- else
- begin
- // v is inside x-z projection, but outside y projection
- Result := (v.Y <= r);
- end
- end
- else
- begin
- if (v.Z > 0) then
- begin
- // v is inside x-y axis projection, but outside z projection
- Result := (v.Z <= r);
- end
- else
- begin
- // v is inside all axes parallel projection, so it is inside cube
- Result := true;
- end;
- end
- end;
- end;
- function FastCheckEllipsoidVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckPointVsEllipsoid(obj2, obj1);
- end;
- function FastCheckEllipsoidVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckSphereVsEllipsoid(obj2, obj1);
- end;
- function FastCheckEllipsoidVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- v1, v2: TVector4f;
- begin
- // express in local coordinates (for obj2)
- v1 := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // calc local vector, and rescale to unit dimensions
- // VectorSubstract(pt, obj2.AbsolutePosition, v1);
- DivideVector(v1, obj2.AxisAlignedDimensions);
- v1.W := 0;
- // express in local coordinates (for obj1)
- v2 := VectorTransform(obj2.AbsolutePosition, obj1.InvAbsoluteMatrix);
- // calc local vector, and rescale to unit dimensions
- // VectorSubstract(pt, obj1.AbsolutePosition, v2);
- DivideVector(v2, obj1.AxisAlignedDimensions);
- v2.W := 0;
- // if sum of norms is below 2, collision
- Result := (VectorNorm(v1) + VectorNorm(v2) <= 2);
- end;
- function FastCheckEllipsoidVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- { current implementation assumes Ellipsoid as Sphere }
- var
- v: TVector4f;
- aad: TVector4f;
- begin
- // express in local coordinates (for obj2)
- v := VectorTransform(obj1.AbsolutePosition, obj2.InvAbsoluteMatrix);
- // calc local vector, and rescale to unit dimensions
- aad := VectorAdd(obj2.AxisAlignedDimensionsUnscaled,
- obj1.BoundingSphereRadius);
- DivideVector(v, aad);
- v.W := 0;
- // if norm is below 1, collision
- Result := (VectorNorm(v) <= 1);
- end;
- function FastCheckCubeVsPoint(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckPointVsCube(obj2, obj1);
- end;
- function FastCheckCubeVsSphere(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckSphereVsCube(obj2, obj1);
- end;
- function FastCheckCubeVsEllipsoid(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckEllipsoidVsCube(obj2, obj1);
- end;
- procedure InitArray(v: TVector4f; var pt: array of TVector4f);
- // calculate the cube edge points from the axis aligned dimension
- begin
- pt[0] := VectorMake(-v.X, -v.Y, -v.Z, 1);
- pt[1] := VectorMake(v.X, -v.Y, -v.Z, 1);
- pt[2] := VectorMake(v.X, v.Y, -v.Z, 1);
- pt[3] := VectorMake(-v.X, v.Y, -v.Z, 1);
- pt[4] := VectorMake(-v.X, -v.Y, v.Z, 1);
- pt[5] := VectorMake(v.X, -v.Y, v.Z, 1);
- pt[6] := VectorMake(v.X, v.Y, v.Z, 1);
- pt[7] := VectorMake(-v.X, v.Y, v.Z, 1);
- end;
- function DoCubesIntersectPrim(obj1, obj2: TgxBaseSceneObject): Boolean;
- // first check if any edge point of "cube" obj1 lies within "cube" obj2
- // else, for each "wire" in then wireframe of the "cube" obj1, check if it
- // intersects with one of the "planes" of "cube" obj2
- function CheckWire(p0, p1, pl: TVector4f): Boolean;
- // check "wire" line (p0,p1) for intersection with each plane, given from
- // axis aligned dimensions pl
- // - calculate "direction" d: p0 -> p1
- // - for each axis (0..2) do
- // - calculate line parameter t of intersection with plane pl[I]
- // - if not in range [0..1] (= not within p0->p1), no intersection
- // - else
- // - calculate intersection point s = p0 + t*d
- // - for both other axes check if coordinates are within range
- // - do the same for opposite plane -pl[I]
- var
- t: Single;
- d, s: TVector4f;
- i, j, k: Integer;
- begin
- Result := true;
- VectorSubtract(p1, p0, d); // d: direction p0 -> p1
- for i := 0 to 2 do
- begin
- if d.v[i] = 0 then
- begin // wire is parallel to plane
- // this case will be handled by the other planes
- end
- else
- begin
- j := (i + 1) mod 3;
- k := (j + 1) mod 3;
- t := (pl.V[i] - p0.V[i]) / d.V[i]; // t: line parameter of intersection
- if IsInRange(t, 0, 1) then
- begin
- s := p0;
- CombineVector(s, d, t); // calculate intersection
- // if the other two coordinates lie within the ranges, collision
- if IsInRange(s.v[j], -pl.v[j], pl.v[j]) and
- IsInRange(s.v[k], -pl.v[k], pl.v[k]) then
- Exit;
- end;
- t := (-pl.v[i] - p0.v[i]) / d.v[i]; // t: parameter of intersection
- if IsInRange(t, 0, 1) then
- begin
- s := p0;
- CombineVector(s, d, t); // calculate intersection
- // if the other two coordinates lie within the ranges, collision
- if IsInRange(s.v[j], -pl.v[j], pl.v[j]) and
- IsInRange(s.v[k], -pl.v[k], pl.v[k]) then
- Exit;
- end;
- end;
- end;
- Result := false;
- end;
- const
- cWires: array [0 .. 11, 0 .. 1] of Integer = ((0, 1), (1, 2), (2, 3), (3, 0),
- (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7));
- var
- pt1: array [0 .. 7] of TVector4f;
- M: TMatrix4f;
- i: Integer;
- aad: TVector4f;
- begin
- Result := true;
- aad := obj2.AxisAlignedDimensionsUnscaled; // DanB experiment
- InitArray(obj1.AxisAlignedDimensionsUnscaled, pt1);
- // calculate the matrix to transform obj1 into obj2
- MatrixMultiply(obj1.AbsoluteMatrix, obj2.InvAbsoluteMatrix, M);
- for i := 0 to 7 do
- begin // transform points of obj1
- pt1[i] := VectorTransform(pt1[i], M);
- // check if point lies inside "cube" obj2, collision
- if IsInCube(pt1[i], aad) then
- Exit;
- end;
- for i := 0 to 11 do
- begin
- if CheckWire(pt1[cWires[i, 0]], pt1[cWires[i, 1]], aad) then
- Exit;
- end;
- Result := false;
- end;
- function FastCheckCubeVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- { var
- aad1,aad2 : TVector4f;
- D1,D2,D : Double;
- }
- begin
- // DanB -this bit of code isn't needed (since collision code does BoundingBox elimination)
- // also is incorrect when objects further up the "object tree" are scaled
- {
- aad1 := obj1.AxisAlignedDimensions;
- aad2 := obj2.AxisAlignedDimensions;
- D1 := VectorLength(aad1);
- D2 := VectorLength(aad2);
- D := Sqrt(obj1.SqrDistanceTo(obj2.AbsolutePosition));
- if D>(D1+D2) then result := false
- else begin
- D1 := MinAbsXYZComponent(aad1);
- D2 := MinAbsXYZComponent(aad2);
- if D<(D1+D2) then result := true
- else begin
- }
- // DanB
- Result := DoCubesIntersectPrim(obj1, obj2) or
- DoCubesIntersectPrim(obj2, obj1);
- { end;
- end;
- }
- end;
- { Behaviour - Checks for collisions between Faces and cube by Checking
- whether triangles on the mesh have a point inside the cube,
- or a triangle intersects the side
- Issues - Checks whether triangles on the mesh have a point inside the cube
- 1) When the cube is completely inside a mesh, it will contain
- no triangles hence no collision detected
- 2) When the mesh is (almost) completely inside the cube
- Octree.GetTrianglesInCube returns no points, why? }
- function FastCheckCubeVsFace(obj1, obj2: TgxBaseSceneObject): Boolean;
- // var
- // triList : TgxAffineVectorList;
- // m1to2, m2to1 : TMatrix4f;
- // i:integer;
- begin
- if (obj2 is TgxFreeForm) then
- begin
- // check if we are initialized correctly
- if not Assigned(TgxFreeForm(obj2).Octree) then
- TgxFreeForm(obj2).BuildOctree;
- Result := TgxFreeForm(obj2).OctreeAABBIntersect
- (obj1.AxisAlignedBoundingBoxUnscaled, obj1.AbsoluteMatrix,
- obj1.InvAbsoluteMatrix)
- // could then analyse triangles and return contact points
- end
- else
- begin
- // CubeVsFace only works if one is FreeForm Object
- Result := IntersectCubes(obj1, obj2);
- end;
- end;
- function FastCheckFaceVsCube(obj1, obj2: TgxBaseSceneObject): Boolean;
- begin
- Result := FastCheckCubeVsFace(obj2, obj1);
- end;
- // this function does not check for rounds that results from Smoth rendering
- // if anybody needs this, you are welcome to show a solution, but usually this should be good enough
- function FastCheckFaceVsFace(obj1, obj2: TgxBaseSceneObject): Boolean;
- type
- TTriangle = array [0 .. 2] of TAffineVector;
- PTriangle = ^TTriangle;
- var
- i: Integer;
- triList: TgxAffineVectorList;
- tri: PTriangle;
- m1to2, m2to1: TMatrix4f;
- AABB2: TAABB;
- begin
- Result := false;
- if (obj1 is TgxFreeForm) and (obj2 is TgxFreeForm) then
- begin
- // check if we are initialized correctly
- if not Assigned(TgxFreeForm(obj1).Octree) then
- TgxFreeForm(obj1).BuildOctree;
- if not Assigned(TgxFreeForm(obj2).Octree) then
- TgxFreeForm(obj2).BuildOctree;
- // Check triangles against the other object
- // check only the one that are near the destination object (using octree of obj1)
- // get the 'hot' ones using the tree
- MatrixMultiply(obj2.AbsoluteMatrix, obj1.InvAbsoluteMatrix, m1to2);
- MatrixMultiply(obj1.AbsoluteMatrix, obj2.InvAbsoluteMatrix, m2to1);
- AABB2 := obj2.AxisAlignedBoundingBoxUnscaled;
- triList := TgxFreeForm(obj1).Octree.GetTrianglesFromNodesIntersectingCube
- (AABB2, m1to2, m2to1);
- // in the list originally are the local coords, TransformAsPoints-> now we have obj1 absolute coords
- triList.TransformAsPoints(obj1.AbsoluteMatrix);
- // Transform to Absolute Coords
- try
- i := 0;
- while i < triList.Count - 2 do
- begin
- // here we pass absolute coords, then these are transformed with Obj2's InvAbsoluteMatrix to match the local Obj2 System
- tri := @triList.List[i];
- // the next function will check the given Triangle against only these ones that are close (using the octree of obj2)
- if TgxFreeForm(obj2).OctreeTriangleIntersect(tri[0], tri[1], tri[2])
- then
- begin
- Result := true;
- { TODO : Optimize, exit was disabled for performance checks }
- Exit;
- end;
- Inc(i, 3);
- end;
- finally
- triList.Free;
- end;
- end
- else
- begin
- // FaceVsFace does work only for two FreeForm Objects
- Result := IntersectCubes(obj1, obj2);
- end;
- end;
- function IntersectCubes(obj1, obj2: TgxBaseSceneObject): Boolean;
- var
- aabb1, AABB2: TAABB;
- m1to2, m2to1: TMatrix4f;
- begin
- // Calc AABBs
- aabb1 := obj1.AxisAlignedBoundingBoxUnscaled;
- AABB2 := obj2.AxisAlignedBoundingBoxUnscaled;
- // Calc Conversion Matrixes
- MatrixMultiply(obj1.AbsoluteMatrix, obj2.InvAbsoluteMatrix, m1to2);
- MatrixMultiply(obj2.AbsoluteMatrix, obj1.InvAbsoluteMatrix, m2to1);
- Result := IntersectAABBs(aabb1, AABB2, m1to2, m2to1);
- end;
- // ------------------
- // ------------------ TCollisionManager ------------------
- // ------------------
- constructor TgxCollisionManager.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FClients := TList.Create;
- RegisterManager(Self);
- end;
- destructor TgxCollisionManager.Destroy;
- begin
- DeRegisterAllClients;
- DeRegisterManager(Self);
- FClients.Free;
- inherited Destroy;
- end;
- procedure TgxCollisionManager.RegisterClient(aClient: TgxBCollision);
- begin
- if Assigned(aClient) then
- if FClients.IndexOf(aClient) < 0 then
- begin
- FClients.Add(aClient);
- aClient.FManager := Self;
- end;
- end;
- procedure TgxCollisionManager.DeRegisterClient(aClient: TgxBCollision);
- begin
- if Assigned(aClient) then
- begin
- aClient.FManager := nil;
- FClients.Remove(aClient);
- end;
- end;
- procedure TgxCollisionManager.DeRegisterAllClients;
- var
- i: Integer;
- begin
- // Fast deregistration
- for i := 0 to FClients.Count - 1 do
- TgxBCollision(FClients[i]).FManager := nil;
- FClients.Clear;
- end;
- // Reference code
- {
- procedure TCollisionManager.CheckCollisions;
- var
- obj1, obj2 : TgxBaseSceneObject;
- cli1, cli2 : TgxBCollision;
- grp1, grp2 : Integer; // GroupIndex of collisions
- i, j : Integer;
- begin
- if not Assigned(FOnCollision) then Exit;
- // if you know a code slower than current one, call me ;)
- // TODO : speed improvements & distance cacheing
- for i:=0 to FClients.Count-2 do begin
- cli1:=TgxBCollision(FClients[i]);
- obj1:=cli1.OwnerBaseSceneObject;
- grp1:=cli1.GroupIndex;
- for j:=i+1 to FClients.Count-1 do begin
- cli2:=TgxBCollision(FClients[j]);
- obj2:=cli2.OwnerBaseSceneObject;
- grp2:=cli2.GroupIndex;
- // if either one GroupIndex=0 or both are different, check for collision
- if ((grp1=0) or (grp2=0) or (grp1<>grp2)) then begin
- if cFastCollisionChecker[cli1.BoundingMode, cli2.BoundingMode](obj1, obj2) then
- FOnCollision(Self, obj1, obj2);
- end;
- end;
- end;
- end;
- }
- // [---- new CheckCollisions / Dan Bartlett
- // CheckCollisions (By Dan Bartlett) - sort according to Z axis
- //
- // Some comments: Much faster than original, especially when objects are spread out.
- // TODO:
- // Try to make faster when objects are close
- // Still more improvements can be made, better method (dynamic octree?)
- // Faster sorting? (If a faster way than Delphi's QuickSort is available)
- // Another Event called OnNoCollisionEvent could be added
- // Fit bounding box methods into GLScene "Grand Scheme Of Things"
- //
- // Behaviour:
- // If GroupIndex < 0 then it will not be checked for collisions against
- // any other object *** WARNING: THIS IS DIFFERENT FROM PREVIOUS VERSION ***
- //
- // If GroupIndex = 0 then object will be tested against all objects with GroupIndex >= 0
- // Collision Testing will only be performed on objects from different groups
- // Collision testing occurs even when an object is not visible, allowing low-triangle count
- // collision shapes to be used to model complex objects (Different to previous version)
- type
- // only add collision node to list if GroupIndex>=0
- TCollisionNode = class
- public
- Collision: TgxBCollision;
- AABB: TAABB;
- constructor Create(Collision: TgxBCollision; AABB: TAABB);
- end;
- constructor TCollisionNode.Create(Collision: TgxBCollision; AABB: TAABB);
- begin
- inherited Create();
- Self.Collision := Collision;
- Self.AABB := AABB;
- end;
- function CompareDistance(Item1, Item2: Pointer): Integer;
- var
- d: Extended;
- begin
- // Z-axis sort
- d := (TCollisionNode(Item2).AABB.min.Z - TCollisionNode(Item1).AABB.min.Z);
- if d > 0 then
- Result := -1
- else if d < 0 then
- Result := 1
- else
- Result := 0;
- end;
- procedure TgxCollisionManager.CheckCollisions;
- var
- NodeList: TList;
- CollisionNode1, CollisionNode2: TCollisionNode;
- obj1, obj2: TgxBaseSceneObject;
- cli1, cli2: TgxBCollision;
- grp1, grp2: Integer; // GroupIndex of collisions
- i, j: Integer;
- box1: TAABB;
- begin
- if not Assigned(FOnCollision) then
- Exit;
- // this next bit of code would be faster if bounding box was stored
- NodeList := TList.Create;
- try
- NodeList.Count := 0;
- for i := 0 to FClients.Count - 1 do
- begin
- cli1 := TgxBCollision(FClients[i]);
- grp1 := cli1.GroupIndex;
- if grp1 < 0 then // if groupindex is negative don't add to list
- Continue;
- obj1 := cli1.OwnerBaseSceneObject;
- // TODO: need to do different things for different objects, especially points (to improve speed)
- box1 := obj1.AxisAlignedBoundingBoxUnscaled;
- // get obj1 axis-aligned bounding box
- if box1.min.Z >= box1.max.Z then
- Continue; // check for case where no bb exists
- AABBTransform(box1, obj1.AbsoluteMatrix); // & transform it to world axis
- CollisionNode1 := TCollisionNode.Create(cli1, box1);
- NodeList.Add(CollisionNode1);
- end;
- if NodeList.Count < 2 then
- Exit;
- NodeList.Sort(@CompareDistance);
- // depth-sort bounding boxes (min bb.z values)
- for i := 0 to NodeList.Count - 2 do
- begin
- CollisionNode1 := TCollisionNode(NodeList[i]);
- cli1 := CollisionNode1.Collision;
- grp1 := cli1.GroupIndex;
- for j := i + 1 to NodeList.Count - 1 do
- begin
- CollisionNode2 := TCollisionNode(NodeList[j]);
- cli2 := CollisionNode2.Collision;
- // Check BBox1 and BBox2 overlap in the z-direction
- if (CollisionNode2.AABB.min.Z > CollisionNode1.AABB.max.Z) then
- Break;
- grp2 := cli2.GroupIndex;
- // if either one GroupIndex=0 or both are different, check for collision
- if ((grp1 = 0) or (grp2 = 0) or (grp1 <> grp2)) = false then
- Continue;
- // check whether box1 and box2 overlap in the XY Plane
- if IntersectAABBsAbsoluteXY(CollisionNode1.AABB, CollisionNode2.AABB)
- then
- begin
- obj1 := cli1.OwnerBaseSceneObject;
- obj2 := cli2.OwnerBaseSceneObject;
- if cFastCollisionChecker[cli1.BoundingMode, cli2.BoundingMode]
- (obj1, obj2) then
- FOnCollision(Self, obj1, obj2);
- end;
- end;
- end;
- finally
- for i := 0 to NodeList.Count - 1 do
- begin
- CollisionNode1 := NodeList.Items[i];
- CollisionNode1.Free;
- end;
- NodeList.Free;
- end;
- end;
- // new CheckCollisions / Dan Bartlett -----]
- // ------------------
- // ------------------ TgxBCollision ------------------
- // ------------------
- constructor TgxBCollision.Create(AOwner: TXCollection);
- begin
- inherited Create(AOwner);
- end;
- destructor TgxBCollision.Destroy;
- begin
- Manager := nil;
- inherited Destroy;
- end;
- class function TgxBCollision.FriendlyName: String;
- begin
- Result := 'Collision';
- end;
- class function TgxBCollision.FriendlyDescription: String;
- begin
- Result := 'Collision-detection registration';
- end;
- procedure TgxBCollision.WriteToFiler(writer: TWriter);
- begin
- with writer do
- begin
- // ArchiveVersion 1, added FGroupIndex
- // ArchiveVersion 2, added inherited call
- WriteInteger(2);
- inherited;
- if Assigned(FManager) then
- WriteString(FManager.GetNamePath)
- else
- WriteString('');
- WriteInteger(Integer(BoundingMode));
- WriteInteger(FGroupIndex);
- end;
- end;
- procedure TgxBCollision.ReadFromFiler(reader: TReader);
- var
- archiveVersion: Integer;
- begin
- with reader do
- begin
- archiveVersion := ReadInteger;
- Assert(archiveVersion in [0 .. 2]);
- if archiveVersion >= 2 then
- inherited;
- FManagerName := ReadString;
- BoundingMode := TCollisionBoundingMode(ReadInteger);
- Manager := nil;
- if archiveVersion >= 1 then
- FGroupIndex := ReadInteger
- else
- FGroupIndex := 0;
- end;
- end;
- procedure TgxBCollision.Loaded;
- var
- mng: TComponent;
- begin
- inherited;
- if FManagerName <> '' then
- begin
- mng := FindManager(TgxCollisionManager, FManagerName);
- if Assigned(mng) then
- Manager := TgxCollisionManager(mng);
- FManagerName := '';
- end;
- end;
- procedure TgxBCollision.Assign(Source: TPersistent);
- begin
- if Source is TgxBCollision then
- begin
- Manager := TgxBCollision(Source).Manager;
- BoundingMode := TgxBCollision(Source).BoundingMode;
- end;
- inherited Assign(Source);
- end;
- procedure TgxBCollision.SetManager(const val: TgxCollisionManager);
- begin
- if val <> FManager then
- begin
- if Assigned(FManager) then
- FManager.DeRegisterClient(Self);
- if Assigned(val) then
- val.RegisterClient(Self);
- end;
- end;
- procedure TgxBCollision.SetGroupIndex(const value: Integer);
- begin
- FGroupIndex := value;
- end;
- function GetOrCreateCollision(behaviours: TgxBehaviours): TgxBCollision;
- var
- i: Integer;
- begin
- i := behaviours.IndexOfClass(TgxBCollision);
- if i >= 0 then
- Result := TgxBCollision(behaviours[i])
- else
- Result := TgxBCollision.Create(behaviours);
- end;
- function GetOrCreateCollision(obj: TgxBaseSceneObject): TgxBCollision;
- begin
- Result := GetOrCreateCollision(obj.behaviours);
- end;
- initialization // -----------------------------------------------------------
- RegisterXCollectionItemClass(TgxBCollision);
- finalization // -------------------------------------------------------------
- UnregisterXCollectionItemClass(TgxBCollision);
- end.
|