cglm 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,271 @@
1
+ #include "rb_cglm.h"
2
+
3
+ /* call-seq: transform(mat4[, dest]) => dest | new AABB
4
+ *
5
+ * Applies transform to an axis-aligned bounding box, placing the result in
6
+ * `dest` and allocating it if necessary.
7
+ */
8
+ VALUE rb_cglm_aabb_transform(int argc, VALUE *argv, VALUE self) {
9
+ VALUE matr, dest;
10
+ rb_scan_args(argc, argv, "11", &matr, &dest);
11
+ if (NIL_P(dest)) dest = AABB_NEW(ALLOC_AABB);
12
+ mat4 *m;
13
+ aabb *a, *b;
14
+ m = &VAL2MAT4(matr);
15
+ a = &VAL2AABB(self);
16
+ b = &VAL2AABB(dest);
17
+ glm_aabb_transform(a->corners, *m, a->corners);
18
+ return dest;
19
+ }
20
+
21
+ /* call-seq: merge(other[, dest]) => dest | new AABB
22
+ *
23
+ * Merges two axis-aligned bounding boxes, storing the result in `dest`. If
24
+ * `dest` is omitted, a new AABB is created. Returns `dest`.
25
+ *
26
+ * Both boxes must be in the same space.
27
+ */
28
+ VALUE rb_cglm_aabb_merge(int argc, VALUE *argv, VALUE self) {
29
+ VALUE other, dest;
30
+ rb_scan_args(argc, argv, "11", &other, &dest);
31
+ if (NIL_P(dest)) dest = AABB_NEW(ALLOC_AABB);
32
+ aabb *a, *b, *out;
33
+ a = &VAL2AABB(self);
34
+ b = &VAL2AABB(other);
35
+ out = &VAL2AABB(dest);
36
+ glm_aabb_merge(a->corners, b->corners, out->corners);
37
+ return dest;
38
+ }
39
+
40
+ /* call-seq: crop(crop[, dest]) => dest | new AABB
41
+ *
42
+ * Crops this AABB by `crop`, placing the result in `dest` or creating a new
43
+ * one. Returns `dest`.
44
+ *
45
+ * This could be useful for gettng an AABB which fits with view frustum and
46
+ * object bounding boxes. In this case you crop view frustum box with object's
47
+ * box.
48
+ */
49
+ VALUE rb_cglm_aabb_crop(int argc, VALUE *argv, VALUE self) {
50
+ VALUE other, dest;
51
+ rb_scan_args(argc, argv, "11", &other, &dest);
52
+ if (NIL_P(dest)) dest = AABB_NEW(ALLOC_AABB);
53
+ aabb *a, *b, *out;
54
+ a = &VAL2AABB(self);
55
+ b = &VAL2AABB(other);
56
+ out = &VAL2AABB(dest);
57
+ glm_aabb_crop(a->corners, b->corners, out->corners);
58
+ return dest;
59
+ }
60
+
61
+ /* call-seq: crop_until(crop, clamp[, dest]) => dest | new AABB
62
+ *
63
+ * Crops this AABB by `crop`, placing the result in `dest` or creating a new
64
+ * one. Returns `dest`. If the result would be smaller than `clamp`, it is
65
+ * restricted to the extents of `clamp` instead.
66
+ *
67
+ * This could be useful for gettng a bbox which fits with view frustum and
68
+ * object bounding boxes. In this case you crop view frustum box with objects
69
+ * box.
70
+ */
71
+ VALUE rb_cglm_aabb_crop_until(int argc, VALUE *argv, VALUE self) {
72
+ VALUE other, clamp, dest;
73
+ rb_scan_args(argc, argv, "21", &other, &clamp, &dest);
74
+ if (NIL_P(dest)) dest = AABB_NEW(ALLOC_AABB);
75
+ aabb *a, *b, *c, *out;
76
+ a = &VAL2AABB(self);
77
+ b = &VAL2AABB(other);
78
+ c = &VAL2AABB(clamp);
79
+ out = &VAL2AABB(dest);
80
+ glm_aabb_crop_until(a->corners, b->corners, c->corners, out->corners);
81
+ return dest;
82
+ }
83
+
84
+ /* call-seq: intersects_frustum?(frustum) => true|false
85
+ *
86
+ * Returns true if this AABB intersects the given Frustum, false otherwise.
87
+ */
88
+ VALUE rb_cglm_aabb_intersect_frustum(VALUE self, VALUE frstm) {
89
+ aabb *a;
90
+ frustum *f;
91
+ a = &VAL2AABB(self);
92
+ f = &VAL2FRUSTUM(frstm);
93
+ return glm_aabb_frustum(a->corners, f->planes) ? Qtrue : Qfalse;
94
+ }
95
+
96
+ /* call-seq: invalidate => self
97
+ *
98
+ * Invalidates the AABB min and max values.
99
+ */
100
+ VALUE rb_cglm_aabb_invalidate_self(VALUE self) {
101
+ aabb *a;
102
+ a = &VAL2AABB(self);
103
+ glm_aabb_invalidate(a->corners);
104
+ return self;
105
+ }
106
+
107
+ /* call-seq: valid? => true|false
108
+ *
109
+ * Returns true if this AABB is valid, false otherwise.
110
+ */
111
+ VALUE rb_cglm_aabb_is_valid(VALUE self) {
112
+ aabb *a;
113
+ a = &VAL2AABB(self);
114
+ glm_aabb_isvalid(a->corners);
115
+ return self;
116
+ }
117
+
118
+ /* call-seq: size => number
119
+ *
120
+ * Returns the length of the diagonal line between the corners of this AABB.
121
+ */
122
+ VALUE rb_cglm_aabb_size(VALUE self) {
123
+ aabb *a;
124
+ a = &VAL2AABB(self);
125
+ return DBL2NUM(glm_aabb_size(a->corners));
126
+ }
127
+
128
+ /* call-seq: radius => number
129
+ *
130
+ * Returns the radius of a sphere which surrounds this AABB.
131
+ */
132
+ VALUE rb_cglm_aabb_radius(VALUE self) {
133
+ aabb *a;
134
+ a = &VAL2AABB(self);
135
+ return DBL2NUM(glm_aabb_radius(a->corners));
136
+ }
137
+
138
+ /* call-seq: center([dest]) => dest | new Vec3
139
+ *
140
+ * Computes the center point of the AABB and places it into the Vec3 `dest`,
141
+ * creating a new one if `dest` is omitted.
142
+ */
143
+ VALUE rb_cglm_aabb_center(int argc, VALUE *argv, VALUE self) {
144
+ VALUE dest;
145
+ rb_scan_args(argc, argv, "01", &dest);
146
+ if (NIL_P(dest)) dest = VEC3_NEW(ALLOC_VEC3);
147
+ aabb *a;
148
+ vec3 *b;
149
+ a = &VAL2AABB(self);
150
+ b = &VAL2VEC3(dest);
151
+ glm_aabb_center(a->corners, *b);
152
+ return dest;
153
+ }
154
+
155
+ /* call-seq: intersects_aabb?(aabb) => true|false
156
+ *
157
+ * Returns true if the two AABB's overlap, false otherwise.
158
+ */
159
+ VALUE rb_cglm_aabb_intersect_aabb(VALUE self, VALUE other) {
160
+ aabb *a, *b;
161
+ a = &VAL2AABB(self);
162
+ b = &VAL2AABB(other);
163
+ return glm_aabb_aabb(a->corners, b->corners) ? Qtrue : Qfalse;
164
+ }
165
+
166
+ /* call-seq: intersects_sphere?(vec4) => true|false
167
+ *
168
+ * Returns true if the sphere described by the given Vec4 intersects this
169
+ * AABB, false otherwise.
170
+ */
171
+ VALUE rb_cglm_aabb_intersect_sphere(VALUE self, VALUE sphere) {
172
+ aabb *a;
173
+ vec4 *b;
174
+ a = &VAL2AABB(self);
175
+ b = &VAL2VEC4(sphere);
176
+ return glm_aabb_sphere(a->corners, *b) ? Qtrue : Qfalse;
177
+ }
178
+
179
+ /* call-seq: contains_point?(vec3) => true|false
180
+ *
181
+ * Returns true if this AABB contains the specified point (inclusive), false
182
+ * otherwise.
183
+ */
184
+ VALUE rb_cglm_aabb_contains_point(VALUE self, VALUE point) {
185
+ aabb *a;
186
+ vec3 *b;
187
+ a = &VAL2AABB(self);
188
+ b = &VAL2VEC3(point);
189
+ return glm_aabb_point(a->corners, *b) ? Qtrue : Qfalse;
190
+ }
191
+
192
+ /* call-seq: contains_aabb?(aabb) => true|false
193
+ *
194
+ * Returns true if this AABB contains the specified AABB, false otherwise.
195
+ */
196
+ VALUE rb_cglm_aabb_contains_aabb(VALUE self, VALUE other) {
197
+ aabb *a, *b;
198
+ a = &VAL2AABB(self);
199
+ b = &VAL2AABB(other);
200
+ return glm_aabb_contains(a->corners, b->corners) ? Qtrue : Qfalse;
201
+ }
202
+
203
+ VALUE rb_cglm_aabb_aref(VALUE self, VALUE corner_index) {
204
+ CHECK_RANGE(corner_index, 0, 1);
205
+ return VEC3_NEW(VAL2AABB(self).corners[NUM2INT(corner_index)]);
206
+ }
207
+
208
+ VALUE rb_cglm_aabb_aset(VALUE self, VALUE index, VALUE val) {
209
+ CHECK_RANGE(index, 0, 1);
210
+ memcpy(VAL2AABB(self).corners[NUM2INT(index)], VAL2VEC3(val), sizeof(vec3));
211
+ return self;
212
+ }
213
+
214
+ VALUE rb_cglm_aabb_size_bytes(VALUE klass) { return SIZET2NUM(aabb_size()); }
215
+
216
+ VALUE rb_cglm_aabb_alignment_bytes(VALUE klass) { return SIZET2NUM(AABB_ALIGNMENT); }
217
+
218
+ VALUE rb_cglm_aabb_equals_aabb(VALUE self, VALUE other) {
219
+ if (memcmp(&VAL2AABB(self), &VAL2AABB(other), sizeof(aabb))) return Qfalse;
220
+ return Qtrue;
221
+ }
222
+
223
+ /* call-seq: a =~ b => true|false
224
+ *
225
+ * Returns true if each member of `a` is very close to, but not necessarily
226
+ * exactly equal to, each corresponding member of `b`. This is useful in many
227
+ * circumstances because imprecision introduced by floating point calculations
228
+ * can lead to two expressions which are otherwise mathematically equivalent
229
+ * returning false.
230
+ */
231
+ VALUE rb_cglm_aabb_equalish_aabb(int argc, VALUE *argv, VALUE self) {
232
+ VALUE other, epsilon;
233
+ float feps = FLT_EPSILON;
234
+ rb_scan_args(argc, argv, "11", &other, &epsilon);
235
+ if (!NIL_P(epsilon)) feps = NUM2FLT(epsilon);
236
+ aabb *a = &VAL2AABB(self);
237
+ aabb *b = &VAL2AABB(other);
238
+ for (int i = 0; i < 2; i++) {
239
+ for (int j = 0; j < 3; j++) {
240
+ if (fabsf((*a).corners[i][j] - (*b).corners[i][j]) > feps)
241
+ return Qfalse;
242
+ }
243
+ }
244
+ return Qtrue;
245
+ }
246
+
247
+ void Init_cglm_box() {
248
+ rb_define_method(rb_cAABB, "==", rb_cglm_aabb_equals_aabb, 1);
249
+ rb_define_method(rb_cAABB, "equalish", rb_cglm_aabb_equalish_aabb, -1);
250
+ rb_define_method(rb_cAABB, "transform", rb_cglm_aabb_transform, -1);
251
+ rb_define_method(rb_cAABB, "merge", rb_cglm_aabb_merge, -1);
252
+ rb_define_method(rb_cAABB, "crop", rb_cglm_aabb_crop, -1);
253
+ rb_define_method(rb_cAABB, "crop_until", rb_cglm_aabb_crop_until, -1);
254
+ rb_define_method(rb_cAABB, "intersects_frustum?", rb_cglm_aabb_intersect_frustum, 1);
255
+ rb_define_method(rb_cAABB, "invalidate!", rb_cglm_aabb_invalidate_self, 0);
256
+ rb_define_method(rb_cAABB, "valid?", rb_cglm_aabb_is_valid, 0);
257
+ rb_define_method(rb_cAABB, "size", rb_cglm_aabb_size, 0);
258
+ rb_define_method(rb_cAABB, "radius", rb_cglm_aabb_radius, 0);
259
+ rb_define_method(rb_cAABB, "center", rb_cglm_aabb_center, -1);
260
+ rb_define_method(rb_cAABB, "intersects_aabb?", rb_cglm_aabb_intersect_aabb, 1);
261
+ rb_define_method(rb_cAABB, "intersects_sphere?", rb_cglm_aabb_intersect_sphere, 1);
262
+ rb_define_method(rb_cAABB, "contains_point?", rb_cglm_aabb_contains_point, 1);
263
+ rb_define_method(rb_cAABB, "contains_aabb?", rb_cglm_aabb_contains_aabb, 1);
264
+ rb_define_method(rb_cAABB, "[]", rb_cglm_aabb_aref, 1);
265
+ rb_define_method(rb_cAABB, "[]=", rb_cglm_aabb_aset, 2);
266
+
267
+ rb_define_alias(rb_cAABB, "=~", "equalish");
268
+
269
+ rb_define_singleton_method(rb_cAABB, "size", rb_cglm_aabb_size_bytes, 0);
270
+ rb_define_singleton_method(rb_cAABB, "alignment", rb_cglm_aabb_alignment_bytes, 0);
271
+ }
@@ -0,0 +1,325 @@
1
+ #include "rb_cglm.h"
2
+
3
+ /*
4
+ * call-seq: affine_mul_rot(m[, dest]) => dest | new Mat4
5
+ *
6
+ * This is similar to #mul but specialized to affine transform.
7
+ *
8
+ * Right Matrix format should be:
9
+ *
10
+ * R R R 0
11
+ * R R R 0
12
+ * R R R 0
13
+ * 0 0 0 1
14
+ *
15
+ * This reduces some multiplications. It should be faster than #mul.
16
+ * if you are not sure about matrix format then DON'T use this! use #mul.
17
+ *
18
+ * * `m` is the second affine Mat4 to multiply `self` against.
19
+ * * `dest` is the out Mat4 to place the result in, and will be allocated if
20
+ * omitted.
21
+ *
22
+ */
23
+ VALUE rb_cglm_affine_mul_rot(int argc, VALUE *argv, VALUE self) {
24
+ VALUE m_v, dest_v;
25
+ rb_scan_args(argc, argv, "11", &m_v, &dest_v);
26
+ if (NIL_P(dest_v)) dest_v = MAT4_NEW(ALLOC_MAT4);
27
+
28
+ mat4 *m1 = NULL, *m2 = NULL, *dest = NULL;
29
+ m1 = &VAL2MAT4(self);
30
+ m2 = &VAL2MAT4(m_v);
31
+ dest = &VAL2MAT4(dest_v);
32
+ glm_mul_rot(*m1, *m2, *dest);
33
+ return dest_v;
34
+ }
35
+
36
+ /*
37
+ * call-seq: inv_tr() => self
38
+ *
39
+ * inverse orthonormal rotation + translation matrix (ridgid-body)
40
+ *
41
+ * X = | R T | X' = | R' -R'T |
42
+ * | 0 1 | | 0 1 |
43
+ *
44
+ */
45
+ VALUE rb_cglm_affine_inv_tr(VALUE mat) {
46
+ mat4 *m = NULL;
47
+ m = &VAL2MAT4(mat);
48
+ glm_inv_tr(*m);
49
+ return mat;
50
+ }
51
+
52
+ /*
53
+ * call-seq: affine_mul(m[, dest]) => dest | new Mat4
54
+ *
55
+ * This is similar to #mul but specialized to affine transform.
56
+ *
57
+ * Right Matrix format should be:
58
+ *
59
+ * R R R X
60
+ * R R R Y
61
+ * R R R Z
62
+ * 0 0 0 W
63
+ *
64
+ * This reduces some multiplications. It should be faster than #mul.
65
+ * if you are not sure about matrix format then DON'T use this! use #mul.
66
+ *
67
+ * * `m` is the second affine Mat4 to multiply `self` against.
68
+ * * `dest` is the out Mat4 to place the result in, and will be allocated if
69
+ * omitted.
70
+ *
71
+ */
72
+ VALUE rb_cglm_affine_mul(int argc, VALUE *argv, VALUE self) {
73
+ VALUE m_v, dest_v;
74
+ rb_scan_args(argc, argv, "11", &m_v, &dest_v);
75
+ if (NIL_P(dest_v)) dest_v = MAT4_NEW(ALLOC_MAT4);
76
+ mat4 *m1 = NULL, *m2 = NULL, *dest = NULL;
77
+ m1 = &VAL2MAT4(self);
78
+ m2 = &VAL2MAT4(m_v);
79
+ dest = &VAL2MAT4(dest_v);
80
+ glm_mul(*m1, *m2, *dest);
81
+ return dest_v;
82
+ }
83
+
84
+ /* call-seq: translate(vec3[, dest]) => dest | new Mat4 */
85
+ VALUE rb_cglm_translate(int argc, VALUE *argv, VALUE self) {
86
+ VALUE vec_v, dest_v;
87
+ rb_scan_args(argc, argv, "11", &vec_v, &dest_v);
88
+ if (NIL_P(dest_v)) dest_v = MAT4_NEW(ALLOC_MAT4);
89
+ mat4 *m1 = NULL, *dest = NULL;
90
+ vec3 *vec = NULL;
91
+ m1 = &VAL2MAT4(self);
92
+ vec = &VAL2VEC3(vec_v);
93
+ dest = &VAL2MAT4(dest_v);
94
+ glm_translate_to(*m1, *vec, *dest);
95
+ return dest_v;
96
+ }
97
+
98
+ /* call-seq: translate!(vec3) => self */
99
+ VALUE rb_cglm_translate_self(VALUE self, VALUE vec_v) {
100
+ mat4 *m1 = NULL;
101
+ vec3 *vec = NULL;
102
+ m1 = &VAL2MAT4(self);
103
+ vec = &VAL2VEC3(vec_v);
104
+ glm_translate(*m1, *vec);
105
+ return self;
106
+ }
107
+
108
+ /* call-seq: translate_x!(float) => self */
109
+ VALUE rb_cglm_translate_x_self(VALUE self, VALUE flt) {
110
+ mat4 *m1 = NULL;
111
+ m1 = &VAL2MAT4(self);
112
+ glm_translate_x(*m1, (float) NUM2DBL(flt));
113
+ return self;
114
+ }
115
+
116
+ /* call-seq: mat4.translate_y!(float) => mat4 */
117
+ VALUE rb_cglm_translate_y_self(VALUE self, VALUE flt) {
118
+ mat4 *m1 = NULL;
119
+ m1 = &VAL2MAT4(self);
120
+ glm_translate_y(*m1, (float) NUM2DBL(flt));
121
+ return self;
122
+ }
123
+
124
+ /* call-seq: translate_z!(float) => self */
125
+ VALUE rb_cglm_translate_z_self(VALUE self, VALUE flt) {
126
+ mat4 *m1 = NULL;
127
+ m1 = &VAL2MAT4(self);
128
+ glm_translate_z(*m1, (float) NUM2DBL(flt));
129
+ return self;
130
+ }
131
+
132
+ /* call-seq: translate(vec3) => new Mat4 */
133
+ VALUE rb_cglm_translate_new(VALUE klass, VALUE vec_v) {
134
+ VALUE dest = MAT4_NEW(ALLOC_MAT4);
135
+ glm_translate_make(VAL2MAT4(dest), VAL2VEC3(vec_v));
136
+ return dest;
137
+ }
138
+
139
+ /* call-seq: scale(vec3) => new Mat4 */
140
+ VALUE rb_cglm_scale_new(VALUE klass, VALUE vec_v) {
141
+ VALUE dest = MAT4_NEW(ALLOC_MAT4);
142
+ glm_scale_make(VAL2MAT4(dest), VAL2VEC3(vec_v));
143
+ return dest;
144
+ }
145
+
146
+ /* call-seq: rotate(axis, angle) => new Mat4 */
147
+ VALUE rb_cglm_rotate_new(VALUE klass, VALUE axis, VALUE angle) {
148
+ VALUE dest = MAT4_NEW(ALLOC_MAT4);
149
+ glm_rotate_make(VAL2MAT4(dest), (float) NUM2DBL(angle), VAL2VEC3(axis));
150
+ return dest;
151
+ }
152
+
153
+ /* call-seq: scale(vec3|float[, dest]) => dest | new Mat4 */
154
+ VALUE rb_cglm_scale(int argc, VALUE *argv, VALUE self) {
155
+ VALUE factor, dest;
156
+ rb_scan_args(argc, argv, "11", &factor, &dest);
157
+ if (NIL_P(dest)) dest = MAT4_NEW(ALLOC_MAT4);
158
+
159
+ if (RB_FLOAT_TYPE_P(factor) || RB_INTEGER_TYPE_P(factor)) {
160
+ memcpy(&VAL2MAT4(dest), &VAL2MAT4(self), sizeof(mat4));
161
+ glm_scale_uni(VAL2MAT4(dest), (float) NUM2DBL(factor));
162
+ } else {
163
+ glm_scale_to(VAL2MAT4(self), VAL2VEC3(factor), VAL2MAT4(dest));
164
+ }
165
+
166
+ return dest;
167
+ }
168
+
169
+ /* call-seq: scale!(vec3|float) => self */
170
+ VALUE rb_cglm_scale_self(VALUE self, VALUE factor) {
171
+ mat4 *m1 = NULL;
172
+ m1 = &VAL2MAT4(self);
173
+ if (RB_FLOAT_TYPE_P(factor) || RB_INTEGER_TYPE_P(factor)) {
174
+ glm_scale_uni(*m1, (float) NUM2DBL(factor));
175
+ } else {
176
+ vec3 *vec = NULL;
177
+ vec = &VAL2VEC3(factor);
178
+ glm_scale(*m1, *vec);
179
+ }
180
+ return self;
181
+ }
182
+
183
+ /* call-seq: rotate_x(float[, dest]) => dest | new Mat4 */
184
+ VALUE rb_cglm_rotate_x(int argc, VALUE *argv, VALUE self) {
185
+ VALUE angle, dest;
186
+ rb_scan_args(argc, argv, "11", &angle, &dest);
187
+ if (NIL_P(dest)) dest = MAT4_NEW(ALLOC_MAT4);
188
+ mat4 *m1, *m2;
189
+ m1 = &VAL2MAT4(self);
190
+ m2 = &VAL2MAT4(dest);
191
+ glm_rotate_x(*m1, (float) NUM2DBL(angle), *m2);
192
+ return dest;
193
+ }
194
+
195
+ /* call-seq: rotate_y(float[, dest]) => dest | new Mat4 */
196
+ VALUE rb_cglm_rotate_y(int argc, VALUE *argv, VALUE self) {
197
+ VALUE angle, dest;
198
+ rb_scan_args(argc, argv, "11", &angle, &dest);
199
+ if (NIL_P(dest)) dest = MAT4_NEW(ALLOC_MAT4);
200
+ mat4 *m1, *m2;
201
+ m1 = &VAL2MAT4(self);
202
+ m2 = &VAL2MAT4(dest);
203
+ glm_rotate_y(*m1, (float) NUM2DBL(angle), *m2);
204
+ return dest;
205
+ }
206
+
207
+ /* call-seq: rotate_z(float[, dest]) => dest | new Mat4 */
208
+ VALUE rb_cglm_rotate_z(int argc, VALUE *argv, VALUE self) {
209
+ VALUE angle, dest;
210
+ rb_scan_args(argc, argv, "11", &angle, &dest);
211
+ if (NIL_P(dest)) dest = MAT4_NEW(ALLOC_MAT4);
212
+ mat4 *m1, *m2;
213
+ m1 = &VAL2MAT4(self);
214
+ m2 = &VAL2MAT4(dest);
215
+ glm_rotate_z(*m1, (float) NUM2DBL(angle), *m2);
216
+ return dest;
217
+ }
218
+
219
+ /* call-seq: rotate!(axis, angle) => self */
220
+ VALUE rb_cglm_rotate_self(VALUE self, VALUE axis, VALUE angle) {
221
+ mat4 *mat;
222
+ vec3 *vec;
223
+ mat = &VAL2MAT4(self);
224
+ vec = &VAL2VEC3(axis);
225
+ glm_rotate(*mat, (float) NUM2DBL(angle), *vec);
226
+ return self;
227
+ }
228
+
229
+ /* call-seq: rotate_at!(pivot_point, axis, angle) => self */
230
+ VALUE rb_cglm_rotate_self_at(VALUE self, VALUE pivot, VALUE axis, VALUE angle) {
231
+ mat4 *m;
232
+ vec3 *v1, *v2;
233
+ m = &VAL2MAT4(self);
234
+ v1 = &VAL2VEC3(pivot);
235
+ v2 = &VAL2VEC3(axis);
236
+ glm_rotate_at(*m, *v1, (float) NUM2DBL(angle), *v2);
237
+ return self;
238
+ }
239
+
240
+ /* call-seq: axis_angle_rotate_at(pivot_point, axis, angle) => new Mat4 */
241
+ VALUE rb_cglm_rotate_at_new(VALUE klass, VALUE pivot, VALUE axis, VALUE angle) {
242
+ VALUE self = MAT4_NEW(ALLOC_MAT4);
243
+ mat4 *m;
244
+ vec3 *v1, *v2;
245
+ m = &VAL2MAT4(self);
246
+ v1 = &VAL2VEC3(pivot);
247
+ v2 = &VAL2VEC3(axis);
248
+ glm_rotate_atm(*m, *v1, (float) NUM2DBL(angle), *v2);
249
+ return self;
250
+ }
251
+
252
+ /* call-seq: decompose_scale(vec3) => vec3 */
253
+ VALUE rb_cglm_decompose_scale(VALUE self, VALUE out) {
254
+ mat4 *m;
255
+ vec3 *v;
256
+ m = &VAL2MAT4(self);
257
+ v = &VAL2VEC3(self);
258
+ glm_decompose_scalev(*m, *v);
259
+ return out;
260
+ }
261
+
262
+ /* call-seq: uniform_scale? => true|false */
263
+ VALUE rb_cglm_is_uniform_scale(VALUE self) {
264
+ mat4 *m;
265
+ m = &VAL2MAT4(self);
266
+ return glm_uniscaled(*m) ? Qtrue : Qfalse;
267
+ }
268
+
269
+ /* call-seq: decompose_rs(r, s) => self
270
+ *
271
+ * Decomposes `self` into a rotation Mat4 `r` and scale Vec3 `s`.
272
+ */
273
+ VALUE rb_cglm_decompose_rs(VALUE self, VALUE rot, VALUE scale) {
274
+ mat4 *m, *r;
275
+ vec3 *s;
276
+ m = &VAL2MAT4(self);
277
+ r = &VAL2MAT4(rot);
278
+ s = &VAL2VEC3(scale);
279
+ glm_decompose_rs(*m, *r, *s);
280
+ return self;
281
+ }
282
+
283
+ /* call-seq: decompose(t, r, s) => self
284
+ *
285
+ * Decomposes `self` into a translation Vec4 `t`, rotation Mat4 `r` and scale
286
+ * Vec3 `s`.
287
+ */
288
+ VALUE rb_cglm_decompose(VALUE self, VALUE trans, VALUE rot, VALUE scale) {
289
+ mat4 *m, *r;
290
+ vec3 *s;
291
+ vec4 *t;
292
+ m = &VAL2MAT4(self);
293
+ t = &VAL2VEC4(trans);
294
+ r = &VAL2MAT4(rot);
295
+ s = &VAL2VEC3(scale);
296
+ glm_decompose(*m, *t, *r, *s);
297
+ return self;
298
+ }
299
+
300
+ void Init_cglm_affine() {
301
+ rb_define_method(rb_cMat4, "affine_inv_tr", rb_cglm_affine_inv_tr, 0);
302
+ rb_define_method(rb_cMat4, "affine_mul_rot", rb_cglm_affine_mul_rot, -1);
303
+ rb_define_method(rb_cMat4, "affine_mul", rb_cglm_affine_mul, -1);
304
+ rb_define_method(rb_cMat4, "translate_to", rb_cglm_translate, -1);
305
+ rb_define_method(rb_cMat4, "translate!", rb_cglm_translate_self, 1);
306
+ rb_define_method(rb_cMat4, "translate_x!", rb_cglm_translate_x_self, 1);
307
+ rb_define_method(rb_cMat4, "translate_y!", rb_cglm_translate_y_self, 1);
308
+ rb_define_method(rb_cMat4, "translate_z!", rb_cglm_translate_z_self, 1);
309
+ rb_define_method(rb_cMat4, "scale", rb_cglm_scale, -1);
310
+ rb_define_method(rb_cMat4, "scale!", rb_cglm_scale_self, 1);
311
+ rb_define_method(rb_cMat4, "rotate_x", rb_cglm_rotate_x, -1);
312
+ rb_define_method(rb_cMat4, "rotate_y", rb_cglm_rotate_y, -1);
313
+ rb_define_method(rb_cMat4, "rotate_z", rb_cglm_rotate_z, -1);
314
+ rb_define_method(rb_cMat4, "rotate!", rb_cglm_rotate_self, 2);
315
+ rb_define_method(rb_cMat4, "rotate_at!", rb_cglm_rotate_self_at, 3);
316
+ rb_define_method(rb_cMat4, "decompose_scale",rb_cglm_decompose_scale, 1);
317
+ rb_define_method(rb_cMat4, "uniform_scale?", rb_cglm_is_uniform_scale, 0);
318
+ rb_define_method(rb_cMat4, "decompose_rs", rb_cglm_decompose_rs, 2);
319
+ rb_define_method(rb_cMat4, "decompose", rb_cglm_decompose, 3);
320
+
321
+ rb_define_singleton_method(rb_cMat4, "translate", rb_cglm_translate_new, 1);
322
+ rb_define_singleton_method(rb_cMat4, "scale", rb_cglm_scale_new, 1);
323
+ rb_define_singleton_method(rb_cMat4, "rotate", rb_cglm_rotate_new, 2);
324
+ rb_define_singleton_method(rb_cMat4, "axis_angle_rotate_at", rb_cglm_rotate_at_new, 3);
325
+ }