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
324
325
326
327
328
329
330
331
|
/**
@file CoordinateFrame.h
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-03-04
@edited 2009-04-29
Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#ifndef G3D_CFrame_h
#define G3D_CFrame_h
#include "G3D/platform.h"
#include "G3D/Vector3.h"
#include "G3D/Vector4.h"
#include "G3D/Matrix3.h"
#include "G3D/Array.h"
#include <math.h>
#include <string>
#include <stdio.h>
#include <cstdarg>
#include <assert.h>
#ifdef _MSC_VER
// Turn off "conditional expression is constant" warning; MSVC generates this
// for debug assertions in inlined methods.
# pragma warning (disable : 4127)
#endif
namespace G3D {
class Any;
/**
A rigid body RT (rotation-translation) transformation.
CoordinateFrame abstracts a 4x4 matrix that maps object space to world space:
v_world = C * v_object
CoordinateFrame::rotation is the upper 3x3 submatrix, CoordinateFrame::translation
is the right 3x1 column. The 4th row is always [0 0 0 1], so it isn't stored.
So you don't have to remember which way the multiplication and transformation work,
it provides explicit toWorldSpace and toObjectSpace methods. Also, points, vectors
(directions), and surface normals transform differently, so they have separate methods.
Some helper functions transform whole primitives like boxes in and out of object space.
Convert to Matrix4 using CoordinateFrame::toMatrix4. You <I>can</I> construct a CoordinateFrame
from a Matrix4 using Matrix4::approxCoordinateFrame, however, because a Matrix4 is more
general than a CoordinateFrame, some information may be lost.
@sa G3D::UprightFrame, G3D::PhysicsFrame, G3D::Matrix4, G3D::Quat
*/
class CoordinateFrame {
public:
/** Takes object space points to world space. */
Matrix3 rotation;
/** Takes object space points to world space. */
Vector3 translation;
/** \param any Must be in one of the following forms:
- CFrame((matrix3 expr), (vector3 expr))
- CFrame::fromXYZYPRDegrees(#, #, #, #, #, #)
- CFrame { rotation = (matrix3 expr), translation = (vector3 expr) }
*/
CoordinateFrame(const Any& any);
/** Converts the CFrame to an Any. */
operator Any() const;
inline bool operator==(const CoordinateFrame& other) const {
return (translation == other.translation) && (rotation == other.rotation);
}
inline bool operator!=(const CoordinateFrame& other) const {
return !(*this == other);
}
bool fuzzyEq(const CoordinateFrame& other) const;
bool fuzzyIsIdentity() const;
bool isIdentity() const;
/**
Initializes to the identity coordinate frame.
*/
CoordinateFrame();
CoordinateFrame(const Vector3& _translation) :
rotation(Matrix3::identity()), translation(_translation) {
}
CoordinateFrame(const Matrix3 &rotation, const Vector3 &translation) :
rotation(rotation), translation(translation) {
}
CoordinateFrame(const Matrix3 &rotation) :
rotation(rotation), translation(Vector3::zero()) {
}
CoordinateFrame(const class UprightFrame& f);
static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
/** Construct a coordinate frame from translation = (x,y,z) and
rotations (in that order) about Y, object space X, object space
Z. Note that because object-space axes are used, these are not
equivalent to Euler angles; they are known as Tait-Bryan
rotations and are more convenient for intuitive positioning.*/
static CoordinateFrame fromXYZYPRDegrees(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
CoordinateFrame(class BinaryInput& b);
void deserialize(class BinaryInput& b);
void serialize(class BinaryOutput& b) const;
CoordinateFrame(const CoordinateFrame &other) :
rotation(other.rotation), translation(other.translation) {}
/**
Computes the inverse of this coordinate frame.
*/
inline CoordinateFrame inverse() const {
CoordinateFrame out;
out.rotation = rotation.transpose();
out.translation = -out.rotation * translation;
return out;
}
inline ~CoordinateFrame() {}
/** See also Matrix4::approxCoordinateFrame */
class Matrix4 toMatrix4() const;
void getXYZYPRRadians(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
void getXYZYPRDegrees(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
/**
Produces an XML serialization of this coordinate frame.
@deprecated
*/
std::string toXML() const;
/**
Returns the heading of the lookVector as an angle in radians relative to
the world -z axis. That is, a counter-clockwise heading where north (-z)
is 0 and west (-x) is PI/2.
Note that the heading ignores the Y axis, so an inverted
object has an inverted heading.
*/
inline float getHeading() const {
Vector3 look = rotation.column(2);
float angle = -(float) atan2(-look.x, look.z);
return angle;
}
/**
Takes the coordinate frame into object space.
this->inverse() * c
*/
inline CoordinateFrame toObjectSpace(const CoordinateFrame& c) const {
return this->inverse() * c;
}
inline Vector4 toObjectSpace(const Vector4& v) const {
return this->inverse().toWorldSpace(v);
}
inline Vector4 toWorldSpace(const Vector4& v) const {
return Vector4(rotation * Vector3(v.x, v.y, v.z) + translation * v.w, v.w);
}
/**
Transforms the point into world space.
*/
inline Vector3 pointToWorldSpace(const Vector3& v) const {
return Vector3(
rotation[0][0] * v[0] + rotation[0][1] * v[1] + rotation[0][2] * v[2] + translation[0],
rotation[1][0] * v[0] + rotation[1][1] * v[1] + rotation[1][2] * v[2] + translation[1],
rotation[2][0] * v[0] + rotation[2][1] * v[1] + rotation[2][2] * v[2] + translation[2]);
}
/**
Transforms the point into object space. Assumes that the rotation matrix is orthonormal.
*/
inline Vector3 pointToObjectSpace(const Vector3& v) const {
float p[3];
p[0] = v[0] - translation[0];
p[1] = v[1] - translation[1];
p[2] = v[2] - translation[2];
debugAssert(G3D::fuzzyEq(rotation.determinant(), 1.0f));
return Vector3(
rotation[0][0] * p[0] + rotation[1][0] * p[1] + rotation[2][0] * p[2],
rotation[0][1] * p[0] + rotation[1][1] * p[1] + rotation[2][1] * p[2],
rotation[0][2] * p[0] + rotation[1][2] * p[1] + rotation[2][2] * p[2]);
}
/**
Transforms the vector into world space (no translation).
*/
inline Vector3 vectorToWorldSpace(const Vector3& v) const {
return rotation * v;
}
inline Vector3 normalToWorldSpace(const Vector3& v) const {
return rotation * v;
}
class Ray toObjectSpace(const Ray& r) const;
Ray toWorldSpace(const Ray& r) const;
/**
Transforms the vector into object space (no translation).
*/
inline Vector3 vectorToObjectSpace(const Vector3 &v) const {
// Multiply on the left (same as rotation.transpose() * v)
return v * rotation;
}
inline Vector3 normalToObjectSpace(const Vector3 &v) const {
// Multiply on the left (same as rotation.transpose() * v)
return v * rotation;
}
void pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
void normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
void vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
void pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
void normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
void vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
class Box toWorldSpace(const class AABox& b) const;
class Box toWorldSpace(const class Box& b) const;
class Cylinder toWorldSpace(const class Cylinder& b) const;
class Capsule toWorldSpace(const class Capsule& b) const;
class Plane toWorldSpace(const class Plane& p) const;
class Sphere toWorldSpace(const class Sphere& b) const;
class Triangle toWorldSpace(const class Triangle& t) const;
class Box toObjectSpace(const AABox& b) const;
class Box toObjectSpace(const Box& b) const;
class Plane toObjectSpace(const Plane& p) const;
class Sphere toObjectSpace(const Sphere& b) const;
Triangle toObjectSpace(const Triangle& t) const;
/** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
CoordinateFrame operator*(const CoordinateFrame &other) const {
return CoordinateFrame(rotation * other.rotation,
pointToWorldSpace(other.translation));
}
CoordinateFrame operator+(const Vector3& v) const {
return CoordinateFrame(rotation, translation + v);
}
CoordinateFrame operator-(const Vector3& v) const {
return CoordinateFrame(rotation, translation - v);
}
void lookAt(const Vector3& target);
void lookAt(
const Vector3& target,
Vector3 up);
/** The direction this camera is looking (its negative z axis)*/
inline Vector3 lookVector() const {
return -rotation.column(2);
}
/** Returns the ray starting at the camera origin travelling in direction CoordinateFrame::lookVector. */
class Ray lookRay() const;
/** Up direction for this camera (its y axis). */
inline Vector3 upVector() const {
return rotation.column(1);
}
inline Vector3 rightVector() const {
return rotation.column(0);
}
/**
If a viewer looks along the look vector, this is the viewer's "left".
Useful for strafing motions and building alternative coordinate frames.
*/
inline Vector3 leftVector() const {
return -rotation.column(0);
}
/**
Linearly interpolates between two coordinate frames, using
Quat::slerp for the rotations.
*/
CoordinateFrame lerp(
const CoordinateFrame& other,
float alpha) const;
};
typedef CoordinateFrame CFrame;
} // namespace
#endif
|