reze-engine 0.55.4 → 0.55.7
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/camera.d.ts +10 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +14 -2
- package/dist/engine.d.ts +27 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +145 -29
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/shaders/lights.d.ts +1 -1
- package/dist/shaders/lights.d.ts.map +1 -1
- package/dist/shaders/lights.js +5 -1
- package/dist/shaders/passes/grid.d.ts +1 -1
- package/dist/shaders/passes/grid.d.ts.map +1 -1
- package/dist/shaders/passes/grid.js +4 -1
- package/dist/shaders/passes/particles.d.ts +4 -0
- package/dist/shaders/passes/particles.d.ts.map +1 -1
- package/dist/shaders/passes/particles.js +2 -0
- package/dist/shaders/passes/trails.d.ts +2 -0
- package/dist/shaders/passes/trails.d.ts.map +1 -1
- package/dist/shaders/passes/trails.js +1 -0
- package/package.json +1 -1
- package/src/camera.ts +12 -2
- package/src/engine.ts +180 -27
- package/src/index.ts +14 -0
- package/src/shaders/lights.ts +5 -0
- package/src/shaders/passes/grid.ts +4 -1
- package/src/shaders/passes/particles.ts +6 -0
- package/src/shaders/passes/trails.ts +3 -0
package/dist/engine.js
CHANGED
|
@@ -593,6 +593,16 @@ function vertexMorphReach(model) {
|
|
|
593
593
|
}
|
|
594
594
|
/** Tried in order when a PMX names a texture without an extension. */
|
|
595
595
|
const TEXTURE_EXTENSION_GUESSES = [".png", ".jpg", ".jpeg", ".bmp", ".tga", ".dds", ".spa", ".sph"];
|
|
596
|
+
/** One effect's GPU particle pool. Per effect: each declares its own count. */
|
|
597
|
+
/**
|
|
598
|
+
* The binding number an effect's params take in each pass.
|
|
599
|
+
*
|
|
600
|
+
* 7 in the composite, the particle stages and the ribbon pass, which all stop
|
|
601
|
+
* below it. The GRID reaches 8 on its own, so it takes the next one up rather
|
|
602
|
+
* than everything else moving to accommodate one mount.
|
|
603
|
+
*/
|
|
604
|
+
const EFFECT_PARAMS_BINDING = 7;
|
|
605
|
+
const EFFECT_PARAMS_BINDING_GRID = 9;
|
|
596
606
|
/**
|
|
597
607
|
* How one field draw lands on the ones before it: OVER, into a premultiplied
|
|
598
608
|
* target. Shared by both field attachments so they cannot drift apart, which
|
|
@@ -2214,6 +2224,9 @@ export class Engine {
|
|
|
2214
2224
|
{ binding: 6, resource: { buffer: this.compositeUniformBuffer } },
|
|
2215
2225
|
{ binding: 7, resource: { buffer: this.midiBuffer } },
|
|
2216
2226
|
{ binding: 8, resource: { buffer: this.lyricsBuffer } },
|
|
2227
|
+
// The grid is the one pass whose own bindings reach 8, so its params sit
|
|
2228
|
+
// above them rather than everything else shifting for one mount.
|
|
2229
|
+
...(g.params ? [{ binding: EFFECT_PARAMS_BINDING_GRID, resource: { buffer: g.params } }] : []),
|
|
2217
2230
|
],
|
|
2218
2231
|
});
|
|
2219
2232
|
}
|
|
@@ -2251,7 +2264,7 @@ export class Engine {
|
|
|
2251
2264
|
if (e.grid)
|
|
2252
2265
|
e.grid.binds = [this.gridBindGroup(e.grid, 0), this.gridBindGroup(e.grid, 1)];
|
|
2253
2266
|
if (e.lights)
|
|
2254
|
-
e.lights.bind = this.lightEmitBindGroup(e.lights.layout, e.lights.uniform);
|
|
2267
|
+
e.lights.bind = this.lightEmitBindGroup(e.lights.layout, e.lights.uniform, e.lights.params);
|
|
2255
2268
|
}
|
|
2256
2269
|
}
|
|
2257
2270
|
/**
|
|
@@ -2545,9 +2558,18 @@ export class Engine {
|
|
|
2545
2558
|
paramsData[slot.offset + 2] = value.z;
|
|
2546
2559
|
}
|
|
2547
2560
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2561
|
+
/**
|
|
2562
|
+
* The params block, at whatever binding the pass asking has free.
|
|
2563
|
+
*
|
|
2564
|
+
* A single hardcoded slot cannot serve every mount — the grid's layout
|
|
2565
|
+
* already reaches binding 8 — so the struct is generated per pass and each
|
|
2566
|
+
* one states the number it can spare. Empty when nothing is declared: WGSL
|
|
2567
|
+
* has no empty struct, and a binding nothing reads is a layout mismatch.
|
|
2568
|
+
*/
|
|
2569
|
+
const paramsWgsl = (binding) => entries.length
|
|
2570
|
+
? `struct EffectParams {\n${fields.join("\n")}\n}\n@group(0) @binding(${binding}) var<uniform> params: EffectParams;\n`
|
|
2550
2571
|
: "";
|
|
2572
|
+
const paramsDecl = paramsWgsl(EFFECT_PARAMS_BINDING);
|
|
2551
2573
|
// ── Compile with validation captured, not thrown at the console. Line
|
|
2552
2574
|
// numbers in diagnostics are rebased to the USER's source.
|
|
2553
2575
|
// The composite is STATIC: user field code compiles in its own half-res
|
|
@@ -2654,7 +2676,31 @@ export class Engine {
|
|
|
2654
2676
|
let particles = null;
|
|
2655
2677
|
let grid = null;
|
|
2656
2678
|
let trails = null;
|
|
2679
|
+
// PARAMS ARE NOT A FIELD-MOUNT FEATURE, and used to be one by accident.
|
|
2680
|
+
//
|
|
2681
|
+
// `paramsDecl` was spliced into the composite alone, so `#param` worked for
|
|
2682
|
+
// background/foreground and produced "unresolved value 'params'" for every
|
|
2683
|
+
// particle, grid and ribbon effect — which is most of the ones anyone wants
|
|
2684
|
+
// a dial on. Rain's fall speed is the whole example.
|
|
2685
|
+
//
|
|
2686
|
+
// The buffer is created HERE rather than beside the instance it ends up on,
|
|
2687
|
+
// because the mount builders below need to bind it and they run first. That
|
|
2688
|
+
// makes `abandon` its owner: a mount that fails to compile must not leak it.
|
|
2689
|
+
let paramsBuffer = null;
|
|
2690
|
+
if (entries.length) {
|
|
2691
|
+
paramsBuffer = this.device.createBuffer({
|
|
2692
|
+
label: "effect params",
|
|
2693
|
+
size: paramsData.byteLength,
|
|
2694
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2695
|
+
});
|
|
2696
|
+
this.device.queue.writeBuffer(paramsBuffer, 0, paramsData);
|
|
2697
|
+
}
|
|
2698
|
+
/** What every mount splices and binds: a generator for the struct decl at
|
|
2699
|
+
* the binding that mount has free, and the buffer behind it. The buffer is
|
|
2700
|
+
* null when nothing is declared, and then no binding is added at all. */
|
|
2701
|
+
const paramsFor = { wgsl: paramsWgsl, buffer: paramsBuffer };
|
|
2657
2702
|
const abandon = (diagnostics) => {
|
|
2703
|
+
paramsBuffer?.destroy();
|
|
2658
2704
|
particles?.buffer.destroy();
|
|
2659
2705
|
particles?.uniform.destroy();
|
|
2660
2706
|
grid?.textures[0].destroy();
|
|
@@ -2664,13 +2710,13 @@ export class Engine {
|
|
|
2664
2710
|
return { ok: false, diagnostics, mounts, params: d.params, duration: d.duration };
|
|
2665
2711
|
};
|
|
2666
2712
|
if (wantsParticles) {
|
|
2667
|
-
const built = await this.buildParticles(wgsl, d, anchors, alias);
|
|
2713
|
+
const built = await this.buildParticles(wgsl, d, anchors, alias, paramsFor);
|
|
2668
2714
|
if (!built.ok)
|
|
2669
2715
|
return abandon(built.diagnostics);
|
|
2670
2716
|
particles = built.state;
|
|
2671
2717
|
}
|
|
2672
2718
|
if (gridEntryPoint(wgsl)) {
|
|
2673
|
-
const built = await this.buildSim(wgsl, d, anchors, alias);
|
|
2719
|
+
const built = await this.buildSim(wgsl, d, anchors, alias, paramsFor);
|
|
2674
2720
|
if (!built.ok)
|
|
2675
2721
|
return abandon(built.diagnostics);
|
|
2676
2722
|
grid = built.state;
|
|
@@ -2680,9 +2726,9 @@ export class Engine {
|
|
|
2680
2726
|
// bone recorded without one would read zeroes and paint a line to the origin.
|
|
2681
2727
|
const trailSlots = anchors.filter((a) => a.trail).length;
|
|
2682
2728
|
if (trailSlots === 0) {
|
|
2683
|
-
return
|
|
2729
|
+
return abandon(["a ribbon effect needs at least one #anchor <bone> trail"]);
|
|
2684
2730
|
}
|
|
2685
|
-
const built = await this.buildTrails(wgsl, d, anchors, alias);
|
|
2731
|
+
const built = await this.buildTrails(wgsl, d, anchors, alias, paramsFor);
|
|
2686
2732
|
if (!built.ok)
|
|
2687
2733
|
return abandon(built.diagnostics);
|
|
2688
2734
|
trails = built.state;
|
|
@@ -2705,7 +2751,7 @@ export class Engine {
|
|
|
2705
2751
|
]);
|
|
2706
2752
|
}
|
|
2707
2753
|
if (declaredLights > 0) {
|
|
2708
|
-
const built = await this.buildLightEmit(wgsl, declaredLights, alias, anchors);
|
|
2754
|
+
const built = await this.buildLightEmit(wgsl, declaredLights, alias, anchors, paramsFor);
|
|
2709
2755
|
if (!built.ok)
|
|
2710
2756
|
return abandon(built.diagnostics);
|
|
2711
2757
|
lights = built.state;
|
|
@@ -2718,15 +2764,6 @@ export class Engine {
|
|
|
2718
2764
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2719
2765
|
})
|
|
2720
2766
|
: null;
|
|
2721
|
-
let paramsBuffer = null;
|
|
2722
|
-
if (entries.length) {
|
|
2723
|
-
paramsBuffer = this.device.createBuffer({
|
|
2724
|
-
label: "effect params",
|
|
2725
|
-
size: paramsData.byteLength,
|
|
2726
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
2727
|
-
});
|
|
2728
|
-
this.device.queue.writeBuffer(paramsBuffer, 0, paramsData);
|
|
2729
|
-
}
|
|
2730
2767
|
const instance = {
|
|
2731
2768
|
wgsl,
|
|
2732
2769
|
paramDecls: d.params,
|
|
@@ -2955,11 +2992,14 @@ export class Engine {
|
|
|
2955
2992
|
/** This effect's local→scene slot map. Passed rather than read off the
|
|
2956
2993
|
* engine: the builders run BEFORE the swap, so this.anchorTable still
|
|
2957
2994
|
* describes the effect that is still on screen. */
|
|
2958
|
-
alias
|
|
2995
|
+
alias,
|
|
2996
|
+
/** The effect's declared dials. Spliced into both stages and bound at 7,
|
|
2997
|
+
* the same binding the composite gives them. */
|
|
2998
|
+
params) {
|
|
2959
2999
|
// No pragma means "some": an author who wrote the trio clearly wants
|
|
2960
3000
|
// particles, and failing over a missing comment would be pedantry.
|
|
2961
3001
|
const count = Math.min(d.particles || 1024, Engine.MAX_PARTICLES);
|
|
2962
|
-
const src = { wgsl, count, blend: d.particleBlend, bloom: d.bloom };
|
|
3002
|
+
const src = { wgsl, count, blend: d.particleBlend, bloom: d.bloom, paramsDecl: params.wgsl(EFFECT_PARAMS_BINDING) };
|
|
2963
3003
|
// Sparks want to spawn where a trail is, so the particle stages see the same
|
|
2964
3004
|
// cast buffer the trail draw reads.
|
|
2965
3005
|
const cast = {
|
|
@@ -3022,6 +3062,9 @@ export class Engine {
|
|
|
3022
3062
|
// rule and a background read the same instant.
|
|
3023
3063
|
{ binding: 5, visibility, buffer: { type: "read-only-storage" } },
|
|
3024
3064
|
{ binding: 6, visibility, buffer: { type: "read-only-storage" } },
|
|
3065
|
+
// Only when the effect declared any: WGSL has no empty struct, so a
|
|
3066
|
+
// param-less effect must not carry the decl OR the binding.
|
|
3067
|
+
...(params.buffer ? [{ binding: 7, visibility, buffer: { type: "uniform" } }] : []),
|
|
3025
3068
|
],
|
|
3026
3069
|
});
|
|
3027
3070
|
const bindFor = (layout, camera) => this.device.createBindGroup({
|
|
@@ -3034,6 +3077,7 @@ export class Engine {
|
|
|
3034
3077
|
{ binding: 4, resource: { buffer: this.audioBuffer } },
|
|
3035
3078
|
{ binding: 5, resource: { buffer: this.midiBuffer } },
|
|
3036
3079
|
{ binding: 6, resource: { buffer: this.lyricsBuffer } },
|
|
3080
|
+
...(params.buffer ? [{ binding: 7, resource: { buffer: params.buffer } }] : []),
|
|
3037
3081
|
],
|
|
3038
3082
|
});
|
|
3039
3083
|
const computeLayout = layoutFor("storage", GPUShaderStage.COMPUTE);
|
|
@@ -3115,7 +3159,7 @@ export class Engine {
|
|
|
3115
3159
|
* writable view here means a material pipeline cannot accidentally acquire
|
|
3116
3160
|
* one.
|
|
3117
3161
|
*/
|
|
3118
|
-
async buildLightEmit(wgsl, count, alias, anchors) {
|
|
3162
|
+
async buildLightEmit(wgsl, count, alias, anchors, params) {
|
|
3119
3163
|
const layout = this.device.createBindGroupLayout({
|
|
3120
3164
|
label: "light emit layout",
|
|
3121
3165
|
entries: [
|
|
@@ -3126,13 +3170,16 @@ export class Engine {
|
|
|
3126
3170
|
{ binding: 4, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3127
3171
|
{ binding: 5, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3128
3172
|
{ binding: 6, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3173
|
+
...(params.buffer
|
|
3174
|
+
? [{ binding: EFFECT_PARAMS_BINDING, visibility: GPUShaderStage.COMPUTE, buffer: { type: "uniform" } }]
|
|
3175
|
+
: []),
|
|
3129
3176
|
],
|
|
3130
3177
|
});
|
|
3131
3178
|
const module = this.device.createShaderModule({
|
|
3132
3179
|
label: "light emit",
|
|
3133
3180
|
// The same scene API and the same per-effect anchor alias its drawing
|
|
3134
3181
|
// half gets, so a lamp reads the cast exactly as the beam that paints it.
|
|
3135
|
-
code: buildLightEmitShader(wgsl, EFFECT_SCENE_API + anchorAliasWgsl(alias), { trailCount: anchors.filter((a) => a.trail).length }),
|
|
3182
|
+
code: buildLightEmitShader(wgsl, EFFECT_SCENE_API + anchorAliasWgsl(alias), { trailCount: anchors.filter((a) => a.trail).length }, params.wgsl(EFFECT_PARAMS_BINDING)),
|
|
3136
3183
|
});
|
|
3137
3184
|
const info = await module.getCompilationInfo();
|
|
3138
3185
|
const diagnostics = info.messages.filter((m) => m.type === "error").map((m) => `${m.lineNum}:${m.linePos} ${m.message}`);
|
|
@@ -3155,14 +3202,14 @@ export class Engine {
|
|
|
3155
3202
|
uniform.destroy();
|
|
3156
3203
|
return { ok: false, diagnostics: [scoped.message] };
|
|
3157
3204
|
}
|
|
3158
|
-
const bind = this.lightEmitBindGroup(layout, uniform);
|
|
3205
|
+
const bind = this.lightEmitBindGroup(layout, uniform, params.buffer);
|
|
3159
3206
|
// The layout travels with the state so the emitter can be rebound when a
|
|
3160
3207
|
// shared buffer under it is replaced — see rebindSharedBuffers.
|
|
3161
|
-
return { ok: true, state: { pipeline, bind, layout, uniform, data, count } };
|
|
3208
|
+
return { ok: true, state: { pipeline, bind, layout, uniform, data, count, params: params.buffer } };
|
|
3162
3209
|
}
|
|
3163
3210
|
/** The light emitter's bind group. One author, for the reason gridBindGroup
|
|
3164
3211
|
* gives: it is built once and rebuilt on every shared-buffer swap. */
|
|
3165
|
-
lightEmitBindGroup(layout, uniform) {
|
|
3212
|
+
lightEmitBindGroup(layout, uniform, params) {
|
|
3166
3213
|
return this.device.createBindGroup({
|
|
3167
3214
|
label: "light emit bind",
|
|
3168
3215
|
layout,
|
|
@@ -3174,6 +3221,7 @@ export class Engine {
|
|
|
3174
3221
|
{ binding: 4, resource: { buffer: this.audioBuffer } },
|
|
3175
3222
|
{ binding: 5, resource: { buffer: this.midiBuffer } },
|
|
3176
3223
|
{ binding: 6, resource: { buffer: this.lyricsBuffer } },
|
|
3224
|
+
...(params ? [{ binding: EFFECT_PARAMS_BINDING, resource: { buffer: params } }] : []),
|
|
3177
3225
|
],
|
|
3178
3226
|
});
|
|
3179
3227
|
}
|
|
@@ -3281,7 +3329,9 @@ export class Engine {
|
|
|
3281
3329
|
/** This effect's local→scene slot map. Passed rather than read off the
|
|
3282
3330
|
* engine: the builders run BEFORE the swap, so this.anchorTable still
|
|
3283
3331
|
* describes the effect that is still on screen. */
|
|
3284
|
-
alias
|
|
3332
|
+
alias,
|
|
3333
|
+
/** The effect's declared dials, spliced and bound at 7 as everywhere else. */
|
|
3334
|
+
params) {
|
|
3285
3335
|
// `slots` here is how many RIBBONS to draw — one per trailed anchor — which
|
|
3286
3336
|
// is a different number from the anchor ADDRESS SPACE the accessors index
|
|
3287
3337
|
// by. Conflating the two is what made a trail declared after a bare anchor
|
|
@@ -3291,7 +3341,7 @@ export class Engine {
|
|
|
3291
3341
|
// nothing before: ribbon i was read as anchor slot i.
|
|
3292
3342
|
const ribbonSlots = anchors.map((a, i) => (a.trail ? i : -1)).filter((i) => i >= 0);
|
|
3293
3343
|
const slots = ribbonSlots.length;
|
|
3294
|
-
const src = { wgsl, slots, ribbonSlots, blend: d.particleBlend, bloom: d.bloom };
|
|
3344
|
+
const src = { wgsl, slots, ribbonSlots, blend: d.particleBlend, bloom: d.bloom, paramsDecl: params.wgsl(EFFECT_PARAMS_BINDING) };
|
|
3295
3345
|
const code = buildTrailShader(src, {
|
|
3296
3346
|
subjects: MAX_EFFECT_SUBJECTS,
|
|
3297
3347
|
samples: TRAIL_SAMPLES,
|
|
@@ -3336,6 +3386,9 @@ export class Engine {
|
|
|
3336
3386
|
{ binding: 5, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "read-only-storage" } },
|
|
3337
3387
|
// The lyrics, for rzLyric*.
|
|
3338
3388
|
{ binding: 6, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "read-only-storage" } },
|
|
3389
|
+
...(params.buffer
|
|
3390
|
+
? [{ binding: 7, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }]
|
|
3391
|
+
: []),
|
|
3339
3392
|
],
|
|
3340
3393
|
});
|
|
3341
3394
|
// TWO targets, the scene pass's own: HDR colour and the aux (bloom mask,
|
|
@@ -3388,6 +3441,7 @@ export class Engine {
|
|
|
3388
3441
|
{ binding: 4, resource: { buffer: this.audioBuffer } },
|
|
3389
3442
|
{ binding: 5, resource: { buffer: this.midiBuffer } },
|
|
3390
3443
|
{ binding: 6, resource: { buffer: this.lyricsBuffer } },
|
|
3444
|
+
...(params.buffer ? [{ binding: 7, resource: { buffer: params.buffer } }] : []),
|
|
3391
3445
|
],
|
|
3392
3446
|
}),
|
|
3393
3447
|
mirrorBind: this.device.createBindGroup({
|
|
@@ -3399,6 +3453,7 @@ export class Engine {
|
|
|
3399
3453
|
{ binding: 4, resource: { buffer: this.audioBuffer } },
|
|
3400
3454
|
{ binding: 5, resource: { buffer: this.midiBuffer } },
|
|
3401
3455
|
{ binding: 6, resource: { buffer: this.lyricsBuffer } },
|
|
3456
|
+
...(params.buffer ? [{ binding: 7, resource: { buffer: params.buffer } }] : []),
|
|
3402
3457
|
],
|
|
3403
3458
|
}),
|
|
3404
3459
|
},
|
|
@@ -3595,7 +3650,9 @@ export class Engine {
|
|
|
3595
3650
|
/** This effect's local→scene slot map. Passed rather than read off the
|
|
3596
3651
|
* engine: the builders run BEFORE the swap, so this.anchorTable still
|
|
3597
3652
|
* describes the effect that is still on screen. */
|
|
3598
|
-
alias
|
|
3653
|
+
alias,
|
|
3654
|
+
/** The effect's declared dials, spliced and bound above the grid's own. */
|
|
3655
|
+
params) {
|
|
3599
3656
|
const size = Math.min(d.grid || 256, GRID_MAX);
|
|
3600
3657
|
const cast = {
|
|
3601
3658
|
subjects: MAX_EFFECT_SUBJECTS,
|
|
@@ -3606,7 +3663,7 @@ export class Engine {
|
|
|
3606
3663
|
trailCount: anchors.filter((x) => x.trail).length,
|
|
3607
3664
|
alias,
|
|
3608
3665
|
};
|
|
3609
|
-
const code = buildSimShader(wgsl, size, cast);
|
|
3666
|
+
const code = buildSimShader(wgsl, size, cast, params.wgsl(EFFECT_PARAMS_BINDING_GRID));
|
|
3610
3667
|
const offset = code.slice(0, code.indexOf(wgsl)).split("\n").length - 1;
|
|
3611
3668
|
this.device.pushErrorScope("validation");
|
|
3612
3669
|
const module = this.device.createShaderModule({ label: "grid step", code });
|
|
@@ -3635,6 +3692,9 @@ export class Engine {
|
|
|
3635
3692
|
{ binding: 6, visibility: GPUShaderStage.COMPUTE, buffer: { type: "uniform" } },
|
|
3636
3693
|
{ binding: 7, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3637
3694
|
{ binding: 8, visibility: GPUShaderStage.COMPUTE, buffer: { type: "read-only-storage" } },
|
|
3695
|
+
...(params.buffer
|
|
3696
|
+
? [{ binding: EFFECT_PARAMS_BINDING_GRID, visibility: GPUShaderStage.COMPUTE, buffer: { type: "uniform" } }]
|
|
3697
|
+
: []),
|
|
3638
3698
|
],
|
|
3639
3699
|
});
|
|
3640
3700
|
const make = (n) => this.device.createTexture({
|
|
@@ -3665,7 +3725,7 @@ export class Engine {
|
|
|
3665
3725
|
return { ok: false, diagnostics: [scoped.message] };
|
|
3666
3726
|
}
|
|
3667
3727
|
// One per parity: binds[i] READS textures[i] and WRITES the other.
|
|
3668
|
-
const bindFor = (i) => this.gridBindGroup({ layout, uniform, read, textures }, i);
|
|
3728
|
+
const bindFor = (i) => this.gridBindGroup({ layout, uniform, read, textures, params: params.buffer }, i);
|
|
3669
3729
|
return {
|
|
3670
3730
|
ok: true,
|
|
3671
3731
|
state: {
|
|
@@ -3679,6 +3739,7 @@ export class Engine {
|
|
|
3679
3739
|
data: new Float32Array(4),
|
|
3680
3740
|
parity: 0,
|
|
3681
3741
|
frame: 0,
|
|
3742
|
+
params: params.buffer,
|
|
3682
3743
|
},
|
|
3683
3744
|
};
|
|
3684
3745
|
}
|
|
@@ -6339,17 +6400,23 @@ export class Engine {
|
|
|
6339
6400
|
this.camera.attachControl(this.canvas);
|
|
6340
6401
|
}
|
|
6341
6402
|
setCameraTarget(modelOrVec, boneName, offset) {
|
|
6403
|
+
// panLocked tracks the BINDING, on every path that changes it — a target
|
|
6404
|
+
// set to a fixed point is pannable again, and forgetting it here is how the
|
|
6405
|
+
// pan stays dead after the follow was turned off. See Camera.panLocked.
|
|
6342
6406
|
if (modelOrVec === null) {
|
|
6407
|
+
this.camera.panLocked = false;
|
|
6343
6408
|
this.cameraTargetModel = null;
|
|
6344
6409
|
return;
|
|
6345
6410
|
}
|
|
6346
6411
|
if ("x" in modelOrVec && "y" in modelOrVec && "z" in modelOrVec) {
|
|
6412
|
+
this.camera.panLocked = false;
|
|
6347
6413
|
this.cameraTargetModel = null;
|
|
6348
6414
|
this.camera.target.x = modelOrVec.x;
|
|
6349
6415
|
this.camera.target.y = modelOrVec.y;
|
|
6350
6416
|
this.camera.target.z = modelOrVec.z;
|
|
6351
6417
|
return;
|
|
6352
6418
|
}
|
|
6419
|
+
this.camera.panLocked = true;
|
|
6353
6420
|
this.cameraTargetModel = modelOrVec;
|
|
6354
6421
|
this.cameraTargetBoneName = boneName ?? "";
|
|
6355
6422
|
this.cameraTargetOffset.x = offset?.x ?? 0;
|
|
@@ -6358,6 +6425,9 @@ export class Engine {
|
|
|
6358
6425
|
}
|
|
6359
6426
|
/** Souls-style follow cam: orbit center tracks a model bone each frame. Shorthand for setCameraTarget(model, boneName, offset). */
|
|
6360
6427
|
setCameraFollow(model, boneName, offset, smoothing) {
|
|
6428
|
+
// Panning sets the very thing the follow overwrites each frame, so it is
|
|
6429
|
+
// refused for as long as the shot is riding a bone. See Camera.panLocked.
|
|
6430
|
+
this.camera.panLocked = model !== null;
|
|
6361
6431
|
if (model === null) {
|
|
6362
6432
|
this.cameraTargetModel = null;
|
|
6363
6433
|
return;
|
|
@@ -7958,6 +8028,52 @@ export class Engine {
|
|
|
7958
8028
|
else
|
|
7959
8029
|
inst.hiddenMaterials.add(materialName);
|
|
7960
8030
|
}
|
|
8031
|
+
/**
|
|
8032
|
+
* Push a colour/shading edit straight into a material's own uniform buffer —
|
|
8033
|
+
* the same block createMaterialUniformBuffer wrote at load, offset for
|
|
8034
|
+
* offset. A single write per call, on the fields that actually moved, so
|
|
8035
|
+
* dragging one slider does not touch the other eleven.
|
|
8036
|
+
*
|
|
8037
|
+
* UNGROUPED materials only. A grouped material renders through its style
|
|
8038
|
+
* group's own compiled graph (setupPipelines' neutral/DEFAULT_GRAPH path is
|
|
8039
|
+
* what reads this buffer, and a grouped material never runs it) — the call
|
|
8040
|
+
* still writes the bytes, they are simply never sampled, which would look
|
|
8041
|
+
* like the edit silently failing. Callers check groupsByModel first and this
|
|
8042
|
+
* quietly no-ops rather than assume that check was made, since a document
|
|
8043
|
+
* edit landing to the WRONG channel (the group's own colour input) would be
|
|
8044
|
+
* a worse failure than one that does nothing.
|
|
8045
|
+
*
|
|
8046
|
+
* Structural fields — anything that changes which draw bucket a material is
|
|
8047
|
+
* in, or which texture it binds — are NOT here: alpha crossing the 1.0
|
|
8048
|
+
* opaque/transparent line, edge on/off, a texture swap, all need the draw
|
|
8049
|
+
* list or the bind group rebuilt, not a uniform write. Those get their own
|
|
8050
|
+
* call when something needs them.
|
|
8051
|
+
*/
|
|
8052
|
+
setMaterialUniforms(modelName, materialName, patch) {
|
|
8053
|
+
const inst = this.modelInstances.get(modelName);
|
|
8054
|
+
if (!inst)
|
|
8055
|
+
return false;
|
|
8056
|
+
const materials = inst.model.getMaterials();
|
|
8057
|
+
const index = materials.findIndex((m) => m.name === materialName);
|
|
8058
|
+
if (index < 0)
|
|
8059
|
+
return false;
|
|
8060
|
+
const buffer = inst.materialUniformBuffers[index];
|
|
8061
|
+
if (!buffer)
|
|
8062
|
+
return false;
|
|
8063
|
+
if (patch.diffuse) {
|
|
8064
|
+
this.device.queue.writeBuffer(buffer, 0, new Float32Array(patch.diffuse));
|
|
8065
|
+
}
|
|
8066
|
+
if (patch.ambient) {
|
|
8067
|
+
this.device.queue.writeBuffer(buffer, 16, new Float32Array(patch.ambient));
|
|
8068
|
+
}
|
|
8069
|
+
if (patch.specularPower !== undefined) {
|
|
8070
|
+
this.device.queue.writeBuffer(buffer, 28, new Float32Array([patch.specularPower]));
|
|
8071
|
+
}
|
|
8072
|
+
if (patch.specular) {
|
|
8073
|
+
this.device.queue.writeBuffer(buffer, 32, new Float32Array(patch.specular));
|
|
8074
|
+
}
|
|
8075
|
+
return true;
|
|
8076
|
+
}
|
|
7961
8077
|
isMaterialVisible(modelName, materialName) {
|
|
7962
8078
|
const inst = this.modelInstances.get(modelName);
|
|
7963
8079
|
return inst ? !inst.hiddenMaterials.has(materialName) : false;
|
package/dist/index.d.ts
CHANGED
|
@@ -36,4 +36,17 @@ export { RezePhysics } from "./physics";
|
|
|
36
36
|
export type { WindOptions } from "./physics/world";
|
|
37
37
|
export { RigidbodyShape, RigidbodyType, type Rigidbody, type Joint } from "./physics/types";
|
|
38
38
|
export { boneOverlay, boneMarkerPositions, rigidbodyOverlay, jointOverlay, buildOverlayShapes, lineBetween, writeOverlayInstance, OVERLAY_SHAPES, OVERLAY_INSTANCE_FLOATS, OVERLAY_VERTEX_FLOATS, DEFAULT_BONE_PALETTE, DEFAULT_RIGIDBODY_PALETTE, DEFAULT_JOINT_PALETTE, DEFAULT_VERTEX_COLOR, OVERLAY_STYLE, type OverlayPrimitive, type OverlayShape, type OverlayGeometry, type BoneOverlayOptions, type BoneClass, type RigidbodyOverlayOptions, type RigidbodyClass, type JointOverlayOptions, type RGBA, type Vec3Tuple, } from "./overlay";
|
|
39
|
+
/**
|
|
40
|
+
* The WGSL an effect's own source is compiled against — the hashes, the noise,
|
|
41
|
+
* the falloff, and the Particle struct the particle trio is written in terms of.
|
|
42
|
+
*
|
|
43
|
+
* Exported because reze-design renders LIBRARY PREVIEWS by hosting an effect's
|
|
44
|
+
* source itself, and a preview that reimplements this drifts from the renderer
|
|
45
|
+
* it is previewing. `rzHash13` meaning one thing on a card and another in the
|
|
46
|
+
* scene is worse than no card at all — and a preview missing them entirely just
|
|
47
|
+
* fails to compile, which is a blank card with nothing said anywhere.
|
|
48
|
+
*
|
|
49
|
+
* Pure strings, no GPU: importing them costs a host nothing.
|
|
50
|
+
*/
|
|
51
|
+
export { EFFECT_MATH_API, PARTICLE_STRUCT_WGSL } from "./shaders/passes/hosted-api";
|
|
39
52
|
//# 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":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -50,3 +50,16 @@ export { RigidbodyShape, RigidbodyType } from "./physics/types";
|
|
|
50
50
|
// engine methods that call them, so a host can produce the same list itself —
|
|
51
51
|
// to show it as data, diff two poses, or hit-test it on the CPU.
|
|
52
52
|
export { boneOverlay, boneMarkerPositions, rigidbodyOverlay, jointOverlay, buildOverlayShapes, lineBetween, writeOverlayInstance, OVERLAY_SHAPES, OVERLAY_INSTANCE_FLOATS, OVERLAY_VERTEX_FLOATS, DEFAULT_BONE_PALETTE, DEFAULT_RIGIDBODY_PALETTE, DEFAULT_JOINT_PALETTE, DEFAULT_VERTEX_COLOR, OVERLAY_STYLE, } from "./overlay";
|
|
53
|
+
/**
|
|
54
|
+
* The WGSL an effect's own source is compiled against — the hashes, the noise,
|
|
55
|
+
* the falloff, and the Particle struct the particle trio is written in terms of.
|
|
56
|
+
*
|
|
57
|
+
* Exported because reze-design renders LIBRARY PREVIEWS by hosting an effect's
|
|
58
|
+
* source itself, and a preview that reimplements this drifts from the renderer
|
|
59
|
+
* it is previewing. `rzHash13` meaning one thing on a card and another in the
|
|
60
|
+
* scene is worse than no card at all — and a preview missing them entirely just
|
|
61
|
+
* fails to compile, which is a blank card with nothing said anywhere.
|
|
62
|
+
*
|
|
63
|
+
* Pure strings, no GPU: importing them costs a host nothing.
|
|
64
|
+
*/
|
|
65
|
+
export { EFFECT_MATH_API, PARTICLE_STRUCT_WGSL } from "./shaders/passes/hosted-api";
|
package/dist/shaders/lights.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare function hasLightEmit(wgsl: string): boolean;
|
|
|
63
63
|
*/
|
|
64
64
|
export declare function buildLightEmitShader(wgsl: string, sceneApi: string, cast: {
|
|
65
65
|
trailCount: number;
|
|
66
|
-
}): string;
|
|
66
|
+
}, paramsDecl?: string): string;
|
|
67
67
|
/** The rz*Light accessors, with the buffer declared at the given binding. */
|
|
68
68
|
export declare function lightsApi(group: number, binding: number): string;
|
|
69
69
|
//# sourceMappingURL=lights.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lights.d.ts","sourceRoot":"","sources":["../../src/shaders/lights.ts"],"names":[],"mappings":"AAmCA;yEACyE;AACzE,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B,+CAA+C;AAC/C,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B;;;;;GAKG;AACH,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,kCAAkC;AAClC,eAAO,MAAM,aAAa,QAA2C,CAAA;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,qJAQhC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,idAa9B,CAAA;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"lights.d.ts","sourceRoot":"","sources":["../../src/shaders/lights.ts"],"names":[],"mappings":"AAmCA;yEACyE;AACzE,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B,+CAA+C;AAC/C,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B;;;;;GAKG;AACH,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,kCAAkC;AAClC,eAAO,MAAM,aAAa,QAA2C,CAAA;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,qJAQhC,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,idAa9B,CAAA;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EAC5B,UAAU,SAAK,GACd,MAAM,CAoFR;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAkEhE"}
|
package/dist/shaders/lights.js
CHANGED
|
@@ -117,7 +117,7 @@ export function hasLightEmit(wgsl) {
|
|
|
117
117
|
* did not know where she was could only sit where the fixture hangs. It also
|
|
118
118
|
* brings RzLight, which is why this builder does not declare it again.
|
|
119
119
|
*/
|
|
120
|
-
export function buildLightEmitShader(wgsl, sceneApi, cast) {
|
|
120
|
+
export function buildLightEmitShader(wgsl, sceneApi, cast, paramsDecl = "") {
|
|
121
121
|
return /* wgsl */ `
|
|
122
122
|
// read_write HERE and read-only in the material shaders. Different passes, so
|
|
123
123
|
// the two never coexist: this compute runs before the scene pass that reads it.
|
|
@@ -154,6 +154,10 @@ ${trailSlotsApi(cast.trailCount)}
|
|
|
154
154
|
// The id accessors, stubbed: this module cannot read an attachment the
|
|
155
155
|
// scene pass writes. See id-api.ts — the author's whole file compiles here.
|
|
156
156
|
${idApi(false, 0, 0) + castDistanceStub()}
|
|
157
|
+
// The dials the author declared, if any. A lamp is exactly the thing someone
|
|
158
|
+
// retunes — its colour and its reach — so an emitter reads params like every
|
|
159
|
+
// other mount rather than being the one place a #param resolves to nothing.
|
|
160
|
+
${paramsDecl}
|
|
157
161
|
${wgsl}
|
|
158
162
|
|
|
159
163
|
@compute @workgroup_size(64)
|
|
@@ -25,5 +25,5 @@ export declare function gridReadApi(group: number, tex: number, samp: number, si
|
|
|
25
25
|
* from", and that place is almost never a texel centre. Point-sampling it is
|
|
26
26
|
* what turns smoke into a staircase.
|
|
27
27
|
*/
|
|
28
|
-
export declare function buildSimShader(wgsl: string, size: number, cast: CastLayout): string;
|
|
28
|
+
export declare function buildSimShader(wgsl: string, size: number, cast: CastLayout, paramsDecl?: string): string;
|
|
29
29
|
//# sourceMappingURL=grid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/grid.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAkC7C,eAAO,MAAM,QAAQ,OAAO,CAAA;AAC5B,uEAAuE;AACvE,eAAO,MAAM,UAAU,EAAE,gBAAgC,CAAA;AAEzD,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA6B1F;AAWD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/grid.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAkC7C,eAAO,MAAM,QAAQ,OAAO,CAAA;AAC5B,uEAAuE;AACvE,eAAO,MAAM,UAAU,EAAE,gBAAgC,CAAA;AAEzD,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA6B1F;AAWD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,SAAK,GAAG,MAAM,CAiFpG"}
|
|
@@ -100,8 +100,11 @@ struct SimU {
|
|
|
100
100
|
* from", and that place is almost never a texel centre. Point-sampling it is
|
|
101
101
|
* what turns smoke into a staircase.
|
|
102
102
|
*/
|
|
103
|
-
export function buildSimShader(wgsl, size, cast) {
|
|
103
|
+
export function buildSimShader(wgsl, size, cast, paramsDecl = "") {
|
|
104
104
|
return (SIM_UNIFORMS +
|
|
105
|
+
// The effect's declared dials, ahead of everything that reads them. Empty
|
|
106
|
+
// when it declared none.
|
|
107
|
+
paramsDecl +
|
|
105
108
|
/* wgsl */ `
|
|
106
109
|
@group(0) @binding(0) var<uniform> su: SimU;
|
|
107
110
|
@group(0) @binding(1) var _rzGridPrevTex: texture_2d<f32>;
|
|
@@ -23,6 +23,10 @@ type ParticleSource = {
|
|
|
23
23
|
blend: ParticleBlend;
|
|
24
24
|
/** Feed the bloom pyramid. Off by default — rain should not glow. */
|
|
25
25
|
bloom: boolean;
|
|
26
|
+
/** The effect's `#param` block, or empty. Spliced into BOTH stages: a spawn
|
|
27
|
+
* rule reads the same dial the shading does, and rain's fall speed is used
|
|
28
|
+
* in the compute half. */
|
|
29
|
+
paramsDecl: string;
|
|
26
30
|
};
|
|
27
31
|
/** Bytes per particle. Explicitly padded — see the struct below. */
|
|
28
32
|
export declare const PARTICLE_STRIDE = 48;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/particles.ts"],"names":[],"mappings":"AA2BA,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb;;mFAE+E;IAC/E,UAAU,EAAE,MAAM,CAAA;IAClB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAiCD,qDAAqD;AACrD,KAAK,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEzC,KAAK,cAAc,GAAG;IACpB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,aAAa,CAAA;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/particles.ts"],"names":[],"mappings":"AA2BA,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAA;IACb;;mFAE+E;IAC/E,UAAU,EAAE,MAAM,CAAA;IAClB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAiCD,qDAAqD;AACrD,KAAK,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAEzC,KAAK,cAAc,GAAG;IACpB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,aAAa,CAAA;IACpB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAA;IACd;;+BAE2B;IAC3B,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,KAAK,CAAA;AAuDjC,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAMlG;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA8CxF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA6HvF"}
|
|
@@ -110,6 +110,7 @@ export function buildParticleComputeShader(src, cast) {
|
|
|
110
110
|
CAMERA_STRUCT +
|
|
111
111
|
PARTICLE_UNIFORMS +
|
|
112
112
|
`
|
|
113
|
+
${src.paramsDecl}
|
|
113
114
|
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
|
|
114
115
|
@group(0) @binding(1) var<uniform> pu: ParticleU;
|
|
115
116
|
@group(0) @binding(2) var<uniform> cam: CameraU;
|
|
@@ -169,6 +170,7 @@ override ADDITIVE: bool = ${src.blend === "additive" ? "true" : "false"};\n` +
|
|
|
169
170
|
CAMERA_STRUCT +
|
|
170
171
|
PARTICLE_UNIFORMS +
|
|
171
172
|
`
|
|
173
|
+
${src.paramsDecl}
|
|
172
174
|
@group(0) @binding(0) var<storage, read> particles: array<Particle>;
|
|
173
175
|
@group(0) @binding(1) var<uniform> pu: ParticleU;
|
|
174
176
|
@group(0) @binding(2) var<uniform> cam: CameraU;
|
|
@@ -6,6 +6,8 @@ type TrailSource = {
|
|
|
6
6
|
/** For each ribbon, the LOCAL anchor slot it belongs to. Identity when every
|
|
7
7
|
* anchor is trailed; otherwise it skips the untrailed ones. */
|
|
8
8
|
ribbonSlots: number[];
|
|
9
|
+
/** The effect's `#param` block, or empty. */
|
|
10
|
+
paramsDecl: string;
|
|
9
11
|
/** Additive, like most glowing ribbons, or straight alpha. */
|
|
10
12
|
blend: "alpha" | "additive";
|
|
11
13
|
bloom: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trails.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/trails.ts"],"names":[],"mappings":"AA2BA,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb;oEACgE;IAChE,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,8DAA8D;IAC9D,KAAK,EAAE,OAAO,GAAG,UAAU,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAKjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;kFAI8E;IAC9E,SAAS,EAAE,OAAO,CAAA;CACnB,GACA,MAAM,
|
|
1
|
+
{"version":3,"file":"trails.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/trails.ts"],"names":[],"mappings":"AA2BA,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb;oEACgE;IAChE,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,KAAK,EAAE,OAAO,GAAG,UAAU,CAAA;IAC3B,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAKjF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,EAAE,CAAA;IACf;;;;kFAI8E;IAC9E,SAAS,EAAE,OAAO,CAAA;CACnB,GACA,MAAM,CAiVR"}
|
package/package.json
CHANGED
package/src/camera.ts
CHANGED
|
@@ -44,6 +44,16 @@ export class Camera {
|
|
|
44
44
|
// Input state
|
|
45
45
|
private canvas: HTMLCanvasElement | null = null
|
|
46
46
|
private inputLocked: boolean = false
|
|
47
|
+
/**
|
|
48
|
+
* Panning is refused while the orbit rides a bone (Engine.setCameraFollow).
|
|
49
|
+
*
|
|
50
|
+
* A pan moves `target`, and the follow rewrites `target` from the bone every
|
|
51
|
+
* frame — so the drag was already going nowhere, it just spent a frame
|
|
52
|
+
* fighting for it and read as the camera stuttering under the pointer.
|
|
53
|
+
* Refusing it says the same thing the follow already means: while the shot is
|
|
54
|
+
* on somebody, where it points is theirs to decide.
|
|
55
|
+
*/
|
|
56
|
+
panLocked: boolean = false
|
|
47
57
|
private isDragging: boolean = false
|
|
48
58
|
private mouseButton: number | null = null // Track which mouse button is pressed (0 = left, 2 = right)
|
|
49
59
|
private lastMousePos = { x: 0, y: 0 }
|
|
@@ -430,7 +440,7 @@ export class Camera {
|
|
|
430
440
|
|
|
431
441
|
if (this.mouseButton === 2) {
|
|
432
442
|
// Right-click: pan the camera target
|
|
433
|
-
this.panCamera(deltaX, deltaY)
|
|
443
|
+
if (!this.panLocked) this.panCamera(deltaX, deltaY)
|
|
434
444
|
} else {
|
|
435
445
|
// Left-click (or default): rotate the camera
|
|
436
446
|
this.alpha += deltaX * this.angularSensitivity
|
|
@@ -549,7 +559,7 @@ export class Camera {
|
|
|
549
559
|
if (isPanGesture) {
|
|
550
560
|
// Primary gesture is pan (two-finger drag)
|
|
551
561
|
// Use panning similar to right-click pan
|
|
552
|
-
this.panCamera(midpointDeltaX, midpointDeltaY)
|
|
562
|
+
if (!this.panLocked) this.panCamera(midpointDeltaX, midpointDeltaY)
|
|
553
563
|
}
|
|
554
564
|
|
|
555
565
|
// Update tracking values
|