Fix issue in Rocket.lua script.
[Cafu-Engine.git] / Libs / Math3D / Brush.cpp
blobec6becaf71bba21d07567841c5136b1a3abae1be
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
7 #include "Brush.hpp"
10 template<class T> Brush3T<T>::Brush3T(const BoundingBox3T<T>& BB, const Vector3T<T>& Pos)
12 Planes.PushBack(Plane3T<T>(Vector3T<T>( 1, 0, 0), BB.Max.x + Pos.x));
13 Planes.PushBack(Plane3T<T>(Vector3T<T>(-1, 0, 0), -BB.Min.x - Pos.x));
14 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0, 1, 0), BB.Max.y + Pos.y));
15 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0, -1, 0), -BB.Min.y - Pos.y));
16 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0, 0, 1), BB.Max.z + Pos.z));
17 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0, 0, -1), -BB.Min.z - Pos.z));
21 template<class T> Brush3T<T>::Brush3T(const Vector3T<T>& A, const Vector3T<T>& B, const Vector3T<T>& C, const T Epsilon, bool IncludeBevelPlanes)
23 Planes.PushBack(Plane3T<T>(A, B, C, Epsilon));
24 Planes.PushBack(Planes[0].GetMirror());
25 Planes.PushBack(Plane3T<T>(A, B, B + Planes[0].Normal, Epsilon));
26 Planes.PushBack(Plane3T<T>(B, C, C + Planes[0].Normal, Epsilon));
27 Planes.PushBack(Plane3T<T>(C, A, A + Planes[0].Normal, Epsilon));
29 if (!IncludeBevelPlanes) return;
31 BoundingBox3T<T> BB(A, B);
32 BB.Insert(C);
34 Planes.PushBack(Plane3T<T>(Vector3T<T>(-1.0, 0.0, 0.0), -BB.Min.x)); // Left plane.
35 Planes.PushBack(Plane3T<T>(Vector3T<T>( 1.0, 0.0, 0.0), BB.Max.x)); // Right plane.
36 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0.0, -1.0, 0.0), -BB.Min.y)); // Near plane.
37 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0.0, 1.0, 0.0), BB.Max.y)); // Far plane.
38 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0.0, 0.0, -1.0), -BB.Min.z)); // Bottom plane.
39 Planes.PushBack(Plane3T<T>(Vector3T<T>( 0.0, 0.0, 1.0), BB.Max.z)); // Top plane.
43 template<class T> void Brush3T<T>::TraceBoundingBox(const BoundingBox3T<T>& BB, const Vector3T<T>& Origin, const Vector3T<T>& Dir, VB_Trace3T<T>& Trace) const
45 static ArrayT<T> BloatDistanceOffsets;
46 const T Epsilon = T(0.0625);
48 // Bloat-Distance-Offsets für alle Planes dieses Brushs bestimmen.
49 while (BloatDistanceOffsets.Size() < Planes.Size())
50 BloatDistanceOffsets.PushBack(0.0);
52 for (unsigned long PlaneNr = 0; PlaneNr < Planes.Size(); PlaneNr++)
54 const Plane3T<T>& P = Planes[PlaneNr];
56 BloatDistanceOffsets[PlaneNr] = dot(P.Normal, Vector3T<T>(
57 P.Normal.x < 0 ? BB.Max.x : BB.Min.x,
58 P.Normal.y < 0 ? BB.Max.y : BB.Min.y,
59 P.Normal.z < 0 ? BB.Max.z : BB.Min.z));
62 // Wenn 'Origin' im Inneren des (soliden) Brushs liegt, sitzen wir fest.
63 unsigned long PlaneNr;
64 const Plane3T<T>* StuckOnSurface = NULL;
66 for (PlaneNr = 0; PlaneNr < Planes.Size(); PlaneNr++)
68 const T Dist = Planes[PlaneNr].GetDistance(Origin) + BloatDistanceOffsets[PlaneNr];
70 if (Dist >= 0)
72 // Definitively not stuck inside.
73 break;
76 if (Dist > -Epsilon)
78 // We are slightly inside the brush (probably due to rounding errors).
79 const T Nenner = dot(Planes[PlaneNr].Normal, Dir); // Computed exactly as below.
81 if (Nenner >= 0)
83 // Slightly inside, but moving out. As moving out makes matters better,
84 // not worse, let's consider this case as "not stuck inside" as well.
85 // Note that we must be able to reproduce the same conclusion below, so here
86 // and there, the computation of Nenner must be exactly the same!
87 break;
89 else
91 // Slightly inside, and moving further in. As we *would* get out if Dir was
92 // in another direction, flag this case explicitly, so that the result is a
93 // Trace.Fraction of 0, but Trace.StartSolid is false.
94 // Note that another plane might still clear us entirely.
95 StuckOnSurface = &Planes[PlaneNr];
100 if (PlaneNr == Planes.Size())
102 Trace.StartSolid = (StuckOnSurface == NULL);
103 Trace.Fraction = 0;
104 if (StuckOnSurface) Trace.ImpactNormal = StuckOnSurface->Normal;
105 return;
108 for (PlaneNr = 0; PlaneNr < Planes.Size(); PlaneNr++)
110 // Bestimmen, bei welchem Bruchteil (Fraction F) von Dir wir die Plane schneiden.
111 const T Nenner = dot(Planes[PlaneNr].Normal, Dir); // Computed exactly as above.
113 // Dir muß dem Normalenvektor der Ebene wirklich entgegenzeigen! Ist der Nenner==0, so ist Dir parallel zur Plane,
114 // und mit dieser Plane ex. kein Schnittpunkt. Ist der Nenner>0, nutzen wir die Konvexität des Brushs aus:
115 // Es gibt damit nur genau einen Schnittpunkt mit dem Brush (Eintrittsstelle) und die Plane behindert nicht
116 // eine Bewegung von ihr weg (Feststecken wenn Dist==0 (s.u.)).
117 if (Nenner >= 0) continue;
119 const T Dist = Planes[PlaneNr].GetDistance(Origin) + BloatDistanceOffsets[PlaneNr];
120 T F = -Dist / Nenner;
122 // Der Schnitt macht nur Sinn, wenn F im gewünschten Intervall liegt
123 if (F < 0 || F > Trace.Fraction) continue;
125 // Prüfen, ob Schnittpunkt wirklich auf dem Brush liegt
126 const Vector3T<T> HitPos = Origin + Dir * F;
127 unsigned long PNr;
129 for (PNr = 0; PNr < Planes.Size(); PNr++)
131 const T d = Planes[PNr].GetDistance(HitPos) + BloatDistanceOffsets[PNr];
133 // The (PNr != PlaneNr) part is for anticipating rounding errors.
134 // In the (d > 0) part, there is no need to use Epsilon in place of the 0,
135 // because HitPos is supposed to be at 0 distance of Planes[PlaneNr] anyway.
136 if (PNr != PlaneNr /*Rundungsfehlern zuvorkommen!*/ && d > 0)
137 break;
140 if (PNr < Planes.Size()) continue;
142 // Wir haben die einzige Eintrittsstelle gefunden!
143 // Eigentlich ist das errechete F einwandfrei. Wir wollen es jedoch nochmal etwas verringern, sodaß der sich
144 // ergebende Schnittpunkt (HitPos) in einem Abstand von 0.03125 ÜBER der Ebene liegt! Bildhaft wird dazu die
145 // Schnittebene um 0.03125 in Richtung ihres Normalenvektors geschoben und F wie oben neu errechnet.
146 // Dies erspart uns ansonsten ernste Probleme:
147 // - Wird diese Funktion nochmals mit dem Trace-Ergebnis (HitPos) als Origin-Vektor aufgerufen,
148 // kann dieser neue Origin-Vektor nicht wegen Rundungsfehlern in den Brush geraten (Solid!).
149 // - Wird unser Trace-Ergebnis (HitPos) als Origin-Vektor dieser Funktion, aber mit einem anderen
150 // Brush benutzt, der gegenüber diesem Brush nur in der Schnittebene verschoben ist (z.B. eine lange
151 // Wand, die aus mehreren "Backsteinen" besteht), kommt es auch hier nicht zum (falschen)
152 // "Hängenbleiben" an einer gemeinsamen Brush-Kante.
153 // Aber: Die HitPos kann natürlich trotzdem näher als 0.03125 an der Ebene liegen, nämlich genau dann, wenn
154 // es nicht zu einem Schnitt kam und Dir zufällig dort endet. Wir ignorieren diese Möglichkeit: Kommt es doch
155 // noch zu einem Schnitt, wird eben F==0. Deshalb können wir uns auch in diesem Fall nicht durch Rundungsfehler
156 // ins Innere des Brushs schaukeln.
157 F = -(Dist - Epsilon) / Nenner; // Vgl. Berechnung von F oben!
159 if (F < 0 ) F = 0;
160 if (F > Trace.Fraction) F = Trace.Fraction; // Pro forma.
162 Trace.Fraction = F;
163 Trace.ImpactNormal = Planes[PlaneNr].Normal;
164 break;
169 template class Brush3T<float>;
170 template class Brush3T<double>;