numerix 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +66 -0
  9. data/Rakefile +18 -0
  10. data/TODO.txt +25 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/ext/numerix/common.h +107 -0
  14. data/ext/numerix/extconf.rb +3 -0
  15. data/ext/numerix/matrix3x2.c +638 -0
  16. data/ext/numerix/matrix3x2.h +52 -0
  17. data/ext/numerix/matrix4x4.c +1807 -0
  18. data/ext/numerix/matrix4x4.h +90 -0
  19. data/ext/numerix/matrix_base.c +307 -0
  20. data/ext/numerix/matrix_base.h +70 -0
  21. data/ext/numerix/numerix.c +33 -0
  22. data/ext/numerix/numerix.h +19 -0
  23. data/ext/numerix/plane.c +311 -0
  24. data/ext/numerix/plane.h +34 -0
  25. data/ext/numerix/quaternion.c +712 -0
  26. data/ext/numerix/quaternion.h +53 -0
  27. data/ext/numerix/structure.c +154 -0
  28. data/ext/numerix/structure.h +24 -0
  29. data/ext/numerix/vector.c +326 -0
  30. data/ext/numerix/vector.h +57 -0
  31. data/ext/numerix/vector2.c +641 -0
  32. data/ext/numerix/vector2.h +64 -0
  33. data/ext/numerix/vector3.c +805 -0
  34. data/ext/numerix/vector3.h +68 -0
  35. data/ext/numerix/vector4.c +727 -0
  36. data/ext/numerix/vector4.h +63 -0
  37. data/ext/numerix/vector_base.c +94 -0
  38. data/ext/numerix/vector_base.h +30 -0
  39. data/extra/numerix128.png +0 -0
  40. data/extra/numerix24.png +0 -0
  41. data/extra/numerix32.png +0 -0
  42. data/extra/numerix320.png +0 -0
  43. data/extra/numerix48.png +0 -0
  44. data/extra/numerix96.png +0 -0
  45. data/lib/numerix/error.rb +36 -0
  46. data/lib/numerix/matrix3x2.rb +420 -0
  47. data/lib/numerix/matrix4x4.rb +676 -0
  48. data/lib/numerix/matrix_base.rb +14 -0
  49. data/lib/numerix/plane.rb +154 -0
  50. data/lib/numerix/quaternion.rb +355 -0
  51. data/lib/numerix/structure.rb +124 -0
  52. data/lib/numerix/vector.rb +13 -0
  53. data/lib/numerix/vector2.rb +534 -0
  54. data/lib/numerix/vector3.rb +572 -0
  55. data/lib/numerix/vector4.rb +551 -0
  56. data/lib/numerix/vector_base.rb +14 -0
  57. data/lib/numerix/version.rb +6 -0
  58. data/lib/numerix.rb +10 -0
  59. data/numerix.gemspec +30 -0
  60. metadata +167 -0
@@ -0,0 +1,1807 @@
1
+
2
+ #include "matrix4x4.h"
3
+
4
+ void Init_matrix4x4(VALUE outer) {
5
+ rb_define_alloc_func(rb_cMatrix4x4, rb_matrix4x4_allocate);
6
+ rb_define_method(rb_cMatrix4x4, "initialize", rb_matrix4x4_initialize, -1);
7
+
8
+ // Instance
9
+ rb_define_method(rb_cMatrix4x4, "identity?", rb_matrix4x4_identity_p, 0);
10
+ rb_define_method(rb_cMatrix4x4, "translation", rb_matrix4x4_translation, 0);
11
+ rb_define_method(rb_cMatrix4x4, "translation=", rb_matrix4x4_translation_set, 1);
12
+ rb_define_method(rb_cMatrix4x4, "determinant", rb_matrix4x4_determinant, 0);
13
+ rb_define_method(rb_cMatrix4x4, "row", rb_matrix4x4_row, 1);
14
+ rb_define_method(rb_cMatrix4x4, "column", rb_matrix4x4_column, 1);
15
+ rb_define_method(rb_cMatrix4x4, "each_row", rb_matrix4x4_each_row, 0);
16
+ rb_define_method(rb_cMatrix4x4, "each_column", rb_matrix4x4_each_column, 0);
17
+ rb_define_method(rb_cMatrix4x4, "invert", rb_matrix4x4_invert, 0);
18
+ rb_define_method(rb_cMatrix4x4, "transform", rb_matrix4x4_transform, 1);
19
+ rb_define_method(rb_cMatrix4x4, "transform!", rb_matrix4x4_transform_bang, 1);
20
+ rb_define_method(rb_cMatrix4x4, "transpose", rb_matrix4x4_transpose, 0);
21
+ rb_define_method(rb_cMatrix4x4, "lerp", rb_matrix4x4_lerp, 2);
22
+ rb_define_method(rb_cMatrix4x4, "lerp!", rb_matrix4x4_lerp_bang, 2);
23
+ rb_define_method(rb_cMatrix4x4, "map", rb_matrix4x4_map, 0);
24
+ rb_define_method(rb_cMatrix4x4, "map2!", rb_matrix4x4_map_bang, 0);
25
+
26
+ // Alias
27
+ rb_define_alias(rb_cMatrix4x4, "collect", "map");
28
+ rb_define_alias(rb_cMatrix4x4, "collect2!", "map2!");
29
+
30
+ // Conversion
31
+ rb_define_method(rb_cMatrix4x4, "to_s", rb_matrix4x4_to_s, 0);
32
+ rb_define_method(rb_cMatrix4x4, "to_a", rb_matrix4x4_to_a, 0);
33
+ rb_define_method(rb_cMatrix4x4, "to_h", rb_matrix4x4_to_h, 0);
34
+ // to_mat3
35
+
36
+ // Operators
37
+ rb_define_method(rb_cMatrix4x4, "+", rb_matrix4x4_add, 1);
38
+ rb_define_method(rb_cMatrix4x4, "-", rb_matrix4x4_subtract, 1);
39
+ rb_define_method(rb_cMatrix4x4, "*", rb_matrix4x4_multiply, 1);
40
+ rb_define_method(rb_cMatrix4x4, "-@", rb_matrix4x4_negate, 0);
41
+ rb_define_method(rb_cMatrix4x4, "==", rb_matrix4x4_equal, 1);
42
+ rb_define_method(rb_cMatrix4x4, "[]", rb_matrix4x4_aref, -1);
43
+ rb_define_method(rb_cMatrix4x4, "[]=", rb_matrix4x4_aset, -1);
44
+ rb_define_method(rb_cMatrix4x4, "**", rb_matrix4x4_pow, 1);
45
+
46
+ // // Class
47
+ rb_define_singleton_method(rb_cMatrix4x4, "identity", rb_matrix4x4_identity, 0);
48
+ rb_define_singleton_method(rb_cMatrix4x4, "create_billboard", rb_matrix4x4_create_billboard, 4);
49
+ rb_define_singleton_method(rb_cMatrix4x4, "create_constrained_billboard", rb_matrix4x4_create_constrained_billboard, 5);
50
+ rb_define_singleton_method(rb_cMatrix4x4, "create_translation", rb_matrix4x4_create_translation, -1);
51
+ rb_define_singleton_method(rb_cMatrix4x4, "create_scale", rb_matrix4x4_create_scale, -1);
52
+ rb_define_singleton_method(rb_cMatrix4x4, "create_rotation_x", rb_matrix4x4_create_rotation_x, -1);
53
+ rb_define_singleton_method(rb_cMatrix4x4, "create_rotation_y", rb_matrix4x4_create_rotation_y, -1);
54
+ rb_define_singleton_method(rb_cMatrix4x4, "create_rotation_z", rb_matrix4x4_create_rotation_z, -1);
55
+ rb_define_singleton_method(rb_cMatrix4x4, "from_axis_angle", rb_matrix4x4_from_axis_angle, 2);
56
+ rb_define_singleton_method(rb_cMatrix4x4, "create_perspective_fov", rb_matrix4x4_create_perspective_fov, 4);
57
+ rb_define_singleton_method(rb_cMatrix4x4, "create_perspective", rb_matrix4x4_create_perspective_fov, 4);
58
+ rb_define_singleton_method(rb_cMatrix4x4, "create_perspective_off_center", rb_matrix4x4_create_perspective_off_center, 6);
59
+ rb_define_singleton_method(rb_cMatrix4x4, "create_orthographic", rb_matrix4x4_create_orthographic, 4);
60
+ rb_define_singleton_method(rb_cMatrix4x4, "create_orthographic_off_center", rb_matrix4x4_create_orthographic_off_center, 6);
61
+ rb_define_singleton_method(rb_cMatrix4x4, "create_look_at", rb_matrix4x4_create_look_at, 3);
62
+ rb_define_singleton_method(rb_cMatrix4x4, "create_world", rb_matrix4x4_create_world, 3);
63
+ rb_define_singleton_method(rb_cMatrix4x4, "from_quaternion", rb_matrix4x4_from_quaternion, 1);
64
+ rb_define_singleton_method(rb_cMatrix4x4, "from_yaw_pitch_roll", rb_matrix4x4_from_yaw_pitch_roll, 3);
65
+ rb_define_singleton_method(rb_cMatrix4x4, "create_shadow", rb_matrix4x4_create_shadow, 2);
66
+ rb_define_singleton_method(rb_cMatrix4x4, "create_reflection", rb_matrix4x4_create_reflection, 1);
67
+ rb_define_singleton_method(rb_cMatrix4x4, "lerp", rb_matrix4x4_lerp_s, 3);
68
+ }
69
+
70
+ static inline void numerix_cross_norm(Vector3 *v1, Vector3 *v2, Vector3 *result) {
71
+ float x = v1->y * v2->z - v1->z * v2->y;
72
+ float y = v1->z * v2->x - v1->x * v2->z;
73
+ float z = v1->x * v2->y - v1->y * v2->x;
74
+ float invLength = 1.0f / sqrtf(x * x + y * y + z * z);
75
+ result->x = x * invLength;
76
+ result->y = y * invLength;
77
+ result->z = z * invLength;
78
+ }
79
+
80
+ static inline void numerix_vec3_normalize(Vector3 *v) {
81
+ float invLength = 1.0f / sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
82
+ v->x *= invLength;
83
+ v->y *= invLength;
84
+ v->z *= invLength;
85
+ }
86
+
87
+ static inline void numerix_vec3_cross(Vector3 *v1, Vector3 *v2, Vector3 *result) {
88
+ result->x = v1->y * v2->z - v1->z * v2->y;
89
+ result->y = v1->z * v2->x - v1->x * v2->z;
90
+ result->z = v1->x * v2->y - v1->y * v2->x;
91
+ }
92
+
93
+ static inline float numerix_vec3_dot(Vector3 *v1, Vector3 *v2) {
94
+ return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z;
95
+ }
96
+
97
+ static VALUE rb_matrix4x4_allocate(VALUE klass) {
98
+ Matrix4x4 *m = ALLOC(Matrix4x4);
99
+ memset(m, 0, sizeof(Matrix4x4));
100
+ return NUMERIX_WRAP(klass, m);
101
+ }
102
+
103
+ VALUE rb_matrix4x4_initialize(int argc, VALUE *argv, VALUE self) {
104
+ MATRIX4X4();
105
+ if (argc == 16) {
106
+ m->m11 = NUM2FLT(argv[0]);
107
+ m->m12 = NUM2FLT(argv[1]);
108
+ m->m13 = NUM2FLT(argv[2]);
109
+ m->m14 = NUM2FLT(argv[3]);
110
+ m->m21 = NUM2FLT(argv[4]);
111
+ m->m22 = NUM2FLT(argv[5]);
112
+ m->m23 = NUM2FLT(argv[6]);
113
+ m->m24 = NUM2FLT(argv[7]);
114
+ m->m31 = NUM2FLT(argv[8]);
115
+ m->m32 = NUM2FLT(argv[9]);
116
+ m->m33 = NUM2FLT(argv[10]);
117
+ m->m34 = NUM2FLT(argv[11]);
118
+ m->m41 = NUM2FLT(argv[12]);
119
+ m->m42 = NUM2FLT(argv[13]);
120
+ m->m43 = NUM2FLT(argv[14]);
121
+ m->m44 = NUM2FLT(argv[15]);
122
+ } else if (argc == 1) {
123
+ if (NUMERIX_TYPE_P(argv[0], rb_cMatrix3x2)) {
124
+ Matrix3x2 *value;
125
+ Data_Get_Struct(argv[0], Matrix3x2, value);
126
+ m->m11 = value->m11;
127
+ m->m12 = value->m12;
128
+ m->m13 = 0.0f;
129
+ m->m14 = 0.0f;
130
+ m->m21 = value->m21;
131
+ m->m22 = value->m22;
132
+ m->m23 = 0.0f;
133
+ m->m24 = 0.0f;
134
+ m->m31 = 0.0f;
135
+ m->m32 = 0.0f;
136
+ m->m33 = 1.0f;
137
+ m->m34 = 0.0f;
138
+ m->m41 = value->m31;
139
+ m->m42 = value->m32;
140
+ m->m43 = 0.0f;
141
+ m->m44 = 1.0f;
142
+ } else {
143
+ float value = NUM2FLT(argv[0]);
144
+ float *p = (float *)m;
145
+ for (int i = 0; i < 16; i++)
146
+ p[i] = value;
147
+ }
148
+ } else if (argc != 0)
149
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 0, 16)", argc);
150
+
151
+ return Qnil;
152
+ }
153
+
154
+ VALUE rb_matrix4x4_lerp(VALUE self, VALUE other, VALUE amount) {
155
+ Matrix4x4 *m1, *m2, *result;
156
+ Data_Get_Struct(self, Matrix4x4, m1);
157
+ Data_Get_Struct(other, Matrix4x4, m2);
158
+ result = ALLOC(Matrix4x4);
159
+ float weight = NUMERIX_CLAMP(NUM2FLT(amount), 0.0f, 1.0f);
160
+
161
+ // First row
162
+ result->m11 = m1->m11 + (m2->m11 - m1->m11) * weight;
163
+ result->m12 = m1->m12 + (m2->m12 - m1->m12) * weight;
164
+ result->m13 = m1->m13 + (m2->m13 - m1->m13) * weight;
165
+ result->m14 = m1->m14 + (m2->m14 - m1->m14) * weight;
166
+
167
+ // Second row
168
+ result->m21 = m1->m21 + (m2->m21 - m1->m21) * weight;
169
+ result->m22 = m1->m22 + (m2->m22 - m1->m22) * weight;
170
+ result->m23 = m1->m23 + (m2->m23 - m1->m23) * weight;
171
+ result->m24 = m1->m24 + (m2->m24 - m1->m24) * weight;
172
+
173
+ // Third row
174
+ result->m31 = m1->m31 + (m2->m31 - m1->m31) * weight;
175
+ result->m32 = m1->m32 + (m2->m32 - m1->m32) * weight;
176
+ result->m33 = m1->m33 + (m2->m33 - m1->m33) * weight;
177
+ result->m34 = m1->m34 + (m2->m34 - m1->m34) * weight;
178
+
179
+ // Fourth row
180
+ result->m41 = m1->m41 + (m2->m41 - m1->m41) * weight;
181
+ result->m42 = m1->m42 + (m2->m42 - m1->m42) * weight;
182
+ result->m43 = m1->m43 + (m2->m43 - m1->m43) * weight;
183
+ result->m44 = m1->m44 + (m2->m44 - m1->m44) * weight;
184
+
185
+ return NUMERIX_WRAP(CLASS_OF(self), result);
186
+ }
187
+
188
+ VALUE rb_matrix4x4_lerp_bang(VALUE self, VALUE other, VALUE amount) {
189
+ Matrix4x4 *m1, *m2;
190
+ Data_Get_Struct(self, Matrix4x4, m1);
191
+ Data_Get_Struct(other, Matrix4x4, m2);
192
+ float weight = NUMERIX_CLAMP(NUM2FLT(amount), 0.0f, 1.0f);
193
+
194
+ // First row
195
+ m1->m11 = m1->m11 + (m2->m11 - m1->m11) * weight;
196
+ m1->m12 = m1->m12 + (m2->m12 - m1->m12) * weight;
197
+ m1->m13 = m1->m13 + (m2->m13 - m1->m13) * weight;
198
+ m1->m14 = m1->m14 + (m2->m14 - m1->m14) * weight;
199
+
200
+ // Second row
201
+ m1->m21 = m1->m21 + (m2->m21 - m1->m21) * weight;
202
+ m1->m22 = m1->m22 + (m2->m22 - m1->m22) * weight;
203
+ m1->m23 = m1->m23 + (m2->m23 - m1->m23) * weight;
204
+ m1->m24 = m1->m24 + (m2->m24 - m1->m24) * weight;
205
+
206
+ // Third row
207
+ m1->m31 = m1->m31 + (m2->m31 - m1->m31) * weight;
208
+ m1->m32 = m1->m32 + (m2->m32 - m1->m32) * weight;
209
+ m1->m33 = m1->m33 + (m2->m33 - m1->m33) * weight;
210
+ m1->m34 = m1->m34 + (m2->m34 - m1->m34) * weight;
211
+
212
+ // Fourth row
213
+ m1->m41 = m1->m41 + (m2->m41 - m1->m41) * weight;
214
+ m1->m42 = m1->m42 + (m2->m42 - m1->m42) * weight;
215
+ m1->m43 = m1->m43 + (m2->m43 - m1->m43) * weight;
216
+ m1->m44 = m1->m44 + (m2->m44 - m1->m44) * weight;
217
+
218
+ return self;
219
+ }
220
+
221
+ VALUE rb_matrix4x4_identity_p(VALUE self) {
222
+ MATRIX4X4();
223
+ // Check diagonal element first for early out.
224
+ return m->m11 == 1.0f && m->m22 == 1.0f && m->m33 == 1.0f && m->m44 == 1.0f &&
225
+ m->m12 == 0.0f && m->m13 == 0.0f && m->m14 == 0.0f && m->m21 == 0.0f &&
226
+ m->m23 == 0.0f && m->m24 == 0.0f && m->m31 == 0.0f && m->m32 == 0.0f &&
227
+ m->m34 == 0.0f && m->m41 == 0.0f && m->m42 == 0.0f && m->m43 == 0.0f
228
+ ? Qtrue
229
+ : Qfalse;
230
+ }
231
+
232
+ VALUE rb_matrix4x4_translation(VALUE self) {
233
+ MATRIX4X4();
234
+ Vector3 *v = ALLOC(Vector3);
235
+ v->x = m->m41;
236
+ v->y = m->m42;
237
+ v->z = m->m43;
238
+ return NUMERIX_WRAP(rb_cVector3, v);
239
+ }
240
+
241
+ VALUE rb_matrix4x4_translation_set(VALUE self, VALUE value) {
242
+ MATRIX4X4();
243
+ Vector3 *v;
244
+ Data_Get_Struct(value, Vector3, v);
245
+ m->m41 = v->x;
246
+ m->m42 = v->y;
247
+ m->m43 = v->z;
248
+ return value;
249
+ }
250
+
251
+ VALUE rb_matrix4x4_determinant(VALUE self) {
252
+ Matrix4x4 *matrix;
253
+ Data_Get_Struct(self, Matrix4x4, matrix);
254
+ // | a b c d | | f g h | | e g h | | e f h | | e f g |
255
+ // | e f g h | = a | j k l | - b | i k l | + c | i j l | - d | i j k |
256
+ // | i j k l | | n o p | | m o p | | m n p | | m n o |
257
+ // | m n o p |
258
+ //
259
+ // | f g h |
260
+ // a | j k l | = a ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
261
+ // | n o p |
262
+ //
263
+ // | e g h |
264
+ // b | i k l | = b ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
265
+ // | m o p |
266
+ //
267
+ // | e f h |
268
+ // c | i j l | = c ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
269
+ // | m n p |
270
+ //
271
+ // | e f g |
272
+ // d | i j k | = d ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
273
+ // | m n o |
274
+ //
275
+ // Cost of operation
276
+ // 17 adds and 28 muls.
277
+ //
278
+ // add: 6 + 8 + 3 = 17
279
+ // mul: 12 + 16 = 28
280
+
281
+ float a = matrix->m11, b = matrix->m12, c = matrix->m13, d = matrix->m14;
282
+ float e = matrix->m21, f = matrix->m22, g = matrix->m23, h = matrix->m24;
283
+ float i = matrix->m31, j = matrix->m32, k = matrix->m33, l = matrix->m34;
284
+ float m = matrix->m41, n = matrix->m42, o = matrix->m43, p = matrix->m44;
285
+
286
+ float kp_lo = k * p - l * o;
287
+ float jp_ln = j * p - l * n;
288
+ float jo_kn = j * o - k * n;
289
+ float ip_lm = i * p - l * m;
290
+ float io_km = i * o - k * m;
291
+ float in_jm = i * n - j * m;
292
+
293
+ return DBL2NUM(a * (f * kp_lo - g * jp_ln + h * jo_kn) -
294
+ b * (e * kp_lo - g * ip_lm + h * io_km) +
295
+ c * (e * jp_ln - f * ip_lm + h * in_jm) -
296
+ d * (e * jo_kn - f * io_km + g * in_jm));
297
+ }
298
+
299
+ VALUE rb_matrix4x4_to_s(VALUE self) {
300
+ MATRIX4X4();
301
+ return rb_sprintf("{{%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}, {%f, %f, %f, %f}}",
302
+ m->m11, m->m12, m->m13, m->m14, m->m21, m->m22, m->m23, m->m24,
303
+ m->m31, m->m32, m->m33, m->m34, m->m41, m->m42, m->m43, m->m44);
304
+ }
305
+
306
+ VALUE rb_matrix4x4_equal(VALUE self, VALUE other) {
307
+ if (CLASS_OF(other) != CLASS_OF(self))
308
+ return Qfalse;
309
+ Matrix4x4 *m1, *m2;
310
+ Data_Get_Struct(self, Matrix4x4, m1);
311
+ Data_Get_Struct(other, Matrix4x4, m2);
312
+ return FLT_EQUAL(m1->m11, m2->m11) && FLT_EQUAL(m1->m12, m2->m12) && FLT_EQUAL(m1->m13, m2->m13) && FLT_EQUAL(m1->m14, m2->m14) &&
313
+ FLT_EQUAL(m1->m21, m2->m21) && FLT_EQUAL(m1->m22, m2->m22) && FLT_EQUAL(m1->m23, m2->m23) && FLT_EQUAL(m1->m24, m2->m24) &&
314
+ FLT_EQUAL(m1->m31, m2->m31) && FLT_EQUAL(m1->m32, m2->m32) && FLT_EQUAL(m1->m33, m2->m33) && FLT_EQUAL(m1->m34, m2->m34) &&
315
+ FLT_EQUAL(m1->m41, m2->m41) && FLT_EQUAL(m1->m42, m2->m42) && FLT_EQUAL(m1->m43, m2->m43) && FLT_EQUAL(m1->m44, m2->m44)
316
+ ? Qtrue
317
+ : Qfalse;
318
+ }
319
+
320
+ VALUE rb_matrix4x4_pow(VALUE self, VALUE exponent) {
321
+ struct RData *rdata = RDATA(self);
322
+ const int count = 16;
323
+ float *result = ruby_xmalloc(sizeof(float) * count);
324
+ float *m = (float *)rdata->data;
325
+ float e = fabsf(NUM2FLT(exponent));
326
+ for (int i = 0; i < count; i++)
327
+ result[i] = powf(fabsf(m[i]), e);
328
+
329
+ return NUMERIX_WRAP(rdata->basic.klass, result);
330
+ }
331
+
332
+ VALUE rb_matrix4x4_to_a(VALUE self) {
333
+ MATRIX4X4();
334
+ VALUE array = rb_ary_new_capa(4);
335
+ // Row 1
336
+ VALUE r1 = rb_ary_new_capa(4);
337
+ rb_ary_store(r1, 0, DBL2NUM(m->m11));
338
+ rb_ary_store(r1, 1, DBL2NUM(m->m12));
339
+ rb_ary_store(r1, 2, DBL2NUM(m->m13));
340
+ rb_ary_store(r1, 3, DBL2NUM(m->m14));
341
+ rb_ary_push(array, r1);
342
+ // Row 2
343
+ VALUE r2 = rb_ary_new_capa(4);
344
+ rb_ary_store(r2, 0, DBL2NUM(m->m21));
345
+ rb_ary_store(r2, 1, DBL2NUM(m->m22));
346
+ rb_ary_store(r2, 2, DBL2NUM(m->m23));
347
+ rb_ary_store(r2, 3, DBL2NUM(m->m24));
348
+ rb_ary_push(array, r2);
349
+ // Row 3
350
+ VALUE r3 = rb_ary_new_capa(4);
351
+ rb_ary_store(r3, 0, DBL2NUM(m->m31));
352
+ rb_ary_store(r3, 1, DBL2NUM(m->m32));
353
+ rb_ary_store(r3, 2, DBL2NUM(m->m33));
354
+ rb_ary_store(r3, 3, DBL2NUM(m->m34));
355
+ rb_ary_push(array, r3);
356
+ // Row 4
357
+ VALUE r4 = rb_ary_new_capa(4);
358
+ rb_ary_store(r4, 0, DBL2NUM(m->m41));
359
+ rb_ary_store(r4, 1, DBL2NUM(m->m42));
360
+ rb_ary_store(r4, 2, DBL2NUM(m->m43));
361
+ rb_ary_store(r4, 3, DBL2NUM(m->m44));
362
+ rb_ary_push(array, r4);
363
+ return array;
364
+ }
365
+
366
+ VALUE rb_matrix4x4_row(VALUE self, VALUE row) {
367
+ MATRIX4X4();
368
+ VALUE args = rb_ary_new_capa(4);
369
+ int r = NUM2INT(row);
370
+ switch (r) {
371
+ case 0: {
372
+ rb_ary_push(args, DBL2NUM(m->m11));
373
+ rb_ary_push(args, DBL2NUM(m->m12));
374
+ rb_ary_push(args, DBL2NUM(m->m13));
375
+ rb_ary_push(args, DBL2NUM(m->m14));
376
+ break;
377
+ }
378
+ case 1: {
379
+ rb_ary_push(args, DBL2NUM(m->m21));
380
+ rb_ary_push(args, DBL2NUM(m->m22));
381
+ rb_ary_push(args, DBL2NUM(m->m23));
382
+ rb_ary_push(args, DBL2NUM(m->m24));
383
+ break;
384
+ }
385
+ case 2: {
386
+ rb_ary_push(args, DBL2NUM(m->m31));
387
+ rb_ary_push(args, DBL2NUM(m->m32));
388
+ rb_ary_push(args, DBL2NUM(m->m33));
389
+ rb_ary_push(args, DBL2NUM(m->m34));
390
+ break;
391
+ }
392
+ case 3: {
393
+ rb_ary_push(args, DBL2NUM(m->m41));
394
+ rb_ary_push(args, DBL2NUM(m->m42));
395
+ rb_ary_push(args, DBL2NUM(m->m43));
396
+ rb_ary_push(args, DBL2NUM(m->m44));
397
+ break;
398
+ }
399
+ default:
400
+ break;
401
+ }
402
+ return args;
403
+ }
404
+
405
+ VALUE rb_matrix4x4_column(VALUE self, VALUE column) {
406
+ MATRIX4X4();
407
+ VALUE args = rb_ary_new_capa(4);
408
+ int c = NUM2INT(column);
409
+ switch (c) {
410
+ case 0: {
411
+ rb_ary_push(args, DBL2NUM(m->m11));
412
+ rb_ary_push(args, DBL2NUM(m->m21));
413
+ rb_ary_push(args, DBL2NUM(m->m31));
414
+ rb_ary_push(args, DBL2NUM(m->m41));
415
+ break;
416
+ }
417
+ case 1: {
418
+ rb_ary_push(args, DBL2NUM(m->m12));
419
+ rb_ary_push(args, DBL2NUM(m->m22));
420
+ rb_ary_push(args, DBL2NUM(m->m32));
421
+ rb_ary_push(args, DBL2NUM(m->m42));
422
+ break;
423
+ }
424
+ case 2: {
425
+ rb_ary_push(args, DBL2NUM(m->m13));
426
+ rb_ary_push(args, DBL2NUM(m->m23));
427
+ rb_ary_push(args, DBL2NUM(m->m33));
428
+ rb_ary_push(args, DBL2NUM(m->m43));
429
+ break;
430
+ }
431
+ case 3: {
432
+ rb_ary_push(args, DBL2NUM(m->m14));
433
+ rb_ary_push(args, DBL2NUM(m->m24));
434
+ rb_ary_push(args, DBL2NUM(m->m34));
435
+ rb_ary_push(args, DBL2NUM(m->m44));
436
+ break;
437
+ }
438
+ default:
439
+ break;
440
+ }
441
+ return args;
442
+ }
443
+
444
+ VALUE rb_matrix4x4_map(VALUE self) {
445
+ const int count = 16;
446
+ struct RData *rdata = RDATA(self);
447
+ float *flt = (float *)rdata->data;
448
+ float *result = (float *)ruby_xmalloc(count * sizeof(float));
449
+
450
+ for (int i = 0; i < count; i++)
451
+ result[i] = NUM2FLT(rb_yield(DBL2NUM(flt[i])));
452
+
453
+ return NUMERIX_WRAP(rdata->basic.klass, result);
454
+ }
455
+
456
+ VALUE rb_matrix4x4_map_bang(VALUE self) {
457
+ const int count = 16;
458
+ float *flt = (float *)RDATA(self)->data;
459
+
460
+ for (int i = 0; i < count; i++)
461
+ flt[i] = NUM2FLT(rb_yield(DBL2NUM(flt[i])));
462
+
463
+ return self;
464
+ }
465
+
466
+ VALUE rb_matrix4x4_to_h(VALUE self) {
467
+ MATRIX4X4();
468
+ VALUE hash = rb_hash_new();
469
+ rb_hash_aset(hash, ID2SYM(rb_intern("m11")), DBL2NUM(m->m11));
470
+ rb_hash_aset(hash, ID2SYM(rb_intern("m12")), DBL2NUM(m->m12));
471
+ rb_hash_aset(hash, ID2SYM(rb_intern("m13")), DBL2NUM(m->m13));
472
+ rb_hash_aset(hash, ID2SYM(rb_intern("m14")), DBL2NUM(m->m14));
473
+
474
+ rb_hash_aset(hash, ID2SYM(rb_intern("m21")), DBL2NUM(m->m21));
475
+ rb_hash_aset(hash, ID2SYM(rb_intern("m22")), DBL2NUM(m->m22));
476
+ rb_hash_aset(hash, ID2SYM(rb_intern("m23")), DBL2NUM(m->m23));
477
+ rb_hash_aset(hash, ID2SYM(rb_intern("m24")), DBL2NUM(m->m24));
478
+
479
+ rb_hash_aset(hash, ID2SYM(rb_intern("m31")), DBL2NUM(m->m31));
480
+ rb_hash_aset(hash, ID2SYM(rb_intern("m32")), DBL2NUM(m->m32));
481
+ rb_hash_aset(hash, ID2SYM(rb_intern("m33")), DBL2NUM(m->m33));
482
+ rb_hash_aset(hash, ID2SYM(rb_intern("m34")), DBL2NUM(m->m34));
483
+
484
+ rb_hash_aset(hash, ID2SYM(rb_intern("m41")), DBL2NUM(m->m41));
485
+ rb_hash_aset(hash, ID2SYM(rb_intern("m42")), DBL2NUM(m->m42));
486
+ rb_hash_aset(hash, ID2SYM(rb_intern("m43")), DBL2NUM(m->m43));
487
+ rb_hash_aset(hash, ID2SYM(rb_intern("m44")), DBL2NUM(m->m44));
488
+ return hash;
489
+ }
490
+
491
+ VALUE rb_matrix4x4_each_row(VALUE self) {
492
+ MATRIX4X4();
493
+ for (int i = 0; i < 4; i++) {
494
+ VALUE index = INT2NUM(i);
495
+ rb_yield(rb_matrix4x4_row(self, index));
496
+ }
497
+ return self;
498
+ }
499
+
500
+ VALUE rb_matrix4x4_each_column(VALUE self) {
501
+ MATRIX4X4();
502
+ for (int i = 0; i < 4; i++) {
503
+ VALUE index = INT2NUM(i);
504
+ rb_yield(rb_matrix4x4_column(self, index));
505
+ }
506
+ return self;
507
+ }
508
+
509
+ VALUE rb_matrix4x4_aref(int argc, VALUE *argv, VALUE self) {
510
+ if (argc == 1) {
511
+ return rb_call_super(1, argv);
512
+ } else if (argc == 2) {
513
+ int r = NUM2INT(argv[0]);
514
+ int c = NUM2INT(argv[1]);
515
+ if (r < 0 || r > 3 || c < 0 || c > 3)
516
+ return Qnil;
517
+ argv[0] = INT2NUM(r + (c * 4));
518
+ return rb_call_super(1, argv);
519
+ }
520
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 2)", argc);
521
+ return Qnil;
522
+ }
523
+
524
+ VALUE rb_matrix4x4_aset(int argc, VALUE *argv, VALUE self) {
525
+ if (argc == 2) {
526
+ return rb_call_super(2, argv);
527
+ } else if (argc == 3) {
528
+ int r = NUM2INT(argv[0]);
529
+ int c = NUM2INT(argv[1]);
530
+ if (r < 0 || r > 3 || c < 0 || c > 3)
531
+ return Qnil;
532
+ argv[1] = INT2NUM(r + (c * 4));
533
+ return rb_call_super(2, &argv[1]);
534
+ }
535
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2, 3)", argc);
536
+ return Qnil;
537
+ }
538
+
539
+ VALUE rb_matrix4x4_identity(VALUE klass) {
540
+ Matrix4x4 *m = ALLOC(Matrix4x4);
541
+ m->m11 = 1.0f;
542
+ m->m12 = 0.0f;
543
+ m->m13 = 0.0f;
544
+ m->m14 = 0.0f;
545
+ m->m21 = 0.0f;
546
+ m->m22 = 1.0f;
547
+ m->m23 = 0.0f;
548
+ m->m24 = 0.0f;
549
+ m->m31 = 0.0f;
550
+ m->m32 = 0.0f;
551
+ m->m33 = 1.0f;
552
+ m->m34 = 0.0f;
553
+ m->m41 = 0.0f;
554
+ m->m42 = 0.0f;
555
+ m->m43 = 0.0f;
556
+ m->m44 = 1.0f;
557
+ return NUMERIX_WRAP(klass, m);
558
+ }
559
+
560
+ VALUE rb_matrix4x4_create_billboard(VALUE klass, VALUE position, VALUE camera, VALUE up, VALUE forward) {
561
+ const float epsilon = 1e-4f;
562
+ Vector3 *pos, *cam, *u, *f;
563
+ Data_Get_Struct(position, Vector3, pos);
564
+ Data_Get_Struct(camera, Vector3, cam);
565
+ Data_Get_Struct(up, Vector3, u);
566
+ Data_Get_Struct(forward, Vector3, f);
567
+
568
+ Vector3 *axis = xmalloc(sizeof(Vector3) * 3);
569
+
570
+ axis[2].x = pos->x - cam->x;
571
+ axis[2].y = pos->y - cam->y;
572
+ axis[2].z = pos->z - cam->z;
573
+
574
+ float norm = axis[2].x * axis[2].x + axis[2].y * axis[2].y + axis[2].z * axis[2].z;
575
+
576
+ if (norm < epsilon) {
577
+ axis[2].x = -f->x;
578
+ axis[2].y = -f->y;
579
+ axis[2].z = -f->z;
580
+ } else {
581
+ float inverseNorm = 1.0f / sqrtf(norm);
582
+ axis[2].x *= inverseNorm;
583
+ axis[2].y *= inverseNorm;
584
+ axis[2].z *= inverseNorm;
585
+ }
586
+
587
+ numerix_cross_norm(u, &axis[2], &axis[0]);
588
+ numerix_vec3_cross(&axis[2], &axis[0], &axis[1]);
589
+
590
+ Matrix4x4 *result = ALLOC(Matrix4x4);
591
+
592
+ result->m11 = axis[0].x;
593
+ result->m12 = axis[0].y;
594
+ result->m13 = axis[0].z;
595
+ result->m14 = 0.0f;
596
+ result->m21 = axis[1].x;
597
+ result->m22 = axis[1].y;
598
+ result->m23 = axis[1].z;
599
+ result->m24 = 0.0f;
600
+ result->m31 = axis[2].x;
601
+ result->m32 = axis[2].y;
602
+ result->m33 = axis[2].z;
603
+ result->m34 = 0.0f;
604
+
605
+ result->m41 = pos->x;
606
+ result->m42 = pos->y;
607
+ result->m43 = pos->z;
608
+ result->m44 = 1.0f;
609
+
610
+ xfree(axis);
611
+
612
+ return NUMERIX_WRAP(klass, result);
613
+ }
614
+
615
+ VALUE rb_matrix4x4_create_constrained_billboard(VALUE klass, VALUE position, VALUE rotate, VALUE camera, VALUE up, VALUE forward) {
616
+ const float epsilon = 1e-4f;
617
+ const float minAngle = 1.0f - (0.1f * (NUMERIX_PI / 180.0f)); // 0.1 degrees
618
+
619
+ Vector3 *pos, *cam, *r, *u, *f;
620
+ Data_Get_Struct(position, Vector3, pos);
621
+ Data_Get_Struct(camera, Vector3, cam);
622
+ Data_Get_Struct(rotate, Vector3, r);
623
+ Data_Get_Struct(up, Vector3, u);
624
+ Data_Get_Struct(forward, Vector3, f);
625
+
626
+ Vector3 *axis = xmalloc(sizeof(Vector3) * 3);
627
+ Vector3 *faceDir = xmalloc(sizeof(Vector3));
628
+
629
+ faceDir->x = pos->x - cam->x;
630
+ faceDir->y = pos->y - cam->y;
631
+ faceDir->z = pos->z - cam->z;
632
+
633
+ float norm = faceDir->x * faceDir->x + faceDir->y * faceDir->y + faceDir->z * faceDir->z;
634
+ if (norm < epsilon) {
635
+ faceDir->x = -f->x;
636
+ faceDir->y = -f->y;
637
+ faceDir->z = -f->z;
638
+ } else {
639
+ float invNorm = 1.0f / sqrtf(norm);
640
+ faceDir->x *= invNorm;
641
+ faceDir->y *= invNorm;
642
+ faceDir->z *= invNorm;
643
+ }
644
+
645
+ // Treat the case when angle between faceDir and rotateAxis is too close to 0.
646
+ float dot = numerix_vec3_dot(r, faceDir);
647
+
648
+ if (fabsf(dot) > minAngle) {
649
+ memcpy(&axis[2], f, sizeof(Vector2));
650
+ // Make sure passed values are useful for compute.
651
+ dot = numerix_vec3_dot(r, &axis[2]);
652
+
653
+ if (fabsf(dot) > minAngle) {
654
+ axis[2].y = 0.0f;
655
+ if (fabsf(r->z) > minAngle) {
656
+ axis[2].x = 1.0f;
657
+ axis[2].z = 0.0f;
658
+ } else {
659
+ axis[2].x = 0.0f;
660
+ axis[2].z = -1.0f;
661
+ }
662
+ }
663
+ numerix_cross_norm(r, &axis[2], &axis[0]);
664
+ numerix_cross_norm(&axis[0], r, &axis[2]);
665
+ } else {
666
+ numerix_cross_norm(r, faceDir, &axis[0]);
667
+ numerix_cross_norm(&axis[0], &axis[1], &axis[2]);
668
+ }
669
+
670
+ Matrix4x4 *result = ALLOC(Matrix4x4);
671
+
672
+ result->m11 = axis[0].x;
673
+ result->m12 = axis[0].y;
674
+ result->m13 = axis[0].z;
675
+ result->m14 = 0.0f;
676
+ result->m21 = axis[1].x;
677
+ result->m22 = axis[1].y;
678
+ result->m23 = axis[1].z;
679
+ result->m24 = 0.0f;
680
+ result->m31 = axis[2].x;
681
+ result->m32 = axis[2].y;
682
+ result->m33 = axis[2].z;
683
+ result->m34 = 0.0f;
684
+
685
+ result->m41 = pos->x;
686
+ result->m42 = pos->y;
687
+ result->m43 = pos->z;
688
+ result->m44 = 1.0f;
689
+
690
+ xfree(axis);
691
+ xfree(faceDir);
692
+
693
+ return NUMERIX_WRAP(klass, result);
694
+ }
695
+
696
+ VALUE rb_matrix4x4_create_translation(int argc, VALUE *argv, VALUE klass) {
697
+ float x, y, z;
698
+ if (argc == 1) {
699
+ Vector3 *v;
700
+ Data_Get_Struct(argv[0], Vector3, v);
701
+ x = v->x;
702
+ y = v->y;
703
+ z = v->z;
704
+ } else if (argc == 3) {
705
+ x = NUM2FLT(argv[0]);
706
+ y = NUM2FLT(argv[1]);
707
+ z = NUM2FLT(argv[2]);
708
+ } else
709
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 3)", argc);
710
+
711
+ Matrix4x4 *result = ALLOC(Matrix4x4);
712
+ result->m11 = 1.0f;
713
+ result->m12 = 0.0f;
714
+ result->m13 = 0.0f;
715
+ result->m14 = 0.0f;
716
+ result->m21 = 0.0f;
717
+ result->m22 = 1.0f;
718
+ result->m23 = 0.0f;
719
+ result->m24 = 0.0f;
720
+ result->m31 = 0.0f;
721
+ result->m32 = 0.0f;
722
+ result->m33 = 1.0f;
723
+ result->m34 = 0.0f;
724
+
725
+ result->m41 = x;
726
+ result->m42 = y;
727
+ result->m43 = z;
728
+ result->m44 = 1.0f;
729
+
730
+ return NUMERIX_WRAP(klass, result);
731
+ }
732
+
733
+ VALUE rb_matrix4x4_create_scale(int argc, VALUE *argv, VALUE klass) {
734
+ float xscale, yscale, zscale, tx = 0.0f, ty = 0.0f, tz = 0.0f;
735
+
736
+ switch (argc) {
737
+ case 1: {
738
+ if (NUMERIX_TYPE_P(argv[0], rb_cVector3)) // (vec3)
739
+ {
740
+ Vector3 *vec3;
741
+ Data_Get_Struct(argv[0], Vector3, vec3);
742
+ xscale = vec3->x;
743
+ yscale = vec3->y;
744
+ zscale = vec3->z;
745
+ } else // (float)
746
+ {
747
+ xscale = yscale = zscale = NUM2FLT(argv[0]);
748
+ }
749
+ break;
750
+ }
751
+ case 2: {
752
+ Vector3 *cp;
753
+ Data_Get_Struct(argv[1], Vector3, cp);
754
+ if (NUMERIX_TYPE_P(argv[0], rb_cVector3)) // (vec3, vec3)
755
+ {
756
+ Vector3 *v3;
757
+ Data_Get_Struct(argv[1], Vector3, v3);
758
+ xscale = v3->x;
759
+ yscale = v3->y;
760
+ zscale = v3->z;
761
+ } else // (float, vec3)
762
+ {
763
+ xscale = yscale = zscale = NUM2FLT(argv[0]);
764
+ }
765
+ tx = cp->x * (1.0f - xscale);
766
+ ty = cp->y * (1.0f - yscale);
767
+ tz = cp->z * (1.0f - zscale);
768
+ break;
769
+ }
770
+ case 3: // (float, float, float)
771
+ {
772
+ xscale = NUM2FLT(argv[0]);
773
+ yscale = NUM2FLT(argv[1]);
774
+ zscale = NUM2FLT(argv[2]);
775
+ break;
776
+ }
777
+ case 4: // (float, float, float, vec3)
778
+ {
779
+ xscale = NUM2FLT(argv[0]);
780
+ yscale = NUM2FLT(argv[1]);
781
+ zscale = NUM2FLT(argv[2]);
782
+ Vector3 *center;
783
+ Data_Get_Struct(argv[3], Vector3, center);
784
+ tx = center->x * (1.0f - xscale);
785
+ ty = center->y * (1.0f - yscale);
786
+ tz = center->z * (1.0f - zscale);
787
+ break;
788
+ }
789
+ default: {
790
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 2, 3, 4)", argc);
791
+ break;
792
+ }
793
+ }
794
+ Matrix4x4 *result = ALLOC(Matrix4x4);
795
+ result->m11 = xscale;
796
+ result->m12 = 0.0f;
797
+ result->m13 = 0.0f;
798
+ result->m14 = 0.0f;
799
+ result->m21 = 0.0f;
800
+ result->m22 = yscale;
801
+ result->m23 = 0.0f;
802
+ result->m24 = 0.0f;
803
+ result->m31 = 0.0f;
804
+ result->m32 = 0.0f;
805
+ result->m33 = zscale;
806
+ result->m34 = 0.0f;
807
+ result->m41 = tx;
808
+ result->m42 = ty;
809
+ result->m43 = tz;
810
+ result->m44 = 1.0f;
811
+
812
+ return NUMERIX_WRAP(klass, result);
813
+ }
814
+
815
+ VALUE rb_matrix4x4_create_rotation_x(int argc, VALUE *argv, VALUE klass) {
816
+ if (argc != 1 && argc != 2)
817
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 2)", argc);
818
+
819
+ float radians = NUM2FLT(argv[0]);
820
+ float c = cosf(radians);
821
+ float s = sinf(radians);
822
+ float y = 0.0f, z = 0.0f;
823
+ if (argc == 2) {
824
+ Vector3 *center;
825
+ Data_Get_Struct(argv[1], Vector3, center);
826
+ y = center->y * (1.0f - c) + center->z * s;
827
+ z = center->z * (1.0f - c) - center->y * s;
828
+ }
829
+
830
+ Matrix4x4 *result = ALLOC(Matrix4x4);
831
+ // [ 1 0 0 0 ]
832
+ // [ 0 c s 0 ]
833
+ // [ 0 -s c 0 ]
834
+ // [ 0 y z 1 ]
835
+ result->m11 = 1.0f;
836
+ result->m12 = 0.0f;
837
+ result->m13 = 0.0f;
838
+ result->m14 = 0.0f;
839
+ result->m21 = 0.0f;
840
+ result->m22 = c;
841
+ result->m23 = s;
842
+ result->m24 = 0.0f;
843
+ result->m31 = 0.0f;
844
+ result->m32 = -s;
845
+ result->m33 = c;
846
+ result->m34 = 0.0f;
847
+ result->m41 = 0.0f;
848
+ result->m42 = y;
849
+ result->m43 = z;
850
+ result->m44 = 1.0f;
851
+
852
+ return NUMERIX_WRAP(klass, result);
853
+ }
854
+
855
+ VALUE rb_matrix4x4_create_rotation_y(int argc, VALUE *argv, VALUE klass) {
856
+ if (argc != 1 && argc != 2)
857
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 2)", argc);
858
+
859
+ float radians = NUM2FLT(argv[0]);
860
+ float c = cosf(radians);
861
+ float s = sinf(radians);
862
+ float x = 0.0f, z = 0.0f;
863
+ if (argc == 2) {
864
+ Vector3 *center;
865
+ Data_Get_Struct(argv[1], Vector3, center);
866
+ x = center->x * (1.0f - c) - center->z * s;
867
+ z = center->z * (1.0f - c) + center->x * s;
868
+ }
869
+
870
+ Matrix4x4 *result = ALLOC(Matrix4x4);
871
+ // [ c 0 -s 0 ]
872
+ // [ 0 1 0 0 ]
873
+ // [ s 0 c 0 ]
874
+ // [ x 0 z 1 ]
875
+ result->m11 = c;
876
+ result->m12 = 0.0f;
877
+ result->m13 = -s;
878
+ result->m14 = 0.0f;
879
+ result->m21 = 0.0f;
880
+ result->m22 = 1.0f;
881
+ result->m23 = 0.0f;
882
+ result->m24 = 0.0f;
883
+ result->m31 = s;
884
+ result->m32 = 0.0f;
885
+ result->m33 = c;
886
+ result->m34 = 0.0f;
887
+ result->m41 = x;
888
+ result->m42 = 0.0f;
889
+ result->m43 = z;
890
+ result->m44 = 1.0f;
891
+
892
+ return NUMERIX_WRAP(klass, result);
893
+ }
894
+
895
+ VALUE rb_matrix4x4_create_rotation_z(int argc, VALUE *argv, VALUE klass) {
896
+ if (argc != 1 && argc != 2)
897
+ rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1, 2)", argc);
898
+
899
+ float radians = NUM2FLT(argv[0]);
900
+ float c = cosf(radians);
901
+ float s = sinf(radians);
902
+ float x = 0.0f, y = 0.0f;
903
+ if (argc == 2) {
904
+ Vector3 *center;
905
+ Data_Get_Struct(argv[1], Vector3, center);
906
+ x = center->x * (1.0f - c) + center->y * s;
907
+ y = center->y * (1.0f - c) - center->x * s;
908
+ }
909
+
910
+ Matrix4x4 *result = ALLOC(Matrix4x4);
911
+ // [ c s 0 0 ]
912
+ // [ -s c 0 0 ]
913
+ // [ 0 0 1 0 ]
914
+ // [ x y 0 1 ]
915
+ result->m11 = c;
916
+ result->m12 = s;
917
+ result->m13 = 0.0f;
918
+ result->m14 = 0.0f;
919
+ result->m21 = -s;
920
+ result->m22 = c;
921
+ result->m23 = 0.0f;
922
+ result->m24 = 0.0f;
923
+ result->m31 = 0.0f;
924
+ result->m32 = 0.0f;
925
+ result->m33 = 1.0f;
926
+ result->m34 = 0.0f;
927
+ result->m41 = x;
928
+ result->m42 = y;
929
+ result->m43 = 0.0f;
930
+ result->m44 = 1.0f;
931
+
932
+ return NUMERIX_WRAP(klass, result);
933
+ }
934
+
935
+ VALUE rb_matrix4x4_from_axis_angle(VALUE klass, VALUE axis, VALUE angle) {
936
+ // a: angle
937
+ // x, y, z: unit vector for axis.
938
+ //
939
+ // Rotation matrix M can compute by using below equation.
940
+ //
941
+ // T T
942
+ // M = uu + (cos a)( I-uu ) + (sin a)S
943
+ //
944
+ // Where:
945
+ //
946
+ // u = ( x, y, z )
947
+ //
948
+ // [ 0 -z y ]
949
+ // S = [ z 0 -x ]
950
+ // [ -y x 0 ]
951
+ //
952
+ // [ 1 0 0 ]
953
+ // I = [ 0 1 0 ]
954
+ // [ 0 0 1 ]
955
+ //
956
+ //
957
+ // [ xx+cosa*(1-xx) yx-cosa*yx-sina*z zx-cosa*xz+sina*y ]
958
+ // M = [ xy-cosa*yx+sina*z yy+cosa(1-yy) yz-cosa*yz-sina*x ]
959
+ // [ zx-cosa*zx-sina*y zy-cosa*zy+sina*x zz+cosa*(1-zz) ]
960
+ //
961
+
962
+ float ang = NUM2FLT(angle);
963
+ Vector3 *v;
964
+ Data_Get_Struct(axis, Vector3, v);
965
+
966
+ float x = v->x, y = v->y, z = v->z;
967
+ float sa = sinf(ang), ca = cosf(ang);
968
+ float xx = x * x, yy = y * y, zz = z * z;
969
+ float xy = x * y, xz = x * z, yz = y * z;
970
+
971
+ Matrix4x4 *result = ALLOC(Matrix4x4);
972
+
973
+ result->m11 = xx + ca * (1.0f - xx);
974
+ result->m12 = xy - ca * xy + sa * z;
975
+ result->m13 = xz - ca * xz - sa * y;
976
+ result->m14 = 0.0f;
977
+ result->m21 = xy - ca * xy - sa * z;
978
+ result->m22 = yy + ca * (1.0f - yy);
979
+ result->m23 = yz - ca * yz + sa * x;
980
+ result->m24 = 0.0f;
981
+ result->m31 = xz - ca * xz + sa * y;
982
+ result->m32 = yz - ca * yz - sa * x;
983
+ result->m33 = zz + ca * (1.0f - zz);
984
+ result->m34 = 0.0f;
985
+ result->m41 = 0.0f;
986
+ result->m42 = 0.0f;
987
+ result->m43 = 0.0f;
988
+ result->m44 = 1.0f;
989
+
990
+ return NUMERIX_WRAP(klass, result);
991
+ }
992
+
993
+ VALUE rb_matrix4x4_create_perspective_fov(VALUE klass, VALUE fieldOfView, VALUE aspectRatio, VALUE nearDist, VALUE farDist) {
994
+ float n = NUM2FLT(nearDist);
995
+ float f = NUM2FLT(farDist);
996
+ if (n <= 0.0f)
997
+ rb_raise(rb_eRangeError, "near plane distance must be value greater than 0.0 (given %f)", n);
998
+ if (f <= 0.0f)
999
+ rb_raise(rb_eRangeError, "far plane distance must be value greater than 0.0 (given %f)", n);
1000
+ if (n <= 0.0f)
1001
+ rb_raise(rb_eRangeError, "near plane distance must be less than far plane distance");
1002
+
1003
+ float ratio = NUM2FLT(aspectRatio);
1004
+ float fov = NUM2FLT(fieldOfView);
1005
+ if (fov <= 0.0f || fov >= NUMERIX_PI)
1006
+ rb_raise(rb_eRangeError, "field of view must be value greater than 0.0 and less than PI (given %f)", fov);
1007
+
1008
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1009
+
1010
+ float yscale = 1.0f / tanf(fov * 0.5f);
1011
+ float xscale = yscale / ratio;
1012
+
1013
+ result->m11 = xscale;
1014
+ result->m12 = result->m13 = result->m14 = 0.0f;
1015
+
1016
+ result->m22 = yscale;
1017
+ result->m21 = result->m23 = result->m24 = 0.0f;
1018
+
1019
+ result->m31 = result->m32 = 0.0f;
1020
+ result->m33 = f / (n - f);
1021
+ result->m34 = -1.0f;
1022
+
1023
+ result->m41 = result->m42 = result->m44 = 0.0f;
1024
+ result->m43 = n * f / (n - f);
1025
+
1026
+ return NUMERIX_WRAP(klass, result);
1027
+ }
1028
+
1029
+ VALUE rb_matrix4x4_create_perspective(VALUE klass, VALUE width, VALUE height, VALUE nearDist, VALUE farDist) {
1030
+ float n = NUM2FLT(nearDist);
1031
+ float f = NUM2FLT(farDist);
1032
+ if (n <= 0.0f)
1033
+ rb_raise(rb_eRangeError, "near plane distance must be value greater than 0.0 (given %f)", n);
1034
+ if (f <= 0.0f)
1035
+ rb_raise(rb_eRangeError, "far plane distance must be value greater than 0.0 (given %f)", n);
1036
+ if (n <= 0.0f)
1037
+ rb_raise(rb_eRangeError, "near plane distance must be less than far plane distance");
1038
+
1039
+ float w = NUM2FLT(width);
1040
+ float h = NUM2FLT(height);
1041
+
1042
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1043
+
1044
+ result->m11 = 2.0f * n / w;
1045
+ result->m12 = result->m13 = result->m14 = 0.0f;
1046
+
1047
+ result->m22 = 2.0f * n / h;
1048
+ result->m21 = result->m23 = result->m24 = 0.0f;
1049
+
1050
+ result->m33 = f / (n - f);
1051
+ result->m31 = result->m32 = 0.0f;
1052
+ result->m34 = -1.0f;
1053
+
1054
+ result->m41 = result->m42 = result->m44 = 0.0f;
1055
+ result->m43 = n * f / (n - f);
1056
+
1057
+ return NUMERIX_WRAP(klass, result);
1058
+ }
1059
+
1060
+ VALUE rb_matrix4x4_create_perspective_off_center(VALUE klass, VALUE left, VALUE right, VALUE bottom, VALUE top, VALUE nearDist, VALUE farDist) {
1061
+ float n = NUM2FLT(nearDist);
1062
+ float f = NUM2FLT(farDist);
1063
+ if (n <= 0.0f)
1064
+ rb_raise(rb_eRangeError, "near plane distance must be value greater than 0.0 (given %f)", n);
1065
+ if (f <= 0.0f)
1066
+ rb_raise(rb_eRangeError, "far plane distance must be value greater than 0.0 (given %f)", n);
1067
+ if (n <= 0.0f)
1068
+ rb_raise(rb_eRangeError, "near plane distance must be less than far plane distance");
1069
+
1070
+ float l = NUM2FLT(left);
1071
+ float r = NUM2FLT(right);
1072
+ float t = NUM2FLT(top);
1073
+ float b = NUM2FLT(bottom);
1074
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1075
+
1076
+ result->m11 = 2.0f * n / (r - l);
1077
+ result->m12 = result->m13 = result->m14 = 0.0f;
1078
+
1079
+ result->m22 = 2.0f * n / (t - b);
1080
+ result->m21 = result->m23 = result->m24 = 0.0f;
1081
+
1082
+ result->m31 = (l + r) / (r - l);
1083
+ result->m32 = (t + b) / (t - b);
1084
+ result->m33 = f / (n - f);
1085
+ result->m34 = -1.0f;
1086
+
1087
+ result->m43 = n * f / (n - f);
1088
+ result->m41 = result->m42 = result->m44 = 0.0f;
1089
+
1090
+ return NUMERIX_WRAP(klass, result);
1091
+ }
1092
+
1093
+ VALUE rb_matrix4x4_create_orthographic(VALUE klass, VALUE width, VALUE height, VALUE zNearPlane, VALUE zFarPlane) {
1094
+ float n = NUM2FLT(zNearPlane);
1095
+ float f = NUM2FLT(zFarPlane);
1096
+
1097
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1098
+
1099
+ result->m11 = 2.0f / NUM2FLT(width);
1100
+ result->m12 = result->m13 = result->m14 = 0.0f;
1101
+
1102
+ result->m22 = 2.0f / NUM2FLT(height);
1103
+ result->m21 = result->m23 = result->m24 = 0.0f;
1104
+
1105
+ result->m33 = 1.0f / (n - f);
1106
+ result->m31 = result->m32 = result->m34 = 0.0f;
1107
+
1108
+ result->m41 = result->m42 = 0.0f;
1109
+ result->m43 = n / (n - f);
1110
+ result->m44 = 1.0f;
1111
+
1112
+ return NUMERIX_WRAP(klass, result);
1113
+ }
1114
+
1115
+ VALUE rb_matrix4x4_create_orthographic_off_center(VALUE klass, VALUE left, VALUE right, VALUE bottom, VALUE top, VALUE zNearPlane, VALUE zFarPlane) {
1116
+ float n = NUM2FLT(zNearPlane);
1117
+ float f = NUM2FLT(zFarPlane);
1118
+ float l = NUM2FLT(left);
1119
+ float r = NUM2FLT(right);
1120
+ float t = NUM2FLT(top);
1121
+ float b = NUM2FLT(bottom);
1122
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1123
+
1124
+ result->m11 = 2.0f / (r - l);
1125
+ result->m12 = result->m13 = result->m14 = 0.0f;
1126
+
1127
+ result->m22 = 2.0f / (t - b);
1128
+ result->m21 = result->m23 = result->m24 = 0.0f;
1129
+
1130
+ result->m33 = 1.0f / (n - f);
1131
+ result->m31 = result->m32 = result->m34 = 0.0f;
1132
+
1133
+ result->m41 = (l + r) / (l - r);
1134
+ result->m42 = (t + b) / (b - t);
1135
+ result->m43 = n / (n - f);
1136
+ result->m44 = 1.0f;
1137
+
1138
+ return NUMERIX_WRAP(klass, result);
1139
+ }
1140
+
1141
+ VALUE rb_matrix4x4_create_look_at(VALUE klass, VALUE camPos, VALUE camTarget, VALUE camUp) {
1142
+ Vector3 *cam, *target, *up;
1143
+ Data_Get_Struct(camPos, Vector3, cam);
1144
+ Data_Get_Struct(camTarget, Vector3, target);
1145
+ Data_Get_Struct(camUp, Vector3, up);
1146
+
1147
+ Vector3 *axis = xmalloc(sizeof(Vector3) * 3);
1148
+ axis[2].x = cam->x - target->x;
1149
+ axis[2].y = cam->y - target->y;
1150
+ axis[2].z = cam->z - target->z;
1151
+ numerix_vec3_normalize(&axis[2]);
1152
+
1153
+ numerix_cross_norm(up, &axis[2], &axis[0]);
1154
+ numerix_cross_norm(&axis[2], &axis[0], &axis[1]);
1155
+
1156
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1157
+
1158
+ result->m11 = axis[0].x;
1159
+ result->m12 = axis[1].x;
1160
+ result->m13 = axis[2].x;
1161
+ result->m14 = 0.0f;
1162
+ result->m21 = axis[0].y;
1163
+ result->m22 = axis[1].y;
1164
+ result->m23 = axis[2].y;
1165
+ result->m24 = 0.0f;
1166
+ result->m31 = axis[0].z;
1167
+ result->m32 = axis[1].z;
1168
+ result->m33 = axis[2].z;
1169
+ result->m34 = 0.0f;
1170
+ result->m41 = -(numerix_vec3_dot(&axis[0], cam));
1171
+ result->m42 = -(numerix_vec3_dot(&axis[1], cam));
1172
+ result->m43 = -(numerix_vec3_dot(&axis[2], cam));
1173
+ result->m44 = 1.0f;
1174
+
1175
+ xfree(axis);
1176
+
1177
+ return NUMERIX_WRAP(klass, result);
1178
+ }
1179
+
1180
+ VALUE rb_matrix4x4_create_world(VALUE klass, VALUE position, VALUE forward, VALUE up) {
1181
+ Vector3 *pos, *f, *u;
1182
+ Data_Get_Struct(position, Vector3, pos);
1183
+ Data_Get_Struct(forward, Vector3, f);
1184
+ Data_Get_Struct(up, Vector3, u);
1185
+
1186
+ Vector3 *axis = xmalloc(sizeof(Vector3) * 3);
1187
+
1188
+ axis[2].x = -f->x;
1189
+ axis[2].y = -f->y;
1190
+ axis[2].z = -f->z;
1191
+ numerix_vec3_normalize(&axis[2]);
1192
+ numerix_cross_norm(u, &axis[2], &axis[0]);
1193
+ numerix_vec3_cross(&axis[2], &axis[0], &axis[1]);
1194
+
1195
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1196
+
1197
+ result->m11 = axis[0].x;
1198
+ result->m12 = axis[0].y;
1199
+ result->m13 = axis[0].z;
1200
+ result->m14 = 0.0f;
1201
+ result->m21 = axis[1].x;
1202
+ result->m22 = axis[1].y;
1203
+ result->m23 = axis[1].z;
1204
+ result->m24 = 0.0f;
1205
+ result->m31 = axis[2].x;
1206
+ result->m32 = axis[2].y;
1207
+ result->m33 = axis[2].z;
1208
+ result->m34 = 0.0f;
1209
+ result->m41 = pos->x;
1210
+ result->m42 = pos->y;
1211
+ result->m43 = pos->z;
1212
+ result->m44 = 1.0f;
1213
+
1214
+ xfree(axis);
1215
+
1216
+ return NUMERIX_WRAP(klass, result);
1217
+ }
1218
+
1219
+ VALUE rb_matrix4x4_from_quaternion(VALUE klass, VALUE quaternion) {
1220
+ Quaternion *q;
1221
+ Data_Get_Struct(quaternion, Quaternion, q);
1222
+
1223
+ float xx = q->x * q->x;
1224
+ float yy = q->y * q->y;
1225
+ float zz = q->z * q->z;
1226
+
1227
+ float xy = q->x * q->y;
1228
+ float wz = q->z * q->w;
1229
+ float xz = q->z * q->x;
1230
+ float wy = q->y * q->w;
1231
+ float yz = q->y * q->z;
1232
+ float wx = q->x * q->w;
1233
+
1234
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1235
+
1236
+ result->m11 = 1.0f - 2.0f * (yy + zz);
1237
+ result->m12 = 2.0f * (xy + wz);
1238
+ result->m13 = 2.0f * (xz - wy);
1239
+ result->m14 = 0.0f;
1240
+ result->m21 = 2.0f * (xy - wz);
1241
+ result->m22 = 1.0f - 2.0f * (zz + xx);
1242
+ result->m23 = 2.0f * (yz + wx);
1243
+ result->m24 = 0.0f;
1244
+ result->m31 = 2.0f * (xz + wy);
1245
+ result->m32 = 2.0f * (yz - wx);
1246
+ result->m33 = 1.0f - 2.0f * (yy + xx);
1247
+ result->m34 = 0.0f;
1248
+ result->m41 = 0.0f;
1249
+ result->m42 = 0.0f;
1250
+ result->m43 = 0.0f;
1251
+ result->m44 = 1.0f;
1252
+
1253
+ return NUMERIX_WRAP(klass, result);
1254
+ }
1255
+
1256
+ VALUE rb_matrix4x4_from_yaw_pitch_roll(VALUE klass, VALUE yaw, VALUE pitch, VALUE roll) {
1257
+ VALUE q = rb_quaternion_from_yaw_pitch_roll(rb_cQuaternion, yaw, pitch, roll);
1258
+ return rb_matrix4x4_from_quaternion(klass, q);
1259
+ }
1260
+
1261
+ VALUE rb_matrix4x4_create_shadow(VALUE klass, VALUE lightDir, VALUE plane) {
1262
+ Vector3 *light;
1263
+ Data_Get_Struct(lightDir, Vector3, light);
1264
+
1265
+ VALUE normPlane = rb_plane_normalize(plane);
1266
+ Plane *p;
1267
+ Data_Get_Struct(normPlane, Plane, p);
1268
+
1269
+ float dot = p->normal.x * light->x + p->normal.y * light->y + p->normal.z * light->z;
1270
+ float a = -p->normal.x;
1271
+ float b = -p->normal.y;
1272
+ float c = -p->normal.z;
1273
+ float d = -p->distance;
1274
+
1275
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1276
+
1277
+ result->m11 = a * light->x + dot;
1278
+ result->m21 = b * light->x;
1279
+ result->m31 = c * light->x;
1280
+ result->m41 = d * light->x;
1281
+
1282
+ result->m12 = a * light->y;
1283
+ result->m22 = b * light->y + dot;
1284
+ result->m32 = c * light->y;
1285
+ result->m42 = d * light->y;
1286
+
1287
+ result->m13 = a * light->z;
1288
+ result->m23 = b * light->z;
1289
+ result->m33 = c * light->z + dot;
1290
+ result->m43 = d * light->z;
1291
+
1292
+ result->m14 = 0.0f;
1293
+ result->m24 = 0.0f;
1294
+ result->m34 = 0.0f;
1295
+ result->m44 = dot;
1296
+
1297
+ return NUMERIX_WRAP(klass, result);
1298
+ }
1299
+
1300
+ VALUE rb_matrix4x4_create_reflection(VALUE klass, VALUE plane) {
1301
+ VALUE normPlane = rb_plane_normalize(plane);
1302
+ Plane *p;
1303
+ Data_Get_Struct(normPlane, Plane, p);
1304
+
1305
+ float a = p->normal.x;
1306
+ float b = p->normal.y;
1307
+ float c = p->normal.z;
1308
+
1309
+ float fa = -2.0f * a;
1310
+ float fb = -2.0f * b;
1311
+ float fc = -2.0f * c;
1312
+
1313
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1314
+
1315
+ result->m11 = fa * a + 1.0f;
1316
+ result->m12 = fb * a;
1317
+ result->m13 = fc * a;
1318
+ result->m14 = 0.0f;
1319
+
1320
+ result->m21 = fa * b;
1321
+ result->m22 = fb * b + 1.0f;
1322
+ result->m23 = fc * b;
1323
+ result->m24 = 0.0f;
1324
+
1325
+ result->m31 = fa * c;
1326
+ result->m32 = fb * c;
1327
+ result->m33 = fc * c + 1.0f;
1328
+ result->m34 = 0.0f;
1329
+
1330
+ result->m41 = fa * p->distance;
1331
+ result->m42 = fb * p->distance;
1332
+ result->m43 = fc * p->distance;
1333
+ result->m44 = 1.0f;
1334
+
1335
+ return NUMERIX_WRAP(klass, result);
1336
+ }
1337
+
1338
+ VALUE rb_matrix4x4_transform(VALUE self, VALUE quaternion) {
1339
+ Matrix4x4 *m, *result;
1340
+ Quaternion *rotation;
1341
+ Data_Get_Struct(self, Matrix4x4, m);
1342
+ Data_Get_Struct(quaternion, Quaternion, rotation);
1343
+
1344
+ // Compute rotation matrix.
1345
+ float x2 = rotation->x + rotation->x;
1346
+ float y2 = rotation->y + rotation->y;
1347
+ float z2 = rotation->z + rotation->z;
1348
+
1349
+ float wx2 = rotation->w * x2;
1350
+ float wy2 = rotation->w * y2;
1351
+ float wz2 = rotation->w * z2;
1352
+ float xx2 = rotation->x * x2;
1353
+ float xy2 = rotation->x * y2;
1354
+ float xz2 = rotation->x * z2;
1355
+ float yy2 = rotation->y * y2;
1356
+ float yz2 = rotation->y * z2;
1357
+ float zz2 = rotation->z * z2;
1358
+
1359
+ float q11 = 1.0f - yy2 - zz2;
1360
+ float q21 = xy2 - wz2;
1361
+ float q31 = xz2 + wy2;
1362
+
1363
+ float q12 = xy2 + wz2;
1364
+ float q22 = 1.0f - xx2 - zz2;
1365
+ float q32 = yz2 - wx2;
1366
+
1367
+ float q13 = xz2 - wy2;
1368
+ float q23 = yz2 + wx2;
1369
+ float q33 = 1.0f - xx2 - yy2;
1370
+
1371
+ result = ALLOC(Matrix4x4);
1372
+
1373
+ // First row
1374
+ result->m11 = m->m11 * q11 + m->m12 * q21 + m->m13 * q31;
1375
+ result->m12 = m->m11 * q12 + m->m12 * q22 + m->m13 * q32;
1376
+ result->m13 = m->m11 * q13 + m->m12 * q23 + m->m13 * q33;
1377
+ result->m14 = m->m14;
1378
+
1379
+ // Second row
1380
+ result->m21 = m->m21 * q11 + m->m22 * q21 + m->m23 * q31;
1381
+ result->m22 = m->m21 * q12 + m->m22 * q22 + m->m23 * q32;
1382
+ result->m23 = m->m21 * q13 + m->m22 * q23 + m->m23 * q33;
1383
+ result->m24 = m->m24;
1384
+
1385
+ // Third row
1386
+ result->m31 = m->m31 * q11 + m->m32 * q21 + m->m33 * q31;
1387
+ result->m32 = m->m31 * q12 + m->m32 * q22 + m->m33 * q32;
1388
+ result->m33 = m->m31 * q13 + m->m32 * q23 + m->m33 * q33;
1389
+ result->m34 = m->m34;
1390
+
1391
+ // Fourth row
1392
+ result->m41 = m->m41 * q11 + m->m42 * q21 + m->m43 * q31;
1393
+ result->m42 = m->m41 * q12 + m->m42 * q22 + m->m43 * q32;
1394
+ result->m43 = m->m41 * q13 + m->m42 * q23 + m->m43 * q33;
1395
+ result->m44 = m->m44;
1396
+
1397
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1398
+ }
1399
+
1400
+ VALUE rb_matrix4x4_transform_bang(VALUE self, VALUE quaternion) {
1401
+ MATRIX4X4();
1402
+ Quaternion *rotation;
1403
+ Data_Get_Struct(quaternion, Quaternion, rotation);
1404
+
1405
+ // Compute rotation matrix.
1406
+ float x2 = rotation->x + rotation->x;
1407
+ float y2 = rotation->y + rotation->y;
1408
+ float z2 = rotation->z + rotation->z;
1409
+
1410
+ float wx2 = rotation->w * x2;
1411
+ float wy2 = rotation->w * y2;
1412
+ float wz2 = rotation->w * z2;
1413
+ float xx2 = rotation->x * x2;
1414
+ float xy2 = rotation->x * y2;
1415
+ float xz2 = rotation->x * z2;
1416
+ float yy2 = rotation->y * y2;
1417
+ float yz2 = rotation->y * z2;
1418
+ float zz2 = rotation->z * z2;
1419
+
1420
+ float q11 = 1.0f - yy2 - zz2;
1421
+ float q21 = xy2 - wz2;
1422
+ float q31 = xz2 + wy2;
1423
+
1424
+ float q12 = xy2 + wz2;
1425
+ float q22 = 1.0f - xx2 - zz2;
1426
+ float q32 = yz2 - wx2;
1427
+
1428
+ float q13 = xz2 - wy2;
1429
+ float q23 = yz2 + wx2;
1430
+ float q33 = 1.0f - xx2 - yy2;
1431
+
1432
+ // First row
1433
+ m->m11 = m->m11 * q11 + m->m12 * q21 + m->m13 * q31;
1434
+ m->m12 = m->m11 * q12 + m->m12 * q22 + m->m13 * q32;
1435
+ m->m13 = m->m11 * q13 + m->m12 * q23 + m->m13 * q33;
1436
+ m->m14 = m->m14;
1437
+
1438
+ // Second row
1439
+ m->m21 = m->m21 * q11 + m->m22 * q21 + m->m23 * q31;
1440
+ m->m22 = m->m21 * q12 + m->m22 * q22 + m->m23 * q32;
1441
+ m->m23 = m->m21 * q13 + m->m22 * q23 + m->m23 * q33;
1442
+ m->m24 = m->m24;
1443
+
1444
+ // Third row
1445
+ m->m31 = m->m31 * q11 + m->m32 * q21 + m->m33 * q31;
1446
+ m->m32 = m->m31 * q12 + m->m32 * q22 + m->m33 * q32;
1447
+ m->m33 = m->m31 * q13 + m->m32 * q23 + m->m33 * q33;
1448
+ m->m34 = m->m34;
1449
+
1450
+ // Fourth row
1451
+ m->m41 = m->m41 * q11 + m->m42 * q21 + m->m43 * q31;
1452
+ m->m42 = m->m41 * q12 + m->m42 * q22 + m->m43 * q32;
1453
+ m->m43 = m->m41 * q13 + m->m42 * q23 + m->m43 * q33;
1454
+ m->m44 = m->m44;
1455
+
1456
+ return self;
1457
+ }
1458
+
1459
+ VALUE rb_matrix4x4_transpose(VALUE self) {
1460
+ MATRIX4X4();
1461
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1462
+
1463
+ result->m11 = m->m11;
1464
+ result->m12 = m->m21;
1465
+ result->m13 = m->m31;
1466
+ result->m14 = m->m41;
1467
+ result->m21 = m->m12;
1468
+ result->m22 = m->m22;
1469
+ result->m23 = m->m32;
1470
+ result->m24 = m->m42;
1471
+ result->m31 = m->m13;
1472
+ result->m32 = m->m23;
1473
+ result->m33 = m->m33;
1474
+ result->m34 = m->m43;
1475
+ result->m41 = m->m14;
1476
+ result->m42 = m->m24;
1477
+ result->m43 = m->m34;
1478
+ result->m44 = m->m44;
1479
+
1480
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1481
+ }
1482
+
1483
+ VALUE rb_matrix4x4_lerp_s(VALUE klass, VALUE matrix1, VALUE matrix2, VALUE amount) {
1484
+ Matrix4x4 *m1, *m2, *result;
1485
+ Data_Get_Struct(matrix1, Matrix4x4, m1);
1486
+ Data_Get_Struct(matrix2, Matrix4x4, m2);
1487
+ result = ALLOC(Matrix4x4);
1488
+ float weight = NUMERIX_CLAMP(NUM2FLT(amount), 0.0f, 1.0f);
1489
+
1490
+ // First row
1491
+ result->m11 = m1->m11 + (m2->m11 - m1->m11) * weight;
1492
+ result->m12 = m1->m12 + (m2->m12 - m1->m12) * weight;
1493
+ result->m13 = m1->m13 + (m2->m13 - m1->m13) * weight;
1494
+ result->m14 = m1->m14 + (m2->m14 - m1->m14) * weight;
1495
+
1496
+ // Second row
1497
+ result->m21 = m1->m21 + (m2->m21 - m1->m21) * weight;
1498
+ result->m22 = m1->m22 + (m2->m22 - m1->m22) * weight;
1499
+ result->m23 = m1->m23 + (m2->m23 - m1->m23) * weight;
1500
+ result->m24 = m1->m24 + (m2->m24 - m1->m24) * weight;
1501
+
1502
+ // Third row
1503
+ result->m31 = m1->m31 + (m2->m31 - m1->m31) * weight;
1504
+ result->m32 = m1->m32 + (m2->m32 - m1->m32) * weight;
1505
+ result->m33 = m1->m33 + (m2->m33 - m1->m33) * weight;
1506
+ result->m34 = m1->m34 + (m2->m34 - m1->m34) * weight;
1507
+
1508
+ // Fourth row
1509
+ result->m41 = m1->m41 + (m2->m41 - m1->m41) * weight;
1510
+ result->m42 = m1->m42 + (m2->m42 - m1->m42) * weight;
1511
+ result->m43 = m1->m43 + (m2->m43 - m1->m43) * weight;
1512
+ result->m44 = m1->m44 + (m2->m44 - m1->m44) * weight;
1513
+
1514
+ return NUMERIX_WRAP(klass, result);
1515
+ }
1516
+
1517
+ VALUE rb_matrix4x4_negate(VALUE self) {
1518
+ MATRIX4X4();
1519
+ Matrix4x4 *result = ALLOC(Matrix4x4);
1520
+
1521
+ result->m11 = -m->m11;
1522
+ result->m12 = -m->m12;
1523
+ result->m13 = -m->m13;
1524
+ result->m14 = -m->m14;
1525
+ result->m21 = -m->m21;
1526
+ result->m22 = -m->m22;
1527
+ result->m23 = -m->m23;
1528
+ result->m24 = -m->m24;
1529
+ result->m31 = -m->m31;
1530
+ result->m32 = -m->m32;
1531
+ result->m33 = -m->m33;
1532
+ result->m34 = -m->m34;
1533
+ result->m41 = -m->m41;
1534
+ result->m42 = -m->m42;
1535
+ result->m43 = -m->m43;
1536
+ result->m44 = -m->m44;
1537
+
1538
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1539
+ }
1540
+
1541
+ VALUE rb_matrix4x4_add(VALUE self, VALUE other) {
1542
+ Matrix4x4 *m1, *m2, *result;
1543
+ Data_Get_Struct(self, Matrix4x4, m1);
1544
+ Data_Get_Struct(other, Matrix4x4, m2);
1545
+ result = ALLOC(Matrix4x4);
1546
+
1547
+ result->m11 = m1->m11 + m2->m11;
1548
+ result->m12 = m1->m12 + m2->m12;
1549
+ result->m13 = m1->m13 + m2->m13;
1550
+ result->m14 = m1->m14 + m2->m14;
1551
+ result->m21 = m1->m21 + m2->m21;
1552
+ result->m22 = m1->m22 + m2->m22;
1553
+ result->m23 = m1->m23 + m2->m23;
1554
+ result->m24 = m1->m24 + m2->m24;
1555
+ result->m31 = m1->m31 + m2->m31;
1556
+ result->m32 = m1->m32 + m2->m32;
1557
+ result->m33 = m1->m33 + m2->m33;
1558
+ result->m34 = m1->m34 + m2->m34;
1559
+ result->m41 = m1->m41 + m2->m41;
1560
+ result->m42 = m1->m42 + m2->m42;
1561
+ result->m43 = m1->m43 + m2->m43;
1562
+ result->m44 = m1->m44 + m2->m44;
1563
+
1564
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1565
+ }
1566
+
1567
+ VALUE rb_matrix4x4_subtract(VALUE self, VALUE other) {
1568
+ Matrix4x4 *m1, *m2, *result;
1569
+ Data_Get_Struct(self, Matrix4x4, m1);
1570
+ Data_Get_Struct(other, Matrix4x4, m2);
1571
+ result = ALLOC(Matrix4x4);
1572
+
1573
+ result->m11 = m1->m11 - m2->m11;
1574
+ result->m12 = m1->m12 - m2->m12;
1575
+ result->m13 = m1->m13 - m2->m13;
1576
+ result->m14 = m1->m14 - m2->m14;
1577
+ result->m21 = m1->m21 - m2->m21;
1578
+ result->m22 = m1->m22 - m2->m22;
1579
+ result->m23 = m1->m23 - m2->m23;
1580
+ result->m24 = m1->m24 - m2->m24;
1581
+ result->m31 = m1->m31 - m2->m31;
1582
+ result->m32 = m1->m32 - m2->m32;
1583
+ result->m33 = m1->m33 - m2->m33;
1584
+ result->m34 = m1->m34 - m2->m34;
1585
+ result->m41 = m1->m41 - m2->m41;
1586
+ result->m42 = m1->m42 - m2->m42;
1587
+ result->m43 = m1->m43 - m2->m43;
1588
+ result->m44 = m1->m44 - m2->m44;
1589
+
1590
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1591
+ }
1592
+
1593
+ VALUE rb_matrix4x4_multiply(VALUE self, VALUE other) {
1594
+ Matrix4x4 *m1, *result;
1595
+ Data_Get_Struct(self, Matrix4x4, m1);
1596
+ result = ALLOC(Matrix4x4);
1597
+
1598
+ if (NUMERIX_TYPE_P(other, rb_cMatrix4x4)) {
1599
+ Matrix4x4 *m2;
1600
+ Data_Get_Struct(other, Matrix4x4, m2);
1601
+
1602
+ // First row
1603
+ result->m11 = m1->m11 * m2->m11 + m1->m12 * m2->m21 + m1->m13 * m2->m31 + m1->m14 * m2->m41;
1604
+ result->m12 = m1->m11 * m2->m12 + m1->m12 * m2->m22 + m1->m13 * m2->m32 + m1->m14 * m2->m42;
1605
+ result->m13 = m1->m11 * m2->m13 + m1->m12 * m2->m23 + m1->m13 * m2->m33 + m1->m14 * m2->m43;
1606
+ result->m14 = m1->m11 * m2->m14 + m1->m12 * m2->m24 + m1->m13 * m2->m34 + m1->m14 * m2->m44;
1607
+
1608
+ // Second row
1609
+ result->m21 = m1->m21 * m2->m11 + m1->m22 * m2->m21 + m1->m23 * m2->m31 + m1->m24 * m2->m41;
1610
+ result->m22 = m1->m21 * m2->m12 + m1->m22 * m2->m22 + m1->m23 * m2->m32 + m1->m24 * m2->m42;
1611
+ result->m23 = m1->m21 * m2->m13 + m1->m22 * m2->m23 + m1->m23 * m2->m33 + m1->m24 * m2->m43;
1612
+ result->m24 = m1->m21 * m2->m14 + m1->m22 * m2->m24 + m1->m23 * m2->m34 + m1->m24 * m2->m44;
1613
+
1614
+ // Third row
1615
+ result->m31 = m1->m31 * m2->m11 + m1->m32 * m2->m21 + m1->m33 * m2->m31 + m1->m34 * m2->m41;
1616
+ result->m32 = m1->m31 * m2->m12 + m1->m32 * m2->m22 + m1->m33 * m2->m32 + m1->m34 * m2->m42;
1617
+ result->m33 = m1->m31 * m2->m13 + m1->m32 * m2->m23 + m1->m33 * m2->m33 + m1->m34 * m2->m43;
1618
+ result->m34 = m1->m31 * m2->m14 + m1->m32 * m2->m24 + m1->m33 * m2->m34 + m1->m34 * m2->m44;
1619
+
1620
+ // Fourth row
1621
+ result->m41 = m1->m41 * m2->m11 + m1->m42 * m2->m21 + m1->m43 * m2->m31 + m1->m44 * m2->m41;
1622
+ result->m42 = m1->m41 * m2->m12 + m1->m42 * m2->m22 + m1->m43 * m2->m32 + m1->m44 * m2->m42;
1623
+ result->m43 = m1->m41 * m2->m13 + m1->m42 * m2->m23 + m1->m43 * m2->m33 + m1->m44 * m2->m43;
1624
+ result->m44 = m1->m41 * m2->m14 + m1->m42 * m2->m24 + m1->m43 * m2->m34 + m1->m44 * m2->m44;
1625
+ } else {
1626
+ float scalar = NUM2FLT(other);
1627
+ result->m11 = m1->m11 * scalar;
1628
+ result->m12 = m1->m12 * scalar;
1629
+ result->m13 = m1->m13 * scalar;
1630
+ result->m14 = m1->m14 * scalar;
1631
+ result->m21 = m1->m21 * scalar;
1632
+ result->m22 = m1->m22 * scalar;
1633
+ result->m23 = m1->m23 * scalar;
1634
+ result->m24 = m1->m24 * scalar;
1635
+ result->m31 = m1->m31 * scalar;
1636
+ result->m32 = m1->m32 * scalar;
1637
+ result->m33 = m1->m33 * scalar;
1638
+ result->m34 = m1->m34 * scalar;
1639
+ result->m41 = m1->m41 * scalar;
1640
+ result->m42 = m1->m42 * scalar;
1641
+ result->m43 = m1->m43 * scalar;
1642
+ result->m44 = m1->m44 * scalar;
1643
+ }
1644
+
1645
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1646
+ }
1647
+
1648
+ VALUE rb_matrix4x4_invert(VALUE self) {
1649
+ Matrix4x4 *mat, *result;
1650
+ Data_Get_Struct(self, Matrix4x4, mat);
1651
+ result = ALLOC(Matrix4x4);
1652
+ // -1
1653
+ // If you have matrix M, inverse Matrix M can compute
1654
+ //
1655
+ // -1 1
1656
+ // M = --------- A
1657
+ // det(M)
1658
+ //
1659
+ // A is adjugate (adjoint) of M, where,
1660
+ //
1661
+ // T
1662
+ // A = C
1663
+ //
1664
+ // C is Cofactor matrix of M, where,
1665
+ // i + j
1666
+ // C = (-1) * det(M )
1667
+ // ij ij
1668
+ //
1669
+ // [ a b c d ]
1670
+ // M = [ e f g h ]
1671
+ // [ i j k l ]
1672
+ // [ m n o p ]
1673
+ //
1674
+ // First Row
1675
+ // 2 | f g h |
1676
+ // C = (-1) | j k l | = + ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
1677
+ // 11 | n o p |
1678
+ //
1679
+ // 3 | e g h |
1680
+ // C = (-1) | i k l | = - ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
1681
+ // 12 | m o p |
1682
+ //
1683
+ // 4 | e f h |
1684
+ // C = (-1) | i j l | = + ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
1685
+ // 13 | m n p |
1686
+ //
1687
+ // 5 | e f g |
1688
+ // C = (-1) | i j k | = - ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
1689
+ // 14 | m n o |
1690
+ //
1691
+ // Second Row
1692
+ // 3 | b c d |
1693
+ // C = (-1) | j k l | = - ( b ( kp - lo ) - c ( jp - ln ) + d ( jo - kn ) )
1694
+ // 21 | n o p |
1695
+ //
1696
+ // 4 | a c d |
1697
+ // C = (-1) | i k l | = + ( a ( kp - lo ) - c ( ip - lm ) + d ( io - km ) )
1698
+ // 22 | m o p |
1699
+ //
1700
+ // 5 | a b d |
1701
+ // C = (-1) | i j l | = - ( a ( jp - ln ) - b ( ip - lm ) + d ( in - jm ) )
1702
+ // 23 | m n p |
1703
+ //
1704
+ // 6 | a b c |
1705
+ // C = (-1) | i j k | = + ( a ( jo - kn ) - b ( io - km ) + c ( in - jm ) )
1706
+ // 24 | m n o |
1707
+ //
1708
+ // Third Row
1709
+ // 4 | b c d |
1710
+ // C = (-1) | f g h | = + ( b ( gp - ho ) - c ( fp - hn ) + d ( fo - gn ) )
1711
+ // 31 | n o p |
1712
+ //
1713
+ // 5 | a c d |
1714
+ // C = (-1) | e g h | = - ( a ( gp - ho ) - c ( ep - hm ) + d ( eo - gm ) )
1715
+ // 32 | m o p |
1716
+ //
1717
+ // 6 | a b d |
1718
+ // C = (-1) | e f h | = + ( a ( fp - hn ) - b ( ep - hm ) + d ( en - fm ) )
1719
+ // 33 | m n p |
1720
+ //
1721
+ // 7 | a b c |
1722
+ // C = (-1) | e f g | = - ( a ( fo - gn ) - b ( eo - gm ) + c ( en - fm ) )
1723
+ // 34 | m n o |
1724
+ //
1725
+ // Fourth Row
1726
+ // 5 | b c d |
1727
+ // C = (-1) | f g h | = - ( b ( gl - hk ) - c ( fl - hj ) + d ( fk - gj ) )
1728
+ // 41 | j k l |
1729
+ //
1730
+ // 6 | a c d |
1731
+ // C = (-1) | e g h | = + ( a ( gl - hk ) - c ( el - hi ) + d ( ek - gi ) )
1732
+ // 42 | i k l |
1733
+ //
1734
+ // 7 | a b d |
1735
+ // C = (-1) | e f h | = - ( a ( fl - hj ) - b ( el - hi ) + d ( ej - fi ) )
1736
+ // 43 | i j l |
1737
+ //
1738
+ // 8 | a b c |
1739
+ // C = (-1) | e f g | = + ( a ( fk - gj ) - b ( ek - gi ) + c ( ej - fi ) )
1740
+ // 44 | i j k |
1741
+ //
1742
+ // Cost of operation
1743
+ // 53 adds, 104 muls, and 1 div.
1744
+ float a = mat->m11, b = mat->m12, c = mat->m13, d = mat->m14;
1745
+ float e = mat->m21, f = mat->m22, g = mat->m23, h = mat->m24;
1746
+ float i = mat->m31, j = mat->m32, k = mat->m33, l = mat->m34;
1747
+ float m = mat->m41, n = mat->m42, o = mat->m43, p = mat->m44;
1748
+
1749
+ float kp_lo = k * p - l * o;
1750
+ float jp_ln = j * p - l * n;
1751
+ float jo_kn = j * o - k * n;
1752
+ float ip_lm = i * p - l * m;
1753
+ float io_km = i * o - k * m;
1754
+ float in_jm = i * n - j * m;
1755
+
1756
+ float a11 = +(f * kp_lo - g * jp_ln + h * jo_kn);
1757
+ float a12 = -(e * kp_lo - g * ip_lm + h * io_km);
1758
+ float a13 = +(e * jp_ln - f * ip_lm + h * in_jm);
1759
+ float a14 = -(e * jo_kn - f * io_km + g * in_jm);
1760
+
1761
+ float det = a * a11 + b * a12 + c * a13 + d * a14;
1762
+
1763
+ if (fabsf(det) < FLT_EPSILON) {
1764
+ // invalid
1765
+ float *matp = (float *)mat;
1766
+ for (int i = 0; i < 16; i++)
1767
+ matp[i] = NAN;
1768
+ } else {
1769
+ float invDet = 1.0f / det;
1770
+
1771
+ result->m11 = a11 * invDet;
1772
+ result->m21 = a12 * invDet;
1773
+ result->m31 = a13 * invDet;
1774
+ result->m41 = a14 * invDet;
1775
+
1776
+ result->m12 = -(b * kp_lo - c * jp_ln + d * jo_kn) * invDet;
1777
+ result->m22 = +(a * kp_lo - c * ip_lm + d * io_km) * invDet;
1778
+ result->m32 = -(a * jp_ln - b * ip_lm + d * in_jm) * invDet;
1779
+ result->m42 = +(a * jo_kn - b * io_km + c * in_jm) * invDet;
1780
+
1781
+ float gp_ho = g * p - h * o;
1782
+ float fp_hn = f * p - h * n;
1783
+ float fo_gn = f * o - g * n;
1784
+ float ep_hm = e * p - h * m;
1785
+ float eo_gm = e * o - g * m;
1786
+ float en_fm = e * n - f * m;
1787
+
1788
+ result->m13 = +(b * gp_ho - c * fp_hn + d * fo_gn) * invDet;
1789
+ result->m23 = -(a * gp_ho - c * ep_hm + d * eo_gm) * invDet;
1790
+ result->m33 = +(a * fp_hn - b * ep_hm + d * en_fm) * invDet;
1791
+ result->m43 = -(a * fo_gn - b * eo_gm + c * en_fm) * invDet;
1792
+
1793
+ float gl_hk = g * l - h * k;
1794
+ float fl_hj = f * l - h * j;
1795
+ float fk_gj = f * k - g * j;
1796
+ float el_hi = e * l - h * i;
1797
+ float ek_gi = e * k - g * i;
1798
+ float ej_fi = e * j - f * i;
1799
+
1800
+ result->m14 = -(b * gl_hk - c * fl_hj + d * fk_gj) * invDet;
1801
+ result->m24 = +(a * gl_hk - c * el_hi + d * ek_gi) * invDet;
1802
+ result->m34 = -(a * fl_hj - b * el_hi + d * ej_fi) * invDet;
1803
+ result->m44 = +(a * fk_gj - b * ek_gi + c * ej_fi) * invDet;
1804
+ }
1805
+
1806
+ return NUMERIX_WRAP(CLASS_OF(self), result);
1807
+ }