Three stores, three importers and three pickers that differ only in their type are one of each (#85)
Refactoring round one: HttpFontStore/HttpSoundStore/HttpTextureStore collapse into a generic HttpFileStore over a FileStore contract; fontImport/soundImport/textureImport share assetImport with an ImportKind per sort; ImagePicker/FontPicker/SoundPicker sit on FilePicker and describe only their row. The group/comment drag logic leaves GraphCanvas for annotationDrag.ts (with its own unit test), declaredRows.ts replaces the duplicated row build in particles.ts and shaders.ts, and dead code in font/sound/compile/emission/transform/actions is gone. README: asset list, check count 958, the two-job CI, the Scenes wiki page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
15bdfdf07a
commit
cc6db5f350
42 files changed
+1375
-1650
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import type { ParticlesAsset } from '../core/model/Asset';
|
||||
import { compileParticles, type ParticlesBuild, particlesFingerprint } from '../core/particles/compile';
|
||||
import { graphFingerprint } from '../core/graph/Graph';
|
||||
import { compileParticles, type ParticlesBuild } from '../core/particles/compile';
|
||||
import { gameClock } from '../core/time';
|
||||
|
||||
/**
|
||||
@@ -22,12 +23,8 @@ export class ParticleLibrary {
|
||||
this.frozenAt = at;
|
||||
}
|
||||
|
||||
get frozen(): number | null {
|
||||
return this.frozenAt;
|
||||
}
|
||||
|
||||
buildFor(asset: ParticlesAsset): ParticlesBuild {
|
||||
const fingerprint = particlesFingerprint(asset.graph);
|
||||
const fingerprint = graphFingerprint(asset.graph);
|
||||
const cached = this.builds.get(asset.id);
|
||||
if (cached && cached.fingerprint === fingerprint) return cached.build;
|
||||
this.compiles += 1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ShaderAsset } from '../core/model/Asset';
|
||||
import { findAssetOf, type Project } from '../core/model/Project';
|
||||
import { compileShader, shaderFingerprint, type ShaderBuild } from '../core/shader/compile';
|
||||
import { graphFingerprint } from '../core/graph/Graph';
|
||||
import { compileShader, type ShaderBuild } from '../core/shader/compile';
|
||||
import { gameClock } from '../core/time';
|
||||
import type { ShaderSource } from './shaders';
|
||||
|
||||
@@ -25,7 +26,7 @@ export class ShaderLibrary {
|
||||
}
|
||||
|
||||
buildFor(asset: ShaderAsset): ShaderBuild {
|
||||
const fingerprint = shaderFingerprint(asset.graph);
|
||||
const fingerprint = graphFingerprint(asset.graph);
|
||||
const cached = this.builds.get(asset.id);
|
||||
if (cached && cached.fingerprint === fingerprint) return cached.build;
|
||||
this.compiles += 1;
|
||||
|
||||
+44
-47
@@ -110,42 +110,58 @@ function readUniforms(
|
||||
}
|
||||
|
||||
/**
|
||||
* A shader asset worn by one sprite mesh: the fragment replaces Pixi's in the sprite's own draw,
|
||||
* so there is no filter and no extra pass (#77). The program is shared (`GlProgram.from` caches
|
||||
* by source), the uniforms are this sprite's; `read` takes them from the item's bag.
|
||||
* A compiled graph as a Pixi program. The program is shared (`GlProgram.from` caches by source),
|
||||
* the uniforms are this wearer's; `read` takes them from the item's bag. `resources` are the slots
|
||||
* Pixi fills for the vertex stage in question – `Shader.resources` only accepts the slots its
|
||||
* constructor declared, so a subclass names them all up front.
|
||||
*/
|
||||
class GraphShader extends Shader {
|
||||
abstract class CompiledGraphShader extends Shader {
|
||||
private readonly uniforms: Record<string, number> | null;
|
||||
private readonly compiled: CompiledShader;
|
||||
readonly fragment: string;
|
||||
/** What `Mesh.shader` asks of a shader; kept in step with the mesh's own by `wear`. */
|
||||
texture: Texture = Texture.WHITE;
|
||||
|
||||
constructor(
|
||||
readonly assetId: string,
|
||||
compiled: CompiledShader,
|
||||
program: { vertex: string; name: string },
|
||||
resources: Record<string, unknown>,
|
||||
) {
|
||||
const group = uniformGroupOf(compiled);
|
||||
super({
|
||||
glProgram: GlProgram.from({
|
||||
vertex: SPRITE_VERTEX,
|
||||
fragment: compiled.fragment,
|
||||
name: `shader-${assetId}`,
|
||||
}),
|
||||
resources: {
|
||||
uTexture: Texture.WHITE.source,
|
||||
uSampler: Texture.WHITE.source.style,
|
||||
...(group ? { shaderUniforms: group } : {}),
|
||||
},
|
||||
glProgram: GlProgram.from({ vertex: program.vertex, fragment: compiled.fragment, name: program.name }),
|
||||
resources: { ...resources, ...(group ? { shaderUniforms: group } : {}) },
|
||||
});
|
||||
this.fragment = compiled.fragment;
|
||||
this.compiled = compiled;
|
||||
this.uniforms = group ? group.uniforms : null;
|
||||
}
|
||||
|
||||
/** True while this is still the shader `props` ask for, so it can be updated instead of rebuilt. */
|
||||
wears(id: string, compiled: CompiledShader): boolean {
|
||||
return this.assetId === id && this.fragment === compiled.fragment;
|
||||
}
|
||||
|
||||
read(props: PropertyBag, now: number): void {
|
||||
if (this.uniforms) readUniforms(this.uniforms, this.compiled, props, now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A shader asset worn by one sprite mesh: the fragment replaces Pixi's in the sprite's own draw,
|
||||
* so there is no filter and no extra pass (#77).
|
||||
*/
|
||||
class GraphShader extends CompiledGraphShader {
|
||||
/** What `Mesh.shader` asks of a shader; kept in step with the mesh's own by `wear`. */
|
||||
texture: Texture = Texture.WHITE;
|
||||
|
||||
constructor(assetId: string, compiled: CompiledShader) {
|
||||
super(
|
||||
assetId,
|
||||
compiled,
|
||||
{ vertex: SPRITE_VERTEX, name: `shader-${assetId}` },
|
||||
{ uTexture: Texture.WHITE.source, uSampler: Texture.WHITE.source.style },
|
||||
);
|
||||
}
|
||||
|
||||
/** Pixi binds a mesh's texture only for its own shader, so a custom one takes it here. */
|
||||
wear(texture: Texture): void {
|
||||
@@ -168,7 +184,7 @@ export function syncShader(mesh: Mesh, props: PropertyBag, shaders: ShaderSource
|
||||
const id = shaderOf(props);
|
||||
const compiled = id ? shaders.compiled(id) : null;
|
||||
const worn = wornBy(mesh);
|
||||
if (worn && compiled && worn.assetId === id && worn.fragment === compiled.fragment) {
|
||||
if (worn && compiled && worn.wears(id, compiled)) {
|
||||
worn.read(props, shaders.now);
|
||||
worn.wear(mesh.texture);
|
||||
return;
|
||||
@@ -195,26 +211,15 @@ export function syncShader(mesh: Mesh, props: PropertyBag, shaders: ShaderSource
|
||||
* The pipe breaks the batch for every field anyway, so unlike a shaded sprite (#77) this costs no
|
||||
* draw call at all: five hundred shaded particles are the one call five hundred plain ones were.
|
||||
*/
|
||||
class ParticleGraphShader extends Shader {
|
||||
private readonly uniforms: Record<string, number> | null;
|
||||
private readonly compiled: CompiledShader;
|
||||
readonly fragment: string;
|
||||
|
||||
constructor(
|
||||
readonly assetId: string,
|
||||
compiled: CompiledShader,
|
||||
) {
|
||||
const group = uniformGroupOf(compiled);
|
||||
super({
|
||||
glProgram: GlProgram.from({
|
||||
vertex: PARTICLE_VERTEX,
|
||||
fragment: compiled.fragment,
|
||||
name: `particle-shader-${assetId}`,
|
||||
}),
|
||||
// `GlParticleContainerAdaptor` writes the field's texture and the pipe's own uniforms into
|
||||
// whichever shader the container names – but `Shader.resources` only accepts the slots its
|
||||
// constructor declared, so both have to be here, empty, for Pixi to fill them.
|
||||
resources: {
|
||||
class ParticleGraphShader extends CompiledGraphShader {
|
||||
constructor(assetId: string, compiled: CompiledShader) {
|
||||
// `GlParticleContainerAdaptor` writes the field's texture and the pipe's own uniforms into
|
||||
// whichever shader the container names, so both slots have to exist, empty, for Pixi to fill.
|
||||
super(
|
||||
assetId,
|
||||
compiled,
|
||||
{ vertex: PARTICLE_VERTEX, name: `particle-shader-${assetId}` },
|
||||
{
|
||||
uTexture: Texture.WHITE.source,
|
||||
uSampler: new TextureStyle({}),
|
||||
uniforms: {
|
||||
@@ -223,16 +228,8 @@ class ParticleGraphShader extends Shader {
|
||||
uRound: { value: 1, type: 'f32' },
|
||||
uResolution: { value: [0, 0], type: 'vec2<f32>' },
|
||||
},
|
||||
...(group ? { shaderUniforms: group } : {}),
|
||||
},
|
||||
});
|
||||
this.fragment = compiled.fragment;
|
||||
this.compiled = compiled;
|
||||
this.uniforms = group ? group.uniforms : null;
|
||||
}
|
||||
|
||||
read(props: PropertyBag, now: number): void {
|
||||
if (this.uniforms) readUniforms(this.uniforms, this.compiled, props, now);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +246,7 @@ export function syncParticleShader(
|
||||
const id = shaderOf(props);
|
||||
const compiled = id ? shaders.compiled(id) : null;
|
||||
const worn = wornByField(container);
|
||||
if (worn && compiled && worn.assetId === id && worn.fragment === compiled.fragment) {
|
||||
if (worn && compiled && worn.wears(id, compiled)) {
|
||||
worn.read(props, shaders.now);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user