@adatechnology/game-characters-three 0.5.1 → 0.7.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/dist/index.d.ts +91 -18
- package/dist/index.global.js +457 -110
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +458 -110
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -993,7 +993,7 @@ var createBestBuilder = (host, { types, random = Math.random }) => {
|
|
|
993
993
|
});
|
|
994
994
|
}
|
|
995
995
|
const neckLen = 0.14 + Math.max(0, P.neck);
|
|
996
|
-
|
|
996
|
+
const neck = cut.head ? void 0 : add(U, CYL(0.058, 0.068, neckLen, SEG), skinM, 0, 1.63 + Math.max(0, P.neck) / 2, 0);
|
|
997
997
|
const headG = new THREE.Group();
|
|
998
998
|
headG.position.set(0, 1.64 + P.neck, P.headFwd || 0);
|
|
999
999
|
headG.rotation.x = P.headX;
|
|
@@ -1200,6 +1200,7 @@ var createBestBuilder = (host, { types, random = Math.random }) => {
|
|
|
1200
1200
|
if (o.userData.flap) flaps.push(o);
|
|
1201
1201
|
});
|
|
1202
1202
|
const rig = { flaps, dangles: arms.filter((a) => a.el.userData.dangle), type: spec.type || "Lento", fig, upper, headG, arms, legs, P, ph: spec.phase ?? options.random() * 6, dropBase: fig.position.y, leanBase: P.lean, hxBase: P.headX };
|
|
1203
|
+
if (neck) Object.defineProperty(rig, "neck", { value: neck });
|
|
1203
1204
|
if (spec.lying) {
|
|
1204
1205
|
root.rotation.x = -PI / 2;
|
|
1205
1206
|
root.position.set(0, 0.16 * P.s, 0.9 * P.s);
|
|
@@ -3751,7 +3752,8 @@ function createCharactersTestModule(options = {}) {
|
|
|
3751
3752
|
const makeKit = () => options.createKit ? options.createKit({ penetration: settings.penetration, deathRule: settings.deathRule }) : createCharacters({ ...options.packs ? { packs: options.packs } : {}, penetration: settings.penetration, damage: { deathRule: settings.deathRule } });
|
|
3752
3753
|
let kit = makeKit();
|
|
3753
3754
|
let undead = options.createUndead ? options.createUndead(kit) : null;
|
|
3754
|
-
|
|
3755
|
+
let stream = createRng3(options.seed ?? 1);
|
|
3756
|
+
const random = () => stream();
|
|
3755
3757
|
let seedCounter = 100;
|
|
3756
3758
|
let host = null;
|
|
3757
3759
|
let three = null;
|
|
@@ -4131,6 +4133,14 @@ function createCharactersTestModule(options = {}) {
|
|
|
4131
4133
|
wounds = createWounds(lent.THREE, { random, scene: scene2, groundAt: (x, z) => lent.groundAt(x, z), ...bridge ? { bridge } : {}, ...sound ? { sound } : {} });
|
|
4132
4134
|
for (const entry of entries.values()) trackEntry(entry);
|
|
4133
4135
|
const stopChange = lent.bench?.onChange(onChange);
|
|
4136
|
+
const stopReset = lent.onReset?.(() => {
|
|
4137
|
+
stream = createRng3(options.seed ?? 1);
|
|
4138
|
+
seedCounter = 100;
|
|
4139
|
+
time = 0;
|
|
4140
|
+
selected = null;
|
|
4141
|
+
lastCrossed = 0;
|
|
4142
|
+
lastEnergy = 1;
|
|
4143
|
+
});
|
|
4134
4144
|
const stopEvents = lent.on((event) => {
|
|
4135
4145
|
if (event.type !== "explosion") return;
|
|
4136
4146
|
for (const entry of entries.values()) {
|
|
@@ -4155,6 +4165,7 @@ function createCharactersTestModule(options = {}) {
|
|
|
4155
4165
|
});
|
|
4156
4166
|
return () => {
|
|
4157
4167
|
stopChange?.();
|
|
4168
|
+
stopReset?.();
|
|
4158
4169
|
stopEvents();
|
|
4159
4170
|
stopFrame();
|
|
4160
4171
|
clearOverlays();
|
|
@@ -4201,6 +4212,7 @@ import {
|
|
|
4201
4212
|
filterPoseChannels,
|
|
4202
4213
|
samplePoseClip,
|
|
4203
4214
|
walkCadence,
|
|
4215
|
+
stepCascade as stepCascade2,
|
|
4204
4216
|
walkGait
|
|
4205
4217
|
} from "@adatechnology/game-characters";
|
|
4206
4218
|
|
|
@@ -4235,12 +4247,272 @@ var player_avatar_default = {
|
|
|
4235
4247
|
shirtColor: "3d5a73"
|
|
4236
4248
|
};
|
|
4237
4249
|
|
|
4250
|
+
// src/first-person/ik.ts
|
|
4251
|
+
var solveTwoBone = (root, target, upper, lower, pole, out) => {
|
|
4252
|
+
const line = out.line.copy(target).sub(root);
|
|
4253
|
+
let distance2 = line.length();
|
|
4254
|
+
if (distance2 < 1e-6) line.set(0, 0, -1);
|
|
4255
|
+
else line.divideScalar(distance2);
|
|
4256
|
+
const min = Math.abs(upper - lower) + 1e-4;
|
|
4257
|
+
const max = upper + lower - 1e-4;
|
|
4258
|
+
out.clamped = distance2 > max || distance2 < min;
|
|
4259
|
+
distance2 = Math.min(max, Math.max(min, distance2));
|
|
4260
|
+
const along = (distance2 * distance2 + upper * upper - lower * lower) / (2 * distance2);
|
|
4261
|
+
const across = Math.sqrt(Math.max(0, upper * upper - along * along));
|
|
4262
|
+
const side = out.side.copy(pole);
|
|
4263
|
+
side.addScaledVector(line, -side.dot(line));
|
|
4264
|
+
if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
|
|
4265
|
+
if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
|
|
4266
|
+
side.normalize();
|
|
4267
|
+
out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
|
|
4268
|
+
out.end.copy(root).addScaledVector(line, distance2);
|
|
4269
|
+
return out;
|
|
4270
|
+
};
|
|
4271
|
+
var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
|
|
4272
|
+
|
|
4273
|
+
// src/player/arm-reach.ts
|
|
4274
|
+
var ARM_REACH = {
|
|
4275
|
+
/** The elbow never straightens past this bend (a locked elbow reads as a stick). */
|
|
4276
|
+
elbowMin: 0.12,
|
|
4277
|
+
/** Full flexion of the elbow. */
|
|
4278
|
+
elbowMax: 2.5,
|
|
4279
|
+
/** The wrist bends (flexion, extension, deviation) up to this angle from the line of the forearm (flexion reaches about 80 degrees). */
|
|
4280
|
+
wristBend: 1.3,
|
|
4281
|
+
/** The forearm turns (pronation and supination) up to this angle about its own axis. */
|
|
4282
|
+
wristTwist: 1.6,
|
|
4283
|
+
/** The most the chest turns to bring the support shoulder toward a far hand. */
|
|
4284
|
+
chestTurn: 0.6,
|
|
4285
|
+
/** The most a shoulder blade slides toward a far hand (protraction and elevation together). */
|
|
4286
|
+
protraction: 0.11,
|
|
4287
|
+
/** A hand is within reach below this fraction of the arm's length. */
|
|
4288
|
+
slack: 0.96,
|
|
4289
|
+
/** Where the elbow points: down and out from the body, a little back (body space, x outward). */
|
|
4290
|
+
elbowPole: [0.55, -1, -0.25],
|
|
4291
|
+
/** The support arm's elbow: under the gun, hardly out of the body (the first-person arms' `elbowPoleLeft`), so the forearm meets the handguard nearer the aim's line. */
|
|
4292
|
+
elbowPoleSupport: [0.1, -1, -0.2],
|
|
4293
|
+
/** From the wrist joint to the middle of the palm, metres of the body. */
|
|
4294
|
+
palm: 0.042,
|
|
4295
|
+
/** In a view the elbow keeps at least this interior bend where a hand is out of reach, right then left (the first-person arms' numbers). */
|
|
4296
|
+
viewBend: [1.35, 0.95],
|
|
4297
|
+
/** In a view a hand stays this far before a surface, metres. */
|
|
4298
|
+
contactMargin: 5e-3,
|
|
4299
|
+
/** In a view a hand is never nearer than this to the eye, metres. */
|
|
4300
|
+
contactNearest: 0.12,
|
|
4301
|
+
/** How far in front of the eye a surface is looked for, metres. */
|
|
4302
|
+
contactProbe: 1.4,
|
|
4303
|
+
/** How far a hand reaches forward of its middle at least (the thickness of the palm), and per metre of fingers pointing ahead. */
|
|
4304
|
+
contactPalm: 0.03,
|
|
4305
|
+
/** From the middle of the palm to the fingertips. */
|
|
4306
|
+
contactFingers: 0.085
|
|
4307
|
+
};
|
|
4308
|
+
var createArmReach = (THREE, rig, hands) => {
|
|
4309
|
+
const arms = {
|
|
4310
|
+
R: { g: rig.arms[1].g, el: rig.arms[1].el, hand: hands.R, outward: -1 },
|
|
4311
|
+
L: { g: rig.arms[0].g, el: rig.arms[0].el, hand: hands.L, outward: 1 }
|
|
4312
|
+
};
|
|
4313
|
+
const upper = rig.upper;
|
|
4314
|
+
const handBasis = {
|
|
4315
|
+
R: new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 0, -1))).invert(),
|
|
4316
|
+
L: new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 1))).invert()
|
|
4317
|
+
};
|
|
4318
|
+
const saved = {
|
|
4319
|
+
twist: 0,
|
|
4320
|
+
arms: { R: { position: new THREE.Vector3(), g: new THREE.Quaternion(), el: new THREE.Quaternion(), hand: new THREE.Quaternion() }, L: { position: new THREE.Vector3(), g: new THREE.Quaternion(), el: new THREE.Quaternion(), hand: new THREE.Quaternion() } }
|
|
4321
|
+
};
|
|
4322
|
+
let applied = false;
|
|
4323
|
+
const bone = createTwoBone(THREE);
|
|
4324
|
+
const v = { shoulder: new THREE.Vector3(), elbow: new THREE.Vector3(), wrist: new THREE.Vector3(), palm: new THREE.Vector3(), target: new THREE.Vector3(), pole: new THREE.Vector3(), a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
|
|
4325
|
+
const q = { hand: new THREE.Quaternion(), parent: new THREE.Quaternion(), body: new THREE.Quaternion(), local: new THREE.Quaternion(), swing: new THREE.Quaternion(), twist: new THREE.Quaternion(), rest: new THREE.Quaternion() };
|
|
4326
|
+
const basis = new THREE.Matrix4();
|
|
4327
|
+
const camera = { position: new THREE.Vector3(), quaternion: new THREE.Quaternion(), inverse: new THREE.Quaternion(), probe: new THREE.Vector3(), forward: new THREE.Vector3(), fingers: new THREE.Vector3(), hand: new THREE.Quaternion() };
|
|
4328
|
+
let bendLimit = null;
|
|
4329
|
+
const yAxis = new THREE.Vector3(0, 1, 0);
|
|
4330
|
+
const lengthsOf = (arm) => {
|
|
4331
|
+
const scale = arm.g.getWorldScale(v.a).y;
|
|
4332
|
+
return { upper: arm.el.position.length() * scale, fore: arm.hand.position.length() * scale, palm: ARM_REACH.palm * scale };
|
|
4333
|
+
};
|
|
4334
|
+
const limitOf = (side, upperLength, fore) => {
|
|
4335
|
+
if (!bendLimit) return (upperLength + fore) * ARM_REACH.slack;
|
|
4336
|
+
return Math.sqrt(upperLength * upperLength + fore * fore + 2 * upperLength * fore * Math.cos(bendLimit[side === "R" ? 0 : 1]));
|
|
4337
|
+
};
|
|
4338
|
+
const wristOf = (side, pose, palm) => {
|
|
4339
|
+
q.hand.set(...pose.quaternion).multiply(handBasis[side]);
|
|
4340
|
+
v.palm.set(...pose.position);
|
|
4341
|
+
v.wrist.copy(v.palm).addScaledVector(v.a.set(0, -1, 0).applyQuaternion(q.hand), -palm);
|
|
4342
|
+
};
|
|
4343
|
+
const turnChest = (pose) => {
|
|
4344
|
+
const { upper: up, fore, palm } = lengthsOf(arms.L);
|
|
4345
|
+
wristOf("L", pose, palm);
|
|
4346
|
+
const excess = arms.L.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limitOf("L", up, fore);
|
|
4347
|
+
if (excess <= 0) return;
|
|
4348
|
+
const chestRadius = Math.hypot(arms.L.g.position.x, arms.L.g.position.z) * arms.L.g.getWorldScale(v.a).y;
|
|
4349
|
+
upper.rotation.y -= Math.min(ARM_REACH.chestTurn, excess / Math.max(0.05, chestRadius));
|
|
4350
|
+
upper.updateMatrixWorld(true);
|
|
4351
|
+
};
|
|
4352
|
+
const protract = (arm, limit) => {
|
|
4353
|
+
const excess = arm.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limit;
|
|
4354
|
+
if (excess <= 0) return;
|
|
4355
|
+
const parent = arm.g.parent;
|
|
4356
|
+
v.b.copy(v.wrist).sub(v.shoulder).normalize().multiplyScalar(Math.min(excess, ARM_REACH.protraction)).add(v.shoulder);
|
|
4357
|
+
arm.g.position.copy(parent.worldToLocal(v.b));
|
|
4358
|
+
arm.g.updateMatrixWorld(true);
|
|
4359
|
+
};
|
|
4360
|
+
const limitWrist = (local) => {
|
|
4361
|
+
const projection = v.c.set(local.x, local.y, local.z).projectOnVector(yAxis);
|
|
4362
|
+
q.twist.set(projection.x, projection.y, projection.z, local.w).normalize();
|
|
4363
|
+
const twistAngle = 2 * Math.atan2(q.twist.y, q.twist.w);
|
|
4364
|
+
const swing = q.swing.copy(local).multiply(q.twist.invert());
|
|
4365
|
+
const swingAngle = 2 * Math.acos(Math.min(1, Math.abs(swing.w)));
|
|
4366
|
+
if (swingAngle > ARM_REACH.wristBend) swing.slerp(q.rest.identity(), 1 - ARM_REACH.wristBend / swingAngle);
|
|
4367
|
+
const clampedTwist = Math.max(-ARM_REACH.wristTwist, Math.min(ARM_REACH.wristTwist, Math.atan2(Math.sin(twistAngle), Math.cos(twistAngle))));
|
|
4368
|
+
return local.copy(swing).multiply(q.twist.setFromAxisAngle(yAxis, clampedTwist));
|
|
4369
|
+
};
|
|
4370
|
+
const reach = (side, pose) => {
|
|
4371
|
+
const arm = arms[side];
|
|
4372
|
+
const { upper: up, fore, palm } = lengthsOf(arm);
|
|
4373
|
+
wristOf(side, pose, palm);
|
|
4374
|
+
protract(arm, limitOf(side, up, fore));
|
|
4375
|
+
arm.g.getWorldPosition(v.shoulder);
|
|
4376
|
+
upper.getWorldQuaternion(q.body);
|
|
4377
|
+
const pole = side === "L" ? ARM_REACH.elbowPoleSupport : ARM_REACH.elbowPole;
|
|
4378
|
+
v.pole.set(pole[0] * arm.outward, pole[1], pole[2]).applyQuaternion(q.body);
|
|
4379
|
+
const lockLimit = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMin));
|
|
4380
|
+
const most = bendLimit ? Math.min(lockLimit, limitOf(side, up, fore)) : lockLimit;
|
|
4381
|
+
const least = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMax));
|
|
4382
|
+
const line = v.c.copy(v.wrist).sub(v.shoulder);
|
|
4383
|
+
const distance2 = Math.min(most, Math.max(least, line.length()));
|
|
4384
|
+
v.target.copy(v.shoulder).addScaledVector(line.normalize(), distance2);
|
|
4385
|
+
solveTwoBone(v.shoulder, v.target, up, fore, v.pole, bone);
|
|
4386
|
+
const along = v.a.copy(bone.joint).sub(v.shoulder).normalize();
|
|
4387
|
+
const forearm = v.b.copy(bone.end).sub(bone.joint).normalize();
|
|
4388
|
+
const fold = v.c.copy(forearm).addScaledVector(along, -forearm.dot(along));
|
|
4389
|
+
if (fold.lengthSq() < 1e-8) fold.copy(v.pole).addScaledVector(along, -v.pole.dot(along)).negate();
|
|
4390
|
+
fold.normalize();
|
|
4391
|
+
const yWorld = v.elbow.copy(along).negate();
|
|
4392
|
+
basis.makeBasis(v.pole.crossVectors(yWorld, fold), yWorld, fold);
|
|
4393
|
+
arm.g.parent.getWorldQuaternion(q.parent);
|
|
4394
|
+
arm.g.quaternion.setFromRotationMatrix(basis).premultiply(q.parent.invert());
|
|
4395
|
+
arm.el.rotation.set(-Math.acos(Math.max(-1, Math.min(1, along.dot(forearm)))), 0, 0);
|
|
4396
|
+
arm.g.updateMatrixWorld(true);
|
|
4397
|
+
const reached = arm.hand.getWorldPosition(v.a).sub(v.shoulder).normalize();
|
|
4398
|
+
q.swing.setFromUnitVectors(reached, v.b.copy(v.target).sub(v.shoulder).normalize());
|
|
4399
|
+
arm.g.parent.getWorldQuaternion(q.parent);
|
|
4400
|
+
arm.g.quaternion.premultiply(q.parent).premultiply(q.swing).premultiply(q.parent.invert());
|
|
4401
|
+
arm.g.updateMatrixWorld(true);
|
|
4402
|
+
arm.el.getWorldQuaternion(q.parent);
|
|
4403
|
+
arm.hand.quaternion.copy(limitWrist(q.local.copy(q.parent.invert()).multiply(q.hand)));
|
|
4404
|
+
};
|
|
4405
|
+
const keepOutside = (pose, outside) => {
|
|
4406
|
+
const place = v.a.set(...pose.position).sub(camera.position).applyQuaternion(camera.inverse);
|
|
4407
|
+
camera.probe.set(place.x, place.y, 0).applyQuaternion(camera.quaternion).add(camera.position);
|
|
4408
|
+
camera.forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
|
|
4409
|
+
const distance2 = outside.contact([camera.probe.x, camera.probe.y, camera.probe.z], [camera.forward.x, camera.forward.y, camera.forward.z], ARM_REACH.contactProbe);
|
|
4410
|
+
if (distance2 === null) return pose;
|
|
4411
|
+
camera.hand.set(...pose.quaternion).premultiply(camera.inverse);
|
|
4412
|
+
const ahead = Math.max(ARM_REACH.contactPalm, -camera.fingers.set(1, 0, 0).applyQuaternion(camera.hand).z * ARM_REACH.contactFingers);
|
|
4413
|
+
const limit = Math.max(outside.nearest ?? ARM_REACH.contactNearest, distance2 - ahead - (outside.margin ?? ARM_REACH.contactMargin));
|
|
4414
|
+
if (-place.z <= limit) return pose;
|
|
4415
|
+
place.z = -limit;
|
|
4416
|
+
place.applyQuaternion(camera.quaternion).add(camera.position);
|
|
4417
|
+
return { ...pose, position: [place.x, place.y, place.z] };
|
|
4418
|
+
};
|
|
4419
|
+
const save = () => {
|
|
4420
|
+
saved.twist = upper.rotation.y;
|
|
4421
|
+
for (const side of ["R", "L"]) {
|
|
4422
|
+
const arm = arms[side];
|
|
4423
|
+
const into = saved.arms[side];
|
|
4424
|
+
into.position.copy(arm.g.position);
|
|
4425
|
+
into.g.copy(arm.g.quaternion);
|
|
4426
|
+
into.el.copy(arm.el.quaternion);
|
|
4427
|
+
into.hand.copy(arm.hand.quaternion);
|
|
4428
|
+
}
|
|
4429
|
+
};
|
|
4430
|
+
return {
|
|
4431
|
+
apply(targets, view) {
|
|
4432
|
+
if (!targets || !targets.R && !targets.L) return;
|
|
4433
|
+
save();
|
|
4434
|
+
applied = true;
|
|
4435
|
+
upper.updateWorldMatrix(true, true);
|
|
4436
|
+
let { R, L } = targets;
|
|
4437
|
+
bendLimit = view ? view.reachBend ?? ARM_REACH.viewBend : null;
|
|
4438
|
+
if (view?.keepOutside) {
|
|
4439
|
+
view.camera.updateWorldMatrix(true, false);
|
|
4440
|
+
view.camera.matrixWorld.decompose(camera.position, camera.quaternion, v.a);
|
|
4441
|
+
camera.inverse.copy(camera.quaternion).invert();
|
|
4442
|
+
if (R) R = keepOutside(R, view.keepOutside);
|
|
4443
|
+
if (L) L = keepOutside(L, view.keepOutside);
|
|
4444
|
+
}
|
|
4445
|
+
if (L && R) turnChest(L);
|
|
4446
|
+
if (R) reach("R", R);
|
|
4447
|
+
if (L) reach("L", L);
|
|
4448
|
+
bendLimit = null;
|
|
4449
|
+
},
|
|
4450
|
+
restore() {
|
|
4451
|
+
if (!applied) return;
|
|
4452
|
+
applied = false;
|
|
4453
|
+
upper.rotation.y = saved.twist;
|
|
4454
|
+
for (const side of ["R", "L"]) {
|
|
4455
|
+
const arm = arms[side];
|
|
4456
|
+
const from = saved.arms[side];
|
|
4457
|
+
arm.g.position.copy(from.position);
|
|
4458
|
+
arm.g.quaternion.copy(from.g);
|
|
4459
|
+
arm.el.quaternion.copy(from.el);
|
|
4460
|
+
arm.hand.quaternion.copy(from.hand);
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
};
|
|
4464
|
+
};
|
|
4465
|
+
|
|
4466
|
+
// src/player/head-hiding.ts
|
|
4467
|
+
var isDrawable = (object) => {
|
|
4468
|
+
const flagged = object;
|
|
4469
|
+
return Boolean(flagged.isMesh || flagged.isSprite || flagged.isPoints);
|
|
4470
|
+
};
|
|
4471
|
+
var createHeadHider = (roots) => {
|
|
4472
|
+
let isHiding = false;
|
|
4473
|
+
let viewCamera = null;
|
|
4474
|
+
const drawables = [];
|
|
4475
|
+
for (const root of roots) root.traverse((object) => isDrawable(object) && drawables.push(object));
|
|
4476
|
+
for (const drawable of drawables) {
|
|
4477
|
+
const previousBefore = drawable.onBeforeRender;
|
|
4478
|
+
const previousAfter = drawable.onAfterRender;
|
|
4479
|
+
let saved = [];
|
|
4480
|
+
drawable.onBeforeRender = (renderer, scene2, camera, geometry, material, group) => {
|
|
4481
|
+
previousBefore.call(drawable, renderer, scene2, camera, geometry, material, group);
|
|
4482
|
+
if (!isHiding || camera !== viewCamera) return;
|
|
4483
|
+
const drawn = Array.isArray(drawable.material) ? drawable.material : [material];
|
|
4484
|
+
saved = drawn.map((entry) => ({ material: entry, colorWrite: entry.colorWrite, depthWrite: entry.depthWrite }));
|
|
4485
|
+
for (const entry of drawn) {
|
|
4486
|
+
entry.colorWrite = false;
|
|
4487
|
+
entry.depthWrite = false;
|
|
4488
|
+
}
|
|
4489
|
+
};
|
|
4490
|
+
drawable.onAfterRender = (renderer, scene2, camera, geometry, material, group) => {
|
|
4491
|
+
previousAfter.call(drawable, renderer, scene2, camera, geometry, material, group);
|
|
4492
|
+
for (const flags of saved) {
|
|
4493
|
+
flags.material.colorWrite = flags.colorWrite;
|
|
4494
|
+
flags.material.depthWrite = flags.depthWrite;
|
|
4495
|
+
}
|
|
4496
|
+
saved = [];
|
|
4497
|
+
};
|
|
4498
|
+
}
|
|
4499
|
+
return {
|
|
4500
|
+
setView(mode, camera) {
|
|
4501
|
+
isHiding = mode === "first" && camera !== null;
|
|
4502
|
+
viewCamera = camera;
|
|
4503
|
+
}
|
|
4504
|
+
};
|
|
4505
|
+
};
|
|
4506
|
+
|
|
4238
4507
|
// src/player/avatar.ts
|
|
4239
4508
|
var TWO_PI = Math.PI * 2;
|
|
4240
4509
|
var MAX_GAIT = 1.4;
|
|
4241
4510
|
var CLIP_BLEND = 0.12;
|
|
4242
4511
|
var CLIP_CASCADE = 5;
|
|
4243
4512
|
var FALL_SECONDS2 = 0.3;
|
|
4513
|
+
var BUILDER_EYE = [0, 1.815, 0.16];
|
|
4514
|
+
var EYE_OFFSET = [0, -0.068, 0];
|
|
4515
|
+
var EYE_BOB = 0.5;
|
|
4244
4516
|
var HIP_HEIGHT = 0.95;
|
|
4245
4517
|
var WALK_ARM = 0.28;
|
|
4246
4518
|
var WALK_BOB = 0.035;
|
|
@@ -4250,7 +4522,11 @@ var RUN_BOB = 0.05;
|
|
|
4250
4522
|
var REST_ARM_Z = 0.18;
|
|
4251
4523
|
var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
|
|
4252
4524
|
var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
|
|
4525
|
+
var SIT = { thigh: -1.5, lean: -0.08, arm: -0.45, armZ: 0.12, elbow: -0.75 };
|
|
4526
|
+
var LIE = { knee: 0.08, armZ: 0.12, elbow: -0.1 };
|
|
4253
4527
|
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 };
|
|
4528
|
+
var DOOR_SHOULDER = 1.42;
|
|
4529
|
+
var DOOR_ARM = 0.62;
|
|
4254
4530
|
var HAND_OFFSET = { x: 0, y: -0.33, z: 0.04 };
|
|
4255
4531
|
var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
|
|
4256
4532
|
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
@@ -4280,6 +4556,8 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4280
4556
|
const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
|
|
4281
4557
|
const smooth = options.cascade ?? true;
|
|
4282
4558
|
const clipCascade = options.clipCascade ?? CLIP_CASCADE;
|
|
4559
|
+
const eyeBob = options.eyeBob ?? EYE_BOB;
|
|
4560
|
+
const eyeOffset = options.eyeOffset ?? EYE_OFFSET;
|
|
4283
4561
|
const root = new THREE.Group();
|
|
4284
4562
|
const tilt = new THREE.Group();
|
|
4285
4563
|
tilt.add(body);
|
|
@@ -4331,6 +4609,7 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4331
4609
|
const built = buildHand({ THREE, add: addMesh, hg, handM: handMaterial, s: side, detailed: true });
|
|
4332
4610
|
hands.push({ side, hg, fingers: built.fingers, th: built.th });
|
|
4333
4611
|
});
|
|
4612
|
+
const armReach = createArmReach(THREE, rig, { R: hands.find((hand) => hand.side < 0).hg, L: hands.find((hand) => hand.side > 0).hg });
|
|
4334
4613
|
const setHandGrip = (hand, grip, wristX = 0) => {
|
|
4335
4614
|
hand.fingers.forEach((finger, j) => {
|
|
4336
4615
|
finger.rotation.z = -hand.side * grip * (j % 2 ? 1.1 : 0.9);
|
|
@@ -4338,7 +4617,23 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4338
4617
|
if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
|
|
4339
4618
|
hand.hg.rotation.x = wristX;
|
|
4340
4619
|
};
|
|
4620
|
+
const eyeSocket = new THREE.Object3D();
|
|
4621
|
+
eyeSocket.name = "eye";
|
|
4622
|
+
eyeSocket.position.set(BUILDER_EYE[0] + eyeOffset[0], BUILDER_EYE[1] + eyeOffset[1], BUILDER_EYE[2] + eyeOffset[2]);
|
|
4623
|
+
rig.headG.children[0].add(eyeSocket);
|
|
4624
|
+
const headHider = createHeadHider([rig.headG, ...rig.neck ? [rig.neck] : []]);
|
|
4625
|
+
const eyeLocal = new THREE.Vector3();
|
|
4626
|
+
let bob = 0;
|
|
4627
|
+
let hipWithoutBob = null;
|
|
4341
4628
|
const legRestX = rig.legs.map((leg) => leg.g.position.x);
|
|
4629
|
+
const legLengths = (() => {
|
|
4630
|
+
const thigh = rig.legs[0].g;
|
|
4631
|
+
const shin = rig.legs[0].kn;
|
|
4632
|
+
root.updateMatrixWorld(true);
|
|
4633
|
+
const hipY = thigh.getWorldPosition(new THREE.Vector3()).y;
|
|
4634
|
+
const kneeY = shin.getWorldPosition(new THREE.Vector3()).y;
|
|
4635
|
+
return { hipBelowTrunk: HIP_HEIGHT - hipY, thigh: hipY - kneeY, shin: kneeY - new THREE.Box3().setFromObject(shin).min.y };
|
|
4636
|
+
})();
|
|
4342
4637
|
const legSide = (index) => index === 0 ? 1 : -1;
|
|
4343
4638
|
let cycles = 0;
|
|
4344
4639
|
let time = 0;
|
|
@@ -4397,10 +4692,29 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4397
4692
|
...strain ? { legL: -0.5, legR: 0.4, kneeL: 0.55, kneeR: 0.08, drop: -0.05 } : {}
|
|
4398
4693
|
};
|
|
4399
4694
|
};
|
|
4695
|
+
const doorPose = (door) => {
|
|
4696
|
+
const { phase, progress } = door;
|
|
4697
|
+
const on = phase === "reach" ? progress : phase === "release" ? 1 - progress : 1;
|
|
4698
|
+
const swing = phase === "swing" ? progress : phase === "release" ? 1 : 0;
|
|
4699
|
+
const shake = phase === "rattle" ? Math.sin(time * 55) * 0.06 * (1 - progress) : 0;
|
|
4700
|
+
const drop = clamp((DOOR_SHOULDER - door.height) / DOOR_ARM, -0.9, 1);
|
|
4701
|
+
const low = clamp((0.75 - door.height) / 0.5, 0, 1);
|
|
4702
|
+
const raise = -Math.acos(clamp(drop, -1, 1)) * on - 0.35 * low * on;
|
|
4703
|
+
const elbow = door.pull ? -0.35 - 0.75 * swing : -0.95 + 0.75 * swing;
|
|
4704
|
+
return {
|
|
4705
|
+
armR: raise + shake,
|
|
4706
|
+
armRz: REST_ARM_Z * (1 - on) + 0.05 * on,
|
|
4707
|
+
elbR: -0.12 + (elbow + 0.12) * on,
|
|
4708
|
+
armLz: -REST_ARM_Z,
|
|
4709
|
+
elbL: -0.12,
|
|
4710
|
+
lean: ((door.pull ? -0.06 : 0.1) * swing + 0.35 * low) * on,
|
|
4711
|
+
headX: 0.06 * on + 0.2 * low * on
|
|
4712
|
+
};
|
|
4713
|
+
};
|
|
4400
4714
|
const holdingPose = (gait, itemUse) => {
|
|
4401
|
-
const
|
|
4715
|
+
const bob2 = Math.sin(cycles * TWO_PI) * 0.05 * gait;
|
|
4402
4716
|
if (!itemUse) {
|
|
4403
|
-
return { armL: -0.45 +
|
|
4717
|
+
return { armL: -0.45 + bob2, armLz: 0.22, elbL: -1.05 };
|
|
4404
4718
|
}
|
|
4405
4719
|
const { id, position, rotation } = itemUse;
|
|
4406
4720
|
if (id === "drink" || id === "sip" || id === "chug") {
|
|
@@ -4413,7 +4727,7 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4413
4727
|
headX: -0.26 * tilt3
|
|
4414
4728
|
};
|
|
4415
4729
|
}
|
|
4416
|
-
if (id === "open" || id === "close") {
|
|
4730
|
+
if (id === "open" || id === "close" || itemUse.gripL !== void 0) {
|
|
4417
4731
|
const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
|
|
4418
4732
|
return {
|
|
4419
4733
|
armL: -0.45 - 0.35 * reach,
|
|
@@ -4512,6 +4826,31 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4512
4826
|
drop: Math.abs(Math.sin(ph)) * WALK_BOB * gait
|
|
4513
4827
|
};
|
|
4514
4828
|
};
|
|
4829
|
+
const sitPose = (hip) => {
|
|
4830
|
+
const knee = hip - legLengths.hipBelowTrunk - legLengths.thigh * Math.cos(SIT.thigh);
|
|
4831
|
+
const shinFromVertical = Math.acos(clamp(knee / legLengths.shin, 0, 1));
|
|
4832
|
+
const bend = -SIT.thigh - shinFromVertical;
|
|
4833
|
+
return {
|
|
4834
|
+
legL: SIT.thigh,
|
|
4835
|
+
legR: SIT.thigh,
|
|
4836
|
+
kneeL: bend,
|
|
4837
|
+
kneeR: bend,
|
|
4838
|
+
lean: SIT.lean,
|
|
4839
|
+
drop: hip - HIP_HEIGHT,
|
|
4840
|
+
armR: SIT.arm,
|
|
4841
|
+
armL: SIT.arm,
|
|
4842
|
+
armRz: SIT.armZ,
|
|
4843
|
+
armLz: -SIT.armZ,
|
|
4844
|
+
elbR: SIT.elbow,
|
|
4845
|
+
elbL: SIT.elbow
|
|
4846
|
+
};
|
|
4847
|
+
};
|
|
4848
|
+
const liePose = () => ({ legL: 0, legR: 0, kneeL: LIE.knee, kneeR: LIE.knee, lean: 0, drop: 0, armR: 0, armL: 0, armRz: LIE.armZ, armLz: -LIE.armZ, elbR: LIE.elbow, elbL: LIE.elbow });
|
|
4849
|
+
const layBody = (hip) => {
|
|
4850
|
+
tilt.rotation.set(-Math.PI / 2, 0, 0, "XYZ");
|
|
4851
|
+
const pivot = new THREE.Vector3(0, HIP_HEIGHT, 0).applyEuler(tilt.rotation);
|
|
4852
|
+
tilt.position.set(0, hip, 0).sub(pivot);
|
|
4853
|
+
};
|
|
4515
4854
|
const placeBody = (progress) => {
|
|
4516
4855
|
const fk = progress * progress * (3 - 2 * progress);
|
|
4517
4856
|
const flip = fract(cycles * 1.7) >= 0.5 ? 1 : 0;
|
|
@@ -4561,6 +4900,7 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4561
4900
|
return run ? run.clip.id : null;
|
|
4562
4901
|
},
|
|
4563
4902
|
update(dt, state = {}) {
|
|
4903
|
+
armReach.restore();
|
|
4564
4904
|
const speed = state.speed ?? 0;
|
|
4565
4905
|
const moving = speed >= 0.05;
|
|
4566
4906
|
const down = !!state.down;
|
|
@@ -4571,13 +4911,27 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4571
4911
|
profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
|
|
4572
4912
|
time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
|
|
4573
4913
|
animator.animate(body, time, { speed });
|
|
4914
|
+
bob = rig.fig.position.y - rig.dropBase;
|
|
4574
4915
|
fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
|
|
4575
4916
|
resetLegYaw();
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4917
|
+
const seat = down ? null : state.seat ?? null;
|
|
4918
|
+
if (down) {
|
|
4919
|
+
animator.setPose(rig, downPose());
|
|
4920
|
+
bob = rig.fig.position.y - rig.dropBase;
|
|
4921
|
+
} else if (seat) {
|
|
4922
|
+
animator.setPose(rig, seat.pose === "lie" ? liePose() : sitPose(seat.hip));
|
|
4923
|
+
bob = 0;
|
|
4924
|
+
} else {
|
|
4925
|
+
if (running) {
|
|
4926
|
+
animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
|
|
4927
|
+
bob = rig.fig.position.y - rig.dropBase;
|
|
4928
|
+
}
|
|
4929
|
+
if (state.crouch) {
|
|
4930
|
+
animator.setPose(rig, crouchPose(gait));
|
|
4931
|
+
bob = rig.fig.position.y - rig.dropBase - CROUCH.drop;
|
|
4932
|
+
}
|
|
4580
4933
|
if (state.push) animator.setPose(rig, pushingPose(state.push));
|
|
4934
|
+
else if (state.door) animator.setPose(rig, doorPose(state.door));
|
|
4581
4935
|
else if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
|
|
4582
4936
|
else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
|
|
4583
4937
|
else if (!running) {
|
|
@@ -4589,20 +4943,33 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4589
4943
|
});
|
|
4590
4944
|
}
|
|
4591
4945
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4946
|
+
if (seat?.pose === "lie") layBody(seat.hip);
|
|
4947
|
+
else placeBody(fall);
|
|
4948
|
+
stepClip(dt, down, Boolean(state.crouch || seat));
|
|
4949
|
+
const cascadeDt = run && !run.clip.loop ? dt * clipCascade : dt;
|
|
4950
|
+
const targetWithoutBob = rig.fig.position.y - bob;
|
|
4951
|
+
if (smooth) animator.cascade(rig, cascadeDt);
|
|
4952
|
+
hipWithoutBob = smooth && hipWithoutBob !== null ? stepCascade2(hipWithoutBob, targetWithoutBob, cascadeDt, "hip") : targetWithoutBob;
|
|
4953
|
+
bob = rig.fig.position.y - hipWithoutBob;
|
|
4595
4954
|
const blow = run?.clip.events?.find((event) => (event.type === "impact" || event.type === "grip") && event.data?.kind)?.data?.kind;
|
|
4596
4955
|
const ph = cycles * TWO_PI;
|
|
4597
4956
|
hands.forEach((hand) => {
|
|
4598
4957
|
const isRight = hand.side < 0;
|
|
4599
4958
|
const holdRight = isRight && (!!state.holding || !!state.itemUse);
|
|
4600
|
-
const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
|
|
4601
|
-
const grip = blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
|
|
4959
|
+
const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close" || state.itemUse?.gripL !== void 0);
|
|
4960
|
+
const grip = !isRight && state.itemUse?.gripL !== void 0 ? 1.22 * state.itemUse.gripL : blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
|
|
4602
4961
|
const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
|
|
4603
4962
|
setHandGrip(hand, grip, wristSwing);
|
|
4604
4963
|
});
|
|
4964
|
+
root.updateMatrixWorld(true);
|
|
4965
|
+
root.worldToLocal(eyeSocket.getWorldPosition(eyeLocal));
|
|
4966
|
+
eyeLocal.y -= (1 - eyeBob) * bob;
|
|
4967
|
+
},
|
|
4968
|
+
eye(out = new THREE.Vector3()) {
|
|
4969
|
+
root.updateWorldMatrix(true, false);
|
|
4970
|
+
return root.localToWorld(out.copy(eyeLocal));
|
|
4605
4971
|
},
|
|
4972
|
+
setView: (mode, camera) => headHider.setView(mode, camera),
|
|
4606
4973
|
play(clip, playOptions = {}) {
|
|
4607
4974
|
const found = typeof clip === "string" ? clips.get(clip) : clip;
|
|
4608
4975
|
if (!found) throw new Error(`Unknown pose clip: ${String(clip)}`);
|
|
@@ -4614,6 +4981,12 @@ var createPlayerAvatar = (THREE, options = {}) => {
|
|
|
4614
4981
|
if (run) run.target = 0;
|
|
4615
4982
|
},
|
|
4616
4983
|
hand: (side) => sockets[side],
|
|
4984
|
+
shoulder: (side) => armOf[side].g,
|
|
4985
|
+
reach(targets, view) {
|
|
4986
|
+
if (run || fall > 0) return;
|
|
4987
|
+
root.updateMatrixWorld(true);
|
|
4988
|
+
armReach.apply(targets, view);
|
|
4989
|
+
},
|
|
4617
4990
|
on(_type, listener) {
|
|
4618
4991
|
listeners.add(listener);
|
|
4619
4992
|
return () => void listeners.delete(listener);
|
|
@@ -4650,7 +5023,9 @@ var first_person_arms_default = {
|
|
|
4650
5023
|
shoulder: [0.16, -0.36, 0.18],
|
|
4651
5024
|
upper: 0.32,
|
|
4652
5025
|
fore: 0.3,
|
|
4653
|
-
reachShift: 0.
|
|
5026
|
+
reachShift: 0.42,
|
|
5027
|
+
reachBend: [1.35, 0.95],
|
|
5028
|
+
elbowView: [[0.96, -0.26, -0.06], null],
|
|
4654
5029
|
wrist: 0.07,
|
|
4655
5030
|
elbowPole: [0.45, -1, 0.2],
|
|
4656
5031
|
shoulderLeft: [-0.07, -0.46, 0.18],
|
|
@@ -4677,22 +5052,14 @@ var first_person_arms_default = {
|
|
|
4677
5052
|
};
|
|
4678
5053
|
|
|
4679
5054
|
// src/first-person/hand.ts
|
|
4680
|
-
var
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
{ z: 9e-3, lengths: [0.035, 0.025, 0.02], width: 0.0155 },
|
|
4684
|
-
{ z: 0.027, lengths: [0.029, 0.02, 0.018], width: 0.0135 }
|
|
4685
|
-
];
|
|
4686
|
-
var PALM = { length: 0.09, thick: 0.026, width: 0.09, wristWidth: 0.066 };
|
|
4687
|
-
var FINGER_THICK = 0.0135;
|
|
4688
|
-
var CURL = { near: 1.05, mid: 1.25, far: 0.85, spread: 0.06 };
|
|
4689
|
-
var THUMB = { base: [5e-3, -6e-3, -0.044], near: 0.034, far: 0.028, width: 0.019 };
|
|
5055
|
+
var PALM = { radius: 0.048, scale: [0.6, 1.05, 1], down: 0.035 };
|
|
5056
|
+
var CURL = { closed: 1.25, near: 0.9, far: 1.1 };
|
|
5057
|
+
var THUMB = { open: 0.32, closed: 0.25 };
|
|
4690
5058
|
var createHandMesh = (THREE, skin, wristSkin = skin) => {
|
|
4691
5059
|
const root = new THREE.Group();
|
|
4692
5060
|
root.name = "first-person-hand";
|
|
4693
5061
|
const geometries = [];
|
|
4694
|
-
const
|
|
4695
|
-
const geometry = new THREE.BoxGeometry(sx, sy, sz);
|
|
5062
|
+
const add = (parent, geometry, material, x = 0, y = 0, z = 0) => {
|
|
4696
5063
|
geometries.push(geometry);
|
|
4697
5064
|
const mesh = new THREE.Mesh(geometry, material);
|
|
4698
5065
|
mesh.position.set(x, y, z);
|
|
@@ -4700,64 +5067,26 @@ var createHandMesh = (THREE, skin, wristSkin = skin) => {
|
|
|
4700
5067
|
parent.add(mesh);
|
|
4701
5068
|
return mesh;
|
|
4702
5069
|
};
|
|
4703
|
-
const
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
const palm = new THREE.Mesh(palmGeometry, skin);
|
|
4711
|
-
palm.frustumCulled = false;
|
|
4712
|
-
root.add(palm);
|
|
4713
|
-
const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 8);
|
|
5070
|
+
const group = new THREE.Group();
|
|
5071
|
+
group.quaternion.setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 0, -1)));
|
|
5072
|
+
group.position.set(-PALM.down, 0, 0);
|
|
5073
|
+
root.add(group);
|
|
5074
|
+
add(group, new THREE.SphereGeometry(PALM.radius, 14, 10), skin, 0, -PALM.down, 0).scale.set(...PALM.scale);
|
|
5075
|
+
const built = buildHand({ THREE, add, hg: group, handM: skin, s: -1, detailed: true });
|
|
5076
|
+
const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 10);
|
|
4714
5077
|
wristGeometry.rotateZ(Math.PI / 2);
|
|
4715
|
-
|
|
4716
|
-
const wrist = new THREE.Mesh(wristGeometry, wristSkin);
|
|
4717
|
-
wrist.position.set(-PALM.length / 2 - 0.018, 0, 0);
|
|
4718
|
-
wrist.frustumCulled = false;
|
|
4719
|
-
root.add(wrist);
|
|
4720
|
-
const joints = [];
|
|
4721
|
-
FINGERS.forEach((finger, index) => {
|
|
4722
|
-
const segments = [];
|
|
4723
|
-
let parent = root;
|
|
4724
|
-
let at = PALM.length / 2;
|
|
4725
|
-
finger.lengths.forEach((length, k) => {
|
|
4726
|
-
const group = new THREE.Group();
|
|
4727
|
-
group.position.set(at, k === 0 ? 2e-3 : 0, k === 0 ? finger.z : 0);
|
|
4728
|
-
const taper = 1 - k * 0.07;
|
|
4729
|
-
box(group, length, FINGER_THICK * taper, finger.width * taper, length / 2, 0, 0);
|
|
4730
|
-
parent.add(group);
|
|
4731
|
-
segments.push(group);
|
|
4732
|
-
parent = group;
|
|
4733
|
-
at = length;
|
|
4734
|
-
});
|
|
4735
|
-
joints.push({ segments, weight: 1 + index * (CURL.spread / CURL.near) });
|
|
4736
|
-
});
|
|
4737
|
-
const thumbNear = new THREE.Group();
|
|
4738
|
-
thumbNear.rotation.order = "YZX";
|
|
4739
|
-
thumbNear.position.set(...THUMB.base);
|
|
4740
|
-
box(thumbNear, THUMB.near, FINGER_THICK * 1.1, THUMB.width, THUMB.near / 2, 0, 0);
|
|
4741
|
-
const thumbFar = new THREE.Group();
|
|
4742
|
-
thumbFar.position.set(THUMB.near, 0, 0);
|
|
4743
|
-
box(thumbFar, THUMB.far, FINGER_THICK, THUMB.width * 0.9, THUMB.far / 2, 0, 0);
|
|
4744
|
-
thumbNear.add(thumbFar);
|
|
4745
|
-
root.add(thumbNear);
|
|
5078
|
+
add(root, wristGeometry, wristSkin, -PALM.down - 0.03, 0, 0);
|
|
4746
5079
|
let lastGrip = 0;
|
|
4747
5080
|
let indexCurl;
|
|
4748
|
-
const
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
if (!finger) return;
|
|
4752
|
-
const k = indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl));
|
|
4753
|
-
finger.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * finger.weight);
|
|
5081
|
+
const curlFinger = (finger, k) => {
|
|
5082
|
+
built.fingers[finger * 2].rotation.z = k * CURL.closed * CURL.near;
|
|
5083
|
+
built.fingers[finger * 2 + 1].rotation.z = k * CURL.closed * CURL.far;
|
|
4754
5084
|
};
|
|
5085
|
+
const applyIndex = () => curlFinger(0, indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl)));
|
|
4755
5086
|
const setGrip = (grip) => {
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
thumbNear.rotation.set(0, 0.55 - 1.45 * k, -0.1 - 0.75 * k);
|
|
4760
|
-
thumbFar.rotation.z = -0.1 - 0.7 * k;
|
|
5087
|
+
lastGrip = Math.min(1, Math.max(0, grip));
|
|
5088
|
+
for (let finger = 1; finger < 4; finger++) curlFinger(finger, lastGrip);
|
|
5089
|
+
if (built.th) built.th.rotation.z = THUMB.open + lastGrip * THUMB.closed;
|
|
4761
5090
|
applyIndex();
|
|
4762
5091
|
};
|
|
4763
5092
|
const setIndex = (value) => {
|
|
@@ -4775,33 +5104,12 @@ var createHandMesh = (THREE, skin, wristSkin = skin) => {
|
|
|
4775
5104
|
};
|
|
4776
5105
|
};
|
|
4777
5106
|
|
|
4778
|
-
// src/first-person/ik.ts
|
|
4779
|
-
var solveTwoBone = (root, target, upper, lower, pole, out) => {
|
|
4780
|
-
const line = out.line.copy(target).sub(root);
|
|
4781
|
-
let distance2 = line.length();
|
|
4782
|
-
if (distance2 < 1e-6) line.set(0, 0, -1);
|
|
4783
|
-
else line.divideScalar(distance2);
|
|
4784
|
-
const min = Math.abs(upper - lower) + 1e-4;
|
|
4785
|
-
const max = upper + lower - 1e-4;
|
|
4786
|
-
out.clamped = distance2 > max || distance2 < min;
|
|
4787
|
-
distance2 = Math.min(max, Math.max(min, distance2));
|
|
4788
|
-
const along = (distance2 * distance2 + upper * upper - lower * lower) / (2 * distance2);
|
|
4789
|
-
const across = Math.sqrt(Math.max(0, upper * upper - along * along));
|
|
4790
|
-
const side = out.side.copy(pole);
|
|
4791
|
-
side.addScaledVector(line, -side.dot(line));
|
|
4792
|
-
if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
|
|
4793
|
-
if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
|
|
4794
|
-
side.normalize();
|
|
4795
|
-
out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
|
|
4796
|
-
out.end.copy(root).addScaledVector(line, distance2);
|
|
4797
|
-
return out;
|
|
4798
|
-
};
|
|
4799
|
-
var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
|
|
4800
|
-
|
|
4801
5107
|
// src/first-person/arms.ts
|
|
4802
5108
|
var DEFAULT_BLEND = 0.18;
|
|
4803
5109
|
var ELBOW_SEGMENTS = 8;
|
|
4804
5110
|
var REACH_SLACK = 0.97;
|
|
5111
|
+
var ELBOW_VIEW_MARGIN = 0.86;
|
|
5112
|
+
var ELBOW_VIEW_PASSES = 4;
|
|
4805
5113
|
var CONTACT_PROBE = 1.4;
|
|
4806
5114
|
var CONTACT_MIN = 0.12;
|
|
4807
5115
|
var CONTACT_MARGIN = 5e-3;
|
|
@@ -4950,6 +5258,11 @@ function createFirstPersonArms(THREE, options = {}) {
|
|
|
4950
5258
|
const shoulderOf = (sign, out) => sign === -1 && rig.shoulderLeft ? out.set(...rig.shoulderLeft) : out.set(rig.shoulder[0] * sign, rig.shoulder[1], rig.shoulder[2]);
|
|
4951
5259
|
const poleOf = (sign) => sign === -1 && rig.elbowPoleLeft ? new THREE.Vector3(...rig.elbowPoleLeft) : new THREE.Vector3(rig.elbowPole[0] * sign, rig.elbowPole[1], rig.elbowPole[2]);
|
|
4952
5260
|
const kneePole = new THREE.Vector3(...rig.kneePole);
|
|
5261
|
+
const reachOf = (sign) => {
|
|
5262
|
+
const bend = rig.reachBend?.[sign === 1 ? 0 : 1];
|
|
5263
|
+
if (bend === void 0) return (rig.upper + rig.fore) * REACH_SLACK;
|
|
5264
|
+
return Math.sqrt(rig.upper * rig.upper + rig.fore * rig.fore + 2 * rig.upper * rig.fore * Math.cos(bend));
|
|
5265
|
+
};
|
|
4953
5266
|
const hip = new THREE.Vector3(...rig.hip);
|
|
4954
5267
|
const euler = new THREE.Euler(0, 0, 0, "YZX");
|
|
4955
5268
|
const quaternionOf = (r) => new THREE.Quaternion().setFromEuler(euler.set(r[0], r[1], r[2], "YZX"));
|
|
@@ -5039,15 +5352,45 @@ function createFirstPersonArms(THREE, options = {}) {
|
|
|
5039
5352
|
out.scale = base.scale;
|
|
5040
5353
|
return out;
|
|
5041
5354
|
};
|
|
5042
|
-
const
|
|
5355
|
+
const elbowAt = new THREE.Vector3();
|
|
5356
|
+
const viewOf = (camera) => {
|
|
5357
|
+
const lens = camera;
|
|
5358
|
+
if (!lens.isPerspectiveCamera) return null;
|
|
5359
|
+
const tanV = Math.tan(lens.fov * Math.PI / 360);
|
|
5360
|
+
return { tanV, tanH: tanV * lens.aspect };
|
|
5361
|
+
};
|
|
5362
|
+
const placeElbowInView = (sign, view) => {
|
|
5363
|
+
const way = rig.elbowView?.[sign === 1 ? 0 : 1];
|
|
5364
|
+
if (!way) return false;
|
|
5365
|
+
elbowAt.set(way[0], way[1], way[2]).setLength(rig.fore).add(wristAt);
|
|
5366
|
+
for (let pass = 0; pass < ELBOW_VIEW_PASSES; pass++) {
|
|
5367
|
+
const depth = Math.max(0.05, -elbowAt.z);
|
|
5368
|
+
const high = view.tanV * ELBOW_VIEW_MARGIN * depth;
|
|
5369
|
+
const wide = view.tanH * ELBOW_VIEW_MARGIN * depth;
|
|
5370
|
+
elbowAt.y = Math.max(elbowAt.y, -high);
|
|
5371
|
+
elbowAt.x = Math.min(wide, Math.max(-wide, elbowAt.x));
|
|
5372
|
+
elbowAt.sub(wristAt).setLength(rig.fore).add(wristAt);
|
|
5373
|
+
}
|
|
5374
|
+
shoulder.sub(elbowAt).setLength(rig.upper).add(elbowAt);
|
|
5375
|
+
return true;
|
|
5376
|
+
};
|
|
5377
|
+
const drawArm = (arm, pose, view) => {
|
|
5043
5378
|
copyPose(arm.drawn, pose);
|
|
5044
5379
|
const scale = pose.scale;
|
|
5045
5380
|
fingers.set(1, 0, 0).applyQuaternion(pose.q);
|
|
5046
5381
|
wristAt.copy(pose.p).addScaledVector(fingers, -rig.wrist * scale);
|
|
5047
5382
|
shoulderOf(arm.sign, shoulder).add(bobOffset);
|
|
5048
|
-
|
|
5383
|
+
if (view && placeElbowInView(arm.sign, view)) {
|
|
5384
|
+
solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, towards.copy(elbowAt).sub(shoulder).add(elbowAt).sub(wristAt), bone);
|
|
5385
|
+
finishArm(arm, pose, scale);
|
|
5386
|
+
return;
|
|
5387
|
+
}
|
|
5388
|
+
const excess = shoulder.distanceTo(wristAt) - reachOf(arm.sign);
|
|
5049
5389
|
if (excess > 0 && rig.reachShift > 0) shoulder.addScaledVector(towards.subVectors(wristAt, shoulder).normalize(), Math.min(excess, rig.reachShift));
|
|
5050
5390
|
solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, poleOf(arm.sign), bone);
|
|
5391
|
+
finishArm(arm, pose, scale);
|
|
5392
|
+
};
|
|
5393
|
+
const finishArm = (arm, pose, scale) => {
|
|
5051
5394
|
palmAt.copy(bone.end).addScaledVector(fingers, rig.wrist * scale);
|
|
5052
5395
|
arm.drawn.p.copy(palmAt);
|
|
5053
5396
|
arm.anchor.position.copy(palmAt);
|
|
@@ -5159,7 +5502,7 @@ function createFirstPersonArms(THREE, options = {}) {
|
|
|
5159
5502
|
dropped.p.y -= AWAY_DROP * ease(arm.away);
|
|
5160
5503
|
pose = dropped;
|
|
5161
5504
|
}
|
|
5162
|
-
drawArm(arm, pose);
|
|
5505
|
+
drawArm(arm, pose, mode === "target" && targets?.elbows !== "body" ? viewOf(state.camera) : null);
|
|
5163
5506
|
const shown = arm.away < AWAY_HIDDEN;
|
|
5164
5507
|
for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
|
|
5165
5508
|
}
|
|
@@ -5207,7 +5550,7 @@ var impactOf = (clip) => clip.events?.find((event) => event.type === "impact")?.
|
|
|
5207
5550
|
var isPunch = (clip) => clip.events?.some((event) => event.type === "impact" && event.data?.kind === "punch") ?? false;
|
|
5208
5551
|
var GUARD_SPEED = 0.3;
|
|
5209
5552
|
var toRoomAvatar = (avatar, options = {}) => {
|
|
5210
|
-
const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
|
|
5553
|
+
const arms = options.firstPerson === false || options.firstPerson === void 0 ? null : createFirstPersonArms(avatar.three, {
|
|
5211
5554
|
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 },
|
|
5212
5555
|
clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
|
|
5213
5556
|
hideWhenIdle: true,
|
|
@@ -5228,7 +5571,7 @@ var toRoomAvatar = (avatar, options = {}) => {
|
|
|
5228
5571
|
let guarding = false;
|
|
5229
5572
|
const stepGuard = (dt, state) => {
|
|
5230
5573
|
if (!guard) return;
|
|
5231
|
-
const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.itemUse && !state.running;
|
|
5574
|
+
const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.door && !state.itemUse && !state.running;
|
|
5232
5575
|
if (strikeLeft > 0) {
|
|
5233
5576
|
strikeLeft -= dt;
|
|
5234
5577
|
if (strikeLeft <= 0 && guardLeft > 0 && free) {
|
|
@@ -5247,7 +5590,8 @@ var toRoomAvatar = (avatar, options = {}) => {
|
|
|
5247
5590
|
root: avatar.root,
|
|
5248
5591
|
avatar,
|
|
5249
5592
|
arms,
|
|
5250
|
-
|
|
5593
|
+
eye: (out) => avatar.eye(out),
|
|
5594
|
+
setView: (mode, camera) => avatar.setView(mode, camera),
|
|
5251
5595
|
update: (dt, state) => {
|
|
5252
5596
|
stepGuard(dt, state);
|
|
5253
5597
|
avatar.update(dt, {
|
|
@@ -5259,10 +5603,14 @@ var toRoomAvatar = (avatar, options = {}) => {
|
|
|
5259
5603
|
pitch: -state.pitch,
|
|
5260
5604
|
...state.holding !== void 0 ? { holding: state.holding } : {},
|
|
5261
5605
|
...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {},
|
|
5262
|
-
...state.push !== void 0 ? { push: state.push } : {}
|
|
5606
|
+
...state.push !== void 0 ? { push: state.push } : {},
|
|
5607
|
+
...state.seat !== void 0 ? { seat: state.seat } : {},
|
|
5608
|
+
...state.door !== void 0 ? { door: state.door } : {}
|
|
5263
5609
|
});
|
|
5264
5610
|
},
|
|
5265
5611
|
hand: (side) => avatar.hand(side),
|
|
5612
|
+
shoulder: (side) => avatar.shoulder(side),
|
|
5613
|
+
reach: (targets, view) => avatar.reach(targets, view),
|
|
5266
5614
|
strike: (clipId, strikeOptions) => {
|
|
5267
5615
|
const clip = strikes.get(clipId);
|
|
5268
5616
|
if (!clip) return null;
|