pragma Style_Checks (Off);
pragma Warnings (Off);
————————————————————————-
— GLOBE_3D.Collision_detection
—
— Copyright (c) Gautier de Montmollin 1999 .. 2008
— SWITZERLAND
—
— Permission is hereby granted, free of charge, to any person obtaining a copy
— of this software and associated documentation files (the “Software”), to deal
— in the Software without restriction, including without limitation the rights
— to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
— copies of the Software, and to permit persons to whom the Software is
— furnished to do so, subject to the following conditions:
— The above copyright notice and this permission notice shall be included in
— all copies or substantial portions of the Software.
— THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
— IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
— FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
— AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
— LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
— OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
— THE SOFTWARE.
— NB : this is the MIT License, as found 12 – Sep – 2007 on the site
— http://www.opensource.org/licenses/mit – license.php
————————————————————————-
with GLOBE_3D.Math; use GLOBE_3D.Math;
with GLOBE_3D.Options;
package body GLOBE_3D.Collision_detection is
check_normals : constant Boolean := GLOBE_3D.Options.strict_geometry;
procedure Reaction (
o : Object_3D’Class;
ball : Ball_type;
method : Reaction_method;
step : in out Vector_3D; — Whole step (in : desired, out : effective)
reacted : out Real — in proportion to step
)
is
P_after_step, P_face : Point_3D;
u, n : Vector_3D;
dist_after, dist_before, nn : Real; — distance orientee
retour : Real := 0.0;
lstep0 : constant Real := Norm (step);
— This function check whether we are inside the prism above face f
function Dans_prisme_epaissi (f : Positive) return Boolean is
sfp1 : Positive;
Ps, Psp1 : Point_3D;
u, edge_vector, npa : Vector_3D;
dist_edge, nnpa : Real;
facteur : constant := 1.05;
begin
— Cycle through face’s vertices
for sf in reverse 1 .. o.Face_Invariant (f).last_edge loop
sfp1 := 1 + sf mod o.Face_Invariant (f).last_edge;
Ps := o.point (o.Face_Invariant (f).P_compact (sf));
Psp1 := o.point (o.Face_Invariant (f).P_compact (sfp1));
edge_vector := Psp1 – Ps;
npa := n * edge_vector;
nnpa := Norm (npa);
if Almost_Zero (nnpa) then — degenerated edge
return False;
end if;
npa := 1.0/nnpa * npa;
— npa points towards the prism’s interior
u := P_after_step – (Ps + o.Centre);
dist_edge := u * npa;
if dist_edge < - ball.radius * facteur then
return False;
end if;
end loop;
return True;
end Dans_prisme_epaissi;
begin
reacted := 0.0;
if Almost_Zero (lstep0) then
return;
end if;
P_after_step := ball.centre + step;
for face in reverse 1 .. o.Max_faces loop
n := o.Face_Invariant (face).normal;
if check_normals then
nn := Norm (n);
if Almost_zero (nn) then
raise Zero_normal;
elsif abs (nn - 1.0) > 1.0e-7 then
raise Not_one_normal with ” norm = ” & Real’Image (nn);
end if;
end if;
if step * n < 0.0 then
P_face := o.point (o.Face_Invariant (face).P_compact (1)) + o.Centre;
-- ^ any point on the face, to measure distance to face's plane.
u := ball.centre - P_face;
dist_before := u * n;
if dist_before > 0.0 then
— ^ Fine, we are on the right side of the face.
— Test added to Engine_3D’s algo, since objects are
— not always hollow, convex polyhedrons anymore.
u := P_after_step – P_face;
dist_after := u * n;
if dist_after < ball.radius
-- ^ Ouch! React we must!
-- This includes negatives values of dist_after, in cases
-- the intended step makes going through the face!
and then
Dans_prisme_epaissi (face)
then
if o.face (face).skin /= invisible then
-- ^ this assumes : invisible < => can go through
reacted := reacted + retour / lstep0;
— !! seems wrong if reactions in different directions
— should be something like step * step0
case method is
when elastic =>
raise Unsupported with “elastic reaction”;
— should compute the time the “ball” takes from rebound to
— next face or portal.
when slide =>
retour := ball.radius – dist_after; — always > 0
step := step + retour * n;
— Since step and n have a negative dot product – checked –
— and dist (ball.centre + step_old, face) < ball.radius - checked -
-- then:
-- ||step_new|| < ||step_old|| --> decreasing algo : – )
end case;
end if;
end if;
end if;
end if;
end loop;
end Reaction;
end GLOBE_3D.Collision_detection;