OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 // based on code from | |
6 // http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/vec
/mat4.js | |
7 | |
8 /** | |
9 * Thrown if you attempt to normalize a zero length vector. | |
10 */ | |
11 class ZeroLengthVectorException implements Exception { | |
12 ZeroLengthVectorException() {} | |
13 } | |
14 | |
15 /** | |
16 * Thrown if you attempt to invert a singular matrix. (A | |
17 * singular matrix has no inverse.) | |
18 */ | |
19 class SingularMatrixException implements Exception { | |
20 SingularMatrixException() {} | |
21 } | |
22 | |
23 /** | |
24 * 3 dimensional vector. | |
25 */ | |
26 class Vector3 { | |
27 final double x; | |
28 final double y; | |
29 final double z; | |
30 | |
31 // TODO - should be const, but cannot because of | |
32 // bug http://code.google.com/p/dart/issues/detail?id=777 | |
33 | |
34 // TODO - switch to initializing formal syntax once we have type | |
35 // checking for this.x style constructors. See bug | |
36 // http://code.google.com/p/dart/issues/detail?id=464 | |
37 Vector3(double x, double y, double z) : x = x, y = y, z = z; | |
38 | |
39 double magnitude() => Math.sqrt(x*x + y*y + z*z); | |
40 | |
41 Vector3 normalize() { | |
42 double len = magnitude(); | |
43 if (len == 0.0) { | |
44 throw new ZeroLengthVectorException(); | |
45 } | |
46 return new Vector3(x/len, y/len, z/len); | |
47 } | |
48 | |
49 Vector3 operator negate() { | |
50 return new Vector3(-x, -y, -z); | |
51 } | |
52 | |
53 Vector3 operator -(Vector3 other) { | |
54 return new Vector3(x - other.x, y - other.y, z - other.z); | |
55 } | |
56 | |
57 Vector3 cross(Vector3 other) { | |
58 double xResult = y * other.z - z * other.y; | |
59 double yResult = z * other.x - x * other.z; | |
60 double zResult = x * other.y - y * other.x; | |
61 return new Vector3(xResult, yResult, zResult); | |
62 } | |
63 | |
64 String toString() { | |
65 return "Vector3($x,$y,$z)"; | |
66 } | |
67 } | |
68 | |
69 /** | |
70 * A 4x4 transformation matrix (for use with webgl) | |
71 * | |
72 * We label the elements of the matrix as follows: | |
73 * | |
74 * m00 m01 m02 m03 | |
75 * m10 m11 m12 m13 | |
76 * m20 m21 m22 m23 | |
77 * m30 m31 m32 m33 | |
78 * | |
79 * These are stored in a 16 element [Float32Array], in column major | |
80 * order, so they are ordered like this: | |
81 * | |
82 * [ m00,m10,m20,m30, m11,m21,m31,m41, m02,m12,m22,m32, m03,m13,m23,m33 ] | |
83 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
84 * | |
85 * We use column major order because that is what WebGL APIs expect. | |
86 * | |
87 */ | |
88 class Matrix4 { | |
89 final Float32Array buf; | |
90 | |
91 /** | |
92 * Constructs a new Matrix4 with all entries initialized | |
93 * to zero. | |
94 */ | |
95 Matrix4() : buf = new Float32Array(16); | |
96 | |
97 /** | |
98 * returns the index into [buf] for a given | |
99 * row and column. | |
100 */ | |
101 static int rc(int row, int col) => row + col * 4; | |
102 | |
103 double get m00() => buf[rc(0, 0)]; | |
104 double get m01() => buf[rc(0, 1)]; | |
105 double get m02() => buf[rc(0, 2)]; | |
106 double get m03() => buf[rc(0, 3)]; | |
107 double get m10() => buf[rc(1, 0)]; | |
108 double get m11() => buf[rc(1, 1)]; | |
109 double get m12() => buf[rc(1, 2)]; | |
110 double get m13() => buf[rc(1, 3)]; | |
111 double get m20() => buf[rc(2, 0)]; | |
112 double get m21() => buf[rc(2, 1)]; | |
113 double get m22() => buf[rc(2, 2)]; | |
114 double get m23() => buf[rc(2, 3)]; | |
115 double get m30() => buf[rc(3, 0)]; | |
116 double get m31() => buf[rc(3, 1)]; | |
117 double get m32() => buf[rc(3, 2)]; | |
118 double get m33() => buf[rc(3, 3)]; | |
119 | |
120 void set m00(double m) { buf[rc(0, 0)] = m; } | |
121 void set m01(double m) { buf[rc(0, 1)] = m; } | |
122 void set m02(double m) { buf[rc(0, 2)] = m; } | |
123 void set m03(double m) { buf[rc(0, 3)] = m; } | |
124 void set m10(double m) { buf[rc(1, 0)] = m; } | |
125 void set m11(double m) { buf[rc(1, 1)] = m; } | |
126 void set m12(double m) { buf[rc(1, 2)] = m; } | |
127 void set m13(double m) { buf[rc(1, 3)] = m; } | |
128 void set m20(double m) { buf[rc(2, 0)] = m; } | |
129 void set m21(double m) { buf[rc(2, 1)] = m; } | |
130 void set m22(double m) { buf[rc(2, 2)] = m; } | |
131 void set m23(double m) { buf[rc(2, 3)] = m; } | |
132 void set m30(double m) { buf[rc(3, 0)] = m; } | |
133 void set m31(double m) { buf[rc(3, 1)] = m; } | |
134 void set m32(double m) { buf[rc(3, 2)] = m; } | |
135 void set m33(double m) { buf[rc(3, 3)] = m; } | |
136 | |
137 String toString() { | |
138 List<String> rows = new List(); | |
139 for (int row = 0; row < 4; row++) { | |
140 List<String> items = new List(); | |
141 for (int col = 0; col < 4; col++) { | |
142 double v = buf[rc(row, col)]; | |
143 if (v.abs() < 1e-16) { | |
144 v = 0.0; | |
145 } | |
146 String display; | |
147 try { | |
148 display = v.toStringAsPrecision(4); | |
149 } catch (Object e) { | |
150 // TODO - remove this once toStringAsPrecision is implemented in vm | |
151 display = v.toString(); | |
152 } | |
153 items.add(display); | |
154 } | |
155 rows.add("| ${Strings.join(items, ", ")} |"); | |
156 } | |
157 return "Matrix4:\n${Strings.join(rows, '\n')}"; | |
158 } | |
159 | |
160 /** | |
161 * Cosntructs a new Matrix4 that represents the identity transformation | |
162 * (all the diagonal entries are 1, and everything else is zero). | |
163 */ | |
164 static Matrix4 identity() { | |
165 Matrix4 m = new Matrix4(); | |
166 m.m00 = 1.0; | |
167 m.m11 = 1.0; | |
168 m.m22 = 1.0; | |
169 m.m33 = 1.0; | |
170 return m; | |
171 } | |
172 | |
173 /** | |
174 * Constructs a new Matrix4 that represents a rotation around an axis. | |
175 * | |
176 * [degrees] number of degrees to rotate | |
177 * [axis] direction of axis of rotation (must not be zero length) | |
178 */ | |
179 static Matrix4 rotation(double degrees, Vector3 axis) { | |
180 double radians = degrees / 180.0 * Math.PI; | |
181 axis = axis.normalize(); | |
182 | |
183 double x = axis.x; | |
184 double y = axis.y; | |
185 double z = axis.z; | |
186 double s = Math.sin(radians); | |
187 double c = Math.cos(radians); | |
188 double t = 1 - c; | |
189 | |
190 Matrix4 m = new Matrix4(); | |
191 m.m00 = x * x * t + c; | |
192 m.m10 = x * y * t + z * s; | |
193 m.m20 = x * z * t - y * s; | |
194 | |
195 m.m01 = x * y * t - z * s; | |
196 m.m11 = y * y * t + c; | |
197 m.m21 = y * z * t + x * s; | |
198 | |
199 m.m02 = x * z * t + y * s; | |
200 m.m12 = y * z * t - x * s; | |
201 m.m22 = z * z * t + c; | |
202 | |
203 m.m33 = 1.0; | |
204 return m; | |
205 } | |
206 | |
207 /** | |
208 * Constructs a new Matrix4 that represents a translation. | |
209 * | |
210 * [v] vector representing which direction to move and how much to move | |
211 */ | |
212 static Matrix4 translation(Vector3 v) { | |
213 Matrix4 m = identity(); | |
214 m.m03 = v.x; | |
215 m.m13 = v.y; | |
216 m.m23 = v.z; | |
217 return m; | |
218 } | |
219 | |
220 /** | |
221 * returns the transpose of this matrix | |
222 */ | |
223 Matrix4 transpose() { | |
224 Matrix4 m = new Matrix4(); | |
225 for (int row = 0; row < 4; row++) { | |
226 for (int col = 0; col < 4; col++) { | |
227 m.buf[rc(col, row)] = this.buf[rc(row, col)]; | |
228 } | |
229 } | |
230 return m; | |
231 } | |
232 | |
233 /** | |
234 * Returns result of multiplication of this matrix | |
235 * by another matrix. | |
236 * | |
237 * In this equation: | |
238 * | |
239 * C = A * B | |
240 * | |
241 * C is the result of multiplying A * B. | |
242 * A is this matrix | |
243 * B is another matrix | |
244 * | |
245 */ | |
246 Matrix4 operator *(Matrix4 matrixB) { | |
247 Matrix4 matrixC = new Matrix4(); | |
248 Float32Array bufA = this.buf; | |
249 Float32Array bufB = matrixB.buf; | |
250 Float32Array bufC = matrixC.buf; | |
251 for (int row = 0; row < 4; row++) { | |
252 for (int col = 0; col < 4; col++) { | |
253 for (int i = 0; i < 4; i++) { | |
254 bufC[rc(row, col)] += bufA[rc(row, i)] * bufB[rc(i, col)]; | |
255 } | |
256 } | |
257 } | |
258 return matrixC; | |
259 } | |
260 | |
261 /** | |
262 * Constructs a 4x4 matrix matrix so that the eye is 'looking at' a | |
263 * given center point. (What this means is that the returned matrix can be | |
264 * used transform points from world coordinates to a new coordinate system | |
265 * where the eye is at the origin, and the negative z-axis of the new | |
266 * coordinate system goes from the eye towards the center point.) | |
267 * | |
268 * [eye] position of the eye (i.e. camera origin). | |
269 * [center] point to aim the camera at. | |
270 * [up] vector that identifies the up direction of the camera | |
271 */ | |
272 static Matrix4 lookAt(Vector3 eye, Vector3 center, Vector3 up) { | |
273 // Compute the z basis vector. (The z-axis negative direction is | |
274 // from eye to center point.) | |
275 Vector3 zBasis = (eye - center).normalize(); | |
276 | |
277 // Compute x basis. (The positive x-axis points right.) | |
278 Vector3 xBasis = up.cross(zBasis).normalize(); | |
279 | |
280 // Compute the y basis. (The positive y-axis points approximately the same | |
281 // direction as the supplied [up] direction, and is perpendicular to z and | |
282 // x.) | |
283 Vector3 yBasis = zBasis.cross(xBasis); | |
284 | |
285 // We now have an orthonormal basis. | |
286 Matrix4 b = new Matrix4(); | |
287 b.m00 = xBasis.x; b.m01 = xBasis.y; b.m02 = xBasis.z; | |
288 b.m10 = yBasis.x; b.m11 = yBasis.y; b.m12 = yBasis.z; | |
289 b.m20 = zBasis.x; b.m21 = zBasis.y; b.m22 = zBasis.z; | |
290 b.m33 = 1.0; | |
291 | |
292 // Before switching to the new basis, first translate by the negation | |
293 // of the eye point. (This will put the eye at the origin of the | |
294 // new coordinate system.) | |
295 return b * Matrix4.translation(-eye); | |
296 } | |
297 | |
298 /** | |
299 * Makse a 4x4 matrix perspective projection matrix given a field of view and | |
300 * aspect ratio. | |
301 * | |
302 * [fovyDegrees] field of view (in degrees) of the y-axis | |
303 * [aspectRatio] width to height aspect ratio. | |
304 * [zNear] distance to the near clipping plane. | |
305 * [zFar] distance to the far clipping plane. | |
306 */ | |
307 static Matrix4 perspective(double fovyDegrees, double aspectRatio, | |
308 double zNear, double zFar) { | |
309 double yTop = Math.tan(fovyDegrees * Math.PI / 180.0 / 2.0) * zNear; | |
310 double xRight = aspectRatio * yTop; | |
311 double zDepth = zFar - zNear; | |
312 | |
313 Matrix4 m = new Matrix4(); | |
314 m.m00 = zNear / xRight; | |
315 m.m11 = zNear / yTop; | |
316 m.m22 = -(zFar + zNear) / zDepth; | |
317 m.m23 = -(2 * zNear * zFar) / zDepth; | |
318 m.m32 = -1; | |
319 return m; | |
320 } | |
321 | |
322 /** | |
323 * Returns the inverse of this matrix. | |
324 */ | |
325 Matrix4 inverse() { | |
326 double a0 = m00 * m11 - m10 * m01; | |
327 double a1 = m00 * m21 - m20 * m01; | |
328 double a2 = m00 * m31 - m30 * m01; | |
329 double a3 = m10 * m21 - m20 * m11; | |
330 double a4 = m10 * m31 - m30 * m11; | |
331 double a5 = m20 * m31 - m30 * m21; | |
332 | |
333 double b0 = m02 * m13 - m12 * m03; | |
334 double b1 = m02 * m23 - m22 * m03; | |
335 double b2 = m02 * m33 - m32 * m03; | |
336 double b3 = m12 * m23 - m22 * m13; | |
337 double b4 = m12 * m33 - m32 * m13; | |
338 double b5 = m22 * m33 - m32 * m23; | |
339 | |
340 // compute determinant | |
341 double det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; | |
342 if (det == 0) { | |
343 throw new SingularMatrixException(); | |
344 } | |
345 | |
346 Matrix4 m = new Matrix4(); | |
347 m.m00 = (m11 * b5 - m21 * b4 + m31 * b3) / det; | |
348 m.m10 = (-m10 * b5 + m20 * b4 - m30 * b3) / det; | |
349 m.m20 = (m13 * a5 - m23 * a4 + m33 * a3) / det; | |
350 m.m30 = (-m12 * a5 + m22 * a4 - m32 * a3) / det; | |
351 | |
352 m.m01 = (-m01 * b5 + m21 * b2 - m31 * b1) / det; | |
353 m.m11 = (m00 * b5 - m20 * b2 + m30 * b1) / det; | |
354 m.m21 = (-m03 * a5 + m23 * a2 - m33 * a1) / det; | |
355 m.m31 = (m02 * a5 - m22 * a2 + m32 * a1) / det; | |
356 | |
357 m.m02 = (m01 * b4 - m11 * b2 + m31 * b0) / det; | |
358 m.m12 = (-m00 * b4 + m10 * b2 - m30 * b0) / det; | |
359 m.m22 = (m03 * a4 - m13 * a2 + m33 * a0) / det; | |
360 m.m32 = (-m02 * a4 + m12 * a2 - m32 * a0) / det; | |
361 | |
362 m.m03 = (-m01 * b3 + m11 * b1 - m21 * b0) / det; | |
363 m.m13 = (m00 * b3 - m10 * b1 + m20 * b0) / det; | |
364 m.m23 = (-m03 * a3 + m13 * a1 - m23 * a0) / det; | |
365 m.m33 = (m02 * a3 - m12 * a1 + m22 * a0) / det; | |
366 | |
367 return m; | |
368 } | |
369 } | |
OLD | NEW |