reze-engine 0.39.0 → 0.41.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 +8 -5
- package/dist/engine.d.ts +96 -39
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +246 -85
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +10 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +0 -5
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -36
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +21 -0
- package/dist/shaders/passes/composite.d.ts +27 -12
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +191 -43
- package/package.json +1 -1
- package/src/engine.ts +286 -93
- package/src/graph/registry.ts +11 -0
- package/src/index.ts +4 -3
- package/src/model.ts +0 -36
- package/src/shaders/materials/nodes.ts +21 -0
- package/src/shaders/passes/composite.ts +218 -59
- package/dist/particles.d.ts +0 -43
- package/dist/particles.d.ts.map +0 -1
- package/dist/particles.js +0 -183
- package/dist/shaders/passes/particles.d.ts +0 -2
- package/dist/shaders/passes/particles.d.ts.map +0 -1
- package/dist/shaders/passes/particles.js +0 -99
- package/src/particles.ts +0 -232
- package/src/shaders/passes/particles.ts +0 -100
package/dist/engine.js
CHANGED
|
@@ -5,7 +5,6 @@ import { MORPH_COMPUTE_WGSL } from "./shaders/passes/morph";
|
|
|
5
5
|
import { decodeTga } from "./tga-loader";
|
|
6
6
|
import { VMDLoader } from "./vmd-loader";
|
|
7
7
|
import { CameraAnimation } from "./camera-animation";
|
|
8
|
-
import { ParticleSystem } from "./particles";
|
|
9
8
|
import { PmxLoader } from "./pmx-loader";
|
|
10
9
|
import { RezePhysics } from "./physics";
|
|
11
10
|
import { createFetchAssetReader, createFileMapAssetReader, deriveBasePathFromPmxPath, fileListToMap, findFirstPmxFileInList, joinAssetPath, normalizeAssetPath, } from "./asset-reader";
|
|
@@ -145,6 +144,16 @@ export const DEFAULT_BLOOM_OPTIONS = {
|
|
|
145
144
|
intensity: 0.05,
|
|
146
145
|
clamp: 0.0,
|
|
147
146
|
};
|
|
147
|
+
export const DEFAULT_DEPTH_OF_FIELD_OPTIONS = {
|
|
148
|
+
enabled: false,
|
|
149
|
+
focusMode: "auto",
|
|
150
|
+
focusDistance: 25,
|
|
151
|
+
focusRange: 2,
|
|
152
|
+
aperture: 1,
|
|
153
|
+
maxBlurRadius: 18,
|
|
154
|
+
bladeCount: 6,
|
|
155
|
+
quality: "balanced",
|
|
156
|
+
};
|
|
148
157
|
// Matches the reference Blender project: Filmic view, Medium High Contrast look,
|
|
149
158
|
// exposure 0.3, gamma 1.0, sRGB display, no curves.
|
|
150
159
|
export const DEFAULT_VIEW_TRANSFORM = {
|
|
@@ -287,22 +296,29 @@ export class Engine {
|
|
|
287
296
|
// the fragment shader and treats missing dst.a as 1, so the blend math is
|
|
288
297
|
// unchanged).
|
|
289
298
|
this.hdrFormat = "rgba16float";
|
|
290
|
-
|
|
291
|
-
this.
|
|
299
|
+
this.depthOfField = { ...DEFAULT_DEPTH_OF_FIELD_OPTIONS };
|
|
300
|
+
this.dofUniformData = new Float32Array(12);
|
|
301
|
+
this.dofFocusScratch = new Vec3(0, 0, 0);
|
|
302
|
+
/** Depth-only view of the scene's MSAA depth buffer, read by the DoF gather. */
|
|
303
|
+
this.depthReadView = null;
|
|
292
304
|
// [exposure, invGamma, _, _, bloomTint.x, bloomTint.y, bloomTint.z, bloomIntensity]
|
|
293
|
-
|
|
305
|
+
// 11 × vec4f — see the viewU comment in composite.ts. The last one is the
|
|
306
|
+
// camera's world position, which is what lets a foreground effect turn the
|
|
307
|
+
// depth it is handed into a PLACE (bgWorldPos) rather than a distance.
|
|
308
|
+
this.compositeUniformData = new Float32Array(44);
|
|
294
309
|
/** Composite background (display-space sRGB 0–1) — null = transparent canvas. */
|
|
295
310
|
this.backgroundColor = null;
|
|
296
311
|
// 360 backdrop (equirectangular skybox, sampled by view ray in composite).
|
|
297
312
|
this.backdropEquirectTexture = null;
|
|
298
313
|
this.backdropEquirectView = null;
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
314
|
+
// The scene's user WGSL effect (setEffect). ONE per scene, mounted under the
|
|
315
|
+
// scene, over it, or both — whichever of background()/foreground() the code
|
|
316
|
+
// defines. The composite pipelines are REBUILT with the user code injected;
|
|
317
|
+
// params live in their own uniform buffer so setEffectParam is a write, not a
|
|
302
318
|
// recompile (the same instant tier as setStyleParam).
|
|
303
|
-
this.
|
|
304
|
-
/** time=0 origin for the active effect — reset each
|
|
305
|
-
this.
|
|
319
|
+
this.effect = null;
|
|
320
|
+
/** time=0 origin for the active effect — reset each setEffect. */
|
|
321
|
+
this.effectEpochMs = 0;
|
|
306
322
|
this.compositeBloomView = null;
|
|
307
323
|
this.bloomBlitUniformData = new Float32Array(4);
|
|
308
324
|
this.bloomUpsampleUniformData = new Float32Array(4);
|
|
@@ -725,10 +741,10 @@ export class Engine {
|
|
|
725
741
|
u[8] = bg?.x ?? 0;
|
|
726
742
|
u[9] = bg?.y ?? 0;
|
|
727
743
|
u[10] = bg?.z ?? 0;
|
|
728
|
-
// Base-layer mode
|
|
729
|
-
//
|
|
744
|
+
// Base-layer mode only. A user effect is a separate LAYER over whichever
|
|
745
|
+
// base is active, and needs no flag of its own: the composite pipeline is
|
|
746
|
+
// rebuilt per effect, so the compiled variant IS the flag. u[25] is spare.
|
|
730
747
|
u[11] = this.backdropEquirectView ? 2 : bg ? 1 : 0;
|
|
731
|
-
u[25] = this.backgroundEffect ? 1 : 0;
|
|
732
748
|
u[26] = this.canvas.width;
|
|
733
749
|
u[27] = this.canvas.height;
|
|
734
750
|
// ── Grade (viewU[7..9]) ── The UI's three tonal COLORS map to ASC CDL here,
|
|
@@ -774,7 +790,7 @@ export class Engine {
|
|
|
774
790
|
this.outlineEnabled = on;
|
|
775
791
|
}
|
|
776
792
|
rebuildCompositeBindGroup() {
|
|
777
|
-
if (!this.device || !this.hdrResolveTexture || !this.compositeBloomView)
|
|
793
|
+
if (!this.device || !this.hdrResolveTexture || !this.compositeBloomView || !this.depthReadView)
|
|
778
794
|
return;
|
|
779
795
|
this.compositeBindGroup = this.device.createBindGroup({
|
|
780
796
|
label: "composite bind group",
|
|
@@ -787,7 +803,9 @@ export class Engine {
|
|
|
787
803
|
{ binding: 4, resource: this.maskResolveView },
|
|
788
804
|
{ binding: 5, resource: this.filmicLutView },
|
|
789
805
|
{ binding: 6, resource: this.backdropEquirectView ?? this.fallbackEquirectView },
|
|
790
|
-
{ binding: 7, resource: { buffer: this.
|
|
806
|
+
{ binding: 7, resource: { buffer: this.effect?.paramsBuffer ?? this.bgParamsDummyBuffer } },
|
|
807
|
+
{ binding: 8, resource: this.depthReadView },
|
|
808
|
+
{ binding: 9, resource: { buffer: this.dofUniformBuffer } },
|
|
791
809
|
],
|
|
792
810
|
});
|
|
793
811
|
}
|
|
@@ -850,42 +868,71 @@ export class Engine {
|
|
|
850
868
|
});
|
|
851
869
|
}
|
|
852
870
|
/**
|
|
853
|
-
* Install
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
* or transparency) — its alpha lets the base show through, so a starfield is
|
|
857
|
-
* stars over the user's background color. Display-space: never affects
|
|
858
|
-
* lighting, bloom, or tonemapping, and is captured by offline export like any
|
|
859
|
-
* background.
|
|
860
|
-
*
|
|
861
|
-
* `wgsl` must define:
|
|
871
|
+
* Install the scene's WGSL effect (shadertoy-style), rendered per-pixel in the
|
|
872
|
+
* composite pass. ONE effect per scene, and the code says where it mounts by
|
|
873
|
+
* which of these it defines — either, or both in one file:
|
|
862
874
|
*
|
|
863
875
|
* fn background(ray: vec3f, uv: vec2f, time: f32) -> vec4f
|
|
876
|
+
* fn foreground(ray: vec3f, uv: vec2f, time: f32, depth: f32) -> vec4f
|
|
877
|
+
*
|
|
878
|
+
* `background` is a LAYER between the base background and the scene,
|
|
879
|
+
* over-composited onto whichever base is active (solid color, 360 equirect, or
|
|
880
|
+
* transparency) — its alpha lets the base show through, so a starfield is
|
|
881
|
+
* stars over the user's background color. `foreground` composites over the
|
|
882
|
+
* finished frame instead, which is where rain, snow, petals and fog live, and
|
|
883
|
+
* is handed `depth`: the camera-space distance in metres of whatever the scene
|
|
884
|
+
* drew at that pixel (the far plane where it drew nothing). Compare a
|
|
885
|
+
* particle's own distance against it and the model occludes it; fog just reads
|
|
886
|
+
* it, since fog's alpha IS a function of distance.
|
|
864
887
|
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
888
|
+
* `ray` is the pixel's normalized world-space view direction (LH, +Z forward —
|
|
889
|
+
* what the skybox samples by), `uv` is 0..1 bottom-left origin, `time` is
|
|
890
|
+
* seconds since apply, and `bgResolution()` gives the canvas size. Return sRGB
|
|
891
|
+
* + alpha; alpha is the only "how much does this replace" control there is.
|
|
892
|
+
* Declared `params` arrive as `params.<name>` (number → f32, Vec3 → vec3f),
|
|
893
|
+
* shared by both mounts, and are later tweaked without recompiling via
|
|
894
|
+
* setEffectParam.
|
|
871
895
|
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
*
|
|
896
|
+
* Both mounts are display-space: neither affects lighting, bloom or
|
|
897
|
+
* tonemapping, and both are captured by offline export. A foreground makes the
|
|
898
|
+
* scene pass STORE its depth buffer (it otherwise discards it into tile
|
|
899
|
+
* memory) for as long as one is installed.
|
|
900
|
+
*
|
|
901
|
+
* Compiles off the hot path (async pipelines): on failure the previous effect
|
|
902
|
+
* is KEPT and diagnostics are returned with line numbers relative to the
|
|
903
|
+
* user's WGSL. Pass null to remove the effect.
|
|
875
904
|
*/
|
|
876
|
-
async
|
|
905
|
+
async setEffect(wgsl, params) {
|
|
906
|
+
const noMounts = { background: false, foreground: false };
|
|
877
907
|
if (!this.device)
|
|
878
|
-
return { ok: false, diagnostics: ["
|
|
908
|
+
return { ok: false, diagnostics: ["setEffect requires init() to have run"], mounts: noMounts };
|
|
879
909
|
if (wgsl === null) {
|
|
880
|
-
this.
|
|
881
|
-
this.
|
|
910
|
+
this.effect?.paramsBuffer?.destroy();
|
|
911
|
+
this.effect = null;
|
|
882
912
|
const module = this.device.createShaderModule({ label: "composite shader", code: buildCompositeShader(null) });
|
|
883
913
|
this.compositePipelineIdentity = this.makeCompositePipeline(module, false, "composite pipeline (gamma=1)");
|
|
884
914
|
this.compositePipelineGamma = this.makeCompositePipeline(module, true, "composite pipeline (gamma!=1)");
|
|
885
915
|
this.rebuildCompositeBindGroup();
|
|
886
916
|
this.writeCompositeViewUniforms();
|
|
887
|
-
return { ok: true, diagnostics: [] };
|
|
917
|
+
return { ok: true, diagnostics: [], mounts: noMounts };
|
|
918
|
+
}
|
|
919
|
+
// ── Which mounts did the author ask for? A declaration, not a setting: the
|
|
920
|
+
// entry points present in the source are the ones compiled in. Matching the
|
|
921
|
+
// `fn` keyword is enough to be safe against a `foreground` LOCAL or a call
|
|
922
|
+
// to one — those never follow `fn`.
|
|
923
|
+
const hasBackground = /\bfn\s+background\s*\(/.test(wgsl);
|
|
924
|
+
const hasForeground = /\bfn\s+foreground\s*\(/.test(wgsl);
|
|
925
|
+
if (!hasBackground && !hasForeground) {
|
|
926
|
+
return {
|
|
927
|
+
ok: false,
|
|
928
|
+
diagnostics: [
|
|
929
|
+
"an effect must define fn background(ray: vec3f, uv: vec2f, time: f32) -> vec4f " +
|
|
930
|
+
"or fn foreground(ray: vec3f, uv: vec2f, time: f32, depth: f32) -> vec4f (or both)",
|
|
931
|
+
],
|
|
932
|
+
mounts: noMounts,
|
|
933
|
+
};
|
|
888
934
|
}
|
|
935
|
+
const mounts = { background: hasBackground, foreground: hasForeground };
|
|
889
936
|
// ── Params: codegen a WGSL struct and mirror its uniform layout on the CPU.
|
|
890
937
|
// Fields are emitted in declaration order; offsets follow WGSL's natural
|
|
891
938
|
// uniform rules (f32 align 4, vec3f align 16 size 12), computed identically
|
|
@@ -896,7 +943,7 @@ export class Engine {
|
|
|
896
943
|
let cursor = 0;
|
|
897
944
|
for (const [name, value] of entries) {
|
|
898
945
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
899
|
-
return { ok: false, diagnostics: [`invalid param name "${name}" (must be a WGSL identifier)`] };
|
|
946
|
+
return { ok: false, diagnostics: [`invalid param name "${name}" (must be a WGSL identifier)`], mounts };
|
|
900
947
|
}
|
|
901
948
|
const isVec = typeof value !== "number";
|
|
902
949
|
const align = isVec ? 16 : 4;
|
|
@@ -917,14 +964,14 @@ export class Engine {
|
|
|
917
964
|
}
|
|
918
965
|
}
|
|
919
966
|
const paramsDecl = entries.length
|
|
920
|
-
? `struct
|
|
967
|
+
? `struct EffectParams {\n${fields.join("\n")}\n}\n@group(0) @binding(7) var<uniform> params: EffectParams;\n`
|
|
921
968
|
: "";
|
|
922
969
|
// ── Compile with validation captured, not thrown at the console. Line
|
|
923
970
|
// numbers in diagnostics are rebased to the USER's source.
|
|
924
|
-
const source = buildCompositeShader({ wgsl, paramsDecl });
|
|
971
|
+
const source = buildCompositeShader({ wgsl, paramsDecl, hasBackground, hasForeground });
|
|
925
972
|
const userLineOffset = source.slice(0, source.indexOf(wgsl)).split("\n").length - 1;
|
|
926
973
|
this.device.pushErrorScope("validation");
|
|
927
|
-
const module = this.device.createShaderModule({ label: "composite shader (
|
|
974
|
+
const module = this.device.createShaderModule({ label: "composite shader (effect)", code: source });
|
|
928
975
|
const info = await module.getCompilationInfo();
|
|
929
976
|
const scopeErr = await this.device.popErrorScope();
|
|
930
977
|
const diagnostics = info.messages
|
|
@@ -933,7 +980,7 @@ export class Engine {
|
|
|
933
980
|
if (diagnostics.length === 0 && scopeErr)
|
|
934
981
|
diagnostics.push(scopeErr.message);
|
|
935
982
|
if (diagnostics.length > 0)
|
|
936
|
-
return { ok: false, diagnostics };
|
|
983
|
+
return { ok: false, diagnostics, mounts };
|
|
937
984
|
let identity;
|
|
938
985
|
let gamma;
|
|
939
986
|
try {
|
|
@@ -950,36 +997,40 @@ export class Engine {
|
|
|
950
997
|
primitive: { topology: "triangle-list" },
|
|
951
998
|
});
|
|
952
999
|
[identity, gamma] = await Promise.all([
|
|
953
|
-
make(false, "composite pipeline (
|
|
954
|
-
make(true, "composite pipeline (
|
|
1000
|
+
make(false, "composite pipeline (effect, gamma=1)"),
|
|
1001
|
+
make(true, "composite pipeline (effect, gamma!=1)"),
|
|
955
1002
|
]);
|
|
956
1003
|
}
|
|
957
1004
|
catch (e) {
|
|
958
|
-
return { ok: false, diagnostics: [e instanceof Error ? e.message : String(e)] };
|
|
1005
|
+
return { ok: false, diagnostics: [e instanceof Error ? e.message : String(e)], mounts };
|
|
959
1006
|
}
|
|
960
1007
|
// ── Swap — only now does the old effect (and its params buffer) go away.
|
|
961
|
-
this.
|
|
1008
|
+
this.effect?.paramsBuffer?.destroy();
|
|
962
1009
|
let paramsBuffer = null;
|
|
963
1010
|
if (entries.length) {
|
|
964
1011
|
paramsBuffer = this.device.createBuffer({
|
|
965
|
-
label: "
|
|
1012
|
+
label: "effect params",
|
|
966
1013
|
size: paramsData.byteLength,
|
|
967
1014
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
968
1015
|
});
|
|
969
1016
|
this.device.queue.writeBuffer(paramsBuffer, 0, paramsData);
|
|
970
1017
|
}
|
|
971
|
-
this.
|
|
1018
|
+
this.effect = { wgsl, paramLayout: layout, paramsBuffer, paramsData, hasBackground, hasForeground };
|
|
972
1019
|
this.compositePipelineIdentity = identity;
|
|
973
1020
|
this.compositePipelineGamma = gamma;
|
|
974
|
-
this.
|
|
1021
|
+
this.effectEpochMs = performance.now();
|
|
975
1022
|
this.rebuildCompositeBindGroup();
|
|
976
1023
|
this.writeCompositeViewUniforms();
|
|
977
|
-
return { ok: true, diagnostics: [] };
|
|
1024
|
+
return { ok: true, diagnostics: [], mounts };
|
|
1025
|
+
}
|
|
1026
|
+
/** Which mounts the installed effect declared. Both false when none is set. */
|
|
1027
|
+
getEffectMounts() {
|
|
1028
|
+
return { background: this.effect?.hasBackground ?? false, foreground: this.effect?.hasForeground ?? false };
|
|
978
1029
|
}
|
|
979
|
-
/** Write one
|
|
980
|
-
*
|
|
981
|
-
|
|
982
|
-
const fx = this.
|
|
1030
|
+
/** Write one effect param (declared at setEffect) — a uniform write, no
|
|
1031
|
+
* recompile; the instant tier, like setStyleParam. */
|
|
1032
|
+
setEffectParam(name, value) {
|
|
1033
|
+
const fx = this.effect;
|
|
983
1034
|
if (!fx || !fx.paramsBuffer)
|
|
984
1035
|
return;
|
|
985
1036
|
const slot = fx.paramLayout.get(name);
|
|
@@ -995,6 +1046,92 @@ export class Engine {
|
|
|
995
1046
|
this.device.queue.writeBuffer(fx.paramsBuffer, 0, fx.paramsData);
|
|
996
1047
|
}
|
|
997
1048
|
/** Patch bloom; GPU uniforms update immediately if `init()` has run. */
|
|
1049
|
+
/** Camera depth of field (see DepthOfFieldOptions). Free while disabled —
|
|
1050
|
+
* the scene pass only stores its depth buffer on frames the gather reads. */
|
|
1051
|
+
setDepthOfField(patch) {
|
|
1052
|
+
this.depthOfField = { ...this.depthOfField, ...patch };
|
|
1053
|
+
if (!this.device || !this.dofUniformBuffer)
|
|
1054
|
+
return;
|
|
1055
|
+
if (this.depthOfField.enabled) {
|
|
1056
|
+
this.writeDepthOfFieldUniforms();
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
// One last write so the shader's uniform branch reads a clean zero.
|
|
1060
|
+
this.dofUniformData[0] = 0;
|
|
1061
|
+
this.device.queue.writeBuffer(this.dofUniformBuffer, 0, this.dofUniformData);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
getDepthOfField() {
|
|
1065
|
+
return { ...this.depthOfField };
|
|
1066
|
+
}
|
|
1067
|
+
/** Auto-focus target: the camera-space depth span of the first visible
|
|
1068
|
+
* character's bones. Focus sits at the span's midpoint; the range covers the
|
|
1069
|
+
* span plus a margin for what bones don't reach (shoes, hair, cloth). */
|
|
1070
|
+
getModelBodyFocus() {
|
|
1071
|
+
if (!this.camera)
|
|
1072
|
+
return null;
|
|
1073
|
+
const view = this.camera.getViewMatrix().values;
|
|
1074
|
+
for (const inst of this.modelInstances.values()) {
|
|
1075
|
+
if (!inst.model.visible || inst.isStage)
|
|
1076
|
+
continue;
|
|
1077
|
+
const model = inst.model;
|
|
1078
|
+
const matrices = model.getWorldMatrices();
|
|
1079
|
+
if (matrices.length === 0)
|
|
1080
|
+
continue;
|
|
1081
|
+
const scale = model.scale;
|
|
1082
|
+
const local = this.dofFocusScratch;
|
|
1083
|
+
let minDepth = Infinity;
|
|
1084
|
+
let maxDepth = -Infinity;
|
|
1085
|
+
for (const matrix of matrices) {
|
|
1086
|
+
const values = matrix.values;
|
|
1087
|
+
// Bone matrices are model-space; apply the same root transform the
|
|
1088
|
+
// renderer bakes into skinning, then take camera-space z.
|
|
1089
|
+
local.setXYZ(values[12] * scale, values[13] * scale, values[14] * scale);
|
|
1090
|
+
Quat.rotateVecInto(model.rotation, local, local);
|
|
1091
|
+
const x = model.position.x + local.x;
|
|
1092
|
+
const y = model.position.y + local.y;
|
|
1093
|
+
const z = model.position.z + local.z;
|
|
1094
|
+
const depth = view[2] * x + view[6] * y + view[10] * z + view[14];
|
|
1095
|
+
if (!Number.isFinite(depth) || depth <= this.camera.near)
|
|
1096
|
+
continue;
|
|
1097
|
+
minDepth = Math.min(minDepth, depth);
|
|
1098
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
1099
|
+
}
|
|
1100
|
+
if (Number.isFinite(minDepth) && Number.isFinite(maxDepth)) {
|
|
1101
|
+
const span = Math.max(0, maxDepth - minDepth);
|
|
1102
|
+
const meshMargin = Math.max(2.0, span * 0.15);
|
|
1103
|
+
return { distance: (minDepth + maxDepth) * 0.5, range: Math.max(2.0, span + meshMargin) };
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
writeDepthOfFieldUniforms() {
|
|
1109
|
+
if (!this.device || !this.dofUniformBuffer)
|
|
1110
|
+
return;
|
|
1111
|
+
const d = this.depthOfField;
|
|
1112
|
+
const u = this.dofUniformData;
|
|
1113
|
+
// `d.enabled &&`, because a foreground effect also drives this write (for
|
|
1114
|
+
// projA/projB alone) and auto-focus walks every visible character's bones —
|
|
1115
|
+
// work nothing would read with the gather switched off.
|
|
1116
|
+
const auto = d.enabled && d.focusMode === "auto" ? this.getModelBodyFocus() : null;
|
|
1117
|
+
u[0] = d.enabled ? 1 : 0;
|
|
1118
|
+
u[1] = auto?.distance ?? Math.max(d.focusDistance, 0.05);
|
|
1119
|
+
// In auto mode the authored range is a floor — the sharp band never cuts
|
|
1120
|
+
// into the character's own depth span.
|
|
1121
|
+
u[2] = Math.max(d.focusRange, auto?.range ?? 0.02, 0.02);
|
|
1122
|
+
u[3] = Math.max(d.aperture, 0);
|
|
1123
|
+
u[4] = Math.max(d.maxBlurRadius, 0);
|
|
1124
|
+
u[5] = Math.min(12, Math.max(3, Math.round(d.bladeCount)));
|
|
1125
|
+
u[6] = d.quality === "performance" ? 8 : d.quality === "cinematic" ? 24 : 16;
|
|
1126
|
+
u[7] = 1; // anamorphic ratio, reserved (the shader clamps ≥ 0.25)
|
|
1127
|
+
// viewZ = projB / (z − projA), the inverse of perspectiveInto's z mapping.
|
|
1128
|
+
// near/far track the camera radius, so these refresh every enabled frame.
|
|
1129
|
+
const near = this.camera.near;
|
|
1130
|
+
const far = this.camera.far;
|
|
1131
|
+
u[8] = (far + near) / (far - near);
|
|
1132
|
+
u[9] = (-2 * near * far) / (far - near);
|
|
1133
|
+
this.device.queue.writeBuffer(this.dofUniformBuffer, 0, u);
|
|
1134
|
+
}
|
|
998
1135
|
setBloomOptions(patch) {
|
|
999
1136
|
const b = this.bloomSettings;
|
|
1000
1137
|
if (patch.enabled !== undefined)
|
|
@@ -1794,11 +1931,18 @@ export class Engine {
|
|
|
1794
1931
|
// mirroring EEVEE where bloom color/intensity are combine-stage params, not prefilter).
|
|
1795
1932
|
this.compositeUniformBuffer = this.device.createBuffer({
|
|
1796
1933
|
label: "composite view uniforms",
|
|
1797
|
-
//
|
|
1934
|
+
// 11 × vec4f: (exposure, invGamma, _, _) · (bloom tint, intensity) ·
|
|
1798
1935
|
// (bg rgb, mode) · camera right/up/forward basis for the 360 skybox ray ·
|
|
1799
|
-
// (time, _, canvas width, canvas height) for user
|
|
1800
|
-
//
|
|
1801
|
-
|
|
1936
|
+
// (time, _, canvas width, canvas height) for user effects · three grade
|
|
1937
|
+
// vectors (CDL offset+contrast, power+saturation, slope+flag) · camera
|
|
1938
|
+
// world position, for an effect placing itself in the scene.
|
|
1939
|
+
size: 176,
|
|
1940
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
1941
|
+
});
|
|
1942
|
+
this.dofUniformBuffer = this.device.createBuffer({
|
|
1943
|
+
label: "depth of field uniforms",
|
|
1944
|
+
// 3 × vec4f — see the dofU comment in composite.ts.
|
|
1945
|
+
size: 48,
|
|
1802
1946
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
1803
1947
|
});
|
|
1804
1948
|
this.bgParamsDummyBuffer = this.device.createBuffer({
|
|
@@ -1823,6 +1967,15 @@ export class Engine {
|
|
|
1823
1967
|
// User background-effect params — dummy buffer when no effect is set. The
|
|
1824
1968
|
// layout is explicit, so the base shader legally ignores the binding.
|
|
1825
1969
|
{ binding: 7, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
1970
|
+
// The scene pass's MSAA depth, depth-only aspect — read by the DoF
|
|
1971
|
+
// gather. Contents are undefined while DoF is off (the scene pass
|
|
1972
|
+
// discards depth then), and the shader never reads it then either.
|
|
1973
|
+
{
|
|
1974
|
+
binding: 8,
|
|
1975
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
1976
|
+
texture: { sampleType: "depth", viewDimension: "2d", multisampled: true },
|
|
1977
|
+
},
|
|
1978
|
+
{ binding: 9, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } },
|
|
1826
1979
|
],
|
|
1827
1980
|
});
|
|
1828
1981
|
this.fallbackEquirectTexture = this.device.createTexture({
|
|
@@ -2034,9 +2187,12 @@ export class Engine {
|
|
|
2034
2187
|
size: [width, height],
|
|
2035
2188
|
sampleCount: Engine.MULTISAMPLE_COUNT,
|
|
2036
2189
|
format: "depth24plus-stencil8",
|
|
2037
|
-
usage
|
|
2190
|
+
// TEXTURE_BINDING for the DoF gather — a usage flag, not a copy; the
|
|
2191
|
+
// zero-cost-when-off story lives in depthStoreOp, not here.
|
|
2192
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
2038
2193
|
});
|
|
2039
2194
|
const depthTextureView = this.depthTexture.createView();
|
|
2195
|
+
this.depthReadView = this.depthTexture.createView({ aspect: "depth-only" });
|
|
2040
2196
|
// storeOp="discard" on MSAA views keeps per-sample data in Apple TBDR tile memory —
|
|
2041
2197
|
// only the resolveTarget (hdrResolveTexture / maskResolveView) gets written to RAM.
|
|
2042
2198
|
// With storeOp="store" Safari's Metal backend spills the full MS buffer every frame
|
|
@@ -2421,6 +2577,12 @@ export class Engine {
|
|
|
2421
2577
|
hasCameraVmd() {
|
|
2422
2578
|
return this.cameraAnimation !== null;
|
|
2423
2579
|
}
|
|
2580
|
+
/** Seconds the loaded camera VMD runs for — its last keyframe — or 0 with none
|
|
2581
|
+
* loaded. A timeline cannot draw a lane to scale without it, and the camera's
|
|
2582
|
+
* length is its own: it does not have to match any model's clip. */
|
|
2583
|
+
getCameraVmdDuration() {
|
|
2584
|
+
return this.cameraAnimation?.duration ?? 0;
|
|
2585
|
+
}
|
|
2424
2586
|
/** Drop the loaded camera VMD and return to orbit control. */
|
|
2425
2587
|
clearCameraVmd() {
|
|
2426
2588
|
this.cameraAnimation = null;
|
|
@@ -2471,24 +2633,6 @@ export class Engine {
|
|
|
2471
2633
|
setCameraFov(fov) {
|
|
2472
2634
|
this.camera.fov = fov;
|
|
2473
2635
|
}
|
|
2474
|
-
/** Fire a one-shot particle effect (see ParticleEmitOptions): "burst" scatters
|
|
2475
|
-
* glowing sparks from the given world points, "converge" condenses them onto
|
|
2476
|
-
* the points. Pair with Model.sampleSurfacePoints for dissolve/materialize
|
|
2477
|
-
* transitions shaped like the model. Stateless GPU particles — the emission
|
|
2478
|
-
* cleans itself up when it expires. */
|
|
2479
|
-
emitParticles(opts) {
|
|
2480
|
-
if (!this.device)
|
|
2481
|
-
return;
|
|
2482
|
-
if (!this.particles) {
|
|
2483
|
-
this.particles = new ParticleSystem(this.device, this.cameraUniformBuffer, {
|
|
2484
|
-
hdr: this.hdrFormat,
|
|
2485
|
-
mask: Engine.BLOOM_MASK_FORMAT,
|
|
2486
|
-
depth: "depth24plus-stencil8",
|
|
2487
|
-
sampleCount: Engine.MULTISAMPLE_COUNT,
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
this.particles.emit(opts, performance.now() / 1000);
|
|
2491
|
-
}
|
|
2492
2636
|
// Step 5: Create lighting buffers
|
|
2493
2637
|
setupLighting() {
|
|
2494
2638
|
this.lightUniformBuffer = this.device.createBuffer({
|
|
@@ -2625,8 +2769,6 @@ export class Engine {
|
|
|
2625
2769
|
Engine.instance = null;
|
|
2626
2770
|
if (this.camera)
|
|
2627
2771
|
this.camera.detachControl();
|
|
2628
|
-
this.particles?.dispose();
|
|
2629
|
-
this.particles = null;
|
|
2630
2772
|
// Remove raycasting event listeners
|
|
2631
2773
|
if (this.onRaycast) {
|
|
2632
2774
|
this.canvas.removeEventListener("dblclick", this.handleCanvasDoubleClick);
|
|
@@ -4209,6 +4351,22 @@ export class Engine {
|
|
|
4209
4351
|
}
|
|
4210
4352
|
this.updateCameraUniforms();
|
|
4211
4353
|
this.updateShadowLightVP();
|
|
4354
|
+
// Depth of field's entire disabled cost is this branch: depth stays in
|
|
4355
|
+
// TBDR tile memory (discard) unless something in the composite reads it this
|
|
4356
|
+
// frame. Two things can — the DoF gather, and the depth handed to a
|
|
4357
|
+
// foreground effect — and either one makes the pass store it.
|
|
4358
|
+
//
|
|
4359
|
+
// The uniform refresh is shared for the same reason: linearDepth() inverts
|
|
4360
|
+
// the z-buffer with projA/projB out of dofU[2], which track the camera's
|
|
4361
|
+
// near/far and so must be rewritten every frame either reader is live. A
|
|
4362
|
+
// foreground with a stale pair would read metres from the wrong frustum. The
|
|
4363
|
+
// write leaves dofU[0].x at 0 while DoF is off, so refreshing it does not
|
|
4364
|
+
// switch the gather on.
|
|
4365
|
+
const dofOn = this.depthOfField.enabled;
|
|
4366
|
+
const depthRead = dofOn || (this.effect?.hasForeground ?? false);
|
|
4367
|
+
this.renderPassDescriptor.depthStencilAttachment.depthStoreOp = depthRead ? "store" : "discard";
|
|
4368
|
+
if (depthRead)
|
|
4369
|
+
this.writeDepthOfFieldUniforms();
|
|
4212
4370
|
const encoder = this.device.createCommandEncoder();
|
|
4213
4371
|
// GPU vertex morphs: write morphed positions into vertex buffers before any pass reads
|
|
4214
4372
|
// them. WebGPU inserts the storage→vertex barrier between this pass and the render passes.
|
|
@@ -4257,9 +4415,6 @@ export class Engine {
|
|
|
4257
4415
|
if (inst.model.visible)
|
|
4258
4416
|
this.renderModelTransparentPhase(pass, inst);
|
|
4259
4417
|
});
|
|
4260
|
-
// Particle emissions last: additive light over everything, depth-tested
|
|
4261
|
-
// against the scene, feeding the bloom mask so the sparks glow.
|
|
4262
|
-
this.particles?.draw(pass, performance.now() / 1000);
|
|
4263
4418
|
pass.end();
|
|
4264
4419
|
// Bloom pyramid (EEVEE 3.6):
|
|
4265
4420
|
// 1. Blit: HDR → bloomDown[0] (Karis prefilter, half-res)
|
|
@@ -4838,7 +4993,7 @@ export class Engine {
|
|
|
4838
4993
|
// is LEFT-HANDED (+Z forward, see Mat4.lookAtInto), so the world-space
|
|
4839
4994
|
// right/up/FORWARD vectors are rows 0/1/2 of its rotation block directly
|
|
4840
4995
|
// (column-major storage: row i = values[i], values[i+4], values[i+8]).
|
|
4841
|
-
if ((this.backdropEquirectView || this.
|
|
4996
|
+
if ((this.backdropEquirectView || this.effect) && this.compositeUniformBuffer) {
|
|
4842
4997
|
const v = viewMatrix.values;
|
|
4843
4998
|
const u = this.compositeUniformData;
|
|
4844
4999
|
const tanHalf = Math.tan((this.camera.fov ?? Math.PI / 4) / 2);
|
|
@@ -4856,9 +5011,15 @@ export class Engine {
|
|
|
4856
5011
|
u[22] = v[10];
|
|
4857
5012
|
u[23] = 0;
|
|
4858
5013
|
// Effect clock + canvas size (viewU[6]) — written on the same refresh.
|
|
4859
|
-
u[24] = (performance.now() - this.
|
|
5014
|
+
u[24] = (performance.now() - this.effectEpochMs) / 1000;
|
|
4860
5015
|
u[26] = this.canvas.width;
|
|
4861
5016
|
u[27] = this.canvas.height;
|
|
5017
|
+
// Camera world position (viewU[10]) — the other half of bgWorldPos. It
|
|
5018
|
+
// rides this refresh rather than writeCompositeViewUniforms because it
|
|
5019
|
+
// changes every frame the camera does, exactly like the basis above.
|
|
5020
|
+
u[40] = cameraPos.x;
|
|
5021
|
+
u[41] = cameraPos.y;
|
|
5022
|
+
u[42] = cameraPos.z;
|
|
4862
5023
|
this.device.queue.writeBuffer(this.compositeUniformBuffer, 0, u);
|
|
4863
5024
|
}
|
|
4864
5025
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/graph/registry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC9B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAA;IAC/C,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC,CAAA;AAMD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAgBhE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAIpE;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAM1D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpE;AA6BD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/graph/registry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC9B,2FAA2F;IAC3F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAA;IAC/C,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC,CAAA;AAMD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,MAAM,CAgBhE;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,CAIpE;AAMD,wBAAgB,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAM1D;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpE;AA6BD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAkQlD,CAAA"}
|
package/dist/graph/registry.js
CHANGED
|
@@ -126,6 +126,16 @@ export const NODE_REGISTRY = {
|
|
|
126
126
|
outputs: { color: "color" },
|
|
127
127
|
contextOutputs: { color: "material.diffuseColor" },
|
|
128
128
|
},
|
|
129
|
+
// The PMX material's sphere map, which is where an MMD model keeps its
|
|
130
|
+
// highlights — every PMX ships one, and hair without it reads flat. The mode
|
|
131
|
+
// is the material's own (.sph multiplies the shaded base, .spa adds a
|
|
132
|
+
// highlight), so a graph asks for the effect and the model decides which; a
|
|
133
|
+
// material with no sphere texture is an exact no-op.
|
|
134
|
+
sphere_map: {
|
|
135
|
+
inputs: { base: C([0, 0, 0], true), strength: F(1) },
|
|
136
|
+
outputs: { color: "color" },
|
|
137
|
+
emit: (a) => `pmx_sphere_map(${a.base}, ${a.strength}, n)`,
|
|
138
|
+
},
|
|
129
139
|
// ── Literals as nodes (for editor ergonomics; inlined literals work too) ──
|
|
130
140
|
value: { inputs: { value: F(0) }, outputs: { value: "float" }, emit: (a) => a.value },
|
|
131
141
|
rgb: { inputs: { color: C() }, outputs: { color: "color" }, emit: (a) => a.color },
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, type ColorGradingOptions, type EngineStats, type EngineOptions, type BloomOptions, type ViewTransformOptions, type LoadModelFromFilesOptions, type MaterialPreset, type MaterialPresetMap, type ModelTransform, type GizmoDragEvent, type GizmoDragCallback, type GizmoDragKind, type
|
|
1
|
+
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_DEPTH_OF_FIELD_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, type ColorGradingOptions, type EngineStats, type EngineOptions, type BloomOptions, type DepthOfFieldOptions, type ViewTransformOptions, type LoadModelFromFilesOptions, type MaterialPreset, type MaterialPresetMap, type ModelTransform, type GizmoDragEvent, type GizmoDragCallback, type GizmoDragKind, type EffectParamValue, type EffectResult, } from "./engine";
|
|
2
2
|
export { parsePmxFolderInput, pmxFileAtRelativePath, type PmxFolderInputResult } from "./folder-upload";
|
|
3
3
|
export { compileGraph, validateGraph, assignStyleSlots, type CompileOptions, type CompileResult, type StyleSlot, } from "./graph/compile";
|
|
4
4
|
export type { ShaderGraph, GraphNode, GraphLink, ExposedParam, SocketValue, Diagnostic, } from "./graph/schema";
|
|
@@ -26,5 +26,4 @@ export { PmxLoader } from "./pmx-loader";
|
|
|
26
26
|
export { CameraAnimation, type CameraPose } from "./camera-animation";
|
|
27
27
|
export { RezePhysics } from "./physics";
|
|
28
28
|
export type { WindOptions } from "./physics/world";
|
|
29
|
-
export { type ParticleEmitOptions } from "./particles";
|
|
30
29
|
//# sourceMappingURL=index.d.ts.map
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, } from "./engine";
|
|
1
|
+
export { Engine, DEFAULT_BLOOM_OPTIONS, DEFAULT_DEPTH_OF_FIELD_OPTIONS, DEFAULT_VIEW_TRANSFORM, DEFAULT_COLOR_GRADING, } from "./engine";
|
|
2
2
|
export { parsePmxFolderInput, pmxFileAtRelativePath } from "./folder-upload";
|
|
3
3
|
export { compileGraph, validateGraph, assignStyleSlots, } from "./graph/compile";
|
|
4
4
|
export { NODE_REGISTRY } from "./graph/registry";
|
package/dist/model.d.ts
CHANGED
|
@@ -312,11 +312,6 @@ export declare class Model {
|
|
|
312
312
|
getWorldMatrices(): Mat4[];
|
|
313
313
|
getBoneWorldMatrices(): Float32Array;
|
|
314
314
|
getBoneInverseBindMatrices(): Float32Array;
|
|
315
|
-
/** World-space positions of `count` vertices sampled evenly across the mesh
|
|
316
|
-
* under the CURRENT pose — the model's skinning replayed on the CPU for
|
|
317
|
-
* this one call, root transform included. Feed to Engine.emitParticles for
|
|
318
|
-
* dissolve/materialize effects shaped like the model itself. */
|
|
319
|
-
sampleSurfacePoints(count: number): Float32Array;
|
|
320
315
|
getSkinMatrices(): Float32Array;
|
|
321
316
|
setMorphWeight(name: string, weight: number, durationMs?: number): void;
|
|
322
317
|
private applyMorphs;
|