@carbonenginejs/core-math 0.1.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/LICENSE +21 -0
- package/NOTICE +24 -0
- package/README.md +110 -0
- package/THIRD-PARTY-NOTICES.md +38 -0
- package/package.json +74 -0
- package/src/box3.js +1329 -0
- package/src/constants.js +16 -0
- package/src/curve.js +390 -0
- package/src/geometry/box.js +132 -0
- package/src/geometry/cylinder.js +234 -0
- package/src/geometry/helpers/LICENSE +15 -0
- package/src/geometry/helpers/earcut.js +766 -0
- package/src/geometry/helpers/misc.js +103 -0
- package/src/geometry/index.js +7 -0
- package/src/geometry/json.js +115 -0
- package/src/geometry/lathe.js +148 -0
- package/src/geometry/octahedron.js +0 -0
- package/src/geometry/plane.js +76 -0
- package/src/geometry/shape.js +95 -0
- package/src/geometry/sphere.js +116 -0
- package/src/geometry/torus.js +88 -0
- package/src/index.js +29 -0
- package/src/lne3.js +488 -0
- package/src/mat3.js +131 -0
- package/src/mat4.js +600 -0
- package/src/mesh.js +298 -0
- package/src/noise.js +225 -0
- package/src/num.js +735 -0
- package/src/pln.js +740 -0
- package/src/pool.js +160 -0
- package/src/quat.js +110 -0
- package/src/ray3.js +1056 -0
- package/src/sph3.js +718 -0
- package/src/tangent.js +256 -0
- package/src/tri3.js +650 -0
- package/src/utils.js +15 -0
- package/src/vec2.js +229 -0
- package/src/vec3.js +1116 -0
- package/src/vec4.js +315 -0
- package/src/vertex.js +109 -0
package/src/tangent.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
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
|
+
generateTangents
|
|
13
|
+
} from "./mesh.js";
|
|
14
|
+
import { num } from "./num.js";
|
|
15
|
+
import { vec3 } from "./vec3.js";
|
|
16
|
+
|
|
17
|
+
/** Full-turn float32 constant used by the CCP tangent-frame shader. */
|
|
18
|
+
export const TANGENT_TAU = 6.28318548;
|
|
19
|
+
|
|
20
|
+
/** Half-turn float32 constant used by the CCP tangent-frame shader. */
|
|
21
|
+
export const TANGENT_PI = 3.14159274;
|
|
22
|
+
|
|
23
|
+
const
|
|
24
|
+
TAU = TANGENT_TAU,
|
|
25
|
+
PI = TANGENT_PI;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Packed UNorm sentinel used for vertices with no authored tangent frame.
|
|
29
|
+
*
|
|
30
|
+
* @type {number[]}
|
|
31
|
+
*/
|
|
32
|
+
export const NULL_TANGENT_UNORM = Object.freeze([ 0, 1, 0, 1 ]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Test whether a packed tangent payload is the null-frame sentinel.
|
|
36
|
+
*
|
|
37
|
+
* @param {ArrayLike<number>} u Four UNorm values.
|
|
38
|
+
* @returns {boolean} Whether the payload marks a missing authored frame.
|
|
39
|
+
*/
|
|
40
|
+
export function isNullTangent(u)
|
|
41
|
+
{
|
|
42
|
+
const
|
|
43
|
+
e1 = u[1],
|
|
44
|
+
e3 = u[3];
|
|
45
|
+
|
|
46
|
+
return (e1 <= 1e-3 || e1 >= 1 - 1e-3) && (e3 <= 1e-3 || e3 >= 1 - 1e-3);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const
|
|
50
|
+
scratchT = new Float64Array(3),
|
|
51
|
+
scratchB = new Float64Array(3),
|
|
52
|
+
scratchN = new Float64Array(3);
|
|
53
|
+
|
|
54
|
+
function decodeTangentFrameInto(u0, u1, u2, u3, outT, outB, outN)
|
|
55
|
+
{
|
|
56
|
+
const
|
|
57
|
+
a0 = u0 * TAU - PI,
|
|
58
|
+
a1 = u1 * TAU - PI,
|
|
59
|
+
a2 = u2 * TAU - PI,
|
|
60
|
+
a3 = u3 * TAU - PI,
|
|
61
|
+
s1 = Math.abs(Math.sin(a1)),
|
|
62
|
+
s3 = Math.abs(Math.sin(a3));
|
|
63
|
+
|
|
64
|
+
outT[0] = s1 * Math.cos(a0);
|
|
65
|
+
outT[1] = s1 * Math.sin(a0);
|
|
66
|
+
outT[2] = Math.cos(a1);
|
|
67
|
+
|
|
68
|
+
outB[0] = s3 * Math.cos(a2);
|
|
69
|
+
outB[1] = s3 * Math.sin(a2);
|
|
70
|
+
outB[2] = Math.cos(a3);
|
|
71
|
+
|
|
72
|
+
const sign = (a1 > 0 && a3 > 0) ? 1 : -1;
|
|
73
|
+
outN[0] = (outT[1] * outB[2] - outT[2] * outB[1]) * sign;
|
|
74
|
+
outN[1] = (outT[2] * outB[0] - outT[0] * outB[2]) * sign;
|
|
75
|
+
outN[2] = (outT[0] * outB[1] - outT[1] * outB[0]) * sign;
|
|
76
|
+
|
|
77
|
+
return s1 < 1e-6 && s3 < 1e-6;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Decode a packed tangent frame.
|
|
82
|
+
*
|
|
83
|
+
* @param {ArrayLike<number>} u Four UNorm values in `[0, 1]`.
|
|
84
|
+
* @returns {{T: number[], B: number[], N: number[], null: boolean}} Decoded basis.
|
|
85
|
+
*/
|
|
86
|
+
export function decodeTangentFrame(u)
|
|
87
|
+
{
|
|
88
|
+
const isNull = decodeTangentFrameInto(u[0], u[1], u[2], u[3], scratchT, scratchB, scratchN);
|
|
89
|
+
return { T: Array.from(scratchT), B: Array.from(scratchB), N: Array.from(scratchN), null: isNull };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Encode a tangent frame back to four UNorm angles.
|
|
94
|
+
*
|
|
95
|
+
* @param {ArrayLike<number>} T Unit tangent.
|
|
96
|
+
* @param {ArrayLike<number>} B Unit binormal.
|
|
97
|
+
* @param {ArrayLike<number>} [N] Unit normal; only handedness is used.
|
|
98
|
+
* @returns {number[]} Four UNorm values in `[0, 1]`.
|
|
99
|
+
*/
|
|
100
|
+
export function encodeTangentFrame(T, B, N)
|
|
101
|
+
{
|
|
102
|
+
let a0 = Math.atan2(T[1], T[0]),
|
|
103
|
+
a1 = Math.acos(num.clamp(T[2], -1, 1));
|
|
104
|
+
|
|
105
|
+
const
|
|
106
|
+
a2 = Math.atan2(B[1], B[0]),
|
|
107
|
+
a3 = Math.acos(num.clamp(B[2], -1, 1));
|
|
108
|
+
|
|
109
|
+
if (N && vec3.dot(N, vec3.cross([ 0, 0, 0 ], T, B)) < 0)
|
|
110
|
+
{
|
|
111
|
+
a1 = -a1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const enc = angle => num.clamp((angle + PI) / TAU, 0, 1);
|
|
115
|
+
return [ enc(a0), enc(a1), enc(a2), enc(a3) ];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function vertexCount(mesh)
|
|
119
|
+
{
|
|
120
|
+
const p = mesh.vertex && mesh.vertex.position;
|
|
121
|
+
return p ? (p.length / 3) | 0 : 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Is this shared JSON mesh's tangent frame packed?
|
|
126
|
+
*
|
|
127
|
+
* @param {object} mesh Shared JSON mesh.
|
|
128
|
+
* @returns {boolean} Whether the mesh has packed tangent frames.
|
|
129
|
+
*/
|
|
130
|
+
export function isPacked(mesh)
|
|
131
|
+
{
|
|
132
|
+
const v = mesh.vertex;
|
|
133
|
+
if (!v || !v.tangent || !v.tangent.length) return false;
|
|
134
|
+
|
|
135
|
+
const n = vertexCount(mesh);
|
|
136
|
+
if (!n) return false;
|
|
137
|
+
|
|
138
|
+
const
|
|
139
|
+
comps = v.tangent.length / n,
|
|
140
|
+
empty = value => !value || value.length === 0;
|
|
141
|
+
|
|
142
|
+
return comps === 4 && empty(v.normal) && empty(v.binormal);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Unpack a packed shared JSON mesh in place.
|
|
147
|
+
*
|
|
148
|
+
* @param {object} mesh Shared JSON mesh to mutate.
|
|
149
|
+
* @returns {boolean} Whether unpacking happened.
|
|
150
|
+
*/
|
|
151
|
+
export function unpackMeshTangents(mesh)
|
|
152
|
+
{
|
|
153
|
+
if (!isPacked(mesh)) return false;
|
|
154
|
+
|
|
155
|
+
const
|
|
156
|
+
v = mesh.vertex,
|
|
157
|
+
n = vertexCount(mesh),
|
|
158
|
+
src = v.tangent,
|
|
159
|
+
normal = new Array(n * 3),
|
|
160
|
+
tangent = new Array(n * 3),
|
|
161
|
+
binormal = new Array(n * 3);
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < n; i++)
|
|
164
|
+
{
|
|
165
|
+
const
|
|
166
|
+
s = i * 4,
|
|
167
|
+
o = i * 3,
|
|
168
|
+
isNull = decodeTangentFrameInto(src[s], src[s + 1], src[s + 2], src[s + 3], scratchT, scratchB, scratchN);
|
|
169
|
+
|
|
170
|
+
if (isNull)
|
|
171
|
+
{
|
|
172
|
+
normal[o] = normal[o + 1] = normal[o + 2] = 0;
|
|
173
|
+
tangent[o] = tangent[o + 1] = tangent[o + 2] = 0;
|
|
174
|
+
binormal[o] = binormal[o + 1] = binormal[o + 2] = 0;
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
{
|
|
178
|
+
normal[o] = scratchN[0];
|
|
179
|
+
normal[o + 1] = scratchN[1];
|
|
180
|
+
normal[o + 2] = scratchN[2];
|
|
181
|
+
tangent[o] = scratchT[0];
|
|
182
|
+
tangent[o + 1] = scratchT[1];
|
|
183
|
+
tangent[o + 2] = scratchT[2];
|
|
184
|
+
binormal[o] = scratchB[0];
|
|
185
|
+
binormal[o + 1] = scratchB[1];
|
|
186
|
+
binormal[o + 2] = scratchB[2];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
v.normal = normal;
|
|
191
|
+
v.tangent = tangent;
|
|
192
|
+
v.binormal = binormal;
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Pack explicit tangent frames into GR2-style four-component tangent data.
|
|
198
|
+
*
|
|
199
|
+
* @param {ArrayLike<number>} normals Flat xyz normals.
|
|
200
|
+
* @param {ArrayLike<number>} tangents Flat xyz tangents.
|
|
201
|
+
* @param {ArrayLike<number>} binormals Flat xyz binormals.
|
|
202
|
+
* @returns {number[]} Flat xyzw packed tangent-frame values.
|
|
203
|
+
*/
|
|
204
|
+
export function packTangentFrames(normals, tangents, binormals)
|
|
205
|
+
{
|
|
206
|
+
if (normals.length !== tangents.length || normals.length !== binormals.length)
|
|
207
|
+
{
|
|
208
|
+
throw new Error("packTangentFrames requires normal, tangent, and binormal channels with matching lengths");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const packed = new Array((normals.length / 3) * 4);
|
|
212
|
+
for (let i = 0, o = 0; i < normals.length; i += 3, o += 4)
|
|
213
|
+
{
|
|
214
|
+
const
|
|
215
|
+
normal = vec3.normalize([ 0, 0, 0 ], [ normals[i], normals[i + 1], normals[i + 2] ]),
|
|
216
|
+
tangent = vec3.normalize([ 0, 0, 0 ], [ tangents[i], tangents[i + 1], tangents[i + 2] ]),
|
|
217
|
+
binormal = vec3.normalize([ 0, 0, 0 ], [ binormals[i], binormals[i + 1], binormals[i + 2] ]);
|
|
218
|
+
|
|
219
|
+
if (vec3.length(normal) <= num.EPSILON || vec3.length(tangent) <= num.EPSILON || vec3.length(binormal) <= num.EPSILON)
|
|
220
|
+
{
|
|
221
|
+
packed[o] = NULL_TANGENT_UNORM[0];
|
|
222
|
+
packed[o + 1] = NULL_TANGENT_UNORM[1];
|
|
223
|
+
packed[o + 2] = NULL_TANGENT_UNORM[2];
|
|
224
|
+
packed[o + 3] = NULL_TANGENT_UNORM[3];
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const encoded = encodeTangentFrame(tangent, binormal, normal);
|
|
229
|
+
packed[o] = encoded[0];
|
|
230
|
+
packed[o + 1] = encoded[1];
|
|
231
|
+
packed[o + 2] = encoded[2];
|
|
232
|
+
packed[o + 3] = encoded[3];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return packed;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const tangent = Object.freeze({
|
|
239
|
+
TAU: TANGENT_TAU,
|
|
240
|
+
PI: TANGENT_PI,
|
|
241
|
+
NULL_TANGENT_UNORM,
|
|
242
|
+
isNull: isNullTangent,
|
|
243
|
+
isNullTangent,
|
|
244
|
+
decode: decodeTangentFrame,
|
|
245
|
+
decodeTangentFrame,
|
|
246
|
+
pack: encodeTangentFrame,
|
|
247
|
+
encode: encodeTangentFrame,
|
|
248
|
+
encodeTangentFrame,
|
|
249
|
+
packTangentFrames,
|
|
250
|
+
unpack: unpackMeshTangents,
|
|
251
|
+
unpackMeshTangents,
|
|
252
|
+
isPacked,
|
|
253
|
+
generateNormals,
|
|
254
|
+
generateTangents,
|
|
255
|
+
generateBiNormals
|
|
256
|
+
});
|