@kiberon-labs/behave-graph-scene 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +34 -0
- package/src/Abstractions/Drivers/DummyScene.ts +79 -0
- package/src/Abstractions/IScene.ts +18 -0
- package/src/GLTFJson.ts +34 -0
- package/src/Nodes/Actions/EaseSceneProperty.ts +162 -0
- package/src/Nodes/Actions/SetSceneProperty.ts +35 -0
- package/src/Nodes/Events/OnSceneNodeClick.ts +66 -0
- package/src/Nodes/Logic/ColorNodes.ts +121 -0
- package/src/Nodes/Logic/EulerNodes.ts +115 -0
- package/src/Nodes/Logic/Mat3Nodes.ts +202 -0
- package/src/Nodes/Logic/Mat4Nodes.ts +257 -0
- package/src/Nodes/Logic/QuatNodes.ts +178 -0
- package/src/Nodes/Logic/Vec2Nodes.ts +111 -0
- package/src/Nodes/Logic/Vec3Nodes.ts +121 -0
- package/src/Nodes/Logic/Vec4Nodes.ts +112 -0
- package/src/Nodes/Logic/VecElements.ts +34 -0
- package/src/Nodes/Queries/GetSceneProperty.ts +35 -0
- package/src/Values/ColorValue.ts +22 -0
- package/src/Values/EulerValue.ts +22 -0
- package/src/Values/Internal/Mat2.ts +214 -0
- package/src/Values/Internal/Mat3.ts +422 -0
- package/src/Values/Internal/Mat4.ts +831 -0
- package/src/Values/Internal/Vec2.ts +97 -0
- package/src/Values/Internal/Vec3.ts +244 -0
- package/src/Values/Internal/Vec4.ts +350 -0
- package/src/Values/Mat3Value.ts +20 -0
- package/src/Values/Mat4Value.ts +20 -0
- package/src/Values/QuatValue.ts +22 -0
- package/src/Values/Vec2Value.ts +14 -0
- package/src/Values/Vec3Value.ts +22 -0
- package/src/Values/Vec4Value.ts +21 -0
- package/src/buildScene.ts +479 -0
- package/src/index.ts +38 -0
- package/src/loadScene.ts +81 -0
- package/src/registerSceneProfile.ts +105 -0
- package/tests/graphs/logic/Color.json +53 -0
- package/tests/graphs/logic/Euler.json +53 -0
- package/tests/graphs/logic/Quaternion.json +56 -0
- package/tests/graphs/logic/Vector2.json +50 -0
- package/tests/graphs/logic/Vector3.json +53 -0
- package/tests/graphs/logic/Vector4.json +56 -0
- package/tests/readSceneGraphs.test.ts +57 -0
- package/tests/registerSceneProfile.test.ts +62 -0
- package/tests/tsconfig.json +11 -0
- package/tests/values/internal/Vec2.test.ts +74 -0
- package/tests/values/internal/Vec3.test.ts +83 -0
- package/tests/values/internal/Vec4.test.ts +91 -0
- package/tsconfig.json +55 -0
- package/tsdown.config.ts +13 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EPSILON,
|
|
3
|
+
equalsTolerance,
|
|
4
|
+
parseSafeFloats,
|
|
5
|
+
toSafeString
|
|
6
|
+
} from '@kiberon-labs/behave-graph';
|
|
7
|
+
|
|
8
|
+
export type Vec2JSON = number[];
|
|
9
|
+
|
|
10
|
+
export class Vec2 {
|
|
11
|
+
public x: number;
|
|
12
|
+
public y: number;
|
|
13
|
+
constructor(x: number = 0, y: number = 0) {
|
|
14
|
+
this.x = x;
|
|
15
|
+
this.y = y;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
clone(result = new Vec2()): Vec2 {
|
|
19
|
+
return result.set(this.x, this.y);
|
|
20
|
+
}
|
|
21
|
+
set(x: number, y: number): this {
|
|
22
|
+
this.x = x;
|
|
23
|
+
this.y = y;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function vec2Equals(
|
|
29
|
+
a: Vec2,
|
|
30
|
+
b: Vec2,
|
|
31
|
+
tolerance: number = EPSILON
|
|
32
|
+
): boolean {
|
|
33
|
+
return (
|
|
34
|
+
equalsTolerance(a.x, b.x, tolerance) && equalsTolerance(a.y, b.y, tolerance)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
export function vec2Add(a: Vec2, b: Vec2, result: Vec2 = new Vec2()): Vec2 {
|
|
38
|
+
return result.set(a.x + b.x, a.y + b.y);
|
|
39
|
+
}
|
|
40
|
+
export function vec2Subtract(
|
|
41
|
+
a: Vec2,
|
|
42
|
+
b: Vec2,
|
|
43
|
+
result: Vec2 = new Vec2()
|
|
44
|
+
): Vec2 {
|
|
45
|
+
return result.set(a.x - b.x, a.y - b.y);
|
|
46
|
+
}
|
|
47
|
+
export function vec2MultiplyByScalar(
|
|
48
|
+
a: Vec2,
|
|
49
|
+
b: number,
|
|
50
|
+
result: Vec2 = new Vec2()
|
|
51
|
+
): Vec2 {
|
|
52
|
+
return result.set(a.x * b, a.y * b);
|
|
53
|
+
}
|
|
54
|
+
export function vec2Negate(a: Vec2, result: Vec2 = new Vec2()): Vec2 {
|
|
55
|
+
return result.set(-a.x, -a.y);
|
|
56
|
+
}
|
|
57
|
+
export function vec2Length(a: Vec2): number {
|
|
58
|
+
return Math.sqrt(vec2Dot(a, a));
|
|
59
|
+
}
|
|
60
|
+
export function vec2Normalize(a: Vec2, result: Vec2 = new Vec2()): Vec2 {
|
|
61
|
+
const invLength = 1 / vec2Length(a);
|
|
62
|
+
return vec2MultiplyByScalar(a, invLength, result);
|
|
63
|
+
}
|
|
64
|
+
export function vec2Dot(a: Vec2, b: Vec2): number {
|
|
65
|
+
return a.x * b.x + a.y * b.y;
|
|
66
|
+
}
|
|
67
|
+
export function vec2Mix(
|
|
68
|
+
a: Vec2,
|
|
69
|
+
b: Vec2,
|
|
70
|
+
t: number,
|
|
71
|
+
result = new Vec2()
|
|
72
|
+
): Vec2 {
|
|
73
|
+
const s = 1 - t;
|
|
74
|
+
return result.set(a.x * s + b.x * t, a.y * s + b.y * t);
|
|
75
|
+
}
|
|
76
|
+
export function vec2FromArray(
|
|
77
|
+
array: Float32Array | number[],
|
|
78
|
+
offset = 0,
|
|
79
|
+
result: Vec2 = new Vec2()
|
|
80
|
+
): Vec2 {
|
|
81
|
+
return result.set(array[offset + 0]!, array[offset + 1]!);
|
|
82
|
+
}
|
|
83
|
+
export function vec2ToArray(
|
|
84
|
+
a: Vec2,
|
|
85
|
+
array: Float32Array | number[],
|
|
86
|
+
offset = 0
|
|
87
|
+
): void {
|
|
88
|
+
array[offset + 0] = a.x;
|
|
89
|
+
array[offset + 1] = a.y;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function vec2ToString(a: Vec2): string {
|
|
93
|
+
return toSafeString([a.x, a.y]);
|
|
94
|
+
}
|
|
95
|
+
export function vec2Parse(text: string, result = new Vec2()): Vec2 {
|
|
96
|
+
return vec2FromArray(parseSafeFloats(text), 0, result);
|
|
97
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clamp,
|
|
3
|
+
EPSILON,
|
|
4
|
+
equalsTolerance,
|
|
5
|
+
parseSafeFloats,
|
|
6
|
+
toSafeString
|
|
7
|
+
} from '@kiberon-labs/behave-graph';
|
|
8
|
+
|
|
9
|
+
import { Mat3, mat4ToMat3, quatToMat3 } from './Mat3.js';
|
|
10
|
+
import { Mat4 } from './Mat4.js';
|
|
11
|
+
import { Vec4 } from './Vec4.js';
|
|
12
|
+
|
|
13
|
+
export type Vec3JSON = number[];
|
|
14
|
+
|
|
15
|
+
export class Vec3 {
|
|
16
|
+
public x: number;
|
|
17
|
+
public y: number;
|
|
18
|
+
public z: number;
|
|
19
|
+
constructor(x: number = 0, y: number = 0, z: number = 0) {
|
|
20
|
+
this.x = x;
|
|
21
|
+
this.y = y;
|
|
22
|
+
this.z = z;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
clone(result = new Vec3()): Vec3 {
|
|
26
|
+
return result.set(this.x, this.y, this.z);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set(x: number, y: number, z: number): this {
|
|
30
|
+
this.x = x;
|
|
31
|
+
this.y = y;
|
|
32
|
+
this.z = z;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function vec3Equals(
|
|
38
|
+
a: Vec3,
|
|
39
|
+
b: Vec3,
|
|
40
|
+
tolerance: number = EPSILON
|
|
41
|
+
): boolean {
|
|
42
|
+
return (
|
|
43
|
+
equalsTolerance(a.x, b.x, tolerance) &&
|
|
44
|
+
equalsTolerance(a.y, b.y, tolerance) &&
|
|
45
|
+
equalsTolerance(a.z, b.z, tolerance)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export function vec3Add(a: Vec3, b: Vec3, result = new Vec3()): Vec3 {
|
|
49
|
+
return result.set(a.x + b.x, a.y + b.y, a.z + b.z);
|
|
50
|
+
}
|
|
51
|
+
export function vec3Subtract(a: Vec3, b: Vec3, result = new Vec3()): Vec3 {
|
|
52
|
+
return result.set(a.x - b.x, a.y - b.y, a.z - b.z);
|
|
53
|
+
}
|
|
54
|
+
export function vec3MultiplyByScalar(
|
|
55
|
+
a: Vec3,
|
|
56
|
+
b: number,
|
|
57
|
+
result = new Vec3()
|
|
58
|
+
): Vec3 {
|
|
59
|
+
return result.set(a.x * b, a.y * b, a.z * b);
|
|
60
|
+
}
|
|
61
|
+
export function vec3Negate(a: Vec3, result = new Vec3()): Vec3 {
|
|
62
|
+
return result.set(-a.x, -a.y, -a.z);
|
|
63
|
+
}
|
|
64
|
+
export function vec3Length(a: Vec3): number {
|
|
65
|
+
return Math.sqrt(vec3Dot(a, a));
|
|
66
|
+
}
|
|
67
|
+
export function vec3Normalize(a: Vec3, result = new Vec3()): Vec3 {
|
|
68
|
+
const invLength = 1 / vec3Length(a);
|
|
69
|
+
return vec3MultiplyByScalar(a, invLength, result);
|
|
70
|
+
}
|
|
71
|
+
export function vec3Dot(a: Vec3, b: Vec3): number {
|
|
72
|
+
return a.x * b.x + a.y * b.y + a.z * b.z;
|
|
73
|
+
}
|
|
74
|
+
export function vec3Cross(a: Vec3, b: Vec3, result = new Vec3()): Vec3 {
|
|
75
|
+
const ax = a.x;
|
|
76
|
+
const ay = a.y;
|
|
77
|
+
const az = a.z;
|
|
78
|
+
const bx = b.x;
|
|
79
|
+
const by = b.y;
|
|
80
|
+
const bz = b.z;
|
|
81
|
+
|
|
82
|
+
return result.set(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx);
|
|
83
|
+
}
|
|
84
|
+
export function vec3Mix(
|
|
85
|
+
a: Vec3,
|
|
86
|
+
b: Vec3,
|
|
87
|
+
t: number,
|
|
88
|
+
result = new Vec3()
|
|
89
|
+
): Vec3 {
|
|
90
|
+
const s = 1 - t;
|
|
91
|
+
return result.set(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t);
|
|
92
|
+
}
|
|
93
|
+
export function vec3FromArray(
|
|
94
|
+
array: Float32Array | number[],
|
|
95
|
+
offset = 0,
|
|
96
|
+
result = new Vec3()
|
|
97
|
+
): Vec3 {
|
|
98
|
+
return result.set(array[offset + 0]!, array[offset + 1]!, array[offset + 2]!);
|
|
99
|
+
}
|
|
100
|
+
export function vec3ToArray(
|
|
101
|
+
a: Vec3,
|
|
102
|
+
array: Float32Array | number[],
|
|
103
|
+
offset = 0
|
|
104
|
+
): void {
|
|
105
|
+
array[offset + 0] = a.x;
|
|
106
|
+
array[offset + 1] = a.y;
|
|
107
|
+
array[offset + 2] = a.z;
|
|
108
|
+
}
|
|
109
|
+
export function vec3ToString(a: Vec3): string {
|
|
110
|
+
return toSafeString([a.x, a.y, a.z]);
|
|
111
|
+
}
|
|
112
|
+
export function vec3Parse(text: string, result = new Vec3()): Vec3 {
|
|
113
|
+
return vec3FromArray(parseSafeFloats(text), 0, result);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function hslToRGB(hsl: Vec3, result = new Vec3()): Vec3 {
|
|
117
|
+
function hue2rgb(p: number, q: number, t: number): number {
|
|
118
|
+
if (t < 0) {
|
|
119
|
+
t += 1;
|
|
120
|
+
}
|
|
121
|
+
if (t > 1) {
|
|
122
|
+
t -= 1;
|
|
123
|
+
}
|
|
124
|
+
if (t < 1 / 6) {
|
|
125
|
+
return p + (q - p) * 6 * t;
|
|
126
|
+
}
|
|
127
|
+
if (t < 1 / 2) {
|
|
128
|
+
return q;
|
|
129
|
+
}
|
|
130
|
+
if (t < 2 / 3) {
|
|
131
|
+
return p + (q - p) * 6 * (2 / 3 - t);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return p;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// h,s,l ranges are in 0.0 - 1.0
|
|
138
|
+
const h = ((hsl.x % 1) + 1) % 1; // euclidean modulo
|
|
139
|
+
const s = Math.min(Math.max(hsl.y, 0), 1);
|
|
140
|
+
const l = Math.min(Math.max(hsl.z, 0), 1);
|
|
141
|
+
|
|
142
|
+
if (s === 0) {
|
|
143
|
+
return result.set(1, 1, 1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const p = l <= 0.5 ? l * (1 + s) : l + s - l * s;
|
|
147
|
+
const q = 2 * l - p;
|
|
148
|
+
|
|
149
|
+
return result.set(
|
|
150
|
+
hue2rgb(q, p, h + 1 / 3),
|
|
151
|
+
hue2rgb(q, p, h),
|
|
152
|
+
hue2rgb(q, p, h - 1 / 3)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function rgbToHSL(rgb: Vec3, result = new Vec3()): Vec3 {
|
|
157
|
+
// h,s,l ranges are in 0.0 - 1.0
|
|
158
|
+
const r = rgb.x,
|
|
159
|
+
g = rgb.y,
|
|
160
|
+
b = rgb.z;
|
|
161
|
+
|
|
162
|
+
const max = Math.max(r, g, b);
|
|
163
|
+
const min = Math.min(r, g, b);
|
|
164
|
+
|
|
165
|
+
let hue = 0;
|
|
166
|
+
let saturation = 0;
|
|
167
|
+
const lightness = (min + max) / 2;
|
|
168
|
+
|
|
169
|
+
if (min === max) {
|
|
170
|
+
hue = 0;
|
|
171
|
+
saturation = 0;
|
|
172
|
+
} else {
|
|
173
|
+
const delta = max - min;
|
|
174
|
+
|
|
175
|
+
saturation =
|
|
176
|
+
lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
|
|
177
|
+
|
|
178
|
+
switch (max) {
|
|
179
|
+
case r:
|
|
180
|
+
hue = (g - b) / delta + (g < b ? 6 : 0);
|
|
181
|
+
break;
|
|
182
|
+
case g:
|
|
183
|
+
hue = (b - r) / delta + 2;
|
|
184
|
+
break;
|
|
185
|
+
case b:
|
|
186
|
+
hue = (r - g) / delta + 4;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
hue /= 6;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return result.set(hue, saturation, lightness);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function hexToRGB(hex: number, result = new Vec3()): Vec3 {
|
|
197
|
+
hex = Math.floor(hex);
|
|
198
|
+
return result.set(
|
|
199
|
+
((hex >> 16) & 255) / 255,
|
|
200
|
+
((hex >> 8) & 255) / 255,
|
|
201
|
+
(hex & 255) / 255
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function rgbToHex(rgb: Vec3): number {
|
|
206
|
+
return ((rgb.x * 255) << 16) ^ ((rgb.y * 255) << 8) ^ ((rgb.z * 255) << 0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// from three.js
|
|
210
|
+
export function mat3ToEuler(m: Mat3, result = new Vec3()): Vec3 {
|
|
211
|
+
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
|
|
212
|
+
|
|
213
|
+
const te = m.elements;
|
|
214
|
+
const m11 = te[0],
|
|
215
|
+
m12 = te[3],
|
|
216
|
+
m13 = te[6];
|
|
217
|
+
const m21 = te[1],
|
|
218
|
+
m22 = te[4],
|
|
219
|
+
m23 = te[7];
|
|
220
|
+
const m31 = te[2],
|
|
221
|
+
m32 = te[5],
|
|
222
|
+
m33 = te[8];
|
|
223
|
+
|
|
224
|
+
result.y = Math.asin(clamp(m13, -1, 1));
|
|
225
|
+
|
|
226
|
+
if (Math.abs(m13) < 0.9999999) {
|
|
227
|
+
result.x = Math.atan2(-m23, m33);
|
|
228
|
+
result.z = Math.atan2(-m12, m11);
|
|
229
|
+
} else {
|
|
230
|
+
result.x = Math.atan2(m32, m22);
|
|
231
|
+
result.z = 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// from three.js
|
|
238
|
+
export function mat4ToEuler(m: Mat4, result = new Vec3()): Vec3 {
|
|
239
|
+
return mat3ToEuler(mat4ToMat3(m), result);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function quatToEuler(q: Vec4, result = new Vec3()): Vec3 {
|
|
243
|
+
return mat3ToEuler(quatToMat3(q), result);
|
|
244
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EPSILON,
|
|
3
|
+
equalsTolerance,
|
|
4
|
+
parseSafeFloats,
|
|
5
|
+
toSafeString
|
|
6
|
+
} from '@kiberon-labs/behave-graph';
|
|
7
|
+
|
|
8
|
+
import { Mat3, mat4ToMat3 } from './Mat3.js';
|
|
9
|
+
import { Mat4 } from './Mat4.js';
|
|
10
|
+
import { Vec3 } from './Vec3.js';
|
|
11
|
+
|
|
12
|
+
export type Vec4JSON = number[];
|
|
13
|
+
|
|
14
|
+
export class Vec4 {
|
|
15
|
+
public x: number;
|
|
16
|
+
public y: number;
|
|
17
|
+
public z: number;
|
|
18
|
+
public w: number;
|
|
19
|
+
constructor(x: number = 0, y: number = 0, z: number = 0, w: number = 0) {
|
|
20
|
+
this.x = x;
|
|
21
|
+
this.y = y;
|
|
22
|
+
this.z = z;
|
|
23
|
+
this.w = w;
|
|
24
|
+
}
|
|
25
|
+
clone(result = new Vec4()): Vec4 {
|
|
26
|
+
return result.set(this.x, this.y, this.z, this.w);
|
|
27
|
+
}
|
|
28
|
+
set(x: number, y: number, z: number, w: number): this {
|
|
29
|
+
this.x = x;
|
|
30
|
+
this.y = y;
|
|
31
|
+
this.z = z;
|
|
32
|
+
this.w = w;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function vec4Equals(
|
|
37
|
+
a: Vec4,
|
|
38
|
+
b: Vec4,
|
|
39
|
+
tolerance: number = EPSILON
|
|
40
|
+
): boolean {
|
|
41
|
+
return (
|
|
42
|
+
equalsTolerance(a.x, b.x, tolerance) &&
|
|
43
|
+
equalsTolerance(a.y, b.y, tolerance) &&
|
|
44
|
+
equalsTolerance(a.z, b.z, tolerance) &&
|
|
45
|
+
equalsTolerance(a.w, b.w, tolerance)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export function vec4Add(a: Vec4, b: Vec4, result = new Vec4()): Vec4 {
|
|
49
|
+
return result.set(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
|
50
|
+
}
|
|
51
|
+
export function vec4Subtract(a: Vec4, b: Vec4, result = new Vec4()): Vec4 {
|
|
52
|
+
return result.set(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
|
53
|
+
}
|
|
54
|
+
export function vec4MultiplyByScalar(
|
|
55
|
+
a: Vec4,
|
|
56
|
+
b: number,
|
|
57
|
+
result = new Vec4()
|
|
58
|
+
): Vec4 {
|
|
59
|
+
return result.set(a.x * b, a.y * b, a.z * b, a.w * b);
|
|
60
|
+
}
|
|
61
|
+
export function vec4Negate(a: Vec4, result = new Vec4()): Vec4 {
|
|
62
|
+
return result.set(-a.x, -a.y, -a.z, -a.w);
|
|
63
|
+
}
|
|
64
|
+
export function vec4Length(a: Vec4): number {
|
|
65
|
+
return Math.sqrt(vec4Dot(a, a));
|
|
66
|
+
}
|
|
67
|
+
export function vec4Normalize(a: Vec4, result = new Vec4()): Vec4 {
|
|
68
|
+
const invLength = 1 / vec4Length(a);
|
|
69
|
+
return vec4MultiplyByScalar(a, invLength, result);
|
|
70
|
+
}
|
|
71
|
+
export function vec4Dot(a: Vec4, b: Vec4): number {
|
|
72
|
+
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
|
73
|
+
}
|
|
74
|
+
export function vec4Mix(
|
|
75
|
+
a: Vec4,
|
|
76
|
+
b: Vec4,
|
|
77
|
+
t: number,
|
|
78
|
+
result = new Vec4()
|
|
79
|
+
): Vec4 {
|
|
80
|
+
const s = 1 - t;
|
|
81
|
+
return result.set(
|
|
82
|
+
a.x * s + b.x * t,
|
|
83
|
+
a.y * s + b.y * t,
|
|
84
|
+
a.z * s + b.z * t,
|
|
85
|
+
a.w * s + b.w * t
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
export function vec4FromArray(
|
|
89
|
+
array: Float32Array | number[],
|
|
90
|
+
offset = 0,
|
|
91
|
+
result = new Vec4()
|
|
92
|
+
): Vec4 {
|
|
93
|
+
return result.set(
|
|
94
|
+
array[offset + 0]!,
|
|
95
|
+
array[offset + 1]!,
|
|
96
|
+
array[offset + 2]!,
|
|
97
|
+
array[offset + 3]!
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
export function vec4ToArray(
|
|
101
|
+
a: Vec4,
|
|
102
|
+
array: Float32Array | number[],
|
|
103
|
+
offset = 0
|
|
104
|
+
): void {
|
|
105
|
+
array[offset + 0] = a.x;
|
|
106
|
+
array[offset + 1] = a.y;
|
|
107
|
+
array[offset + 2] = a.z;
|
|
108
|
+
array[offset + 3] = a.w;
|
|
109
|
+
}
|
|
110
|
+
export function vec4ToString(a: Vec4): string {
|
|
111
|
+
return toSafeString([a.x, a.y, a.z, a.w]);
|
|
112
|
+
}
|
|
113
|
+
export function vec4Parse(text: string, result = new Vec4()): Vec4 {
|
|
114
|
+
return vec4FromArray(parseSafeFloats(text), 0, result);
|
|
115
|
+
}
|
|
116
|
+
export function quatConjugate(a: Vec4, result = new Vec4()): Vec4 {
|
|
117
|
+
return result.set(-a.x, -a.y, -a.z, a.w);
|
|
118
|
+
}
|
|
119
|
+
export function quatMultiply(a: Vec4, b: Vec4, result = new Vec4()): Vec4 {
|
|
120
|
+
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
|
|
121
|
+
|
|
122
|
+
const qax = a.x;
|
|
123
|
+
const qay = a.y;
|
|
124
|
+
const qaz = a.z;
|
|
125
|
+
const qaw = a.w;
|
|
126
|
+
const qbx = b.x;
|
|
127
|
+
const qby = b.y;
|
|
128
|
+
const qbz = b.z;
|
|
129
|
+
const qbw = b.w;
|
|
130
|
+
|
|
131
|
+
return result.set(
|
|
132
|
+
qax * qbw + qaw * qbx + qay * qbz - qaz * qby,
|
|
133
|
+
qay * qbw + qaw * qby + qaz * qbx - qax * qbz,
|
|
134
|
+
qaz * qbw + qaw * qbz + qax * qby - qay * qbx,
|
|
135
|
+
qaw * qbw - qax * qbx - qay * qby - qaz * qbz
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function quatSlerp(
|
|
140
|
+
a: Vec4,
|
|
141
|
+
b: Vec4,
|
|
142
|
+
t: number,
|
|
143
|
+
result = new Vec4()
|
|
144
|
+
): Vec4 {
|
|
145
|
+
if (t <= 0) return a.clone(result);
|
|
146
|
+
if (t >= 1) return b.clone(result);
|
|
147
|
+
|
|
148
|
+
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
|
|
149
|
+
|
|
150
|
+
let cosHalfTheta = vec4Dot(a, b);
|
|
151
|
+
|
|
152
|
+
if (cosHalfTheta < 0) {
|
|
153
|
+
vec4Negate(b, result);
|
|
154
|
+
|
|
155
|
+
cosHalfTheta = -cosHalfTheta;
|
|
156
|
+
} else {
|
|
157
|
+
b.clone(result);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (cosHalfTheta >= 1) {
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const sqrSinHalfTheta = 1 - cosHalfTheta * cosHalfTheta;
|
|
165
|
+
|
|
166
|
+
if (sqrSinHalfTheta <= Number.EPSILON) {
|
|
167
|
+
vec4Mix(a, result, t);
|
|
168
|
+
vec4Normalize(result, result);
|
|
169
|
+
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const sinHalfTheta = Math.sqrt(sqrSinHalfTheta);
|
|
174
|
+
const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta);
|
|
175
|
+
const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta;
|
|
176
|
+
const ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
|
|
177
|
+
|
|
178
|
+
result.w = a.w * ratioA + result.w * ratioB;
|
|
179
|
+
result.x = a.x * ratioA + result.x * ratioB;
|
|
180
|
+
result.y = a.y * ratioA + result.y * ratioB;
|
|
181
|
+
result.z = a.z * ratioA + result.z * ratioB;
|
|
182
|
+
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Calculate the exponential of a unit quaternion.
|
|
188
|
+
*
|
|
189
|
+
* @param {quat} out the receiving quaternion
|
|
190
|
+
* @param {ReadonlyQuat} a quat to calculate the exponential of
|
|
191
|
+
* @returns {quat} out
|
|
192
|
+
*/
|
|
193
|
+
export function quatExp(a: Vec4, result = new Vec4()): Vec4 {
|
|
194
|
+
const x = a.x,
|
|
195
|
+
y = a.y,
|
|
196
|
+
z = a.z,
|
|
197
|
+
w = a.w;
|
|
198
|
+
|
|
199
|
+
const r = Math.sqrt(x * x + y * y + z * z);
|
|
200
|
+
const et = Math.exp(w);
|
|
201
|
+
const s = r > 0 ? (et * Math.sin(r)) / r : 0;
|
|
202
|
+
|
|
203
|
+
return result.set(x * s, y * s, z * s, et * Math.cos(r));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// from gl-matrix
|
|
207
|
+
export function quatLn(a: Vec4, result = new Vec4()): Vec4 {
|
|
208
|
+
const x = a.x,
|
|
209
|
+
y = a.y,
|
|
210
|
+
z = a.z,
|
|
211
|
+
w = a.w;
|
|
212
|
+
|
|
213
|
+
const r = Math.sqrt(x * x + y * y + z * z);
|
|
214
|
+
const t = r > 0 ? Math.atan2(r, w) / r : 0;
|
|
215
|
+
|
|
216
|
+
return result.set(
|
|
217
|
+
x * t,
|
|
218
|
+
y * t,
|
|
219
|
+
z * t,
|
|
220
|
+
0.5 * Math.log(x * x + y * y + z * z + w * w)
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// from gl-matrix
|
|
225
|
+
export function quatPow(a: Vec4, b: number, result = new Vec4()): Vec4 {
|
|
226
|
+
const ln = quatLn(a);
|
|
227
|
+
const lnScaled = vec4MultiplyByScalar(ln, b);
|
|
228
|
+
quatExp(lnScaled, result);
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function eulerToQuat(euler: Vec3, result: Vec4 = new Vec4()): Vec4 {
|
|
233
|
+
// eslint-disable-next-line max-len
|
|
234
|
+
// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
|
|
235
|
+
|
|
236
|
+
const c1 = Math.cos(euler.x / 2);
|
|
237
|
+
const c2 = Math.cos(euler.y / 2);
|
|
238
|
+
const c3 = Math.cos(euler.z / 2);
|
|
239
|
+
|
|
240
|
+
const s1 = Math.sin(euler.x / 2);
|
|
241
|
+
const s2 = Math.sin(euler.y / 2);
|
|
242
|
+
const s3 = Math.sin(euler.z / 2);
|
|
243
|
+
|
|
244
|
+
// XYZ order only
|
|
245
|
+
return result.set(
|
|
246
|
+
s1 * c2 * c3 + c1 * s2 * s3,
|
|
247
|
+
c1 * s2 * c3 - s1 * c2 * s3,
|
|
248
|
+
c1 * c2 * s3 + s1 * s2 * c3,
|
|
249
|
+
c1 * c2 * c3 - s1 * s2 * s3
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function angleAxisToQuat(
|
|
254
|
+
angle: number,
|
|
255
|
+
axis: Vec3,
|
|
256
|
+
result = new Vec4()
|
|
257
|
+
): Vec4 {
|
|
258
|
+
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
|
|
259
|
+
|
|
260
|
+
// assumes axis is normalized
|
|
261
|
+
|
|
262
|
+
const halfAngle = angle / 2;
|
|
263
|
+
const s = Math.sin(halfAngle);
|
|
264
|
+
|
|
265
|
+
return result.set(axis.x * s, axis.y * s, axis.z * s, Math.cos(halfAngle));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// from gl-matrix
|
|
269
|
+
export function quatToAngleAxis(
|
|
270
|
+
q: Vec4,
|
|
271
|
+
result = new Vec3()
|
|
272
|
+
): [angle: number, axis: Vec3] {
|
|
273
|
+
const rad = Math.acos(q.w) * 2;
|
|
274
|
+
const s = Math.sin(rad / 2);
|
|
275
|
+
if (s > EPSILON) {
|
|
276
|
+
result.x = q.x / s;
|
|
277
|
+
result.y = q.y / s;
|
|
278
|
+
result.z = q.z / s;
|
|
279
|
+
} else {
|
|
280
|
+
// If s is zero, return any axis (no rotation - axis does not matter)
|
|
281
|
+
result.x = 1;
|
|
282
|
+
result.y = 0;
|
|
283
|
+
result.z = 0;
|
|
284
|
+
}
|
|
285
|
+
return [rad, result];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function mat4ToQuat(m: Mat4, result = new Vec4()): Vec4 {
|
|
289
|
+
return mat3ToQuat(mat4ToMat3(m), result);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function mat3ToQuat(m: Mat3, result = new Vec4()): Vec4 {
|
|
293
|
+
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
|
|
294
|
+
|
|
295
|
+
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
|
|
296
|
+
|
|
297
|
+
// TODO, allocate x, y, z, w and only set q.* at the end.
|
|
298
|
+
|
|
299
|
+
const te = m.elements,
|
|
300
|
+
m11 = te[0],
|
|
301
|
+
m12 = te[3],
|
|
302
|
+
m13 = te[6],
|
|
303
|
+
m21 = te[1],
|
|
304
|
+
m22 = te[4],
|
|
305
|
+
m23 = te[7],
|
|
306
|
+
m31 = te[2],
|
|
307
|
+
m32 = te[5],
|
|
308
|
+
m33 = te[8],
|
|
309
|
+
trace = m11 + m22 + m33;
|
|
310
|
+
|
|
311
|
+
if (trace > 0) {
|
|
312
|
+
const s = 0.5 / Math.sqrt(trace + 1);
|
|
313
|
+
|
|
314
|
+
return result.set(
|
|
315
|
+
(m32 - m23) * s,
|
|
316
|
+
(m13 - m31) * s,
|
|
317
|
+
(m21 - m12) * s,
|
|
318
|
+
0.25 / s
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if (m11 > m22 && m11 > m33) {
|
|
322
|
+
const s = 2 * Math.sqrt(1 + m11 - m22 - m33);
|
|
323
|
+
|
|
324
|
+
return result.set(
|
|
325
|
+
0.25 * s,
|
|
326
|
+
(m12 + m21) / s,
|
|
327
|
+
(m13 + m31) / s,
|
|
328
|
+
(m32 - m23) / s
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (m22 > m33) {
|
|
332
|
+
const s = 2 * Math.sqrt(1 + m22 - m11 - m33);
|
|
333
|
+
|
|
334
|
+
return result.set(
|
|
335
|
+
(m12 + m21) / s,
|
|
336
|
+
0.25 * s,
|
|
337
|
+
(m23 + m32) / s,
|
|
338
|
+
(m13 - m31) / s
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const s = 2 * Math.sqrt(1 + m33 - m11 - m22);
|
|
343
|
+
|
|
344
|
+
return result.set(
|
|
345
|
+
(m13 + m31) / s,
|
|
346
|
+
(m23 + m32) / s,
|
|
347
|
+
0.25 * s,
|
|
348
|
+
(m21 - m12) / s
|
|
349
|
+
);
|
|
350
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ValueType } from '@kiberon-labs/behave-graph';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Mat3,
|
|
5
|
+
mat3Equals,
|
|
6
|
+
type Mat3JSON,
|
|
7
|
+
mat3Mix,
|
|
8
|
+
mat3Parse
|
|
9
|
+
} from './Internal/Mat3.js';
|
|
10
|
+
|
|
11
|
+
export const Mat3Value: ValueType = {
|
|
12
|
+
name: 'mat3',
|
|
13
|
+
creator: () => new Mat3(),
|
|
14
|
+
deserialize: (value: string | Mat3JSON) =>
|
|
15
|
+
typeof value === 'string' ? mat3Parse(value) : new Mat3(value),
|
|
16
|
+
serialize: (value) => value.elements as Mat3JSON,
|
|
17
|
+
lerp: (start: Mat3, end: Mat3, t: number) => mat3Mix(start, end, t),
|
|
18
|
+
equals: (a: Mat3, b: Mat3) => mat3Equals(a, b),
|
|
19
|
+
clone: (value: Mat3) => value.clone()
|
|
20
|
+
};
|