@carbonenginejs/runtime-utils 0.1.1 → 0.1.3

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.
Files changed (104) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +23 -23
  3. package/README.md +69 -69
  4. package/THIRD-PARTY-NOTICES.md +64 -64
  5. package/docs/README.md +74 -73
  6. package/docs/architecture.md +101 -101
  7. package/docs/concepts/foundation-consolidation.md +85 -85
  8. package/docs/concepts/model-lifecycle.md +111 -0
  9. package/docs/const-kb.md +85 -87
  10. package/docs/core-types/DECORATOR-TODOS.md +25 -25
  11. package/docs/core-types/README.md +229 -168
  12. package/docs/reference/api.md +105 -102
  13. package/docs/reference/classes/README.md +135 -135
  14. package/package.json +139 -139
  15. package/src/arrays.js +5 -5
  16. package/src/audio/audioFormats.js +34 -34
  17. package/src/audio/index.js +1 -1
  18. package/src/box3.js +331 -331
  19. package/src/bytes.js +56 -56
  20. package/src/compression.js +56 -56
  21. package/src/constants/index.js +7 -7
  22. package/src/constants/trinity.js +13 -13
  23. package/src/constants.js +15 -15
  24. package/src/curve.js +79 -79
  25. package/src/d3d/dxgiFormats.js +46 -46
  26. package/src/d3d/index.js +2 -2
  27. package/src/d3d/primitiveTopology.js +11 -11
  28. package/src/document/CjsCarbonDocument.js +212 -212
  29. package/src/document/CjsClassRegistry.js +373 -373
  30. package/src/document/CjsDocumentDehydrator.js +142 -142
  31. package/src/document/CjsDocumentHydrator.js +156 -156
  32. package/src/document/CjsStructRegistry.js +348 -348
  33. package/src/document/hydrationAdapter.js +129 -129
  34. package/src/document/index.js +6 -6
  35. package/src/errors/CjsError.js +286 -286
  36. package/src/errors/index.js +5 -5
  37. package/src/geometry/box.js +22 -22
  38. package/src/geometry/cylinder.js +22 -22
  39. package/src/geometry/helpers/earcut.js +1 -1
  40. package/src/geometry/helpers/misc.js +3 -3
  41. package/src/geometry/index.js +8 -8
  42. package/src/geometry/json.js +67 -67
  43. package/src/geometry/lathe.js +44 -44
  44. package/src/geometry/plane.js +14 -14
  45. package/src/geometry/shape.js +4 -4
  46. package/src/geometry/sphere.js +24 -24
  47. package/src/geometry/torus.js +14 -14
  48. package/src/graphics/colorSpaces.js +22 -22
  49. package/src/graphics/index.js +4 -4
  50. package/src/graphics/pixelFormats.js +158 -158
  51. package/src/graphics/textureDimensions.js +22 -22
  52. package/src/graphics/trinityEnums.js +87 -87
  53. package/src/index.js +62 -62
  54. package/src/is.js +108 -46
  55. package/src/json.js +23 -23
  56. package/src/lifecycle/CjsLifecycleState.js +77 -77
  57. package/src/lifecycle/index.js +1 -1
  58. package/src/lne3.js +70 -70
  59. package/src/lookup.js +48 -48
  60. package/src/mat3.js +51 -51
  61. package/src/mat4.js +699 -699
  62. package/src/math/index.js +25 -25
  63. package/src/math/scalar.js +63 -63
  64. package/src/media/index.js +1 -1
  65. package/src/media/mediaTypes.js +50 -50
  66. package/src/mesh.js +424 -424
  67. package/src/model/CjsEventEmitter.js +333 -333
  68. package/src/model/CjsModel.js +1589 -1544
  69. package/src/model/CjsModelState.js +72 -72
  70. package/src/model/index.js +4 -4
  71. package/src/model/sourceRecordUtils.js +54 -54
  72. package/src/noise.js +310 -310
  73. package/src/num.js +827 -827
  74. package/src/object.js +39 -39
  75. package/src/path.js +53 -53
  76. package/src/pln.js +125 -125
  77. package/src/pool.js +9 -9
  78. package/src/quat.js +144 -144
  79. package/src/ray3.js +188 -188
  80. package/src/renderContext/formats.js +145 -145
  81. package/src/renderContext/index.js +5 -5
  82. package/src/renderContext/presentation.js +125 -125
  83. package/src/renderContext/resources.js +27 -27
  84. package/src/renderContext/upscaling.js +22 -22
  85. package/src/renderContext/window.js +20 -20
  86. package/src/runtime/CjsRuntimeState.js +50 -50
  87. package/src/schema/CjsSchema.js +555 -318
  88. package/src/schema/index.js +4 -4
  89. package/src/shader/index.js +1 -1
  90. package/src/shader/shaderStages.js +37 -37
  91. package/src/sph3.js +181 -181
  92. package/src/tangent.js +288 -288
  93. package/src/text.js +40 -40
  94. package/src/tri3.js +98 -98
  95. package/src/types/carbonTypes.js +635 -635
  96. package/src/types/index.js +2 -2
  97. package/src/utils.js +58 -58
  98. package/src/validation.js +46 -46
  99. package/src/vec2.js +229 -229
  100. package/src/vec3.js +1188 -1172
  101. package/src/vec4.js +347 -347
  102. package/src/vertex.js +108 -108
  103. package/src/webgpu/index.js +1 -1
  104. package/src/webgpu/textureFormats.js +121 -121
package/src/tangent.js CHANGED
@@ -1,288 +1,288 @@
1
- /**
2
- * Packed tangent-frame helpers for CarbonEngineJS/GR2-style mesh data.
3
- *
4
- * The packed-frame constants and decode/encode behavior are based on observed
5
- * Fenris Creations (CCP Games) shader behavior for EVE/Carbon packed tangent
6
- * frames. No shader source is included here.
7
- */
8
-
9
- import {
10
- generateBiNormals,
11
- generateNormals,
12
- generateTangentFrames,
13
- generateTangents
14
- } from "./mesh.js";
15
- import {
16
- EPSILON,
17
- clamp
18
- } from "./num.js";
19
- import {
20
- cross,
21
- dot,
22
- length as vec3Length,
23
- normalize
24
- } from "gl-matrix/esm/vec3.js";
25
-
26
- /** Full-turn float32 constant used by the CCP tangent-frame shader. */
27
- export const TANGENT_TAU = 6.28318548;
28
-
29
- /** Half-turn float32 constant used by the CCP tangent-frame shader. */
30
- export const TANGENT_PI = 3.14159274;
31
-
32
- const
33
- TAU = TANGENT_TAU,
34
- PI = TANGENT_PI,
35
- POLAR_EPSILON = 1e-6;
36
-
37
- /**
38
- * Packed UNorm sentinel used for vertices with no authored tangent frame.
39
- *
40
- * @type {number[]}
41
- */
42
- export const NULL_TANGENT_UNORM = Object.freeze([ 0, 1, 0, 1 ]);
43
-
44
- /**
45
- * Test whether a packed tangent payload is the null-frame sentinel.
46
- *
47
- * @param {ArrayLike<number>} u Four UNorm values.
48
- * @returns {boolean} Whether the payload marks a missing authored frame.
49
- */
50
- export function isNullTangent(u)
51
- {
52
- const
53
- e1 = u[1],
54
- e3 = u[3];
55
-
56
- return (e1 <= 1e-3 || e1 >= 1 - 1e-3) && (e3 <= 1e-3 || e3 >= 1 - 1e-3);
57
- }
58
-
59
- const
60
- scratchT = new Float64Array(3),
61
- scratchB = new Float64Array(3),
62
- scratchN = new Float64Array(3);
63
-
64
- function decodeTangentFrameInto(u0, u1, u2, u3, outT, outB, outN)
65
- {
66
- const
67
- a0 = u0 * TAU - PI,
68
- a1 = u1 * TAU - PI,
69
- a2 = u2 * TAU - PI,
70
- a3 = u3 * TAU - PI,
71
- s1 = Math.abs(Math.sin(a1)),
72
- s3 = Math.abs(Math.sin(a3));
73
-
74
- outT[0] = s1 * Math.cos(a0);
75
- outT[1] = s1 * Math.sin(a0);
76
- outT[2] = Math.cos(a1);
77
-
78
- outB[0] = s3 * Math.cos(a2);
79
- outB[1] = s3 * Math.sin(a2);
80
- outB[2] = Math.cos(a3);
81
-
82
- const sign = (a1 > 0 && a3 > 0) ? 1 : -1;
83
- outN[0] = (outT[1] * outB[2] - outT[2] * outB[1]) * sign;
84
- outN[1] = (outT[2] * outB[0] - outT[0] * outB[2]) * sign;
85
- outN[2] = (outT[0] * outB[1] - outT[1] * outB[0]) * sign;
86
-
87
- return s1 < 1e-6 && s3 < 1e-6;
88
- }
89
-
90
- /**
91
- * Decode a packed tangent frame.
92
- *
93
- * @param {ArrayLike<number>} u Four UNorm values in `[0, 1]`.
94
- * @returns {{T: number[], B: number[], N: number[], null: boolean}} Decoded basis.
95
- */
96
- export function decodeTangentFrame(u)
97
- {
98
- const isNull = decodeTangentFrameInto(u[0], u[1], u[2], u[3], scratchT, scratchB, scratchN);
99
- return { T: Array.from(scratchT), B: Array.from(scratchB), N: Array.from(scratchN), null: isNull };
100
- }
101
-
102
- /**
103
- * Encode a tangent frame back to four UNorm angles.
104
- *
105
- * @param {ArrayLike<number>} T Unit tangent.
106
- * @param {ArrayLike<number>} B Unit binormal.
107
- * @param {ArrayLike<number>} [N] Unit normal; only handedness is used.
108
- * @returns {number[]} Four UNorm values in `[0, 1]`.
109
- */
110
- export function encodeTangentFrame(T, B, N)
111
- {
112
- let a0 = Math.atan2(T[1], T[0]),
113
- a1 = Math.acos(clamp(T[2], -1, 1));
114
-
115
- const a2 = Math.atan2(B[1], B[0]);
116
- let a3 = Math.acos(clamp(B[2], -1, 1));
117
-
118
- const negativeHandedness = N && dot(N, cross([ 0, 0, 0 ], T, B)) < 0;
119
- if (negativeHandedness)
120
- {
121
- a1 = a1 === 0 ? -POLAR_EPSILON : -a1;
122
- }
123
- else
124
- {
125
- if (a1 === 0) a1 = POLAR_EPSILON;
126
- if (a3 === 0) a3 = POLAR_EPSILON;
127
- }
128
-
129
- const enc = angle => clamp((angle + PI) / TAU, 0, 1);
130
- return [ enc(a0), enc(a1), enc(a2), enc(a3) ];
131
- }
132
-
133
- function vertexCount(mesh)
134
- {
135
- const p = mesh.vertex && mesh.vertex.position;
136
- return p ? (p.length / 3) | 0 : 0;
137
- }
138
-
139
- /**
140
- * Is this shared JSON mesh's tangent frame packed?
141
- *
142
- * @param {object} mesh Shared JSON mesh.
143
- * @returns {boolean} Whether the mesh has packed tangent frames.
144
- */
145
- export function isPacked(mesh)
146
- {
147
- const v = mesh.vertex;
148
- if (!v || !v.tangent || !v.tangent.length) return false;
149
-
150
- const n = vertexCount(mesh);
151
- if (!n) return false;
152
-
153
- const
154
- comps = v.tangent.length / n,
155
- empty = value => !value || value.length === 0;
156
-
157
- return comps === 4 && empty(v.normal) && empty(v.binormal);
158
- }
159
-
160
- /**
161
- * Unpack a packed shared JSON mesh in place.
162
- *
163
- * @param {object} mesh Shared JSON mesh to mutate.
164
- * @returns {boolean} Whether unpacking happened.
165
- */
166
- export function unpackMeshTangents(mesh)
167
- {
168
- if (!isPacked(mesh)) return false;
169
-
170
- const
171
- v = mesh.vertex,
172
- n = vertexCount(mesh),
173
- src = v.tangent,
174
- normal = new Array(n * 3),
175
- tangent = new Array(n * 3),
176
- binormal = new Array(n * 3);
177
-
178
- for (let i = 0; i < n; i++)
179
- {
180
- const
181
- s = i * 4,
182
- o = i * 3,
183
- isNull = decodeTangentFrameInto(src[s], src[s + 1], src[s + 2], src[s + 3], scratchT, scratchB, scratchN);
184
-
185
- if (isNull)
186
- {
187
- normal[o] = normal[o + 1] = normal[o + 2] = 0;
188
- tangent[o] = tangent[o + 1] = tangent[o + 2] = 0;
189
- binormal[o] = binormal[o + 1] = binormal[o + 2] = 0;
190
- }
191
- else
192
- {
193
- normal[o] = scratchN[0];
194
- normal[o + 1] = scratchN[1];
195
- normal[o + 2] = scratchN[2];
196
- tangent[o] = scratchT[0];
197
- tangent[o + 1] = scratchT[1];
198
- tangent[o + 2] = scratchT[2];
199
- binormal[o] = scratchB[0];
200
- binormal[o + 1] = scratchB[1];
201
- binormal[o + 2] = scratchB[2];
202
- }
203
- }
204
-
205
- v.normal = normal;
206
- v.tangent = tangent;
207
- v.binormal = binormal;
208
- return true;
209
- }
210
-
211
- /**
212
- * Pack explicit tangent frames into GR2-style four-component tangent data.
213
- *
214
- * @param {ArrayLike<number>} normals Flat xyz normals.
215
- * @param {ArrayLike<number>} tangents Flat xyz tangents.
216
- * @param {ArrayLike<number>} binormals Flat xyz binormals.
217
- * @returns {number[]} Flat xyzw packed tangent-frame values.
218
- */
219
- export function packTangentFrames(normals, tangents, binormals)
220
- {
221
- if (normals.length !== tangents.length ||
222
- normals.length !== binormals.length ||
223
- normals.length % 3 !== 0)
224
- {
225
- throw new Error("packTangentFrames requires matching complete xyz channels");
226
- }
227
-
228
- const packed = new Array((normals.length / 3) * 4);
229
- for (let i = 0, o = 0; i < normals.length; i += 3, o += 4)
230
- {
231
- const components = [
232
- normals[i], normals[i + 1], normals[i + 2],
233
- tangents[i], tangents[i + 1], tangents[i + 2],
234
- binormals[i], binormals[i + 1], binormals[i + 2]
235
- ];
236
- if (!components.every(Number.isFinite))
237
- {
238
- throw new Error(`packTangentFrames received non-finite data at vertex ${i / 3}`);
239
- }
240
-
241
- const
242
- normal = normalize([ 0, 0, 0 ], components.slice(0, 3)),
243
- tangent = normalize([ 0, 0, 0 ], components.slice(3, 6)),
244
- binormal = normalize([ 0, 0, 0 ], components.slice(6, 9)),
245
- frameNormalLength = vec3Length(cross([ 0, 0, 0 ], tangent, binormal));
246
-
247
- if (vec3Length(normal) <= EPSILON ||
248
- vec3Length(tangent) <= EPSILON ||
249
- vec3Length(binormal) <= EPSILON ||
250
- frameNormalLength <= EPSILON)
251
- {
252
- packed[o] = NULL_TANGENT_UNORM[0];
253
- packed[o + 1] = NULL_TANGENT_UNORM[1];
254
- packed[o + 2] = NULL_TANGENT_UNORM[2];
255
- packed[o + 3] = NULL_TANGENT_UNORM[3];
256
- continue;
257
- }
258
-
259
- const encoded = encodeTangentFrame(tangent, binormal, normal);
260
- packed[o] = encoded[0];
261
- packed[o + 1] = encoded[1];
262
- packed[o + 2] = encoded[2];
263
- packed[o + 3] = encoded[3];
264
- }
265
-
266
- return packed;
267
- }
268
-
269
- export const tangent = Object.freeze({
270
- TAU: TANGENT_TAU,
271
- PI: TANGENT_PI,
272
- NULL_TANGENT_UNORM,
273
- isNull: isNullTangent,
274
- isNullTangent,
275
- decode: decodeTangentFrame,
276
- decodeTangentFrame,
277
- pack: encodeTangentFrame,
278
- encode: encodeTangentFrame,
279
- encodeTangentFrame,
280
- packTangentFrames,
281
- unpack: unpackMeshTangents,
282
- unpackMeshTangents,
283
- isPacked,
284
- generateNormals,
285
- generateTangents,
286
- generateTangentFrames,
287
- generateBiNormals
288
- });
1
+ /**
2
+ * Packed tangent-frame helpers for CarbonEngineJS/GR2-style mesh data.
3
+ *
4
+ * The packed-frame constants and decode/encode behavior are based on observed
5
+ * Fenris Creations (CCP Games) shader behavior for EVE/Carbon packed tangent
6
+ * frames. No shader source is included here.
7
+ */
8
+
9
+ import {
10
+ generateBiNormals,
11
+ generateNormals,
12
+ generateTangentFrames,
13
+ generateTangents
14
+ } from "./mesh.js";
15
+ import {
16
+ EPSILON,
17
+ clamp
18
+ } from "./num.js";
19
+ import {
20
+ cross,
21
+ dot,
22
+ length as vec3Length,
23
+ normalize
24
+ } from "gl-matrix/esm/vec3.js";
25
+
26
+ /** Full-turn float32 constant used by the CCP tangent-frame shader. */
27
+ export const TANGENT_TAU = 6.28318548;
28
+
29
+ /** Half-turn float32 constant used by the CCP tangent-frame shader. */
30
+ export const TANGENT_PI = 3.14159274;
31
+
32
+ const
33
+ TAU = TANGENT_TAU,
34
+ PI = TANGENT_PI,
35
+ POLAR_EPSILON = 1e-6;
36
+
37
+ /**
38
+ * Packed UNorm sentinel used for vertices with no authored tangent frame.
39
+ *
40
+ * @type {number[]}
41
+ */
42
+ export const NULL_TANGENT_UNORM = Object.freeze([ 0, 1, 0, 1 ]);
43
+
44
+ /**
45
+ * Test whether a packed tangent payload is the null-frame sentinel.
46
+ *
47
+ * @param {ArrayLike<number>} u Four UNorm values.
48
+ * @returns {boolean} Whether the payload marks a missing authored frame.
49
+ */
50
+ export function isNullTangent(u)
51
+ {
52
+ const
53
+ e1 = u[1],
54
+ e3 = u[3];
55
+
56
+ return (e1 <= 1e-3 || e1 >= 1 - 1e-3) && (e3 <= 1e-3 || e3 >= 1 - 1e-3);
57
+ }
58
+
59
+ const
60
+ scratchT = new Float64Array(3),
61
+ scratchB = new Float64Array(3),
62
+ scratchN = new Float64Array(3);
63
+
64
+ function decodeTangentFrameInto(u0, u1, u2, u3, outT, outB, outN)
65
+ {
66
+ const
67
+ a0 = u0 * TAU - PI,
68
+ a1 = u1 * TAU - PI,
69
+ a2 = u2 * TAU - PI,
70
+ a3 = u3 * TAU - PI,
71
+ s1 = Math.abs(Math.sin(a1)),
72
+ s3 = Math.abs(Math.sin(a3));
73
+
74
+ outT[0] = s1 * Math.cos(a0);
75
+ outT[1] = s1 * Math.sin(a0);
76
+ outT[2] = Math.cos(a1);
77
+
78
+ outB[0] = s3 * Math.cos(a2);
79
+ outB[1] = s3 * Math.sin(a2);
80
+ outB[2] = Math.cos(a3);
81
+
82
+ const sign = (a1 > 0 && a3 > 0) ? 1 : -1;
83
+ outN[0] = (outT[1] * outB[2] - outT[2] * outB[1]) * sign;
84
+ outN[1] = (outT[2] * outB[0] - outT[0] * outB[2]) * sign;
85
+ outN[2] = (outT[0] * outB[1] - outT[1] * outB[0]) * sign;
86
+
87
+ return s1 < 1e-6 && s3 < 1e-6;
88
+ }
89
+
90
+ /**
91
+ * Decode a packed tangent frame.
92
+ *
93
+ * @param {ArrayLike<number>} u Four UNorm values in `[0, 1]`.
94
+ * @returns {{T: number[], B: number[], N: number[], null: boolean}} Decoded basis.
95
+ */
96
+ export function decodeTangentFrame(u)
97
+ {
98
+ const isNull = decodeTangentFrameInto(u[0], u[1], u[2], u[3], scratchT, scratchB, scratchN);
99
+ return { T: Array.from(scratchT), B: Array.from(scratchB), N: Array.from(scratchN), null: isNull };
100
+ }
101
+
102
+ /**
103
+ * Encode a tangent frame back to four UNorm angles.
104
+ *
105
+ * @param {ArrayLike<number>} T Unit tangent.
106
+ * @param {ArrayLike<number>} B Unit binormal.
107
+ * @param {ArrayLike<number>} [N] Unit normal; only handedness is used.
108
+ * @returns {number[]} Four UNorm values in `[0, 1]`.
109
+ */
110
+ export function encodeTangentFrame(T, B, N)
111
+ {
112
+ let a0 = Math.atan2(T[1], T[0]),
113
+ a1 = Math.acos(clamp(T[2], -1, 1));
114
+
115
+ const a2 = Math.atan2(B[1], B[0]);
116
+ let a3 = Math.acos(clamp(B[2], -1, 1));
117
+
118
+ const negativeHandedness = N && dot(N, cross([ 0, 0, 0 ], T, B)) < 0;
119
+ if (negativeHandedness)
120
+ {
121
+ a1 = a1 === 0 ? -POLAR_EPSILON : -a1;
122
+ }
123
+ else
124
+ {
125
+ if (a1 === 0) a1 = POLAR_EPSILON;
126
+ if (a3 === 0) a3 = POLAR_EPSILON;
127
+ }
128
+
129
+ const enc = angle => clamp((angle + PI) / TAU, 0, 1);
130
+ return [ enc(a0), enc(a1), enc(a2), enc(a3) ];
131
+ }
132
+
133
+ function vertexCount(mesh)
134
+ {
135
+ const p = mesh.vertex && mesh.vertex.position;
136
+ return p ? (p.length / 3) | 0 : 0;
137
+ }
138
+
139
+ /**
140
+ * Is this shared JSON mesh's tangent frame packed?
141
+ *
142
+ * @param {object} mesh Shared JSON mesh.
143
+ * @returns {boolean} Whether the mesh has packed tangent frames.
144
+ */
145
+ export function isPacked(mesh)
146
+ {
147
+ const v = mesh.vertex;
148
+ if (!v || !v.tangent || !v.tangent.length) return false;
149
+
150
+ const n = vertexCount(mesh);
151
+ if (!n) return false;
152
+
153
+ const
154
+ comps = v.tangent.length / n,
155
+ empty = value => !value || value.length === 0;
156
+
157
+ return comps === 4 && empty(v.normal) && empty(v.binormal);
158
+ }
159
+
160
+ /**
161
+ * Unpack a packed shared JSON mesh in place.
162
+ *
163
+ * @param {object} mesh Shared JSON mesh to mutate.
164
+ * @returns {boolean} Whether unpacking happened.
165
+ */
166
+ export function unpackMeshTangents(mesh)
167
+ {
168
+ if (!isPacked(mesh)) return false;
169
+
170
+ const
171
+ v = mesh.vertex,
172
+ n = vertexCount(mesh),
173
+ src = v.tangent,
174
+ normal = new Array(n * 3),
175
+ tangent = new Array(n * 3),
176
+ binormal = new Array(n * 3);
177
+
178
+ for (let i = 0; i < n; i++)
179
+ {
180
+ const
181
+ s = i * 4,
182
+ o = i * 3,
183
+ isNull = decodeTangentFrameInto(src[s], src[s + 1], src[s + 2], src[s + 3], scratchT, scratchB, scratchN);
184
+
185
+ if (isNull)
186
+ {
187
+ normal[o] = normal[o + 1] = normal[o + 2] = 0;
188
+ tangent[o] = tangent[o + 1] = tangent[o + 2] = 0;
189
+ binormal[o] = binormal[o + 1] = binormal[o + 2] = 0;
190
+ }
191
+ else
192
+ {
193
+ normal[o] = scratchN[0];
194
+ normal[o + 1] = scratchN[1];
195
+ normal[o + 2] = scratchN[2];
196
+ tangent[o] = scratchT[0];
197
+ tangent[o + 1] = scratchT[1];
198
+ tangent[o + 2] = scratchT[2];
199
+ binormal[o] = scratchB[0];
200
+ binormal[o + 1] = scratchB[1];
201
+ binormal[o + 2] = scratchB[2];
202
+ }
203
+ }
204
+
205
+ v.normal = normal;
206
+ v.tangent = tangent;
207
+ v.binormal = binormal;
208
+ return true;
209
+ }
210
+
211
+ /**
212
+ * Pack explicit tangent frames into GR2-style four-component tangent data.
213
+ *
214
+ * @param {ArrayLike<number>} normals Flat xyz normals.
215
+ * @param {ArrayLike<number>} tangents Flat xyz tangents.
216
+ * @param {ArrayLike<number>} binormals Flat xyz binormals.
217
+ * @returns {number[]} Flat xyzw packed tangent-frame values.
218
+ */
219
+ export function packTangentFrames(normals, tangents, binormals)
220
+ {
221
+ if (normals.length !== tangents.length ||
222
+ normals.length !== binormals.length ||
223
+ normals.length % 3 !== 0)
224
+ {
225
+ throw new Error("packTangentFrames requires matching complete xyz channels");
226
+ }
227
+
228
+ const packed = new Array((normals.length / 3) * 4);
229
+ for (let i = 0, o = 0; i < normals.length; i += 3, o += 4)
230
+ {
231
+ const components = [
232
+ normals[i], normals[i + 1], normals[i + 2],
233
+ tangents[i], tangents[i + 1], tangents[i + 2],
234
+ binormals[i], binormals[i + 1], binormals[i + 2]
235
+ ];
236
+ if (!components.every(Number.isFinite))
237
+ {
238
+ throw new Error(`packTangentFrames received non-finite data at vertex ${i / 3}`);
239
+ }
240
+
241
+ const
242
+ normal = normalize([ 0, 0, 0 ], components.slice(0, 3)),
243
+ tangent = normalize([ 0, 0, 0 ], components.slice(3, 6)),
244
+ binormal = normalize([ 0, 0, 0 ], components.slice(6, 9)),
245
+ frameNormalLength = vec3Length(cross([ 0, 0, 0 ], tangent, binormal));
246
+
247
+ if (vec3Length(normal) <= EPSILON ||
248
+ vec3Length(tangent) <= EPSILON ||
249
+ vec3Length(binormal) <= EPSILON ||
250
+ frameNormalLength <= EPSILON)
251
+ {
252
+ packed[o] = NULL_TANGENT_UNORM[0];
253
+ packed[o + 1] = NULL_TANGENT_UNORM[1];
254
+ packed[o + 2] = NULL_TANGENT_UNORM[2];
255
+ packed[o + 3] = NULL_TANGENT_UNORM[3];
256
+ continue;
257
+ }
258
+
259
+ const encoded = encodeTangentFrame(tangent, binormal, normal);
260
+ packed[o] = encoded[0];
261
+ packed[o + 1] = encoded[1];
262
+ packed[o + 2] = encoded[2];
263
+ packed[o + 3] = encoded[3];
264
+ }
265
+
266
+ return packed;
267
+ }
268
+
269
+ export const tangent = Object.freeze({
270
+ TAU: TANGENT_TAU,
271
+ PI: TANGENT_PI,
272
+ NULL_TANGENT_UNORM,
273
+ isNull: isNullTangent,
274
+ isNullTangent,
275
+ decode: decodeTangentFrame,
276
+ decodeTangentFrame,
277
+ pack: encodeTangentFrame,
278
+ encode: encodeTangentFrame,
279
+ encodeTangentFrame,
280
+ packTangentFrames,
281
+ unpack: unpackMeshTangents,
282
+ unpackMeshTangents,
283
+ isPacked,
284
+ generateNormals,
285
+ generateTangents,
286
+ generateTangentFrames,
287
+ generateBiNormals
288
+ });
package/src/text.js CHANGED
@@ -1,40 +1,40 @@
1
- import { asUint8Array } from "./bytes.js";
2
-
3
- /** Encodes a value as UTF-8 without importing a platform-specific module. */
4
- export function encodeUtf8(value)
5
- {
6
- const TextEncoderClass = globalThis.TextEncoder;
7
-
8
- if (typeof TextEncoderClass !== "function")
9
- {
10
- throw unsupportedTextCodec("TextEncoder");
11
- }
12
-
13
- return new TextEncoderClass().encode(String(value));
14
- }
15
-
16
- /** Decodes supported byte input as UTF-8. */
17
- export function decodeUtf8(value, options = {})
18
- {
19
- const TextDecoderClass = globalThis.TextDecoder;
20
-
21
- if (typeof TextDecoderClass !== "function")
22
- {
23
- throw unsupportedTextCodec("TextDecoder");
24
- }
25
-
26
- const decoder = new TextDecoderClass("utf-8", {
27
- fatal: Boolean(options.fatal),
28
- ignoreBOM: Boolean(options.ignoreBOM)
29
- });
30
-
31
- return decoder.decode(asUint8Array(value, "UTF-8 input"));
32
- }
33
-
34
- function unsupportedTextCodec(name)
35
- {
36
- const error = new Error(`${name} is unavailable in this environment.`);
37
-
38
- error.code = "CJS_TEXT_CODEC_UNSUPPORTED";
39
- return error;
40
- }
1
+ import { asUint8Array } from "./bytes.js";
2
+
3
+ /** Encodes a value as UTF-8 without importing a platform-specific module. */
4
+ export function encodeUtf8(value)
5
+ {
6
+ const TextEncoderClass = globalThis.TextEncoder;
7
+
8
+ if (typeof TextEncoderClass !== "function")
9
+ {
10
+ throw unsupportedTextCodec("TextEncoder");
11
+ }
12
+
13
+ return new TextEncoderClass().encode(String(value));
14
+ }
15
+
16
+ /** Decodes supported byte input as UTF-8. */
17
+ export function decodeUtf8(value, options = {})
18
+ {
19
+ const TextDecoderClass = globalThis.TextDecoder;
20
+
21
+ if (typeof TextDecoderClass !== "function")
22
+ {
23
+ throw unsupportedTextCodec("TextDecoder");
24
+ }
25
+
26
+ const decoder = new TextDecoderClass("utf-8", {
27
+ fatal: Boolean(options.fatal),
28
+ ignoreBOM: Boolean(options.ignoreBOM)
29
+ });
30
+
31
+ return decoder.decode(asUint8Array(value, "UTF-8 input"));
32
+ }
33
+
34
+ function unsupportedTextCodec(name)
35
+ {
36
+ const error = new Error(`${name} is unavailable in this environment.`);
37
+
38
+ error.code = "CJS_TEXT_CODEC_UNSUPPORTED";
39
+ return error;
40
+ }