reze-engine 0.36.1 → 0.38.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 +10 -2
- package/dist/camera.d.ts +17 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +25 -0
- package/dist/engine.d.ts +99 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +259 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/locomotion.d.ts +7 -0
- package/dist/locomotion.d.ts.map +1 -1
- package/dist/locomotion.js +27 -0
- package/dist/model.d.ts +107 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +250 -0
- package/dist/physics/autofit.d.ts +147 -0
- package/dist/physics/autofit.d.ts.map +1 -0
- package/dist/physics/autofit.js +501 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +84 -44
- package/package.json +1 -1
- package/src/camera.ts +26 -0
- package/src/engine.ts +314 -18
- package/src/index.ts +12 -1
- package/src/locomotion.ts +28 -0
- package/src/model.ts +295 -1
- package/src/pmx-loader.ts +84 -44
package/dist/engine.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Camera } from "./camera";
|
|
2
2
|
import { Mat4, Quat, Vec3 } from "./math";
|
|
3
|
+
import { MATERIAL_MORPH_MULTIPLY } from "./model";
|
|
3
4
|
import { MORPH_COMPUTE_WGSL } from "./shaders/passes/morph";
|
|
4
5
|
import { decodeTga } from "./tga-loader";
|
|
5
6
|
import { VMDLoader } from "./vmd-loader";
|
|
@@ -2457,6 +2458,15 @@ export class Engine {
|
|
|
2457
2458
|
setCameraBeta(b) {
|
|
2458
2459
|
this.camera.beta = b;
|
|
2459
2460
|
}
|
|
2461
|
+
/** Vertical field of view in radians (default π/4). While a camera VMD
|
|
2462
|
+
* drives the view it animates fov itself; the orbit value set here is
|
|
2463
|
+
* restored when the VMD releases the camera. */
|
|
2464
|
+
getCameraFov() {
|
|
2465
|
+
return this.camera.fov;
|
|
2466
|
+
}
|
|
2467
|
+
setCameraFov(fov) {
|
|
2468
|
+
this.camera.fov = fov;
|
|
2469
|
+
}
|
|
2460
2470
|
// Step 5: Create lighting buffers
|
|
2461
2471
|
setupLighting() {
|
|
2462
2472
|
this.lightUniformBuffer = this.device.createBuffer({
|
|
@@ -2639,7 +2649,29 @@ export class Engine {
|
|
|
2639
2649
|
await this.addModel(model, pmxPath, name);
|
|
2640
2650
|
return model;
|
|
2641
2651
|
}
|
|
2642
|
-
|
|
2652
|
+
/** loadModel's folder/zip path for a stage. Shares the whole prelude — only
|
|
2653
|
+
* what the PMX becomes differs. */
|
|
2654
|
+
async loadStage(name, options) {
|
|
2655
|
+
const { model, pmxKey, reader } = await this.openPmxFromFiles(name, options);
|
|
2656
|
+
await this.addStage(model, pmxKey, { name, transform: options.transform, assetReader: reader });
|
|
2657
|
+
return model;
|
|
2658
|
+
}
|
|
2659
|
+
/** Read a PMX out of a picked folder / expanded zip. Shared by loadModel and
|
|
2660
|
+
* loadStage so the file-map and path handling exist in exactly one place. */
|
|
2661
|
+
async openPmxFromFiles(name, options) {
|
|
2662
|
+
const pmxFile = options.pmxFile ?? findFirstPmxFileInList(options.files);
|
|
2663
|
+
if (!pmxFile)
|
|
2664
|
+
throw new Error("No .pmx file found in the selected folder");
|
|
2665
|
+
const map = fileListToMap(options.files);
|
|
2666
|
+
// `||`, not `??`: flat-picked files carry webkitRelativePath === "" (see
|
|
2667
|
+
// fileListToMap) — `""` must fall through to the filename.
|
|
2668
|
+
const pmxKey = normalizeAssetPath(pmxFile.webkitRelativePath || pmxFile.name);
|
|
2669
|
+
const reader = createFileMapAssetReader(map);
|
|
2670
|
+
const model = await PmxLoader.loadFromReader(reader, pmxKey);
|
|
2671
|
+
model.setName(name);
|
|
2672
|
+
return { model, pmxKey, reader };
|
|
2673
|
+
}
|
|
2674
|
+
async addModel(model, pmxPath, name, assetReader, options) {
|
|
2643
2675
|
const requested = name ?? model.name;
|
|
2644
2676
|
let key = requested;
|
|
2645
2677
|
let n = 1;
|
|
@@ -2649,9 +2681,42 @@ export class Engine {
|
|
|
2649
2681
|
const reader = assetReader ?? createFetchAssetReader();
|
|
2650
2682
|
const basePath = deriveBasePathFromPmxPath(pmxPath);
|
|
2651
2683
|
model.setAssetContext(reader, basePath);
|
|
2652
|
-
await this.setupModelInstance(key, model, basePath, reader);
|
|
2684
|
+
await this.setupModelInstance(key, model, basePath, reader, options?.stage ?? false);
|
|
2685
|
+
return key;
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Add a PMX as the scene's environment rather than as a character.
|
|
2689
|
+
*
|
|
2690
|
+
* A stage is the same geometry and the same materials — style groups and
|
|
2691
|
+
* shader graphs work on it unchanged, which is the whole reason pure-PMX
|
|
2692
|
+
* stages are worth supporting — but it is not a performer:
|
|
2693
|
+
*
|
|
2694
|
+
* - no physics. A stage's rigidbodies are set dressing for MMD's solver and
|
|
2695
|
+
* cost a full simulation island for scenery that never moves.
|
|
2696
|
+
* - no IK. Nothing drives a stage's chains, and solving them every frame is
|
|
2697
|
+
* pure waste on what is usually the heaviest mesh in the scene.
|
|
2698
|
+
* - no per-frame pose work while it is idle: with no clip and no morph
|
|
2699
|
+
* change there is nothing to recompute, so update is skipped entirely.
|
|
2700
|
+
* - it owns the floor. See groundIsSuppressed — the built-in ground plane
|
|
2701
|
+
* and a stage's own floor both sit at y=0 and z-fight.
|
|
2702
|
+
*
|
|
2703
|
+
* Bone and material morphs still apply, because that is how a stage's doors,
|
|
2704
|
+
* lifts and colour switches are rigged.
|
|
2705
|
+
*/
|
|
2706
|
+
async addStage(model, pmxPath, options) {
|
|
2707
|
+
const key = await this.addModel(model, pmxPath, options?.name, options?.assetReader, { stage: true });
|
|
2708
|
+
if (options?.transform)
|
|
2709
|
+
this.setModelTransform(key, options.transform);
|
|
2653
2710
|
return key;
|
|
2654
2711
|
}
|
|
2712
|
+
/** True while a stage is in the scene, which is when the built-in ground plane
|
|
2713
|
+
* must not draw. */
|
|
2714
|
+
groundIsSuppressed() {
|
|
2715
|
+
for (const inst of this.modelInstances.values())
|
|
2716
|
+
if (inst.isStage)
|
|
2717
|
+
return true;
|
|
2718
|
+
return false;
|
|
2719
|
+
}
|
|
2655
2720
|
removeModel(name) {
|
|
2656
2721
|
const inst = this.modelInstances.get(name);
|
|
2657
2722
|
if (!inst)
|
|
@@ -2699,8 +2764,9 @@ export class Engine {
|
|
|
2699
2764
|
* character — its colliders won't scale; scale stages (which are typically physics-free).
|
|
2700
2765
|
*/
|
|
2701
2766
|
setModelTransform(name, transform) {
|
|
2702
|
-
const
|
|
2703
|
-
|
|
2767
|
+
const inst = this.modelInstances.get(name);
|
|
2768
|
+
const model = inst?.model;
|
|
2769
|
+
if (!inst || !model)
|
|
2704
2770
|
return;
|
|
2705
2771
|
if (transform.position)
|
|
2706
2772
|
model.setPosition(transform.position);
|
|
@@ -2710,6 +2776,11 @@ export class Engine {
|
|
|
2710
2776
|
model.setScale(transform.scale);
|
|
2711
2777
|
if (transform.visible !== undefined)
|
|
2712
2778
|
model.setVisible(transform.visible);
|
|
2779
|
+
// The root transform is baked into the skin matrices, so moving a model is a
|
|
2780
|
+
// reason to re-upload them even though no pose pass ran. A cast member gets
|
|
2781
|
+
// one every frame anyway; an idle stage would otherwise never see the change
|
|
2782
|
+
// — which is exactly the case this API exists to serve.
|
|
2783
|
+
inst.skinMatricesDirty = true;
|
|
2713
2784
|
}
|
|
2714
2785
|
/** Read a model's scene transform (for serialization into a scene descriptor). */
|
|
2715
2786
|
getModelTransform(name) {
|
|
@@ -2858,8 +2929,22 @@ export class Engine {
|
|
|
2858
2929
|
let physicsMs = 0;
|
|
2859
2930
|
this.forEachInstance((inst) => {
|
|
2860
2931
|
const tAnim = performance.now();
|
|
2861
|
-
|
|
2932
|
+
// A stage never solves IK — nothing drives its chains — and skips the pose
|
|
2933
|
+
// pass entirely while it is idle. Morph changes still come through, since
|
|
2934
|
+
// that is the one thing a stage's controls do move.
|
|
2935
|
+
const stageIdle = inst.isStage && inst.model.isIdle();
|
|
2936
|
+
let verticesChanged = false;
|
|
2937
|
+
if (!stageIdle) {
|
|
2938
|
+
verticesChanged = inst.model.update(deltaTime, inst.isStage ? false : this.ikEnabled);
|
|
2939
|
+
inst.skinMatricesDirty = true;
|
|
2940
|
+
}
|
|
2862
2941
|
animMs += performance.now() - tAnim;
|
|
2942
|
+
// Material morphs ride the same weight change as vertex morphs but land in
|
|
2943
|
+
// uniform buffers, so they consume their own flag — a model whose only
|
|
2944
|
+
// morphs are material morphs never enters the GPU vertex path below.
|
|
2945
|
+
if (inst.materialMorphTargets && inst.model.consumeAuxMorphDirty()) {
|
|
2946
|
+
this.applyMaterialMorphs(inst);
|
|
2947
|
+
}
|
|
2863
2948
|
if (inst.gpuMorph) {
|
|
2864
2949
|
// GPU path: on a weight change, upload effective weights (thresholding tiny values
|
|
2865
2950
|
// to 0 to match the CPU skip) and flag the compute dispatch for this frame.
|
|
@@ -2935,7 +3020,7 @@ export class Engine {
|
|
|
2935
3020
|
if (pass)
|
|
2936
3021
|
pass.end();
|
|
2937
3022
|
}
|
|
2938
|
-
async setupModelInstance(name, model, basePath, assetReader) {
|
|
3023
|
+
async setupModelInstance(name, model, basePath, assetReader, isStage = false) {
|
|
2939
3024
|
const vertices = model.getVertices();
|
|
2940
3025
|
const skinning = model.getSkinning();
|
|
2941
3026
|
const skeleton = model.getSkeleton();
|
|
@@ -2975,7 +3060,10 @@ export class Engine {
|
|
|
2975
3060
|
});
|
|
2976
3061
|
this.device.queue.writeBuffer(indexBuffer, 0, indices);
|
|
2977
3062
|
const rbs = model.getRigidbodies();
|
|
2978
|
-
|
|
3063
|
+
// A stage never simulates, so its bodies are never built — constructing the
|
|
3064
|
+
// solver for the heaviest mesh in the scene and dropping it afterwards was
|
|
3065
|
+
// both wasted work and an invariant maintained in the wrong place.
|
|
3066
|
+
const physics = !isStage && rbs.length > 0 ? new RezePhysics(rbs, model.getJoints()) : null;
|
|
2979
3067
|
// Adopt the scene's air, or a model added mid-session would fall under
|
|
2980
3068
|
// different gravity from the ones already on stage.
|
|
2981
3069
|
if (physics) {
|
|
@@ -3022,7 +3110,13 @@ export class Engine {
|
|
|
3022
3110
|
mainPerInstanceBindGroup,
|
|
3023
3111
|
pickPerInstanceBindGroup,
|
|
3024
3112
|
pickDrawCalls: [],
|
|
3113
|
+
isStage,
|
|
3114
|
+
// Seeded true: the bind pose has to reach the GPU once before any frame.
|
|
3115
|
+
skinMatricesDirty: true,
|
|
3025
3116
|
hiddenMaterials: new Set(),
|
|
3117
|
+
morphHiddenMaterials: new Set(),
|
|
3118
|
+
materialMorphTargets: null,
|
|
3119
|
+
materialMorphByIndex: null,
|
|
3026
3120
|
physics,
|
|
3027
3121
|
vertexBufferNeedsUpdate: false,
|
|
3028
3122
|
gpuMorph,
|
|
@@ -3247,9 +3341,28 @@ export class Engine {
|
|
|
3247
3341
|
const meshIndices = model.getIndices();
|
|
3248
3342
|
// 頭 bone index for the eye shader's rear-view gate (-1 when absent).
|
|
3249
3343
|
const headBoneIndex = model.getSkeleton().bones.findIndex((b) => b.name === "頭");
|
|
3344
|
+
// Materials a type-8 morph can reach. -1 in an offset means "all of them",
|
|
3345
|
+
// so the presence of ANY material morph makes every material a target.
|
|
3346
|
+
const morphedMaterials = new Set();
|
|
3347
|
+
for (const morph of model.getMorphing().morphs) {
|
|
3348
|
+
if (morph.type !== 8 || !morph.materialOffsets)
|
|
3349
|
+
continue;
|
|
3350
|
+
for (const off of morph.materialOffsets) {
|
|
3351
|
+
if (off.materialIndex < 0)
|
|
3352
|
+
for (let i = 0; i < materials.length; i++)
|
|
3353
|
+
morphedMaterials.add(i);
|
|
3354
|
+
else
|
|
3355
|
+
morphedMaterials.add(off.materialIndex);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
const morphTargets = [];
|
|
3250
3359
|
let currentIndexOffset = 0;
|
|
3251
3360
|
let materialId = 0;
|
|
3361
|
+
// The PMX index, which is what a material morph points at — distinct from
|
|
3362
|
+
// materialId, which only counts materials that produced a draw.
|
|
3363
|
+
let pmxMaterialIndex = -1;
|
|
3252
3364
|
for (const mat of materials) {
|
|
3365
|
+
pmxMaterialIndex++;
|
|
3253
3366
|
const indexCount = mat.vertexCount;
|
|
3254
3367
|
if (indexCount === 0)
|
|
3255
3368
|
continue;
|
|
@@ -3299,6 +3412,19 @@ export class Engine {
|
|
|
3299
3412
|
}
|
|
3300
3413
|
const materialUniformBuffer = this.createMaterialUniformBuffer(prefix + mat.name, mat, sphereMode, headBoneIndex);
|
|
3301
3414
|
inst.gpuBuffers.push(materialUniformBuffer);
|
|
3415
|
+
if (morphedMaterials.has(pmxMaterialIndex)) {
|
|
3416
|
+
const base = this.materialUniformData(mat, sphereMode, headBoneIndex);
|
|
3417
|
+
morphTargets.push({
|
|
3418
|
+
pmxIndex: pmxMaterialIndex,
|
|
3419
|
+
materialName: mat.name,
|
|
3420
|
+
buffer: materialUniformBuffer,
|
|
3421
|
+
base,
|
|
3422
|
+
work: new Float32Array(base.length),
|
|
3423
|
+
// Seeded from base: that is what createMaterialUniformBuffer already
|
|
3424
|
+
// uploaded, so an unmorphed material never writes a first time.
|
|
3425
|
+
last: Float32Array.from(base),
|
|
3426
|
+
});
|
|
3427
|
+
}
|
|
3302
3428
|
const textureView = diffuseTexture.createView();
|
|
3303
3429
|
const baseBindGroupEntries = [
|
|
3304
3430
|
{ binding: 0, resource: textureView },
|
|
@@ -3314,8 +3440,13 @@ export class Engine {
|
|
|
3314
3440
|
// its own hull where it is see-through instead of us skipping it here.
|
|
3315
3441
|
// Drawn interleaved right after this material's color draw (babylon-mmd's
|
|
3316
3442
|
// per-mesh afterRender outline stage) — see drawMaterials.
|
|
3443
|
+
// Stages get no outline hulls. The inverted hull is a SECOND full draw of
|
|
3444
|
+
// the material's geometry, and stage PMX routinely set the edge flag across
|
|
3445
|
+
// every material — on the heaviest mesh in the scene that doubles the
|
|
3446
|
+
// geometry submitted per frame to draw cartoon outlines around
|
|
3447
|
+
// architecture, which is not the look anyone is after.
|
|
3317
3448
|
let outline;
|
|
3318
|
-
if ((mat.edgeFlag & 0x10) !== 0 && mat.edgeSize > 0) {
|
|
3449
|
+
if (!inst.isStage && (mat.edgeFlag & 0x10) !== 0 && mat.edgeSize > 0) {
|
|
3319
3450
|
const materialUniformData = new Float32Array([
|
|
3320
3451
|
mat.edgeColor[0],
|
|
3321
3452
|
mat.edgeColor[1],
|
|
@@ -3371,10 +3502,17 @@ export class Engine {
|
|
|
3371
3502
|
// by render-class when groups are assigned. Array.sort is stable → PMX order preserved
|
|
3372
3503
|
// within a bucket.
|
|
3373
3504
|
this.sortDrawCalls(inst);
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3505
|
+
inst.materialMorphTargets = morphTargets.length > 0 ? morphTargets : null;
|
|
3506
|
+
inst.materialMorphByIndex = inst.materialMorphTargets
|
|
3507
|
+
? new Map(morphTargets.map((t) => [t.pmxIndex, t]))
|
|
3508
|
+
: null;
|
|
3509
|
+
// Seed from the current weights: a scene can open with a switch already on.
|
|
3510
|
+
if (inst.materialMorphTargets)
|
|
3511
|
+
this.applyMaterialMorphs(inst);
|
|
3512
|
+
}
|
|
3513
|
+
/** Matches the WGSL MaterialUniforms struct in common.ts — 64 bytes
|
|
3514
|
+
* (diffuse+alpha | ambient+shininess | specular+sphereMode | headIdx+pad). */
|
|
3515
|
+
materialUniformData(mat, sphereMode, headBoneIndex) {
|
|
3378
3516
|
const data = new Float32Array(16);
|
|
3379
3517
|
data[0] = mat.diffuse[0];
|
|
3380
3518
|
data[1] = mat.diffuse[1];
|
|
@@ -3389,7 +3527,102 @@ export class Engine {
|
|
|
3389
3527
|
data[10] = mat.specular[2];
|
|
3390
3528
|
data[11] = sphereMode;
|
|
3391
3529
|
data[12] = headBoneIndex;
|
|
3392
|
-
return
|
|
3530
|
+
return data;
|
|
3531
|
+
}
|
|
3532
|
+
createMaterialUniformBuffer(label, mat, sphereMode, headBoneIndex) {
|
|
3533
|
+
return this.createUniformBuffer(`material uniform: ${label}`, this.materialUniformData(mat, sphereMode, headBoneIndex));
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Re-derive every morph-targeted material's uniform block from base and push
|
|
3537
|
+
* the ones that moved.
|
|
3538
|
+
*
|
|
3539
|
+
* Blend maths follow MMD (and babylon-mmd's _applyMaterialMorph): multiply
|
|
3540
|
+
* lerps from base toward base*morph, add offsets from base. Weight 0 must
|
|
3541
|
+
* therefore land exactly on base, which is why this recomputes rather than
|
|
3542
|
+
* accumulates.
|
|
3543
|
+
*
|
|
3544
|
+
* A material driven to zero alpha is dropped from the draw instead of being
|
|
3545
|
+
* written through: the opaque/transparent bucket is decided at load from the
|
|
3546
|
+
* PMX alpha, so an opaque draw cannot become see-through by uniform alone.
|
|
3547
|
+
* Full-off is the switch stage artists actually ship (帽子消失 and friends);
|
|
3548
|
+
* a partial fade on a material that loaded opaque still will not blend.
|
|
3549
|
+
*/
|
|
3550
|
+
applyMaterialMorphs(inst) {
|
|
3551
|
+
const targets = inst.materialMorphTargets;
|
|
3552
|
+
if (!targets)
|
|
3553
|
+
return;
|
|
3554
|
+
const morphs = inst.model.getMorphing().morphs;
|
|
3555
|
+
const weights = inst.model.getEffectiveMorphWeights();
|
|
3556
|
+
for (const target of targets) {
|
|
3557
|
+
target.work.set(target.base);
|
|
3558
|
+
}
|
|
3559
|
+
for (let i = 0; i < morphs.length; i++) {
|
|
3560
|
+
const w = weights[i];
|
|
3561
|
+
if (w < 0.0001)
|
|
3562
|
+
continue;
|
|
3563
|
+
const morph = morphs[i];
|
|
3564
|
+
if (morph.type !== 8 || !morph.materialOffsets)
|
|
3565
|
+
continue;
|
|
3566
|
+
for (const off of morph.materialOffsets) {
|
|
3567
|
+
// A named material resolves in one lookup. Only the -1 wildcard walks
|
|
3568
|
+
// every target — and once any offset uses it, every material in the
|
|
3569
|
+
// model is a target, so scanning per offset would be quadratic on the
|
|
3570
|
+
// large stages this is meant to serve.
|
|
3571
|
+
const hit = off.materialIndex >= 0 ? inst.materialMorphByIndex?.get(off.materialIndex) : undefined;
|
|
3572
|
+
const affected = off.materialIndex >= 0 ? (hit ? [hit] : []) : targets;
|
|
3573
|
+
for (const target of affected) {
|
|
3574
|
+
const d = target.work;
|
|
3575
|
+
if (off.offsetType === MATERIAL_MORPH_MULTIPLY) {
|
|
3576
|
+
d[0] += (d[0] * off.diffuse[0] - d[0]) * w;
|
|
3577
|
+
d[1] += (d[1] * off.diffuse[1] - d[1]) * w;
|
|
3578
|
+
d[2] += (d[2] * off.diffuse[2] - d[2]) * w;
|
|
3579
|
+
d[3] += (d[3] * off.diffuse[3] - d[3]) * w;
|
|
3580
|
+
d[4] += (d[4] * off.ambient[0] - d[4]) * w;
|
|
3581
|
+
d[5] += (d[5] * off.ambient[1] - d[5]) * w;
|
|
3582
|
+
d[6] += (d[6] * off.ambient[2] - d[6]) * w;
|
|
3583
|
+
d[7] += (d[7] * off.shininess - d[7]) * w;
|
|
3584
|
+
d[8] += (d[8] * off.specular[0] - d[8]) * w;
|
|
3585
|
+
d[9] += (d[9] * off.specular[1] - d[9]) * w;
|
|
3586
|
+
d[10] += (d[10] * off.specular[2] - d[10]) * w;
|
|
3587
|
+
}
|
|
3588
|
+
else {
|
|
3589
|
+
d[0] += off.diffuse[0] * w;
|
|
3590
|
+
d[1] += off.diffuse[1] * w;
|
|
3591
|
+
d[2] += off.diffuse[2] * w;
|
|
3592
|
+
d[3] += off.diffuse[3] * w;
|
|
3593
|
+
d[4] += off.ambient[0] * w;
|
|
3594
|
+
d[5] += off.ambient[1] * w;
|
|
3595
|
+
d[6] += off.ambient[2] * w;
|
|
3596
|
+
d[7] += off.shininess * w;
|
|
3597
|
+
d[8] += off.specular[0] * w;
|
|
3598
|
+
d[9] += off.specular[1] * w;
|
|
3599
|
+
d[10] += off.specular[2] * w;
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
inst.morphHiddenMaterials.clear();
|
|
3605
|
+
for (const target of targets) {
|
|
3606
|
+
const d = target.work;
|
|
3607
|
+
// Alpha is the switch; clamp the rest so a stacked multiply cannot send a
|
|
3608
|
+
// colour negative and light the material from the inside.
|
|
3609
|
+
for (let k = 0; k < 11; k++)
|
|
3610
|
+
if (d[k] < 0)
|
|
3611
|
+
d[k] = 0;
|
|
3612
|
+
if (d[3] < 0.0001)
|
|
3613
|
+
inst.morphHiddenMaterials.add(target.materialName);
|
|
3614
|
+
let changed = false;
|
|
3615
|
+
for (let k = 0; k < 11; k++) {
|
|
3616
|
+
if (d[k] !== target.last[k]) {
|
|
3617
|
+
changed = true;
|
|
3618
|
+
break;
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
if (!changed)
|
|
3622
|
+
continue;
|
|
3623
|
+
target.last.set(d);
|
|
3624
|
+
this.device.queue.writeBuffer(target.buffer, 0, d);
|
|
3625
|
+
}
|
|
3393
3626
|
}
|
|
3394
3627
|
createUniformBuffer(label, data) {
|
|
3395
3628
|
const buffer = this.device.createBuffer({
|
|
@@ -3401,7 +3634,7 @@ export class Engine {
|
|
|
3401
3634
|
return buffer;
|
|
3402
3635
|
}
|
|
3403
3636
|
shouldRenderDrawCall(inst, drawCall) {
|
|
3404
|
-
return !inst.hiddenMaterials.has(drawCall.materialName);
|
|
3637
|
+
return !inst.hiddenMaterials.has(drawCall.materialName) && !inst.morphHiddenMaterials.has(drawCall.materialName);
|
|
3405
3638
|
}
|
|
3406
3639
|
async createTextureFromLogicalPath(inst, logicalPath) {
|
|
3407
3640
|
const cacheKey = logicalPath;
|
|
@@ -3526,6 +3759,12 @@ export class Engine {
|
|
|
3526
3759
|
this.device.queue.submit([encoder.finish()]);
|
|
3527
3760
|
}
|
|
3528
3761
|
renderGround(pass) {
|
|
3762
|
+
// A stage brings its own floor. Both sit at y=0, so drawing the built-in
|
|
3763
|
+
// plane underneath produces z-fighting across the whole scene — enforced
|
|
3764
|
+
// here rather than left to callers, who cannot see the conflict coming.
|
|
3765
|
+
// hasGround is left alone: remove the stage and the ground comes back.
|
|
3766
|
+
if (this.groundIsSuppressed())
|
|
3767
|
+
return;
|
|
3529
3768
|
if (!this.hasGround || !this.groundVertexBuffer || !this.groundIndexBuffer || !this.groundDrawCall)
|
|
3530
3769
|
return;
|
|
3531
3770
|
pass.setPipeline(this.groundShadowPipeline);
|
|
@@ -4594,8 +4833,14 @@ export class Engine {
|
|
|
4594
4833
|
}
|
|
4595
4834
|
updateSkinMatrices() {
|
|
4596
4835
|
this.forEachInstance((inst) => {
|
|
4836
|
+
// Only a pose pass can change these, and an idle stage did not run one —
|
|
4837
|
+
// re-uploading bones×64 bytes for scenery that never moves is the one
|
|
4838
|
+
// per-frame cost a stage would otherwise still pay in full.
|
|
4839
|
+
if (!inst.skinMatricesDirty)
|
|
4840
|
+
return;
|
|
4597
4841
|
const skinMatrices = inst.model.getSkinMatrices();
|
|
4598
4842
|
this.device.queue.writeBuffer(inst.skinMatrixBuffer, 0, skinMatrices.buffer, skinMatrices.byteOffset, skinMatrices.byteLength);
|
|
4843
|
+
inst.skinMatricesDirty = false;
|
|
4599
4844
|
});
|
|
4600
4845
|
}
|
|
4601
4846
|
// frameIntervalMs is the true vsync-to-vsync frame interval (render dt), NOT the CPU
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { BODY_GRAPH } from "./graph/presets/body";
|
|
|
14
14
|
export { STOCKINGS_GRAPH } from "./graph/presets/stockings";
|
|
15
15
|
export { EYE_GRAPH } from "./graph/presets/eye";
|
|
16
16
|
export { FACE_GRAPH } from "./graph/presets/face";
|
|
17
|
-
export { Model, type ClipEventInfo } from "./model";
|
|
17
|
+
export { Model, MATERIAL_MORPH_MULTIPLY, MATERIAL_MORPH_ADD, type ClipEventInfo, type RootMotionProfile, type Morph, type Morphing, type BoneMorphOffset, type MaterialMorphOffset, type UvMorphOffset, } from "./model";
|
|
18
18
|
export { Vec3, Quat, Mat4, easeInOut, type EulerOrder } from "./math";
|
|
19
19
|
export type { AnimationClip, AnimationPlayOptions, AnimationProgress, BlendEntry, BoneKeyframe, IkKeyframe, MorphKeyframe, BoneInterpolation, ControlPoint, } from "./animation";
|
|
20
20
|
export { LocomotionController, type LocomotionClips, type LocomotionOptions, type LocomotionPose, type StrafeClipEntry, type TurnClipEntry, type RunTurnClipEntry, type StopClipEntry, } from "./locomotion";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7G,YAAY,EACV,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EACL,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAA;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,GACX,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC7G,YAAY,EACV,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EACL,KAAK,EACL,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAA;AACrE,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,qBAAqB,EAAE,KAAK,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC5H,OAAO,EACL,GAAG,EACH,iBAAiB,EACjB,wBAAwB,EACxB,mCAAmC,GACpC,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { BODY_GRAPH } from "./graph/presets/body";
|
|
|
12
12
|
export { STOCKINGS_GRAPH } from "./graph/presets/stockings";
|
|
13
13
|
export { EYE_GRAPH } from "./graph/presets/eye";
|
|
14
14
|
export { FACE_GRAPH } from "./graph/presets/face";
|
|
15
|
-
export { Model } from "./model";
|
|
15
|
+
export { Model, MATERIAL_MORPH_MULTIPLY, MATERIAL_MORPH_ADD, } from "./model";
|
|
16
16
|
export { Vec3, Quat, Mat4, easeInOut } from "./math";
|
|
17
17
|
export { LocomotionController, } from "./locomotion";
|
|
18
18
|
export { AnimationStateMachine } from "./state-machine";
|
package/dist/locomotion.d.ts
CHANGED
|
@@ -177,6 +177,13 @@ export declare class LocomotionController {
|
|
|
177
177
|
setDrive(forward: number, steer: number, sprint?: boolean): void;
|
|
178
178
|
/** Place the character (initial spawn or respawn). */
|
|
179
179
|
teleport(x: number, y: number, z: number, yaw?: number): void;
|
|
180
|
+
/** Teleport PLUS a hard reset of every transient motion commitment: any
|
|
181
|
+
* in-flight authored stop/turn (whose stored start position would otherwise
|
|
182
|
+
* keep driving the root from where it began), the exit ghost, speed and
|
|
183
|
+
* momentum, the heading-hold gate, and the held inputs. For handing the
|
|
184
|
+
* root back after an externally-driven action (a root-motion clip state):
|
|
185
|
+
* wherever the action ended is simply where she now stands. */
|
|
186
|
+
reset(x: number, y: number, z: number, yaw?: number): void;
|
|
180
187
|
/** Strafe mode: hold the body at this world yaw (a camera forward, a lock-on target)
|
|
181
188
|
* while setMove's vector drives the directional strafe ring — requires
|
|
182
189
|
* clips.strafeRun. null returns to turn-toward-movement. */
|
package/dist/locomotion.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locomotion.d.ts","sourceRoot":"","sources":["../src/locomotion.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"locomotion.d.ts","sourceRoot":"","sources":["../src/locomotion.ts"],"names":[],"mappings":"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"}
|
package/dist/locomotion.js
CHANGED
|
@@ -113,6 +113,33 @@ export class LocomotionController {
|
|
|
113
113
|
this.position.setXYZ(x, y, z);
|
|
114
114
|
this.yaw = yaw;
|
|
115
115
|
}
|
|
116
|
+
/** Teleport PLUS a hard reset of every transient motion commitment: any
|
|
117
|
+
* in-flight authored stop/turn (whose stored start position would otherwise
|
|
118
|
+
* keep driving the root from where it began), the exit ghost, speed and
|
|
119
|
+
* momentum, the heading-hold gate, and the held inputs. For handing the
|
|
120
|
+
* root back after an externally-driven action (a root-motion clip state):
|
|
121
|
+
* wherever the action ended is simply where she now stands. */
|
|
122
|
+
reset(x, y, z, yaw = 0) {
|
|
123
|
+
this.position.setXYZ(x, y, z);
|
|
124
|
+
this.yaw = wrapAngle(yaw);
|
|
125
|
+
this.dirX = Math.sin(this.yaw);
|
|
126
|
+
this.dirZ = Math.cos(this.yaw);
|
|
127
|
+
this.speedLevel = 0;
|
|
128
|
+
this.recentSpeed = 0;
|
|
129
|
+
this.headingHold = 0;
|
|
130
|
+
this.headingDirX = 0;
|
|
131
|
+
this.headingDirY = 0;
|
|
132
|
+
this.gaitPhase = 0;
|
|
133
|
+
this.stopping = null;
|
|
134
|
+
this.turning = null;
|
|
135
|
+
this.runTurning = null;
|
|
136
|
+
this.exitGhost = null;
|
|
137
|
+
this.inputX = 0;
|
|
138
|
+
this.inputY = 0;
|
|
139
|
+
this.inputSprint = false;
|
|
140
|
+
this.inputForward = 0;
|
|
141
|
+
this.inputSteer = 0;
|
|
142
|
+
}
|
|
116
143
|
/** Strafe mode: hold the body at this world yaw (a camera forward, a lock-on target)
|
|
117
144
|
* while setMove's vector drives the directional strafe ring — requires
|
|
118
145
|
* clips.strafeRun. null returns to turn-toward-movement. */
|
package/dist/model.d.ts
CHANGED
|
@@ -9,6 +9,14 @@ export interface ClipEventInfo {
|
|
|
9
9
|
/** The clip's blend weight at the moment of firing. */
|
|
10
10
|
weight: number;
|
|
11
11
|
}
|
|
12
|
+
/** A clip's authored horizontal root path, lifted off センター by
|
|
13
|
+
* Model.extractRootMotion: per-key offsets from the FIRST key, raw clip
|
|
14
|
+
* units, clip space (rest facing -Z). `frames` are VMD frames (30fps). */
|
|
15
|
+
export interface RootMotionProfile {
|
|
16
|
+
frames: number[];
|
|
17
|
+
x: number[];
|
|
18
|
+
z: number[];
|
|
19
|
+
}
|
|
12
20
|
export interface Texture {
|
|
13
21
|
path: string;
|
|
14
22
|
name: string;
|
|
@@ -78,11 +86,44 @@ export interface GroupMorphReference {
|
|
|
78
86
|
morphIndex: number;
|
|
79
87
|
ratio: number;
|
|
80
88
|
}
|
|
89
|
+
export interface BoneMorphOffset {
|
|
90
|
+
boneIndex: number;
|
|
91
|
+
translation: [number, number, number];
|
|
92
|
+
/** Rotation quaternion (x, y, z, w). */
|
|
93
|
+
rotation: [number, number, number, number];
|
|
94
|
+
}
|
|
95
|
+
/** PMX material-morph blend mode. Multiply lerps toward base*morph; add offsets from base. */
|
|
96
|
+
export declare const MATERIAL_MORPH_MULTIPLY = 0;
|
|
97
|
+
export declare const MATERIAL_MORPH_ADD = 1;
|
|
98
|
+
export interface MaterialMorphOffset {
|
|
99
|
+
/** -1 targets EVERY material in the model, per the PMX spec. */
|
|
100
|
+
materialIndex: number;
|
|
101
|
+
/** MATERIAL_MORPH_MULTIPLY | MATERIAL_MORPH_ADD */
|
|
102
|
+
offsetType: number;
|
|
103
|
+
diffuse: [number, number, number, number];
|
|
104
|
+
specular: [number, number, number];
|
|
105
|
+
shininess: number;
|
|
106
|
+
ambient: [number, number, number];
|
|
107
|
+
edgeColor: [number, number, number, number];
|
|
108
|
+
edgeSize: number;
|
|
109
|
+
textureCoeff: [number, number, number, number];
|
|
110
|
+
sphereCoeff: [number, number, number, number];
|
|
111
|
+
toonCoeff: [number, number, number, number];
|
|
112
|
+
}
|
|
113
|
+
export interface UvMorphOffset {
|
|
114
|
+
vertexIndex: number;
|
|
115
|
+
/** (u, v, z, w) — only u/v apply to the base channel. */
|
|
116
|
+
offset: [number, number, number, number];
|
|
117
|
+
}
|
|
81
118
|
export interface Morph {
|
|
82
119
|
name: string;
|
|
120
|
+
/** 0=group, 1=vertex, 2=bone, 3–7=UV, 8=material, 9=flip, 10=impulse. */
|
|
83
121
|
type: number;
|
|
84
122
|
vertexOffsets: VertexMorphOffset[];
|
|
85
123
|
groupReferences?: GroupMorphReference[];
|
|
124
|
+
boneOffsets?: BoneMorphOffset[];
|
|
125
|
+
materialOffsets?: MaterialMorphOffset[];
|
|
126
|
+
uvOffsets?: UvMorphOffset[];
|
|
86
127
|
}
|
|
87
128
|
export interface Morphing {
|
|
88
129
|
morphs: Morph[];
|
|
@@ -146,6 +187,14 @@ export declare class Model {
|
|
|
146
187
|
private runtimeSkeleton;
|
|
147
188
|
private runtimeMorph;
|
|
148
189
|
private morphsDirty;
|
|
190
|
+
private boneMorphPlan;
|
|
191
|
+
private boneMorphBones;
|
|
192
|
+
private boneMorphRestoreR;
|
|
193
|
+
private boneMorphRestoreT;
|
|
194
|
+
private boneMorphApplied;
|
|
195
|
+
/** A full pose pass has run, so the world matrices are real. See isIdle. */
|
|
196
|
+
private posedOnce;
|
|
197
|
+
private auxMorphDirty;
|
|
149
198
|
private _position;
|
|
150
199
|
private _rotation;
|
|
151
200
|
private _scale;
|
|
@@ -189,6 +238,33 @@ export declare class Model {
|
|
|
189
238
|
private buildDeformOrder;
|
|
190
239
|
private initializeTweenBuffers;
|
|
191
240
|
private initializeRuntimeMorph;
|
|
241
|
+
/**
|
|
242
|
+
* Bone morphs (type 2) compose over whatever the pose sources produced, the
|
|
243
|
+
* same way boneRotationOffsets do — they are an offset on the animated local
|
|
244
|
+
* transform, not a replacement for it. Re-applied every frame because each
|
|
245
|
+
* pose source rewrites the locals it touches.
|
|
246
|
+
*
|
|
247
|
+
* Stages are the reason this exists: a door or a lift is rigged as a bone
|
|
248
|
+
* morph and there is no VMD anywhere that drives it.
|
|
249
|
+
*/
|
|
250
|
+
private applyBoneMorphs;
|
|
251
|
+
/** True (once) when effective morph weights changed — the engine re-derives
|
|
252
|
+
* material-morph uniforms from it. Separate from the GPU vertex path's flag
|
|
253
|
+
* so both can consume the same change. */
|
|
254
|
+
consumeAuxMorphDirty(): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Morph indices this model can actually act on, so a UI never offers a control
|
|
257
|
+
* that moves nothing.
|
|
258
|
+
*
|
|
259
|
+
* Driven directly: vertex (1), bone (2), material (8). Excluded: UV (3–7),
|
|
260
|
+
* which are parsed and kept but not yet applied; flip (9) and impulse (10),
|
|
261
|
+
* which are PMX 2.1 and would need the rigidbody solver.
|
|
262
|
+
*
|
|
263
|
+
* A group morph (0) is only as alive as what it points at — one referencing
|
|
264
|
+
* nothing but UV morphs is just as dead as the UV morphs themselves, so it is
|
|
265
|
+
* resolved rather than assumed.
|
|
266
|
+
*/
|
|
267
|
+
getSupportedMorphIndices(): number[];
|
|
192
268
|
private updateTweens;
|
|
193
269
|
getVertices(): Float32Array<ArrayBuffer>;
|
|
194
270
|
getTextures(): Texture[];
|
|
@@ -239,6 +315,24 @@ export declare class Model {
|
|
|
239
315
|
resetAllBones(): void;
|
|
240
316
|
resetAllMorphs(): void;
|
|
241
317
|
getClip(name: string): AnimationClip | null;
|
|
318
|
+
/** Lift a clip's authored horizontal root path off センター so the host can
|
|
319
|
+
* drive the MODEL ROOT along it — game-style root motion. The clip keeps
|
|
320
|
+
* its vertical bob but its horizontal センター flattens to `rest` (default
|
|
321
|
+
* 0,0): a displaced first frame — common in per-pose game exports — would
|
|
322
|
+
* otherwise ride the whole clip as a constant and visibly slide off during
|
|
323
|
+
* the exit crossfade. Pass the pack's NEUTRAL standing offset (the idle
|
|
324
|
+
* clip's first センター key) as `rest` so the flattened pose blends into
|
|
325
|
+
* the surrounding states without even a micro-slide. The removed path
|
|
326
|
+
* returns RELATIVE TO THE FIRST KEY, raw clip units, clip space (rest
|
|
327
|
+
* facing -Z). Leg-IK target position tracks lose the same path so feet
|
|
328
|
+
* keep oscillating around the body. Call once per clip, after
|
|
329
|
+
* loadVmd/loadClip; null if the clip or its センター track doesn't exist. */
|
|
330
|
+
extractRootMotion(name: string, rest?: {
|
|
331
|
+
x?: number;
|
|
332
|
+
z?: number;
|
|
333
|
+
}): RootMotionProfile | null;
|
|
334
|
+
/** Linear sample of a keyed scalar track at fractional frame f. */
|
|
335
|
+
private static sampleTrack;
|
|
242
336
|
exportVmd(name: string): ArrayBuffer;
|
|
243
337
|
play(): void;
|
|
244
338
|
play(name: string): boolean;
|
|
@@ -326,6 +420,19 @@ export declare class Model {
|
|
|
326
420
|
* animationState, already ticked by update), shape the weight with easeInOut,
|
|
327
421
|
* and hand both to the blend sampler. Holds in place while paused. */
|
|
328
422
|
private applyCrossfade;
|
|
423
|
+
/**
|
|
424
|
+
* Nothing can have moved this frame: no clip, no blend, no live tween, no
|
|
425
|
+
* morph weight change.
|
|
426
|
+
*
|
|
427
|
+
* Environment geometry is in this state almost every frame, so the engine
|
|
428
|
+
* skips the whole pose pass — sampling, world matrices, and the skin-matrix
|
|
429
|
+
* upload — for a stage that reports idle. A stage is usually the heaviest mesh
|
|
430
|
+
* in the scene and the one that never moves; paying a full pose pass for it
|
|
431
|
+
* every frame is the thing worth not doing.
|
|
432
|
+
*/
|
|
433
|
+
isIdle(): boolean;
|
|
434
|
+
/** Any live rotation / translation / morph tween. */
|
|
435
|
+
private hasActiveTweens;
|
|
329
436
|
update(deltaTime: number, ikEnabled?: boolean): boolean;
|
|
330
437
|
private solveIKChains;
|
|
331
438
|
private ikComputedSet;
|