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.
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
);
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
];
72 // Definitively not stuck inside.
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.
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!
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
);
104 if (StuckOnSurface
) Trace
.ImpactNormal
= StuckOnSurface
->Normal
;
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
;
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)
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!
160 if (F
> Trace
.Fraction
) F
= Trace
.Fraction
; // Pro forma.
163 Trace
.ImpactNormal
= Planes
[PlaneNr
].Normal
;
169 template class Brush3T
<float>;
170 template class Brush3T
<double>;