Frames: cut each frame into its own GPU texture, no more fill matrix (#47)
game-maker / suite (push) Successful in 8m18s
game-maker / suite (push) Successful in 8m18s
A frame drawn straight from the sheet showed a hairline of the neighbouring cell along any edge that fell inside a screen pixel: with antialias on, a partly covered pixel is still evaluated at its centre, past the edge. On top of that Pixi rewrites a Graphics fill texture from clamp-to-edge to repeat at draw time (generateTextureFillMatrix.mjs), so the top row of a sheet read its bottom row. TextureLibrary.get(ref) now cuts the rectangle out of the loaded sheet with an OffscreenCanvas on first use and uploads the cut (key file|x,y,w,h, kept for the session). Address mode is mirror-repeat: Pixi only rewrites clamp-to-edge, and within a texel of the border mirror behaves like clamp, for linear as well as nearest. Loaded sheets get it too, since whole pictures wrapped at their own borders the same way. viewport/textureFill.ts and its test are gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
06e1494f94
commit
05f7a2745e
9 files changed
+150
-150
No files matched your search
@@ -338,6 +338,68 @@ export async function animation(t) {
|
||||
);
|
||||
await shot('03-scene-first-frame');
|
||||
|
||||
// 25j'. A frame is a texture of its own. Drawn from the sheet, an edge that landed inside a screen
|
||||
// pixel showed the neighbouring cell: the partly covered pixel is evaluated at its centre,
|
||||
// past the edge. Measured in play, where the background is flat.
|
||||
await mutate(`__gm.state.project.assets.find((a) => a.id === 'an-sprite').props.texture = 'an-f1'`);
|
||||
const rgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
||||
const fraction = (v) => v - Math.floor(v);
|
||||
const edgesOf = async () => {
|
||||
const r = await json("__gm.gameRect('an-box')");
|
||||
return { y: Math.round(r.y), left: r.x - r.w / 2, right: r.x + r.w / 2, scale: r.w / 80 };
|
||||
};
|
||||
// Moves the box so the edge's fraction lands where the pixel is partly covered but its centre
|
||||
// lies outside; `outward` says which way the neighbouring cell would be.
|
||||
const edgePixels = async (side, target, outward) => {
|
||||
await t.play();
|
||||
await sleep(300);
|
||||
const first = await edgesOf();
|
||||
await stop();
|
||||
await sleep(200);
|
||||
const shift = (target - fraction(first[side]) + 1) % 1;
|
||||
await mutate(
|
||||
`__gm.state.activeScene.objects.find((o) => o.id === 'an-box').position.x += ${shift / first.scale}`,
|
||||
);
|
||||
await t.play();
|
||||
await sleep(300);
|
||||
const e = await edgesOf();
|
||||
const x = Math.floor(e[side]);
|
||||
const edge = rgb(await json(`__gm.pixelAt(${x}, ${e.y})`));
|
||||
const outside = rgb(await json(`__gm.pixelAt(${x + outward * 2}, ${e.y})`));
|
||||
await stop();
|
||||
await sleep(200);
|
||||
return { fraction: fraction(e[side]).toFixed(2), edge, outside };
|
||||
};
|
||||
// The edge pixel is the cell's green over the background and nothing else: red and blue rise
|
||||
// above the background only in the cell's own proportion to the rise in green (plus rounding),
|
||||
// and green rises enough to prove it is the partly covered pixel, not plain background.
|
||||
const cell = rgb(CELLS[1]);
|
||||
const onlyOwnColour = ({ edge, outside }) => {
|
||||
const green = edge[1] - outside[1];
|
||||
return (
|
||||
green > 10 &&
|
||||
[0, 2].every(
|
||||
(c) => edge[c] - outside[c] <= (green * (cell[c] - outside[c])) / (cell[1] - outside[1]) + 3,
|
||||
)
|
||||
);
|
||||
};
|
||||
const leftEdge = await edgePixels('left', 0.75, -1);
|
||||
check(
|
||||
'the left edge of a frame shows nothing of the cell to its left',
|
||||
onlyOwnColour(leftEdge),
|
||||
JSON.stringify(leftEdge),
|
||||
);
|
||||
const rightEdge = await edgePixels('right', 0.25, 1);
|
||||
check(
|
||||
'nor the right edge anything of the cell to its right',
|
||||
onlyOwnColour(rightEdge),
|
||||
JSON.stringify(rightEdge),
|
||||
);
|
||||
await mutate(
|
||||
`__gm.state.project.assets.find((a) => a.id === 'an-sprite').props.texture = ${JSON.stringify(walk.id)}`,
|
||||
);
|
||||
await sleep(300);
|
||||
|
||||
// 25k. Play runs it: the pixels the game draws change colour over time.
|
||||
await t.play();
|
||||
await sleep(150);
|
||||
|
||||
Reference in new issue
Block a user