/usr/src/castle-game-engine-4.1.1/x3d/triangle_raysegment_nonleaf.inc is in castle-game-engine-src 4.1.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | {
Copyright 2003-2013 Michalis Kamburelis.
This file is part of "Castle Game Engine".
"Castle Game Engine" is free software; see the file COPYING.txt,
included in this distribution, for details about the copyright.
"Castle Game Engine" 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.
----------------------------------------------------------------------------
}
{ Implementacja funkcji CommonSegment i CommonRay w jednym kodzie.
See vrmltriangleoctree_raysegmentcollisions.inc for some comments.
}
{$define TYPE_SCALAR := Single}
{$define TYPE_VECTOR2 := TVector2Single}
{$define TYPE_VECTOR3 := TVector3Single}
{$define TYPE_VECTOR4 := TVector4Single}
{$define TYPE_TRIANGLE2 := TTriangle2Single}
{$define TYPE_TRIANGLE3 := TTriangle3Single}
{$define TYPE_MATRIX4 := TMatrix4Single}
{$define SCALAR_EQUALITY_EPSILON := SingleEqualityEpsilon}
{$define ONE_VECTOR := OneVectorSingle}
{$define IDENTITY_MATRIX := IdentityMatrix4Single}
{ CommonRay/Segment begins --------------------------------------------------- }
function DoNotLeaf: PTriangle;
{$ifdef SEGMENT_COLLISION}
var
RayOrigin: TYPE_VECTOR3 absolute Pos1;
RayDirection: TYPE_VECTOR3;
{$endif}
{ For demo_models/vrml_1/flat_triangles_octree_test.wrl,
this really needs some tolerance. And SingleEqualityEpsilon
was too small. }
function Box3DPointInsideTolerant(
const pt: TVector3Single; const box: TBox3D): boolean;
begin
if box.IsEmpty then exit(false);
result := Between(pt[0], Box.Data[0, 0] - 0.001, Box.Data[1, 0] + 0.001) and
Between(pt[1], Box.Data[0, 1] - 0.001, Box.Data[1, 1] + 0.001) and
Between(pt[2], Box.Data[0, 2] - 0.001, Box.Data[1, 2] + 0.001);
end;
var
{ FirstBoxIntr = first ray with box intersection if RayOrigin is outside
if the Box or simply RayOrigin if RayOrigin is inside our Box }
FirstBoxIntr: TYPE_VECTOR3;
{ poczatkowy Subnode, inicjowany na poczatku na SubnodeWithPoint(FirstBoxIntr).
Moze byc modyfikowany w pewnym specjalnym przypadku przy okazji obliczania
PlaneIntersects }
StartSubnode: TOctreeSubnodeIndex;
{ Element [0] tej tablicy okesla przeciecie z plaszczyzna X = MiddlePoint[0],
element [1] - z plaszczyzna Y = MiddlePoint[1] itd.
Exists mowi czy w ogole promien przecina ta plaszczyzne,
jezeli tak to IntrT to T z jakim zaszlo intersection miedzy
promieniem FirstBoxIntr, RayDirection (lub odcinkiem FirstBoxIntr, Pos2).
Czyli im wieksze T tym dalej przeciecie jest od FirstBoxIntr a wiec
i od RayOrigin/Pos1.
PlaneIntersectsCount to liczba planes ktore maja Exists.
PlaneIntersectsCount moze byc <= 3.}
PlaneIntersects: array[0..2]of record
Exists: boolean;
PlaneIntersectionDistance: TYPE_SCALAR;
end;
ExistingPlaneIntersectsCount: integer;
{ posortowane wzgledem SqrDistanceFromRayOrigin (od najmniejszego do najwiekszego)
PlaneIntersects, tzn. SortedPlaneIntersects[0] to indeks
najmniejszego PlaneIntersects, itd. az
do SortedPlaneIntersects[ExistingPlaneIntersectsCount-1] }
SortedPlaneIntersects: array[0..2]of integer;
{ SubnodesToCheck to ulozone w kolejnosci Subnode'y ktore powinnismy
sprawdzic czy jest w nich przeciecie promienia ze scena. Tzn. najpierw
powinnismy sprawdzic Subnode nr 0 (w nim jest punkt RayOrigin), potem
Subnode 1 itd - az do SubnodesToCheckCount-1.
Jak widac z rozmiaru tej tablicy wiemy ze bedziemy musieli wejsc
w maksymalnie 4 subnode'y - bo przeciez bedziemy mieli maksymalnie
3 przeciecia z 3 plaszczyznami dzielacymi ten wezel drzewa, co dzieli
nam promien na maksymalnie 4 segmenty. }
SubnodesToCheck: array[0..3]of TOctreeSubnodeIndex;
SubnodesToCheckCount: integer;
procedure CalculatePlaneIntersects;
var i: integer;
PlaneIntersect: TYPE_VECTOR3;
begin
ExistingPlaneIntersectsCount := 0;
for i := 0 to 2 do
with PlaneIntersects[i] do
begin
if (Box.Data[1][I] - Box.Data[0][I]) < SCALAR_EQUALITY_EPSILON then
begin
{ This means that the Box is practically flat, so all 3 planes
(Box.Data[0][I], MiddlePoint[I], Box.Data[1][I]) are practically
coplanar.
We want in this case to make sure that we actually
enter both sides of MiddlePoint[I] plane (since it's pretty random
where did we put some flat triangles). So we want to make
sure Exists is set to @true.
Starting StartSubnode[i] should be the one closer the RayOrigin.
See demo_models/vrml_1/flat_triangles_octree_test.wrl
for example when this is needed (with new okDynamicCollisions,
this plane is flat within it's octree, even when it's transformed
by rotation). }
Exists:=
{$ifdef SEGMENT_COLLISION}
TrySimplePlaneSegmentIntersection(PlaneIntersect, PlaneIntersectionDistance,
i, MiddlePoint[i], FirstBoxIntr, Pos2)
{$else}
TrySimplePlaneRayIntersection(PlaneIntersect, PlaneIntersectionDistance,
i, MiddlePoint[i], FirstBoxIntr, RayDirection)
{$endif};
if not Exists then
begin
{ Just force Exists to true.
We know that FirstBoxIntr is practically lying on MiddlePoint[I] plane,
so it's a good candidate. }
Exists := true;
PlaneIntersect := FirstBoxIntr;
PlaneIntersectionDistance := 0;
end;
StartSubnode[i] := RayDirection[i] <= 0;
end else
if (PointToSimplePlaneDistance(FirstBoxIntr, i, MiddlePoint[i])
< SCALAR_EQUALITY_EPSILON) and not Zero(RayDirection[i]) then
begin
{ This means that FirstBoxIntr lies on MiddlePoint[I] plane.
Although the box is not flat (this case was eliminated above).
Which means that the ray
somehow enters the box exactly in the middle, or maybe the
RayOrigin was exactly in the middle (e.g. consider RayOrigin = exactly
middle point of octree box).
We handle it specially, because normal code would be numerically
unstable: on one hand, StartSubnode may be detected on any side
--- it's practically random. On the other hand, whether
Exists is set is also random. So various strange things
could happen (e.g. StartSubNode is on the farther (RayDirection) side,
but intersection is detected too, so we incorrectly go back;
or StartSubNode is on the closer (-RayDirection) side,
but intersection is not detected, so we incorrectly do not change
sides).
The correct solution in this case is to have StartSubNode
on the farther side, and do not see intersection with middle plane. }
StartSubnode[i] := RayDirection[i] > 0;
Exists := false;
end else
begin
{ jezeli przeciecie nie znajduje sie w naszym Box to na pewno nie
chcemy go dolaczyc do PlaneIntersect wiec ustawiamy Exists := false;
Dzieki temu ze badamy przeciecia od FirstBoxIntr a nie od RayOrigin
(a nie od Pos1, w przypadku odcinka) to
wiemy ze przeciecie jest juz POTEM jak promien wyjdzie z naszego
Boxa (a nie ZANIM promien wszedl do naszego Boxa, co byloby tu troche
klopotliwe;) }
Exists:=
{$ifdef SEGMENT_COLLISION}
TrySimplePlaneSegmentIntersection(PlaneIntersect, PlaneIntersectionDistance,
i, MiddlePoint[i], FirstBoxIntr, Pos2)
{$else}
TrySimplePlaneRayIntersection(PlaneIntersect, PlaneIntersectionDistance,
i, MiddlePoint[i], FirstBoxIntr, RayDirection)
{$endif}
and Box.PointInside(PlaneIntersect);
end;
if Exists then Inc(ExistingPlaneIntersectsCount);
end;
end;
procedure CalculateSortedPlaneIntersects;
var SortedPlaneIntersectsCount: integer;
procedure NextIntersect(c: integer);
begin
SortedPlaneIntersects[SortedPlaneIntersectsCount] := c;
Inc(SortedPlaneIntersectsCount);
end;
var c1, c2, cm: integer;
begin
SortedPlaneIntersectsCount := 0;
{wyznacz c1 i c2, ew. wrzucajac juz pierwsze przeciecie do SortedPlaneIntersects}
if ExistingPlaneIntersectsCount = 3 then
begin
cm := IndexMin(PlaneIntersects[0].PlaneIntersectionDistance,
PlaneIntersects[1].PlaneIntersectionDistance,
PlaneIntersects[2].PlaneIntersectionDistance);
RestOf3dCoords(cm, c1, c2);
NextIntersect(cm);
end else
if not PlaneIntersects[0].Exists then
begin c1 := 1; c2 := 2 end else
if not PlaneIntersects[1].Exists then
begin c1 := 0; c2 := 2 end else
{wiec wiemy ze (not PlaneIntersects[2].Exists)}
begin c1 := 0; c2 := 1 end;
case ExistingPlaneIntersectsCount-SortedPlaneIntersectsCount of
1: if PlaneIntersects[c1].Exists then
NextIntersect(c1) else
NextIntersect(c2);
2: begin
if PlaneIntersects[c1].PlaneIntersectionDistance <
PlaneIntersects[c2].PlaneIntersectionDistance then
begin NextIntersect(c1); NextIntersect(c2) end else
begin NextIntersect(c2); NextIntersect(c1) end;
end;
end;
end;
procedure CalculateSubnodesToCheck;
var i, IntersectPlane: integer;
begin
SubnodesToCheckCount := ExistingPlaneIntersectsCount+1;
SubnodesToCheck[0] := StartSubnode;
for i := 1 to ExistingPlaneIntersectsCount do
begin
SubnodesToCheck[i] := SubnodesToCheck[i-1];
{ jezeli wchodzimy do tego Subnode'a z powodu przeciecia z plane dzielacym
o stalym X (czyli IntersectPlane = 0) to znaczy ze wspolrzedna X promienia
przechodzi na druga strone tej plaszczyzny (a pozostale wspolrzedne
zostaja w tych czesciach gdzie byly). Dlatego wystarczy zanegowac
zmienna b[IntersectPlane] w nowym plane. }
IntersectPlane := SortedPlaneIntersects[i-1];
SubnodesToCheck[i, IntersectPlane] := not SubnodesToCheck[i, IntersectPlane];
end;
end;
var
i: integer;
SubNode: TBaseTrianglesOctreeNode;
begin
{$ifdef SEGMENT_COLLISION}
RayDirection := VectorSubtract(Pos2, Pos1);
{$endif}
if not Box.TryRayEntrance(FirstBoxIntr, RayOrigin, RayDirection) then
Exit(nil);
StartSubnode := SubnodeWithPoint(FirstBoxIntr);
CalculatePlaneIntersects;
CalculateSortedPlaneIntersects;
CalculateSubnodesToCheck;
{ szukamy najblizszego Intersection wchodzac w SubNodes. Zauwazmy ze
w calym DoNotLeaf zawsze dzialamy jakby ReturnClosestIntersection = true
za wyjatkiem malego szczegoliku ponizej przy "if result <> nil ...". }
result := nil;
for i := 0 to SubnodesToCheckCount - 1 do
begin
SubNode := TBaseTrianglesOctreeNode(TreeSubNodes[
SubnodesToCheck[i, 0],
SubnodesToCheck[i, 1],
SubnodesToCheck[i, 2]]);
result := SubNode.
{ jest ponizej bardzo wazne zebysmy nie zmienili Pos1 ani RayOrigin wywolujac
sie rekurencyjnie (np. zeby nie przekazac FirstBoxIntr) bo to
spowodowaloby ze zmienna IgnoreMarginAtStart powinna byc (byc moze)
ustawiona na false. }
{$ifdef SEGMENT_COLLISION}
CommonSegment(Intersection, IntersectionDistance, Pos1, Pos2,
Tag,
ReturnClosestIntersection, TriangleToIgnore, IgnoreMarginAtStart,
TrianglesToIgnoreFunc)
{$else}
CommonRay(Intersection, IntersectionDistance, RayOrigin, RayDirection,
Tag,
ReturnClosestIntersection, TriangleToIgnore, IgnoreMarginAtStart,
TrianglesToIgnoreFunc)
{$endif}
;
{ musimy sie tu upewnic ze zwrocone Intersection jest rzeczywiscie
w pytanym SubnodesToCheck[i]. Jezeli nie to znaczy ze to przeciecie
nalezy do innego subnodea'a i to on je zwroci (a my nie powinnismy
teraz wychodzic jesli ReturnClosestIntersection bo byc moze to przeciecie
nie jest najblizszym z mozliwych) }
if (result <> nil) and
((not ReturnClosestIntersection) or
Box3DPointInsideTolerant(Intersection, SubNode.Box)) then
Exit;
end;
end;
begin
if not IsLeaf then
Result := DoNotLeaf else
Result :=
{$ifdef SEGMENT_COLLISION}
CommonSegmentLeaf(Intersection, IntersectionDistance, Pos1, Pos2,
Tag,
ReturnClosestIntersection, TriangleToIgnore, IgnoreMarginAtStart,
TrianglesToIgnoreFunc)
{$else}
CommonRayLeaf(Intersection, IntersectionDistance, RayOrigin, RayDirection,
Tag,
ReturnClosestIntersection, TriangleToIgnore, IgnoreMarginAtStart,
TrianglesToIgnoreFunc)
{$endif}
;
end;
|