@adatechnology/game-characters-three 0.2.1 → 0.3.1
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/README.md +31 -0
- package/dist/index.d.ts +341 -10
- package/dist/index.global.js +3685 -140
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1296 -15
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
package/dist/index.global.js
CHANGED
|
@@ -21,6 +21,7 @@ var GameCharactersThree = (() => {
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
|
+
AVATAR_AREA_ID: () => AVATAR_AREA_ID,
|
|
24
25
|
BAKE_NEUTRAL_POSE: () => BAKE_NEUTRAL_POSE,
|
|
25
26
|
BODY_CHUNK: () => BODY_CHUNK,
|
|
26
27
|
BODY_MATERIAL_DEF: () => BODY_MATERIAL_DEF,
|
|
@@ -30,6 +31,10 @@ var GameCharactersThree = (() => {
|
|
|
30
31
|
CHARACTER_SOUND_BANK: () => CHARACTER_SOUND_BANK,
|
|
31
32
|
EXTRAS: () => EXTRAS,
|
|
32
33
|
FACE_FEATURES: () => FACE_FEATURES,
|
|
34
|
+
FIRST_PERSON_BOB: () => FIRST_PERSON_BOB,
|
|
35
|
+
FIRST_PERSON_COLORS: () => FIRST_PERSON_COLORS,
|
|
36
|
+
FIRST_PERSON_RIG: () => FIRST_PERSON_RIG,
|
|
37
|
+
FIRST_PERSON_STANCES: () => FIRST_PERSON_STANCES,
|
|
33
38
|
HAND_ITEMS: () => HAND_ITEMS,
|
|
34
39
|
HATS: () => HATS,
|
|
35
40
|
HIT_PRESETS: () => HIT_PRESETS,
|
|
@@ -51,6 +56,7 @@ var GameCharactersThree = (() => {
|
|
|
51
56
|
blobRadii: () => blobRadii,
|
|
52
57
|
bloodLookOfAge: () => bloodLookOfAge,
|
|
53
58
|
buildInstancedBody: () => buildInstancedBody,
|
|
59
|
+
createAvatarTestModule: () => createAvatarTestModule,
|
|
54
60
|
createBestBuilder: () => createBestBuilder,
|
|
55
61
|
createBodyBatch: () => createBodyBatch,
|
|
56
62
|
createBodyChunk: () => createBodyChunk,
|
|
@@ -58,12 +64,14 @@ var GameCharactersThree = (() => {
|
|
|
58
64
|
createCharacterSound: () => createCharacterSound,
|
|
59
65
|
createCharactersTestModule: () => createCharactersTestModule,
|
|
60
66
|
createEye: () => createEye,
|
|
67
|
+
createFirstPersonArms: () => createFirstPersonArms,
|
|
61
68
|
createFluidsBridge: () => createFluidsBridge,
|
|
62
69
|
createFlyingLimbs: () => createFlyingLimbs,
|
|
63
70
|
createFlyingPieces: () => createFlyingPieces,
|
|
64
71
|
createGlowTexture: () => createGlowTexture,
|
|
65
72
|
createPendulum: () => createPendulum,
|
|
66
73
|
createPersonBuilder: () => createPersonBuilder,
|
|
74
|
+
createPlayerAvatar: () => createPlayerAvatar,
|
|
67
75
|
createRigAnimator: () => createRigAnimator,
|
|
68
76
|
createVariantBatches: () => createVariantBatches,
|
|
69
77
|
createWoundMaterials: () => createWoundMaterials,
|
|
@@ -78,8 +86,10 @@ var GameCharactersThree = (() => {
|
|
|
78
86
|
makeStump: () => makeStump,
|
|
79
87
|
registerBodyMaterial: () => registerBodyMaterial,
|
|
80
88
|
ringWound: () => ringWound,
|
|
89
|
+
solveTwoBone: () => solveTwoBone,
|
|
81
90
|
starGeometry: () => starGeometry,
|
|
82
91
|
stepPendulum: () => stepPendulum,
|
|
92
|
+
toRoomAvatar: () => toRoomAvatar,
|
|
83
93
|
woundBodyOf: () => woundBodyOf,
|
|
84
94
|
woundDecal: () => woundDecal
|
|
85
95
|
});
|
|
@@ -160,7 +170,1540 @@ var GameCharactersThree = (() => {
|
|
|
160
170
|
var HIP_PIVOT = "vec3(0.,0.95,0.)";
|
|
161
171
|
var createBodyChunk = ({ hipHeight = 0.95 } = {}) => hipHeight === 0.95 ? BODY_CHUNK : BODY_CHUNK.replaceAll(HIP_PIVOT, `vec3(0.,${hipHeight},0.)`);
|
|
162
172
|
|
|
173
|
+
// ../game-shapes/dist/chunk-R4RY23RH.js
|
|
174
|
+
function canonicalJson(value) {
|
|
175
|
+
if (value === null) return "null";
|
|
176
|
+
if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
|
|
177
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
178
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
179
|
+
if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
|
|
180
|
+
if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
|
|
181
|
+
if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
|
|
182
|
+
if (typeof value === "object") {
|
|
183
|
+
const record = value;
|
|
184
|
+
const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
|
|
185
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
186
|
+
}
|
|
187
|
+
return "null";
|
|
188
|
+
}
|
|
189
|
+
function needsUint32(array) {
|
|
190
|
+
for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
194
|
+
function pack(type, parameters, a) {
|
|
195
|
+
return {
|
|
196
|
+
type,
|
|
197
|
+
parameters,
|
|
198
|
+
positions: new Float32Array(a.vertices),
|
|
199
|
+
normals: new Float32Array(a.normals),
|
|
200
|
+
uvs: new Float32Array(a.uvs),
|
|
201
|
+
...a.indices ? { indices: indexArray(a.indices) } : {},
|
|
202
|
+
groups: a.groups
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
var normalize3 = (x, y, z) => {
|
|
206
|
+
const length = Math.sqrt(x * x + y * y + z * z);
|
|
207
|
+
const s = 1 / (length || 1);
|
|
208
|
+
return [x * s, y * s, z * s];
|
|
209
|
+
};
|
|
210
|
+
function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
|
|
211
|
+
const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
|
|
212
|
+
widthSegments = Math.floor(widthSegments);
|
|
213
|
+
heightSegments = Math.floor(heightSegments);
|
|
214
|
+
depthSegments = Math.floor(depthSegments);
|
|
215
|
+
const indices = [];
|
|
216
|
+
const vertices = [];
|
|
217
|
+
const normals = [];
|
|
218
|
+
const uvs = [];
|
|
219
|
+
const groups = [];
|
|
220
|
+
let numberOfVertices = 0;
|
|
221
|
+
let groupStart = 0;
|
|
222
|
+
const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
|
|
223
|
+
const segmentWidth = width2 / gridX;
|
|
224
|
+
const segmentHeight = height2 / gridY;
|
|
225
|
+
const widthHalf = width2 / 2;
|
|
226
|
+
const heightHalf = height2 / 2;
|
|
227
|
+
const depthHalf = depth2 / 2;
|
|
228
|
+
const gridX1 = gridX + 1;
|
|
229
|
+
const gridY1 = gridY + 1;
|
|
230
|
+
let vertexCounter = 0;
|
|
231
|
+
let groupCount = 0;
|
|
232
|
+
const vector = { x: 0, y: 0, z: 0 };
|
|
233
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
234
|
+
const y = iy * segmentHeight - heightHalf;
|
|
235
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
236
|
+
const x = ix * segmentWidth - widthHalf;
|
|
237
|
+
vector[u] = x * udir;
|
|
238
|
+
vector[v] = y * vdir;
|
|
239
|
+
vector[w] = depthHalf;
|
|
240
|
+
vertices.push(vector.x, vector.y, vector.z);
|
|
241
|
+
vector[u] = 0;
|
|
242
|
+
vector[v] = 0;
|
|
243
|
+
vector[w] = depth2 > 0 ? 1 : -1;
|
|
244
|
+
normals.push(vector.x, vector.y, vector.z);
|
|
245
|
+
uvs.push(ix / gridX);
|
|
246
|
+
uvs.push(1 - iy / gridY);
|
|
247
|
+
vertexCounter += 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
251
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
252
|
+
const a = numberOfVertices + ix + gridX1 * iy;
|
|
253
|
+
const b = numberOfVertices + ix + gridX1 * (iy + 1);
|
|
254
|
+
const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
|
|
255
|
+
const d = numberOfVertices + (ix + 1) + gridX1 * iy;
|
|
256
|
+
indices.push(a, b, d);
|
|
257
|
+
indices.push(b, c, d);
|
|
258
|
+
groupCount += 6;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex });
|
|
262
|
+
groupStart += groupCount;
|
|
263
|
+
numberOfVertices += vertexCounter;
|
|
264
|
+
};
|
|
265
|
+
buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
|
|
266
|
+
buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
|
|
267
|
+
buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
|
|
268
|
+
buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
|
|
269
|
+
buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
|
|
270
|
+
buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
|
|
271
|
+
return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
|
|
272
|
+
}
|
|
273
|
+
function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
|
|
274
|
+
radialSegments = Math.floor(radialSegments);
|
|
275
|
+
heightSegments = Math.floor(heightSegments);
|
|
276
|
+
const indices = [];
|
|
277
|
+
const vertices = [];
|
|
278
|
+
const normals = [];
|
|
279
|
+
const uvs = [];
|
|
280
|
+
const groups = [];
|
|
281
|
+
let index = 0;
|
|
282
|
+
const indexArray2 = [];
|
|
283
|
+
const halfHeight = height / 2;
|
|
284
|
+
let groupStart = 0;
|
|
285
|
+
const generateTorso = () => {
|
|
286
|
+
let groupCount = 0;
|
|
287
|
+
const slope = (radiusBottom - radiusTop) / height;
|
|
288
|
+
for (let y = 0; y <= heightSegments; y++) {
|
|
289
|
+
const indexRow = [];
|
|
290
|
+
const v = y / heightSegments;
|
|
291
|
+
const radius = v * (radiusBottom - radiusTop) + radiusTop;
|
|
292
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
293
|
+
const u = x / radialSegments;
|
|
294
|
+
const theta = u * thetaLength + thetaStart;
|
|
295
|
+
const sinTheta = Math.sin(theta);
|
|
296
|
+
const cosTheta = Math.cos(theta);
|
|
297
|
+
vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
|
|
298
|
+
const n = normalize3(sinTheta, slope, cosTheta);
|
|
299
|
+
normals.push(n[0], n[1], n[2]);
|
|
300
|
+
uvs.push(u, 1 - v);
|
|
301
|
+
indexRow.push(index++);
|
|
302
|
+
}
|
|
303
|
+
indexArray2.push(indexRow);
|
|
304
|
+
}
|
|
305
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
306
|
+
for (let y = 0; y < heightSegments; y++) {
|
|
307
|
+
const a = indexArray2[y][x];
|
|
308
|
+
const b = indexArray2[y + 1][x];
|
|
309
|
+
const c = indexArray2[y + 1][x + 1];
|
|
310
|
+
const d = indexArray2[y][x + 1];
|
|
311
|
+
indices.push(a, b, d);
|
|
312
|
+
indices.push(b, c, d);
|
|
313
|
+
groupCount += 6;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
|
|
317
|
+
groupStart += groupCount;
|
|
318
|
+
};
|
|
319
|
+
const generateCap = (top) => {
|
|
320
|
+
const centerIndexStart = index;
|
|
321
|
+
let groupCount = 0;
|
|
322
|
+
const radius = top === true ? radiusTop : radiusBottom;
|
|
323
|
+
const sign2 = top === true ? 1 : -1;
|
|
324
|
+
for (let x = 1; x <= radialSegments; x++) {
|
|
325
|
+
vertices.push(0, halfHeight * sign2, 0);
|
|
326
|
+
normals.push(0, sign2, 0);
|
|
327
|
+
uvs.push(0.5, 0.5);
|
|
328
|
+
index++;
|
|
329
|
+
}
|
|
330
|
+
const centerIndexEnd = index;
|
|
331
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
332
|
+
const u = x / radialSegments;
|
|
333
|
+
const theta = u * thetaLength + thetaStart;
|
|
334
|
+
const cosTheta = Math.cos(theta);
|
|
335
|
+
const sinTheta = Math.sin(theta);
|
|
336
|
+
vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
|
|
337
|
+
normals.push(0, sign2, 0);
|
|
338
|
+
uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
|
|
339
|
+
index++;
|
|
340
|
+
}
|
|
341
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
342
|
+
const c = centerIndexStart + x;
|
|
343
|
+
const i = centerIndexEnd + x;
|
|
344
|
+
if (top === true) indices.push(i, i + 1, c);
|
|
345
|
+
else indices.push(i + 1, i, c);
|
|
346
|
+
groupCount += 3;
|
|
347
|
+
}
|
|
348
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
|
|
349
|
+
groupStart += groupCount;
|
|
350
|
+
};
|
|
351
|
+
generateTorso();
|
|
352
|
+
if (openEnded === false) {
|
|
353
|
+
if (radiusTop > 0) generateCap(true);
|
|
354
|
+
if (radiusBottom > 0) generateCap(false);
|
|
355
|
+
}
|
|
356
|
+
return { vertices, normals, uvs, indices, groups };
|
|
357
|
+
}
|
|
358
|
+
function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
359
|
+
const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
360
|
+
return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
361
|
+
}
|
|
362
|
+
function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
363
|
+
const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
364
|
+
return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
365
|
+
}
|
|
366
|
+
function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
|
|
367
|
+
const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
|
|
368
|
+
widthSegments = Math.max(3, Math.floor(widthSegments));
|
|
369
|
+
heightSegments = Math.max(2, Math.floor(heightSegments));
|
|
370
|
+
const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
|
|
371
|
+
let index = 0;
|
|
372
|
+
const grid = [];
|
|
373
|
+
const indices = [];
|
|
374
|
+
const vertices = [];
|
|
375
|
+
const normals = [];
|
|
376
|
+
const uvs = [];
|
|
377
|
+
for (let iy = 0; iy <= heightSegments; iy++) {
|
|
378
|
+
const verticesRow = [];
|
|
379
|
+
const v = iy / heightSegments;
|
|
380
|
+
let uOffset = 0;
|
|
381
|
+
if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
|
|
382
|
+
else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
|
|
383
|
+
for (let ix = 0; ix <= widthSegments; ix++) {
|
|
384
|
+
const u = ix / widthSegments;
|
|
385
|
+
const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
386
|
+
const y = radius * Math.cos(thetaStart + v * thetaLength);
|
|
387
|
+
const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
388
|
+
vertices.push(x, y, z);
|
|
389
|
+
const n = normalize3(x, y, z);
|
|
390
|
+
normals.push(n[0], n[1], n[2]);
|
|
391
|
+
uvs.push(u + uOffset, 1 - v);
|
|
392
|
+
verticesRow.push(index++);
|
|
393
|
+
}
|
|
394
|
+
grid.push(verticesRow);
|
|
395
|
+
}
|
|
396
|
+
for (let iy = 0; iy < heightSegments; iy++) {
|
|
397
|
+
for (let ix = 0; ix < widthSegments; ix++) {
|
|
398
|
+
const a = grid[iy][ix + 1];
|
|
399
|
+
const b = grid[iy][ix];
|
|
400
|
+
const c = grid[iy + 1][ix];
|
|
401
|
+
const d = grid[iy + 1][ix + 1];
|
|
402
|
+
if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
|
|
403
|
+
if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
407
|
+
}
|
|
408
|
+
function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
|
|
409
|
+
const parameters = { width, height, widthSegments, heightSegments };
|
|
410
|
+
const widthHalf = width / 2;
|
|
411
|
+
const heightHalf = height / 2;
|
|
412
|
+
const gridX = Math.floor(widthSegments);
|
|
413
|
+
const gridY = Math.floor(heightSegments);
|
|
414
|
+
const gridX1 = gridX + 1;
|
|
415
|
+
const gridY1 = gridY + 1;
|
|
416
|
+
const segmentWidth = width / gridX;
|
|
417
|
+
const segmentHeight = height / gridY;
|
|
418
|
+
const indices = [];
|
|
419
|
+
const vertices = [];
|
|
420
|
+
const normals = [];
|
|
421
|
+
const uvs = [];
|
|
422
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
423
|
+
const y = iy * segmentHeight - heightHalf;
|
|
424
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
425
|
+
const x = ix * segmentWidth - widthHalf;
|
|
426
|
+
vertices.push(x, -y, 0);
|
|
427
|
+
normals.push(0, 0, 1);
|
|
428
|
+
uvs.push(ix / gridX);
|
|
429
|
+
uvs.push(1 - iy / gridY);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
433
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
434
|
+
const a = ix + gridX1 * iy;
|
|
435
|
+
const b = ix + gridX1 * (iy + 1);
|
|
436
|
+
const c = ix + 1 + gridX1 * (iy + 1);
|
|
437
|
+
const d = ix + 1 + gridX1 * iy;
|
|
438
|
+
indices.push(a, b, d);
|
|
439
|
+
indices.push(b, c, d);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
443
|
+
}
|
|
444
|
+
function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
445
|
+
const parameters = { radius, segments, thetaStart, thetaLength };
|
|
446
|
+
segments = Math.max(3, segments);
|
|
447
|
+
const indices = [];
|
|
448
|
+
const vertices = [];
|
|
449
|
+
const normals = [];
|
|
450
|
+
const uvs = [];
|
|
451
|
+
vertices.push(0, 0, 0);
|
|
452
|
+
normals.push(0, 0, 1);
|
|
453
|
+
uvs.push(0.5, 0.5);
|
|
454
|
+
for (let s = 0, i = 3; s <= segments; s++, i += 3) {
|
|
455
|
+
const segment = thetaStart + s / segments * thetaLength;
|
|
456
|
+
vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
|
|
457
|
+
normals.push(0, 0, 1);
|
|
458
|
+
uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
|
|
459
|
+
}
|
|
460
|
+
for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
|
|
461
|
+
return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
462
|
+
}
|
|
463
|
+
function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
464
|
+
const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
|
|
465
|
+
thetaSegments = Math.max(3, thetaSegments);
|
|
466
|
+
phiSegments = Math.max(1, phiSegments);
|
|
467
|
+
const indices = [];
|
|
468
|
+
const vertices = [];
|
|
469
|
+
const normals = [];
|
|
470
|
+
const uvs = [];
|
|
471
|
+
let radius = innerRadius;
|
|
472
|
+
const radiusStep = (outerRadius - innerRadius) / phiSegments;
|
|
473
|
+
for (let j = 0; j <= phiSegments; j++) {
|
|
474
|
+
for (let i = 0; i <= thetaSegments; i++) {
|
|
475
|
+
const segment = thetaStart + i / thetaSegments * thetaLength;
|
|
476
|
+
const x = radius * Math.cos(segment);
|
|
477
|
+
const y = radius * Math.sin(segment);
|
|
478
|
+
vertices.push(x, y, 0);
|
|
479
|
+
normals.push(0, 0, 1);
|
|
480
|
+
uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
|
|
481
|
+
}
|
|
482
|
+
radius += radiusStep;
|
|
483
|
+
}
|
|
484
|
+
for (let j = 0; j < phiSegments; j++) {
|
|
485
|
+
const thetaSegmentLevel = j * (thetaSegments + 1);
|
|
486
|
+
for (let i = 0; i < thetaSegments; i++) {
|
|
487
|
+
const segment = i + thetaSegmentLevel;
|
|
488
|
+
const a = segment;
|
|
489
|
+
const b = segment + thetaSegments + 1;
|
|
490
|
+
const c = segment + thetaSegments + 2;
|
|
491
|
+
const d = segment + 1;
|
|
492
|
+
indices.push(a, b, d);
|
|
493
|
+
indices.push(b, c, d);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
497
|
+
}
|
|
498
|
+
function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
|
|
499
|
+
const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
|
|
500
|
+
segments = Math.floor(segments);
|
|
501
|
+
phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
|
|
502
|
+
const indices = [];
|
|
503
|
+
const vertices = [];
|
|
504
|
+
const uvs = [];
|
|
505
|
+
const initNormals = [];
|
|
506
|
+
const normals = [];
|
|
507
|
+
const inverseSegments = 1 / segments;
|
|
508
|
+
const normal = { x: 0, y: 0, z: 0 };
|
|
509
|
+
const curNormal = { x: 0, y: 0, z: 0 };
|
|
510
|
+
const prevNormal = { x: 0, y: 0, z: 0 };
|
|
511
|
+
let dx = 0;
|
|
512
|
+
let dy = 0;
|
|
513
|
+
const last = points.length - 1;
|
|
514
|
+
const px = (j) => points[j][0];
|
|
515
|
+
const py = (j) => points[j][1];
|
|
516
|
+
const setNormalized = () => {
|
|
517
|
+
const n = normalize3(normal.x, normal.y, normal.z);
|
|
518
|
+
normal.x = n[0];
|
|
519
|
+
normal.y = n[1];
|
|
520
|
+
normal.z = n[2];
|
|
521
|
+
};
|
|
522
|
+
for (let j = 0; j <= last; j++) {
|
|
523
|
+
if (j === 0) {
|
|
524
|
+
dx = px(j + 1) - px(j);
|
|
525
|
+
dy = py(j + 1) - py(j);
|
|
526
|
+
normal.x = dy * 1;
|
|
527
|
+
normal.y = -dx;
|
|
528
|
+
normal.z = dy * 0;
|
|
529
|
+
prevNormal.x = normal.x;
|
|
530
|
+
prevNormal.y = normal.y;
|
|
531
|
+
prevNormal.z = normal.z;
|
|
532
|
+
setNormalized();
|
|
533
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
534
|
+
} else if (j === last) {
|
|
535
|
+
initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
|
|
536
|
+
} else {
|
|
537
|
+
dx = px(j + 1) - px(j);
|
|
538
|
+
dy = py(j + 1) - py(j);
|
|
539
|
+
normal.x = dy * 1;
|
|
540
|
+
normal.y = -dx;
|
|
541
|
+
normal.z = dy * 0;
|
|
542
|
+
curNormal.x = normal.x;
|
|
543
|
+
curNormal.y = normal.y;
|
|
544
|
+
curNormal.z = normal.z;
|
|
545
|
+
normal.x += prevNormal.x;
|
|
546
|
+
normal.y += prevNormal.y;
|
|
547
|
+
normal.z += prevNormal.z;
|
|
548
|
+
setNormalized();
|
|
549
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
550
|
+
prevNormal.x = curNormal.x;
|
|
551
|
+
prevNormal.y = curNormal.y;
|
|
552
|
+
prevNormal.z = curNormal.z;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
for (let i = 0; i <= segments; i++) {
|
|
556
|
+
const phi = phiStart + i * inverseSegments * phiLength;
|
|
557
|
+
const sin = Math.sin(phi);
|
|
558
|
+
const cos = Math.cos(phi);
|
|
559
|
+
for (let j = 0; j <= last; j++) {
|
|
560
|
+
vertices.push(px(j) * sin, py(j), px(j) * cos);
|
|
561
|
+
uvs.push(i / segments, j / (points.length - 1));
|
|
562
|
+
const x = initNormals[3 * j + 0] * sin;
|
|
563
|
+
const y = initNormals[3 * j + 1];
|
|
564
|
+
const z = initNormals[3 * j + 0] * cos;
|
|
565
|
+
normals.push(x, y, z);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
for (let i = 0; i < segments; i++) {
|
|
569
|
+
for (let j = 0; j < points.length - 1; j++) {
|
|
570
|
+
const base = j + i * points.length;
|
|
571
|
+
const a = base;
|
|
572
|
+
const b = base + points.length;
|
|
573
|
+
const c = base + points.length + 1;
|
|
574
|
+
const d = base + 1;
|
|
575
|
+
indices.push(a, b, d);
|
|
576
|
+
indices.push(c, d, b);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
580
|
+
}
|
|
581
|
+
function torus(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
|
|
582
|
+
const parameters = { radius, tube, radialSegments, tubularSegments, arc };
|
|
583
|
+
radialSegments = Math.floor(radialSegments);
|
|
584
|
+
tubularSegments = Math.floor(tubularSegments);
|
|
585
|
+
const indices = [];
|
|
586
|
+
const vertices = [];
|
|
587
|
+
const normals = [];
|
|
588
|
+
const uvs = [];
|
|
589
|
+
for (let j = 0; j <= radialSegments; j++) {
|
|
590
|
+
for (let i = 0; i <= tubularSegments; i++) {
|
|
591
|
+
const u = i / tubularSegments * arc;
|
|
592
|
+
const v = j / radialSegments * Math.PI * 2;
|
|
593
|
+
const x = (radius + tube * Math.cos(v)) * Math.cos(u);
|
|
594
|
+
const y = (radius + tube * Math.cos(v)) * Math.sin(u);
|
|
595
|
+
const z = tube * Math.sin(v);
|
|
596
|
+
vertices.push(x, y, z);
|
|
597
|
+
const cx = radius * Math.cos(u);
|
|
598
|
+
const cy = radius * Math.sin(u);
|
|
599
|
+
const n = normalize3(x - cx, y - cy, z - 0);
|
|
600
|
+
normals.push(n[0], n[1], n[2]);
|
|
601
|
+
uvs.push(i / tubularSegments);
|
|
602
|
+
uvs.push(j / radialSegments);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
for (let j = 1; j <= radialSegments; j++) {
|
|
606
|
+
for (let i = 1; i <= tubularSegments; i++) {
|
|
607
|
+
const a = (tubularSegments + 1) * j + i - 1;
|
|
608
|
+
const b = (tubularSegments + 1) * (j - 1) + i - 1;
|
|
609
|
+
const c = (tubularSegments + 1) * (j - 1) + i;
|
|
610
|
+
const d = (tubularSegments + 1) * j + i;
|
|
611
|
+
indices.push(a, b, d);
|
|
612
|
+
indices.push(b, c, d);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
616
|
+
}
|
|
617
|
+
function difference(size, subtract) {
|
|
618
|
+
const [w, h, d] = size;
|
|
619
|
+
const hw = w / 2;
|
|
620
|
+
const hh = h / 2;
|
|
621
|
+
const hd = d / 2;
|
|
622
|
+
const holes = subtract.map((hole) => ({ x0: Math.max(-hw, Math.min(hole.min[0], hole.max[0])), x1: Math.min(hw, Math.max(hole.min[0], hole.max[0])), y0: Math.max(-hh, Math.min(hole.min[1], hole.max[1])), y1: Math.min(hh, Math.max(hole.min[1], hole.max[1])) })).filter((hole) => hole.x1 > hole.x0 && hole.y1 > hole.y0);
|
|
623
|
+
const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
|
|
624
|
+
const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
|
|
625
|
+
const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
|
|
626
|
+
const nx = xs.length - 1;
|
|
627
|
+
const ny = ys.length - 1;
|
|
628
|
+
const solid = (i, j) => {
|
|
629
|
+
if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
|
|
630
|
+
const cx = (xs[i] + xs[i + 1]) / 2;
|
|
631
|
+
const cy = (ys[j] + ys[j + 1]) / 2;
|
|
632
|
+
return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
|
|
633
|
+
};
|
|
634
|
+
const positions = [];
|
|
635
|
+
const normals = [];
|
|
636
|
+
const uvs = [];
|
|
637
|
+
const indices = [];
|
|
638
|
+
const quad = (corners, normal, uv) => {
|
|
639
|
+
const base = positions.length / 3;
|
|
640
|
+
for (let k = 0; k < 4; k++) {
|
|
641
|
+
positions.push(...corners[k]);
|
|
642
|
+
normals.push(...normal);
|
|
643
|
+
uvs.push(...uv[k]);
|
|
644
|
+
}
|
|
645
|
+
indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
|
|
646
|
+
};
|
|
647
|
+
const fu = (x) => (x + hw) / w;
|
|
648
|
+
const fv = (y) => (y + hh) / h;
|
|
649
|
+
for (let j = 0; j < ny; j++) {
|
|
650
|
+
for (let i = 0; i < nx; i++) {
|
|
651
|
+
if (!solid(i, j)) continue;
|
|
652
|
+
const x0 = xs[i];
|
|
653
|
+
const x1 = xs[i + 1];
|
|
654
|
+
const y0 = ys[j];
|
|
655
|
+
const y1 = ys[j + 1];
|
|
656
|
+
quad([[x0, y0, hd], [x1, y0, hd], [x1, y1, hd], [x0, y1, hd]], [0, 0, 1], [[fu(x0), fv(y0)], [fu(x1), fv(y0)], [fu(x1), fv(y1)], [fu(x0), fv(y1)]]);
|
|
657
|
+
quad([[x1, y0, -hd], [x0, y0, -hd], [x0, y1, -hd], [x1, y1, -hd]], [0, 0, -1], [[1 - fu(x1), fv(y0)], [1 - fu(x0), fv(y0)], [1 - fu(x0), fv(y1)], [1 - fu(x1), fv(y1)]]);
|
|
658
|
+
const zu = (z) => (z + hd) / d;
|
|
659
|
+
if (!solid(i - 1, j)) quad([[x0, y0, -hd], [x0, y0, hd], [x0, y1, hd], [x0, y1, -hd]], [-1, 0, 0], [[zu(-hd), fv(y0)], [zu(hd), fv(y0)], [zu(hd), fv(y1)], [zu(-hd), fv(y1)]]);
|
|
660
|
+
if (!solid(i + 1, j)) quad([[x1, y0, hd], [x1, y0, -hd], [x1, y1, -hd], [x1, y1, hd]], [1, 0, 0], [[zu(hd), fv(y0)], [zu(-hd), fv(y0)], [zu(-hd), fv(y1)], [zu(hd), fv(y1)]]);
|
|
661
|
+
if (!solid(i, j - 1)) quad([[x0, y0, -hd], [x1, y0, -hd], [x1, y0, hd], [x0, y0, hd]], [0, -1, 0], [[fu(x0), zu(-hd)], [fu(x1), zu(-hd)], [fu(x1), zu(hd)], [fu(x0), zu(hd)]]);
|
|
662
|
+
if (!solid(i, j + 1)) quad([[x0, y1, hd], [x1, y1, hd], [x1, y1, -hd], [x0, y1, -hd]], [0, 1, 0], [[fu(x0), zu(hd)], [fu(x1), zu(hd)], [fu(x1), zu(-hd)], [fu(x0), zu(-hd)]]);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
type: "BufferGeometry",
|
|
667
|
+
parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
|
|
668
|
+
positions: new Float32Array(positions),
|
|
669
|
+
normals: new Float32Array(normals),
|
|
670
|
+
uvs: new Float32Array(uvs),
|
|
671
|
+
indices: indexArray(indices),
|
|
672
|
+
groups: []
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
var Earcut = {
|
|
676
|
+
triangulate: function(data, holeIndices, dim = 2) {
|
|
677
|
+
const hasHoles = holeIndices && holeIndices.length;
|
|
678
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
679
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
680
|
+
const triangles = [];
|
|
681
|
+
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
|
|
682
|
+
let minX, minY, maxX, maxY, x, y, invSize;
|
|
683
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
684
|
+
if (data.length > 80 * dim) {
|
|
685
|
+
minX = maxX = data[0];
|
|
686
|
+
minY = maxY = data[1];
|
|
687
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
688
|
+
x = data[i];
|
|
689
|
+
y = data[i + 1];
|
|
690
|
+
if (x < minX) minX = x;
|
|
691
|
+
if (y < minY) minY = y;
|
|
692
|
+
if (x > maxX) maxX = x;
|
|
693
|
+
if (y > maxY) maxY = y;
|
|
694
|
+
}
|
|
695
|
+
invSize = Math.max(maxX - minX, maxY - minY);
|
|
696
|
+
invSize = invSize !== 0 ? 32767 / invSize : 0;
|
|
697
|
+
}
|
|
698
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
|
|
699
|
+
return triangles;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
function linkedList(data, start, end, dim, clockwise) {
|
|
703
|
+
let i, last;
|
|
704
|
+
if (clockwise === signedArea(data, start, end, dim) > 0) {
|
|
705
|
+
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
706
|
+
} else {
|
|
707
|
+
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
708
|
+
}
|
|
709
|
+
if (last && equals(last, last.next)) {
|
|
710
|
+
removeNode(last);
|
|
711
|
+
last = last.next;
|
|
712
|
+
}
|
|
713
|
+
return last;
|
|
714
|
+
}
|
|
715
|
+
function filterPoints(start, end) {
|
|
716
|
+
if (!start) return start;
|
|
717
|
+
if (!end) end = start;
|
|
718
|
+
let p2 = start, again;
|
|
719
|
+
do {
|
|
720
|
+
again = false;
|
|
721
|
+
if (!p2.steiner && (equals(p2, p2.next) || area(p2.prev, p2, p2.next) === 0)) {
|
|
722
|
+
removeNode(p2);
|
|
723
|
+
p2 = end = p2.prev;
|
|
724
|
+
if (p2 === p2.next) break;
|
|
725
|
+
again = true;
|
|
726
|
+
} else {
|
|
727
|
+
p2 = p2.next;
|
|
728
|
+
}
|
|
729
|
+
} while (again || p2 !== end);
|
|
730
|
+
return end;
|
|
731
|
+
}
|
|
732
|
+
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
|
|
733
|
+
if (!ear) return;
|
|
734
|
+
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
|
|
735
|
+
let stop = ear, prev, next;
|
|
736
|
+
while (ear.prev !== ear.next) {
|
|
737
|
+
prev = ear.prev;
|
|
738
|
+
next = ear.next;
|
|
739
|
+
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
|
|
740
|
+
triangles.push(prev.i / dim | 0);
|
|
741
|
+
triangles.push(ear.i / dim | 0);
|
|
742
|
+
triangles.push(next.i / dim | 0);
|
|
743
|
+
removeNode(ear);
|
|
744
|
+
ear = next.next;
|
|
745
|
+
stop = next.next;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
ear = next;
|
|
749
|
+
if (ear === stop) {
|
|
750
|
+
if (!pass) {
|
|
751
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
|
|
752
|
+
} else if (pass === 1) {
|
|
753
|
+
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
|
|
754
|
+
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
|
|
755
|
+
} else if (pass === 2) {
|
|
756
|
+
splitEarcut(ear, triangles, dim, minX, minY, invSize);
|
|
757
|
+
}
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function isEar(ear) {
|
|
763
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
764
|
+
if (area(a, b, c) >= 0) return false;
|
|
765
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
766
|
+
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
|
|
767
|
+
let p2 = c.next;
|
|
768
|
+
while (p2 !== a) {
|
|
769
|
+
if (p2.x >= x0 && p2.x <= x1 && p2.y >= y0 && p2.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p2.x, p2.y) && area(p2.prev, p2, p2.next) >= 0) return false;
|
|
770
|
+
p2 = p2.next;
|
|
771
|
+
}
|
|
772
|
+
return true;
|
|
773
|
+
}
|
|
774
|
+
function isEarHashed(ear, minX, minY, invSize) {
|
|
775
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
776
|
+
if (area(a, b, c) >= 0) return false;
|
|
777
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
778
|
+
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
|
|
779
|
+
const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
|
|
780
|
+
let p2 = ear.prevZ, n = ear.nextZ;
|
|
781
|
+
while (p2 && p2.z >= minZ && n && n.z <= maxZ) {
|
|
782
|
+
if (p2.x >= x0 && p2.x <= x1 && p2.y >= y0 && p2.y <= y1 && p2 !== a && p2 !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p2.x, p2.y) && area(p2.prev, p2, p2.next) >= 0) return false;
|
|
783
|
+
p2 = p2.prevZ;
|
|
784
|
+
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
785
|
+
n = n.nextZ;
|
|
786
|
+
}
|
|
787
|
+
while (p2 && p2.z >= minZ) {
|
|
788
|
+
if (p2.x >= x0 && p2.x <= x1 && p2.y >= y0 && p2.y <= y1 && p2 !== a && p2 !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p2.x, p2.y) && area(p2.prev, p2, p2.next) >= 0) return false;
|
|
789
|
+
p2 = p2.prevZ;
|
|
790
|
+
}
|
|
791
|
+
while (n && n.z <= maxZ) {
|
|
792
|
+
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
793
|
+
n = n.nextZ;
|
|
794
|
+
}
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
function cureLocalIntersections(start, triangles, dim) {
|
|
798
|
+
let p2 = start;
|
|
799
|
+
do {
|
|
800
|
+
const a = p2.prev, b = p2.next.next;
|
|
801
|
+
if (!equals(a, b) && intersects(a, p2, p2.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
802
|
+
triangles.push(a.i / dim | 0);
|
|
803
|
+
triangles.push(p2.i / dim | 0);
|
|
804
|
+
triangles.push(b.i / dim | 0);
|
|
805
|
+
removeNode(p2);
|
|
806
|
+
removeNode(p2.next);
|
|
807
|
+
p2 = start = b;
|
|
808
|
+
}
|
|
809
|
+
p2 = p2.next;
|
|
810
|
+
} while (p2 !== start);
|
|
811
|
+
return filterPoints(p2);
|
|
812
|
+
}
|
|
813
|
+
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
|
|
814
|
+
let a = start;
|
|
815
|
+
do {
|
|
816
|
+
let b = a.next.next;
|
|
817
|
+
while (b !== a.prev) {
|
|
818
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
819
|
+
let c = splitPolygon(a, b);
|
|
820
|
+
a = filterPoints(a, a.next);
|
|
821
|
+
c = filterPoints(c, c.next);
|
|
822
|
+
earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
|
|
823
|
+
earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
b = b.next;
|
|
827
|
+
}
|
|
828
|
+
a = a.next;
|
|
829
|
+
} while (a !== start);
|
|
830
|
+
}
|
|
831
|
+
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
832
|
+
const queue = [];
|
|
833
|
+
let i, len, start, end, list2;
|
|
834
|
+
for (i = 0, len = holeIndices.length; i < len; i++) {
|
|
835
|
+
start = holeIndices[i] * dim;
|
|
836
|
+
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
837
|
+
list2 = linkedList(data, start, end, dim, false);
|
|
838
|
+
if (list2 === list2.next) list2.steiner = true;
|
|
839
|
+
queue.push(getLeftmost(list2));
|
|
840
|
+
}
|
|
841
|
+
queue.sort(compareX);
|
|
842
|
+
for (i = 0; i < queue.length; i++) {
|
|
843
|
+
outerNode = eliminateHole(queue[i], outerNode);
|
|
844
|
+
}
|
|
845
|
+
return outerNode;
|
|
846
|
+
}
|
|
847
|
+
function compareX(a, b) {
|
|
848
|
+
return a.x - b.x;
|
|
849
|
+
}
|
|
850
|
+
function eliminateHole(hole, outerNode) {
|
|
851
|
+
const bridge = findHoleBridge(hole, outerNode);
|
|
852
|
+
if (!bridge) {
|
|
853
|
+
return outerNode;
|
|
854
|
+
}
|
|
855
|
+
const bridgeReverse = splitPolygon(bridge, hole);
|
|
856
|
+
filterPoints(bridgeReverse, bridgeReverse.next);
|
|
857
|
+
return filterPoints(bridge, bridge.next);
|
|
858
|
+
}
|
|
859
|
+
function findHoleBridge(hole, outerNode) {
|
|
860
|
+
let p2 = outerNode, qx = -Infinity, m;
|
|
861
|
+
const hx = hole.x, hy = hole.y;
|
|
862
|
+
do {
|
|
863
|
+
if (hy <= p2.y && hy >= p2.next.y && p2.next.y !== p2.y) {
|
|
864
|
+
const x = p2.x + (hy - p2.y) * (p2.next.x - p2.x) / (p2.next.y - p2.y);
|
|
865
|
+
if (x <= hx && x > qx) {
|
|
866
|
+
qx = x;
|
|
867
|
+
m = p2.x < p2.next.x ? p2 : p2.next;
|
|
868
|
+
if (x === hx) return m;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
p2 = p2.next;
|
|
872
|
+
} while (p2 !== outerNode);
|
|
873
|
+
if (!m) return null;
|
|
874
|
+
const stop = m, mx = m.x, my = m.y;
|
|
875
|
+
let tanMin = Infinity, tan;
|
|
876
|
+
p2 = m;
|
|
877
|
+
do {
|
|
878
|
+
if (hx >= p2.x && p2.x >= mx && hx !== p2.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p2.x, p2.y)) {
|
|
879
|
+
tan = Math.abs(hy - p2.y) / (hx - p2.x);
|
|
880
|
+
if (locallyInside(p2, hole) && (tan < tanMin || tan === tanMin && (p2.x > m.x || p2.x === m.x && sectorContainsSector(m, p2)))) {
|
|
881
|
+
m = p2;
|
|
882
|
+
tanMin = tan;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
p2 = p2.next;
|
|
886
|
+
} while (p2 !== stop);
|
|
887
|
+
return m;
|
|
888
|
+
}
|
|
889
|
+
function sectorContainsSector(m, p2) {
|
|
890
|
+
return area(m.prev, m, p2.prev) < 0 && area(p2.next, m, m.next) < 0;
|
|
891
|
+
}
|
|
892
|
+
function indexCurve(start, minX, minY, invSize) {
|
|
893
|
+
let p2 = start;
|
|
894
|
+
do {
|
|
895
|
+
if (p2.z === 0) p2.z = zOrder(p2.x, p2.y, minX, minY, invSize);
|
|
896
|
+
p2.prevZ = p2.prev;
|
|
897
|
+
p2.nextZ = p2.next;
|
|
898
|
+
p2 = p2.next;
|
|
899
|
+
} while (p2 !== start);
|
|
900
|
+
p2.prevZ.nextZ = null;
|
|
901
|
+
p2.prevZ = null;
|
|
902
|
+
sortLinked(p2);
|
|
903
|
+
}
|
|
904
|
+
function sortLinked(list2) {
|
|
905
|
+
let i, p2, q, e, tail, numMerges, pSize, qSize, inSize = 1;
|
|
906
|
+
do {
|
|
907
|
+
p2 = list2;
|
|
908
|
+
list2 = null;
|
|
909
|
+
tail = null;
|
|
910
|
+
numMerges = 0;
|
|
911
|
+
while (p2) {
|
|
912
|
+
numMerges++;
|
|
913
|
+
q = p2;
|
|
914
|
+
pSize = 0;
|
|
915
|
+
for (i = 0; i < inSize; i++) {
|
|
916
|
+
pSize++;
|
|
917
|
+
q = q.nextZ;
|
|
918
|
+
if (!q) break;
|
|
919
|
+
}
|
|
920
|
+
qSize = inSize;
|
|
921
|
+
while (pSize > 0 || qSize > 0 && q) {
|
|
922
|
+
if (pSize !== 0 && (qSize === 0 || !q || p2.z <= q.z)) {
|
|
923
|
+
e = p2;
|
|
924
|
+
p2 = p2.nextZ;
|
|
925
|
+
pSize--;
|
|
926
|
+
} else {
|
|
927
|
+
e = q;
|
|
928
|
+
q = q.nextZ;
|
|
929
|
+
qSize--;
|
|
930
|
+
}
|
|
931
|
+
if (tail) tail.nextZ = e;
|
|
932
|
+
else list2 = e;
|
|
933
|
+
e.prevZ = tail;
|
|
934
|
+
tail = e;
|
|
935
|
+
}
|
|
936
|
+
p2 = q;
|
|
937
|
+
}
|
|
938
|
+
tail.nextZ = null;
|
|
939
|
+
inSize *= 2;
|
|
940
|
+
} while (numMerges > 1);
|
|
941
|
+
return list2;
|
|
942
|
+
}
|
|
943
|
+
function zOrder(x, y, minX, minY, invSize) {
|
|
944
|
+
x = (x - minX) * invSize | 0;
|
|
945
|
+
y = (y - minY) * invSize | 0;
|
|
946
|
+
x = (x | x << 8) & 16711935;
|
|
947
|
+
x = (x | x << 4) & 252645135;
|
|
948
|
+
x = (x | x << 2) & 858993459;
|
|
949
|
+
x = (x | x << 1) & 1431655765;
|
|
950
|
+
y = (y | y << 8) & 16711935;
|
|
951
|
+
y = (y | y << 4) & 252645135;
|
|
952
|
+
y = (y | y << 2) & 858993459;
|
|
953
|
+
y = (y | y << 1) & 1431655765;
|
|
954
|
+
return x | y << 1;
|
|
955
|
+
}
|
|
956
|
+
function getLeftmost(start) {
|
|
957
|
+
let p2 = start, leftmost = start;
|
|
958
|
+
do {
|
|
959
|
+
if (p2.x < leftmost.x || p2.x === leftmost.x && p2.y < leftmost.y) leftmost = p2;
|
|
960
|
+
p2 = p2.next;
|
|
961
|
+
} while (p2 !== start);
|
|
962
|
+
return leftmost;
|
|
963
|
+
}
|
|
964
|
+
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
965
|
+
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
|
|
966
|
+
}
|
|
967
|
+
function isValidDiagonal(a, b) {
|
|
968
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
|
|
969
|
+
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
|
|
970
|
+
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
|
971
|
+
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
|
|
972
|
+
}
|
|
973
|
+
function area(p2, q, r) {
|
|
974
|
+
return (q.y - p2.y) * (r.x - q.x) - (q.x - p2.x) * (r.y - q.y);
|
|
975
|
+
}
|
|
976
|
+
function equals(p1, p2) {
|
|
977
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
978
|
+
}
|
|
979
|
+
function intersects(p1, q1, p2, q2) {
|
|
980
|
+
const o1 = sign(area(p1, q1, p2));
|
|
981
|
+
const o2 = sign(area(p1, q1, q2));
|
|
982
|
+
const o3 = sign(area(p2, q2, p1));
|
|
983
|
+
const o4 = sign(area(p2, q2, q1));
|
|
984
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
985
|
+
if (o1 === 0 && onSegment(p1, p2, q1)) return true;
|
|
986
|
+
if (o2 === 0 && onSegment(p1, q2, q1)) return true;
|
|
987
|
+
if (o3 === 0 && onSegment(p2, p1, q2)) return true;
|
|
988
|
+
if (o4 === 0 && onSegment(p2, q1, q2)) return true;
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
function onSegment(p2, q, r) {
|
|
992
|
+
return q.x <= Math.max(p2.x, r.x) && q.x >= Math.min(p2.x, r.x) && q.y <= Math.max(p2.y, r.y) && q.y >= Math.min(p2.y, r.y);
|
|
993
|
+
}
|
|
994
|
+
function sign(num2) {
|
|
995
|
+
return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
|
|
996
|
+
}
|
|
997
|
+
function intersectsPolygon(a, b) {
|
|
998
|
+
let p2 = a;
|
|
999
|
+
do {
|
|
1000
|
+
if (p2.i !== a.i && p2.next.i !== a.i && p2.i !== b.i && p2.next.i !== b.i && intersects(p2, p2.next, a, b)) return true;
|
|
1001
|
+
p2 = p2.next;
|
|
1002
|
+
} while (p2 !== a);
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
function locallyInside(a, b) {
|
|
1006
|
+
return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
|
|
1007
|
+
}
|
|
1008
|
+
function middleInside(a, b) {
|
|
1009
|
+
let p2 = a, inside = false;
|
|
1010
|
+
const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
|
|
1011
|
+
do {
|
|
1012
|
+
if (p2.y > py !== p2.next.y > py && p2.next.y !== p2.y && px < (p2.next.x - p2.x) * (py - p2.y) / (p2.next.y - p2.y) + p2.x)
|
|
1013
|
+
inside = !inside;
|
|
1014
|
+
p2 = p2.next;
|
|
1015
|
+
} while (p2 !== a);
|
|
1016
|
+
return inside;
|
|
1017
|
+
}
|
|
1018
|
+
function splitPolygon(a, b) {
|
|
1019
|
+
const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev;
|
|
1020
|
+
a.next = b;
|
|
1021
|
+
b.prev = a;
|
|
1022
|
+
a2.next = an;
|
|
1023
|
+
an.prev = a2;
|
|
1024
|
+
b2.next = a2;
|
|
1025
|
+
a2.prev = b2;
|
|
1026
|
+
bp.next = b2;
|
|
1027
|
+
b2.prev = bp;
|
|
1028
|
+
return b2;
|
|
1029
|
+
}
|
|
1030
|
+
function insertNode(i, x, y, last) {
|
|
1031
|
+
const p2 = new Node(i, x, y);
|
|
1032
|
+
if (!last) {
|
|
1033
|
+
p2.prev = p2;
|
|
1034
|
+
p2.next = p2;
|
|
1035
|
+
} else {
|
|
1036
|
+
p2.next = last.next;
|
|
1037
|
+
p2.prev = last;
|
|
1038
|
+
last.next.prev = p2;
|
|
1039
|
+
last.next = p2;
|
|
1040
|
+
}
|
|
1041
|
+
return p2;
|
|
1042
|
+
}
|
|
1043
|
+
function removeNode(p2) {
|
|
1044
|
+
p2.next.prev = p2.prev;
|
|
1045
|
+
p2.prev.next = p2.next;
|
|
1046
|
+
if (p2.prevZ) p2.prevZ.nextZ = p2.nextZ;
|
|
1047
|
+
if (p2.nextZ) p2.nextZ.prevZ = p2.prevZ;
|
|
1048
|
+
}
|
|
1049
|
+
function Node(i, x, y) {
|
|
1050
|
+
this.i = i;
|
|
1051
|
+
this.x = x;
|
|
1052
|
+
this.y = y;
|
|
1053
|
+
this.prev = null;
|
|
1054
|
+
this.next = null;
|
|
1055
|
+
this.z = 0;
|
|
1056
|
+
this.prevZ = null;
|
|
1057
|
+
this.nextZ = null;
|
|
1058
|
+
this.steiner = false;
|
|
1059
|
+
}
|
|
1060
|
+
function signedArea(data, start, end, dim) {
|
|
1061
|
+
let sum = 0;
|
|
1062
|
+
for (let i = start, j = end - dim; i < end; i += dim) {
|
|
1063
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
1064
|
+
j = i;
|
|
1065
|
+
}
|
|
1066
|
+
return sum;
|
|
1067
|
+
}
|
|
1068
|
+
var equals2 = (a, b) => a.x === b.x && a.y === b.y;
|
|
1069
|
+
var linePoint = (v1, v2, t) => t === 1 ? { x: v2.x, y: v2.y } : { x: (v2.x - v1.x) * t + v1.x, y: (v2.y - v1.y) * t + v1.y };
|
|
1070
|
+
function pathPoints(points) {
|
|
1071
|
+
const out = [];
|
|
1072
|
+
let last;
|
|
1073
|
+
for (let i = 1; i < points.length; i++) {
|
|
1074
|
+
const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
|
|
1075
|
+
const v2 = { x: points[i][0], y: points[i][1] };
|
|
1076
|
+
for (let d = 0; d <= 1; d++) {
|
|
1077
|
+
const point = linePoint(v1, v2, d / 1);
|
|
1078
|
+
if (last && equals2(last, point)) continue;
|
|
1079
|
+
out.push(point);
|
|
1080
|
+
last = point;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return out;
|
|
1084
|
+
}
|
|
1085
|
+
function area2(contour) {
|
|
1086
|
+
const n = contour.length;
|
|
1087
|
+
let a = 0;
|
|
1088
|
+
for (let p2 = n - 1, q = 0; q < n; p2 = q++) a += contour[p2].x * contour[q].y - contour[q].x * contour[p2].y;
|
|
1089
|
+
return a * 0.5;
|
|
1090
|
+
}
|
|
1091
|
+
var isClockWise = (pts) => area2(pts) < 0;
|
|
1092
|
+
function removeDupEndPts(points) {
|
|
1093
|
+
const l = points.length;
|
|
1094
|
+
if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
|
|
1095
|
+
}
|
|
1096
|
+
function triangulateShape(contour, holes) {
|
|
1097
|
+
const vertices = [];
|
|
1098
|
+
const holeIndices = [];
|
|
1099
|
+
const faces = [];
|
|
1100
|
+
removeDupEndPts(contour);
|
|
1101
|
+
for (const p2 of contour) vertices.push(p2.x, p2.y);
|
|
1102
|
+
let holeIndex = contour.length;
|
|
1103
|
+
holes.forEach(removeDupEndPts);
|
|
1104
|
+
for (let i = 0; i < holes.length; i++) {
|
|
1105
|
+
holeIndices.push(holeIndex);
|
|
1106
|
+
holeIndex += holes[i].length;
|
|
1107
|
+
for (const p2 of holes[i]) vertices.push(p2.x, p2.y);
|
|
1108
|
+
}
|
|
1109
|
+
const triangles = Earcut.triangulate(vertices, holeIndices);
|
|
1110
|
+
for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
|
|
1111
|
+
return faces;
|
|
1112
|
+
}
|
|
1113
|
+
function computeVertexNormals(positions, indices) {
|
|
1114
|
+
const normals = new Float32Array(positions.length);
|
|
1115
|
+
const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
|
|
1116
|
+
const face = (vA, vB, vC) => {
|
|
1117
|
+
const pA = get(positions, vA);
|
|
1118
|
+
const pB = get(positions, vB);
|
|
1119
|
+
const pC = get(positions, vC);
|
|
1120
|
+
const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
|
|
1121
|
+
const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
|
|
1122
|
+
return [cb[1] * ab[2] - cb[2] * ab[1], cb[2] * ab[0] - cb[0] * ab[2], cb[0] * ab[1] - cb[1] * ab[0]];
|
|
1123
|
+
};
|
|
1124
|
+
const set = (i, x, y, z) => {
|
|
1125
|
+
normals[i * 3] = x;
|
|
1126
|
+
normals[i * 3 + 1] = y;
|
|
1127
|
+
normals[i * 3 + 2] = z;
|
|
1128
|
+
};
|
|
1129
|
+
if (indices) {
|
|
1130
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
1131
|
+
const vA = indices[i];
|
|
1132
|
+
const vB = indices[i + 1];
|
|
1133
|
+
const vC = indices[i + 2];
|
|
1134
|
+
const cb = face(vA, vB, vC);
|
|
1135
|
+
for (const v of [vA, vB, vC]) {
|
|
1136
|
+
const n = get(normals, v);
|
|
1137
|
+
set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
} else {
|
|
1141
|
+
for (let i = 0; i < positions.length / 3; i += 3) {
|
|
1142
|
+
const cb = face(i, i + 1, i + 2);
|
|
1143
|
+
set(i, cb[0], cb[1], cb[2]);
|
|
1144
|
+
set(i + 1, cb[0], cb[1], cb[2]);
|
|
1145
|
+
set(i + 2, cb[0], cb[1], cb[2]);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
for (let i = 0; i < normals.length / 3; i++) {
|
|
1149
|
+
const [x, y, z] = get(normals, i);
|
|
1150
|
+
const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
|
|
1151
|
+
set(i, x * s, y * s, z * s);
|
|
1152
|
+
}
|
|
1153
|
+
return normals;
|
|
1154
|
+
}
|
|
1155
|
+
function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
|
|
1156
|
+
const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
|
|
1157
|
+
const verticesArray = [];
|
|
1158
|
+
const uvArray = [];
|
|
1159
|
+
const groups = [];
|
|
1160
|
+
const placeholder = [];
|
|
1161
|
+
const bevelEnabled = bevel !== false;
|
|
1162
|
+
const spec = bevel === false || bevel === void 0 ? {} : bevel;
|
|
1163
|
+
let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
|
|
1164
|
+
let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
|
|
1165
|
+
let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
|
|
1166
|
+
let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
|
|
1167
|
+
if (!bevelEnabled) {
|
|
1168
|
+
bevelSegments = 0;
|
|
1169
|
+
bevelThickness = 0;
|
|
1170
|
+
bevelSize = 0;
|
|
1171
|
+
bevelOffset = 0;
|
|
1172
|
+
}
|
|
1173
|
+
let vertices = pathPoints(outline.outer);
|
|
1174
|
+
const holes = (outline.holes ?? []).map(pathPoints);
|
|
1175
|
+
const reverse = !isClockWise(vertices);
|
|
1176
|
+
if (reverse) {
|
|
1177
|
+
vertices = vertices.reverse();
|
|
1178
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1179
|
+
const ahole = holes[h];
|
|
1180
|
+
if (isClockWise(ahole)) holes[h] = ahole.reverse();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const faces = triangulateShape(vertices, holes);
|
|
1184
|
+
const contour = vertices;
|
|
1185
|
+
for (const ahole of holes) vertices = vertices.concat(ahole);
|
|
1186
|
+
const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
|
|
1187
|
+
const vlen = vertices.length;
|
|
1188
|
+
const flen = faces.length;
|
|
1189
|
+
const getBevelVec = (inPt, inPrev, inNext) => {
|
|
1190
|
+
let vTransX;
|
|
1191
|
+
let vTransY;
|
|
1192
|
+
let shrinkBy;
|
|
1193
|
+
const vPrevX = inPt.x - inPrev.x;
|
|
1194
|
+
const vPrevY = inPt.y - inPrev.y;
|
|
1195
|
+
const vNextX = inNext.x - inPt.x;
|
|
1196
|
+
const vNextY = inNext.y - inPt.y;
|
|
1197
|
+
const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
|
|
1198
|
+
const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
|
|
1199
|
+
if (Math.abs(collinear0) > Number.EPSILON) {
|
|
1200
|
+
const vPrevLen = Math.sqrt(vPrevLensq);
|
|
1201
|
+
const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
|
|
1202
|
+
const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
|
|
1203
|
+
const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
|
|
1204
|
+
const ptNextShiftX = inNext.x - vNextY / vNextLen;
|
|
1205
|
+
const ptNextShiftY = inNext.y + vNextX / vNextLen;
|
|
1206
|
+
const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
|
|
1207
|
+
vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
|
|
1208
|
+
vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
|
|
1209
|
+
const vTransLensq = vTransX * vTransX + vTransY * vTransY;
|
|
1210
|
+
if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
|
|
1211
|
+
shrinkBy = Math.sqrt(vTransLensq / 2);
|
|
1212
|
+
} else {
|
|
1213
|
+
let directionEq = false;
|
|
1214
|
+
if (vPrevX > Number.EPSILON) {
|
|
1215
|
+
if (vNextX > Number.EPSILON) directionEq = true;
|
|
1216
|
+
} else if (vPrevX < -Number.EPSILON) {
|
|
1217
|
+
if (vNextX < -Number.EPSILON) directionEq = true;
|
|
1218
|
+
} else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
|
|
1219
|
+
directionEq = true;
|
|
1220
|
+
}
|
|
1221
|
+
if (directionEq) {
|
|
1222
|
+
vTransX = -vPrevY;
|
|
1223
|
+
vTransY = vPrevX;
|
|
1224
|
+
shrinkBy = Math.sqrt(vPrevLensq);
|
|
1225
|
+
} else {
|
|
1226
|
+
vTransX = vPrevX;
|
|
1227
|
+
vTransY = vPrevY;
|
|
1228
|
+
shrinkBy = Math.sqrt(vPrevLensq / 2);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
|
|
1232
|
+
};
|
|
1233
|
+
const movements = (ring2) => {
|
|
1234
|
+
const out = [];
|
|
1235
|
+
for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
|
|
1236
|
+
if (j === il) j = 0;
|
|
1237
|
+
if (k === il) k = 0;
|
|
1238
|
+
out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
|
|
1239
|
+
}
|
|
1240
|
+
return out;
|
|
1241
|
+
};
|
|
1242
|
+
const contourMovements = movements(contour);
|
|
1243
|
+
const holesMovements = [];
|
|
1244
|
+
let verticesMovements = contourMovements.concat();
|
|
1245
|
+
for (const ahole of holes) {
|
|
1246
|
+
const oneHoleMovements = movements(ahole);
|
|
1247
|
+
holesMovements.push(oneHoleMovements);
|
|
1248
|
+
verticesMovements = verticesMovements.concat(oneHoleMovements);
|
|
1249
|
+
}
|
|
1250
|
+
const v = (x, y, z) => {
|
|
1251
|
+
placeholder.push(x);
|
|
1252
|
+
placeholder.push(y);
|
|
1253
|
+
placeholder.push(z);
|
|
1254
|
+
};
|
|
1255
|
+
const bevelLayer = (b, zOf) => {
|
|
1256
|
+
const t = b / bevelSegments;
|
|
1257
|
+
const z = bevelThickness * Math.cos(t * Math.PI / 2);
|
|
1258
|
+
const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
|
|
1259
|
+
for (let i = 0; i < contour.length; i++) {
|
|
1260
|
+
const vert = scalePt2(contour[i], contourMovements[i], bs2);
|
|
1261
|
+
v(vert.x, vert.y, zOf(z));
|
|
1262
|
+
}
|
|
1263
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1264
|
+
const ahole = holes[h];
|
|
1265
|
+
const oneHoleMovements = holesMovements[h];
|
|
1266
|
+
for (let i = 0; i < ahole.length; i++) {
|
|
1267
|
+
const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
|
|
1268
|
+
v(vert.x, vert.y, zOf(z));
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
|
|
1273
|
+
const bs = bevelSize + bevelOffset;
|
|
1274
|
+
for (let i = 0; i < vlen; i++) {
|
|
1275
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1276
|
+
v(vert.x, vert.y, 0);
|
|
1277
|
+
}
|
|
1278
|
+
for (let s = 1; s <= steps; s++) {
|
|
1279
|
+
for (let i = 0; i < vlen; i++) {
|
|
1280
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1281
|
+
v(vert.x, vert.y, depth / steps * s);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
|
|
1285
|
+
const addVertex = (index) => {
|
|
1286
|
+
verticesArray.push(placeholder[index * 3 + 0]);
|
|
1287
|
+
verticesArray.push(placeholder[index * 3 + 1]);
|
|
1288
|
+
verticesArray.push(placeholder[index * 3 + 2]);
|
|
1289
|
+
};
|
|
1290
|
+
const addUV = (x, y) => {
|
|
1291
|
+
uvArray.push(x);
|
|
1292
|
+
uvArray.push(y);
|
|
1293
|
+
};
|
|
1294
|
+
const at = (index, axis) => verticesArray[index * 3 + axis];
|
|
1295
|
+
const f3 = (a, b, c) => {
|
|
1296
|
+
addVertex(a);
|
|
1297
|
+
addVertex(b);
|
|
1298
|
+
addVertex(c);
|
|
1299
|
+
const nextIndex = verticesArray.length / 3;
|
|
1300
|
+
for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
|
|
1301
|
+
};
|
|
1302
|
+
const f4 = (a, b, c, d) => {
|
|
1303
|
+
addVertex(a);
|
|
1304
|
+
addVertex(b);
|
|
1305
|
+
addVertex(d);
|
|
1306
|
+
addVertex(b);
|
|
1307
|
+
addVertex(c);
|
|
1308
|
+
addVertex(d);
|
|
1309
|
+
const nextIndex = verticesArray.length / 3;
|
|
1310
|
+
const iA = nextIndex - 6;
|
|
1311
|
+
const iB = nextIndex - 3;
|
|
1312
|
+
const iC = nextIndex - 2;
|
|
1313
|
+
const iD = nextIndex - 1;
|
|
1314
|
+
const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
|
|
1315
|
+
const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
|
|
1316
|
+
const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
|
|
1317
|
+
for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
|
|
1318
|
+
};
|
|
1319
|
+
let start = verticesArray.length / 3;
|
|
1320
|
+
if (bevelEnabled) {
|
|
1321
|
+
let offset = 0;
|
|
1322
|
+
for (let i = 0; i < flen; i++) {
|
|
1323
|
+
const face = faces[i];
|
|
1324
|
+
f3(face[2] + offset, face[1] + offset, face[0] + offset);
|
|
1325
|
+
}
|
|
1326
|
+
const layer = steps + bevelSegments * 2;
|
|
1327
|
+
offset = vlen * layer;
|
|
1328
|
+
for (let i = 0; i < flen; i++) {
|
|
1329
|
+
const face = faces[i];
|
|
1330
|
+
f3(face[0] + offset, face[1] + offset, face[2] + offset);
|
|
1331
|
+
}
|
|
1332
|
+
} else {
|
|
1333
|
+
for (let i = 0; i < flen; i++) {
|
|
1334
|
+
const face = faces[i];
|
|
1335
|
+
f3(face[2], face[1], face[0]);
|
|
1336
|
+
}
|
|
1337
|
+
for (let i = 0; i < flen; i++) {
|
|
1338
|
+
const face = faces[i];
|
|
1339
|
+
f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
|
|
1343
|
+
start = verticesArray.length / 3;
|
|
1344
|
+
const sidewalls = (ring2, layeroffset2) => {
|
|
1345
|
+
let i = ring2.length;
|
|
1346
|
+
while (--i >= 0) {
|
|
1347
|
+
const j = i;
|
|
1348
|
+
let k = i - 1;
|
|
1349
|
+
if (k < 0) k = ring2.length - 1;
|
|
1350
|
+
for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
|
|
1351
|
+
const slen1 = vlen * s;
|
|
1352
|
+
const slen2 = vlen * (s + 1);
|
|
1353
|
+
f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
let layeroffset = 0;
|
|
1358
|
+
sidewalls(contour, layeroffset);
|
|
1359
|
+
layeroffset += contour.length;
|
|
1360
|
+
for (const ahole of holes) {
|
|
1361
|
+
sidewalls(ahole, layeroffset);
|
|
1362
|
+
layeroffset += ahole.length;
|
|
1363
|
+
}
|
|
1364
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
|
|
1365
|
+
const positions = new Float32Array(verticesArray);
|
|
1366
|
+
return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
|
|
1367
|
+
}
|
|
1368
|
+
var PARAMETER_KEYS = {
|
|
1369
|
+
box: ["size", "segments"],
|
|
1370
|
+
cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1371
|
+
cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1372
|
+
sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
|
|
1373
|
+
plane: ["width", "height", "widthSegments", "heightSegments"],
|
|
1374
|
+
circle: ["radius", "segments", "thetaStart", "thetaLength"],
|
|
1375
|
+
ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
|
|
1376
|
+
lathe: ["points", "segments", "phiStart", "phiLength"],
|
|
1377
|
+
torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
|
|
1378
|
+
extrude: ["shape", "depth", "steps", "bevel"],
|
|
1379
|
+
difference: ["size", "subtract"],
|
|
1380
|
+
mesh: ["positions", "normals", "uvs", "indices"]
|
|
1381
|
+
};
|
|
1382
|
+
var SEGMENT_KEYS = {
|
|
1383
|
+
cylinder: ["radialSegments"],
|
|
1384
|
+
cone: ["radialSegments"],
|
|
1385
|
+
sphere: ["widthSegments", "heightSegments"],
|
|
1386
|
+
circle: ["segments"],
|
|
1387
|
+
ring: ["thetaSegments"],
|
|
1388
|
+
lathe: ["segments"],
|
|
1389
|
+
torus: ["radialSegments", "tubularSegments"]
|
|
1390
|
+
};
|
|
1391
|
+
var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
|
|
1392
|
+
var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
|
|
1393
|
+
function primitiveParameters(node, options = {}) {
|
|
1394
|
+
const out = {};
|
|
1395
|
+
const source = node;
|
|
1396
|
+
for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
|
|
1397
|
+
const detail = options.detail ?? 1;
|
|
1398
|
+
if (detail !== 1) {
|
|
1399
|
+
const floor = options.minSegments ?? 3;
|
|
1400
|
+
for (const key of SEGMENT_KEYS[node.kind] ?? []) {
|
|
1401
|
+
const given = out[key] ?? segmentDefault(node.kind, key);
|
|
1402
|
+
out[key] = Math.max(floor, Math.round(given * detail));
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return out;
|
|
1406
|
+
}
|
|
1407
|
+
function mesh(p2) {
|
|
1408
|
+
const positions = new Float32Array(p2.positions);
|
|
1409
|
+
const indices = p2.indices ? indexArray(p2.indices) : void 0;
|
|
1410
|
+
return {
|
|
1411
|
+
type: "BufferGeometry",
|
|
1412
|
+
parameters: {},
|
|
1413
|
+
positions,
|
|
1414
|
+
normals: p2.normals ? new Float32Array(p2.normals) : computeVertexNormals(positions, indices),
|
|
1415
|
+
uvs: p2.uvs ? new Float32Array(p2.uvs) : new Float32Array(positions.length / 3 * 2),
|
|
1416
|
+
...indices ? { indices } : {},
|
|
1417
|
+
groups: []
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function make(kind, p2) {
|
|
1421
|
+
switch (kind) {
|
|
1422
|
+
case "box": {
|
|
1423
|
+
const size = p2.size ?? [1, 1, 1];
|
|
1424
|
+
const segments = p2.segments ?? [1, 1, 1];
|
|
1425
|
+
return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
|
|
1426
|
+
}
|
|
1427
|
+
case "cylinder":
|
|
1428
|
+
return cylinder(p2.radiusTop, p2.radiusBottom, p2.height, p2.radialSegments, p2.heightSegments, p2.openEnded, p2.thetaStart, p2.thetaLength);
|
|
1429
|
+
case "cone":
|
|
1430
|
+
return cone(p2.radius, p2.height, p2.radialSegments, p2.heightSegments, p2.openEnded, p2.thetaStart, p2.thetaLength);
|
|
1431
|
+
case "sphere":
|
|
1432
|
+
return sphere(p2.radius, p2.widthSegments, p2.heightSegments, p2.phiStart, p2.phiLength, p2.thetaStart, p2.thetaLength);
|
|
1433
|
+
case "plane":
|
|
1434
|
+
return plane(p2.width, p2.height, p2.widthSegments, p2.heightSegments);
|
|
1435
|
+
case "circle":
|
|
1436
|
+
return circle(p2.radius, p2.segments, p2.thetaStart, p2.thetaLength);
|
|
1437
|
+
case "ring":
|
|
1438
|
+
return ring(p2.innerRadius, p2.outerRadius, p2.thetaSegments, p2.phiSegments, p2.thetaStart, p2.thetaLength);
|
|
1439
|
+
case "lathe":
|
|
1440
|
+
return lathe(p2.points, p2.segments, p2.phiStart, p2.phiLength);
|
|
1441
|
+
case "torus":
|
|
1442
|
+
return torus(p2.radius, p2.tube, p2.radialSegments, p2.tubularSegments, p2.arc);
|
|
1443
|
+
case "extrude":
|
|
1444
|
+
return extrude(p2.shape, p2.depth, p2.steps, p2.bevel);
|
|
1445
|
+
case "difference":
|
|
1446
|
+
return difference(p2.size, p2.subtract ?? []);
|
|
1447
|
+
case "mesh":
|
|
1448
|
+
return mesh(p2);
|
|
1449
|
+
default:
|
|
1450
|
+
throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
var templates = /* @__PURE__ */ new Map();
|
|
1454
|
+
var templateLimit = 4096;
|
|
1455
|
+
var stats = { hits: 0, misses: 0 };
|
|
1456
|
+
function buildPrimitive(node, options = {}) {
|
|
1457
|
+
const parameters = primitiveParameters(node, options);
|
|
1458
|
+
if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
|
|
1459
|
+
const key = `${node.kind}:${canonicalJson(parameters)}`;
|
|
1460
|
+
const cached = templates.get(key);
|
|
1461
|
+
if (cached) {
|
|
1462
|
+
stats.hits++;
|
|
1463
|
+
templates.delete(key);
|
|
1464
|
+
templates.set(key, cached);
|
|
1465
|
+
return cached;
|
|
1466
|
+
}
|
|
1467
|
+
stats.misses++;
|
|
1468
|
+
const built = make(node.kind, parameters);
|
|
1469
|
+
templates.set(key, built);
|
|
1470
|
+
if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
|
|
1471
|
+
return built;
|
|
1472
|
+
}
|
|
1473
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
|
|
1474
|
+
var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
|
|
1475
|
+
var str = (v) => typeof v === "string" ? null : "expected a string";
|
|
1476
|
+
var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
|
|
1477
|
+
var range = (lo, hi, loOpen = false) => (v) => typeof v === "number" && (loOpen ? v > lo : v >= lo) && v <= hi ? null : `expected a number in ${loOpen ? "(" : "["}${lo}, ${hi}]`;
|
|
1478
|
+
var tuple = (n, item = num) => (v) => {
|
|
1479
|
+
if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
|
|
1480
|
+
for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
|
|
1481
|
+
return null;
|
|
1482
|
+
};
|
|
1483
|
+
var list = (item, min = 0) => (v) => {
|
|
1484
|
+
if (!Array.isArray(v)) return "expected an array";
|
|
1485
|
+
if (v.length < min) return `expected at least ${min} items`;
|
|
1486
|
+
for (let i = 0; i < v.length; i++) {
|
|
1487
|
+
const e = item(v[i]);
|
|
1488
|
+
if (e) return `[${i}]: ${e}`;
|
|
1489
|
+
}
|
|
1490
|
+
return null;
|
|
1491
|
+
};
|
|
1492
|
+
var color = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 16777215 || typeof v === "string" && /^#[0-9a-fA-F]{6}$/.test(v) ? null : 'expected 0xRRGGBB or "#rrggbb"';
|
|
1493
|
+
var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
|
|
1494
|
+
var object = (fields, required = []) => (v) => {
|
|
1495
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
|
|
1496
|
+
const record = v;
|
|
1497
|
+
for (const key of required) if (record[key] === void 0) return `${key}: required`;
|
|
1498
|
+
for (const key of Object.keys(record)) {
|
|
1499
|
+
if (record[key] === void 0) continue;
|
|
1500
|
+
const check = fields[key];
|
|
1501
|
+
if (!check) return `${key}: unknown field`;
|
|
1502
|
+
const e = check(record[key]);
|
|
1503
|
+
if (e) return `${key}: ${e}`;
|
|
1504
|
+
}
|
|
1505
|
+
return null;
|
|
1506
|
+
};
|
|
1507
|
+
var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
|
|
1508
|
+
var TRANSFORM = {
|
|
1509
|
+
position: tuple(3),
|
|
1510
|
+
rotation: tuple(3),
|
|
1511
|
+
rotationOrder: oneOf(ORDERS),
|
|
1512
|
+
scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
|
|
1513
|
+
heading: num,
|
|
1514
|
+
matrix: tuple(16)
|
|
1515
|
+
};
|
|
1516
|
+
var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
|
|
1517
|
+
var INSTANCE = object({ ...TRANSFORM, color });
|
|
1518
|
+
var COMMON = {
|
|
1519
|
+
...TRANSFORM,
|
|
1520
|
+
kind: str,
|
|
1521
|
+
name: str,
|
|
1522
|
+
part: str,
|
|
1523
|
+
material: str,
|
|
1524
|
+
color,
|
|
1525
|
+
uv: UV,
|
|
1526
|
+
instances: list(INSTANCE),
|
|
1527
|
+
minDetail: range(0, 1, true),
|
|
1528
|
+
tags: list(str)
|
|
1529
|
+
};
|
|
1530
|
+
var vec2 = tuple(2);
|
|
1531
|
+
var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
|
|
1532
|
+
var KINDS = {
|
|
1533
|
+
group: { fields: {} },
|
|
1534
|
+
box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
|
|
1535
|
+
cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1536
|
+
cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1537
|
+
sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
|
|
1538
|
+
plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
|
|
1539
|
+
circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
|
|
1540
|
+
ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
|
|
1541
|
+
lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
|
|
1542
|
+
torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
|
|
1543
|
+
extrude: {
|
|
1544
|
+
fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
|
|
1545
|
+
required: ["shape"]
|
|
1546
|
+
},
|
|
1547
|
+
difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
|
|
1548
|
+
mesh: {
|
|
1549
|
+
fields: {
|
|
1550
|
+
positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
|
|
1551
|
+
normals: list(num),
|
|
1552
|
+
uvs: list(num),
|
|
1553
|
+
indices: list(int(0))
|
|
1554
|
+
},
|
|
1555
|
+
required: ["positions"]
|
|
1556
|
+
},
|
|
1557
|
+
point: { fields: { data: () => null } },
|
|
1558
|
+
custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
// ../game-shapes-three/dist/index.js
|
|
1562
|
+
var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
|
|
1563
|
+
var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
|
|
1564
|
+
var isPrimitive = (value) => typeof value.type === "string";
|
|
1565
|
+
function threeParameters(THREE, p2) {
|
|
1566
|
+
if (p2.type === "LatheGeometry") {
|
|
1567
|
+
const points = p2.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
|
|
1568
|
+
return { ...p2.parameters, points };
|
|
1569
|
+
}
|
|
1570
|
+
return { ...p2.parameters };
|
|
1571
|
+
}
|
|
1572
|
+
function toGeometry(THREE, source, options = {}) {
|
|
1573
|
+
const copy = options.copy !== false;
|
|
1574
|
+
const primitive = isPrimitive(source) ? source : null;
|
|
1575
|
+
const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
|
|
1576
|
+
const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
|
|
1577
|
+
if (primitive && ThreeClass) {
|
|
1578
|
+
Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
|
|
1579
|
+
}
|
|
1580
|
+
const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
|
|
1581
|
+
const index = source.indices;
|
|
1582
|
+
const setIndex = () => {
|
|
1583
|
+
if (!index) return;
|
|
1584
|
+
geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
|
|
1585
|
+
};
|
|
1586
|
+
const type = primitive?.type ?? "BufferGeometry";
|
|
1587
|
+
if (INDEX_FIRST.has(type)) setIndex();
|
|
1588
|
+
geometry.setAttribute("position", attribute(source.positions, 3));
|
|
1589
|
+
if (UV_BEFORE_NORMAL.has(type)) {
|
|
1590
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1591
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1592
|
+
} else {
|
|
1593
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1594
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1595
|
+
}
|
|
1596
|
+
if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
|
|
1597
|
+
if (!INDEX_FIRST.has(type)) setIndex();
|
|
1598
|
+
if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
|
|
1599
|
+
return geometry;
|
|
1600
|
+
}
|
|
1601
|
+
var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
|
|
1602
|
+
function linePoints(path) {
|
|
1603
|
+
if (path.autoClose || path.curves.length === 0) return null;
|
|
1604
|
+
const points = [];
|
|
1605
|
+
for (const [index, curve] of path.curves.entries()) {
|
|
1606
|
+
if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
|
|
1607
|
+
if (index === 0) points.push([curve.v1.x, curve.v1.y]);
|
|
1608
|
+
else {
|
|
1609
|
+
const last = points[points.length - 1];
|
|
1610
|
+
if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
|
|
1611
|
+
}
|
|
1612
|
+
points.push([curve.v2.x, curve.v2.y]);
|
|
1613
|
+
}
|
|
1614
|
+
return points;
|
|
1615
|
+
}
|
|
1616
|
+
function extrudeOf(shapes, options = {}) {
|
|
1617
|
+
if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
|
|
1618
|
+
const shape = shapes;
|
|
1619
|
+
const outer = linePoints(shape);
|
|
1620
|
+
if (!outer) return null;
|
|
1621
|
+
const holes = [];
|
|
1622
|
+
for (const hole of shape.holes ?? []) {
|
|
1623
|
+
const points = linePoints(hole);
|
|
1624
|
+
if (!points) return null;
|
|
1625
|
+
holes.push(points);
|
|
1626
|
+
}
|
|
1627
|
+
const outline = holes.length ? { outer, holes } : { outer };
|
|
1628
|
+
const bevel = options.bevelEnabled === false ? false : {
|
|
1629
|
+
...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
|
|
1630
|
+
...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
|
|
1631
|
+
...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
|
|
1632
|
+
...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
|
|
1633
|
+
};
|
|
1634
|
+
return extrude(outline, options.depth, options.steps, bevel);
|
|
1635
|
+
}
|
|
1636
|
+
var namespaces = /* @__PURE__ */ new WeakMap();
|
|
1637
|
+
var shims = /* @__PURE__ */ new WeakSet();
|
|
1638
|
+
function shapeGeometries(THREE, options = {}) {
|
|
1639
|
+
if (shims.has(THREE)) return THREE;
|
|
1640
|
+
const cache = options.cache !== false;
|
|
1641
|
+
const key = cache ? "cache" : "nocache";
|
|
1642
|
+
let byKey = namespaces.get(THREE);
|
|
1643
|
+
if (!byKey) {
|
|
1644
|
+
byKey = /* @__PURE__ */ new Map();
|
|
1645
|
+
namespaces.set(THREE, byKey);
|
|
1646
|
+
}
|
|
1647
|
+
const existing = byKey.get(key);
|
|
1648
|
+
if (existing) return existing;
|
|
1649
|
+
const made = (p2) => toGeometry(THREE, p2);
|
|
1650
|
+
const viaCache = (node, direct2) => cache ? buildPrimitive(node) : direct2();
|
|
1651
|
+
const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1652
|
+
const constructors = {
|
|
1653
|
+
BoxGeometry: (width, height, depth, ws = 1, hs = 1, ds = 1) => made(viaCache({ kind: "box", size: [width ?? 1, height ?? 1, depth ?? 1], segments: [ws, hs, ds] }, () => box(width, height, depth, ws, hs, ds))),
|
|
1654
|
+
CylinderGeometry: (radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cylinder", ...defined({ radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cylinder(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
|
|
1655
|
+
ConeGeometry: (radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cone", ...defined({ radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cone(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
|
|
1656
|
+
SphereGeometry: (radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) => made(viaCache({ kind: "sphere", ...defined({ radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }) }, () => sphere(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength))),
|
|
1657
|
+
PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
|
|
1658
|
+
CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
|
|
1659
|
+
RingGeometry: (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) => made(viaCache({ kind: "ring", ...defined({ innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }) }, () => ring(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength))),
|
|
1660
|
+
LatheGeometry: (points, segments, phiStart, phiLength) => {
|
|
1661
|
+
if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
|
|
1662
|
+
const profile = points.map((p2) => [p2.x, p2.y]);
|
|
1663
|
+
return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
|
|
1664
|
+
},
|
|
1665
|
+
TorusGeometry: (radius, tube, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube, radialSegments, tubularSegments, arc))),
|
|
1666
|
+
ExtrudeGeometry: (shapes, extrudeOptions) => {
|
|
1667
|
+
const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
|
|
1668
|
+
if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
|
|
1669
|
+
const geometry = toGeometry(THREE, buffers);
|
|
1670
|
+
geometry.parameters = { shapes, options: extrudeOptions ?? {} };
|
|
1671
|
+
return geometry;
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
const namespace = { ...THREE };
|
|
1675
|
+
for (const type of SHAPE_GEOMETRY_TYPES) {
|
|
1676
|
+
const make2 = constructors[type];
|
|
1677
|
+
const Constructor = function(...args) {
|
|
1678
|
+
return make2(...args);
|
|
1679
|
+
};
|
|
1680
|
+
Constructor.prototype = THREE[type].prototype;
|
|
1681
|
+
Object.defineProperty(Constructor, "name", { value: type });
|
|
1682
|
+
namespace[type] = Constructor;
|
|
1683
|
+
}
|
|
1684
|
+
const result = namespace;
|
|
1685
|
+
byKey.set(key, result);
|
|
1686
|
+
shims.add(result);
|
|
1687
|
+
return result;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
163
1690
|
// ../game-contracts/dist/index.js
|
|
1691
|
+
var DEFAULT_BLAST_SHOVE = { coupling: 0.02, maxSpeed: 20, lift: 0.3, minSpeed: 0.3, falloffPower: 2 };
|
|
1692
|
+
function blastShove(blast2, target, options = {}) {
|
|
1693
|
+
const o = { ...DEFAULT_BLAST_SHOVE, ...options };
|
|
1694
|
+
const dx = target.position[0] - blast2.point[0];
|
|
1695
|
+
const dy = target.position[1] - blast2.point[1];
|
|
1696
|
+
const dz = target.position[2] - blast2.point[2];
|
|
1697
|
+
const distance3 = Math.hypot(dx, dy, dz);
|
|
1698
|
+
if (!(blast2.radius > 0) || distance3 > blast2.radius) return void 0;
|
|
1699
|
+
const falloff = (1 - distance3 / blast2.radius) ** o.falloffPower;
|
|
1700
|
+
const mass = Math.max(target.mass, 0.05);
|
|
1701
|
+
const speed = Math.min(o.maxSpeed, Math.sqrt(2 * blast2.energy * (blast2.reach ?? 1) * o.coupling / mass)) * falloff;
|
|
1702
|
+
if (!(speed >= o.minSpeed)) return void 0;
|
|
1703
|
+
const direction = distance3 > 1e-4 ? [dx / distance3, dy / distance3 + o.lift, dz / distance3] : [0, 1, 0];
|
|
1704
|
+
const length = Math.hypot(direction[0], direction[1], direction[2]);
|
|
1705
|
+
return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance: distance3, falloff };
|
|
1706
|
+
}
|
|
164
1707
|
var MISSING_BITS = {
|
|
165
1708
|
head: 1,
|
|
166
1709
|
armL: 2,
|
|
@@ -1231,9 +2774,9 @@ var GameCharactersThree = (() => {
|
|
|
1231
2774
|
if (region === "torso") return bodyCount > 1 ? cost.bruteTorso : cost.torso;
|
|
1232
2775
|
return cost.limb;
|
|
1233
2776
|
};
|
|
1234
|
-
var teamGameFalloff = (distance22,
|
|
2777
|
+
var teamGameFalloff = (distance22, range2) => {
|
|
1235
2778
|
const floor = TEAM_GAME_RULES.falloff.atTwiceRange;
|
|
1236
|
-
return distance22 <=
|
|
2779
|
+
return distance22 <= range2 * TEAM_GAME_RULES.falloff.fullAtRange ? 1 : Math.max(floor, 1 - (distance22 - range2) / range2 * (1 - floor));
|
|
1237
2780
|
};
|
|
1238
2781
|
var NO_HEADSHOT = { headshot: false };
|
|
1239
2782
|
var HEADSHOT = { headshot: true };
|
|
@@ -1286,8 +2829,8 @@ var GameCharactersThree = (() => {
|
|
|
1286
2829
|
var DEFAULT_PROJECTILE_GRAMS = 8;
|
|
1287
2830
|
var RIFLE_JOULES = 1e3;
|
|
1288
2831
|
var penetrationForce = (energy, grams, diameterMm) => {
|
|
1289
|
-
const
|
|
1290
|
-
return
|
|
2832
|
+
const area3 = Math.PI * diameterMm ** 2 / 4;
|
|
2833
|
+
return area3 > 0 && energy > 0 ? Math.sqrt(energy * grams / area3) : 0;
|
|
1291
2834
|
};
|
|
1292
2835
|
var projectileOf = (hit, caliber) => {
|
|
1293
2836
|
const pellets = hit.pellets ?? caliber?.pellets ?? 1;
|
|
@@ -1375,7 +2918,15 @@ var GameCharactersThree = (() => {
|
|
|
1375
2918
|
if (points >= rules.severity.stunAtPoints) ops.stun();
|
|
1376
2919
|
return result;
|
|
1377
2920
|
};
|
|
1378
|
-
var
|
|
2921
|
+
var INJURY_POINTS = 15;
|
|
2922
|
+
var pointsAt = (params, metres) => {
|
|
2923
|
+
const { points, radius, lethalRadius, ops } = params;
|
|
2924
|
+
if (lethalRadius === void 0 || lethalRadius >= radius) return points * Math.max(0, 1 - metres / radius);
|
|
2925
|
+
if (metres <= lethalRadius) return points;
|
|
2926
|
+
const injury = Math.min(points, ops.rules.blast.injuryPoints ?? INJURY_POINTS);
|
|
2927
|
+
return injury * Math.max(0, 1 - (metres - lethalRadius) / (radius - lethalRadius));
|
|
2928
|
+
};
|
|
2929
|
+
var shares = (params, targets, pivot) => targets.map((target) => ({ target, points: pointsAt(params, distance(params.jointOf(pivot(target)), params.center)) })).filter((share) => share.points > params.ops.rules.blast.minimumPoints).sort((a, b) => b.points - a.points);
|
|
1379
2930
|
var segmentBlast = (params) => {
|
|
1380
2931
|
const { ops, rule, blow } = params;
|
|
1381
2932
|
const { blast: blast2, segments } = ops.rules;
|
|
@@ -1615,7 +3166,7 @@ var GameCharactersThree = (() => {
|
|
|
1615
3166
|
};
|
|
1616
3167
|
var blast = ({ ops, hit, setup, localPoint, jointOf }) => {
|
|
1617
3168
|
const points = hit.points ?? pointsFromEnergy(hit.energy ?? 0, setup.rules);
|
|
1618
|
-
const base = { ops, center: localPoint, points, radius: hit.radius ?? 1, jointOf };
|
|
3169
|
+
const base = { ops, center: localPoint, points, radius: hit.radius ?? 1, ...hit.lethalRadius !== void 0 ? { lethalRadius: hit.lethalRadius } : {}, jointOf };
|
|
1619
3170
|
const common = { cut: hit.cut ?? setup.rules.defaultCut.explosive, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE };
|
|
1620
3171
|
const rule = SEGMENT_RULES[setup.rules.segmentRule] ?? kitRule;
|
|
1621
3172
|
const result = ops.body.detail === "regions" ? regionBlast({ ...base, blow: { ...common, protection: NO_PROTECTION } }) : segmentBlast({ ...base, rule, blow: { ...common, limbFactor: ops.spec.limbFactor } });
|
|
@@ -1824,14 +3375,14 @@ var GameCharactersThree = (() => {
|
|
|
1824
3375
|
var createCharacters = (options = {}) => {
|
|
1825
3376
|
const body = createBodySpec(options.body);
|
|
1826
3377
|
const packs = options.packs ?? [];
|
|
1827
|
-
const presets = mergeById([PERSON_PRESET], packs.flatMap((
|
|
3378
|
+
const presets = mergeById([PERSON_PRESET], packs.flatMap((pack2) => pack2.presets ?? []));
|
|
1828
3379
|
applyPatches(presets, options.patch);
|
|
1829
3380
|
removeIds(presets, options.remove);
|
|
1830
3381
|
const damage = resolveDamageRules(options.damage);
|
|
1831
3382
|
const penetration = modelOf(options.penetration);
|
|
1832
|
-
const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((
|
|
3383
|
+
const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((pack2) => pack2.protection ?? []), ...options.protection ?? []]);
|
|
1833
3384
|
const shippedCalibers = penetration.id === "team-game" ? TEAM_GAME_CALIBERS : [];
|
|
1834
|
-
const calibers = mergeById(shippedCalibers, [...packs.flatMap((
|
|
3385
|
+
const calibers = mergeById(shippedCalibers, [...packs.flatMap((pack2) => pack2.calibers ?? []), ...options.calibers ?? []]);
|
|
1835
3386
|
const setup = { rules: damage, model: penetration, protection, calibers };
|
|
1836
3387
|
let counter = 0;
|
|
1837
3388
|
return {
|
|
@@ -2271,6 +3822,486 @@ var GameCharactersThree = (() => {
|
|
|
2271
3822
|
const apron = random() < APRON_SHARE;
|
|
2272
3823
|
return { sleeve, hair, headWidth, headHeight, jaw, nose, beard, glasses, cap, capColor, bag, bagColor, torn, apron };
|
|
2273
3824
|
};
|
|
3825
|
+
var samplePoseClip = (clip, time) => {
|
|
3826
|
+
const keys = clip.keys;
|
|
3827
|
+
if (keys.length === 0) return {};
|
|
3828
|
+
if (keys.length === 1) return { ...keys[0].pose };
|
|
3829
|
+
let index = 0;
|
|
3830
|
+
while (index < keys.length - 2 && time > keys[index + 1].t) index++;
|
|
3831
|
+
const a = keys[index];
|
|
3832
|
+
const b = keys[index + 1];
|
|
3833
|
+
const k = Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t)));
|
|
3834
|
+
const eased = k * k * (3 - 2 * k);
|
|
3835
|
+
return lerpPose(a.pose, b.pose, eased);
|
|
3836
|
+
};
|
|
3837
|
+
var clipEventsBetween = (clip, from, to) => (clip.events ?? []).filter((event) => event.t > from && event.t <= to).sort((x, y) => x.t - y.t);
|
|
3838
|
+
var POSE_CHANNEL_JOINTS = {
|
|
3839
|
+
full: void 0,
|
|
3840
|
+
upper: ["lean", "twist", "side", "headX", "headY", "headZ", "armR", "armL", "elbR", "elbL", "armRz", "armLz"],
|
|
3841
|
+
lower: ["drop", "legL", "legR", "kneeL", "kneeR"]
|
|
3842
|
+
};
|
|
3843
|
+
var filterPoseChannels = (pose, channels = "full") => {
|
|
3844
|
+
const allowed = POSE_CHANNEL_JOINTS[channels];
|
|
3845
|
+
if (!allowed) return pose;
|
|
3846
|
+
const out = {};
|
|
3847
|
+
for (const name of allowed) if (pose[name] !== void 0) out[name] = pose[name];
|
|
3848
|
+
return out;
|
|
3849
|
+
};
|
|
3850
|
+
var MIRROR_PAIRS = [["armR", "armL"], ["elbR", "elbL"], ["armRz", "armLz"], ["legR", "legL"], ["kneeR", "kneeL"]];
|
|
3851
|
+
var MIRROR_NEGATED = /* @__PURE__ */ new Set(["side", "twist", "headY", "headZ"]);
|
|
3852
|
+
var mirrorPoseClip = (clip, id) => ({
|
|
3853
|
+
...clip,
|
|
3854
|
+
id,
|
|
3855
|
+
keys: clip.keys.map((key) => {
|
|
3856
|
+
const pose = {};
|
|
3857
|
+
for (const [name, value] of Object.entries(key.pose)) pose[name] = MIRROR_NEGATED.has(name) ? -value : value;
|
|
3858
|
+
for (const [a, b] of MIRROR_PAIRS) {
|
|
3859
|
+
const va = key.pose[a];
|
|
3860
|
+
const vb = key.pose[b];
|
|
3861
|
+
const sign2 = a === "armRz" ? -1 : 1;
|
|
3862
|
+
if (vb !== void 0) pose[a] = sign2 * vb;
|
|
3863
|
+
else delete pose[a];
|
|
3864
|
+
if (va !== void 0) pose[b] = sign2 * va;
|
|
3865
|
+
else delete pose[b];
|
|
3866
|
+
}
|
|
3867
|
+
return { t: key.t, pose };
|
|
3868
|
+
}),
|
|
3869
|
+
...clip.events ? { events: clip.events.map((event) => ({ ...event })) } : {}
|
|
3870
|
+
});
|
|
3871
|
+
var pose_clips_default = {
|
|
3872
|
+
clips: [
|
|
3873
|
+
{
|
|
3874
|
+
id: "grabbed_bitten_human",
|
|
3875
|
+
source: {
|
|
3876
|
+
file: "site/pages/zumbis-personagens/bestiario.html",
|
|
3877
|
+
scene: "ataque (Agarr\xE3o e mordida)",
|
|
3878
|
+
variable: "HK"
|
|
3879
|
+
},
|
|
3880
|
+
duration: 8,
|
|
3881
|
+
loop: false,
|
|
3882
|
+
channels: "full",
|
|
3883
|
+
keys: [
|
|
3884
|
+
{
|
|
3885
|
+
t: 0,
|
|
3886
|
+
pose: {
|
|
3887
|
+
lean: 0,
|
|
3888
|
+
headY: 0,
|
|
3889
|
+
armL: -0.1,
|
|
3890
|
+
armR: 0.05,
|
|
3891
|
+
elbL: -0.2,
|
|
3892
|
+
elbR: -0.2,
|
|
3893
|
+
drop: 0,
|
|
3894
|
+
kneeL: 0.05,
|
|
3895
|
+
kneeR: 0.05,
|
|
3896
|
+
legL: -0.05,
|
|
3897
|
+
legR: 0.05,
|
|
3898
|
+
headX: 0
|
|
3899
|
+
}
|
|
3900
|
+
},
|
|
3901
|
+
{
|
|
3902
|
+
t: 1.5,
|
|
3903
|
+
pose: {
|
|
3904
|
+
lean: -0.1,
|
|
3905
|
+
headY: -0.6,
|
|
3906
|
+
armL: -0.1,
|
|
3907
|
+
armR: 0.05,
|
|
3908
|
+
elbL: -0.2,
|
|
3909
|
+
elbR: -0.2,
|
|
3910
|
+
drop: 0,
|
|
3911
|
+
kneeL: 0.05,
|
|
3912
|
+
kneeR: 0.05,
|
|
3913
|
+
legL: -0.05,
|
|
3914
|
+
legR: 0.05,
|
|
3915
|
+
headX: -0.1
|
|
3916
|
+
}
|
|
3917
|
+
},
|
|
3918
|
+
{
|
|
3919
|
+
t: 2.1,
|
|
3920
|
+
pose: {
|
|
3921
|
+
lean: -0.3,
|
|
3922
|
+
headY: -0.2,
|
|
3923
|
+
armL: -1.3,
|
|
3924
|
+
armR: -1.2,
|
|
3925
|
+
elbL: -1.4,
|
|
3926
|
+
elbR: -1.5,
|
|
3927
|
+
drop: -0.04,
|
|
3928
|
+
kneeL: 0.25,
|
|
3929
|
+
kneeR: 0.2,
|
|
3930
|
+
legL: -0.3,
|
|
3931
|
+
legR: 0.25,
|
|
3932
|
+
headX: -0.25
|
|
3933
|
+
}
|
|
3934
|
+
},
|
|
3935
|
+
{
|
|
3936
|
+
t: 3.6,
|
|
3937
|
+
pose: {
|
|
3938
|
+
lean: -0.35,
|
|
3939
|
+
headY: 0.3,
|
|
3940
|
+
armL: -1.5,
|
|
3941
|
+
armR: -1,
|
|
3942
|
+
elbL: -0.6,
|
|
3943
|
+
elbR: -1.8,
|
|
3944
|
+
drop: -0.05,
|
|
3945
|
+
kneeL: 0.3,
|
|
3946
|
+
kneeR: 0.2,
|
|
3947
|
+
legL: -0.4,
|
|
3948
|
+
legR: 0.35,
|
|
3949
|
+
headX: -0.2
|
|
3950
|
+
}
|
|
3951
|
+
},
|
|
3952
|
+
{
|
|
3953
|
+
t: 4.3,
|
|
3954
|
+
pose: {
|
|
3955
|
+
lean: -0.2,
|
|
3956
|
+
headY: 0.7,
|
|
3957
|
+
headZ: -0.5,
|
|
3958
|
+
armL: -0.6,
|
|
3959
|
+
armR: -1.9,
|
|
3960
|
+
elbL: -0.4,
|
|
3961
|
+
elbR: -1.2,
|
|
3962
|
+
drop: -0.06,
|
|
3963
|
+
kneeL: 0.3,
|
|
3964
|
+
kneeR: 0.3,
|
|
3965
|
+
legL: -0.3,
|
|
3966
|
+
legR: 0.3,
|
|
3967
|
+
headX: -0.3
|
|
3968
|
+
}
|
|
3969
|
+
},
|
|
3970
|
+
{
|
|
3971
|
+
t: 5.6,
|
|
3972
|
+
pose: {
|
|
3973
|
+
lean: 0.55,
|
|
3974
|
+
headY: 0.4,
|
|
3975
|
+
headZ: -0.3,
|
|
3976
|
+
armL: -0.3,
|
|
3977
|
+
armR: -0.5,
|
|
3978
|
+
elbL: -0.6,
|
|
3979
|
+
elbR: -0.8,
|
|
3980
|
+
drop: -0.5,
|
|
3981
|
+
kneeL: 1.9,
|
|
3982
|
+
kneeR: 1.7,
|
|
3983
|
+
legL: -1.1,
|
|
3984
|
+
legR: -0.9,
|
|
3985
|
+
headX: 0.3
|
|
3986
|
+
}
|
|
3987
|
+
},
|
|
3988
|
+
{
|
|
3989
|
+
t: 8,
|
|
3990
|
+
pose: {
|
|
3991
|
+
lean: 0.6,
|
|
3992
|
+
headY: 0.4,
|
|
3993
|
+
headZ: -0.3,
|
|
3994
|
+
armL: -0.25,
|
|
3995
|
+
armR: -0.45,
|
|
3996
|
+
elbL: -0.6,
|
|
3997
|
+
elbR: -0.8,
|
|
3998
|
+
drop: -0.52,
|
|
3999
|
+
kneeL: 1.95,
|
|
4000
|
+
kneeR: 1.75,
|
|
4001
|
+
legL: -1.1,
|
|
4002
|
+
legR: -0.95,
|
|
4003
|
+
headX: 0.4
|
|
4004
|
+
}
|
|
4005
|
+
}
|
|
4006
|
+
],
|
|
4007
|
+
events: [
|
|
4008
|
+
{
|
|
4009
|
+
t: 0,
|
|
4010
|
+
type: "phase",
|
|
4011
|
+
data: {
|
|
4012
|
+
label: "pose_clip.grab_bite.phase.approach"
|
|
4013
|
+
}
|
|
4014
|
+
},
|
|
4015
|
+
{
|
|
4016
|
+
t: 1.7,
|
|
4017
|
+
type: "phase",
|
|
4018
|
+
data: {
|
|
4019
|
+
label: "pose_clip.grab_bite.phase.pounce"
|
|
4020
|
+
}
|
|
4021
|
+
},
|
|
4022
|
+
{
|
|
4023
|
+
t: 2.1,
|
|
4024
|
+
type: "phase",
|
|
4025
|
+
data: {
|
|
4026
|
+
label: "pose_clip.grab_bite.phase.grab"
|
|
4027
|
+
}
|
|
4028
|
+
},
|
|
4029
|
+
{
|
|
4030
|
+
t: 4.3,
|
|
4031
|
+
type: "phase",
|
|
4032
|
+
data: {
|
|
4033
|
+
label: "pose_clip.grab_bite.phase.bite"
|
|
4034
|
+
}
|
|
4035
|
+
},
|
|
4036
|
+
{
|
|
4037
|
+
t: 5.6,
|
|
4038
|
+
type: "phase",
|
|
4039
|
+
data: {
|
|
4040
|
+
label: "pose_clip.grab_bite.phase.fall"
|
|
4041
|
+
}
|
|
4042
|
+
},
|
|
4043
|
+
{
|
|
4044
|
+
t: 4.4,
|
|
4045
|
+
type: "impact",
|
|
4046
|
+
data: {
|
|
4047
|
+
kind: "bite",
|
|
4048
|
+
source: "bite.visible=T>4.4 in the scene's step()"
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
]
|
|
4052
|
+
},
|
|
4053
|
+
{
|
|
4054
|
+
id: "grab_bite_zombie",
|
|
4055
|
+
source: {
|
|
4056
|
+
file: "site/pages/zumbis-personagens/bestiario.html",
|
|
4057
|
+
scene: "ataque (Agarr\xE3o e mordida)",
|
|
4058
|
+
variable: "ZK"
|
|
4059
|
+
},
|
|
4060
|
+
duration: 8,
|
|
4061
|
+
loop: false,
|
|
4062
|
+
channels: "full",
|
|
4063
|
+
keys: [
|
|
4064
|
+
{
|
|
4065
|
+
t: 0,
|
|
4066
|
+
pose: {
|
|
4067
|
+
x: -6,
|
|
4068
|
+
lean: 0.75,
|
|
4069
|
+
armL: -1.35,
|
|
4070
|
+
armR: -1.05,
|
|
4071
|
+
elbL: -0.08,
|
|
4072
|
+
elbR: -0.12,
|
|
4073
|
+
headX: -0.55,
|
|
4074
|
+
drop: -0.1,
|
|
4075
|
+
run: 1
|
|
4076
|
+
}
|
|
4077
|
+
},
|
|
4078
|
+
{
|
|
4079
|
+
t: 1.7,
|
|
4080
|
+
pose: {
|
|
4081
|
+
x: -0.9,
|
|
4082
|
+
lean: 0.75,
|
|
4083
|
+
armL: -1.35,
|
|
4084
|
+
armR: -1.05,
|
|
4085
|
+
elbL: -0.08,
|
|
4086
|
+
elbR: -0.12,
|
|
4087
|
+
headX: -0.55,
|
|
4088
|
+
drop: -0.1,
|
|
4089
|
+
run: 1
|
|
4090
|
+
}
|
|
4091
|
+
},
|
|
4092
|
+
{
|
|
4093
|
+
t: 2.1,
|
|
4094
|
+
pose: {
|
|
4095
|
+
x: -0.32,
|
|
4096
|
+
lean: 0.45,
|
|
4097
|
+
armL: -1.65,
|
|
4098
|
+
armR: -1.6,
|
|
4099
|
+
elbL: -0.25,
|
|
4100
|
+
elbR: -0.3,
|
|
4101
|
+
headX: -0.3,
|
|
4102
|
+
drop: -0.08,
|
|
4103
|
+
legL: -0.6,
|
|
4104
|
+
legR: 0.4,
|
|
4105
|
+
kneeL: 0.5,
|
|
4106
|
+
kneeR: 0.6,
|
|
4107
|
+
run: 0
|
|
4108
|
+
}
|
|
4109
|
+
},
|
|
4110
|
+
{
|
|
4111
|
+
t: 3.6,
|
|
4112
|
+
pose: {
|
|
4113
|
+
x: -0.2,
|
|
4114
|
+
lean: 0.35,
|
|
4115
|
+
armL: -1.5,
|
|
4116
|
+
armR: -1.45,
|
|
4117
|
+
elbL: -1.1,
|
|
4118
|
+
elbR: -1.2,
|
|
4119
|
+
headX: -0.2,
|
|
4120
|
+
drop: -0.06,
|
|
4121
|
+
legL: -0.4,
|
|
4122
|
+
legR: 0.3,
|
|
4123
|
+
kneeL: 0.4,
|
|
4124
|
+
kneeR: 0.4,
|
|
4125
|
+
run: 0
|
|
4126
|
+
}
|
|
4127
|
+
},
|
|
4128
|
+
{
|
|
4129
|
+
t: 4.3,
|
|
4130
|
+
pose: {
|
|
4131
|
+
x: -0.1,
|
|
4132
|
+
lean: 0.5,
|
|
4133
|
+
armL: -1.4,
|
|
4134
|
+
armR: -1.3,
|
|
4135
|
+
elbL: -1.5,
|
|
4136
|
+
elbR: -1.5,
|
|
4137
|
+
headX: 0.35,
|
|
4138
|
+
headY: 0.35,
|
|
4139
|
+
drop: -0.08,
|
|
4140
|
+
legL: -0.35,
|
|
4141
|
+
legR: 0.3,
|
|
4142
|
+
kneeL: 0.4,
|
|
4143
|
+
kneeR: 0.4,
|
|
4144
|
+
run: 0
|
|
4145
|
+
}
|
|
4146
|
+
},
|
|
4147
|
+
{
|
|
4148
|
+
t: 5.6,
|
|
4149
|
+
pose: {
|
|
4150
|
+
x: 0,
|
|
4151
|
+
lean: 0.95,
|
|
4152
|
+
armL: -1.2,
|
|
4153
|
+
armR: -1,
|
|
4154
|
+
elbL: -1.4,
|
|
4155
|
+
elbR: -1.3,
|
|
4156
|
+
headX: 0.5,
|
|
4157
|
+
headY: 0.3,
|
|
4158
|
+
drop: -0.45,
|
|
4159
|
+
legL: -1.2,
|
|
4160
|
+
legR: -0.8,
|
|
4161
|
+
kneeL: 1.8,
|
|
4162
|
+
kneeR: 1.5,
|
|
4163
|
+
run: 0
|
|
4164
|
+
}
|
|
4165
|
+
},
|
|
4166
|
+
{
|
|
4167
|
+
t: 8,
|
|
4168
|
+
pose: {
|
|
4169
|
+
x: 0,
|
|
4170
|
+
lean: 1,
|
|
4171
|
+
armL: -1.15,
|
|
4172
|
+
armR: -0.95,
|
|
4173
|
+
elbL: -1.4,
|
|
4174
|
+
elbR: -1.3,
|
|
4175
|
+
headX: 0.55,
|
|
4176
|
+
headY: 0.3,
|
|
4177
|
+
drop: -0.47,
|
|
4178
|
+
legL: -1.2,
|
|
4179
|
+
legR: -0.8,
|
|
4180
|
+
kneeL: 1.85,
|
|
4181
|
+
kneeR: 1.5,
|
|
4182
|
+
run: 0
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
],
|
|
4186
|
+
events: [
|
|
4187
|
+
{
|
|
4188
|
+
t: 0,
|
|
4189
|
+
type: "phase",
|
|
4190
|
+
data: {
|
|
4191
|
+
label: "pose_clip.grab_bite.phase.approach"
|
|
4192
|
+
}
|
|
4193
|
+
},
|
|
4194
|
+
{
|
|
4195
|
+
t: 1.7,
|
|
4196
|
+
type: "phase",
|
|
4197
|
+
data: {
|
|
4198
|
+
label: "pose_clip.grab_bite.phase.pounce"
|
|
4199
|
+
}
|
|
4200
|
+
},
|
|
4201
|
+
{
|
|
4202
|
+
t: 2.1,
|
|
4203
|
+
type: "phase",
|
|
4204
|
+
data: {
|
|
4205
|
+
label: "pose_clip.grab_bite.phase.grab"
|
|
4206
|
+
}
|
|
4207
|
+
},
|
|
4208
|
+
{
|
|
4209
|
+
t: 4.3,
|
|
4210
|
+
type: "phase",
|
|
4211
|
+
data: {
|
|
4212
|
+
label: "pose_clip.grab_bite.phase.bite"
|
|
4213
|
+
}
|
|
4214
|
+
},
|
|
4215
|
+
{
|
|
4216
|
+
t: 5.6,
|
|
4217
|
+
type: "phase",
|
|
4218
|
+
data: {
|
|
4219
|
+
label: "pose_clip.grab_bite.phase.fall"
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
]
|
|
4223
|
+
}
|
|
4224
|
+
],
|
|
4225
|
+
locales: {
|
|
4226
|
+
"pt-BR": {
|
|
4227
|
+
"pose_clip.grab_bite.phase.approach": "1. Aproxima\xE7\xE3o",
|
|
4228
|
+
"pose_clip.grab_bite.phase.pounce": "2. O bote",
|
|
4229
|
+
"pose_clip.grab_bite.phase.grab": "3. Agarr\xE3o: 3 s para se soltar",
|
|
4230
|
+
"pose_clip.grab_bite.phase.bite": "4. Mordida: infec\xE7\xE3o 100%",
|
|
4231
|
+
"pose_clip.grab_bite.phase.fall": "5. Queda"
|
|
4232
|
+
},
|
|
4233
|
+
en: {
|
|
4234
|
+
"pose_clip.grab_bite.phase.approach": "1. Approach",
|
|
4235
|
+
"pose_clip.grab_bite.phase.pounce": "2. The pounce",
|
|
4236
|
+
"pose_clip.grab_bite.phase.grab": "3. Grab: 3 s to break free",
|
|
4237
|
+
"pose_clip.grab_bite.phase.bite": "4. Bite: 100% infection",
|
|
4238
|
+
"pose_clip.grab_bite.phase.fall": "5. The fall"
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
};
|
|
4242
|
+
var createPoseClipRegistry = (initial = []) => {
|
|
4243
|
+
const clips = /* @__PURE__ */ new Map();
|
|
4244
|
+
const registry = {
|
|
4245
|
+
register(clip) {
|
|
4246
|
+
clips.set(clip.id, clip);
|
|
4247
|
+
return registry;
|
|
4248
|
+
},
|
|
4249
|
+
get: (id) => clips.get(id),
|
|
4250
|
+
list: () => [...clips.values()],
|
|
4251
|
+
remove: (id) => clips.delete(id)
|
|
4252
|
+
};
|
|
4253
|
+
for (const clip of initial) registry.register(clip);
|
|
4254
|
+
return registry;
|
|
4255
|
+
};
|
|
4256
|
+
var POSE_CLIPS = pose_clips_default.clips;
|
|
4257
|
+
var POSE_CLIP_LOCALES = pose_clips_default.locales;
|
|
4258
|
+
var ease = (k) => k * k * (3 - 2 * k);
|
|
4259
|
+
var mix = (a, b, k) => a + (b - a) * k;
|
|
4260
|
+
var mixTriple = (a, b, k) => [mix(a[0], b[0], k), mix(a[1], b[1], k), mix(a[2], b[2], k)];
|
|
4261
|
+
var trackOf = (clip, side) => {
|
|
4262
|
+
const out = [];
|
|
4263
|
+
for (const key of clip.keys) {
|
|
4264
|
+
if (side === "L") {
|
|
4265
|
+
if (key.L === "mirror") {
|
|
4266
|
+
if (key.R) out.push({ t: key.t, key: key.R, mirror: true });
|
|
4267
|
+
} else if (key.L) out.push({ t: key.t, key: key.L, mirror: false });
|
|
4268
|
+
} else {
|
|
4269
|
+
const entry = key[side];
|
|
4270
|
+
if (entry) out.push({ t: key.t, key: entry, mirror: false });
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
return out.sort((a, b) => a.t - b.t);
|
|
4274
|
+
};
|
|
4275
|
+
var sampleTrack = (track, time) => {
|
|
4276
|
+
if (track.length === 0) return void 0;
|
|
4277
|
+
const first = track[0];
|
|
4278
|
+
const last = track[track.length - 1];
|
|
4279
|
+
const of = (entry) => ({ p: [...entry.key.p], r: [...entry.key.r], grip: entry.key.grip, w: entry.key.w ?? 1, mirror: entry.mirror });
|
|
4280
|
+
if (time <= first.t) return of(first);
|
|
4281
|
+
if (time >= last.t) return of(last);
|
|
4282
|
+
let index = 0;
|
|
4283
|
+
while (index < track.length - 2 && time > track[index + 1].t) index++;
|
|
4284
|
+
const a = track[index];
|
|
4285
|
+
const b = track[index + 1];
|
|
4286
|
+
const k = ease(Math.min(1, Math.max(0, (time - a.t) / (b.t - a.t))));
|
|
4287
|
+
const ga = a.key.grip;
|
|
4288
|
+
const gb = b.key.grip;
|
|
4289
|
+
return {
|
|
4290
|
+
p: mixTriple(a.key.p, b.key.p, k),
|
|
4291
|
+
r: mixTriple(a.key.r, b.key.r, k),
|
|
4292
|
+
grip: ga === void 0 ? gb : gb === void 0 ? ga : mix(ga, gb, k),
|
|
4293
|
+
w: mix(a.key.w ?? 1, b.key.w ?? 1, k),
|
|
4294
|
+
mirror: a.mirror
|
|
4295
|
+
};
|
|
4296
|
+
};
|
|
4297
|
+
var sampleFirstPersonClip = (clip, time) => {
|
|
4298
|
+
const out = {};
|
|
4299
|
+
for (const side of ["R", "L", "F"]) {
|
|
4300
|
+
const sample = sampleTrack(trackOf(clip, side), time);
|
|
4301
|
+
if (sample) out[side] = sample;
|
|
4302
|
+
}
|
|
4303
|
+
return out;
|
|
4304
|
+
};
|
|
2274
4305
|
|
|
2275
4306
|
// src/instanced/body-geometry.ts
|
|
2276
4307
|
var negativeFirst = (first, second) => first[0] <= second[0] ? [first, second] : [second, first];
|
|
@@ -2305,13 +4336,13 @@ var GameCharactersThree = (() => {
|
|
|
2305
4336
|
return source;
|
|
2306
4337
|
};
|
|
2307
4338
|
var MERGED_ATTRIBUTES = [["position", 3], ["normal", 3], ["bone", 1], ["vcol", 4], ["pivot", 3], ["ppivot", 3]];
|
|
2308
|
-
var mergeBody = (
|
|
2309
|
-
const total =
|
|
4339
|
+
var mergeBody = (list2, three) => {
|
|
4340
|
+
const total = list2.reduce((sum, geometry) => sum + geometry.attributes.position.count, 0);
|
|
2310
4341
|
const merged = new three.BufferGeometry();
|
|
2311
4342
|
for (const [name, size] of MERGED_ATTRIBUTES) {
|
|
2312
4343
|
const array = new Float32Array(total * size);
|
|
2313
4344
|
let offset = 0;
|
|
2314
|
-
for (const geometry of
|
|
4345
|
+
for (const geometry of list2) {
|
|
2315
4346
|
const part = geometry.attributes[name].array;
|
|
2316
4347
|
array.set(part, offset);
|
|
2317
4348
|
offset += part.length;
|
|
@@ -2321,7 +4352,8 @@ var GameCharactersThree = (() => {
|
|
|
2321
4352
|
merged.computeBoundingSphere();
|
|
2322
4353
|
return merged;
|
|
2323
4354
|
};
|
|
2324
|
-
var buildInstancedBody = (
|
|
4355
|
+
var buildInstancedBody = (host, { segments, body = TEAM_GAME }) => {
|
|
4356
|
+
const three = shapeGeometries(host);
|
|
2325
4357
|
const joints = jointsOf(body);
|
|
2326
4358
|
const parts = [];
|
|
2327
4359
|
const half = Math.max(4, segments >> 1);
|
|
@@ -2449,15 +4481,15 @@ var GameCharactersThree = (() => {
|
|
|
2449
4481
|
copy.setAttribute(name, attribute);
|
|
2450
4482
|
attributes[name] = attribute;
|
|
2451
4483
|
}
|
|
2452
|
-
const
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
4484
|
+
const mesh2 = new three.InstancedMesh(copy, createBodyMaterial(three, { time }), max);
|
|
4485
|
+
mesh2.customDepthMaterial = createBodyMaterial(three, { depth: true, time });
|
|
4486
|
+
mesh2.frustumCulled = false;
|
|
4487
|
+
mesh2.castShadow = castShadow;
|
|
4488
|
+
mesh2.receiveShadow = true;
|
|
4489
|
+
mesh2.count = 0;
|
|
2458
4490
|
let count = 0;
|
|
2459
4491
|
return {
|
|
2460
|
-
mesh,
|
|
4492
|
+
mesh: mesh2,
|
|
2461
4493
|
max,
|
|
2462
4494
|
get count() {
|
|
2463
4495
|
return count;
|
|
@@ -2465,7 +4497,7 @@ var GameCharactersThree = (() => {
|
|
|
2465
4497
|
begin() {
|
|
2466
4498
|
count = 0;
|
|
2467
4499
|
},
|
|
2468
|
-
push({ a, b, c, color, eye, y = 0 }) {
|
|
4500
|
+
push({ a, b, c, color: color2, eye, y = 0 }) {
|
|
2469
4501
|
if (count >= max) return;
|
|
2470
4502
|
const index = count++;
|
|
2471
4503
|
const fourOffset = index * 4;
|
|
@@ -2473,11 +4505,11 @@ var GameCharactersThree = (() => {
|
|
|
2473
4505
|
attributes.iA.array.set(a, fourOffset);
|
|
2474
4506
|
attributes.iB.array.set([b[0], b[1], b[2], b[3] + POSE_SLOTS2 * Math.round((Math.min(MAX_HEIGHT2, Math.max(MIN_HEIGHT2, y)) - MIN_HEIGHT2) * HEIGHT_STEPS2)], fourOffset);
|
|
2475
4507
|
attributes.iC.array.set(c, fourOffset);
|
|
2476
|
-
attributes.iCol.array.set([
|
|
4508
|
+
attributes.iCol.array.set([color2.r, color2.g, color2.b], threeOffset);
|
|
2477
4509
|
attributes.iEye.array.set(eye ? [eye.r, eye.g, eye.b] : [0, 0, 0], threeOffset);
|
|
2478
4510
|
},
|
|
2479
4511
|
end() {
|
|
2480
|
-
|
|
4512
|
+
mesh2.count = count;
|
|
2481
4513
|
for (const attribute of Object.values(attributes)) {
|
|
2482
4514
|
attribute.clearUpdateRanges();
|
|
2483
4515
|
attribute.addUpdateRange(0, count * attribute.itemSize);
|
|
@@ -2502,7 +4534,8 @@ var GameCharactersThree = (() => {
|
|
|
2502
4534
|
var FAR_LOD = 0.6;
|
|
2503
4535
|
var EYE_SCALE = 2.2;
|
|
2504
4536
|
var EYE_DEPTH_SCALE = 1.4;
|
|
2505
|
-
var bakeBody = (
|
|
4537
|
+
var bakeBody = (host, builders, spec, lod) => {
|
|
4538
|
+
const three = shapeGeometries(host);
|
|
2506
4539
|
const root = spec.retrato && builders.buildPortrait ? builders.buildPortrait(spec.retrato, lod) : builders.buildBest({ ...spec, pose: { ...BAKE_NEUTRAL_POSE, ...spec.pose ?? {} } }, lod);
|
|
2507
4540
|
const rig = root.userData.rig;
|
|
2508
4541
|
const P = rig.P;
|
|
@@ -2518,7 +4551,7 @@ var GameCharactersThree = (() => {
|
|
|
2518
4551
|
leg.kn.rotation.set(0, 0, 0);
|
|
2519
4552
|
}
|
|
2520
4553
|
root.updateMatrixWorld(true);
|
|
2521
|
-
const worldPosition2 = (
|
|
4554
|
+
const worldPosition2 = (object2) => object2.getWorldPosition(new three.Vector3()).toArray();
|
|
2522
4555
|
const bones = /* @__PURE__ */ new Map();
|
|
2523
4556
|
bones.set(rig.headG, [1, worldPosition2(rig.headG), [0, 0, 0]]);
|
|
2524
4557
|
for (const arm of rig.arms) {
|
|
@@ -2534,13 +4567,13 @@ var GameCharactersThree = (() => {
|
|
|
2534
4567
|
const trunk = [0, [0, 0.9 * P.s, 0], [0, 0, 0]];
|
|
2535
4568
|
const parts = [];
|
|
2536
4569
|
const topMaterial = spec.retrato ? null : builders.topMaterial();
|
|
2537
|
-
root.traverse((
|
|
2538
|
-
const
|
|
2539
|
-
const material =
|
|
2540
|
-
if (!
|
|
2541
|
-
for (let ancestor =
|
|
4570
|
+
root.traverse((object2) => {
|
|
4571
|
+
const mesh2 = object2;
|
|
4572
|
+
const material = mesh2.material;
|
|
4573
|
+
if (!mesh2.isMesh || !mesh2.geometry || material.transparent) return;
|
|
4574
|
+
for (let ancestor = object2; ancestor; ancestor = ancestor.parent) if (!ancestor.visible) return;
|
|
2542
4575
|
let tag = trunk;
|
|
2543
|
-
for (let ancestor =
|
|
4576
|
+
for (let ancestor = object2.parent; ancestor; ancestor = ancestor.parent) {
|
|
2544
4577
|
const found = bones.get(ancestor);
|
|
2545
4578
|
if (found) {
|
|
2546
4579
|
tag = found;
|
|
@@ -2548,21 +4581,21 @@ var GameCharactersThree = (() => {
|
|
|
2548
4581
|
}
|
|
2549
4582
|
}
|
|
2550
4583
|
let [bone, pivot, parentPivot] = tag;
|
|
2551
|
-
if (
|
|
4584
|
+
if (object2.userData.bone === 11) {
|
|
2552
4585
|
bone = 11;
|
|
2553
4586
|
pivot = bones.get(rig.headG)[1];
|
|
2554
4587
|
parentPivot = [0, 0, 0];
|
|
2555
4588
|
}
|
|
2556
|
-
const isEye2 = !!
|
|
2557
|
-
const
|
|
4589
|
+
const isEye2 = !!object2.userData.eye;
|
|
4590
|
+
const color2 = material.color;
|
|
2558
4591
|
const materialId = isEye2 ? 3 : material === topMaterial ? 1 : 6;
|
|
2559
4592
|
if (isEye2 && bone === 1) bone = 6;
|
|
2560
|
-
const geometry =
|
|
2561
|
-
geometry.applyMatrix4(
|
|
4593
|
+
const geometry = mesh2.geometry.clone();
|
|
4594
|
+
geometry.applyMatrix4(mesh2.matrixWorld);
|
|
2562
4595
|
const isBlood = material.polygonOffset && material.isMeshStandardMaterial;
|
|
2563
4596
|
if (isBlood) liftAlongNormals(geometry, lod < FAR_LOD ? BLOOD_LIFT_FAR : BLOOD_LIFT_NEAR);
|
|
2564
4597
|
if (isEye2) enlargeEye(three, geometry, lod < FAR_LOD ? 1 : 0.68);
|
|
2565
|
-
const colors = isBlood ? [Math.max(
|
|
4598
|
+
const colors = isBlood ? [Math.max(color2.r * 1.7, 0.3), Math.min(color2.g * 1.7, 0.04), Math.min(color2.b * 1.7, 0.04), materialId] : [color2.r, color2.g, color2.b, materialId];
|
|
2566
4599
|
parts.push({ g: geometry, bone, pv: pivot, pp: parentPivot, col: colors });
|
|
2567
4600
|
});
|
|
2568
4601
|
return { geo: mergeBaked(three, parts), s: P.s, bulk: P.bulk, verts: parts.reduce((sum, part) => sum + part.g.attributes.position.count, 0) };
|
|
@@ -2588,7 +4621,7 @@ var GameCharactersThree = (() => {
|
|
|
2588
4621
|
}
|
|
2589
4622
|
const position = new Float32Array(vertexTotal * 3);
|
|
2590
4623
|
const normal = new Float32Array(vertexTotal * 3);
|
|
2591
|
-
const
|
|
4624
|
+
const color2 = new Float32Array(vertexTotal * 4);
|
|
2592
4625
|
const bone = new Float32Array(vertexTotal);
|
|
2593
4626
|
const pivot = new Float32Array(vertexTotal * 3);
|
|
2594
4627
|
const parentPivot = new Float32Array(vertexTotal * 3);
|
|
@@ -2600,7 +4633,7 @@ var GameCharactersThree = (() => {
|
|
|
2600
4633
|
position.set(part.g.attributes.position.array, vertexOffset * 3);
|
|
2601
4634
|
normal.set(part.g.attributes.normal.array, vertexOffset * 3);
|
|
2602
4635
|
for (let vertex = 0; vertex < count; vertex++) {
|
|
2603
|
-
|
|
4636
|
+
color2.set(part.col, (vertexOffset + vertex) * 4);
|
|
2604
4637
|
bone[vertexOffset + vertex] = part.bone;
|
|
2605
4638
|
pivot.set(part.pv, (vertexOffset + vertex) * 3);
|
|
2606
4639
|
parentPivot.set(part.pp, (vertexOffset + vertex) * 3);
|
|
@@ -2615,7 +4648,7 @@ var GameCharactersThree = (() => {
|
|
|
2615
4648
|
const out = new three.BufferGeometry();
|
|
2616
4649
|
out.setAttribute("position", new three.BufferAttribute(position, 3));
|
|
2617
4650
|
out.setAttribute("normal", new three.BufferAttribute(normal, 3));
|
|
2618
|
-
out.setAttribute("vcol", new three.BufferAttribute(
|
|
4651
|
+
out.setAttribute("vcol", new three.BufferAttribute(color2, 4));
|
|
2619
4652
|
out.setAttribute("bone", new three.BufferAttribute(bone, 1));
|
|
2620
4653
|
out.setAttribute("pivot", new three.BufferAttribute(pivot, 3));
|
|
2621
4654
|
out.setAttribute("ppivot", new three.BufferAttribute(parentPivot, 3));
|
|
@@ -2651,20 +4684,21 @@ var GameCharactersThree = (() => {
|
|
|
2651
4684
|
var SPIN_DAMPING = 0.5;
|
|
2652
4685
|
var REST_SPEED = 0.4;
|
|
2653
4686
|
var LEG_COLOR = 2896704;
|
|
2654
|
-
var createFlyingLimbs = (
|
|
4687
|
+
var createFlyingLimbs = (host, { max = 40, random, effects = {} }) => {
|
|
4688
|
+
const three = shapeGeometries(host);
|
|
2655
4689
|
const limbs = [];
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
4690
|
+
const mesh2 = new three.InstancedMesh(new three.CapsuleGeometry(0.065, 0.32, 2, 6), new three.MeshLambertMaterial({ color: 16777215 }), max);
|
|
4691
|
+
mesh2.count = 0;
|
|
4692
|
+
mesh2.castShadow = true;
|
|
4693
|
+
mesh2.setColorAt(0, new three.Color());
|
|
2660
4694
|
const matrix = new three.Matrix4();
|
|
2661
4695
|
const quaternion = new three.Quaternion();
|
|
2662
4696
|
const scale = new three.Vector3();
|
|
2663
4697
|
const position = new three.Vector3();
|
|
2664
4698
|
const euler = new three.Euler();
|
|
2665
|
-
const
|
|
4699
|
+
const color2 = new three.Color();
|
|
2666
4700
|
return {
|
|
2667
|
-
mesh,
|
|
4701
|
+
mesh: mesh2,
|
|
2668
4702
|
limbs,
|
|
2669
4703
|
launch({ x, z, kind, color: sleeve, blood }) {
|
|
2670
4704
|
const isLeg2 = kind === "leg";
|
|
@@ -2717,12 +4751,12 @@ var GameCharactersThree = (() => {
|
|
|
2717
4751
|
}
|
|
2718
4752
|
}
|
|
2719
4753
|
matrix.compose(position.set(limb2.x, limb2.y, limb2.z), quaternion.setFromEuler(euler.set(limb2.rx, limb2.rz, 0)), scale.set(1, limb2.len, 1));
|
|
2720
|
-
|
|
2721
|
-
|
|
4754
|
+
mesh2.setMatrixAt(index, matrix);
|
|
4755
|
+
mesh2.setColorAt(index, color2.set(limb2.col));
|
|
2722
4756
|
});
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
if (
|
|
4757
|
+
mesh2.count = limbs.length;
|
|
4758
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
4759
|
+
if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
|
|
2726
4760
|
}
|
|
2727
4761
|
};
|
|
2728
4762
|
};
|
|
@@ -2837,7 +4871,8 @@ var GameCharactersThree = (() => {
|
|
|
2837
4871
|
};
|
|
2838
4872
|
|
|
2839
4873
|
// src/rich/best-builder.ts
|
|
2840
|
-
var createBestBuilder = (
|
|
4874
|
+
var createBestBuilder = (host, { types, random = Math.random }) => {
|
|
4875
|
+
const THREE = shapeGeometries(host);
|
|
2841
4876
|
const options = { types, random };
|
|
2842
4877
|
let LK = 1, topM_ref = null;
|
|
2843
4878
|
const SPH = (r, w = 8, h = 6, ...a) => new THREE.SphereGeometry(r, Math.max(4, Math.round(w * LK)), Math.max(3, Math.round(h * LK)), ...a);
|
|
@@ -2863,7 +4898,7 @@ var GameCharactersThree = (() => {
|
|
|
2863
4898
|
const PANTS = 2896704, GRAY2 = 4868678, RED2 = 8000012;
|
|
2864
4899
|
const col = (a, b, t) => new THREE.Color(a).lerp(new THREE.Color(b), t);
|
|
2865
4900
|
const geoBox = (w, h, d) => new THREE.BoxGeometry(w, h, d);
|
|
2866
|
-
const
|
|
4901
|
+
const box2 = (w, h, d, m) => new THREE.Mesh(geoBox(w, h, d), m);
|
|
2867
4902
|
const limbGeo = (w, len) => {
|
|
2868
4903
|
const g = geoBox(w, len, w);
|
|
2869
4904
|
g.translate(0, -len / 2, 0);
|
|
@@ -3017,7 +5052,7 @@ var GameCharactersThree = (() => {
|
|
|
3017
5052
|
st.scale.set(1, 0.35, 1);
|
|
3018
5053
|
add(p2, CYL(r * 0.32, r * 0.36, 0.07, 10), M(15261900), 0, y + (up ? 0.04 : -0.04), 0);
|
|
3019
5054
|
};
|
|
3020
|
-
const
|
|
5055
|
+
const ring2 = (p2, r, h, m, y, sx, sz) => {
|
|
3021
5056
|
const o = add(p2, CYL(r, r, h, 24), m, 0, y, 0);
|
|
3022
5057
|
o.scale.set(sx || 1.12, 1, sz || 0.7);
|
|
3023
5058
|
return o;
|
|
@@ -3141,8 +5176,8 @@ var GameCharactersThree = (() => {
|
|
|
3141
5176
|
for (let i = 0; i < 3; i++) add(U, geoBox(0.11, 0.13, 0.05), M(O.vest), -0.15 + i * 0.15, 1.17, 0.17);
|
|
3142
5177
|
}
|
|
3143
5178
|
if (O.stripes) {
|
|
3144
|
-
|
|
3145
|
-
|
|
5179
|
+
ring2(U, 0.205, 0.05, M(O.stripes), 1.03, 1.13, 0.68);
|
|
5180
|
+
ring2(U, 0.268, 0.05, M(O.stripes), 1.42, 1.13, 0.67);
|
|
3146
5181
|
}
|
|
3147
5182
|
if (d === 3 && !P.bloat) [0, 1, 2].forEach((k) => add(U, geoBox(0.2, 0.022, 0.02), M(14208952), 0, 1.3 + k * 0.065, 0.168));
|
|
3148
5183
|
if (!spec.human) {
|
|
@@ -3436,8 +5471,8 @@ var GameCharactersThree = (() => {
|
|
|
3436
5471
|
},
|
|
3437
5472
|
{
|
|
3438
5473
|
ids: ["stubble"],
|
|
3439
|
-
apply: ({ Hd, THREE, add, cmat, scaleHex, sp }) => {
|
|
3440
|
-
const b = add(Hd, new THREE.SphereGeometry(0.123, 16, 12), cmat(
|
|
5474
|
+
apply: ({ Hd, THREE, add, cmat, scaleHex: scaleHex2, sp }) => {
|
|
5475
|
+
const b = add(Hd, new THREE.SphereGeometry(0.123, 16, 12), cmat(scaleHex2(sp.skin, 0.62)), 0, 1.705, 0.032);
|
|
3441
5476
|
b.scale.set(1, 0.74, 1);
|
|
3442
5477
|
}
|
|
3443
5478
|
},
|
|
@@ -3755,8 +5790,8 @@ var GameCharactersThree = (() => {
|
|
|
3755
5790
|
add(U, Bx(0.1, 0.12, 0.09), m, -0.17, 0.95, -0.15);
|
|
3756
5791
|
return;
|
|
3757
5792
|
},
|
|
3758
|
-
belt: ({ U, m, ring }) => {
|
|
3759
|
-
|
|
5793
|
+
belt: ({ U, m, ring: ring2 }) => {
|
|
5794
|
+
ring2(U, 0.212, 0.07, m, 0.95, 1.12, 0.72);
|
|
3760
5795
|
return;
|
|
3761
5796
|
},
|
|
3762
5797
|
holster: ({ Bx, add, cmat, legs }) => {
|
|
@@ -3768,9 +5803,9 @@ var GameCharactersThree = (() => {
|
|
|
3768
5803
|
add(U, Bx(0.012, 0.1, 0.012), cmat(1710618), 0.17, 1.52, 0.18);
|
|
3769
5804
|
return;
|
|
3770
5805
|
},
|
|
3771
|
-
stripes: ({ THREE, U, add, arms, m, ring }) => {
|
|
3772
|
-
|
|
3773
|
-
|
|
5806
|
+
stripes: ({ THREE, U, add, arms, m, ring: ring2 }) => {
|
|
5807
|
+
ring2(U, 0.205, 0.05, m, 1.03, 1.13, 0.68);
|
|
5808
|
+
ring2(U, 0.268, 0.05, m, 1.42, 1.13, 0.67);
|
|
3774
5809
|
arms.forEach((a) => {
|
|
3775
5810
|
const r = add(a.el, new THREE.CylinderGeometry(0.058, 0.054, 0.05, 18), m, 0, -0.17, 0);
|
|
3776
5811
|
});
|
|
@@ -3880,17 +5915,17 @@ var GameCharactersThree = (() => {
|
|
|
3880
5915
|
add(arms[1].g, new THREE.CylinderGeometry(0.08, 0.078, 0.08, 18), m, 0, -0.12, 0);
|
|
3881
5916
|
return;
|
|
3882
5917
|
},
|
|
3883
|
-
tiedsleeves: ({ Bx, U, add, legs, m, ring }) => {
|
|
3884
|
-
|
|
5918
|
+
tiedsleeves: ({ Bx, U, add, legs, m, ring: ring2 }) => {
|
|
5919
|
+
ring2(U, 0.215, 0.1, m, 0.95, 1.12, 0.74);
|
|
3885
5920
|
legs.forEach((l) => add(l.g, Bx(0.1, 0.26, 0.05), m, l.s * 0.02, -0.1, 0.105));
|
|
3886
5921
|
return;
|
|
3887
5922
|
},
|
|
3888
|
-
labcoat: ({ Bx, THREE, U, add, arms, cmat, fem, legs, m, ring, torsoGeo }) => {
|
|
5923
|
+
labcoat: ({ Bx, THREE, U, add, arms, cmat, fem, legs, m, ring: ring2, torsoGeo }) => {
|
|
3889
5924
|
{
|
|
3890
5925
|
const g = torsoGeo(fem);
|
|
3891
5926
|
g.scale(1.06, 1, 1.12);
|
|
3892
5927
|
add(U, g, m, 0, 0, 0);
|
|
3893
|
-
|
|
5928
|
+
ring2(U, 0.22, 0.14, m, 0.9, 1.12, 0.76);
|
|
3894
5929
|
legs.forEach((l) => add(l.g, new THREE.CylinderGeometry(0.145, 0.165, 0.46, 20, 1, true).translate(0, -0.21, 0), m, 0, 0, 0));
|
|
3895
5930
|
add(U, Bx(0.025, 0.62, 0.012), cmat(10132114), 0, 1.2, 0.19);
|
|
3896
5931
|
arms.forEach((a) => {
|
|
@@ -3980,8 +6015,8 @@ var GameCharactersThree = (() => {
|
|
|
3980
6015
|
add(U, Bx(0.05, 0.75, 0.02), cmat(4864546), 0, 1.22, 0.195, 0, 0, -0.75);
|
|
3981
6016
|
return;
|
|
3982
6017
|
},
|
|
3983
|
-
scarf: ({ PI, THREE, U, add, m, ring }) => {
|
|
3984
|
-
|
|
6018
|
+
scarf: ({ PI, THREE, U, add, m, ring: ring2 }) => {
|
|
6019
|
+
ring2(U, 0.1, 0.08, m, 1.57, 1.3, 1.2);
|
|
3985
6020
|
add(U, new THREE.ConeGeometry(0.08, 0.14, 3), m, 0, 1.47, 0.15, PI, 0, 0);
|
|
3986
6021
|
return;
|
|
3987
6022
|
},
|
|
@@ -4024,12 +6059,12 @@ var GameCharactersThree = (() => {
|
|
|
4024
6059
|
return;
|
|
4025
6060
|
}
|
|
4026
6061
|
},
|
|
4027
|
-
refl: ({ Bx, THREE, U, add, cmat, m, ring }) => {
|
|
6062
|
+
refl: ({ Bx, THREE, U, add, cmat, m, ring: ring2 }) => {
|
|
4028
6063
|
{
|
|
4029
6064
|
const v = add(U, new THREE.CylinderGeometry(0.287, 0.252, 0.46, 20), m, 0, 1.3, 0);
|
|
4030
6065
|
v.scale.set(1.13, 1, 0.72);
|
|
4031
|
-
|
|
4032
|
-
|
|
6066
|
+
ring2(U, 0.29, 0.035, cmat(13159632, { metalness: 0.3, roughness: 0.4 }), 1.2, 1.13, 0.73);
|
|
6067
|
+
ring2(U, 0.3, 0.035, cmat(13159632, { metalness: 0.3, roughness: 0.4 }), 1.37, 1.13, 0.73);
|
|
4033
6068
|
add(U, Bx(0.17, 0.055, 0.02), cmat(1714794), 0, 1.47, 0.205);
|
|
4034
6069
|
return;
|
|
4035
6070
|
}
|
|
@@ -4129,9 +6164,10 @@ var GameCharactersThree = (() => {
|
|
|
4129
6164
|
EXTRAS.dbl_front = EXTRAS.rifle_front;
|
|
4130
6165
|
|
|
4131
6166
|
// src/rich/person-kit.ts
|
|
4132
|
-
var createPersonKit = (
|
|
6167
|
+
var createPersonKit = (host, { random: R, createCanvas = () => document.createElement("canvas") }) => {
|
|
6168
|
+
const THREE = shapeGeometries(host);
|
|
4133
6169
|
const T = THREE;
|
|
4134
|
-
const
|
|
6170
|
+
const scaleHex2 = (hex2, factor) => {
|
|
4135
6171
|
const channel = (shift) => (hex2 >> shift & 255) / 255 * factor * 255;
|
|
4136
6172
|
return channel(16) << 16 ^ channel(8) << 8 ^ channel(0) << 0;
|
|
4137
6173
|
};
|
|
@@ -4188,7 +6224,7 @@ var GameCharactersThree = (() => {
|
|
|
4188
6224
|
function ball(parent, r, m, x, y, z) {
|
|
4189
6225
|
return add(parent, new THREE.SphereGeometry(r, SEG, 12), m, x || 0, y || 0, z || 0);
|
|
4190
6226
|
}
|
|
4191
|
-
function
|
|
6227
|
+
function ring2(parent, r, h, m, y, sx, sz) {
|
|
4192
6228
|
const o = add(parent, new THREE.CylinderGeometry(r, r, h, 20), m, 0, y, 0);
|
|
4193
6229
|
o.scale.set(sx || 1.12, 1, sz || 0.7);
|
|
4194
6230
|
return o;
|
|
@@ -4234,17 +6270,17 @@ var GameCharactersThree = (() => {
|
|
|
4234
6270
|
const k = c + "|" + o;
|
|
4235
6271
|
return stainMs[k] || (stainMs[k] = new THREE.MeshStandardMaterial(Object.assign({ color: c, roughness: 0.95, metalness: 0, polygonOffset: true, polygonOffsetFactor: -2, polygonOffsetUnits: -2, side: THREE.DoubleSide }, o || {})));
|
|
4236
6272
|
}
|
|
4237
|
-
function splat(parent, x, y, z, r,
|
|
4238
|
-
const skin = kind === "skin", fresh =
|
|
6273
|
+
function splat(parent, x, y, z, r, color2, n, out, kind) {
|
|
6274
|
+
const skin = kind === "skin", fresh = color2 === 9046538, push = (o) => {
|
|
4239
6275
|
o.castShadow = false;
|
|
4240
6276
|
if (out) out.push(o);
|
|
4241
6277
|
return o;
|
|
4242
6278
|
};
|
|
4243
6279
|
const sy = 0.8 + R() * 0.4, seed = R() * 10;
|
|
4244
|
-
const core = skin ? stainM(
|
|
6280
|
+
const core = skin ? stainM(color2, { roughness: 0.12, metalness: 0.1 }) : stainM(scaleHex2(color2, 0.72));
|
|
4245
6281
|
const a = push(add(parent, blobGeo(blobR(40, skin ? 0.4 : 0.9, seed), r * 0.82, r * 0.82 * sy), core, x, y, z + 4e-3));
|
|
4246
6282
|
if (!skin) {
|
|
4247
|
-
push(add(parent, blobGeo(blobR(48, 1.6, seed + 0.3), r * 1.3, r * 1.3 * sy), stainM(
|
|
6283
|
+
push(add(parent, blobGeo(blobR(48, 1.6, seed + 0.3), r * 1.3, r * 1.3 * sy), stainM(color2, { transparent: true, opacity: 0.5, depthWrite: false }), x, y, z + 2e-3));
|
|
4248
6284
|
}
|
|
4249
6285
|
for (let k = 0; k < (n || 0); k++) {
|
|
4250
6286
|
const ang = R() * 6.28, d = r * (1.2 + R() * 0.9), rr = r * (0.16 + R() * 0.2);
|
|
@@ -4261,14 +6297,15 @@ var GameCharactersThree = (() => {
|
|
|
4261
6297
|
}
|
|
4262
6298
|
return a;
|
|
4263
6299
|
}
|
|
4264
|
-
return { THREE, PI, SEG, R, scaleHex, Bx, add, cmat, ball, ring, cap, limb: limb2, mlimb, torsoGeo, splat, stainM, chessTex };
|
|
6300
|
+
return { THREE, PI, SEG, R, scaleHex: scaleHex2, Bx, add, cmat, ball, ring: ring2, cap, limb: limb2, mlimb, torsoGeo, splat, stainM, chessTex };
|
|
4265
6301
|
};
|
|
4266
6302
|
|
|
4267
6303
|
// src/rich/person-builder.ts
|
|
4268
6304
|
var PAGE_EYES = { lento: 14926657, corredor: 15022379, brutamonte: 15698474 };
|
|
4269
|
-
var createPersonBuilder = (
|
|
6305
|
+
var createPersonBuilder = (host, options = {}) => {
|
|
6306
|
+
const THREE = shapeGeometries(host);
|
|
4270
6307
|
const kit = createPersonKit(THREE, { random: options.random ?? createRng(77), createCanvas: options.createCanvas });
|
|
4271
|
-
const { PI, R, scaleHex, Bx, add, cmat, ball, ring, cap, limb: limb2, mlimb, torsoGeo, splat, stainM, chessTex } = kit;
|
|
6308
|
+
const { PI, R, scaleHex: scaleHex2, Bx, add, cmat, ball, ring: ring2, cap, limb: limb2, mlimb, torsoGeo, splat, stainM, chessTex } = kit;
|
|
4272
6309
|
const reduce = options.reducedMotion ?? false;
|
|
4273
6310
|
const detailed = (options.detail ?? 0) === 0;
|
|
4274
6311
|
const EYE = { ...PAGE_EYES, ...options.zombieEyes };
|
|
@@ -4369,7 +6406,7 @@ var GameCharactersThree = (() => {
|
|
|
4369
6406
|
const ridge = ball(Hd, 0.1, skinM, 0, 1.842, 0.105);
|
|
4370
6407
|
ridge.scale.set(1.3, 0.28, 0.55);
|
|
4371
6408
|
add(Hd, new THREE.SphereGeometry(0.021, 10, 8), skinM, 0, 1.762, 0.182);
|
|
4372
|
-
add(Hd, Bx(0.064, 0.013, 0.02), cmat(
|
|
6409
|
+
add(Hd, Bx(0.064, 0.013, 0.02), cmat(scaleHex2(sp.skin, 0.68)), 0, 1.703, 0.156);
|
|
4373
6410
|
const bags = add(Hd, Bx(0.2, 0.022, 0.012), new THREE.MeshBasicMaterial({ color: 3811882, transparent: true, opacity: 0 }), 0, 1.79, 0.166);
|
|
4374
6411
|
bags.castShadow = false;
|
|
4375
6412
|
const hc = cmat(sp.hair.c), hs = sp.hair.s;
|
|
@@ -4406,7 +6443,7 @@ var GameCharactersThree = (() => {
|
|
|
4406
6443
|
t.rotation.x = 0.25;
|
|
4407
6444
|
}
|
|
4408
6445
|
const f = sp.face || [];
|
|
4409
|
-
for (const feature of FACE_FEATURES) if (feature.ids.some((id) => f.includes(id))) feature.apply({ THREE, PI, Hd, add, Bx, cmat, hc, sp, f, scaleHex });
|
|
6446
|
+
for (const feature of FACE_FEATURES) if (feature.ids.some((id) => f.includes(id))) feature.apply({ THREE, PI, Hd, add, Bx, cmat, hc, sp, f, scaleHex: scaleHex2 });
|
|
4410
6447
|
if (sp.hat) {
|
|
4411
6448
|
const k = sp.hat.k, c = cmat(sp.hat.c);
|
|
4412
6449
|
HATS.find((hat) => hat.kinds.includes(k))?.apply({ THREE, PI, Hd, add, Bx, cmat, cap, c, k, sp });
|
|
@@ -4445,7 +6482,7 @@ var GameCharactersThree = (() => {
|
|
|
4445
6482
|
for (const e of sp.ex || []) {
|
|
4446
6483
|
const [k, c] = e;
|
|
4447
6484
|
const m = c !== void 0 ? cmat(c) : null;
|
|
4448
|
-
EXTRAS[k]?.({ THREE, PI, U, Hd, root, add, Bx, cmat, ring, torsoGeo, legs, arms, fem, topM, m, e, k, headG, fig, upper, skinM, sp, splat, R, ball, cap, hc });
|
|
6485
|
+
EXTRAS[k]?.({ THREE, PI, U, Hd, root, add, Bx, cmat, ring: ring2, torsoGeo, legs, arms, fem, topM, m, e, k, headG, fig, upper, skinM, sp, splat, R, ball, cap, hc });
|
|
4449
6486
|
}
|
|
4450
6487
|
if (sp.back === "shotgun") {
|
|
4451
6488
|
const g = new THREE.Group();
|
|
@@ -4625,9 +6662,9 @@ var GameCharactersThree = (() => {
|
|
|
4625
6662
|
// src/eyes.ts
|
|
4626
6663
|
var GLOW_SIZE = 64;
|
|
4627
6664
|
var createGlowTexture = (three, createCanvas) => {
|
|
4628
|
-
const
|
|
4629
|
-
if (!
|
|
4630
|
-
const canvas =
|
|
6665
|
+
const make2 = createCanvas ?? (typeof document === "undefined" ? void 0 : () => document.createElement("canvas"));
|
|
6666
|
+
if (!make2) return null;
|
|
6667
|
+
const canvas = make2();
|
|
4631
6668
|
canvas.width = canvas.height = GLOW_SIZE;
|
|
4632
6669
|
const context = canvas.getContext("2d");
|
|
4633
6670
|
if (!context) return null;
|
|
@@ -4639,35 +6676,36 @@ var GameCharactersThree = (() => {
|
|
|
4639
6676
|
context.fillRect(0, 0, GLOW_SIZE, GLOW_SIZE);
|
|
4640
6677
|
return new three.CanvasTexture(canvas);
|
|
4641
6678
|
};
|
|
4642
|
-
var createEye = (
|
|
6679
|
+
var createEye = (host, { color: color2, radius = 0.02, glow, glowOpacity = 0.55, texture = null }) => {
|
|
6680
|
+
const three = shapeGeometries(host);
|
|
4643
6681
|
const eye = new three.Group();
|
|
4644
|
-
const ball = new three.Mesh(new three.SphereGeometry(radius, 10, 8), new three.MeshBasicMaterial({ color }));
|
|
6682
|
+
const ball = new three.Mesh(new three.SphereGeometry(radius, 10, 8), new three.MeshBasicMaterial({ color: color2 }));
|
|
4645
6683
|
ball.userData.eye = 1;
|
|
4646
6684
|
eye.add(ball);
|
|
4647
6685
|
const size = glow ?? radius * 5;
|
|
4648
6686
|
if (size > 0) {
|
|
4649
|
-
const sprite = new three.Sprite(new three.SpriteMaterial({ map: texture, color, transparent: true, blending: three.AdditiveBlending, depthWrite: false, opacity: glowOpacity }));
|
|
6687
|
+
const sprite = new three.Sprite(new three.SpriteMaterial({ map: texture, color: color2, transparent: true, blending: three.AdditiveBlending, depthWrite: false, opacity: glowOpacity }));
|
|
4650
6688
|
sprite.scale.setScalar(size);
|
|
4651
6689
|
sprite.userData.eyeGlow = 1;
|
|
4652
6690
|
eye.add(sprite);
|
|
4653
6691
|
}
|
|
4654
6692
|
return eye;
|
|
4655
6693
|
};
|
|
4656
|
-
var isEye = (
|
|
6694
|
+
var isEye = (object2) => object2.userData.eye === 1 || object2.userData.eye === true;
|
|
4657
6695
|
var findEyes = (three, root) => {
|
|
4658
6696
|
const markable = [];
|
|
4659
6697
|
const glows = [];
|
|
4660
6698
|
const base = /* @__PURE__ */ new Map();
|
|
4661
|
-
root.traverse((
|
|
4662
|
-
const
|
|
4663
|
-
if (
|
|
4664
|
-
const sprite =
|
|
6699
|
+
root.traverse((object2) => {
|
|
6700
|
+
const mesh2 = object2;
|
|
6701
|
+
if (mesh2.isMesh && isEye(object2)) markable.push(mesh2);
|
|
6702
|
+
const sprite = object2;
|
|
4665
6703
|
if (sprite.isSprite && sprite.material.blending === three.AdditiveBlending) glows.push(sprite);
|
|
4666
6704
|
});
|
|
4667
6705
|
const remember = (material) => {
|
|
4668
6706
|
if (material.color && !base.has(material)) base.set(material, { color: material.color.clone(), opacity: material.opacity });
|
|
4669
6707
|
};
|
|
4670
|
-
markable.forEach((
|
|
6708
|
+
markable.forEach((mesh2) => remember(mesh2.material));
|
|
4671
6709
|
glows.forEach((sprite) => remember(sprite.material));
|
|
4672
6710
|
const setBrightness = (level) => {
|
|
4673
6711
|
const factor = Math.min(1, Math.max(0, level));
|
|
@@ -4687,14 +6725,14 @@ var GameCharactersThree = (() => {
|
|
|
4687
6725
|
on: () => setBrightness(1),
|
|
4688
6726
|
isolate(only) {
|
|
4689
6727
|
if (!only) {
|
|
4690
|
-
hidden.splice(0).forEach((
|
|
6728
|
+
hidden.splice(0).forEach((object2) => object2.visible = true);
|
|
4691
6729
|
return;
|
|
4692
6730
|
}
|
|
4693
|
-
root.traverse((
|
|
4694
|
-
const
|
|
4695
|
-
if (
|
|
4696
|
-
|
|
4697
|
-
hidden.push(
|
|
6731
|
+
root.traverse((object2) => {
|
|
6732
|
+
const mesh2 = object2;
|
|
6733
|
+
if (mesh2.isMesh && !isEye(object2) && object2.visible) {
|
|
6734
|
+
object2.visible = false;
|
|
6735
|
+
hidden.push(object2);
|
|
4698
6736
|
}
|
|
4699
6737
|
});
|
|
4700
6738
|
}
|
|
@@ -4814,9 +6852,9 @@ var GameCharactersThree = (() => {
|
|
|
4814
6852
|
group.add(end);
|
|
4815
6853
|
strands.push(end);
|
|
4816
6854
|
}
|
|
4817
|
-
group.traverse((
|
|
4818
|
-
|
|
4819
|
-
if (
|
|
6855
|
+
group.traverse((object2) => {
|
|
6856
|
+
object2.userData.stump = 1;
|
|
6857
|
+
if (object2.isMesh) object2.castShadow = true;
|
|
4820
6858
|
});
|
|
4821
6859
|
return strands;
|
|
4822
6860
|
};
|
|
@@ -4831,15 +6869,15 @@ var GameCharactersThree = (() => {
|
|
|
4831
6869
|
const group = new three.Group();
|
|
4832
6870
|
group.position.y = y;
|
|
4833
6871
|
parent.add(group);
|
|
4834
|
-
const
|
|
4835
|
-
|
|
4836
|
-
group.add(
|
|
6872
|
+
const torus2 = new three.Mesh(new three.TorusGeometry(radius, 0.022, 8, 18), materials.wound);
|
|
6873
|
+
torus2.rotation.x = Math.PI / 2;
|
|
6874
|
+
group.add(torus2);
|
|
4837
6875
|
for (let k = 0; k < 3; k++) {
|
|
4838
6876
|
const strip = new three.Mesh(new three.CylinderGeometry(8e-3, 8e-3, 0.12, 6), materials.wound);
|
|
4839
6877
|
strip.position.set((k - 1) * 0.03, 0.03, 0.02);
|
|
4840
6878
|
group.add(strip);
|
|
4841
6879
|
}
|
|
4842
|
-
group.traverse((
|
|
6880
|
+
group.traverse((object2) => object2.userData.stump = 1);
|
|
4843
6881
|
return group;
|
|
4844
6882
|
};
|
|
4845
6883
|
|
|
@@ -4858,8 +6896,8 @@ var GameCharactersThree = (() => {
|
|
|
4858
6896
|
const trim = () => {
|
|
4859
6897
|
while (pieces.length > max) pieces.shift()?.object.removeFromParent();
|
|
4860
6898
|
};
|
|
4861
|
-
const add = (
|
|
4862
|
-
const piece = { object, velocity, spin, rest: false, ...tag ? { tag } : {} };
|
|
6899
|
+
const add = (object2, velocity, spin, tag) => {
|
|
6900
|
+
const piece = { object: object2, velocity, spin, rest: false, ...tag ? { tag } : {} };
|
|
4863
6901
|
pieces.push(piece);
|
|
4864
6902
|
trim();
|
|
4865
6903
|
return piece;
|
|
@@ -4936,8 +6974,8 @@ var GameCharactersThree = (() => {
|
|
|
4936
6974
|
};
|
|
4937
6975
|
var exitWound = (three, materials, random, segment, point, direction, damage) => {
|
|
4938
6976
|
const meshes = [];
|
|
4939
|
-
segment.traverse((
|
|
4940
|
-
if (
|
|
6977
|
+
segment.traverse((object2) => {
|
|
6978
|
+
if (object2.isMesh && !object2.userData.wound && !object2.userData.stump) meshes.push(object2);
|
|
4941
6979
|
});
|
|
4942
6980
|
const raycaster = new three.Raycaster(point.clone().addScaledVector(direction, 0.6), direction.clone().negate(), 0, 0.6);
|
|
4943
6981
|
const hit = raycaster.intersectObjects(meshes)[0];
|
|
@@ -4982,9 +7020,9 @@ var GameCharactersThree = (() => {
|
|
|
4982
7020
|
crater.userData.wound = 1;
|
|
4983
7021
|
return crater;
|
|
4984
7022
|
};
|
|
4985
|
-
var explodeChunks = (three, materials, random,
|
|
4986
|
-
const centre =
|
|
4987
|
-
const size =
|
|
7023
|
+
var explodeChunks = (three, materials, random, box2, direction, fling) => {
|
|
7024
|
+
const centre = box2.getCenter(new three.Vector3());
|
|
7025
|
+
const size = box2.getSize(new three.Vector3()).length();
|
|
4988
7026
|
const chunkMaterials = [materials.flesh, materials.bruise, materials.bone, materials.flesh, materials.meat];
|
|
4989
7027
|
for (let i = 0; i < 14; i++) {
|
|
4990
7028
|
const chunk = new three.Mesh(new three.BoxGeometry(0.015 + random() * size * 0.18, 0.015 + random() * size * 0.14, 0.015 + random() * size * 0.14), chunkMaterials[i % 5]);
|
|
@@ -5017,7 +7055,7 @@ var GameCharactersThree = (() => {
|
|
|
5017
7055
|
};
|
|
5018
7056
|
|
|
5019
7057
|
// src/wounds/wounds.ts
|
|
5020
|
-
var worldPosition = (three,
|
|
7058
|
+
var worldPosition = (three, object2) => object2.getWorldPosition(new three.Vector3());
|
|
5021
7059
|
var createWounds = (three, options) => {
|
|
5022
7060
|
const { random, scene: scene2, bridge, sound } = options;
|
|
5023
7061
|
const materials = options.materials ?? createWoundMaterials(three);
|
|
@@ -5028,11 +7066,11 @@ var GameCharactersThree = (() => {
|
|
|
5028
7066
|
if (!found) throw new Error("game-characters-three: this body is not tracked by the wounds (call `track` first)");
|
|
5029
7067
|
return found;
|
|
5030
7068
|
};
|
|
5031
|
-
const burstBlood = (state, change,
|
|
5032
|
-
const point = worldPosition(three,
|
|
7069
|
+
const burstBlood = (state, change, object2, direction) => {
|
|
7070
|
+
const point = worldPosition(three, object2);
|
|
5033
7071
|
bridge?.burst(change, [point.x, point.y, point.z], state.body.blood, direction ? [direction.x, direction.y, direction.z] : void 0);
|
|
5034
7072
|
};
|
|
5035
|
-
const sideOf2 = (
|
|
7073
|
+
const sideOf2 = (object2, body) => new three.Vector3(object2.position.x >= 0 ? 1 : -1, 0, 0).applyQuaternion(body.root.quaternion);
|
|
5036
7074
|
const hideBelow = (state, id) => {
|
|
5037
7075
|
for (const below2 of segmentsBelow(id)) state.gone.add(below2);
|
|
5038
7076
|
};
|
|
@@ -5066,8 +7104,8 @@ var GameCharactersThree = (() => {
|
|
|
5066
7104
|
const target = state.body.nodes[id];
|
|
5067
7105
|
if (!target || state.gone.has(id)) return;
|
|
5068
7106
|
const { node, radius, flip } = target;
|
|
5069
|
-
const
|
|
5070
|
-
explodeChunks(three, materials, random,
|
|
7107
|
+
const box2 = new three.Box3().setFromObject(node);
|
|
7108
|
+
explodeChunks(three, materials, random, box2, direction, (chunk, velocity, spin) => {
|
|
5071
7109
|
scene2.add(chunk);
|
|
5072
7110
|
flying.throwObject(chunk, velocity, spin, id);
|
|
5073
7111
|
});
|
|
@@ -5726,7 +7764,7 @@ var GameCharactersThree = (() => {
|
|
|
5726
7764
|
batches = variants.map((variant) => createVariantBatches(three, variant, time));
|
|
5727
7765
|
for (const set of batches) for (const batch of set) group.add(batch.mesh);
|
|
5728
7766
|
};
|
|
5729
|
-
const
|
|
7767
|
+
const color2 = (hex2) => new three.Color(hex2);
|
|
5730
7768
|
return {
|
|
5731
7769
|
group,
|
|
5732
7770
|
spawn(count) {
|
|
@@ -5757,7 +7795,7 @@ var GameCharactersThree = (() => {
|
|
|
5757
7795
|
const variant = variants[member.variant];
|
|
5758
7796
|
const distance3 = Math.hypot(member.x - camera.position.x, member.z - camera.position.z);
|
|
5759
7797
|
const level = lodAt(distance3, LOD_DISTANCES);
|
|
5760
|
-
const eye =
|
|
7798
|
+
const eye = color2(eyes[variant.typeId] ?? 14926657);
|
|
5761
7799
|
batches[member.variant][level]?.push({ a: [member.x, member.z, member.yaw, member.phase], b: [variant.s, variant.bulk, STRIDE, pose], c: [0, 0.4, 1, member.phase], color: { r: 0.4, g: 0.45, b: 0.5 }, eye });
|
|
5762
7800
|
}
|
|
5763
7801
|
for (const set of batches) for (const batch of set) batch.end();
|
|
@@ -5822,6 +7860,58 @@ var GameCharactersThree = (() => {
|
|
|
5822
7860
|
return spec;
|
|
5823
7861
|
};
|
|
5824
7862
|
|
|
7863
|
+
// src/test-area/knock.ts
|
|
7864
|
+
var KNOCK_DOWN_SPEED = 2.5;
|
|
7865
|
+
var KNOCKED_DOWN_ANGLE = 1.35;
|
|
7866
|
+
var LIE_SECONDS = 1.6;
|
|
7867
|
+
var STAND_UP_PER_SPEED = 0.25;
|
|
7868
|
+
var FALL_SECONDS = 0.25;
|
|
7869
|
+
var RISE_SECONDS = 0.6;
|
|
7870
|
+
var GROUND_FRICTION = 7;
|
|
7871
|
+
var GRAVITY3 = 9.81;
|
|
7872
|
+
var CHEST = 1.1;
|
|
7873
|
+
var bodyMass = (height, build = 1) => 70 * (height / 1.75) ** 2 * build;
|
|
7874
|
+
function knockOf(blast2, feet, mass, options) {
|
|
7875
|
+
const hit = blastShove(blast2, { position: [feet[0], feet[1] + CHEST, feet[2]], mass }, options);
|
|
7876
|
+
if (!hit) return void 0;
|
|
7877
|
+
const [dx, dy, dz] = hit.direction;
|
|
7878
|
+
return { velocity: [dx * hit.speed, Math.max(0, dy * hit.speed), dz * hit.speed], airborne: dy > 0, lying: 0, lieLeft: 0, down: hit.speed >= KNOCK_DOWN_SPEED };
|
|
7879
|
+
}
|
|
7880
|
+
function stepKnock(knock, position, dt, groundAt, alive) {
|
|
7881
|
+
const v = knock.velocity;
|
|
7882
|
+
position.x += v[0] * dt;
|
|
7883
|
+
position.z += v[2] * dt;
|
|
7884
|
+
const ground = groundAt(position.x, position.z);
|
|
7885
|
+
if (knock.airborne || position.y > ground + 1e-4) {
|
|
7886
|
+
v[1] -= GRAVITY3 * dt;
|
|
7887
|
+
position.y += v[1] * dt;
|
|
7888
|
+
if (position.y <= ground) {
|
|
7889
|
+
position.y = ground;
|
|
7890
|
+
v[1] = 0;
|
|
7891
|
+
knock.airborne = false;
|
|
7892
|
+
}
|
|
7893
|
+
} else position.y = ground;
|
|
7894
|
+
const horizontal = Math.hypot(v[0], v[2]);
|
|
7895
|
+
if (!knock.airborne && horizontal > 0) {
|
|
7896
|
+
const slowed = Math.max(0, horizontal - GROUND_FRICTION * dt);
|
|
7897
|
+
v[0] *= slowed / horizontal;
|
|
7898
|
+
v[2] *= slowed / horizontal;
|
|
7899
|
+
}
|
|
7900
|
+
const moving = horizontal > 0.05 || knock.airborne;
|
|
7901
|
+
if (knock.down) {
|
|
7902
|
+
if (moving) knock.lying = Math.min(1, knock.lying + dt / FALL_SECONDS);
|
|
7903
|
+
else {
|
|
7904
|
+
knock.lying = 1;
|
|
7905
|
+
if (alive) {
|
|
7906
|
+
knock.lieLeft = knock.lieLeft > 0 ? knock.lieLeft : LIE_SECONDS + Math.hypot(v[0], v[2]) * STAND_UP_PER_SPEED;
|
|
7907
|
+
knock.lieLeft -= dt;
|
|
7908
|
+
if (knock.lieLeft <= 0) knock.down = false;
|
|
7909
|
+
}
|
|
7910
|
+
}
|
|
7911
|
+
} else if (knock.lying > 0) knock.lying = Math.max(0, knock.lying - dt / RISE_SECONDS);
|
|
7912
|
+
return !moving && !knock.down && knock.lying === 0;
|
|
7913
|
+
}
|
|
7914
|
+
|
|
5825
7915
|
// src/test-area/scenarios.ts
|
|
5826
7916
|
var LIMB_SEGMENTS = ["upperArmL", "forearmL", "handL", "upperArmR", "forearmR", "handR", "thighL", "shinL", "footL", "thighR", "shinR", "footR"];
|
|
5827
7917
|
var BENCHMARK_BODIES = 1e3;
|
|
@@ -5899,8 +7989,8 @@ var GameCharactersThree = (() => {
|
|
|
5899
7989
|
const colors = (decay) => {
|
|
5900
7990
|
const root = deps.buildZombie(deps.typeNames()[0], decay);
|
|
5901
7991
|
const seen = /* @__PURE__ */ new Set();
|
|
5902
|
-
root.traverse((
|
|
5903
|
-
const material =
|
|
7992
|
+
root.traverse((object2) => {
|
|
7993
|
+
const material = object2.material;
|
|
5904
7994
|
if (material?.color) seen.add(material.color.getHexString());
|
|
5905
7995
|
});
|
|
5906
7996
|
return [...seen].sort().join(",");
|
|
@@ -6155,12 +8245,12 @@ var GameCharactersThree = (() => {
|
|
|
6155
8245
|
return built.root;
|
|
6156
8246
|
},
|
|
6157
8247
|
impl: (root, id) => {
|
|
6158
|
-
const
|
|
6159
|
-
const made = builtOf.get(
|
|
8248
|
+
const object2 = root;
|
|
8249
|
+
const made = builtOf.get(object2);
|
|
6160
8250
|
const character = createCharacter(made.kind, made.cardId, id);
|
|
6161
8251
|
const scale = character.spec.height / kit.body.heightMetres;
|
|
6162
|
-
if (Math.abs(scale - 1) > 0.01)
|
|
6163
|
-
const entry = { id, kind: made.kind, cardId: made.cardId, root:
|
|
8252
|
+
if (Math.abs(scale - 1) > 0.01) object2.scale.setScalar(scale);
|
|
8253
|
+
const entry = { id, kind: made.kind, cardId: made.cardId, root: object2, body: woundBodyOf(object2), eyes: three ? findEyes(three, object2) : null, built: made.built, character, walk: 0, tilt: 0 };
|
|
6164
8254
|
entry.character = wrap(entry, character);
|
|
6165
8255
|
const age = made.kind === "zombie" ? undead?.traitsOf(character)?.age : void 0;
|
|
6166
8256
|
const blood = age !== void 0 ? options.bloodOf?.(age) : void 0;
|
|
@@ -6178,7 +8268,7 @@ var GameCharactersThree = (() => {
|
|
|
6178
8268
|
return undead.speedFor(traits, { moment: MOMENTS_WITH_LIMP.has(moment) ? "chase" : moment, limp: moment === "limp", crawling: moment === "crawl" || entry.character.limping.length > 1 });
|
|
6179
8269
|
};
|
|
6180
8270
|
const clearOverlays = () => {
|
|
6181
|
-
for (const
|
|
8271
|
+
for (const object2 of overlays.values()) object2.parent?.remove(object2);
|
|
6182
8272
|
overlays.clear();
|
|
6183
8273
|
};
|
|
6184
8274
|
const overlayFor = (layer, entry) => {
|
|
@@ -6189,16 +8279,16 @@ var GameCharactersThree = (() => {
|
|
|
6189
8279
|
for (const part of entry.character.describe().parts) {
|
|
6190
8280
|
const shape = part.shape;
|
|
6191
8281
|
const geometry = shape.kind === "sphere" ? new three.SphereGeometry(shape.radius, 8, 6) : shape.kind === "cylinder" ? new three.CylinderGeometry(shape.radius, shape.radius, shape.height, 8, 1) : new three.BoxGeometry(...shape.size);
|
|
6192
|
-
const
|
|
6193
|
-
|
|
6194
|
-
if (part.rotation)
|
|
6195
|
-
group.add(
|
|
8282
|
+
const mesh2 = new three.Mesh(geometry, material);
|
|
8283
|
+
mesh2.position.set(...part.position);
|
|
8284
|
+
if (part.rotation) mesh2.rotation.set(...part.rotation);
|
|
8285
|
+
group.add(mesh2);
|
|
6196
8286
|
}
|
|
6197
8287
|
} else if (layer === "joints") {
|
|
6198
8288
|
for (const joint of Object.values(entry.character.rig.joints)) {
|
|
6199
|
-
const
|
|
6200
|
-
|
|
6201
|
-
group.add(
|
|
8289
|
+
const mesh2 = new three.Mesh(new three.SphereGeometry(0.025, 6, 4), material);
|
|
8290
|
+
mesh2.position.set(...joint);
|
|
8291
|
+
group.add(mesh2);
|
|
6202
8292
|
}
|
|
6203
8293
|
} else if (layer === "speed") {
|
|
6204
8294
|
const length = Math.max(0.1, speedOf(entry));
|
|
@@ -6225,6 +8315,17 @@ var GameCharactersThree = (() => {
|
|
|
6225
8315
|
};
|
|
6226
8316
|
const animate = (entry, dt) => {
|
|
6227
8317
|
const { root, character } = entry;
|
|
8318
|
+
if (entry.knock) {
|
|
8319
|
+
const rig = root.userData.rig;
|
|
8320
|
+
const ground = (x, z) => host?.groundAt(x, z) ?? 0;
|
|
8321
|
+
const done = stepKnock(entry.knock, root.position, dt, ground, character.alive);
|
|
8322
|
+
if (done) entry.knock = void 0;
|
|
8323
|
+
if (character.alive && rig) rig.fig.rotation.x = -(entry.knock?.lying ?? 0) * KNOCKED_DOWN_ANGLE;
|
|
8324
|
+
if (entry.knock && entry.knock.lying > 0 && character.alive) {
|
|
8325
|
+
entry.eyes?.off();
|
|
8326
|
+
return;
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
6228
8329
|
if (!character.alive) {
|
|
6229
8330
|
entry.tilt = Math.min(1, entry.tilt + dt * 2);
|
|
6230
8331
|
const rig = root.userData.rig;
|
|
@@ -6430,6 +8531,15 @@ var GameCharactersThree = (() => {
|
|
|
6430
8531
|
wounds = createWounds(lent.THREE, { random, scene: scene2, groundAt: (x, z) => lent.groundAt(x, z), ...bridge ? { bridge } : {}, ...sound ? { sound } : {} });
|
|
6431
8532
|
for (const entry of entries.values()) trackEntry(entry);
|
|
6432
8533
|
const stopChange = lent.bench?.onChange(onChange);
|
|
8534
|
+
const stopEvents = lent.on((event) => {
|
|
8535
|
+
if (event.type !== "explosion") return;
|
|
8536
|
+
for (const entry of entries.values()) {
|
|
8537
|
+
if (!entry.root.parent) continue;
|
|
8538
|
+
const p2 = entry.root.position;
|
|
8539
|
+
const knock = knockOf(event, [p2.x, p2.y, p2.z], bodyMass(entry.character.spec.height, entry.character.spec.build), lent.blastShove);
|
|
8540
|
+
if (knock) entry.knock = entry.knock ? { ...knock, lying: entry.knock.lying } : knock;
|
|
8541
|
+
}
|
|
8542
|
+
});
|
|
6433
8543
|
const stopFrame = lent.onFrame((dt) => {
|
|
6434
8544
|
time += dt;
|
|
6435
8545
|
for (const [id, entry] of entries) {
|
|
@@ -6445,6 +8555,7 @@ var GameCharactersThree = (() => {
|
|
|
6445
8555
|
});
|
|
6446
8556
|
return () => {
|
|
6447
8557
|
stopChange?.();
|
|
8558
|
+
stopEvents();
|
|
6448
8559
|
stopFrame();
|
|
6449
8560
|
clearOverlays();
|
|
6450
8561
|
wounds?.dispose();
|
|
@@ -6480,6 +8591,1440 @@ var GameCharactersThree = (() => {
|
|
|
6480
8591
|
var registerBodyMaterial = (world) => {
|
|
6481
8592
|
if (!world.materials.has(BODY_MATERIAL_DEF.id)) world.materials.set(BODY_MATERIAL_DEF.id, BODY_MATERIAL_DEF);
|
|
6482
8593
|
};
|
|
8594
|
+
|
|
8595
|
+
// src/presets/player-avatar.json
|
|
8596
|
+
var player_avatar_default = {
|
|
8597
|
+
source: {
|
|
8598
|
+
file: "site/pages/jogo/zombiada-modo-livre.html",
|
|
8599
|
+
variable: "PB specs[0] ('voc\xEA') and PLAYER_COR",
|
|
8600
|
+
note: "The team game's player body: the Besti\xE1rio/Elenco human body. Overridable by `createPlayerAvatar(THREE, { spec, shirtColor })`."
|
|
8601
|
+
},
|
|
8602
|
+
spec: {
|
|
8603
|
+
type: "Lento",
|
|
8604
|
+
human: true,
|
|
8605
|
+
decay: 0,
|
|
8606
|
+
seed: 4242,
|
|
8607
|
+
skin: "b07a52",
|
|
8608
|
+
fem: false,
|
|
8609
|
+
life: 1,
|
|
8610
|
+
hs: "short",
|
|
8611
|
+
hair: "2a1e16",
|
|
8612
|
+
pose: {
|
|
8613
|
+
armL: 0,
|
|
8614
|
+
armR: 0,
|
|
8615
|
+
elbL: -0.12,
|
|
8616
|
+
elbR: -0.12,
|
|
8617
|
+
armOut: 0.11,
|
|
8618
|
+
lean: 0.04,
|
|
8619
|
+
headX: 0,
|
|
8620
|
+
headZ: 0
|
|
8621
|
+
}
|
|
8622
|
+
},
|
|
8623
|
+
shirtColor: "3d5a73"
|
|
8624
|
+
};
|
|
8625
|
+
|
|
8626
|
+
// src/player/avatar.ts
|
|
8627
|
+
var TWO_PI2 = Math.PI * 2;
|
|
8628
|
+
var MAX_GAIT = 1.4;
|
|
8629
|
+
var CLIP_BLEND = 0.12;
|
|
8630
|
+
var FALL_SECONDS2 = 0.3;
|
|
8631
|
+
var HIP_HEIGHT = 0.95;
|
|
8632
|
+
var WALK_ARM = 0.28;
|
|
8633
|
+
var WALK_BOB = 0.035;
|
|
8634
|
+
var WALK_LEAN = 0.04;
|
|
8635
|
+
var RUN_LEAN = 0.16;
|
|
8636
|
+
var RUN_BOB = 0.05;
|
|
8637
|
+
var REST_ARM_Z = 0.18;
|
|
8638
|
+
var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
|
|
8639
|
+
var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
|
|
8640
|
+
var DOWN = { thighWalk: 0.5, shinSwing: 0.85, shinBend: 1.3, armHigh: -2.8, armLow: -0.35, armWave: 0.3, armZ: 0.5, armZWave: 0.3, tilt: 1.5, roll: 0.3, hipY: 0.16, hipZ: 0.6, hipZFlip: 1.2 };
|
|
8641
|
+
var HAND_OFFSET = { x: 0, y: -0.33, z: 0.04 };
|
|
8642
|
+
var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
|
|
8643
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
8644
|
+
var fract = (value) => value - Math.floor(value);
|
|
8645
|
+
var createPlayerAvatar = (THREE, options = {}) => {
|
|
8646
|
+
const pose = {
|
|
8647
|
+
armL: 0,
|
|
8648
|
+
armR: 0,
|
|
8649
|
+
elbL: -0.08,
|
|
8650
|
+
elbR: -0.08,
|
|
8651
|
+
lean: 0.04,
|
|
8652
|
+
headX: 0,
|
|
8653
|
+
headZ: 0,
|
|
8654
|
+
...player_avatar_default.spec.pose,
|
|
8655
|
+
...options.spec?.pose
|
|
8656
|
+
};
|
|
8657
|
+
const spec = { ...player_avatar_default.spec, ...options.spec, pose };
|
|
8658
|
+
for (const key of ["skin", "hair"]) if (typeof spec[key] === "string") spec[key] = hexOf(spec[key]);
|
|
8659
|
+
const best = createBestBuilder(THREE, { types: [{ name: String(spec.type), eye: 0 }], random: createRng(Number(spec.seed ?? 1)) });
|
|
8660
|
+
const body = best.build(spec);
|
|
8661
|
+
best.topM().color.setHex(options.shirtColor ?? hexOf(player_avatar_default.shirtColor));
|
|
8662
|
+
const rig = body.userData.rig;
|
|
8663
|
+
const profile = { f: 1.1, a: 0.28, k: 0.35, arm: WALK_ARM, bob: WALK_BOB };
|
|
8664
|
+
const animator = createRigAnimator({ gaits: { [rig.type]: profile } });
|
|
8665
|
+
const clips = options.clips ?? createPoseClipRegistry(POSE_CLIPS);
|
|
8666
|
+
const blendDefault = options.blend ?? CLIP_BLEND;
|
|
8667
|
+
const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
|
|
8668
|
+
const smooth = options.cascade ?? true;
|
|
8669
|
+
const root = new THREE.Group();
|
|
8670
|
+
const tilt = new THREE.Group();
|
|
8671
|
+
tilt.add(body);
|
|
8672
|
+
root.add(tilt);
|
|
8673
|
+
const colorOf2 = (object2) => object2?.material?.color?.getHex();
|
|
8674
|
+
const meshesOf = (node) => node.children.filter((child) => child.isMesh);
|
|
8675
|
+
const shirt = options.shirtColor ?? hexOf(player_avatar_default.shirtColor);
|
|
8676
|
+
const shoe = meshesOf(rig.legs[0].kn).find((mesh2) => mesh2.scale.z > 1.5);
|
|
8677
|
+
const forearm = colorOf2(meshesOf(rig.arms[1].el)[0]);
|
|
8678
|
+
const colors = {
|
|
8679
|
+
skin: Number(spec.skin),
|
|
8680
|
+
shirt,
|
|
8681
|
+
trousers: colorOf2(meshesOf(rig.legs[0].g)[0]) ?? 2962496,
|
|
8682
|
+
shoes: colorOf2(shoe) ?? 1841690,
|
|
8683
|
+
longSleeves: forearm === shirt
|
|
8684
|
+
};
|
|
8685
|
+
const sockets = { R: new THREE.Object3D(), L: new THREE.Object3D() };
|
|
8686
|
+
const armOf = { R: rig.arms[1], L: rig.arms[0] };
|
|
8687
|
+
for (const side of ["R", "L"]) {
|
|
8688
|
+
sockets[side].name = `hand_${side}`;
|
|
8689
|
+
sockets[side].position.set(HAND_OFFSET.x, HAND_OFFSET.y, HAND_OFFSET.z);
|
|
8690
|
+
armOf[side].el.add(sockets[side]);
|
|
8691
|
+
}
|
|
8692
|
+
const addMesh = (parent, geometry, material, x = 0, y = 0, z = 0, rx = 0, ry = 0, rz = 0) => {
|
|
8693
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
8694
|
+
mesh2.position.set(x, y, z);
|
|
8695
|
+
if (rx || ry || rz) mesh2.rotation.set(rx, ry, rz);
|
|
8696
|
+
mesh2.castShadow = true;
|
|
8697
|
+
mesh2.receiveShadow = true;
|
|
8698
|
+
parent.add(mesh2);
|
|
8699
|
+
return mesh2;
|
|
8700
|
+
};
|
|
8701
|
+
const hands = [];
|
|
8702
|
+
rig.arms.forEach((arm, index) => {
|
|
8703
|
+
const el = arm.el;
|
|
8704
|
+
const side = index === 0 ? 1 : -1;
|
|
8705
|
+
const oldHand = meshesOf(el).find((mesh2) => Math.abs(mesh2.position.y + 0.33) < 0.025);
|
|
8706
|
+
const handMaterial = oldHand?.material ?? new THREE.MeshLambertMaterial({ color: colors.skin });
|
|
8707
|
+
if (oldHand) {
|
|
8708
|
+
oldHand.geometry?.dispose();
|
|
8709
|
+
el.remove(oldHand);
|
|
8710
|
+
}
|
|
8711
|
+
const hg = new THREE.Group();
|
|
8712
|
+
hg.name = side > 0 ? "hand_group_L" : "hand_group_R";
|
|
8713
|
+
hg.position.set(side * 0.02, -0.3, 0.01);
|
|
8714
|
+
el.add(hg);
|
|
8715
|
+
const palm = addMesh(hg, new THREE.SphereGeometry(0.048, 14, 10), handMaterial, 0, -0.035, 0);
|
|
8716
|
+
palm.scale.set(0.6, 1.05, 1);
|
|
8717
|
+
const built = buildHand({ THREE, add: addMesh, hg, handM: handMaterial, s: side, detailed: true });
|
|
8718
|
+
hands.push({ side, hg, fingers: built.fingers, th: built.th });
|
|
8719
|
+
});
|
|
8720
|
+
const setHandGrip = (hand, grip, wristX = 0) => {
|
|
8721
|
+
hand.fingers.forEach((finger, j) => {
|
|
8722
|
+
finger.rotation.z = -hand.side * grip * (j % 2 ? 1.1 : 0.9);
|
|
8723
|
+
});
|
|
8724
|
+
if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
|
|
8725
|
+
hand.hg.rotation.x = wristX;
|
|
8726
|
+
};
|
|
8727
|
+
const legRestX = rig.legs.map((leg) => leg.g.position.x);
|
|
8728
|
+
const legSide = (index) => index === 0 ? 1 : -1;
|
|
8729
|
+
let cycles = 0;
|
|
8730
|
+
let time = 0;
|
|
8731
|
+
let fall = 0;
|
|
8732
|
+
let run = null;
|
|
8733
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
8734
|
+
const emit = (event, onEvent) => {
|
|
8735
|
+
onEvent?.(event);
|
|
8736
|
+
for (const listener of listeners) listener(event);
|
|
8737
|
+
};
|
|
8738
|
+
const readPose = () => ({
|
|
8739
|
+
lean: rig.upper.rotation.x,
|
|
8740
|
+
twist: rig.upper.rotation.y,
|
|
8741
|
+
side: rig.upper.rotation.z,
|
|
8742
|
+
drop: rig.fig.position.y - rig.dropBase,
|
|
8743
|
+
headX: rig.headG.rotation.x,
|
|
8744
|
+
headY: rig.headG.rotation.y,
|
|
8745
|
+
headZ: rig.headG.rotation.z,
|
|
8746
|
+
armR: rig.arms[0].g.rotation.x,
|
|
8747
|
+
armL: rig.arms[1].g.rotation.x,
|
|
8748
|
+
elbR: rig.arms[0].el.rotation.x,
|
|
8749
|
+
elbL: rig.arms[1].el.rotation.x,
|
|
8750
|
+
armRz: rig.arms[0].g.rotation.z,
|
|
8751
|
+
armLz: rig.arms[1].g.rotation.z,
|
|
8752
|
+
legL: rig.legs[0].g.rotation.x,
|
|
8753
|
+
legR: rig.legs[1].g.rotation.x,
|
|
8754
|
+
kneeL: rig.legs[0].kn.rotation.x,
|
|
8755
|
+
kneeR: rig.legs[1].kn.rotation.x
|
|
8756
|
+
});
|
|
8757
|
+
const armed = (pitch, gait, hip) => {
|
|
8758
|
+
const aim = clamp((pitch + 1) / 3, 0, 0.99) * 3 - 1;
|
|
8759
|
+
const shoulder = AIMED.shoulder + aim * AIMED.perAim + AIMED.swing * Math.sin(cycles * TWO_PI2) * gait;
|
|
8760
|
+
rig.legs.forEach((leg, index) => {
|
|
8761
|
+
const x = legRestX[index];
|
|
8762
|
+
leg.g.rotation.y = hip;
|
|
8763
|
+
leg.g.position.x = x * Math.cos(hip);
|
|
8764
|
+
leg.g.position.z = -x * Math.sin(hip);
|
|
8765
|
+
});
|
|
8766
|
+
return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
|
|
8767
|
+
};
|
|
8768
|
+
const holdingPose = (gait, itemUse) => {
|
|
8769
|
+
const bob = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
|
|
8770
|
+
if (!itemUse) {
|
|
8771
|
+
return { armL: -0.45 + bob, armLz: 0.22, elbL: -1.05 };
|
|
8772
|
+
}
|
|
8773
|
+
const { id, position, rotation } = itemUse;
|
|
8774
|
+
if (id === "drink" || id === "sip" || id === "chug") {
|
|
8775
|
+
const raise2 = clamp((position[1] + 0.2) / 0.22, 0, 1);
|
|
8776
|
+
const tilt3 = clamp(Math.abs(rotation[0]) / 1.22, 0, 1.15);
|
|
8777
|
+
return {
|
|
8778
|
+
armL: -0.45 - 0.95 * raise2 - 0.22 * tilt3,
|
|
8779
|
+
armLz: 0.22 + 0.44 * raise2,
|
|
8780
|
+
elbL: -1.05 - 0.85 * raise2 + 0.12 * tilt3,
|
|
8781
|
+
headX: -0.26 * tilt3
|
|
8782
|
+
};
|
|
8783
|
+
}
|
|
8784
|
+
if (id === "open" || id === "close") {
|
|
8785
|
+
const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
|
|
8786
|
+
return {
|
|
8787
|
+
armL: -0.45 - 0.35 * reach,
|
|
8788
|
+
armLz: 0.22 + 0.28 * reach,
|
|
8789
|
+
elbL: -1.05 - 0.25 * reach,
|
|
8790
|
+
armR: -0.78 * reach,
|
|
8791
|
+
armRz: -0.48 * reach,
|
|
8792
|
+
elbR: -1.25 * reach
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8795
|
+
const raise = clamp((position[1] + 0.2) / 0.25, 0, 1);
|
|
8796
|
+
const tilt2 = clamp(Math.abs(rotation[0]) / 1.75, 0, 1.2);
|
|
8797
|
+
const ext = Math.max(raise, tilt2 * 0.75);
|
|
8798
|
+
return {
|
|
8799
|
+
armL: -0.45 - 0.48 * ext,
|
|
8800
|
+
armLz: 0.22 + 0.08 * ext,
|
|
8801
|
+
elbL: -1.05 + 0.38 * ext
|
|
8802
|
+
};
|
|
8803
|
+
};
|
|
8804
|
+
const resetLegYaw = () => {
|
|
8805
|
+
rig.legs.forEach((leg, index) => {
|
|
8806
|
+
leg.g.rotation.y = 0;
|
|
8807
|
+
leg.g.position.x = legRestX[index];
|
|
8808
|
+
leg.g.position.z = 0;
|
|
8809
|
+
});
|
|
8810
|
+
};
|
|
8811
|
+
const runningPose = (gait, busyHands = false) => {
|
|
8812
|
+
const ph = cycles * TWO_PI2;
|
|
8813
|
+
const sine = Math.sin(ph);
|
|
8814
|
+
const cosine = Math.cos(ph);
|
|
8815
|
+
const legL = sine * 0.65 * gait;
|
|
8816
|
+
const legR = -sine * 0.65 * gait;
|
|
8817
|
+
const kneeL = Math.max(0, -cosine) * 1.15 * gait;
|
|
8818
|
+
const kneeR = Math.max(0, cosine) * 1.15 * gait;
|
|
8819
|
+
const targets = {
|
|
8820
|
+
lean: RUN_LEAN * Math.min(1, gait),
|
|
8821
|
+
drop: Math.abs(cosine) * RUN_BOB * gait,
|
|
8822
|
+
legL,
|
|
8823
|
+
legR,
|
|
8824
|
+
kneeL,
|
|
8825
|
+
kneeR
|
|
8826
|
+
};
|
|
8827
|
+
if (!busyHands) {
|
|
8828
|
+
targets.armR = -0.32 - sine * 0.65 * gait;
|
|
8829
|
+
targets.armL = -0.32 + sine * 0.65 * gait;
|
|
8830
|
+
targets.elbR = -1.25 + Math.max(0, sine) * 0.3 * gait;
|
|
8831
|
+
targets.elbL = -1.25 + Math.max(0, -sine) * 0.3 * gait;
|
|
8832
|
+
targets.armRz = 0.22;
|
|
8833
|
+
targets.armLz = -0.22;
|
|
8834
|
+
}
|
|
8835
|
+
return targets;
|
|
8836
|
+
};
|
|
8837
|
+
const crouchPose = (gait) => {
|
|
8838
|
+
const swing = (leg) => {
|
|
8839
|
+
const legS = legSide(leg);
|
|
8840
|
+
return CROUCH.thigh - Math.sin(cycles * TWO_PI2) * legS * CROUCH.thighWalk * gait;
|
|
8841
|
+
};
|
|
8842
|
+
return {
|
|
8843
|
+
legL: swing(0),
|
|
8844
|
+
legR: swing(1),
|
|
8845
|
+
kneeL: CROUCH.shin,
|
|
8846
|
+
kneeR: CROUCH.shin,
|
|
8847
|
+
lean: WALK_LEAN * gait + CROUCH.lean,
|
|
8848
|
+
drop: Math.abs(Math.sin(cycles * TWO_PI2)) * WALK_BOB * gait + CROUCH.drop
|
|
8849
|
+
};
|
|
8850
|
+
};
|
|
8851
|
+
const downPose = () => {
|
|
8852
|
+
const ph = cycles * TWO_PI2;
|
|
8853
|
+
const gait = 1;
|
|
8854
|
+
const leg = (index) => {
|
|
8855
|
+
const legS = legSide(index);
|
|
8856
|
+
const walkS = Math.sin(ph) * legS;
|
|
8857
|
+
const bend = fract(cycles * 3) * legS > 0.3 ? fract(cycles * 3) * DOWN.shinBend : Math.max(0, Math.cos(ph + (legS > 0 ? 0 : Math.PI))) * DOWN.shinSwing * gait;
|
|
8858
|
+
return { hip: -walkS * DOWN.thighWalk * gait, knee: bend };
|
|
8859
|
+
};
|
|
8860
|
+
const arm = (index) => {
|
|
8861
|
+
const side = index === 0 ? 1 : -1;
|
|
8862
|
+
const armS = Math.sin(ph) * (side > 0 ? 1 : -1);
|
|
8863
|
+
const shoulder = fract(cycles * 5) > 0.5 && side > 0 ? DOWN.armHigh : DOWN.armLow + DOWN.armWave * Math.sin(cycles * 5 + side);
|
|
8864
|
+
return { shoulder, z: side * (DOWN.armZ + DOWN.armZWave * Math.sin(cycles * 3)), elbow: -0.35 - Math.max(0, armS) * 0.4 * gait };
|
|
8865
|
+
};
|
|
8866
|
+
const [l, r] = [leg(0), leg(1)];
|
|
8867
|
+
const [a0, a1] = [arm(0), arm(1)];
|
|
8868
|
+
return {
|
|
8869
|
+
legL: l.hip,
|
|
8870
|
+
kneeL: l.knee,
|
|
8871
|
+
legR: r.hip,
|
|
8872
|
+
kneeR: r.knee,
|
|
8873
|
+
armR: a0.shoulder,
|
|
8874
|
+
armRz: a0.z,
|
|
8875
|
+
elbR: a0.elbow,
|
|
8876
|
+
armL: a1.shoulder,
|
|
8877
|
+
armLz: a1.z,
|
|
8878
|
+
elbL: a1.elbow,
|
|
8879
|
+
lean: WALK_LEAN * gait,
|
|
8880
|
+
drop: Math.abs(Math.sin(ph)) * WALK_BOB * gait
|
|
8881
|
+
};
|
|
8882
|
+
};
|
|
8883
|
+
const placeBody = (progress) => {
|
|
8884
|
+
const fk = progress * progress * (3 - 2 * progress);
|
|
8885
|
+
const flip = fract(cycles * 1.7) >= 0.5 ? 1 : 0;
|
|
8886
|
+
tilt.rotation.set((-DOWN.tilt + flip * 2 * DOWN.tilt) * fk, 0, Math.sin(cycles * 7) * DOWN.roll * fk, "XYZ");
|
|
8887
|
+
const hip = new THREE.Vector3(0, HIP_HEIGHT, 0);
|
|
8888
|
+
const target = new THREE.Vector3(0, DOWN.hipY, -DOWN.hipZ + flip * DOWN.hipZFlip);
|
|
8889
|
+
const place = new THREE.Vector3(0, HIP_HEIGHT * (1 - fk), 0).addScaledVector(target, fk);
|
|
8890
|
+
tilt.position.copy(place.sub(hip.applyEuler(tilt.rotation)));
|
|
8891
|
+
};
|
|
8892
|
+
const fireClipEvents = (r, from, to) => {
|
|
8893
|
+
for (const event of clipEventsBetween(r.clip, from, to)) emit({ ...event, clip: r.clip.id }, r.onEvent);
|
|
8894
|
+
};
|
|
8895
|
+
const stepClip = (dt, down) => {
|
|
8896
|
+
if (!run) return;
|
|
8897
|
+
const r = run;
|
|
8898
|
+
const from = r.time;
|
|
8899
|
+
r.time += dt;
|
|
8900
|
+
const { duration } = r.clip;
|
|
8901
|
+
if (r.clip.loop && r.time > duration) {
|
|
8902
|
+
fireClipEvents(r, from, duration);
|
|
8903
|
+
r.time -= duration * Math.floor(r.time / duration);
|
|
8904
|
+
fireClipEvents(r, -1e-9, r.time);
|
|
8905
|
+
} else {
|
|
8906
|
+
fireClipEvents(r, from, Math.min(r.time, duration));
|
|
8907
|
+
if (r.time >= duration && !r.clip.loop) r.target = 0;
|
|
8908
|
+
}
|
|
8909
|
+
r.weight = r.blend <= 0 ? r.target : clamp(r.weight + (r.target ? dt : -dt) / r.blend, 0, 1);
|
|
8910
|
+
if (r.target === 0 && r.weight <= 0) {
|
|
8911
|
+
run = null;
|
|
8912
|
+
return;
|
|
8913
|
+
}
|
|
8914
|
+
if (down) return;
|
|
8915
|
+
const sampled = filterPoseChannels(samplePoseClip(r.clip, Math.min(r.time, duration)), r.clip.channels ?? "full");
|
|
8916
|
+
const current = readPose();
|
|
8917
|
+
const blended = {};
|
|
8918
|
+
for (const [name, value] of Object.entries(sampled)) if (name in current) blended[name] = current[name] + (value - current[name]) * r.weight;
|
|
8919
|
+
animator.setPose(rig, blended);
|
|
8920
|
+
};
|
|
8921
|
+
return {
|
|
8922
|
+
root,
|
|
8923
|
+
rig,
|
|
8924
|
+
colors,
|
|
8925
|
+
three: THREE,
|
|
8926
|
+
get playing() {
|
|
8927
|
+
return run ? run.clip.id : null;
|
|
8928
|
+
},
|
|
8929
|
+
update(dt, state = {}) {
|
|
8930
|
+
const speed = state.speed ?? 0;
|
|
8931
|
+
const moving = speed >= 0.05;
|
|
8932
|
+
const down = !!state.down;
|
|
8933
|
+
const running = Boolean(state.running && moving && !down && !state.crouch);
|
|
8934
|
+
if (moving) cycles += dt * (running ? walkCadence(speed) * 1.3 : walkCadence(speed));
|
|
8935
|
+
const gait = Math.min(MAX_GAIT, walkGait(speed));
|
|
8936
|
+
profile.arm = (running ? 0.55 : WALK_ARM) * gait;
|
|
8937
|
+
profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
|
|
8938
|
+
time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
|
|
8939
|
+
animator.animate(body, time, { speed });
|
|
8940
|
+
fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
|
|
8941
|
+
resetLegYaw();
|
|
8942
|
+
if (down) animator.setPose(rig, downPose());
|
|
8943
|
+
else {
|
|
8944
|
+
if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
|
|
8945
|
+
if (state.crouch) animator.setPose(rig, crouchPose(gait));
|
|
8946
|
+
if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
|
|
8947
|
+
else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
|
|
8948
|
+
else if (!running) {
|
|
8949
|
+
animator.setPose(rig, {
|
|
8950
|
+
armRz: REST_ARM_Z,
|
|
8951
|
+
armLz: -REST_ARM_Z,
|
|
8952
|
+
elbR: -0.12,
|
|
8953
|
+
elbL: -0.12
|
|
8954
|
+
});
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8957
|
+
placeBody(fall);
|
|
8958
|
+
stepClip(dt, down);
|
|
8959
|
+
if (smooth) animator.cascade(rig, dt);
|
|
8960
|
+
const clipId = run ? run.clip.id : null;
|
|
8961
|
+
const ph = cycles * TWO_PI2;
|
|
8962
|
+
hands.forEach((hand) => {
|
|
8963
|
+
const isRight = hand.side < 0;
|
|
8964
|
+
const holdRight = isRight && (!!state.holding || !!state.itemUse);
|
|
8965
|
+
const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
|
|
8966
|
+
const grip = clipId === "punch_right" ? 1.4 : clipId === "shove_two_hands" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
|
|
8967
|
+
const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
|
|
8968
|
+
setHandGrip(hand, grip, wristSwing);
|
|
8969
|
+
});
|
|
8970
|
+
},
|
|
8971
|
+
play(clip, playOptions = {}) {
|
|
8972
|
+
const found = typeof clip === "string" ? clips.get(clip) : clip;
|
|
8973
|
+
if (!found) throw new Error(`Unknown pose clip: ${String(clip)}`);
|
|
8974
|
+
run = { clip: found, time: 0, weight: run?.weight ?? 0, target: 1, blend: playOptions.blend ?? blendDefault, ...playOptions.onEvent ? { onEvent: playOptions.onEvent } : {} };
|
|
8975
|
+
fireClipEvents(run, -1e-9, 0);
|
|
8976
|
+
run.time = 0;
|
|
8977
|
+
},
|
|
8978
|
+
stop() {
|
|
8979
|
+
if (run) run.target = 0;
|
|
8980
|
+
},
|
|
8981
|
+
hand: (side) => sockets[side],
|
|
8982
|
+
on(_type, listener) {
|
|
8983
|
+
listeners.add(listener);
|
|
8984
|
+
return () => void listeners.delete(listener);
|
|
8985
|
+
},
|
|
8986
|
+
dispose() {
|
|
8987
|
+
run = null;
|
|
8988
|
+
listeners.clear();
|
|
8989
|
+
body.traverse((object2) => {
|
|
8990
|
+
const mesh2 = object2;
|
|
8991
|
+
mesh2.geometry?.dispose();
|
|
8992
|
+
const material = mesh2.material;
|
|
8993
|
+
if (Array.isArray(material)) material.forEach((m) => m.dispose());
|
|
8994
|
+
else material?.dispose();
|
|
8995
|
+
});
|
|
8996
|
+
root.removeFromParent();
|
|
8997
|
+
}
|
|
8998
|
+
};
|
|
8999
|
+
};
|
|
9000
|
+
|
|
9001
|
+
// ../game-characters-people/dist/index.js
|
|
9002
|
+
var attack_clips_default = {
|
|
9003
|
+
clips: [
|
|
9004
|
+
{
|
|
9005
|
+
id: "punch_right",
|
|
9006
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
9007
|
+
duration: 0.35,
|
|
9008
|
+
loop: false,
|
|
9009
|
+
channels: "upper",
|
|
9010
|
+
keys: [
|
|
9011
|
+
{ t: 0, pose: { lean: 0, twist: 0, side: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0 } },
|
|
9012
|
+
{ t: 0.1, pose: { lean: 0.04, twist: -0.35, side: 0, headX: 0, headY: 0.28, headZ: 0, armR: 0.45, armL: -0.7, elbR: -1.9, elbL: -1.6, armRz: 0, armLz: 0.1 } },
|
|
9013
|
+
{ t: 0.16, pose: { lean: 0.2, twist: 0.5, side: 0, headX: 0, headY: -0.38, headZ: 0, armR: -1.5, armL: -0.6, elbR: -0.1, elbL: -1.7, armRz: -0.1, armLz: 0.1 } },
|
|
9014
|
+
{ t: 0.22, pose: { lean: 0.16, twist: 0.4, side: 0, headX: 0, headY: -0.3, headZ: 0, armR: -1.35, armL: -0.6, elbR: -0.25, elbL: -1.6, armRz: -0.08, armLz: 0.08 } },
|
|
9015
|
+
{ t: 0.35, pose: { lean: 0, twist: 0, side: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0 } }
|
|
9016
|
+
],
|
|
9017
|
+
events: [
|
|
9018
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9019
|
+
{ t: 0.1, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9020
|
+
{ t: 0.16, type: "impact", data: { kind: "punch" } },
|
|
9021
|
+
{ t: 0.22, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9022
|
+
]
|
|
9023
|
+
},
|
|
9024
|
+
{
|
|
9025
|
+
id: "shove_two_hands",
|
|
9026
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
9027
|
+
duration: 0.5,
|
|
9028
|
+
loop: false,
|
|
9029
|
+
channels: "upper",
|
|
9030
|
+
keys: [
|
|
9031
|
+
{ t: 0, pose: { lean: 0, twist: 0, side: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0 } },
|
|
9032
|
+
{ t: 0.15, pose: { lean: -0.08, twist: 0, side: 0, headX: -0.05, headY: 0, headZ: 0, armR: -0.9, armL: -0.9, elbR: -1.9, elbL: -1.9, armRz: -0.12, armLz: 0.12 } },
|
|
9033
|
+
{ t: 0.26, pose: { lean: 0.3, twist: 0, side: 0, headX: 0.06, headY: 0, headZ: 0, armR: -1.5, armL: -1.5, elbR: -0.12, elbL: -0.12, armRz: -0.08, armLz: 0.08 } },
|
|
9034
|
+
{ t: 0.34, pose: { lean: 0.26, twist: 0, side: 0, headX: 0.05, headY: 0, headZ: 0, armR: -1.4, armL: -1.4, elbR: -0.2, elbL: -0.2, armRz: -0.06, armLz: 0.06 } },
|
|
9035
|
+
{ t: 0.5, pose: { lean: 0, twist: 0, side: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0 } }
|
|
9036
|
+
],
|
|
9037
|
+
events: [
|
|
9038
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9039
|
+
{ t: 0.15, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9040
|
+
{ t: 0.26, type: "impact", data: { kind: "shove" } },
|
|
9041
|
+
{ t: 0.34, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9042
|
+
]
|
|
9043
|
+
},
|
|
9044
|
+
{
|
|
9045
|
+
id: "front_kick",
|
|
9046
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
9047
|
+
duration: 0.6,
|
|
9048
|
+
loop: false,
|
|
9049
|
+
channels: "full",
|
|
9050
|
+
keys: [
|
|
9051
|
+
{ t: 0, pose: { lean: 0, twist: 0, side: 0, drop: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0, legL: -0.05, legR: 0.05, kneeL: 0.05, kneeR: 0.05 } },
|
|
9052
|
+
{ t: 0.18, pose: { lean: -0.1, twist: 0, side: 0.04, drop: -0.05, headX: 0.05, headY: 0, headZ: 0, armR: -0.5, armL: 0.4, elbR: -0.5, elbL: -0.4, armRz: -0.1, armLz: 0.1, legL: 0.1, legR: -1.1, kneeL: 0.35, kneeR: 1.8 } },
|
|
9053
|
+
{ t: 0.32, pose: { lean: -0.22, twist: 0, side: 0.04, drop: -0.06, headX: 0.1, headY: 0, headZ: 0, armR: -0.7, armL: 0.7, elbR: -0.6, elbL: -0.3, armRz: -0.15, armLz: 0.15, legL: 0.12, legR: -1.45, kneeL: 0.2, kneeR: 0.1 } },
|
|
9054
|
+
{ t: 0.42, pose: { lean: -0.18, twist: 0, side: 0.03, drop: -0.05, headX: 0.08, headY: 0, headZ: 0, armR: -0.5, armL: 0.5, elbR: -0.5, elbL: -0.3, armRz: -0.1, armLz: 0.1, legL: 0.1, legR: -1.2, kneeL: 0.3, kneeR: 1.2 } },
|
|
9055
|
+
{ t: 0.6, pose: { lean: 0, twist: 0, side: 0, drop: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0, legL: -0.05, legR: 0.05, kneeL: 0.05, kneeR: 0.05 } }
|
|
9056
|
+
],
|
|
9057
|
+
events: [
|
|
9058
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9059
|
+
{ t: 0.18, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9060
|
+
{ t: 0.32, type: "impact", data: { kind: "kick" } },
|
|
9061
|
+
{ t: 0.42, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9062
|
+
]
|
|
9063
|
+
},
|
|
9064
|
+
{
|
|
9065
|
+
id: "recoil_rifle",
|
|
9066
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered. Starts and ends at the armed pose of createPlayerAvatar at pitch 0" },
|
|
9067
|
+
duration: 0.22,
|
|
9068
|
+
loop: false,
|
|
9069
|
+
channels: "upper",
|
|
9070
|
+
keys: [
|
|
9071
|
+
{ t: 0, pose: { lean: 0, twist: 0, headX: 0, armR: -1.42, armL: -1.42, elbR: -0.04, elbL: -0.12, armRz: -0.54, armLz: 0.36 } },
|
|
9072
|
+
{ t: 0.04, pose: { lean: -0.1, twist: 0.03, headX: -0.06, armR: -1.62, armL: -1.62, elbR: -0.35, elbL: -0.45, armRz: -0.5, armLz: 0.34 } },
|
|
9073
|
+
{ t: 0.22, pose: { lean: 0, twist: 0, headX: 0, armR: -1.42, armL: -1.42, elbR: -0.04, elbL: -0.12, armRz: -0.54, armLz: 0.36 } }
|
|
9074
|
+
],
|
|
9075
|
+
events: [
|
|
9076
|
+
{ t: 0, type: "impact", data: { kind: "shot" } },
|
|
9077
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9078
|
+
{ t: 0.08, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9079
|
+
]
|
|
9080
|
+
},
|
|
9081
|
+
{
|
|
9082
|
+
id: "recoil_pistol",
|
|
9083
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered. Starts and ends at the armed pose of createPlayerAvatar at pitch 0" },
|
|
9084
|
+
duration: 0.18,
|
|
9085
|
+
loop: false,
|
|
9086
|
+
channels: "upper",
|
|
9087
|
+
keys: [
|
|
9088
|
+
{ t: 0, pose: { lean: 0, twist: 0, headX: 0, armR: -1.42, armL: -1.42, elbR: -0.04, elbL: -0.12, armRz: -0.54, armLz: 0.36 } },
|
|
9089
|
+
{ t: 0.03, pose: { lean: -0.05, twist: 0.02, headX: -0.03, armR: -1.58, armL: -1.52, elbR: -0.22, elbL: -0.25, armRz: -0.52, armLz: 0.35 } },
|
|
9090
|
+
{ t: 0.18, pose: { lean: 0, twist: 0, headX: 0, armR: -1.42, armL: -1.42, elbR: -0.04, elbL: -0.12, armRz: -0.54, armLz: 0.36 } }
|
|
9091
|
+
],
|
|
9092
|
+
events: [
|
|
9093
|
+
{ t: 0, type: "impact", data: { kind: "shot" } },
|
|
9094
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9095
|
+
{ t: 0.06, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9096
|
+
]
|
|
9097
|
+
}
|
|
9098
|
+
],
|
|
9099
|
+
locales: {
|
|
9100
|
+
"pt-BR": {
|
|
9101
|
+
"attack_clip.phase.windup": "Prepara\xE7\xE3o",
|
|
9102
|
+
"attack_clip.phase.strike": "Golpe",
|
|
9103
|
+
"attack_clip.phase.recover": "Recupera\xE7\xE3o"
|
|
9104
|
+
},
|
|
9105
|
+
en: {
|
|
9106
|
+
"attack_clip.phase.windup": "Wind-up",
|
|
9107
|
+
"attack_clip.phase.strike": "Strike",
|
|
9108
|
+
"attack_clip.phase.recover": "Recover"
|
|
9109
|
+
}
|
|
9110
|
+
}
|
|
9111
|
+
};
|
|
9112
|
+
var first_person_clips_default = {
|
|
9113
|
+
clips: [
|
|
9114
|
+
{
|
|
9115
|
+
id: "punch_right",
|
|
9116
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9117
|
+
duration: 0.35,
|
|
9118
|
+
keys: [
|
|
9119
|
+
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
9120
|
+
{ t: 0.1, R: { p: [0.21, -0.15, -0.29], r: [0.1, 1.45, 0.2], grip: 1 } },
|
|
9121
|
+
{ t: 0.16, R: { p: [0.06, -0.09, -0.62], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9122
|
+
{ t: 0.22, R: { p: [0.07, -0.1, -0.56], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9123
|
+
{ t: 0.35, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
9124
|
+
],
|
|
9125
|
+
events: [
|
|
9126
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9127
|
+
{ t: 0.1, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9128
|
+
{ t: 0.16, type: "impact", data: { kind: "punch" } },
|
|
9129
|
+
{ t: 0.22, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9130
|
+
]
|
|
9131
|
+
},
|
|
9132
|
+
{
|
|
9133
|
+
id: "shove_two_hands",
|
|
9134
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9135
|
+
duration: 0.5,
|
|
9136
|
+
keys: [
|
|
9137
|
+
{ t: 0, R: { p: [0.17, -0.14, -0.36], r: [0, 1.5708, 1.4], grip: 0.05, w: 0 }, L: "mirror" },
|
|
9138
|
+
{ t: 0.15, R: { p: [0.15, -0.09, -0.27], r: [0, 1.5708, 1.5], grip: 0.05 }, L: "mirror" },
|
|
9139
|
+
{ t: 0.26, R: { p: [0.11, -0.02, -0.58], r: [0, 1.5708, 1.75], grip: 0.05 }, L: "mirror" },
|
|
9140
|
+
{ t: 0.34, R: { p: [0.12, -0.03, -0.54], r: [0, 1.5708, 1.65], grip: 0.05 }, L: "mirror" },
|
|
9141
|
+
{ t: 0.5, R: { p: [0.17, -0.14, -0.36], r: [0, 1.5708, 1.4], grip: 0.05, w: 0 }, L: "mirror" }
|
|
9142
|
+
],
|
|
9143
|
+
events: [
|
|
9144
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9145
|
+
{ t: 0.15, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9146
|
+
{ t: 0.26, type: "impact", data: { kind: "shove" } },
|
|
9147
|
+
{ t: 0.34, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9148
|
+
]
|
|
9149
|
+
},
|
|
9150
|
+
{
|
|
9151
|
+
id: "front_kick",
|
|
9152
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9153
|
+
duration: 0.6,
|
|
9154
|
+
keys: [
|
|
9155
|
+
{ t: 0, F: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0], w: 0 } },
|
|
9156
|
+
{ t: 0.18, F: { p: [0.18, -0.58, -0.3], r: [0, 1.5708, 0.6] } },
|
|
9157
|
+
{ t: 0.32, F: { p: [0.12, -0.24, -0.65], r: [0, 1.5708, 0.35] } },
|
|
9158
|
+
{ t: 0.42, F: { p: [0.12, -0.3, -0.58], r: [0, 1.5708, 0.3] } },
|
|
9159
|
+
{ t: 0.6, F: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0], w: 0 } }
|
|
9160
|
+
],
|
|
9161
|
+
events: [
|
|
9162
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9163
|
+
{ t: 0.18, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9164
|
+
{ t: 0.32, type: "impact", data: { kind: "kick" } },
|
|
9165
|
+
{ t: 0.42, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9166
|
+
]
|
|
9167
|
+
}
|
|
9168
|
+
]
|
|
9169
|
+
};
|
|
9170
|
+
var names_default = {
|
|
9171
|
+
sources: {
|
|
9172
|
+
file: "specs/001-migracao-entregas-dos-times/sources/sobreviventes__os-vivos-do-vale.js (site/pages/sobreviventes/os-vivos-do-vale.html)",
|
|
9173
|
+
pools: "NM (lines 610-611): m, f, s, bm, bf",
|
|
9174
|
+
kinship: "RELW (line 612)"
|
|
9175
|
+
},
|
|
9176
|
+
"pt-BR": {
|
|
9177
|
+
male: [
|
|
9178
|
+
"Jo\xE3o",
|
|
9179
|
+
"Paulo",
|
|
9180
|
+
"Marcos",
|
|
9181
|
+
"Andr\xE9",
|
|
9182
|
+
"Rafael",
|
|
9183
|
+
"Thiago",
|
|
9184
|
+
"Bruno",
|
|
9185
|
+
"Luiz",
|
|
9186
|
+
"S\xE9rgio",
|
|
9187
|
+
"Wagner",
|
|
9188
|
+
"C\xEDcero",
|
|
9189
|
+
"Edson",
|
|
9190
|
+
"Igor",
|
|
9191
|
+
"Mateus",
|
|
9192
|
+
"Renan",
|
|
9193
|
+
"Hugo"
|
|
9194
|
+
],
|
|
9195
|
+
female: [
|
|
9196
|
+
"Ana",
|
|
9197
|
+
"Patr\xEDcia",
|
|
9198
|
+
"Juliana",
|
|
9199
|
+
"Camila",
|
|
9200
|
+
"Sandra",
|
|
9201
|
+
"Luana",
|
|
9202
|
+
"M\xE1rcia",
|
|
9203
|
+
"Fernanda",
|
|
9204
|
+
"Aline",
|
|
9205
|
+
"Rita",
|
|
9206
|
+
"Jaqueline",
|
|
9207
|
+
"Simone",
|
|
9208
|
+
"Larissa",
|
|
9209
|
+
"Bruna",
|
|
9210
|
+
"Tereza",
|
|
9211
|
+
"Vanessa"
|
|
9212
|
+
],
|
|
9213
|
+
surnames: [
|
|
9214
|
+
"Silva",
|
|
9215
|
+
"Santos",
|
|
9216
|
+
"Oliveira",
|
|
9217
|
+
"Souza",
|
|
9218
|
+
"Pereira",
|
|
9219
|
+
"Lima",
|
|
9220
|
+
"Carvalho",
|
|
9221
|
+
"Gomes",
|
|
9222
|
+
"Ribeiro",
|
|
9223
|
+
"Martins",
|
|
9224
|
+
"Rocha",
|
|
9225
|
+
"Almeida",
|
|
9226
|
+
"Barbosa",
|
|
9227
|
+
"Teixeira",
|
|
9228
|
+
"Moraes",
|
|
9229
|
+
"Cunha"
|
|
9230
|
+
],
|
|
9231
|
+
babyMale: [
|
|
9232
|
+
"Davi",
|
|
9233
|
+
"Miguel",
|
|
9234
|
+
"Gael",
|
|
9235
|
+
"Bento",
|
|
9236
|
+
"Noah",
|
|
9237
|
+
"Theo"
|
|
9238
|
+
],
|
|
9239
|
+
babyFemale: [
|
|
9240
|
+
"Esperan\xE7a",
|
|
9241
|
+
"Aurora",
|
|
9242
|
+
"Cec\xEDlia",
|
|
9243
|
+
"Luna",
|
|
9244
|
+
"Maria Luz",
|
|
9245
|
+
"Alice"
|
|
9246
|
+
],
|
|
9247
|
+
kinship: {
|
|
9248
|
+
male: [
|
|
9249
|
+
"irm\xE3o",
|
|
9250
|
+
"primo",
|
|
9251
|
+
"ex-aluno",
|
|
9252
|
+
"vizinho",
|
|
9253
|
+
"colega de trabalho",
|
|
9254
|
+
"afilhado",
|
|
9255
|
+
"filho"
|
|
9256
|
+
],
|
|
9257
|
+
female: [
|
|
9258
|
+
"irm\xE3",
|
|
9259
|
+
"prima",
|
|
9260
|
+
"ex-aluna",
|
|
9261
|
+
"vizinha",
|
|
9262
|
+
"colega de trabalho",
|
|
9263
|
+
"afilhada",
|
|
9264
|
+
"filha"
|
|
9265
|
+
]
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
9268
|
+
};
|
|
9269
|
+
var ATTACK_CLIPS = attack_clips_default.clips;
|
|
9270
|
+
var ATTACK_CLIP_LOCALES = attack_clips_default.locales;
|
|
9271
|
+
var FIRST_PERSON_CLIPS = first_person_clips_default.clips;
|
|
9272
|
+
var NAME_POOLS = { "pt-BR": names_default["pt-BR"] };
|
|
9273
|
+
|
|
9274
|
+
// src/presets/first-person-arms.json
|
|
9275
|
+
var first_person_arms_default = {
|
|
9276
|
+
source: {
|
|
9277
|
+
note: "Authored in the kit: no team delivered first-person arms. The rig is in metres in the camera's space (x right, y up, the view looks down -z); rest poses use the hand frame of HandPose in game-contracts, Euler [roll, yaw, pitch] in the order YZX (yaw pi/2 points the fingers forward)."
|
|
9278
|
+
},
|
|
9279
|
+
rig: {
|
|
9280
|
+
shoulder: [0.16, -0.36, 0.18],
|
|
9281
|
+
upper: 0.32,
|
|
9282
|
+
fore: 0.3,
|
|
9283
|
+
reachShift: 0.35,
|
|
9284
|
+
wrist: 0.07,
|
|
9285
|
+
elbowPole: [0.45, -1, 0.2],
|
|
9286
|
+
shoulderLeft: [-0.07, -0.46, 0.18],
|
|
9287
|
+
elbowPoleLeft: [-0.1, -1, 0.2],
|
|
9288
|
+
radius: { shoulder: 0.04, elbow: 0.034, wrist: 0.028, cuff: 0.031 },
|
|
9289
|
+
hip: [0.12, -0.82, 0.04],
|
|
9290
|
+
thigh: 0.46,
|
|
9291
|
+
shin: 0.46,
|
|
9292
|
+
kneePole: [0.1, 0.5, -1],
|
|
9293
|
+
foot: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0] }
|
|
9294
|
+
},
|
|
9295
|
+
stances: {
|
|
9296
|
+
relaxed: {
|
|
9297
|
+
R: { p: [0.2, -0.22, -0.36], r: [0.05, 1.57, -0.05], grip: 0.2 },
|
|
9298
|
+
L: "mirror"
|
|
9299
|
+
},
|
|
9300
|
+
fists: {
|
|
9301
|
+
R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1 },
|
|
9302
|
+
L: "mirror"
|
|
9303
|
+
},
|
|
9304
|
+
hold: { follow: true, fallback: "relaxed" }
|
|
9305
|
+
},
|
|
9306
|
+
bob: { frequency: 2.3, x: 6e-3, y: 8e-3, run: 1.5, fullSpeed: 4, crouch: 0.02 }
|
|
9307
|
+
};
|
|
9308
|
+
|
|
9309
|
+
// src/first-person/hand.ts
|
|
9310
|
+
var FINGERS = [
|
|
9311
|
+
{ z: -0.03, lengths: [0.034, 0.024, 0.02], width: 0.016 },
|
|
9312
|
+
{ z: -0.0105, lengths: [0.038, 0.027, 0.022], width: 0.0165 },
|
|
9313
|
+
{ z: 9e-3, lengths: [0.035, 0.025, 0.02], width: 0.0155 },
|
|
9314
|
+
{ z: 0.027, lengths: [0.029, 0.02, 0.018], width: 0.0135 }
|
|
9315
|
+
];
|
|
9316
|
+
var PALM = { length: 0.09, thick: 0.026, width: 0.09, wristWidth: 0.066 };
|
|
9317
|
+
var FINGER_THICK = 0.0135;
|
|
9318
|
+
var CURL = { near: 1.05, mid: 1.25, far: 0.85, spread: 0.06 };
|
|
9319
|
+
var THUMB = { base: [5e-3, -6e-3, -0.044], near: 0.034, far: 0.028, width: 0.019 };
|
|
9320
|
+
var createHandMesh = (THREE, skin, wristSkin = skin) => {
|
|
9321
|
+
const root = new THREE.Group();
|
|
9322
|
+
root.name = "first-person-hand";
|
|
9323
|
+
const geometries = [];
|
|
9324
|
+
const box2 = (parent, sx, sy, sz, x, y, z, material = skin) => {
|
|
9325
|
+
const geometry = new THREE.BoxGeometry(sx, sy, sz);
|
|
9326
|
+
geometries.push(geometry);
|
|
9327
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9328
|
+
mesh2.position.set(x, y, z);
|
|
9329
|
+
mesh2.frustumCulled = false;
|
|
9330
|
+
parent.add(mesh2);
|
|
9331
|
+
return mesh2;
|
|
9332
|
+
};
|
|
9333
|
+
const palmGeometry = new THREE.BoxGeometry(PALM.length, PALM.thick, PALM.width);
|
|
9334
|
+
const palmPosition = palmGeometry.getAttribute("position");
|
|
9335
|
+
for (let i = 0; i < palmPosition.count; i++) {
|
|
9336
|
+
if (palmPosition.getX(i) < 0) palmPosition.setZ(i, palmPosition.getZ(i) * PALM.wristWidth / PALM.width);
|
|
9337
|
+
}
|
|
9338
|
+
palmGeometry.computeVertexNormals();
|
|
9339
|
+
geometries.push(palmGeometry);
|
|
9340
|
+
const palm = new THREE.Mesh(palmGeometry, skin);
|
|
9341
|
+
palm.frustumCulled = false;
|
|
9342
|
+
root.add(palm);
|
|
9343
|
+
const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 8);
|
|
9344
|
+
wristGeometry.rotateZ(Math.PI / 2);
|
|
9345
|
+
geometries.push(wristGeometry);
|
|
9346
|
+
const wrist = new THREE.Mesh(wristGeometry, wristSkin);
|
|
9347
|
+
wrist.position.set(-PALM.length / 2 - 0.018, 0, 0);
|
|
9348
|
+
wrist.frustumCulled = false;
|
|
9349
|
+
root.add(wrist);
|
|
9350
|
+
const joints = [];
|
|
9351
|
+
FINGERS.forEach((finger, index) => {
|
|
9352
|
+
const segments = [];
|
|
9353
|
+
let parent = root;
|
|
9354
|
+
let at = PALM.length / 2;
|
|
9355
|
+
finger.lengths.forEach((length, k) => {
|
|
9356
|
+
const group = new THREE.Group();
|
|
9357
|
+
group.position.set(at, k === 0 ? 2e-3 : 0, k === 0 ? finger.z : 0);
|
|
9358
|
+
const taper = 1 - k * 0.07;
|
|
9359
|
+
box2(group, length, FINGER_THICK * taper, finger.width * taper, length / 2, 0, 0);
|
|
9360
|
+
parent.add(group);
|
|
9361
|
+
segments.push(group);
|
|
9362
|
+
parent = group;
|
|
9363
|
+
at = length;
|
|
9364
|
+
});
|
|
9365
|
+
joints.push({ segments, weight: 1 + index * (CURL.spread / CURL.near) });
|
|
9366
|
+
});
|
|
9367
|
+
const thumbNear = new THREE.Group();
|
|
9368
|
+
thumbNear.rotation.order = "YZX";
|
|
9369
|
+
thumbNear.position.set(...THUMB.base);
|
|
9370
|
+
box2(thumbNear, THUMB.near, FINGER_THICK * 1.1, THUMB.width, THUMB.near / 2, 0, 0);
|
|
9371
|
+
const thumbFar = new THREE.Group();
|
|
9372
|
+
thumbFar.position.set(THUMB.near, 0, 0);
|
|
9373
|
+
box2(thumbFar, THUMB.far, FINGER_THICK, THUMB.width * 0.9, THUMB.far / 2, 0, 0);
|
|
9374
|
+
thumbNear.add(thumbFar);
|
|
9375
|
+
root.add(thumbNear);
|
|
9376
|
+
let lastGrip = 0;
|
|
9377
|
+
let indexCurl;
|
|
9378
|
+
const curl = [CURL.near, CURL.mid, CURL.far];
|
|
9379
|
+
const applyIndex = () => {
|
|
9380
|
+
const finger = joints[0];
|
|
9381
|
+
if (!finger) return;
|
|
9382
|
+
const k = indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl));
|
|
9383
|
+
finger.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * finger.weight);
|
|
9384
|
+
};
|
|
9385
|
+
const setGrip = (grip) => {
|
|
9386
|
+
const k = Math.min(1, Math.max(0, grip));
|
|
9387
|
+
lastGrip = k;
|
|
9388
|
+
for (const joint of joints) joint.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * joint.weight);
|
|
9389
|
+
thumbNear.rotation.set(0, 0.55 - 1.45 * k, -0.1 - 0.75 * k);
|
|
9390
|
+
thumbFar.rotation.z = -0.1 - 0.7 * k;
|
|
9391
|
+
applyIndex();
|
|
9392
|
+
};
|
|
9393
|
+
const setIndex = (value) => {
|
|
9394
|
+
indexCurl = value;
|
|
9395
|
+
applyIndex();
|
|
9396
|
+
};
|
|
9397
|
+
setGrip(0);
|
|
9398
|
+
return {
|
|
9399
|
+
root,
|
|
9400
|
+
setGrip,
|
|
9401
|
+
setIndex,
|
|
9402
|
+
dispose() {
|
|
9403
|
+
for (const geometry of geometries) geometry.dispose();
|
|
9404
|
+
}
|
|
9405
|
+
};
|
|
9406
|
+
};
|
|
9407
|
+
|
|
9408
|
+
// src/first-person/ik.ts
|
|
9409
|
+
var solveTwoBone = (root, target, upper, lower, pole, out) => {
|
|
9410
|
+
const line = out.line.copy(target).sub(root);
|
|
9411
|
+
let distance3 = line.length();
|
|
9412
|
+
if (distance3 < 1e-6) line.set(0, 0, -1);
|
|
9413
|
+
else line.divideScalar(distance3);
|
|
9414
|
+
const min = Math.abs(upper - lower) + 1e-4;
|
|
9415
|
+
const max = upper + lower - 1e-4;
|
|
9416
|
+
out.clamped = distance3 > max || distance3 < min;
|
|
9417
|
+
distance3 = Math.min(max, Math.max(min, distance3));
|
|
9418
|
+
const along = (distance3 * distance3 + upper * upper - lower * lower) / (2 * distance3);
|
|
9419
|
+
const across = Math.sqrt(Math.max(0, upper * upper - along * along));
|
|
9420
|
+
const side = out.side.copy(pole);
|
|
9421
|
+
side.addScaledVector(line, -side.dot(line));
|
|
9422
|
+
if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
|
|
9423
|
+
if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
|
|
9424
|
+
side.normalize();
|
|
9425
|
+
out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
|
|
9426
|
+
out.end.copy(root).addScaledVector(line, distance3);
|
|
9427
|
+
return out;
|
|
9428
|
+
};
|
|
9429
|
+
var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
|
|
9430
|
+
|
|
9431
|
+
// src/first-person/arms.ts
|
|
9432
|
+
var DEFAULT_BLEND = 0.18;
|
|
9433
|
+
var ELBOW_SEGMENTS = 8;
|
|
9434
|
+
var REACH_SLACK = 0.97;
|
|
9435
|
+
var CONTACT_PROBE = 1.4;
|
|
9436
|
+
var CONTACT_MIN = 0.12;
|
|
9437
|
+
var CONTACT_MARGIN = 5e-3;
|
|
9438
|
+
var CONTACT_PALM = 0.03;
|
|
9439
|
+
var FOOT_LENGTH = 0.24;
|
|
9440
|
+
var FOOT_SHOWN = 1e-3;
|
|
9441
|
+
var AWAY_DROP = 0.55;
|
|
9442
|
+
var AWAY_OUT = 7;
|
|
9443
|
+
var AWAY_IN = 20;
|
|
9444
|
+
var AWAY_HIDDEN = 0.97;
|
|
9445
|
+
var FILL = 0.3;
|
|
9446
|
+
var FIRST_PERSON_STANCES = first_person_arms_default.stances;
|
|
9447
|
+
var FIRST_PERSON_RIG = first_person_arms_default.rig;
|
|
9448
|
+
var FIRST_PERSON_BOB = first_person_arms_default.bob;
|
|
9449
|
+
var hexOf2 = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
|
|
9450
|
+
var scaleHex = (hex2, factor) => {
|
|
9451
|
+
const channel = (shift) => Math.round((hex2 >> shift & 255) * factor);
|
|
9452
|
+
return channel(16) << 16 | channel(8) << 8 | channel(0);
|
|
9453
|
+
};
|
|
9454
|
+
var mix2 = (a, b, k) => a + (b - a) * k;
|
|
9455
|
+
var ease2 = (k) => k * k * (3 - 2 * k);
|
|
9456
|
+
var FIRST_PERSON_COLORS = {
|
|
9457
|
+
skin: hexOf2(player_avatar_default.spec.skin),
|
|
9458
|
+
sleeve: hexOf2(player_avatar_default.shirtColor),
|
|
9459
|
+
cuff: scaleHex(hexOf2(player_avatar_default.shirtColor), 0.72),
|
|
9460
|
+
trouser: 2962496,
|
|
9461
|
+
shoe: 1841690
|
|
9462
|
+
};
|
|
9463
|
+
var newPose = (THREE) => ({ p: new THREE.Vector3(), q: new THREE.Quaternion(), grip: 0, scale: 1 });
|
|
9464
|
+
var copyPose = (to, from) => {
|
|
9465
|
+
to.p.copy(from.p);
|
|
9466
|
+
to.q.copy(from.q);
|
|
9467
|
+
to.grip = from.grip;
|
|
9468
|
+
to.scale = from.scale;
|
|
9469
|
+
return to;
|
|
9470
|
+
};
|
|
9471
|
+
var mixPose = (THREE, out, a, b, k) => {
|
|
9472
|
+
out.p.lerpVectors(a.p, b.p, k);
|
|
9473
|
+
out.q.copy(a.q).slerp(b.q, k);
|
|
9474
|
+
out.grip = mix2(a.grip, b.grip, k);
|
|
9475
|
+
out.scale = mix2(a.scale, b.scale, k);
|
|
9476
|
+
return out;
|
|
9477
|
+
};
|
|
9478
|
+
var mirrorRotation = (THREE, q) => {
|
|
9479
|
+
const m = new THREE.Matrix4().makeRotationFromQuaternion(q);
|
|
9480
|
+
const e = m.elements;
|
|
9481
|
+
for (const [i, j] of [[0, 1], [0, 2], [1, 0], [2, 0]]) e[j * 4 + i] = -e[j * 4 + i];
|
|
9482
|
+
return new THREE.Quaternion().setFromRotationMatrix(m);
|
|
9483
|
+
};
|
|
9484
|
+
var mirrorQuaternion = (THREE, q) => {
|
|
9485
|
+
const m = new THREE.Matrix4().makeRotationFromQuaternion(q);
|
|
9486
|
+
const e = m.elements;
|
|
9487
|
+
for (let column = 0; column < 3; column++) e[column * 4] = -e[column * 4];
|
|
9488
|
+
for (let row = 0; row < 3; row++) e[8 + row] = -e[8 + row];
|
|
9489
|
+
return new THREE.Quaternion().setFromRotationMatrix(m);
|
|
9490
|
+
};
|
|
9491
|
+
function createFirstPersonArms(THREE, options = {}) {
|
|
9492
|
+
const colors = { ...FIRST_PERSON_COLORS, ...options.colors };
|
|
9493
|
+
const rig = {
|
|
9494
|
+
...FIRST_PERSON_RIG,
|
|
9495
|
+
...options.rig,
|
|
9496
|
+
radius: { ...FIRST_PERSON_RIG.radius, ...options.rig?.radius },
|
|
9497
|
+
foot: options.rig?.foot ?? FIRST_PERSON_RIG.foot
|
|
9498
|
+
};
|
|
9499
|
+
const stances = { ...FIRST_PERSON_STANCES, ...options.stances };
|
|
9500
|
+
const bob = { ...FIRST_PERSON_BOB, ...options.bob };
|
|
9501
|
+
const blendSeconds = options.blend ?? DEFAULT_BLEND;
|
|
9502
|
+
const clips = new Map((options.clips ?? []).map((clip) => [clip.id, clip]));
|
|
9503
|
+
const root = new THREE.Group();
|
|
9504
|
+
root.name = "first-person-arms";
|
|
9505
|
+
root.userData.viewmodel = true;
|
|
9506
|
+
const lit = (color2) => new THREE.MeshLambertMaterial({ color: color2, emissive: scaleHex(color2, FILL) });
|
|
9507
|
+
const materials = {
|
|
9508
|
+
skin: lit(colors.skin),
|
|
9509
|
+
sleeve: lit(colors.sleeve),
|
|
9510
|
+
cuff: lit(colors.cuff),
|
|
9511
|
+
trouser: lit(colors.trouser),
|
|
9512
|
+
shoe: lit(colors.shoe),
|
|
9513
|
+
sole: lit(scaleHex(colors.shoe, 0.55))
|
|
9514
|
+
};
|
|
9515
|
+
const geometries = [];
|
|
9516
|
+
const Y = new THREE.Vector3(0, 1, 0);
|
|
9517
|
+
const limb2 = (parent, wide, narrow, length, material) => {
|
|
9518
|
+
const geometry = new THREE.CylinderGeometry(narrow, wide, length, 8, 1);
|
|
9519
|
+
geometry.translate(0, length / 2, 0);
|
|
9520
|
+
geometries.push(geometry);
|
|
9521
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9522
|
+
mesh2.frustumCulled = false;
|
|
9523
|
+
mesh2.userData.length = length;
|
|
9524
|
+
parent.add(mesh2);
|
|
9525
|
+
return mesh2;
|
|
9526
|
+
};
|
|
9527
|
+
const ball = (parent, radius, material) => {
|
|
9528
|
+
const geometry = new THREE.SphereGeometry(radius, ELBOW_SEGMENTS, 6);
|
|
9529
|
+
geometries.push(geometry);
|
|
9530
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9531
|
+
mesh2.frustumCulled = false;
|
|
9532
|
+
parent.add(mesh2);
|
|
9533
|
+
return mesh2;
|
|
9534
|
+
};
|
|
9535
|
+
const place = (segment, from, to) => {
|
|
9536
|
+
const direction = new THREE.Vector3().subVectors(to, from);
|
|
9537
|
+
const length = direction.length();
|
|
9538
|
+
segment.position.copy(from);
|
|
9539
|
+
if (length > 1e-6) segment.quaternion.setFromUnitVectors(Y, direction.divideScalar(length));
|
|
9540
|
+
segment.scale.y = length / segment.userData.length;
|
|
9541
|
+
};
|
|
9542
|
+
const arms = {};
|
|
9543
|
+
for (const side of ["R", "L"]) {
|
|
9544
|
+
const sign2 = side === "R" ? 1 : -1;
|
|
9545
|
+
const hand = createHandMesh(THREE, materials.skin);
|
|
9546
|
+
const anchor = new THREE.Group();
|
|
9547
|
+
anchor.name = `first-person-hand-${side}`;
|
|
9548
|
+
const mirror = new THREE.Group();
|
|
9549
|
+
mirror.scale.z = sign2;
|
|
9550
|
+
mirror.add(hand.root);
|
|
9551
|
+
anchor.add(mirror);
|
|
9552
|
+
root.add(anchor);
|
|
9553
|
+
const upper = limb2(root, rig.radius.shoulder, rig.radius.elbow, rig.upper, materials.sleeve);
|
|
9554
|
+
const fore = limb2(root, rig.radius.elbow, rig.radius.wrist, rig.fore, materials.sleeve);
|
|
9555
|
+
const cuff = limb2(root, rig.radius.cuff, rig.radius.cuff, 0.03, materials.cuff);
|
|
9556
|
+
const elbow = ball(root, rig.radius.elbow * 1.02, materials.sleeve);
|
|
9557
|
+
elbow.name = `first-person-elbow-${side}`;
|
|
9558
|
+
arms[side] = { side, sign: sign2, hand, anchor, upper, fore, cuff, elbow, shown: newPose(THREE), from: newPose(THREE), mode: null, blend: 1, drawn: newPose(THREE), joints: { shoulder: new THREE.Vector3(), elbow: new THREE.Vector3(), wrist: new THREE.Vector3() }, away: options.hideWhenIdle ? 1 : 0 };
|
|
9559
|
+
}
|
|
9560
|
+
const leg = new THREE.Group();
|
|
9561
|
+
leg.name = "first-person-leg";
|
|
9562
|
+
leg.visible = false;
|
|
9563
|
+
root.add(leg);
|
|
9564
|
+
const thigh = limb2(leg, 0.088, 0.068, rig.thigh, materials.trouser);
|
|
9565
|
+
const shin = limb2(leg, 0.068, 0.05, rig.shin, materials.trouser);
|
|
9566
|
+
const knee = ball(leg, 0.07, materials.trouser);
|
|
9567
|
+
const shoe = new THREE.Group();
|
|
9568
|
+
const shoeBox = (sx, sy, sz, x, y, material) => {
|
|
9569
|
+
const geometry = new THREE.BoxGeometry(sx, sy, sz);
|
|
9570
|
+
geometries.push(geometry);
|
|
9571
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9572
|
+
mesh2.position.set(x, y, 0);
|
|
9573
|
+
mesh2.frustumCulled = false;
|
|
9574
|
+
shoe.add(mesh2);
|
|
9575
|
+
};
|
|
9576
|
+
shoeBox(0.27, 0.07, 0.1, 0.065, -0.05, materials.shoe);
|
|
9577
|
+
shoeBox(0.27, 0.025, 0.104, 0.065, -0.095, materials.sole);
|
|
9578
|
+
shoeBox(0.1, 0.05, 0.088, 0.17, -5e-3, materials.shoe);
|
|
9579
|
+
leg.add(shoe);
|
|
9580
|
+
const shoulderOf = (sign2, out) => sign2 === -1 && rig.shoulderLeft ? out.set(...rig.shoulderLeft) : out.set(rig.shoulder[0] * sign2, rig.shoulder[1], rig.shoulder[2]);
|
|
9581
|
+
const poleOf = (sign2) => sign2 === -1 && rig.elbowPoleLeft ? new THREE.Vector3(...rig.elbowPoleLeft) : new THREE.Vector3(rig.elbowPole[0] * sign2, rig.elbowPole[1], rig.elbowPole[2]);
|
|
9582
|
+
const kneePole = new THREE.Vector3(...rig.kneePole);
|
|
9583
|
+
const hip = new THREE.Vector3(...rig.hip);
|
|
9584
|
+
const euler = new THREE.Euler(0, 0, 0, "YZX");
|
|
9585
|
+
const quaternionOf = (r) => new THREE.Quaternion().setFromEuler(euler.set(r[0], r[1], r[2], "YZX"));
|
|
9586
|
+
const restPose = (rest, mirror, out) => {
|
|
9587
|
+
out.p.set(...rest.p);
|
|
9588
|
+
out.q.copy(quaternionOf(rest.r));
|
|
9589
|
+
if (mirror) {
|
|
9590
|
+
out.p.x = -out.p.x;
|
|
9591
|
+
out.q.copy(mirrorQuaternion(THREE, out.q));
|
|
9592
|
+
}
|
|
9593
|
+
out.grip = rest.grip ?? 0;
|
|
9594
|
+
out.scale = 1;
|
|
9595
|
+
return out;
|
|
9596
|
+
};
|
|
9597
|
+
const DEFAULT_REST = { p: [0.17, -0.3, -0.3], r: [0, 1.5, 0], grip: 0.3 };
|
|
9598
|
+
const stanceRest = (id, side, out) => {
|
|
9599
|
+
const stance = stances[id] ?? stances.relaxed;
|
|
9600
|
+
const right = stance?.R ?? DEFAULT_REST;
|
|
9601
|
+
const left = stance?.L;
|
|
9602
|
+
if (side === "R") return restPose(right, false, out);
|
|
9603
|
+
return left && left !== "mirror" ? restPose(left, false, out) : restPose(right, true, out);
|
|
9604
|
+
};
|
|
9605
|
+
const camPosition = new THREE.Vector3();
|
|
9606
|
+
const camQuaternion = new THREE.Quaternion();
|
|
9607
|
+
const camInverse = new THREE.Quaternion();
|
|
9608
|
+
const clamped = newPose(THREE);
|
|
9609
|
+
const probeAt = new THREE.Vector3();
|
|
9610
|
+
const probeForward = new THREE.Vector3();
|
|
9611
|
+
const probeFingers = new THREE.Vector3();
|
|
9612
|
+
let contact;
|
|
9613
|
+
const keepOutside = (pose, reach) => {
|
|
9614
|
+
if (!contact) return pose;
|
|
9615
|
+
probeAt.set(pose.p.x, pose.p.y, 0).applyQuaternion(camQuaternion).add(camPosition);
|
|
9616
|
+
probeForward.set(0, 0, -1).applyQuaternion(camQuaternion);
|
|
9617
|
+
const distance3 = contact([probeAt.x, probeAt.y, probeAt.z], [probeForward.x, probeForward.y, probeForward.z], CONTACT_PROBE);
|
|
9618
|
+
if (distance3 === null) return pose;
|
|
9619
|
+
const limit = Math.max(CONTACT_MIN, distance3 - reach - CONTACT_MARGIN);
|
|
9620
|
+
if (-pose.p.z <= limit) return pose;
|
|
9621
|
+
copyPose(clamped, pose);
|
|
9622
|
+
clamped.p.z = -limit;
|
|
9623
|
+
return clamped;
|
|
9624
|
+
};
|
|
9625
|
+
const forward = (pose, length) => Math.max(CONTACT_PALM, -probeFingers.set(1, 0, 0).applyQuaternion(pose.q).z * length * pose.scale);
|
|
9626
|
+
const handReach = (pose) => forward(pose, 0.085);
|
|
9627
|
+
const camScale = new THREE.Vector3();
|
|
9628
|
+
const toCamera = (target, grip, scale, out) => {
|
|
9629
|
+
out.p.set(...target.position).sub(camPosition).applyQuaternion(camInverse);
|
|
9630
|
+
out.q.set(...target.quaternion).premultiply(camInverse);
|
|
9631
|
+
out.grip = grip;
|
|
9632
|
+
out.scale = scale;
|
|
9633
|
+
return out;
|
|
9634
|
+
};
|
|
9635
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9636
|
+
let run = null;
|
|
9637
|
+
const emit = (event) => {
|
|
9638
|
+
for (const listener of listeners) listener(event);
|
|
9639
|
+
};
|
|
9640
|
+
const fire = (clip, from, to) => {
|
|
9641
|
+
for (const event of clipEventsBetween(clip, from, to)) emit({ ...event, clip: clip.id });
|
|
9642
|
+
};
|
|
9643
|
+
let stanceId = "relaxed";
|
|
9644
|
+
let phase = 0;
|
|
9645
|
+
let swing = 0;
|
|
9646
|
+
let sink = 0;
|
|
9647
|
+
let first = true;
|
|
9648
|
+
const bobOffset = new THREE.Vector3();
|
|
9649
|
+
const desired = newPose(THREE);
|
|
9650
|
+
const channelPose = newPose(THREE);
|
|
9651
|
+
const mixed = newPose(THREE);
|
|
9652
|
+
const dropped = newPose(THREE);
|
|
9653
|
+
let indexCurl;
|
|
9654
|
+
const bone = createTwoBone(THREE);
|
|
9655
|
+
const shoulder = new THREE.Vector3();
|
|
9656
|
+
const wristAt = new THREE.Vector3();
|
|
9657
|
+
const palmAt = new THREE.Vector3();
|
|
9658
|
+
const fingers = new THREE.Vector3();
|
|
9659
|
+
const towards = new THREE.Vector3();
|
|
9660
|
+
const fromChannel = (channel, side, base, out) => {
|
|
9661
|
+
out.p.set(...channel.p);
|
|
9662
|
+
out.q.copy(quaternionOf(channel.r));
|
|
9663
|
+
if (channel.mirror && side === "L") {
|
|
9664
|
+
out.p.x = -out.p.x;
|
|
9665
|
+
out.q.copy(mirrorQuaternion(THREE, out.q));
|
|
9666
|
+
}
|
|
9667
|
+
out.grip = channel.grip ?? base.grip;
|
|
9668
|
+
out.scale = base.scale;
|
|
9669
|
+
return out;
|
|
9670
|
+
};
|
|
9671
|
+
const drawArm = (arm, pose) => {
|
|
9672
|
+
copyPose(arm.drawn, pose);
|
|
9673
|
+
const scale = pose.scale;
|
|
9674
|
+
fingers.set(1, 0, 0).applyQuaternion(pose.q);
|
|
9675
|
+
wristAt.copy(pose.p).addScaledVector(fingers, -rig.wrist * scale);
|
|
9676
|
+
shoulderOf(arm.sign, shoulder).add(bobOffset);
|
|
9677
|
+
const excess = shoulder.distanceTo(wristAt) - (rig.upper + rig.fore) * REACH_SLACK;
|
|
9678
|
+
if (excess > 0 && rig.reachShift > 0) shoulder.addScaledVector(towards.subVectors(wristAt, shoulder).normalize(), Math.min(excess, rig.reachShift));
|
|
9679
|
+
solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, poleOf(arm.sign), bone);
|
|
9680
|
+
palmAt.copy(bone.end).addScaledVector(fingers, rig.wrist * scale);
|
|
9681
|
+
arm.drawn.p.copy(palmAt);
|
|
9682
|
+
arm.anchor.position.copy(palmAt);
|
|
9683
|
+
arm.anchor.quaternion.copy(pose.q);
|
|
9684
|
+
arm.anchor.scale.setScalar(scale);
|
|
9685
|
+
arm.hand.setGrip(pose.grip);
|
|
9686
|
+
arm.hand.setIndex(arm.side === "R" ? indexCurl : void 0);
|
|
9687
|
+
arm.joints.shoulder.copy(shoulder);
|
|
9688
|
+
arm.joints.elbow.copy(bone.joint);
|
|
9689
|
+
arm.joints.wrist.copy(bone.end);
|
|
9690
|
+
place(arm.upper, shoulder, bone.joint);
|
|
9691
|
+
place(arm.fore, bone.joint, bone.end);
|
|
9692
|
+
arm.elbow.position.copy(bone.joint);
|
|
9693
|
+
const toWrist = new THREE.Vector3().subVectors(bone.end, bone.joint).normalize();
|
|
9694
|
+
place(arm.cuff, new THREE.Vector3().copy(bone.end).addScaledVector(toWrist, -0.02), new THREE.Vector3().copy(bone.end).addScaledVector(toWrist, 0.012));
|
|
9695
|
+
};
|
|
9696
|
+
const footPose = newPose(THREE);
|
|
9697
|
+
const footRest = newPose(THREE);
|
|
9698
|
+
const ankle = new THREE.Vector3();
|
|
9699
|
+
const hipMirrored = new THREE.Vector3(-hip.x, hip.y, hip.z);
|
|
9700
|
+
const kneePoleMirrored = new THREE.Vector3(-kneePole.x, kneePole.y, kneePole.z);
|
|
9701
|
+
const drawLeg = (pose, left) => {
|
|
9702
|
+
const origin = left ? hipMirrored : hip;
|
|
9703
|
+
solveTwoBone(origin, pose.p, rig.thigh, rig.shin, left ? kneePoleMirrored : kneePole, bone);
|
|
9704
|
+
ankle.copy(bone.end);
|
|
9705
|
+
place(thigh, origin, bone.joint);
|
|
9706
|
+
place(shin, bone.joint, ankle);
|
|
9707
|
+
knee.position.copy(bone.joint);
|
|
9708
|
+
shoe.position.copy(ankle);
|
|
9709
|
+
shoe.quaternion.copy(pose.q);
|
|
9710
|
+
};
|
|
9711
|
+
const modeOf = (key, arm) => {
|
|
9712
|
+
if (arm.mode === key) return;
|
|
9713
|
+
copyPose(arm.from, arm.shown);
|
|
9714
|
+
arm.mode = key;
|
|
9715
|
+
arm.blend = first ? 1 : 0;
|
|
9716
|
+
};
|
|
9717
|
+
return {
|
|
9718
|
+
root,
|
|
9719
|
+
get playing() {
|
|
9720
|
+
return run ? run.clip.id : null;
|
|
9721
|
+
},
|
|
9722
|
+
get stance() {
|
|
9723
|
+
return stanceId;
|
|
9724
|
+
},
|
|
9725
|
+
play(id, options2) {
|
|
9726
|
+
const clip = clips.get(id);
|
|
9727
|
+
if (!clip) return false;
|
|
9728
|
+
run = { clip, time: 0, left: options2?.leg === "left" };
|
|
9729
|
+
fire(clip, -1e-9, 0);
|
|
9730
|
+
return true;
|
|
9731
|
+
},
|
|
9732
|
+
stop() {
|
|
9733
|
+
run = null;
|
|
9734
|
+
},
|
|
9735
|
+
update(dt, state) {
|
|
9736
|
+
const { camera } = state;
|
|
9737
|
+
contact = state.contact;
|
|
9738
|
+
camera.updateWorldMatrix(true, false);
|
|
9739
|
+
camera.matrixWorld.decompose(camPosition, camQuaternion, camScale);
|
|
9740
|
+
camInverse.copy(camQuaternion).invert();
|
|
9741
|
+
root.position.copy(camPosition);
|
|
9742
|
+
root.quaternion.copy(camQuaternion);
|
|
9743
|
+
root.updateMatrixWorld(true);
|
|
9744
|
+
const speed = state.speed ?? 0;
|
|
9745
|
+
const target = Math.min(1, speed / bob.fullSpeed) * (state.running ? bob.run : 1);
|
|
9746
|
+
swing += (target - swing) * Math.min(1, dt * 8);
|
|
9747
|
+
phase += dt * speed * bob.frequency;
|
|
9748
|
+
sink += ((state.crouch ? 1 : 0) - sink) * Math.min(1, dt * 8);
|
|
9749
|
+
bobOffset.set(Math.sin(phase) * bob.x * swing, Math.sin(phase * 2) * bob.y * swing - bob.crouch * sink, 0);
|
|
9750
|
+
stanceId = state.stance ?? "relaxed";
|
|
9751
|
+
const stance = stances[stanceId] ?? stances.relaxed;
|
|
9752
|
+
const targets = state.targets ?? null;
|
|
9753
|
+
if (run) {
|
|
9754
|
+
const from = run.time;
|
|
9755
|
+
run.time += dt;
|
|
9756
|
+
fire(run.clip, from, Math.min(run.time, run.clip.duration));
|
|
9757
|
+
}
|
|
9758
|
+
const sample = run ? sampleFirstPersonClip(run.clip, Math.min(run.time, run.clip.duration)) : null;
|
|
9759
|
+
for (const side of ["R", "L"]) {
|
|
9760
|
+
const arm = arms[side];
|
|
9761
|
+
const target2 = targets?.[side];
|
|
9762
|
+
let mode;
|
|
9763
|
+
if (stance?.follow && target2) {
|
|
9764
|
+
mode = "target";
|
|
9765
|
+
toCamera(target2, (side === "L" ? targets?.gripL : void 0) ?? targets?.grip ?? 1, targets?.scale ?? 1, desired);
|
|
9766
|
+
} else {
|
|
9767
|
+
mode = `rest:${stance?.follow ? stance.fallback ?? "relaxed" : stanceId}`;
|
|
9768
|
+
stanceRest(stance?.follow ? stance.fallback ?? "relaxed" : stanceId, side, desired);
|
|
9769
|
+
desired.p.add(bobOffset);
|
|
9770
|
+
}
|
|
9771
|
+
modeOf(mode, arm);
|
|
9772
|
+
if (side === "R") indexCurl = mode === "target" ? targets?.fingers?.index : void 0;
|
|
9773
|
+
arm.blend = blendSeconds <= 0 ? 1 : Math.min(1, arm.blend + dt / blendSeconds);
|
|
9774
|
+
if (arm.blend >= 1) copyPose(arm.shown, desired);
|
|
9775
|
+
else mixPose(THREE, arm.shown, arm.from, desired, ease2(arm.blend));
|
|
9776
|
+
const channel = sample?.[side];
|
|
9777
|
+
let pose;
|
|
9778
|
+
if (channel && channel.w > 0) {
|
|
9779
|
+
fromChannel(channel, side, arm.shown, channelPose);
|
|
9780
|
+
mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
|
|
9781
|
+
pose = mode === "target" ? mixed : keepOutside(mixed, handReach(mixed));
|
|
9782
|
+
} else pose = mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown));
|
|
9783
|
+
const idle = options.hideWhenIdle === true && mode === "rest:relaxed" && !(channel && channel.w > 0);
|
|
9784
|
+
arm.away = options.hideWhenIdle ? Math.min(1, Math.max(0, arm.away + ((idle ? 1 : 0) - arm.away) * Math.min(1, dt * (idle ? AWAY_OUT : AWAY_IN)))) : 0;
|
|
9785
|
+
if (arm.away > 0) {
|
|
9786
|
+
copyPose(dropped, pose);
|
|
9787
|
+
dropped.p.y -= AWAY_DROP * ease2(arm.away);
|
|
9788
|
+
pose = dropped;
|
|
9789
|
+
}
|
|
9790
|
+
drawArm(arm, pose);
|
|
9791
|
+
const shown = arm.away < AWAY_HIDDEN;
|
|
9792
|
+
for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
|
|
9793
|
+
}
|
|
9794
|
+
const foot = sample?.F;
|
|
9795
|
+
leg.visible = !!foot && foot.w > FOOT_SHOWN;
|
|
9796
|
+
if (foot && leg.visible) {
|
|
9797
|
+
restPose(rig.foot, false, footRest);
|
|
9798
|
+
fromChannel(foot, "R", footRest, footPose);
|
|
9799
|
+
mixPose(THREE, mixed, footRest, footPose, foot.w);
|
|
9800
|
+
copyPose(footPose, mixed);
|
|
9801
|
+
if (run?.left) {
|
|
9802
|
+
footPose.p.x = -footPose.p.x;
|
|
9803
|
+
footPose.q.copy(mirrorRotation(THREE, footPose.q));
|
|
9804
|
+
}
|
|
9805
|
+
drawLeg(contact ? keepOutside(footPose, forward(footPose, FOOT_LENGTH)) : footPose, run?.left ?? false);
|
|
9806
|
+
}
|
|
9807
|
+
if (run && run.time >= run.clip.duration) run = null;
|
|
9808
|
+
first = false;
|
|
9809
|
+
},
|
|
9810
|
+
on(_type, listener) {
|
|
9811
|
+
listeners.add(listener);
|
|
9812
|
+
return () => void listeners.delete(listener);
|
|
9813
|
+
},
|
|
9814
|
+
joints(side) {
|
|
9815
|
+
const { shoulder: shoulder2, elbow, wrist } = arms[side].joints;
|
|
9816
|
+
return { shoulder: shoulder2.clone(), elbow: elbow.clone(), wrist: wrist.clone() };
|
|
9817
|
+
},
|
|
9818
|
+
handPose(side) {
|
|
9819
|
+
const pose = arms[side].drawn;
|
|
9820
|
+
return { position: pose.p.clone(), quaternion: pose.q.clone(), grip: pose.grip };
|
|
9821
|
+
},
|
|
9822
|
+
dispose() {
|
|
9823
|
+
run = null;
|
|
9824
|
+
listeners.clear();
|
|
9825
|
+
for (const arm of Object.values(arms)) arm.hand.dispose();
|
|
9826
|
+
for (const geometry of geometries) geometry.dispose();
|
|
9827
|
+
for (const material of Object.values(materials)) material.dispose();
|
|
9828
|
+
root.removeFromParent();
|
|
9829
|
+
}
|
|
9830
|
+
};
|
|
9831
|
+
}
|
|
9832
|
+
|
|
9833
|
+
// src/player/room-avatar.ts
|
|
9834
|
+
var impactOf = (clip) => clip.events?.find((event) => event.type === "impact")?.t ?? null;
|
|
9835
|
+
var toRoomAvatar = (avatar, options = {}) => {
|
|
9836
|
+
const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
|
|
9837
|
+
colors: { skin: avatar.colors.skin, sleeve: avatar.colors.longSleeves ? avatar.colors.shirt : avatar.colors.skin, trouser: avatar.colors.trousers, shoe: avatar.colors.shoes, ...options.firstPerson?.colors },
|
|
9838
|
+
clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
|
|
9839
|
+
hideWhenIdle: true,
|
|
9840
|
+
...options.firstPerson
|
|
9841
|
+
});
|
|
9842
|
+
const strikes = new Map((options.strikeClips ?? ATTACK_CLIPS).map((clip) => [clip.id, clip]));
|
|
9843
|
+
const mirrored = /* @__PURE__ */ new Map();
|
|
9844
|
+
const leftOf = (clip) => {
|
|
9845
|
+
let found = mirrored.get(clip.id);
|
|
9846
|
+
if (!found) mirrored.set(clip.id, found = mirrorPoseClip(clip, `${clip.id}_left`));
|
|
9847
|
+
return found;
|
|
9848
|
+
};
|
|
9849
|
+
return {
|
|
9850
|
+
root: avatar.root,
|
|
9851
|
+
avatar,
|
|
9852
|
+
arms,
|
|
9853
|
+
...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
|
|
9854
|
+
update: (dt, state) => avatar.update(dt, {
|
|
9855
|
+
speed: state.speed,
|
|
9856
|
+
running: state.running,
|
|
9857
|
+
crouch: state.crouch,
|
|
9858
|
+
down: state.down,
|
|
9859
|
+
armed: state.armed,
|
|
9860
|
+
pitch: -state.pitch,
|
|
9861
|
+
...state.holding !== void 0 ? { holding: state.holding } : {},
|
|
9862
|
+
...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {}
|
|
9863
|
+
}),
|
|
9864
|
+
hand: (side) => avatar.hand(side),
|
|
9865
|
+
strike: (clipId, strikeOptions) => {
|
|
9866
|
+
const clip = strikes.get(clipId);
|
|
9867
|
+
if (!clip) return null;
|
|
9868
|
+
const left = strikeOptions?.leg === "left";
|
|
9869
|
+
avatar.play(left ? leftOf(clip) : clip);
|
|
9870
|
+
arms?.play(clipId, left ? { leg: "left" } : void 0);
|
|
9871
|
+
return impactOf(clip);
|
|
9872
|
+
}
|
|
9873
|
+
};
|
|
9874
|
+
};
|
|
9875
|
+
|
|
9876
|
+
// src/player/test-text.ts
|
|
9877
|
+
var AVATAR_TEXT = {
|
|
9878
|
+
en: {
|
|
9879
|
+
group: "Avatar",
|
|
9880
|
+
name: "Player body",
|
|
9881
|
+
actions: {
|
|
9882
|
+
camera_first: "Camera: 1st person",
|
|
9883
|
+
camera_third: "Camera: 3rd person",
|
|
9884
|
+
crouch: "Crouch",
|
|
9885
|
+
stand: "Stand up",
|
|
9886
|
+
fall: "Fall down",
|
|
9887
|
+
get_up: "Get up",
|
|
9888
|
+
armed_on: "Armed: on",
|
|
9889
|
+
armed_off: "Armed: off",
|
|
9890
|
+
stop_clip: "Stop clip"
|
|
9891
|
+
},
|
|
9892
|
+
play: "Play",
|
|
9893
|
+
metrics: { camera: "Camera", clip: "Clip playing", last_event: "Last clip event", state: "State", joints: "Joints" },
|
|
9894
|
+
none: "none"
|
|
9895
|
+
},
|
|
9896
|
+
"pt-BR": {
|
|
9897
|
+
group: "Avatar",
|
|
9898
|
+
name: "Corpo do jogador",
|
|
9899
|
+
actions: {
|
|
9900
|
+
camera_first: "C\xE2mera: 1\xAA pessoa",
|
|
9901
|
+
camera_third: "C\xE2mera: 3\xAA pessoa",
|
|
9902
|
+
crouch: "Agachar",
|
|
9903
|
+
stand: "Levantar",
|
|
9904
|
+
fall: "Cair",
|
|
9905
|
+
get_up: "Levantar do ch\xE3o",
|
|
9906
|
+
armed_on: "Armado: ligado",
|
|
9907
|
+
armed_off: "Armado: desligado",
|
|
9908
|
+
stop_clip: "Parar clipe"
|
|
9909
|
+
},
|
|
9910
|
+
play: "Tocar",
|
|
9911
|
+
metrics: { camera: "C\xE2mera", clip: "Clipe tocando", last_event: "\xDAltimo evento do clipe", state: "Estado", joints: "Juntas" },
|
|
9912
|
+
none: "nenhum"
|
|
9913
|
+
}
|
|
9914
|
+
};
|
|
9915
|
+
|
|
9916
|
+
// src/player/test-module.ts
|
|
9917
|
+
var AVATAR_AREA_ID = "avatar_area";
|
|
9918
|
+
var round = (value) => (Math.round(value * 100) / 100).toString();
|
|
9919
|
+
function createAvatarTestModule(options) {
|
|
9920
|
+
const clips = options.clips ?? POSE_CLIPS;
|
|
9921
|
+
const textOf = (locale) => AVATAR_TEXT[locale ?? options.locale ?? "pt-BR"] ?? AVATAR_TEXT["pt-BR"];
|
|
9922
|
+
const phaseLabel = (label, locale) => {
|
|
9923
|
+
const id = locale ?? options.locale ?? "pt-BR";
|
|
9924
|
+
const table = { ...POSE_CLIP_LOCALES[id] ?? POSE_CLIP_LOCALES["pt-BR"], ...options.clipLocales?.[id] ?? options.clipLocales?.["pt-BR"] };
|
|
9925
|
+
return typeof label === "string" ? table[label] ?? label : "";
|
|
9926
|
+
};
|
|
9927
|
+
const state = { crouch: false, down: false, armed: null };
|
|
9928
|
+
let host = null;
|
|
9929
|
+
let lastEvent = "";
|
|
9930
|
+
const log = (action, extra = {}) => {
|
|
9931
|
+
const entry = { type: "avatar", action, ...extra };
|
|
9932
|
+
host?.log(JSON.stringify(entry), entry);
|
|
9933
|
+
};
|
|
9934
|
+
const push = (action, patch, extra = {}) => {
|
|
9935
|
+
Object.assign(state, patch);
|
|
9936
|
+
options.state?.set(patch);
|
|
9937
|
+
log(action, { crouch: state.crouch, down: state.down, armed: state.armed, ...extra });
|
|
9938
|
+
};
|
|
9939
|
+
const setCamera = (mode) => {
|
|
9940
|
+
options.camera?.set(mode);
|
|
9941
|
+
log(`camera_${mode}`, { mode: options.camera?.mode() ?? null });
|
|
9942
|
+
};
|
|
9943
|
+
const onChange = (change) => {
|
|
9944
|
+
if (change.kind !== "action") return;
|
|
9945
|
+
switch (change.id) {
|
|
9946
|
+
case "camera_first":
|
|
9947
|
+
return setCamera("first");
|
|
9948
|
+
case "camera_third":
|
|
9949
|
+
return setCamera("third");
|
|
9950
|
+
case "crouch":
|
|
9951
|
+
return push("crouch", { crouch: true });
|
|
9952
|
+
case "stand":
|
|
9953
|
+
return push("stand", { crouch: false });
|
|
9954
|
+
case "fall":
|
|
9955
|
+
return push("fall", { down: true });
|
|
9956
|
+
case "get_up":
|
|
9957
|
+
return push("get_up", { down: false });
|
|
9958
|
+
case "armed_on":
|
|
9959
|
+
return push("armed_on", { armed: true });
|
|
9960
|
+
case "armed_off":
|
|
9961
|
+
return push("armed_off", { armed: false });
|
|
9962
|
+
case "stop_clip":
|
|
9963
|
+
options.avatar.stop();
|
|
9964
|
+
return log("stop_clip", { playing: options.avatar.playing });
|
|
9965
|
+
default: {
|
|
9966
|
+
const clip = clips.find((item) => `play_${item.id}` === change.id);
|
|
9967
|
+
if (!clip) return;
|
|
9968
|
+
options.avatar.play(clip);
|
|
9969
|
+
log("play_clip", { clip: clip.id, duration: clip.duration, loop: clip.loop ?? false, channels: clip.channels ?? "full" });
|
|
9970
|
+
}
|
|
9971
|
+
}
|
|
9972
|
+
};
|
|
9973
|
+
const joints = () => {
|
|
9974
|
+
const rig = options.avatar.rig;
|
|
9975
|
+
const [armA, armB] = rig.arms;
|
|
9976
|
+
const [legA, legB] = rig.legs;
|
|
9977
|
+
return `arms ${round(armA.g.rotation.x)}/${round(armB.g.rotation.x)} elbows ${round(armA.el.rotation.x)}/${round(armB.el.rotation.x)} legs ${round(legA.g.rotation.x)}/${round(legB.g.rotation.x)} knees ${round(legA.kn.rotation.x)}/${round(legB.kn.rotation.x)} lean ${round(rig.upper.rotation.x)}`;
|
|
9978
|
+
};
|
|
9979
|
+
return {
|
|
9980
|
+
id: AVATAR_AREA_ID,
|
|
9981
|
+
name: textOf().name,
|
|
9982
|
+
group: textOf().group,
|
|
9983
|
+
state,
|
|
9984
|
+
items: () => [],
|
|
9985
|
+
area: () => ({ placements: [], view: { eye: [0, 1.7, 6], target: [0, 1.2, 0] } }),
|
|
9986
|
+
controls: (locale) => {
|
|
9987
|
+
const text = textOf(locale);
|
|
9988
|
+
const action = (id, name, hint) => ({ kind: "action", id, name, ...hint ? { hint } : {} });
|
|
9989
|
+
return [
|
|
9990
|
+
action("camera_first", text.actions.camera_first),
|
|
9991
|
+
action("camera_third", text.actions.camera_third),
|
|
9992
|
+
action("crouch", text.actions.crouch),
|
|
9993
|
+
action("stand", text.actions.stand),
|
|
9994
|
+
action("fall", text.actions.fall),
|
|
9995
|
+
action("get_up", text.actions.get_up),
|
|
9996
|
+
action("armed_on", text.actions.armed_on),
|
|
9997
|
+
action("armed_off", text.actions.armed_off),
|
|
9998
|
+
...clips.map((clip) => action(`play_${clip.id}`, `${text.play}: ${clip.id}`)),
|
|
9999
|
+
action("stop_clip", text.actions.stop_clip)
|
|
10000
|
+
];
|
|
10001
|
+
},
|
|
10002
|
+
metrics: (locale) => {
|
|
10003
|
+
const text = textOf(locale);
|
|
10004
|
+
return [
|
|
10005
|
+
{ id: "camera", name: text.metrics.camera, read: () => options.camera?.mode() ?? text.none },
|
|
10006
|
+
{ id: "clip", name: text.metrics.clip, read: () => options.avatar.playing ?? text.none },
|
|
10007
|
+
{ id: "last_event", name: text.metrics.last_event, read: () => lastEvent || text.none },
|
|
10008
|
+
{ id: "state", name: text.metrics.state, read: () => `${state.crouch ? "crouch " : ""}${state.down ? "down " : ""}${state.armed === null ? "" : state.armed ? "armed" : "unarmed"}`.trim() || text.none },
|
|
10009
|
+
{ id: "joints", name: text.metrics.joints, read: joints }
|
|
10010
|
+
];
|
|
10011
|
+
},
|
|
10012
|
+
attach(lent) {
|
|
10013
|
+
host = lent;
|
|
10014
|
+
const stopChange = lent.bench?.onChange(onChange);
|
|
10015
|
+
const stopEvents = options.avatar.on("event", (event) => {
|
|
10016
|
+
lastEvent = `${event.clip} ${event.type} ${round(event.t)}s${event.data && "label" in event.data ? ` ${phaseLabel(event.data.label)}` : ""}`;
|
|
10017
|
+
log("clip_event", { clip: event.clip, event: event.type, t: event.t, data: event.data ?? null });
|
|
10018
|
+
if (event.type === "impact") log("impact", { clip: event.clip, t: event.t, kind: event.data?.kind ?? null });
|
|
10019
|
+
});
|
|
10020
|
+
return () => {
|
|
10021
|
+
stopChange?.();
|
|
10022
|
+
stopEvents();
|
|
10023
|
+
host = null;
|
|
10024
|
+
};
|
|
10025
|
+
}
|
|
10026
|
+
};
|
|
10027
|
+
}
|
|
6483
10028
|
return __toCommonJS(index_exports);
|
|
6484
10029
|
})();
|
|
6485
10030
|
//# sourceMappingURL=index.global.js.map
|