@adatechnology/game-characters-three 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/index.d.ts +328 -10
- package/dist/index.global.js +3556 -172
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1128 -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,
|
|
@@ -358,6 +1901,13 @@ var GameCharactersThree = (() => {
|
|
|
358
1901
|
}
|
|
359
1902
|
return removed;
|
|
360
1903
|
},
|
|
1904
|
+
remove(index) {
|
|
1905
|
+
const entry = entries[index];
|
|
1906
|
+
if (!entry) return void 0;
|
|
1907
|
+
entries[index] = entries[entries.length - 1];
|
|
1908
|
+
entries.pop();
|
|
1909
|
+
return entry.data;
|
|
1910
|
+
},
|
|
361
1911
|
clear() {
|
|
362
1912
|
entries.length = 0;
|
|
363
1913
|
}
|
|
@@ -1224,9 +2774,9 @@ var GameCharactersThree = (() => {
|
|
|
1224
2774
|
if (region === "torso") return bodyCount > 1 ? cost.bruteTorso : cost.torso;
|
|
1225
2775
|
return cost.limb;
|
|
1226
2776
|
};
|
|
1227
|
-
var teamGameFalloff = (distance22,
|
|
2777
|
+
var teamGameFalloff = (distance22, range2) => {
|
|
1228
2778
|
const floor = TEAM_GAME_RULES.falloff.atTwiceRange;
|
|
1229
|
-
return distance22 <=
|
|
2779
|
+
return distance22 <= range2 * TEAM_GAME_RULES.falloff.fullAtRange ? 1 : Math.max(floor, 1 - (distance22 - range2) / range2 * (1 - floor));
|
|
1230
2780
|
};
|
|
1231
2781
|
var NO_HEADSHOT = { headshot: false };
|
|
1232
2782
|
var HEADSHOT = { headshot: true };
|
|
@@ -1279,8 +2829,8 @@ var GameCharactersThree = (() => {
|
|
|
1279
2829
|
var DEFAULT_PROJECTILE_GRAMS = 8;
|
|
1280
2830
|
var RIFLE_JOULES = 1e3;
|
|
1281
2831
|
var penetrationForce = (energy, grams, diameterMm) => {
|
|
1282
|
-
const
|
|
1283
|
-
return
|
|
2832
|
+
const area3 = Math.PI * diameterMm ** 2 / 4;
|
|
2833
|
+
return area3 > 0 && energy > 0 ? Math.sqrt(energy * grams / area3) : 0;
|
|
1284
2834
|
};
|
|
1285
2835
|
var projectileOf = (hit, caliber) => {
|
|
1286
2836
|
const pellets = hit.pellets ?? caliber?.pellets ?? 1;
|
|
@@ -1368,7 +2918,15 @@ var GameCharactersThree = (() => {
|
|
|
1368
2918
|
if (points >= rules.severity.stunAtPoints) ops.stun();
|
|
1369
2919
|
return result;
|
|
1370
2920
|
};
|
|
1371
|
-
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);
|
|
1372
2930
|
var segmentBlast = (params) => {
|
|
1373
2931
|
const { ops, rule, blow } = params;
|
|
1374
2932
|
const { blast: blast2, segments } = ops.rules;
|
|
@@ -1608,7 +3166,7 @@ var GameCharactersThree = (() => {
|
|
|
1608
3166
|
};
|
|
1609
3167
|
var blast = ({ ops, hit, setup, localPoint, jointOf }) => {
|
|
1610
3168
|
const points = hit.points ?? pointsFromEnergy(hit.energy ?? 0, setup.rules);
|
|
1611
|
-
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 };
|
|
1612
3170
|
const common = { cut: hit.cut ?? setup.rules.defaultCut.explosive, damageType: hit.type, model: setup.model, headResistance: HEAD_RESISTANCE };
|
|
1613
3171
|
const rule = SEGMENT_RULES[setup.rules.segmentRule] ?? kitRule;
|
|
1614
3172
|
const result = ops.body.detail === "regions" ? regionBlast({ ...base, blow: { ...common, protection: NO_PROTECTION } }) : segmentBlast({ ...base, rule, blow: { ...common, limbFactor: ops.spec.limbFactor } });
|
|
@@ -1817,14 +3375,14 @@ var GameCharactersThree = (() => {
|
|
|
1817
3375
|
var createCharacters = (options = {}) => {
|
|
1818
3376
|
const body = createBodySpec(options.body);
|
|
1819
3377
|
const packs = options.packs ?? [];
|
|
1820
|
-
const presets = mergeById([PERSON_PRESET], packs.flatMap((
|
|
3378
|
+
const presets = mergeById([PERSON_PRESET], packs.flatMap((pack2) => pack2.presets ?? []));
|
|
1821
3379
|
applyPatches(presets, options.patch);
|
|
1822
3380
|
removeIds(presets, options.remove);
|
|
1823
3381
|
const damage = resolveDamageRules(options.damage);
|
|
1824
3382
|
const penetration = modelOf(options.penetration);
|
|
1825
|
-
const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((
|
|
3383
|
+
const protection = mergeById(PROTECTION_PRESETS, [...packs.flatMap((pack2) => pack2.protection ?? []), ...options.protection ?? []]);
|
|
1826
3384
|
const shippedCalibers = penetration.id === "team-game" ? TEAM_GAME_CALIBERS : [];
|
|
1827
|
-
const calibers = mergeById(shippedCalibers, [...packs.flatMap((
|
|
3385
|
+
const calibers = mergeById(shippedCalibers, [...packs.flatMap((pack2) => pack2.calibers ?? []), ...options.calibers ?? []]);
|
|
1828
3386
|
const setup = { rules: damage, model: penetration, protection, calibers };
|
|
1829
3387
|
let counter = 0;
|
|
1830
3388
|
return {
|
|
@@ -2231,38 +3789,518 @@ var GameCharactersThree = (() => {
|
|
|
2231
3789
|
const shoeColor = uniform ? UNIFORM_SHOES : pick(SHOE_COLORS);
|
|
2232
3790
|
return { traits: { female, tone, topColor, pantsColor, ...look, hairColor, shoeColor }, random };
|
|
2233
3791
|
};
|
|
2234
|
-
var uniformLook = () => ({
|
|
2235
|
-
sleeve: "short",
|
|
2236
|
-
hair: "short",
|
|
2237
|
-
headWidth: 1,
|
|
2238
|
-
headHeight: 1,
|
|
2239
|
-
jaw: 1,
|
|
2240
|
-
nose: 1,
|
|
2241
|
-
beard: false,
|
|
2242
|
-
glasses: false,
|
|
2243
|
-
cap: false,
|
|
2244
|
-
capColor: 0,
|
|
2245
|
-
bag: false,
|
|
2246
|
-
bagColor: 0,
|
|
2247
|
-
torn: false,
|
|
2248
|
-
apron: false
|
|
2249
|
-
});
|
|
2250
|
-
var drawLook = ({ random, pick, female, outfit, human, params }) => {
|
|
2251
|
-
const sleeve = outfit.cloth ? "long" : pick(SLEEVES);
|
|
2252
|
-
const hair = params.hairStyle ?? (params.wet ? "long" : params.charred ? "bald" : pick(female ? HAIR_STYLES_FEMALE : HAIR_STYLES_MALE));
|
|
2253
|
-
const headWidth = 1 + (random() - 0.5) * 0.16;
|
|
2254
|
-
const headHeight = 1 + (random() - 0.5) * 0.14;
|
|
2255
|
-
const jaw = 1 + (random() - 0.5) * 0.3;
|
|
2256
|
-
const nose = 1 + (random() - 0.5) * 0.5;
|
|
2257
|
-
const beard = !female && random() < BEARD_SHARE;
|
|
2258
|
-
const glasses = random() < GLASSES_SHARE;
|
|
2259
|
-
const cap = !outfit.hat && random() < CAP_SHARE;
|
|
2260
|
-
const capColor = pick(CAP_COLORS);
|
|
2261
|
-
const bag = random() < BAG_SHARE && !human;
|
|
2262
|
-
const bagColor = pick(BAG_COLORS);
|
|
2263
|
-
const torn = random() < TORN_SHARE && !human;
|
|
2264
|
-
const apron = random() < APRON_SHARE;
|
|
2265
|
-
return { sleeve, hair, headWidth, headHeight, jaw, nose, beard, glasses, cap, capColor, bag, bagColor, torn, apron };
|
|
3792
|
+
var uniformLook = () => ({
|
|
3793
|
+
sleeve: "short",
|
|
3794
|
+
hair: "short",
|
|
3795
|
+
headWidth: 1,
|
|
3796
|
+
headHeight: 1,
|
|
3797
|
+
jaw: 1,
|
|
3798
|
+
nose: 1,
|
|
3799
|
+
beard: false,
|
|
3800
|
+
glasses: false,
|
|
3801
|
+
cap: false,
|
|
3802
|
+
capColor: 0,
|
|
3803
|
+
bag: false,
|
|
3804
|
+
bagColor: 0,
|
|
3805
|
+
torn: false,
|
|
3806
|
+
apron: false
|
|
3807
|
+
});
|
|
3808
|
+
var drawLook = ({ random, pick, female, outfit, human, params }) => {
|
|
3809
|
+
const sleeve = outfit.cloth ? "long" : pick(SLEEVES);
|
|
3810
|
+
const hair = params.hairStyle ?? (params.wet ? "long" : params.charred ? "bald" : pick(female ? HAIR_STYLES_FEMALE : HAIR_STYLES_MALE));
|
|
3811
|
+
const headWidth = 1 + (random() - 0.5) * 0.16;
|
|
3812
|
+
const headHeight = 1 + (random() - 0.5) * 0.14;
|
|
3813
|
+
const jaw = 1 + (random() - 0.5) * 0.3;
|
|
3814
|
+
const nose = 1 + (random() - 0.5) * 0.5;
|
|
3815
|
+
const beard = !female && random() < BEARD_SHARE;
|
|
3816
|
+
const glasses = random() < GLASSES_SHARE;
|
|
3817
|
+
const cap = !outfit.hat && random() < CAP_SHARE;
|
|
3818
|
+
const capColor = pick(CAP_COLORS);
|
|
3819
|
+
const bag = random() < BAG_SHARE && !human;
|
|
3820
|
+
const bagColor = pick(BAG_COLORS);
|
|
3821
|
+
const torn = random() < TORN_SHARE && !human;
|
|
3822
|
+
const apron = random() < APRON_SHARE;
|
|
3823
|
+
return { sleeve, hair, headWidth, headHeight, jaw, nose, beard, glasses, cap, capColor, bag, bagColor, torn, apron };
|
|
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;
|
|
2266
4304
|
};
|
|
2267
4305
|
|
|
2268
4306
|
// src/instanced/body-geometry.ts
|
|
@@ -2298,13 +4336,13 @@ var GameCharactersThree = (() => {
|
|
|
2298
4336
|
return source;
|
|
2299
4337
|
};
|
|
2300
4338
|
var MERGED_ATTRIBUTES = [["position", 3], ["normal", 3], ["bone", 1], ["vcol", 4], ["pivot", 3], ["ppivot", 3]];
|
|
2301
|
-
var mergeBody = (
|
|
2302
|
-
const total =
|
|
4339
|
+
var mergeBody = (list2, three) => {
|
|
4340
|
+
const total = list2.reduce((sum, geometry) => sum + geometry.attributes.position.count, 0);
|
|
2303
4341
|
const merged = new three.BufferGeometry();
|
|
2304
4342
|
for (const [name, size] of MERGED_ATTRIBUTES) {
|
|
2305
4343
|
const array = new Float32Array(total * size);
|
|
2306
4344
|
let offset = 0;
|
|
2307
|
-
for (const geometry of
|
|
4345
|
+
for (const geometry of list2) {
|
|
2308
4346
|
const part = geometry.attributes[name].array;
|
|
2309
4347
|
array.set(part, offset);
|
|
2310
4348
|
offset += part.length;
|
|
@@ -2314,7 +4352,8 @@ var GameCharactersThree = (() => {
|
|
|
2314
4352
|
merged.computeBoundingSphere();
|
|
2315
4353
|
return merged;
|
|
2316
4354
|
};
|
|
2317
|
-
var buildInstancedBody = (
|
|
4355
|
+
var buildInstancedBody = (host, { segments, body = TEAM_GAME }) => {
|
|
4356
|
+
const three = shapeGeometries(host);
|
|
2318
4357
|
const joints = jointsOf(body);
|
|
2319
4358
|
const parts = [];
|
|
2320
4359
|
const half = Math.max(4, segments >> 1);
|
|
@@ -2442,15 +4481,15 @@ var GameCharactersThree = (() => {
|
|
|
2442
4481
|
copy.setAttribute(name, attribute);
|
|
2443
4482
|
attributes[name] = attribute;
|
|
2444
4483
|
}
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
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;
|
|
2451
4490
|
let count = 0;
|
|
2452
4491
|
return {
|
|
2453
|
-
mesh,
|
|
4492
|
+
mesh: mesh2,
|
|
2454
4493
|
max,
|
|
2455
4494
|
get count() {
|
|
2456
4495
|
return count;
|
|
@@ -2458,7 +4497,7 @@ var GameCharactersThree = (() => {
|
|
|
2458
4497
|
begin() {
|
|
2459
4498
|
count = 0;
|
|
2460
4499
|
},
|
|
2461
|
-
push({ a, b, c, color, eye, y = 0 }) {
|
|
4500
|
+
push({ a, b, c, color: color2, eye, y = 0 }) {
|
|
2462
4501
|
if (count >= max) return;
|
|
2463
4502
|
const index = count++;
|
|
2464
4503
|
const fourOffset = index * 4;
|
|
@@ -2466,11 +4505,11 @@ var GameCharactersThree = (() => {
|
|
|
2466
4505
|
attributes.iA.array.set(a, fourOffset);
|
|
2467
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);
|
|
2468
4507
|
attributes.iC.array.set(c, fourOffset);
|
|
2469
|
-
attributes.iCol.array.set([
|
|
4508
|
+
attributes.iCol.array.set([color2.r, color2.g, color2.b], threeOffset);
|
|
2470
4509
|
attributes.iEye.array.set(eye ? [eye.r, eye.g, eye.b] : [0, 0, 0], threeOffset);
|
|
2471
4510
|
},
|
|
2472
4511
|
end() {
|
|
2473
|
-
|
|
4512
|
+
mesh2.count = count;
|
|
2474
4513
|
for (const attribute of Object.values(attributes)) {
|
|
2475
4514
|
attribute.clearUpdateRanges();
|
|
2476
4515
|
attribute.addUpdateRange(0, count * attribute.itemSize);
|
|
@@ -2495,7 +4534,8 @@ var GameCharactersThree = (() => {
|
|
|
2495
4534
|
var FAR_LOD = 0.6;
|
|
2496
4535
|
var EYE_SCALE = 2.2;
|
|
2497
4536
|
var EYE_DEPTH_SCALE = 1.4;
|
|
2498
|
-
var bakeBody = (
|
|
4537
|
+
var bakeBody = (host, builders, spec, lod) => {
|
|
4538
|
+
const three = shapeGeometries(host);
|
|
2499
4539
|
const root = spec.retrato && builders.buildPortrait ? builders.buildPortrait(spec.retrato, lod) : builders.buildBest({ ...spec, pose: { ...BAKE_NEUTRAL_POSE, ...spec.pose ?? {} } }, lod);
|
|
2500
4540
|
const rig = root.userData.rig;
|
|
2501
4541
|
const P = rig.P;
|
|
@@ -2511,7 +4551,7 @@ var GameCharactersThree = (() => {
|
|
|
2511
4551
|
leg.kn.rotation.set(0, 0, 0);
|
|
2512
4552
|
}
|
|
2513
4553
|
root.updateMatrixWorld(true);
|
|
2514
|
-
const worldPosition2 = (
|
|
4554
|
+
const worldPosition2 = (object2) => object2.getWorldPosition(new three.Vector3()).toArray();
|
|
2515
4555
|
const bones = /* @__PURE__ */ new Map();
|
|
2516
4556
|
bones.set(rig.headG, [1, worldPosition2(rig.headG), [0, 0, 0]]);
|
|
2517
4557
|
for (const arm of rig.arms) {
|
|
@@ -2527,13 +4567,13 @@ var GameCharactersThree = (() => {
|
|
|
2527
4567
|
const trunk = [0, [0, 0.9 * P.s, 0], [0, 0, 0]];
|
|
2528
4568
|
const parts = [];
|
|
2529
4569
|
const topMaterial = spec.retrato ? null : builders.topMaterial();
|
|
2530
|
-
root.traverse((
|
|
2531
|
-
const
|
|
2532
|
-
const material =
|
|
2533
|
-
if (!
|
|
2534
|
-
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;
|
|
2535
4575
|
let tag = trunk;
|
|
2536
|
-
for (let ancestor =
|
|
4576
|
+
for (let ancestor = object2.parent; ancestor; ancestor = ancestor.parent) {
|
|
2537
4577
|
const found = bones.get(ancestor);
|
|
2538
4578
|
if (found) {
|
|
2539
4579
|
tag = found;
|
|
@@ -2541,21 +4581,21 @@ var GameCharactersThree = (() => {
|
|
|
2541
4581
|
}
|
|
2542
4582
|
}
|
|
2543
4583
|
let [bone, pivot, parentPivot] = tag;
|
|
2544
|
-
if (
|
|
4584
|
+
if (object2.userData.bone === 11) {
|
|
2545
4585
|
bone = 11;
|
|
2546
4586
|
pivot = bones.get(rig.headG)[1];
|
|
2547
4587
|
parentPivot = [0, 0, 0];
|
|
2548
4588
|
}
|
|
2549
|
-
const isEye2 = !!
|
|
2550
|
-
const
|
|
4589
|
+
const isEye2 = !!object2.userData.eye;
|
|
4590
|
+
const color2 = material.color;
|
|
2551
4591
|
const materialId = isEye2 ? 3 : material === topMaterial ? 1 : 6;
|
|
2552
4592
|
if (isEye2 && bone === 1) bone = 6;
|
|
2553
|
-
const geometry =
|
|
2554
|
-
geometry.applyMatrix4(
|
|
4593
|
+
const geometry = mesh2.geometry.clone();
|
|
4594
|
+
geometry.applyMatrix4(mesh2.matrixWorld);
|
|
2555
4595
|
const isBlood = material.polygonOffset && material.isMeshStandardMaterial;
|
|
2556
4596
|
if (isBlood) liftAlongNormals(geometry, lod < FAR_LOD ? BLOOD_LIFT_FAR : BLOOD_LIFT_NEAR);
|
|
2557
4597
|
if (isEye2) enlargeEye(three, geometry, lod < FAR_LOD ? 1 : 0.68);
|
|
2558
|
-
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];
|
|
2559
4599
|
parts.push({ g: geometry, bone, pv: pivot, pp: parentPivot, col: colors });
|
|
2560
4600
|
});
|
|
2561
4601
|
return { geo: mergeBaked(three, parts), s: P.s, bulk: P.bulk, verts: parts.reduce((sum, part) => sum + part.g.attributes.position.count, 0) };
|
|
@@ -2581,7 +4621,7 @@ var GameCharactersThree = (() => {
|
|
|
2581
4621
|
}
|
|
2582
4622
|
const position = new Float32Array(vertexTotal * 3);
|
|
2583
4623
|
const normal = new Float32Array(vertexTotal * 3);
|
|
2584
|
-
const
|
|
4624
|
+
const color2 = new Float32Array(vertexTotal * 4);
|
|
2585
4625
|
const bone = new Float32Array(vertexTotal);
|
|
2586
4626
|
const pivot = new Float32Array(vertexTotal * 3);
|
|
2587
4627
|
const parentPivot = new Float32Array(vertexTotal * 3);
|
|
@@ -2593,7 +4633,7 @@ var GameCharactersThree = (() => {
|
|
|
2593
4633
|
position.set(part.g.attributes.position.array, vertexOffset * 3);
|
|
2594
4634
|
normal.set(part.g.attributes.normal.array, vertexOffset * 3);
|
|
2595
4635
|
for (let vertex = 0; vertex < count; vertex++) {
|
|
2596
|
-
|
|
4636
|
+
color2.set(part.col, (vertexOffset + vertex) * 4);
|
|
2597
4637
|
bone[vertexOffset + vertex] = part.bone;
|
|
2598
4638
|
pivot.set(part.pv, (vertexOffset + vertex) * 3);
|
|
2599
4639
|
parentPivot.set(part.pp, (vertexOffset + vertex) * 3);
|
|
@@ -2608,7 +4648,7 @@ var GameCharactersThree = (() => {
|
|
|
2608
4648
|
const out = new three.BufferGeometry();
|
|
2609
4649
|
out.setAttribute("position", new three.BufferAttribute(position, 3));
|
|
2610
4650
|
out.setAttribute("normal", new three.BufferAttribute(normal, 3));
|
|
2611
|
-
out.setAttribute("vcol", new three.BufferAttribute(
|
|
4651
|
+
out.setAttribute("vcol", new three.BufferAttribute(color2, 4));
|
|
2612
4652
|
out.setAttribute("bone", new three.BufferAttribute(bone, 1));
|
|
2613
4653
|
out.setAttribute("pivot", new three.BufferAttribute(pivot, 3));
|
|
2614
4654
|
out.setAttribute("ppivot", new three.BufferAttribute(parentPivot, 3));
|
|
@@ -2644,20 +4684,21 @@ var GameCharactersThree = (() => {
|
|
|
2644
4684
|
var SPIN_DAMPING = 0.5;
|
|
2645
4685
|
var REST_SPEED = 0.4;
|
|
2646
4686
|
var LEG_COLOR = 2896704;
|
|
2647
|
-
var createFlyingLimbs = (
|
|
4687
|
+
var createFlyingLimbs = (host, { max = 40, random, effects = {} }) => {
|
|
4688
|
+
const three = shapeGeometries(host);
|
|
2648
4689
|
const limbs = [];
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
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());
|
|
2653
4694
|
const matrix = new three.Matrix4();
|
|
2654
4695
|
const quaternion = new three.Quaternion();
|
|
2655
4696
|
const scale = new three.Vector3();
|
|
2656
4697
|
const position = new three.Vector3();
|
|
2657
4698
|
const euler = new three.Euler();
|
|
2658
|
-
const
|
|
4699
|
+
const color2 = new three.Color();
|
|
2659
4700
|
return {
|
|
2660
|
-
mesh,
|
|
4701
|
+
mesh: mesh2,
|
|
2661
4702
|
limbs,
|
|
2662
4703
|
launch({ x, z, kind, color: sleeve, blood }) {
|
|
2663
4704
|
const isLeg2 = kind === "leg";
|
|
@@ -2710,12 +4751,12 @@ var GameCharactersThree = (() => {
|
|
|
2710
4751
|
}
|
|
2711
4752
|
}
|
|
2712
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));
|
|
2713
|
-
|
|
2714
|
-
|
|
4754
|
+
mesh2.setMatrixAt(index, matrix);
|
|
4755
|
+
mesh2.setColorAt(index, color2.set(limb2.col));
|
|
2715
4756
|
});
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
if (
|
|
4757
|
+
mesh2.count = limbs.length;
|
|
4758
|
+
mesh2.instanceMatrix.needsUpdate = true;
|
|
4759
|
+
if (mesh2.instanceColor) mesh2.instanceColor.needsUpdate = true;
|
|
2719
4760
|
}
|
|
2720
4761
|
};
|
|
2721
4762
|
};
|
|
@@ -2830,7 +4871,8 @@ var GameCharactersThree = (() => {
|
|
|
2830
4871
|
};
|
|
2831
4872
|
|
|
2832
4873
|
// src/rich/best-builder.ts
|
|
2833
|
-
var createBestBuilder = (
|
|
4874
|
+
var createBestBuilder = (host, { types, random = Math.random }) => {
|
|
4875
|
+
const THREE = shapeGeometries(host);
|
|
2834
4876
|
const options = { types, random };
|
|
2835
4877
|
let LK = 1, topM_ref = null;
|
|
2836
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);
|
|
@@ -2856,7 +4898,7 @@ var GameCharactersThree = (() => {
|
|
|
2856
4898
|
const PANTS = 2896704, GRAY2 = 4868678, RED2 = 8000012;
|
|
2857
4899
|
const col = (a, b, t) => new THREE.Color(a).lerp(new THREE.Color(b), t);
|
|
2858
4900
|
const geoBox = (w, h, d) => new THREE.BoxGeometry(w, h, d);
|
|
2859
|
-
const
|
|
4901
|
+
const box2 = (w, h, d, m) => new THREE.Mesh(geoBox(w, h, d), m);
|
|
2860
4902
|
const limbGeo = (w, len) => {
|
|
2861
4903
|
const g = geoBox(w, len, w);
|
|
2862
4904
|
g.translate(0, -len / 2, 0);
|
|
@@ -3010,7 +5052,7 @@ var GameCharactersThree = (() => {
|
|
|
3010
5052
|
st.scale.set(1, 0.35, 1);
|
|
3011
5053
|
add(p2, CYL(r * 0.32, r * 0.36, 0.07, 10), M(15261900), 0, y + (up ? 0.04 : -0.04), 0);
|
|
3012
5054
|
};
|
|
3013
|
-
const
|
|
5055
|
+
const ring2 = (p2, r, h, m, y, sx, sz) => {
|
|
3014
5056
|
const o = add(p2, CYL(r, r, h, 24), m, 0, y, 0);
|
|
3015
5057
|
o.scale.set(sx || 1.12, 1, sz || 0.7);
|
|
3016
5058
|
return o;
|
|
@@ -3134,8 +5176,8 @@ var GameCharactersThree = (() => {
|
|
|
3134
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);
|
|
3135
5177
|
}
|
|
3136
5178
|
if (O.stripes) {
|
|
3137
|
-
|
|
3138
|
-
|
|
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);
|
|
3139
5181
|
}
|
|
3140
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));
|
|
3141
5183
|
if (!spec.human) {
|
|
@@ -3429,8 +5471,8 @@ var GameCharactersThree = (() => {
|
|
|
3429
5471
|
},
|
|
3430
5472
|
{
|
|
3431
5473
|
ids: ["stubble"],
|
|
3432
|
-
apply: ({ Hd, THREE, add, cmat, scaleHex, sp }) => {
|
|
3433
|
-
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);
|
|
3434
5476
|
b.scale.set(1, 0.74, 1);
|
|
3435
5477
|
}
|
|
3436
5478
|
},
|
|
@@ -3748,8 +5790,8 @@ var GameCharactersThree = (() => {
|
|
|
3748
5790
|
add(U, Bx(0.1, 0.12, 0.09), m, -0.17, 0.95, -0.15);
|
|
3749
5791
|
return;
|
|
3750
5792
|
},
|
|
3751
|
-
belt: ({ U, m, ring }) => {
|
|
3752
|
-
|
|
5793
|
+
belt: ({ U, m, ring: ring2 }) => {
|
|
5794
|
+
ring2(U, 0.212, 0.07, m, 0.95, 1.12, 0.72);
|
|
3753
5795
|
return;
|
|
3754
5796
|
},
|
|
3755
5797
|
holster: ({ Bx, add, cmat, legs }) => {
|
|
@@ -3761,9 +5803,9 @@ var GameCharactersThree = (() => {
|
|
|
3761
5803
|
add(U, Bx(0.012, 0.1, 0.012), cmat(1710618), 0.17, 1.52, 0.18);
|
|
3762
5804
|
return;
|
|
3763
5805
|
},
|
|
3764
|
-
stripes: ({ THREE, U, add, arms, m, ring }) => {
|
|
3765
|
-
|
|
3766
|
-
|
|
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);
|
|
3767
5809
|
arms.forEach((a) => {
|
|
3768
5810
|
const r = add(a.el, new THREE.CylinderGeometry(0.058, 0.054, 0.05, 18), m, 0, -0.17, 0);
|
|
3769
5811
|
});
|
|
@@ -3873,17 +5915,17 @@ var GameCharactersThree = (() => {
|
|
|
3873
5915
|
add(arms[1].g, new THREE.CylinderGeometry(0.08, 0.078, 0.08, 18), m, 0, -0.12, 0);
|
|
3874
5916
|
return;
|
|
3875
5917
|
},
|
|
3876
|
-
tiedsleeves: ({ Bx, U, add, legs, m, ring }) => {
|
|
3877
|
-
|
|
5918
|
+
tiedsleeves: ({ Bx, U, add, legs, m, ring: ring2 }) => {
|
|
5919
|
+
ring2(U, 0.215, 0.1, m, 0.95, 1.12, 0.74);
|
|
3878
5920
|
legs.forEach((l) => add(l.g, Bx(0.1, 0.26, 0.05), m, l.s * 0.02, -0.1, 0.105));
|
|
3879
5921
|
return;
|
|
3880
5922
|
},
|
|
3881
|
-
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 }) => {
|
|
3882
5924
|
{
|
|
3883
5925
|
const g = torsoGeo(fem);
|
|
3884
5926
|
g.scale(1.06, 1, 1.12);
|
|
3885
5927
|
add(U, g, m, 0, 0, 0);
|
|
3886
|
-
|
|
5928
|
+
ring2(U, 0.22, 0.14, m, 0.9, 1.12, 0.76);
|
|
3887
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));
|
|
3888
5930
|
add(U, Bx(0.025, 0.62, 0.012), cmat(10132114), 0, 1.2, 0.19);
|
|
3889
5931
|
arms.forEach((a) => {
|
|
@@ -3973,8 +6015,8 @@ var GameCharactersThree = (() => {
|
|
|
3973
6015
|
add(U, Bx(0.05, 0.75, 0.02), cmat(4864546), 0, 1.22, 0.195, 0, 0, -0.75);
|
|
3974
6016
|
return;
|
|
3975
6017
|
},
|
|
3976
|
-
scarf: ({ PI, THREE, U, add, m, ring }) => {
|
|
3977
|
-
|
|
6018
|
+
scarf: ({ PI, THREE, U, add, m, ring: ring2 }) => {
|
|
6019
|
+
ring2(U, 0.1, 0.08, m, 1.57, 1.3, 1.2);
|
|
3978
6020
|
add(U, new THREE.ConeGeometry(0.08, 0.14, 3), m, 0, 1.47, 0.15, PI, 0, 0);
|
|
3979
6021
|
return;
|
|
3980
6022
|
},
|
|
@@ -4017,12 +6059,12 @@ var GameCharactersThree = (() => {
|
|
|
4017
6059
|
return;
|
|
4018
6060
|
}
|
|
4019
6061
|
},
|
|
4020
|
-
refl: ({ Bx, THREE, U, add, cmat, m, ring }) => {
|
|
6062
|
+
refl: ({ Bx, THREE, U, add, cmat, m, ring: ring2 }) => {
|
|
4021
6063
|
{
|
|
4022
6064
|
const v = add(U, new THREE.CylinderGeometry(0.287, 0.252, 0.46, 20), m, 0, 1.3, 0);
|
|
4023
6065
|
v.scale.set(1.13, 1, 0.72);
|
|
4024
|
-
|
|
4025
|
-
|
|
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);
|
|
4026
6068
|
add(U, Bx(0.17, 0.055, 0.02), cmat(1714794), 0, 1.47, 0.205);
|
|
4027
6069
|
return;
|
|
4028
6070
|
}
|
|
@@ -4122,9 +6164,10 @@ var GameCharactersThree = (() => {
|
|
|
4122
6164
|
EXTRAS.dbl_front = EXTRAS.rifle_front;
|
|
4123
6165
|
|
|
4124
6166
|
// src/rich/person-kit.ts
|
|
4125
|
-
var createPersonKit = (
|
|
6167
|
+
var createPersonKit = (host, { random: R, createCanvas = () => document.createElement("canvas") }) => {
|
|
6168
|
+
const THREE = shapeGeometries(host);
|
|
4126
6169
|
const T = THREE;
|
|
4127
|
-
const
|
|
6170
|
+
const scaleHex2 = (hex2, factor) => {
|
|
4128
6171
|
const channel = (shift) => (hex2 >> shift & 255) / 255 * factor * 255;
|
|
4129
6172
|
return channel(16) << 16 ^ channel(8) << 8 ^ channel(0) << 0;
|
|
4130
6173
|
};
|
|
@@ -4181,7 +6224,7 @@ var GameCharactersThree = (() => {
|
|
|
4181
6224
|
function ball(parent, r, m, x, y, z) {
|
|
4182
6225
|
return add(parent, new THREE.SphereGeometry(r, SEG, 12), m, x || 0, y || 0, z || 0);
|
|
4183
6226
|
}
|
|
4184
|
-
function
|
|
6227
|
+
function ring2(parent, r, h, m, y, sx, sz) {
|
|
4185
6228
|
const o = add(parent, new THREE.CylinderGeometry(r, r, h, 20), m, 0, y, 0);
|
|
4186
6229
|
o.scale.set(sx || 1.12, 1, sz || 0.7);
|
|
4187
6230
|
return o;
|
|
@@ -4227,17 +6270,17 @@ var GameCharactersThree = (() => {
|
|
|
4227
6270
|
const k = c + "|" + o;
|
|
4228
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 || {})));
|
|
4229
6272
|
}
|
|
4230
|
-
function splat(parent, x, y, z, r,
|
|
4231
|
-
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) => {
|
|
4232
6275
|
o.castShadow = false;
|
|
4233
6276
|
if (out) out.push(o);
|
|
4234
6277
|
return o;
|
|
4235
6278
|
};
|
|
4236
6279
|
const sy = 0.8 + R() * 0.4, seed = R() * 10;
|
|
4237
|
-
const core = skin ? stainM(
|
|
6280
|
+
const core = skin ? stainM(color2, { roughness: 0.12, metalness: 0.1 }) : stainM(scaleHex2(color2, 0.72));
|
|
4238
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));
|
|
4239
6282
|
if (!skin) {
|
|
4240
|
-
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));
|
|
4241
6284
|
}
|
|
4242
6285
|
for (let k = 0; k < (n || 0); k++) {
|
|
4243
6286
|
const ang = R() * 6.28, d = r * (1.2 + R() * 0.9), rr = r * (0.16 + R() * 0.2);
|
|
@@ -4254,14 +6297,15 @@ var GameCharactersThree = (() => {
|
|
|
4254
6297
|
}
|
|
4255
6298
|
return a;
|
|
4256
6299
|
}
|
|
4257
|
-
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 };
|
|
4258
6301
|
};
|
|
4259
6302
|
|
|
4260
6303
|
// src/rich/person-builder.ts
|
|
4261
6304
|
var PAGE_EYES = { lento: 14926657, corredor: 15022379, brutamonte: 15698474 };
|
|
4262
|
-
var createPersonBuilder = (
|
|
6305
|
+
var createPersonBuilder = (host, options = {}) => {
|
|
6306
|
+
const THREE = shapeGeometries(host);
|
|
4263
6307
|
const kit = createPersonKit(THREE, { random: options.random ?? createRng(77), createCanvas: options.createCanvas });
|
|
4264
|
-
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;
|
|
4265
6309
|
const reduce = options.reducedMotion ?? false;
|
|
4266
6310
|
const detailed = (options.detail ?? 0) === 0;
|
|
4267
6311
|
const EYE = { ...PAGE_EYES, ...options.zombieEyes };
|
|
@@ -4362,7 +6406,7 @@ var GameCharactersThree = (() => {
|
|
|
4362
6406
|
const ridge = ball(Hd, 0.1, skinM, 0, 1.842, 0.105);
|
|
4363
6407
|
ridge.scale.set(1.3, 0.28, 0.55);
|
|
4364
6408
|
add(Hd, new THREE.SphereGeometry(0.021, 10, 8), skinM, 0, 1.762, 0.182);
|
|
4365
|
-
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);
|
|
4366
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);
|
|
4367
6411
|
bags.castShadow = false;
|
|
4368
6412
|
const hc = cmat(sp.hair.c), hs = sp.hair.s;
|
|
@@ -4399,7 +6443,7 @@ var GameCharactersThree = (() => {
|
|
|
4399
6443
|
t.rotation.x = 0.25;
|
|
4400
6444
|
}
|
|
4401
6445
|
const f = sp.face || [];
|
|
4402
|
-
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 });
|
|
4403
6447
|
if (sp.hat) {
|
|
4404
6448
|
const k = sp.hat.k, c = cmat(sp.hat.c);
|
|
4405
6449
|
HATS.find((hat) => hat.kinds.includes(k))?.apply({ THREE, PI, Hd, add, Bx, cmat, cap, c, k, sp });
|
|
@@ -4438,7 +6482,7 @@ var GameCharactersThree = (() => {
|
|
|
4438
6482
|
for (const e of sp.ex || []) {
|
|
4439
6483
|
const [k, c] = e;
|
|
4440
6484
|
const m = c !== void 0 ? cmat(c) : null;
|
|
4441
|
-
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 });
|
|
4442
6486
|
}
|
|
4443
6487
|
if (sp.back === "shotgun") {
|
|
4444
6488
|
const g = new THREE.Group();
|
|
@@ -4618,9 +6662,9 @@ var GameCharactersThree = (() => {
|
|
|
4618
6662
|
// src/eyes.ts
|
|
4619
6663
|
var GLOW_SIZE = 64;
|
|
4620
6664
|
var createGlowTexture = (three, createCanvas) => {
|
|
4621
|
-
const
|
|
4622
|
-
if (!
|
|
4623
|
-
const canvas =
|
|
6665
|
+
const make2 = createCanvas ?? (typeof document === "undefined" ? void 0 : () => document.createElement("canvas"));
|
|
6666
|
+
if (!make2) return null;
|
|
6667
|
+
const canvas = make2();
|
|
4624
6668
|
canvas.width = canvas.height = GLOW_SIZE;
|
|
4625
6669
|
const context = canvas.getContext("2d");
|
|
4626
6670
|
if (!context) return null;
|
|
@@ -4632,35 +6676,36 @@ var GameCharactersThree = (() => {
|
|
|
4632
6676
|
context.fillRect(0, 0, GLOW_SIZE, GLOW_SIZE);
|
|
4633
6677
|
return new three.CanvasTexture(canvas);
|
|
4634
6678
|
};
|
|
4635
|
-
var createEye = (
|
|
6679
|
+
var createEye = (host, { color: color2, radius = 0.02, glow, glowOpacity = 0.55, texture = null }) => {
|
|
6680
|
+
const three = shapeGeometries(host);
|
|
4636
6681
|
const eye = new three.Group();
|
|
4637
|
-
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 }));
|
|
4638
6683
|
ball.userData.eye = 1;
|
|
4639
6684
|
eye.add(ball);
|
|
4640
6685
|
const size = glow ?? radius * 5;
|
|
4641
6686
|
if (size > 0) {
|
|
4642
|
-
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 }));
|
|
4643
6688
|
sprite.scale.setScalar(size);
|
|
4644
6689
|
sprite.userData.eyeGlow = 1;
|
|
4645
6690
|
eye.add(sprite);
|
|
4646
6691
|
}
|
|
4647
6692
|
return eye;
|
|
4648
6693
|
};
|
|
4649
|
-
var isEye = (
|
|
6694
|
+
var isEye = (object2) => object2.userData.eye === 1 || object2.userData.eye === true;
|
|
4650
6695
|
var findEyes = (three, root) => {
|
|
4651
6696
|
const markable = [];
|
|
4652
6697
|
const glows = [];
|
|
4653
6698
|
const base = /* @__PURE__ */ new Map();
|
|
4654
|
-
root.traverse((
|
|
4655
|
-
const
|
|
4656
|
-
if (
|
|
4657
|
-
const sprite =
|
|
6699
|
+
root.traverse((object2) => {
|
|
6700
|
+
const mesh2 = object2;
|
|
6701
|
+
if (mesh2.isMesh && isEye(object2)) markable.push(mesh2);
|
|
6702
|
+
const sprite = object2;
|
|
4658
6703
|
if (sprite.isSprite && sprite.material.blending === three.AdditiveBlending) glows.push(sprite);
|
|
4659
6704
|
});
|
|
4660
6705
|
const remember = (material) => {
|
|
4661
6706
|
if (material.color && !base.has(material)) base.set(material, { color: material.color.clone(), opacity: material.opacity });
|
|
4662
6707
|
};
|
|
4663
|
-
markable.forEach((
|
|
6708
|
+
markable.forEach((mesh2) => remember(mesh2.material));
|
|
4664
6709
|
glows.forEach((sprite) => remember(sprite.material));
|
|
4665
6710
|
const setBrightness = (level) => {
|
|
4666
6711
|
const factor = Math.min(1, Math.max(0, level));
|
|
@@ -4680,14 +6725,14 @@ var GameCharactersThree = (() => {
|
|
|
4680
6725
|
on: () => setBrightness(1),
|
|
4681
6726
|
isolate(only) {
|
|
4682
6727
|
if (!only) {
|
|
4683
|
-
hidden.splice(0).forEach((
|
|
6728
|
+
hidden.splice(0).forEach((object2) => object2.visible = true);
|
|
4684
6729
|
return;
|
|
4685
6730
|
}
|
|
4686
|
-
root.traverse((
|
|
4687
|
-
const
|
|
4688
|
-
if (
|
|
4689
|
-
|
|
4690
|
-
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);
|
|
4691
6736
|
}
|
|
4692
6737
|
});
|
|
4693
6738
|
}
|
|
@@ -4807,9 +6852,9 @@ var GameCharactersThree = (() => {
|
|
|
4807
6852
|
group.add(end);
|
|
4808
6853
|
strands.push(end);
|
|
4809
6854
|
}
|
|
4810
|
-
group.traverse((
|
|
4811
|
-
|
|
4812
|
-
if (
|
|
6855
|
+
group.traverse((object2) => {
|
|
6856
|
+
object2.userData.stump = 1;
|
|
6857
|
+
if (object2.isMesh) object2.castShadow = true;
|
|
4813
6858
|
});
|
|
4814
6859
|
return strands;
|
|
4815
6860
|
};
|
|
@@ -4824,15 +6869,15 @@ var GameCharactersThree = (() => {
|
|
|
4824
6869
|
const group = new three.Group();
|
|
4825
6870
|
group.position.y = y;
|
|
4826
6871
|
parent.add(group);
|
|
4827
|
-
const
|
|
4828
|
-
|
|
4829
|
-
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);
|
|
4830
6875
|
for (let k = 0; k < 3; k++) {
|
|
4831
6876
|
const strip = new three.Mesh(new three.CylinderGeometry(8e-3, 8e-3, 0.12, 6), materials.wound);
|
|
4832
6877
|
strip.position.set((k - 1) * 0.03, 0.03, 0.02);
|
|
4833
6878
|
group.add(strip);
|
|
4834
6879
|
}
|
|
4835
|
-
group.traverse((
|
|
6880
|
+
group.traverse((object2) => object2.userData.stump = 1);
|
|
4836
6881
|
return group;
|
|
4837
6882
|
};
|
|
4838
6883
|
|
|
@@ -4851,8 +6896,8 @@ var GameCharactersThree = (() => {
|
|
|
4851
6896
|
const trim = () => {
|
|
4852
6897
|
while (pieces.length > max) pieces.shift()?.object.removeFromParent();
|
|
4853
6898
|
};
|
|
4854
|
-
const add = (
|
|
4855
|
-
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 } : {} };
|
|
4856
6901
|
pieces.push(piece);
|
|
4857
6902
|
trim();
|
|
4858
6903
|
return piece;
|
|
@@ -4929,8 +6974,8 @@ var GameCharactersThree = (() => {
|
|
|
4929
6974
|
};
|
|
4930
6975
|
var exitWound = (three, materials, random, segment, point, direction, damage) => {
|
|
4931
6976
|
const meshes = [];
|
|
4932
|
-
segment.traverse((
|
|
4933
|
-
if (
|
|
6977
|
+
segment.traverse((object2) => {
|
|
6978
|
+
if (object2.isMesh && !object2.userData.wound && !object2.userData.stump) meshes.push(object2);
|
|
4934
6979
|
});
|
|
4935
6980
|
const raycaster = new three.Raycaster(point.clone().addScaledVector(direction, 0.6), direction.clone().negate(), 0, 0.6);
|
|
4936
6981
|
const hit = raycaster.intersectObjects(meshes)[0];
|
|
@@ -4975,9 +7020,9 @@ var GameCharactersThree = (() => {
|
|
|
4975
7020
|
crater.userData.wound = 1;
|
|
4976
7021
|
return crater;
|
|
4977
7022
|
};
|
|
4978
|
-
var explodeChunks = (three, materials, random,
|
|
4979
|
-
const centre =
|
|
4980
|
-
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();
|
|
4981
7026
|
const chunkMaterials = [materials.flesh, materials.bruise, materials.bone, materials.flesh, materials.meat];
|
|
4982
7027
|
for (let i = 0; i < 14; i++) {
|
|
4983
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]);
|
|
@@ -5010,7 +7055,7 @@ var GameCharactersThree = (() => {
|
|
|
5010
7055
|
};
|
|
5011
7056
|
|
|
5012
7057
|
// src/wounds/wounds.ts
|
|
5013
|
-
var worldPosition = (three,
|
|
7058
|
+
var worldPosition = (three, object2) => object2.getWorldPosition(new three.Vector3());
|
|
5014
7059
|
var createWounds = (three, options) => {
|
|
5015
7060
|
const { random, scene: scene2, bridge, sound } = options;
|
|
5016
7061
|
const materials = options.materials ?? createWoundMaterials(three);
|
|
@@ -5021,11 +7066,11 @@ var GameCharactersThree = (() => {
|
|
|
5021
7066
|
if (!found) throw new Error("game-characters-three: this body is not tracked by the wounds (call `track` first)");
|
|
5022
7067
|
return found;
|
|
5023
7068
|
};
|
|
5024
|
-
const burstBlood = (state, change,
|
|
5025
|
-
const point = worldPosition(three,
|
|
7069
|
+
const burstBlood = (state, change, object2, direction) => {
|
|
7070
|
+
const point = worldPosition(three, object2);
|
|
5026
7071
|
bridge?.burst(change, [point.x, point.y, point.z], state.body.blood, direction ? [direction.x, direction.y, direction.z] : void 0);
|
|
5027
7072
|
};
|
|
5028
|
-
const sideOf2 = (
|
|
7073
|
+
const sideOf2 = (object2, body) => new three.Vector3(object2.position.x >= 0 ? 1 : -1, 0, 0).applyQuaternion(body.root.quaternion);
|
|
5029
7074
|
const hideBelow = (state, id) => {
|
|
5030
7075
|
for (const below2 of segmentsBelow(id)) state.gone.add(below2);
|
|
5031
7076
|
};
|
|
@@ -5059,8 +7104,8 @@ var GameCharactersThree = (() => {
|
|
|
5059
7104
|
const target = state.body.nodes[id];
|
|
5060
7105
|
if (!target || state.gone.has(id)) return;
|
|
5061
7106
|
const { node, radius, flip } = target;
|
|
5062
|
-
const
|
|
5063
|
-
explodeChunks(three, materials, random,
|
|
7107
|
+
const box2 = new three.Box3().setFromObject(node);
|
|
7108
|
+
explodeChunks(three, materials, random, box2, direction, (chunk, velocity, spin) => {
|
|
5064
7109
|
scene2.add(chunk);
|
|
5065
7110
|
flying.throwObject(chunk, velocity, spin, id);
|
|
5066
7111
|
});
|
|
@@ -5719,7 +7764,7 @@ var GameCharactersThree = (() => {
|
|
|
5719
7764
|
batches = variants.map((variant) => createVariantBatches(three, variant, time));
|
|
5720
7765
|
for (const set of batches) for (const batch of set) group.add(batch.mesh);
|
|
5721
7766
|
};
|
|
5722
|
-
const
|
|
7767
|
+
const color2 = (hex2) => new three.Color(hex2);
|
|
5723
7768
|
return {
|
|
5724
7769
|
group,
|
|
5725
7770
|
spawn(count) {
|
|
@@ -5750,7 +7795,7 @@ var GameCharactersThree = (() => {
|
|
|
5750
7795
|
const variant = variants[member.variant];
|
|
5751
7796
|
const distance3 = Math.hypot(member.x - camera.position.x, member.z - camera.position.z);
|
|
5752
7797
|
const level = lodAt(distance3, LOD_DISTANCES);
|
|
5753
|
-
const eye =
|
|
7798
|
+
const eye = color2(eyes[variant.typeId] ?? 14926657);
|
|
5754
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 });
|
|
5755
7800
|
}
|
|
5756
7801
|
for (const set of batches) for (const batch of set) batch.end();
|
|
@@ -5815,6 +7860,58 @@ var GameCharactersThree = (() => {
|
|
|
5815
7860
|
return spec;
|
|
5816
7861
|
};
|
|
5817
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
|
+
|
|
5818
7915
|
// src/test-area/scenarios.ts
|
|
5819
7916
|
var LIMB_SEGMENTS = ["upperArmL", "forearmL", "handL", "upperArmR", "forearmR", "handR", "thighL", "shinL", "footL", "thighR", "shinR", "footR"];
|
|
5820
7917
|
var BENCHMARK_BODIES = 1e3;
|
|
@@ -5892,8 +7989,8 @@ var GameCharactersThree = (() => {
|
|
|
5892
7989
|
const colors = (decay) => {
|
|
5893
7990
|
const root = deps.buildZombie(deps.typeNames()[0], decay);
|
|
5894
7991
|
const seen = /* @__PURE__ */ new Set();
|
|
5895
|
-
root.traverse((
|
|
5896
|
-
const material =
|
|
7992
|
+
root.traverse((object2) => {
|
|
7993
|
+
const material = object2.material;
|
|
5897
7994
|
if (material?.color) seen.add(material.color.getHexString());
|
|
5898
7995
|
});
|
|
5899
7996
|
return [...seen].sort().join(",");
|
|
@@ -6148,12 +8245,12 @@ var GameCharactersThree = (() => {
|
|
|
6148
8245
|
return built.root;
|
|
6149
8246
|
},
|
|
6150
8247
|
impl: (root, id) => {
|
|
6151
|
-
const
|
|
6152
|
-
const made = builtOf.get(
|
|
8248
|
+
const object2 = root;
|
|
8249
|
+
const made = builtOf.get(object2);
|
|
6153
8250
|
const character = createCharacter(made.kind, made.cardId, id);
|
|
6154
8251
|
const scale = character.spec.height / kit.body.heightMetres;
|
|
6155
|
-
if (Math.abs(scale - 1) > 0.01)
|
|
6156
|
-
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 };
|
|
6157
8254
|
entry.character = wrap(entry, character);
|
|
6158
8255
|
const age = made.kind === "zombie" ? undead?.traitsOf(character)?.age : void 0;
|
|
6159
8256
|
const blood = age !== void 0 ? options.bloodOf?.(age) : void 0;
|
|
@@ -6171,7 +8268,7 @@ var GameCharactersThree = (() => {
|
|
|
6171
8268
|
return undead.speedFor(traits, { moment: MOMENTS_WITH_LIMP.has(moment) ? "chase" : moment, limp: moment === "limp", crawling: moment === "crawl" || entry.character.limping.length > 1 });
|
|
6172
8269
|
};
|
|
6173
8270
|
const clearOverlays = () => {
|
|
6174
|
-
for (const
|
|
8271
|
+
for (const object2 of overlays.values()) object2.parent?.remove(object2);
|
|
6175
8272
|
overlays.clear();
|
|
6176
8273
|
};
|
|
6177
8274
|
const overlayFor = (layer, entry) => {
|
|
@@ -6182,16 +8279,16 @@ var GameCharactersThree = (() => {
|
|
|
6182
8279
|
for (const part of entry.character.describe().parts) {
|
|
6183
8280
|
const shape = part.shape;
|
|
6184
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);
|
|
6185
|
-
const
|
|
6186
|
-
|
|
6187
|
-
if (part.rotation)
|
|
6188
|
-
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);
|
|
6189
8286
|
}
|
|
6190
8287
|
} else if (layer === "joints") {
|
|
6191
8288
|
for (const joint of Object.values(entry.character.rig.joints)) {
|
|
6192
|
-
const
|
|
6193
|
-
|
|
6194
|
-
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);
|
|
6195
8292
|
}
|
|
6196
8293
|
} else if (layer === "speed") {
|
|
6197
8294
|
const length = Math.max(0.1, speedOf(entry));
|
|
@@ -6218,6 +8315,17 @@ var GameCharactersThree = (() => {
|
|
|
6218
8315
|
};
|
|
6219
8316
|
const animate = (entry, dt) => {
|
|
6220
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
|
+
}
|
|
6221
8329
|
if (!character.alive) {
|
|
6222
8330
|
entry.tilt = Math.min(1, entry.tilt + dt * 2);
|
|
6223
8331
|
const rig = root.userData.rig;
|
|
@@ -6423,6 +8531,15 @@ var GameCharactersThree = (() => {
|
|
|
6423
8531
|
wounds = createWounds(lent.THREE, { random, scene: scene2, groundAt: (x, z) => lent.groundAt(x, z), ...bridge ? { bridge } : {}, ...sound ? { sound } : {} });
|
|
6424
8532
|
for (const entry of entries.values()) trackEntry(entry);
|
|
6425
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
|
+
});
|
|
6426
8543
|
const stopFrame = lent.onFrame((dt) => {
|
|
6427
8544
|
time += dt;
|
|
6428
8545
|
for (const [id, entry] of entries) {
|
|
@@ -6438,6 +8555,7 @@ var GameCharactersThree = (() => {
|
|
|
6438
8555
|
});
|
|
6439
8556
|
return () => {
|
|
6440
8557
|
stopChange?.();
|
|
8558
|
+
stopEvents();
|
|
6441
8559
|
stopFrame();
|
|
6442
8560
|
clearOverlays();
|
|
6443
8561
|
wounds?.dispose();
|
|
@@ -6473,6 +8591,1272 @@ var GameCharactersThree = (() => {
|
|
|
6473
8591
|
var registerBodyMaterial = (world) => {
|
|
6474
8592
|
if (!world.materials.has(BODY_MATERIAL_DEF.id)) world.materials.set(BODY_MATERIAL_DEF.id, BODY_MATERIAL_DEF);
|
|
6475
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
|
+
},
|
|
8613
|
+
shirtColor: "3d5a73"
|
|
8614
|
+
};
|
|
8615
|
+
|
|
8616
|
+
// src/player/avatar.ts
|
|
8617
|
+
var TWO_PI2 = Math.PI * 2;
|
|
8618
|
+
var MAX_GAIT = 1.4;
|
|
8619
|
+
var CLIP_BLEND = 0.12;
|
|
8620
|
+
var FALL_SECONDS2 = 0.3;
|
|
8621
|
+
var HIP_HEIGHT = 0.95;
|
|
8622
|
+
var WALK_ARM = 0.45;
|
|
8623
|
+
var WALK_BOB = 0.035;
|
|
8624
|
+
var WALK_LEAN = 0.04;
|
|
8625
|
+
var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
|
|
8626
|
+
var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
|
|
8627
|
+
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 };
|
|
8628
|
+
var HAND_OFFSET = { x: 0, y: -0.33, z: 0.04 };
|
|
8629
|
+
var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
|
|
8630
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
8631
|
+
var fract = (value) => value - Math.floor(value);
|
|
8632
|
+
var createPlayerAvatar = (THREE, options = {}) => {
|
|
8633
|
+
const spec = { ...player_avatar_default.spec, ...options.spec };
|
|
8634
|
+
for (const key of ["skin", "hair"]) if (typeof spec[key] === "string") spec[key] = hexOf(spec[key]);
|
|
8635
|
+
const best = createBestBuilder(THREE, { types: [{ name: String(spec.type), eye: 0 }], random: createRng(Number(spec.seed ?? 1)) });
|
|
8636
|
+
const body = best.build(spec);
|
|
8637
|
+
best.topM().color.setHex(options.shirtColor ?? hexOf(player_avatar_default.shirtColor));
|
|
8638
|
+
const rig = body.userData.rig;
|
|
8639
|
+
const profile = { f: 1.1, a: 0.28, k: 0.35, arm: WALK_ARM, bob: WALK_BOB };
|
|
8640
|
+
const animator = createRigAnimator({ gaits: { [rig.type]: profile } });
|
|
8641
|
+
const clips = options.clips ?? createPoseClipRegistry(POSE_CLIPS);
|
|
8642
|
+
const blendDefault = options.blend ?? CLIP_BLEND;
|
|
8643
|
+
const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
|
|
8644
|
+
const smooth = options.cascade ?? true;
|
|
8645
|
+
const root = new THREE.Group();
|
|
8646
|
+
const tilt = new THREE.Group();
|
|
8647
|
+
tilt.add(body);
|
|
8648
|
+
root.add(tilt);
|
|
8649
|
+
const colorOf2 = (object2) => object2?.material?.color?.getHex();
|
|
8650
|
+
const meshesOf = (node) => node.children.filter((child) => child.isMesh);
|
|
8651
|
+
const shirt = options.shirtColor ?? hexOf(player_avatar_default.shirtColor);
|
|
8652
|
+
const shoe = meshesOf(rig.legs[0].kn).find((mesh2) => mesh2.scale.z > 1.5);
|
|
8653
|
+
const forearm = colorOf2(meshesOf(rig.arms[1].el)[0]);
|
|
8654
|
+
const colors = {
|
|
8655
|
+
skin: Number(spec.skin),
|
|
8656
|
+
shirt,
|
|
8657
|
+
trousers: colorOf2(meshesOf(rig.legs[0].g)[0]) ?? 2962496,
|
|
8658
|
+
shoes: colorOf2(shoe) ?? 1841690,
|
|
8659
|
+
longSleeves: forearm === shirt
|
|
8660
|
+
};
|
|
8661
|
+
const sockets = { R: new THREE.Object3D(), L: new THREE.Object3D() };
|
|
8662
|
+
const armOf = { R: rig.arms[1], L: rig.arms[0] };
|
|
8663
|
+
for (const side of ["R", "L"]) {
|
|
8664
|
+
sockets[side].name = `hand_${side}`;
|
|
8665
|
+
sockets[side].position.set(HAND_OFFSET.x, HAND_OFFSET.y, HAND_OFFSET.z);
|
|
8666
|
+
armOf[side].el.add(sockets[side]);
|
|
8667
|
+
}
|
|
8668
|
+
const legRestX = rig.legs.map((leg) => leg.g.position.x);
|
|
8669
|
+
const legSide = (index) => index === 0 ? 1 : -1;
|
|
8670
|
+
let cycles = 0;
|
|
8671
|
+
let time = 0;
|
|
8672
|
+
let fall = 0;
|
|
8673
|
+
let run = null;
|
|
8674
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
8675
|
+
const emit = (event, onEvent) => {
|
|
8676
|
+
onEvent?.(event);
|
|
8677
|
+
for (const listener of listeners) listener(event);
|
|
8678
|
+
};
|
|
8679
|
+
const readPose = () => ({
|
|
8680
|
+
lean: rig.upper.rotation.x,
|
|
8681
|
+
twist: rig.upper.rotation.y,
|
|
8682
|
+
side: rig.upper.rotation.z,
|
|
8683
|
+
drop: rig.fig.position.y - rig.dropBase,
|
|
8684
|
+
headX: rig.headG.rotation.x,
|
|
8685
|
+
headY: rig.headG.rotation.y,
|
|
8686
|
+
headZ: rig.headG.rotation.z,
|
|
8687
|
+
armR: rig.arms[0].g.rotation.x,
|
|
8688
|
+
armL: rig.arms[1].g.rotation.x,
|
|
8689
|
+
elbR: rig.arms[0].el.rotation.x,
|
|
8690
|
+
elbL: rig.arms[1].el.rotation.x,
|
|
8691
|
+
armRz: rig.arms[0].g.rotation.z,
|
|
8692
|
+
armLz: rig.arms[1].g.rotation.z,
|
|
8693
|
+
legL: rig.legs[0].g.rotation.x,
|
|
8694
|
+
legR: rig.legs[1].g.rotation.x,
|
|
8695
|
+
kneeL: rig.legs[0].kn.rotation.x,
|
|
8696
|
+
kneeR: rig.legs[1].kn.rotation.x
|
|
8697
|
+
});
|
|
8698
|
+
const armed = (pitch, gait, hip) => {
|
|
8699
|
+
const aim = clamp((pitch + 1) / 3, 0, 0.99) * 3 - 1;
|
|
8700
|
+
const shoulder = AIMED.shoulder + aim * AIMED.perAim + AIMED.swing * Math.sin(cycles * TWO_PI2) * gait;
|
|
8701
|
+
rig.legs.forEach((leg, index) => {
|
|
8702
|
+
const x = legRestX[index];
|
|
8703
|
+
leg.g.rotation.y = hip;
|
|
8704
|
+
leg.g.position.x = x * Math.cos(hip);
|
|
8705
|
+
leg.g.position.z = -x * Math.sin(hip);
|
|
8706
|
+
});
|
|
8707
|
+
return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
|
|
8708
|
+
};
|
|
8709
|
+
const resetLegYaw = () => {
|
|
8710
|
+
rig.legs.forEach((leg, index) => {
|
|
8711
|
+
leg.g.rotation.y = 0;
|
|
8712
|
+
leg.g.position.x = legRestX[index];
|
|
8713
|
+
leg.g.position.z = 0;
|
|
8714
|
+
});
|
|
8715
|
+
};
|
|
8716
|
+
const crouchPose = (gait) => {
|
|
8717
|
+
const swing = (leg) => {
|
|
8718
|
+
const legS = legSide(leg);
|
|
8719
|
+
return CROUCH.thigh - Math.sin(cycles * TWO_PI2) * legS * CROUCH.thighWalk * gait;
|
|
8720
|
+
};
|
|
8721
|
+
return {
|
|
8722
|
+
legL: swing(0),
|
|
8723
|
+
legR: swing(1),
|
|
8724
|
+
kneeL: CROUCH.shin,
|
|
8725
|
+
kneeR: CROUCH.shin,
|
|
8726
|
+
lean: WALK_LEAN * gait + CROUCH.lean,
|
|
8727
|
+
drop: Math.abs(Math.sin(cycles * TWO_PI2)) * WALK_BOB * gait + CROUCH.drop
|
|
8728
|
+
};
|
|
8729
|
+
};
|
|
8730
|
+
const downPose = () => {
|
|
8731
|
+
const ph = cycles * TWO_PI2;
|
|
8732
|
+
const gait = 1;
|
|
8733
|
+
const leg = (index) => {
|
|
8734
|
+
const legS = legSide(index);
|
|
8735
|
+
const walkS = Math.sin(ph) * legS;
|
|
8736
|
+
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;
|
|
8737
|
+
return { hip: -walkS * DOWN.thighWalk * gait, knee: bend };
|
|
8738
|
+
};
|
|
8739
|
+
const arm = (index) => {
|
|
8740
|
+
const side = index === 0 ? 1 : -1;
|
|
8741
|
+
const armS = Math.sin(ph) * (side > 0 ? 1 : -1);
|
|
8742
|
+
const shoulder = fract(cycles * 5) > 0.5 && side > 0 ? DOWN.armHigh : DOWN.armLow + DOWN.armWave * Math.sin(cycles * 5 + side);
|
|
8743
|
+
return { shoulder, z: side * (DOWN.armZ + DOWN.armZWave * Math.sin(cycles * 3)), elbow: -0.35 - Math.max(0, armS) * 0.4 * gait };
|
|
8744
|
+
};
|
|
8745
|
+
const [l, r] = [leg(0), leg(1)];
|
|
8746
|
+
const [a0, a1] = [arm(0), arm(1)];
|
|
8747
|
+
return {
|
|
8748
|
+
legL: l.hip,
|
|
8749
|
+
kneeL: l.knee,
|
|
8750
|
+
legR: r.hip,
|
|
8751
|
+
kneeR: r.knee,
|
|
8752
|
+
armR: a0.shoulder,
|
|
8753
|
+
armRz: a0.z,
|
|
8754
|
+
elbR: a0.elbow,
|
|
8755
|
+
armL: a1.shoulder,
|
|
8756
|
+
armLz: a1.z,
|
|
8757
|
+
elbL: a1.elbow,
|
|
8758
|
+
lean: WALK_LEAN * gait,
|
|
8759
|
+
drop: Math.abs(Math.sin(ph)) * WALK_BOB * gait
|
|
8760
|
+
};
|
|
8761
|
+
};
|
|
8762
|
+
const placeBody = (progress) => {
|
|
8763
|
+
const fk = progress * progress * (3 - 2 * progress);
|
|
8764
|
+
const flip = fract(cycles * 1.7) >= 0.5 ? 1 : 0;
|
|
8765
|
+
tilt.rotation.set((-DOWN.tilt + flip * 2 * DOWN.tilt) * fk, 0, Math.sin(cycles * 7) * DOWN.roll * fk, "XYZ");
|
|
8766
|
+
const hip = new THREE.Vector3(0, HIP_HEIGHT, 0);
|
|
8767
|
+
const target = new THREE.Vector3(0, DOWN.hipY, -DOWN.hipZ + flip * DOWN.hipZFlip);
|
|
8768
|
+
const place = new THREE.Vector3(0, HIP_HEIGHT * (1 - fk), 0).addScaledVector(target, fk);
|
|
8769
|
+
tilt.position.copy(place.sub(hip.applyEuler(tilt.rotation)));
|
|
8770
|
+
};
|
|
8771
|
+
const fireClipEvents = (r, from, to) => {
|
|
8772
|
+
for (const event of clipEventsBetween(r.clip, from, to)) emit({ ...event, clip: r.clip.id }, r.onEvent);
|
|
8773
|
+
};
|
|
8774
|
+
const stepClip = (dt, down) => {
|
|
8775
|
+
if (!run) return;
|
|
8776
|
+
const r = run;
|
|
8777
|
+
const from = r.time;
|
|
8778
|
+
r.time += dt;
|
|
8779
|
+
const { duration } = r.clip;
|
|
8780
|
+
if (r.clip.loop && r.time > duration) {
|
|
8781
|
+
fireClipEvents(r, from, duration);
|
|
8782
|
+
r.time -= duration * Math.floor(r.time / duration);
|
|
8783
|
+
fireClipEvents(r, -1e-9, r.time);
|
|
8784
|
+
} else {
|
|
8785
|
+
fireClipEvents(r, from, Math.min(r.time, duration));
|
|
8786
|
+
if (r.time >= duration && !r.clip.loop) r.target = 0;
|
|
8787
|
+
}
|
|
8788
|
+
r.weight = r.blend <= 0 ? r.target : clamp(r.weight + (r.target ? dt : -dt) / r.blend, 0, 1);
|
|
8789
|
+
if (r.target === 0 && r.weight <= 0) {
|
|
8790
|
+
run = null;
|
|
8791
|
+
return;
|
|
8792
|
+
}
|
|
8793
|
+
if (down) return;
|
|
8794
|
+
const sampled = filterPoseChannels(samplePoseClip(r.clip, Math.min(r.time, duration)), r.clip.channels ?? "full");
|
|
8795
|
+
const current = readPose();
|
|
8796
|
+
const blended = {};
|
|
8797
|
+
for (const [name, value] of Object.entries(sampled)) if (name in current) blended[name] = current[name] + (value - current[name]) * r.weight;
|
|
8798
|
+
animator.setPose(rig, blended);
|
|
8799
|
+
};
|
|
8800
|
+
return {
|
|
8801
|
+
root,
|
|
8802
|
+
rig,
|
|
8803
|
+
colors,
|
|
8804
|
+
three: THREE,
|
|
8805
|
+
get playing() {
|
|
8806
|
+
return run ? run.clip.id : null;
|
|
8807
|
+
},
|
|
8808
|
+
update(dt, state = {}) {
|
|
8809
|
+
const speed = state.speed ?? 0;
|
|
8810
|
+
const moving = speed >= 0.05;
|
|
8811
|
+
if (moving) cycles += dt * walkCadence(speed);
|
|
8812
|
+
const gait = Math.min(MAX_GAIT, walkGait(speed));
|
|
8813
|
+
profile.arm = WALK_ARM * gait;
|
|
8814
|
+
profile.bob = WALK_BOB * gait;
|
|
8815
|
+
time = cycles / walkCadence(Math.max(speed, 0));
|
|
8816
|
+
animator.animate(body, time, { speed });
|
|
8817
|
+
const down = !!state.down;
|
|
8818
|
+
fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
|
|
8819
|
+
resetLegYaw();
|
|
8820
|
+
if (down) animator.setPose(rig, downPose());
|
|
8821
|
+
else {
|
|
8822
|
+
if (state.crouch) animator.setPose(rig, crouchPose(gait));
|
|
8823
|
+
if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
|
|
8824
|
+
}
|
|
8825
|
+
placeBody(fall);
|
|
8826
|
+
stepClip(dt, down);
|
|
8827
|
+
if (smooth) animator.cascade(rig, dt);
|
|
8828
|
+
},
|
|
8829
|
+
play(clip, playOptions = {}) {
|
|
8830
|
+
const found = typeof clip === "string" ? clips.get(clip) : clip;
|
|
8831
|
+
if (!found) throw new Error(`Unknown pose clip: ${String(clip)}`);
|
|
8832
|
+
run = { clip: found, time: 0, weight: run?.weight ?? 0, target: 1, blend: playOptions.blend ?? blendDefault, ...playOptions.onEvent ? { onEvent: playOptions.onEvent } : {} };
|
|
8833
|
+
fireClipEvents(run, -1e-9, 0);
|
|
8834
|
+
run.time = 0;
|
|
8835
|
+
},
|
|
8836
|
+
stop() {
|
|
8837
|
+
if (run) run.target = 0;
|
|
8838
|
+
},
|
|
8839
|
+
hand: (side) => sockets[side],
|
|
8840
|
+
on(_type, listener) {
|
|
8841
|
+
listeners.add(listener);
|
|
8842
|
+
return () => void listeners.delete(listener);
|
|
8843
|
+
},
|
|
8844
|
+
dispose() {
|
|
8845
|
+
run = null;
|
|
8846
|
+
listeners.clear();
|
|
8847
|
+
body.traverse((object2) => {
|
|
8848
|
+
const mesh2 = object2;
|
|
8849
|
+
mesh2.geometry?.dispose();
|
|
8850
|
+
const material = mesh2.material;
|
|
8851
|
+
if (Array.isArray(material)) material.forEach((m) => m.dispose());
|
|
8852
|
+
else material?.dispose();
|
|
8853
|
+
});
|
|
8854
|
+
root.removeFromParent();
|
|
8855
|
+
}
|
|
8856
|
+
};
|
|
8857
|
+
};
|
|
8858
|
+
|
|
8859
|
+
// ../game-characters-people/dist/index.js
|
|
8860
|
+
var attack_clips_default = {
|
|
8861
|
+
clips: [
|
|
8862
|
+
{
|
|
8863
|
+
id: "punch_right",
|
|
8864
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
8865
|
+
duration: 0.35,
|
|
8866
|
+
loop: false,
|
|
8867
|
+
channels: "upper",
|
|
8868
|
+
keys: [
|
|
8869
|
+
{ 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 } },
|
|
8870
|
+
{ 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 } },
|
|
8871
|
+
{ 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 } },
|
|
8872
|
+
{ 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 } },
|
|
8873
|
+
{ 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 } }
|
|
8874
|
+
],
|
|
8875
|
+
events: [
|
|
8876
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
8877
|
+
{ t: 0.1, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8878
|
+
{ t: 0.16, type: "impact", data: { kind: "punch" } },
|
|
8879
|
+
{ t: 0.22, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8880
|
+
]
|
|
8881
|
+
},
|
|
8882
|
+
{
|
|
8883
|
+
id: "shove_two_hands",
|
|
8884
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
8885
|
+
duration: 0.5,
|
|
8886
|
+
loop: false,
|
|
8887
|
+
channels: "upper",
|
|
8888
|
+
keys: [
|
|
8889
|
+
{ 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 } },
|
|
8890
|
+
{ 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 } },
|
|
8891
|
+
{ 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 } },
|
|
8892
|
+
{ 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 } },
|
|
8893
|
+
{ 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 } }
|
|
8894
|
+
],
|
|
8895
|
+
events: [
|
|
8896
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
8897
|
+
{ t: 0.15, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8898
|
+
{ t: 0.26, type: "impact", data: { kind: "shove" } },
|
|
8899
|
+
{ t: 0.34, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8900
|
+
]
|
|
8901
|
+
},
|
|
8902
|
+
{
|
|
8903
|
+
id: "front_kick",
|
|
8904
|
+
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
8905
|
+
duration: 0.6,
|
|
8906
|
+
loop: false,
|
|
8907
|
+
channels: "full",
|
|
8908
|
+
keys: [
|
|
8909
|
+
{ 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 } },
|
|
8910
|
+
{ 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 } },
|
|
8911
|
+
{ 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 } },
|
|
8912
|
+
{ 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 } },
|
|
8913
|
+
{ 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 } }
|
|
8914
|
+
],
|
|
8915
|
+
events: [
|
|
8916
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
8917
|
+
{ t: 0.18, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8918
|
+
{ t: 0.32, type: "impact", data: { kind: "kick" } },
|
|
8919
|
+
{ t: 0.42, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8920
|
+
]
|
|
8921
|
+
},
|
|
8922
|
+
{
|
|
8923
|
+
id: "recoil_rifle",
|
|
8924
|
+
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" },
|
|
8925
|
+
duration: 0.22,
|
|
8926
|
+
loop: false,
|
|
8927
|
+
channels: "upper",
|
|
8928
|
+
keys: [
|
|
8929
|
+
{ 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 } },
|
|
8930
|
+
{ 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 } },
|
|
8931
|
+
{ 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 } }
|
|
8932
|
+
],
|
|
8933
|
+
events: [
|
|
8934
|
+
{ t: 0, type: "impact", data: { kind: "shot" } },
|
|
8935
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8936
|
+
{ t: 0.08, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8937
|
+
]
|
|
8938
|
+
},
|
|
8939
|
+
{
|
|
8940
|
+
id: "recoil_pistol",
|
|
8941
|
+
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" },
|
|
8942
|
+
duration: 0.18,
|
|
8943
|
+
loop: false,
|
|
8944
|
+
channels: "upper",
|
|
8945
|
+
keys: [
|
|
8946
|
+
{ 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 } },
|
|
8947
|
+
{ 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 } },
|
|
8948
|
+
{ 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 } }
|
|
8949
|
+
],
|
|
8950
|
+
events: [
|
|
8951
|
+
{ t: 0, type: "impact", data: { kind: "shot" } },
|
|
8952
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8953
|
+
{ t: 0.06, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8954
|
+
]
|
|
8955
|
+
}
|
|
8956
|
+
],
|
|
8957
|
+
locales: {
|
|
8958
|
+
"pt-BR": {
|
|
8959
|
+
"attack_clip.phase.windup": "Prepara\xE7\xE3o",
|
|
8960
|
+
"attack_clip.phase.strike": "Golpe",
|
|
8961
|
+
"attack_clip.phase.recover": "Recupera\xE7\xE3o"
|
|
8962
|
+
},
|
|
8963
|
+
en: {
|
|
8964
|
+
"attack_clip.phase.windup": "Wind-up",
|
|
8965
|
+
"attack_clip.phase.strike": "Strike",
|
|
8966
|
+
"attack_clip.phase.recover": "Recover"
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
};
|
|
8970
|
+
var first_person_clips_default = {
|
|
8971
|
+
clips: [
|
|
8972
|
+
{
|
|
8973
|
+
id: "punch_right",
|
|
8974
|
+
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" },
|
|
8975
|
+
duration: 0.35,
|
|
8976
|
+
keys: [
|
|
8977
|
+
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
8978
|
+
{ t: 0.1, R: { p: [0.21, -0.15, -0.29], r: [0.1, 1.45, 0.2], grip: 1 } },
|
|
8979
|
+
{ t: 0.16, R: { p: [0.06, -0.09, -0.62], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
8980
|
+
{ t: 0.22, R: { p: [0.07, -0.1, -0.56], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
8981
|
+
{ t: 0.35, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
8982
|
+
],
|
|
8983
|
+
events: [
|
|
8984
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
8985
|
+
{ t: 0.1, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
8986
|
+
{ t: 0.16, type: "impact", data: { kind: "punch" } },
|
|
8987
|
+
{ t: 0.22, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
8988
|
+
]
|
|
8989
|
+
},
|
|
8990
|
+
{
|
|
8991
|
+
id: "shove_two_hands",
|
|
8992
|
+
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" },
|
|
8993
|
+
duration: 0.5,
|
|
8994
|
+
keys: [
|
|
8995
|
+
{ t: 0, R: { p: [0.17, -0.14, -0.36], r: [0, 1.5708, 1.4], grip: 0.05, w: 0 }, L: "mirror" },
|
|
8996
|
+
{ t: 0.15, R: { p: [0.15, -0.09, -0.27], r: [0, 1.5708, 1.5], grip: 0.05 }, L: "mirror" },
|
|
8997
|
+
{ t: 0.26, R: { p: [0.11, -0.02, -0.58], r: [0, 1.5708, 1.75], grip: 0.05 }, L: "mirror" },
|
|
8998
|
+
{ t: 0.34, R: { p: [0.12, -0.03, -0.54], r: [0, 1.5708, 1.65], grip: 0.05 }, L: "mirror" },
|
|
8999
|
+
{ t: 0.5, R: { p: [0.17, -0.14, -0.36], r: [0, 1.5708, 1.4], grip: 0.05, w: 0 }, L: "mirror" }
|
|
9000
|
+
],
|
|
9001
|
+
events: [
|
|
9002
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9003
|
+
{ t: 0.15, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9004
|
+
{ t: 0.26, type: "impact", data: { kind: "shove" } },
|
|
9005
|
+
{ t: 0.34, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9006
|
+
]
|
|
9007
|
+
},
|
|
9008
|
+
{
|
|
9009
|
+
id: "front_kick",
|
|
9010
|
+
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" },
|
|
9011
|
+
duration: 0.6,
|
|
9012
|
+
keys: [
|
|
9013
|
+
{ t: 0, F: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0], w: 0 } },
|
|
9014
|
+
{ t: 0.18, F: { p: [0.18, -0.58, -0.3], r: [0, 1.5708, 0.6] } },
|
|
9015
|
+
{ t: 0.32, F: { p: [0.12, -0.24, -0.65], r: [0, 1.5708, 0.35] } },
|
|
9016
|
+
{ t: 0.42, F: { p: [0.12, -0.3, -0.58], r: [0, 1.5708, 0.3] } },
|
|
9017
|
+
{ t: 0.6, F: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0], w: 0 } }
|
|
9018
|
+
],
|
|
9019
|
+
events: [
|
|
9020
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9021
|
+
{ t: 0.18, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9022
|
+
{ t: 0.32, type: "impact", data: { kind: "kick" } },
|
|
9023
|
+
{ t: 0.42, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9024
|
+
]
|
|
9025
|
+
}
|
|
9026
|
+
]
|
|
9027
|
+
};
|
|
9028
|
+
var names_default = {
|
|
9029
|
+
sources: {
|
|
9030
|
+
file: "specs/001-migracao-entregas-dos-times/sources/sobreviventes__os-vivos-do-vale.js (site/pages/sobreviventes/os-vivos-do-vale.html)",
|
|
9031
|
+
pools: "NM (lines 610-611): m, f, s, bm, bf",
|
|
9032
|
+
kinship: "RELW (line 612)"
|
|
9033
|
+
},
|
|
9034
|
+
"pt-BR": {
|
|
9035
|
+
male: [
|
|
9036
|
+
"Jo\xE3o",
|
|
9037
|
+
"Paulo",
|
|
9038
|
+
"Marcos",
|
|
9039
|
+
"Andr\xE9",
|
|
9040
|
+
"Rafael",
|
|
9041
|
+
"Thiago",
|
|
9042
|
+
"Bruno",
|
|
9043
|
+
"Luiz",
|
|
9044
|
+
"S\xE9rgio",
|
|
9045
|
+
"Wagner",
|
|
9046
|
+
"C\xEDcero",
|
|
9047
|
+
"Edson",
|
|
9048
|
+
"Igor",
|
|
9049
|
+
"Mateus",
|
|
9050
|
+
"Renan",
|
|
9051
|
+
"Hugo"
|
|
9052
|
+
],
|
|
9053
|
+
female: [
|
|
9054
|
+
"Ana",
|
|
9055
|
+
"Patr\xEDcia",
|
|
9056
|
+
"Juliana",
|
|
9057
|
+
"Camila",
|
|
9058
|
+
"Sandra",
|
|
9059
|
+
"Luana",
|
|
9060
|
+
"M\xE1rcia",
|
|
9061
|
+
"Fernanda",
|
|
9062
|
+
"Aline",
|
|
9063
|
+
"Rita",
|
|
9064
|
+
"Jaqueline",
|
|
9065
|
+
"Simone",
|
|
9066
|
+
"Larissa",
|
|
9067
|
+
"Bruna",
|
|
9068
|
+
"Tereza",
|
|
9069
|
+
"Vanessa"
|
|
9070
|
+
],
|
|
9071
|
+
surnames: [
|
|
9072
|
+
"Silva",
|
|
9073
|
+
"Santos",
|
|
9074
|
+
"Oliveira",
|
|
9075
|
+
"Souza",
|
|
9076
|
+
"Pereira",
|
|
9077
|
+
"Lima",
|
|
9078
|
+
"Carvalho",
|
|
9079
|
+
"Gomes",
|
|
9080
|
+
"Ribeiro",
|
|
9081
|
+
"Martins",
|
|
9082
|
+
"Rocha",
|
|
9083
|
+
"Almeida",
|
|
9084
|
+
"Barbosa",
|
|
9085
|
+
"Teixeira",
|
|
9086
|
+
"Moraes",
|
|
9087
|
+
"Cunha"
|
|
9088
|
+
],
|
|
9089
|
+
babyMale: [
|
|
9090
|
+
"Davi",
|
|
9091
|
+
"Miguel",
|
|
9092
|
+
"Gael",
|
|
9093
|
+
"Bento",
|
|
9094
|
+
"Noah",
|
|
9095
|
+
"Theo"
|
|
9096
|
+
],
|
|
9097
|
+
babyFemale: [
|
|
9098
|
+
"Esperan\xE7a",
|
|
9099
|
+
"Aurora",
|
|
9100
|
+
"Cec\xEDlia",
|
|
9101
|
+
"Luna",
|
|
9102
|
+
"Maria Luz",
|
|
9103
|
+
"Alice"
|
|
9104
|
+
],
|
|
9105
|
+
kinship: {
|
|
9106
|
+
male: [
|
|
9107
|
+
"irm\xE3o",
|
|
9108
|
+
"primo",
|
|
9109
|
+
"ex-aluno",
|
|
9110
|
+
"vizinho",
|
|
9111
|
+
"colega de trabalho",
|
|
9112
|
+
"afilhado",
|
|
9113
|
+
"filho"
|
|
9114
|
+
],
|
|
9115
|
+
female: [
|
|
9116
|
+
"irm\xE3",
|
|
9117
|
+
"prima",
|
|
9118
|
+
"ex-aluna",
|
|
9119
|
+
"vizinha",
|
|
9120
|
+
"colega de trabalho",
|
|
9121
|
+
"afilhada",
|
|
9122
|
+
"filha"
|
|
9123
|
+
]
|
|
9124
|
+
}
|
|
9125
|
+
}
|
|
9126
|
+
};
|
|
9127
|
+
var ATTACK_CLIPS = attack_clips_default.clips;
|
|
9128
|
+
var ATTACK_CLIP_LOCALES = attack_clips_default.locales;
|
|
9129
|
+
var FIRST_PERSON_CLIPS = first_person_clips_default.clips;
|
|
9130
|
+
var NAME_POOLS = { "pt-BR": names_default["pt-BR"] };
|
|
9131
|
+
|
|
9132
|
+
// src/presets/first-person-arms.json
|
|
9133
|
+
var first_person_arms_default = {
|
|
9134
|
+
source: {
|
|
9135
|
+
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)."
|
|
9136
|
+
},
|
|
9137
|
+
rig: {
|
|
9138
|
+
shoulder: [0.16, -0.36, 0.18],
|
|
9139
|
+
upper: 0.32,
|
|
9140
|
+
fore: 0.3,
|
|
9141
|
+
reachShift: 0.35,
|
|
9142
|
+
wrist: 0.07,
|
|
9143
|
+
elbowPole: [0.45, -1, 0.2],
|
|
9144
|
+
shoulderLeft: [-0.07, -0.46, 0.18],
|
|
9145
|
+
elbowPoleLeft: [-0.1, -1, 0.2],
|
|
9146
|
+
radius: { shoulder: 0.04, elbow: 0.034, wrist: 0.028, cuff: 0.031 },
|
|
9147
|
+
hip: [0.12, -0.82, 0.04],
|
|
9148
|
+
thigh: 0.46,
|
|
9149
|
+
shin: 0.46,
|
|
9150
|
+
kneePole: [0.1, 0.5, -1],
|
|
9151
|
+
foot: { p: [0.13, -0.85, -0.05], r: [0, 1.5708, 0] }
|
|
9152
|
+
},
|
|
9153
|
+
stances: {
|
|
9154
|
+
relaxed: {
|
|
9155
|
+
R: { p: [0.2, -0.2, -0.36], r: [0.2, 1.45, -0.4], grip: 0.35 },
|
|
9156
|
+
L: "mirror"
|
|
9157
|
+
},
|
|
9158
|
+
fists: {
|
|
9159
|
+
R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1 },
|
|
9160
|
+
L: "mirror"
|
|
9161
|
+
},
|
|
9162
|
+
hold: { follow: true, fallback: "relaxed" }
|
|
9163
|
+
},
|
|
9164
|
+
bob: { frequency: 2.3, x: 6e-3, y: 8e-3, run: 1.5, fullSpeed: 4, crouch: 0.02 }
|
|
9165
|
+
};
|
|
9166
|
+
|
|
9167
|
+
// src/first-person/hand.ts
|
|
9168
|
+
var FINGERS = [
|
|
9169
|
+
{ z: -0.03, lengths: [0.034, 0.024, 0.02], width: 0.016 },
|
|
9170
|
+
{ z: -0.0105, lengths: [0.038, 0.027, 0.022], width: 0.0165 },
|
|
9171
|
+
{ z: 9e-3, lengths: [0.035, 0.025, 0.02], width: 0.0155 },
|
|
9172
|
+
{ z: 0.027, lengths: [0.029, 0.02, 0.018], width: 0.0135 }
|
|
9173
|
+
];
|
|
9174
|
+
var PALM = { length: 0.09, thick: 0.026, width: 0.09, wristWidth: 0.066 };
|
|
9175
|
+
var FINGER_THICK = 0.0135;
|
|
9176
|
+
var CURL = { near: 1.05, mid: 1.25, far: 0.85, spread: 0.06 };
|
|
9177
|
+
var THUMB = { base: [5e-3, -6e-3, -0.044], near: 0.034, far: 0.028, width: 0.019 };
|
|
9178
|
+
var createHandMesh = (THREE, skin, wristSkin = skin) => {
|
|
9179
|
+
const root = new THREE.Group();
|
|
9180
|
+
root.name = "first-person-hand";
|
|
9181
|
+
const geometries = [];
|
|
9182
|
+
const box2 = (parent, sx, sy, sz, x, y, z, material = skin) => {
|
|
9183
|
+
const geometry = new THREE.BoxGeometry(sx, sy, sz);
|
|
9184
|
+
geometries.push(geometry);
|
|
9185
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9186
|
+
mesh2.position.set(x, y, z);
|
|
9187
|
+
mesh2.frustumCulled = false;
|
|
9188
|
+
parent.add(mesh2);
|
|
9189
|
+
return mesh2;
|
|
9190
|
+
};
|
|
9191
|
+
const palmGeometry = new THREE.BoxGeometry(PALM.length, PALM.thick, PALM.width);
|
|
9192
|
+
const palmPosition = palmGeometry.getAttribute("position");
|
|
9193
|
+
for (let i = 0; i < palmPosition.count; i++) {
|
|
9194
|
+
if (palmPosition.getX(i) < 0) palmPosition.setZ(i, palmPosition.getZ(i) * PALM.wristWidth / PALM.width);
|
|
9195
|
+
}
|
|
9196
|
+
palmGeometry.computeVertexNormals();
|
|
9197
|
+
geometries.push(palmGeometry);
|
|
9198
|
+
const palm = new THREE.Mesh(palmGeometry, skin);
|
|
9199
|
+
palm.frustumCulled = false;
|
|
9200
|
+
root.add(palm);
|
|
9201
|
+
const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 8);
|
|
9202
|
+
wristGeometry.rotateZ(Math.PI / 2);
|
|
9203
|
+
geometries.push(wristGeometry);
|
|
9204
|
+
const wrist = new THREE.Mesh(wristGeometry, wristSkin);
|
|
9205
|
+
wrist.position.set(-PALM.length / 2 - 0.018, 0, 0);
|
|
9206
|
+
wrist.frustumCulled = false;
|
|
9207
|
+
root.add(wrist);
|
|
9208
|
+
const joints = [];
|
|
9209
|
+
FINGERS.forEach((finger, index) => {
|
|
9210
|
+
const segments = [];
|
|
9211
|
+
let parent = root;
|
|
9212
|
+
let at = PALM.length / 2;
|
|
9213
|
+
finger.lengths.forEach((length, k) => {
|
|
9214
|
+
const group = new THREE.Group();
|
|
9215
|
+
group.position.set(at, k === 0 ? 2e-3 : 0, k === 0 ? finger.z : 0);
|
|
9216
|
+
const taper = 1 - k * 0.07;
|
|
9217
|
+
box2(group, length, FINGER_THICK * taper, finger.width * taper, length / 2, 0, 0);
|
|
9218
|
+
parent.add(group);
|
|
9219
|
+
segments.push(group);
|
|
9220
|
+
parent = group;
|
|
9221
|
+
at = length;
|
|
9222
|
+
});
|
|
9223
|
+
joints.push({ segments, weight: 1 + index * (CURL.spread / CURL.near) });
|
|
9224
|
+
});
|
|
9225
|
+
const thumbNear = new THREE.Group();
|
|
9226
|
+
thumbNear.rotation.order = "YZX";
|
|
9227
|
+
thumbNear.position.set(...THUMB.base);
|
|
9228
|
+
box2(thumbNear, THUMB.near, FINGER_THICK * 1.1, THUMB.width, THUMB.near / 2, 0, 0);
|
|
9229
|
+
const thumbFar = new THREE.Group();
|
|
9230
|
+
thumbFar.position.set(THUMB.near, 0, 0);
|
|
9231
|
+
box2(thumbFar, THUMB.far, FINGER_THICK, THUMB.width * 0.9, THUMB.far / 2, 0, 0);
|
|
9232
|
+
thumbNear.add(thumbFar);
|
|
9233
|
+
root.add(thumbNear);
|
|
9234
|
+
let lastGrip = 0;
|
|
9235
|
+
let indexCurl;
|
|
9236
|
+
const curl = [CURL.near, CURL.mid, CURL.far];
|
|
9237
|
+
const applyIndex = () => {
|
|
9238
|
+
const finger = joints[0];
|
|
9239
|
+
if (!finger) return;
|
|
9240
|
+
const k = indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl));
|
|
9241
|
+
finger.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * finger.weight);
|
|
9242
|
+
};
|
|
9243
|
+
const setGrip = (grip) => {
|
|
9244
|
+
const k = Math.min(1, Math.max(0, grip));
|
|
9245
|
+
lastGrip = k;
|
|
9246
|
+
for (const joint of joints) joint.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * joint.weight);
|
|
9247
|
+
thumbNear.rotation.set(0, 0.55 - 1.45 * k, -0.1 - 0.75 * k);
|
|
9248
|
+
thumbFar.rotation.z = -0.1 - 0.7 * k;
|
|
9249
|
+
applyIndex();
|
|
9250
|
+
};
|
|
9251
|
+
const setIndex = (value) => {
|
|
9252
|
+
indexCurl = value;
|
|
9253
|
+
applyIndex();
|
|
9254
|
+
};
|
|
9255
|
+
setGrip(0);
|
|
9256
|
+
return {
|
|
9257
|
+
root,
|
|
9258
|
+
setGrip,
|
|
9259
|
+
setIndex,
|
|
9260
|
+
dispose() {
|
|
9261
|
+
for (const geometry of geometries) geometry.dispose();
|
|
9262
|
+
}
|
|
9263
|
+
};
|
|
9264
|
+
};
|
|
9265
|
+
|
|
9266
|
+
// src/first-person/ik.ts
|
|
9267
|
+
var solveTwoBone = (root, target, upper, lower, pole, out) => {
|
|
9268
|
+
const line = out.line.copy(target).sub(root);
|
|
9269
|
+
let distance3 = line.length();
|
|
9270
|
+
if (distance3 < 1e-6) line.set(0, 0, -1);
|
|
9271
|
+
else line.divideScalar(distance3);
|
|
9272
|
+
const min = Math.abs(upper - lower) + 1e-4;
|
|
9273
|
+
const max = upper + lower - 1e-4;
|
|
9274
|
+
out.clamped = distance3 > max || distance3 < min;
|
|
9275
|
+
distance3 = Math.min(max, Math.max(min, distance3));
|
|
9276
|
+
const along = (distance3 * distance3 + upper * upper - lower * lower) / (2 * distance3);
|
|
9277
|
+
const across = Math.sqrt(Math.max(0, upper * upper - along * along));
|
|
9278
|
+
const side = out.side.copy(pole);
|
|
9279
|
+
side.addScaledVector(line, -side.dot(line));
|
|
9280
|
+
if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
|
|
9281
|
+
if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
|
|
9282
|
+
side.normalize();
|
|
9283
|
+
out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
|
|
9284
|
+
out.end.copy(root).addScaledVector(line, distance3);
|
|
9285
|
+
return out;
|
|
9286
|
+
};
|
|
9287
|
+
var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
|
|
9288
|
+
|
|
9289
|
+
// src/first-person/arms.ts
|
|
9290
|
+
var DEFAULT_BLEND = 0.18;
|
|
9291
|
+
var ELBOW_SEGMENTS = 8;
|
|
9292
|
+
var REACH_SLACK = 0.97;
|
|
9293
|
+
var CONTACT_PROBE = 1.4;
|
|
9294
|
+
var CONTACT_MIN = 0.12;
|
|
9295
|
+
var CONTACT_MARGIN = 5e-3;
|
|
9296
|
+
var CONTACT_PALM = 0.03;
|
|
9297
|
+
var FOOT_LENGTH = 0.24;
|
|
9298
|
+
var FOOT_SHOWN = 1e-3;
|
|
9299
|
+
var FILL = 0.3;
|
|
9300
|
+
var FIRST_PERSON_STANCES = first_person_arms_default.stances;
|
|
9301
|
+
var FIRST_PERSON_RIG = first_person_arms_default.rig;
|
|
9302
|
+
var FIRST_PERSON_BOB = first_person_arms_default.bob;
|
|
9303
|
+
var hexOf2 = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
|
|
9304
|
+
var scaleHex = (hex2, factor) => {
|
|
9305
|
+
const channel = (shift) => Math.round((hex2 >> shift & 255) * factor);
|
|
9306
|
+
return channel(16) << 16 | channel(8) << 8 | channel(0);
|
|
9307
|
+
};
|
|
9308
|
+
var mix2 = (a, b, k) => a + (b - a) * k;
|
|
9309
|
+
var ease2 = (k) => k * k * (3 - 2 * k);
|
|
9310
|
+
var FIRST_PERSON_COLORS = {
|
|
9311
|
+
skin: hexOf2(player_avatar_default.spec.skin),
|
|
9312
|
+
sleeve: hexOf2(player_avatar_default.shirtColor),
|
|
9313
|
+
cuff: scaleHex(hexOf2(player_avatar_default.shirtColor), 0.72),
|
|
9314
|
+
trouser: 2962496,
|
|
9315
|
+
shoe: 1841690
|
|
9316
|
+
};
|
|
9317
|
+
var newPose = (THREE) => ({ p: new THREE.Vector3(), q: new THREE.Quaternion(), grip: 0, scale: 1 });
|
|
9318
|
+
var copyPose = (to, from) => {
|
|
9319
|
+
to.p.copy(from.p);
|
|
9320
|
+
to.q.copy(from.q);
|
|
9321
|
+
to.grip = from.grip;
|
|
9322
|
+
to.scale = from.scale;
|
|
9323
|
+
return to;
|
|
9324
|
+
};
|
|
9325
|
+
var mixPose = (THREE, out, a, b, k) => {
|
|
9326
|
+
out.p.lerpVectors(a.p, b.p, k);
|
|
9327
|
+
out.q.copy(a.q).slerp(b.q, k);
|
|
9328
|
+
out.grip = mix2(a.grip, b.grip, k);
|
|
9329
|
+
out.scale = mix2(a.scale, b.scale, k);
|
|
9330
|
+
return out;
|
|
9331
|
+
};
|
|
9332
|
+
var mirrorRotation = (THREE, q) => {
|
|
9333
|
+
const m = new THREE.Matrix4().makeRotationFromQuaternion(q);
|
|
9334
|
+
const e = m.elements;
|
|
9335
|
+
for (const [i, j] of [[0, 1], [0, 2], [1, 0], [2, 0]]) e[j * 4 + i] = -e[j * 4 + i];
|
|
9336
|
+
return new THREE.Quaternion().setFromRotationMatrix(m);
|
|
9337
|
+
};
|
|
9338
|
+
var mirrorQuaternion = (THREE, q) => {
|
|
9339
|
+
const m = new THREE.Matrix4().makeRotationFromQuaternion(q);
|
|
9340
|
+
const e = m.elements;
|
|
9341
|
+
for (let column = 0; column < 3; column++) e[column * 4] = -e[column * 4];
|
|
9342
|
+
for (let row = 0; row < 3; row++) e[8 + row] = -e[8 + row];
|
|
9343
|
+
return new THREE.Quaternion().setFromRotationMatrix(m);
|
|
9344
|
+
};
|
|
9345
|
+
function createFirstPersonArms(THREE, options = {}) {
|
|
9346
|
+
const colors = { ...FIRST_PERSON_COLORS, ...options.colors };
|
|
9347
|
+
const rig = {
|
|
9348
|
+
...FIRST_PERSON_RIG,
|
|
9349
|
+
...options.rig,
|
|
9350
|
+
radius: { ...FIRST_PERSON_RIG.radius, ...options.rig?.radius },
|
|
9351
|
+
foot: options.rig?.foot ?? FIRST_PERSON_RIG.foot
|
|
9352
|
+
};
|
|
9353
|
+
const stances = { ...FIRST_PERSON_STANCES, ...options.stances };
|
|
9354
|
+
const bob = { ...FIRST_PERSON_BOB, ...options.bob };
|
|
9355
|
+
const blendSeconds = options.blend ?? DEFAULT_BLEND;
|
|
9356
|
+
const clips = new Map((options.clips ?? []).map((clip) => [clip.id, clip]));
|
|
9357
|
+
const root = new THREE.Group();
|
|
9358
|
+
root.name = "first-person-arms";
|
|
9359
|
+
root.userData.viewmodel = true;
|
|
9360
|
+
const lit = (color2) => new THREE.MeshLambertMaterial({ color: color2, emissive: scaleHex(color2, FILL) });
|
|
9361
|
+
const materials = {
|
|
9362
|
+
skin: lit(colors.skin),
|
|
9363
|
+
sleeve: lit(colors.sleeve),
|
|
9364
|
+
cuff: lit(colors.cuff),
|
|
9365
|
+
trouser: lit(colors.trouser),
|
|
9366
|
+
shoe: lit(colors.shoe),
|
|
9367
|
+
sole: lit(scaleHex(colors.shoe, 0.55))
|
|
9368
|
+
};
|
|
9369
|
+
const geometries = [];
|
|
9370
|
+
const Y = new THREE.Vector3(0, 1, 0);
|
|
9371
|
+
const limb2 = (parent, wide, narrow, length, material) => {
|
|
9372
|
+
const geometry = new THREE.CylinderGeometry(narrow, wide, length, 8, 1);
|
|
9373
|
+
geometry.translate(0, length / 2, 0);
|
|
9374
|
+
geometries.push(geometry);
|
|
9375
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9376
|
+
mesh2.frustumCulled = false;
|
|
9377
|
+
mesh2.userData.length = length;
|
|
9378
|
+
parent.add(mesh2);
|
|
9379
|
+
return mesh2;
|
|
9380
|
+
};
|
|
9381
|
+
const ball = (parent, radius, material) => {
|
|
9382
|
+
const geometry = new THREE.SphereGeometry(radius, ELBOW_SEGMENTS, 6);
|
|
9383
|
+
geometries.push(geometry);
|
|
9384
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9385
|
+
mesh2.frustumCulled = false;
|
|
9386
|
+
parent.add(mesh2);
|
|
9387
|
+
return mesh2;
|
|
9388
|
+
};
|
|
9389
|
+
const place = (segment, from, to) => {
|
|
9390
|
+
const direction = new THREE.Vector3().subVectors(to, from);
|
|
9391
|
+
const length = direction.length();
|
|
9392
|
+
segment.position.copy(from);
|
|
9393
|
+
if (length > 1e-6) segment.quaternion.setFromUnitVectors(Y, direction.divideScalar(length));
|
|
9394
|
+
segment.scale.y = length / segment.userData.length;
|
|
9395
|
+
};
|
|
9396
|
+
const arms = {};
|
|
9397
|
+
for (const side of ["R", "L"]) {
|
|
9398
|
+
const sign2 = side === "R" ? 1 : -1;
|
|
9399
|
+
const hand = createHandMesh(THREE, materials.skin);
|
|
9400
|
+
const anchor = new THREE.Group();
|
|
9401
|
+
anchor.name = `first-person-hand-${side}`;
|
|
9402
|
+
const mirror = new THREE.Group();
|
|
9403
|
+
mirror.scale.z = sign2;
|
|
9404
|
+
mirror.add(hand.root);
|
|
9405
|
+
anchor.add(mirror);
|
|
9406
|
+
root.add(anchor);
|
|
9407
|
+
const upper = limb2(root, rig.radius.shoulder, rig.radius.elbow, rig.upper, materials.sleeve);
|
|
9408
|
+
const fore = limb2(root, rig.radius.elbow, rig.radius.wrist, rig.fore, materials.sleeve);
|
|
9409
|
+
const cuff = limb2(root, rig.radius.cuff, rig.radius.cuff, 0.03, materials.cuff);
|
|
9410
|
+
const elbow = ball(root, rig.radius.elbow * 1.02, materials.sleeve);
|
|
9411
|
+
elbow.name = `first-person-elbow-${side}`;
|
|
9412
|
+
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() } };
|
|
9413
|
+
}
|
|
9414
|
+
const leg = new THREE.Group();
|
|
9415
|
+
leg.name = "first-person-leg";
|
|
9416
|
+
leg.visible = false;
|
|
9417
|
+
root.add(leg);
|
|
9418
|
+
const thigh = limb2(leg, 0.088, 0.068, rig.thigh, materials.trouser);
|
|
9419
|
+
const shin = limb2(leg, 0.068, 0.05, rig.shin, materials.trouser);
|
|
9420
|
+
const knee = ball(leg, 0.07, materials.trouser);
|
|
9421
|
+
const shoe = new THREE.Group();
|
|
9422
|
+
const shoeBox = (sx, sy, sz, x, y, material) => {
|
|
9423
|
+
const geometry = new THREE.BoxGeometry(sx, sy, sz);
|
|
9424
|
+
geometries.push(geometry);
|
|
9425
|
+
const mesh2 = new THREE.Mesh(geometry, material);
|
|
9426
|
+
mesh2.position.set(x, y, 0);
|
|
9427
|
+
mesh2.frustumCulled = false;
|
|
9428
|
+
shoe.add(mesh2);
|
|
9429
|
+
};
|
|
9430
|
+
shoeBox(0.27, 0.07, 0.1, 0.065, -0.05, materials.shoe);
|
|
9431
|
+
shoeBox(0.27, 0.025, 0.104, 0.065, -0.095, materials.sole);
|
|
9432
|
+
shoeBox(0.1, 0.05, 0.088, 0.17, -5e-3, materials.shoe);
|
|
9433
|
+
leg.add(shoe);
|
|
9434
|
+
const shoulderOf = (sign2, out) => sign2 === -1 && rig.shoulderLeft ? out.set(...rig.shoulderLeft) : out.set(rig.shoulder[0] * sign2, rig.shoulder[1], rig.shoulder[2]);
|
|
9435
|
+
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]);
|
|
9436
|
+
const kneePole = new THREE.Vector3(...rig.kneePole);
|
|
9437
|
+
const hip = new THREE.Vector3(...rig.hip);
|
|
9438
|
+
const euler = new THREE.Euler(0, 0, 0, "YZX");
|
|
9439
|
+
const quaternionOf = (r) => new THREE.Quaternion().setFromEuler(euler.set(r[0], r[1], r[2], "YZX"));
|
|
9440
|
+
const restPose = (rest, mirror, out) => {
|
|
9441
|
+
out.p.set(...rest.p);
|
|
9442
|
+
out.q.copy(quaternionOf(rest.r));
|
|
9443
|
+
if (mirror) {
|
|
9444
|
+
out.p.x = -out.p.x;
|
|
9445
|
+
out.q.copy(mirrorQuaternion(THREE, out.q));
|
|
9446
|
+
}
|
|
9447
|
+
out.grip = rest.grip ?? 0;
|
|
9448
|
+
out.scale = 1;
|
|
9449
|
+
return out;
|
|
9450
|
+
};
|
|
9451
|
+
const DEFAULT_REST = { p: [0.17, -0.3, -0.3], r: [0, 1.5, 0], grip: 0.3 };
|
|
9452
|
+
const stanceRest = (id, side, out) => {
|
|
9453
|
+
const stance = stances[id] ?? stances.relaxed;
|
|
9454
|
+
const right = stance?.R ?? DEFAULT_REST;
|
|
9455
|
+
const left = stance?.L;
|
|
9456
|
+
if (side === "R") return restPose(right, false, out);
|
|
9457
|
+
return left && left !== "mirror" ? restPose(left, false, out) : restPose(right, true, out);
|
|
9458
|
+
};
|
|
9459
|
+
const camPosition = new THREE.Vector3();
|
|
9460
|
+
const camQuaternion = new THREE.Quaternion();
|
|
9461
|
+
const camInverse = new THREE.Quaternion();
|
|
9462
|
+
const clamped = newPose(THREE);
|
|
9463
|
+
const probeAt = new THREE.Vector3();
|
|
9464
|
+
const probeForward = new THREE.Vector3();
|
|
9465
|
+
const probeFingers = new THREE.Vector3();
|
|
9466
|
+
let contact;
|
|
9467
|
+
const keepOutside = (pose, reach) => {
|
|
9468
|
+
if (!contact) return pose;
|
|
9469
|
+
probeAt.set(pose.p.x, pose.p.y, 0).applyQuaternion(camQuaternion).add(camPosition);
|
|
9470
|
+
probeForward.set(0, 0, -1).applyQuaternion(camQuaternion);
|
|
9471
|
+
const distance3 = contact([probeAt.x, probeAt.y, probeAt.z], [probeForward.x, probeForward.y, probeForward.z], CONTACT_PROBE);
|
|
9472
|
+
if (distance3 === null) return pose;
|
|
9473
|
+
const limit = Math.max(CONTACT_MIN, distance3 - reach - CONTACT_MARGIN);
|
|
9474
|
+
if (-pose.p.z <= limit) return pose;
|
|
9475
|
+
copyPose(clamped, pose);
|
|
9476
|
+
clamped.p.z = -limit;
|
|
9477
|
+
return clamped;
|
|
9478
|
+
};
|
|
9479
|
+
const forward = (pose, length) => Math.max(CONTACT_PALM, -probeFingers.set(1, 0, 0).applyQuaternion(pose.q).z * length * pose.scale);
|
|
9480
|
+
const handReach = (pose) => forward(pose, 0.085);
|
|
9481
|
+
const camScale = new THREE.Vector3();
|
|
9482
|
+
const toCamera = (target, grip, scale, out) => {
|
|
9483
|
+
out.p.set(...target.position).sub(camPosition).applyQuaternion(camInverse);
|
|
9484
|
+
out.q.set(...target.quaternion).premultiply(camInverse);
|
|
9485
|
+
out.grip = grip;
|
|
9486
|
+
out.scale = scale;
|
|
9487
|
+
return out;
|
|
9488
|
+
};
|
|
9489
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9490
|
+
let run = null;
|
|
9491
|
+
const emit = (event) => {
|
|
9492
|
+
for (const listener of listeners) listener(event);
|
|
9493
|
+
};
|
|
9494
|
+
const fire = (clip, from, to) => {
|
|
9495
|
+
for (const event of clipEventsBetween(clip, from, to)) emit({ ...event, clip: clip.id });
|
|
9496
|
+
};
|
|
9497
|
+
let stanceId = "relaxed";
|
|
9498
|
+
let phase = 0;
|
|
9499
|
+
let swing = 0;
|
|
9500
|
+
let sink = 0;
|
|
9501
|
+
let first = true;
|
|
9502
|
+
const bobOffset = new THREE.Vector3();
|
|
9503
|
+
const desired = newPose(THREE);
|
|
9504
|
+
const channelPose = newPose(THREE);
|
|
9505
|
+
const mixed = newPose(THREE);
|
|
9506
|
+
let indexCurl;
|
|
9507
|
+
const bone = createTwoBone(THREE);
|
|
9508
|
+
const shoulder = new THREE.Vector3();
|
|
9509
|
+
const wristAt = new THREE.Vector3();
|
|
9510
|
+
const palmAt = new THREE.Vector3();
|
|
9511
|
+
const fingers = new THREE.Vector3();
|
|
9512
|
+
const towards = new THREE.Vector3();
|
|
9513
|
+
const fromChannel = (channel, side, base, out) => {
|
|
9514
|
+
out.p.set(...channel.p);
|
|
9515
|
+
out.q.copy(quaternionOf(channel.r));
|
|
9516
|
+
if (channel.mirror && side === "L") {
|
|
9517
|
+
out.p.x = -out.p.x;
|
|
9518
|
+
out.q.copy(mirrorQuaternion(THREE, out.q));
|
|
9519
|
+
}
|
|
9520
|
+
out.grip = channel.grip ?? base.grip;
|
|
9521
|
+
out.scale = base.scale;
|
|
9522
|
+
return out;
|
|
9523
|
+
};
|
|
9524
|
+
const drawArm = (arm, pose) => {
|
|
9525
|
+
copyPose(arm.drawn, pose);
|
|
9526
|
+
const scale = pose.scale;
|
|
9527
|
+
fingers.set(1, 0, 0).applyQuaternion(pose.q);
|
|
9528
|
+
wristAt.copy(pose.p).addScaledVector(fingers, -rig.wrist * scale);
|
|
9529
|
+
shoulderOf(arm.sign, shoulder).add(bobOffset);
|
|
9530
|
+
const excess = shoulder.distanceTo(wristAt) - (rig.upper + rig.fore) * REACH_SLACK;
|
|
9531
|
+
if (excess > 0 && rig.reachShift > 0) shoulder.addScaledVector(towards.subVectors(wristAt, shoulder).normalize(), Math.min(excess, rig.reachShift));
|
|
9532
|
+
solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, poleOf(arm.sign), bone);
|
|
9533
|
+
palmAt.copy(bone.end).addScaledVector(fingers, rig.wrist * scale);
|
|
9534
|
+
arm.drawn.p.copy(palmAt);
|
|
9535
|
+
arm.anchor.position.copy(palmAt);
|
|
9536
|
+
arm.anchor.quaternion.copy(pose.q);
|
|
9537
|
+
arm.anchor.scale.setScalar(scale);
|
|
9538
|
+
arm.hand.setGrip(pose.grip);
|
|
9539
|
+
arm.hand.setIndex(arm.side === "R" ? indexCurl : void 0);
|
|
9540
|
+
arm.joints.shoulder.copy(shoulder);
|
|
9541
|
+
arm.joints.elbow.copy(bone.joint);
|
|
9542
|
+
arm.joints.wrist.copy(bone.end);
|
|
9543
|
+
place(arm.upper, shoulder, bone.joint);
|
|
9544
|
+
place(arm.fore, bone.joint, bone.end);
|
|
9545
|
+
arm.elbow.position.copy(bone.joint);
|
|
9546
|
+
const toWrist = new THREE.Vector3().subVectors(bone.end, bone.joint).normalize();
|
|
9547
|
+
place(arm.cuff, new THREE.Vector3().copy(bone.end).addScaledVector(toWrist, -0.02), new THREE.Vector3().copy(bone.end).addScaledVector(toWrist, 0.012));
|
|
9548
|
+
};
|
|
9549
|
+
const footPose = newPose(THREE);
|
|
9550
|
+
const footRest = newPose(THREE);
|
|
9551
|
+
const ankle = new THREE.Vector3();
|
|
9552
|
+
const hipMirrored = new THREE.Vector3(-hip.x, hip.y, hip.z);
|
|
9553
|
+
const kneePoleMirrored = new THREE.Vector3(-kneePole.x, kneePole.y, kneePole.z);
|
|
9554
|
+
const drawLeg = (pose, left) => {
|
|
9555
|
+
const origin = left ? hipMirrored : hip;
|
|
9556
|
+
solveTwoBone(origin, pose.p, rig.thigh, rig.shin, left ? kneePoleMirrored : kneePole, bone);
|
|
9557
|
+
ankle.copy(bone.end);
|
|
9558
|
+
place(thigh, origin, bone.joint);
|
|
9559
|
+
place(shin, bone.joint, ankle);
|
|
9560
|
+
knee.position.copy(bone.joint);
|
|
9561
|
+
shoe.position.copy(ankle);
|
|
9562
|
+
shoe.quaternion.copy(pose.q);
|
|
9563
|
+
};
|
|
9564
|
+
const modeOf = (key, arm) => {
|
|
9565
|
+
if (arm.mode === key) return;
|
|
9566
|
+
copyPose(arm.from, arm.shown);
|
|
9567
|
+
arm.mode = key;
|
|
9568
|
+
arm.blend = first ? 1 : 0;
|
|
9569
|
+
};
|
|
9570
|
+
return {
|
|
9571
|
+
root,
|
|
9572
|
+
get playing() {
|
|
9573
|
+
return run ? run.clip.id : null;
|
|
9574
|
+
},
|
|
9575
|
+
get stance() {
|
|
9576
|
+
return stanceId;
|
|
9577
|
+
},
|
|
9578
|
+
play(id, options2) {
|
|
9579
|
+
const clip = clips.get(id);
|
|
9580
|
+
if (!clip) return false;
|
|
9581
|
+
run = { clip, time: 0, left: options2?.leg === "left" };
|
|
9582
|
+
fire(clip, -1e-9, 0);
|
|
9583
|
+
return true;
|
|
9584
|
+
},
|
|
9585
|
+
stop() {
|
|
9586
|
+
run = null;
|
|
9587
|
+
},
|
|
9588
|
+
update(dt, state) {
|
|
9589
|
+
const { camera } = state;
|
|
9590
|
+
contact = state.contact;
|
|
9591
|
+
camera.updateWorldMatrix(true, false);
|
|
9592
|
+
camera.matrixWorld.decompose(camPosition, camQuaternion, camScale);
|
|
9593
|
+
camInverse.copy(camQuaternion).invert();
|
|
9594
|
+
root.position.copy(camPosition);
|
|
9595
|
+
root.quaternion.copy(camQuaternion);
|
|
9596
|
+
root.updateMatrixWorld(true);
|
|
9597
|
+
const speed = state.speed ?? 0;
|
|
9598
|
+
const target = Math.min(1, speed / bob.fullSpeed) * (state.running ? bob.run : 1);
|
|
9599
|
+
swing += (target - swing) * Math.min(1, dt * 8);
|
|
9600
|
+
phase += dt * speed * bob.frequency;
|
|
9601
|
+
sink += ((state.crouch ? 1 : 0) - sink) * Math.min(1, dt * 8);
|
|
9602
|
+
bobOffset.set(Math.sin(phase) * bob.x * swing, Math.sin(phase * 2) * bob.y * swing - bob.crouch * sink, 0);
|
|
9603
|
+
stanceId = state.stance ?? "relaxed";
|
|
9604
|
+
const stance = stances[stanceId] ?? stances.relaxed;
|
|
9605
|
+
const targets = state.targets ?? null;
|
|
9606
|
+
if (run) {
|
|
9607
|
+
const from = run.time;
|
|
9608
|
+
run.time += dt;
|
|
9609
|
+
fire(run.clip, from, Math.min(run.time, run.clip.duration));
|
|
9610
|
+
}
|
|
9611
|
+
const sample = run ? sampleFirstPersonClip(run.clip, Math.min(run.time, run.clip.duration)) : null;
|
|
9612
|
+
for (const side of ["R", "L"]) {
|
|
9613
|
+
const arm = arms[side];
|
|
9614
|
+
const target2 = targets?.[side];
|
|
9615
|
+
let mode;
|
|
9616
|
+
if (stance?.follow && target2) {
|
|
9617
|
+
mode = "target";
|
|
9618
|
+
toCamera(target2, (side === "L" ? targets?.gripL : void 0) ?? targets?.grip ?? 1, targets?.scale ?? 1, desired);
|
|
9619
|
+
} else {
|
|
9620
|
+
mode = `rest:${stance?.follow ? stance.fallback ?? "relaxed" : stanceId}`;
|
|
9621
|
+
stanceRest(stance?.follow ? stance.fallback ?? "relaxed" : stanceId, side, desired);
|
|
9622
|
+
desired.p.add(bobOffset);
|
|
9623
|
+
}
|
|
9624
|
+
modeOf(mode, arm);
|
|
9625
|
+
if (side === "R") indexCurl = mode === "target" ? targets?.fingers?.index : void 0;
|
|
9626
|
+
arm.blend = blendSeconds <= 0 ? 1 : Math.min(1, arm.blend + dt / blendSeconds);
|
|
9627
|
+
if (arm.blend >= 1) copyPose(arm.shown, desired);
|
|
9628
|
+
else mixPose(THREE, arm.shown, arm.from, desired, ease2(arm.blend));
|
|
9629
|
+
const channel = sample?.[side];
|
|
9630
|
+
if (channel && channel.w > 0) {
|
|
9631
|
+
fromChannel(channel, side, arm.shown, channelPose);
|
|
9632
|
+
mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
|
|
9633
|
+
drawArm(arm, mode === "target" ? mixed : keepOutside(mixed, handReach(mixed)));
|
|
9634
|
+
} else drawArm(arm, mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown)));
|
|
9635
|
+
}
|
|
9636
|
+
const foot = sample?.F;
|
|
9637
|
+
leg.visible = !!foot && foot.w > FOOT_SHOWN;
|
|
9638
|
+
if (foot && leg.visible) {
|
|
9639
|
+
restPose(rig.foot, false, footRest);
|
|
9640
|
+
fromChannel(foot, "R", footRest, footPose);
|
|
9641
|
+
mixPose(THREE, mixed, footRest, footPose, foot.w);
|
|
9642
|
+
copyPose(footPose, mixed);
|
|
9643
|
+
if (run?.left) {
|
|
9644
|
+
footPose.p.x = -footPose.p.x;
|
|
9645
|
+
footPose.q.copy(mirrorRotation(THREE, footPose.q));
|
|
9646
|
+
}
|
|
9647
|
+
drawLeg(contact ? keepOutside(footPose, forward(footPose, FOOT_LENGTH)) : footPose, run?.left ?? false);
|
|
9648
|
+
}
|
|
9649
|
+
if (run && run.time >= run.clip.duration) run = null;
|
|
9650
|
+
first = false;
|
|
9651
|
+
},
|
|
9652
|
+
on(_type, listener) {
|
|
9653
|
+
listeners.add(listener);
|
|
9654
|
+
return () => void listeners.delete(listener);
|
|
9655
|
+
},
|
|
9656
|
+
joints(side) {
|
|
9657
|
+
const { shoulder: shoulder2, elbow, wrist } = arms[side].joints;
|
|
9658
|
+
return { shoulder: shoulder2.clone(), elbow: elbow.clone(), wrist: wrist.clone() };
|
|
9659
|
+
},
|
|
9660
|
+
handPose(side) {
|
|
9661
|
+
const pose = arms[side].drawn;
|
|
9662
|
+
return { position: pose.p.clone(), quaternion: pose.q.clone(), grip: pose.grip };
|
|
9663
|
+
},
|
|
9664
|
+
dispose() {
|
|
9665
|
+
run = null;
|
|
9666
|
+
listeners.clear();
|
|
9667
|
+
for (const arm of Object.values(arms)) arm.hand.dispose();
|
|
9668
|
+
for (const geometry of geometries) geometry.dispose();
|
|
9669
|
+
for (const material of Object.values(materials)) material.dispose();
|
|
9670
|
+
root.removeFromParent();
|
|
9671
|
+
}
|
|
9672
|
+
};
|
|
9673
|
+
}
|
|
9674
|
+
|
|
9675
|
+
// src/player/room-avatar.ts
|
|
9676
|
+
var impactOf = (clip) => clip.events?.find((event) => event.type === "impact")?.t ?? null;
|
|
9677
|
+
var toRoomAvatar = (avatar, options = {}) => {
|
|
9678
|
+
const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
|
|
9679
|
+
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 },
|
|
9680
|
+
clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
|
|
9681
|
+
...options.firstPerson
|
|
9682
|
+
});
|
|
9683
|
+
const strikes = new Map((options.strikeClips ?? ATTACK_CLIPS).map((clip) => [clip.id, clip]));
|
|
9684
|
+
const mirrored = /* @__PURE__ */ new Map();
|
|
9685
|
+
const leftOf = (clip) => {
|
|
9686
|
+
let found = mirrored.get(clip.id);
|
|
9687
|
+
if (!found) mirrored.set(clip.id, found = mirrorPoseClip(clip, `${clip.id}_left`));
|
|
9688
|
+
return found;
|
|
9689
|
+
};
|
|
9690
|
+
return {
|
|
9691
|
+
root: avatar.root,
|
|
9692
|
+
avatar,
|
|
9693
|
+
arms,
|
|
9694
|
+
...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
|
|
9695
|
+
update: (dt, state) => avatar.update(dt, { speed: state.speed, running: state.running, crouch: state.crouch, down: state.down, armed: state.armed, pitch: -state.pitch }),
|
|
9696
|
+
hand: (side) => avatar.hand(side),
|
|
9697
|
+
strike: (clipId, strikeOptions) => {
|
|
9698
|
+
const clip = strikes.get(clipId);
|
|
9699
|
+
if (!clip) return null;
|
|
9700
|
+
const left = strikeOptions?.leg === "left";
|
|
9701
|
+
avatar.play(left ? leftOf(clip) : clip);
|
|
9702
|
+
arms?.play(clipId, left ? { leg: "left" } : void 0);
|
|
9703
|
+
return impactOf(clip);
|
|
9704
|
+
}
|
|
9705
|
+
};
|
|
9706
|
+
};
|
|
9707
|
+
|
|
9708
|
+
// src/player/test-text.ts
|
|
9709
|
+
var AVATAR_TEXT = {
|
|
9710
|
+
en: {
|
|
9711
|
+
group: "Avatar",
|
|
9712
|
+
name: "Player body",
|
|
9713
|
+
actions: {
|
|
9714
|
+
camera_first: "Camera: 1st person",
|
|
9715
|
+
camera_third: "Camera: 3rd person",
|
|
9716
|
+
crouch: "Crouch",
|
|
9717
|
+
stand: "Stand up",
|
|
9718
|
+
fall: "Fall down",
|
|
9719
|
+
get_up: "Get up",
|
|
9720
|
+
armed_on: "Armed: on",
|
|
9721
|
+
armed_off: "Armed: off",
|
|
9722
|
+
stop_clip: "Stop clip"
|
|
9723
|
+
},
|
|
9724
|
+
play: "Play",
|
|
9725
|
+
metrics: { camera: "Camera", clip: "Clip playing", last_event: "Last clip event", state: "State", joints: "Joints" },
|
|
9726
|
+
none: "none"
|
|
9727
|
+
},
|
|
9728
|
+
"pt-BR": {
|
|
9729
|
+
group: "Avatar",
|
|
9730
|
+
name: "Corpo do jogador",
|
|
9731
|
+
actions: {
|
|
9732
|
+
camera_first: "C\xE2mera: 1\xAA pessoa",
|
|
9733
|
+
camera_third: "C\xE2mera: 3\xAA pessoa",
|
|
9734
|
+
crouch: "Agachar",
|
|
9735
|
+
stand: "Levantar",
|
|
9736
|
+
fall: "Cair",
|
|
9737
|
+
get_up: "Levantar do ch\xE3o",
|
|
9738
|
+
armed_on: "Armado: ligado",
|
|
9739
|
+
armed_off: "Armado: desligado",
|
|
9740
|
+
stop_clip: "Parar clipe"
|
|
9741
|
+
},
|
|
9742
|
+
play: "Tocar",
|
|
9743
|
+
metrics: { camera: "C\xE2mera", clip: "Clipe tocando", last_event: "\xDAltimo evento do clipe", state: "Estado", joints: "Juntas" },
|
|
9744
|
+
none: "nenhum"
|
|
9745
|
+
}
|
|
9746
|
+
};
|
|
9747
|
+
|
|
9748
|
+
// src/player/test-module.ts
|
|
9749
|
+
var AVATAR_AREA_ID = "avatar_area";
|
|
9750
|
+
var round = (value) => (Math.round(value * 100) / 100).toString();
|
|
9751
|
+
function createAvatarTestModule(options) {
|
|
9752
|
+
const clips = options.clips ?? POSE_CLIPS;
|
|
9753
|
+
const textOf = (locale) => AVATAR_TEXT[locale ?? options.locale ?? "pt-BR"] ?? AVATAR_TEXT["pt-BR"];
|
|
9754
|
+
const phaseLabel = (label, locale) => {
|
|
9755
|
+
const id = locale ?? options.locale ?? "pt-BR";
|
|
9756
|
+
const table = { ...POSE_CLIP_LOCALES[id] ?? POSE_CLIP_LOCALES["pt-BR"], ...options.clipLocales?.[id] ?? options.clipLocales?.["pt-BR"] };
|
|
9757
|
+
return typeof label === "string" ? table[label] ?? label : "";
|
|
9758
|
+
};
|
|
9759
|
+
const state = { crouch: false, down: false, armed: null };
|
|
9760
|
+
let host = null;
|
|
9761
|
+
let lastEvent = "";
|
|
9762
|
+
const log = (action, extra = {}) => {
|
|
9763
|
+
const entry = { type: "avatar", action, ...extra };
|
|
9764
|
+
host?.log(JSON.stringify(entry), entry);
|
|
9765
|
+
};
|
|
9766
|
+
const push = (action, patch, extra = {}) => {
|
|
9767
|
+
Object.assign(state, patch);
|
|
9768
|
+
options.state?.set(patch);
|
|
9769
|
+
log(action, { crouch: state.crouch, down: state.down, armed: state.armed, ...extra });
|
|
9770
|
+
};
|
|
9771
|
+
const setCamera = (mode) => {
|
|
9772
|
+
options.camera?.set(mode);
|
|
9773
|
+
log(`camera_${mode}`, { mode: options.camera?.mode() ?? null });
|
|
9774
|
+
};
|
|
9775
|
+
const onChange = (change) => {
|
|
9776
|
+
if (change.kind !== "action") return;
|
|
9777
|
+
switch (change.id) {
|
|
9778
|
+
case "camera_first":
|
|
9779
|
+
return setCamera("first");
|
|
9780
|
+
case "camera_third":
|
|
9781
|
+
return setCamera("third");
|
|
9782
|
+
case "crouch":
|
|
9783
|
+
return push("crouch", { crouch: true });
|
|
9784
|
+
case "stand":
|
|
9785
|
+
return push("stand", { crouch: false });
|
|
9786
|
+
case "fall":
|
|
9787
|
+
return push("fall", { down: true });
|
|
9788
|
+
case "get_up":
|
|
9789
|
+
return push("get_up", { down: false });
|
|
9790
|
+
case "armed_on":
|
|
9791
|
+
return push("armed_on", { armed: true });
|
|
9792
|
+
case "armed_off":
|
|
9793
|
+
return push("armed_off", { armed: false });
|
|
9794
|
+
case "stop_clip":
|
|
9795
|
+
options.avatar.stop();
|
|
9796
|
+
return log("stop_clip", { playing: options.avatar.playing });
|
|
9797
|
+
default: {
|
|
9798
|
+
const clip = clips.find((item) => `play_${item.id}` === change.id);
|
|
9799
|
+
if (!clip) return;
|
|
9800
|
+
options.avatar.play(clip);
|
|
9801
|
+
log("play_clip", { clip: clip.id, duration: clip.duration, loop: clip.loop ?? false, channels: clip.channels ?? "full" });
|
|
9802
|
+
}
|
|
9803
|
+
}
|
|
9804
|
+
};
|
|
9805
|
+
const joints = () => {
|
|
9806
|
+
const rig = options.avatar.rig;
|
|
9807
|
+
const [armA, armB] = rig.arms;
|
|
9808
|
+
const [legA, legB] = rig.legs;
|
|
9809
|
+
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)}`;
|
|
9810
|
+
};
|
|
9811
|
+
return {
|
|
9812
|
+
id: AVATAR_AREA_ID,
|
|
9813
|
+
name: textOf().name,
|
|
9814
|
+
group: textOf().group,
|
|
9815
|
+
state,
|
|
9816
|
+
items: () => [],
|
|
9817
|
+
area: () => ({ placements: [], view: { eye: [0, 1.7, 6], target: [0, 1.2, 0] } }),
|
|
9818
|
+
controls: (locale) => {
|
|
9819
|
+
const text = textOf(locale);
|
|
9820
|
+
const action = (id, name, hint) => ({ kind: "action", id, name, ...hint ? { hint } : {} });
|
|
9821
|
+
return [
|
|
9822
|
+
action("camera_first", text.actions.camera_first),
|
|
9823
|
+
action("camera_third", text.actions.camera_third),
|
|
9824
|
+
action("crouch", text.actions.crouch),
|
|
9825
|
+
action("stand", text.actions.stand),
|
|
9826
|
+
action("fall", text.actions.fall),
|
|
9827
|
+
action("get_up", text.actions.get_up),
|
|
9828
|
+
action("armed_on", text.actions.armed_on),
|
|
9829
|
+
action("armed_off", text.actions.armed_off),
|
|
9830
|
+
...clips.map((clip) => action(`play_${clip.id}`, `${text.play}: ${clip.id}`)),
|
|
9831
|
+
action("stop_clip", text.actions.stop_clip)
|
|
9832
|
+
];
|
|
9833
|
+
},
|
|
9834
|
+
metrics: (locale) => {
|
|
9835
|
+
const text = textOf(locale);
|
|
9836
|
+
return [
|
|
9837
|
+
{ id: "camera", name: text.metrics.camera, read: () => options.camera?.mode() ?? text.none },
|
|
9838
|
+
{ id: "clip", name: text.metrics.clip, read: () => options.avatar.playing ?? text.none },
|
|
9839
|
+
{ id: "last_event", name: text.metrics.last_event, read: () => lastEvent || text.none },
|
|
9840
|
+
{ id: "state", name: text.metrics.state, read: () => `${state.crouch ? "crouch " : ""}${state.down ? "down " : ""}${state.armed === null ? "" : state.armed ? "armed" : "unarmed"}`.trim() || text.none },
|
|
9841
|
+
{ id: "joints", name: text.metrics.joints, read: joints }
|
|
9842
|
+
];
|
|
9843
|
+
},
|
|
9844
|
+
attach(lent) {
|
|
9845
|
+
host = lent;
|
|
9846
|
+
const stopChange = lent.bench?.onChange(onChange);
|
|
9847
|
+
const stopEvents = options.avatar.on("event", (event) => {
|
|
9848
|
+
lastEvent = `${event.clip} ${event.type} ${round(event.t)}s${event.data && "label" in event.data ? ` ${phaseLabel(event.data.label)}` : ""}`;
|
|
9849
|
+
log("clip_event", { clip: event.clip, event: event.type, t: event.t, data: event.data ?? null });
|
|
9850
|
+
if (event.type === "impact") log("impact", { clip: event.clip, t: event.t, kind: event.data?.kind ?? null });
|
|
9851
|
+
});
|
|
9852
|
+
return () => {
|
|
9853
|
+
stopChange?.();
|
|
9854
|
+
stopEvents();
|
|
9855
|
+
host = null;
|
|
9856
|
+
};
|
|
9857
|
+
}
|
|
9858
|
+
};
|
|
9859
|
+
}
|
|
6476
9860
|
return __toCommonJS(index_exports);
|
|
6477
9861
|
})();
|
|
6478
9862
|
//# sourceMappingURL=index.global.js.map
|