// The sprite layer at its natural scale: hundreds of sprites bouncing at // frame rate, driven imperatively. The component face mounts the layer; the // motion loop grabs sprite handles via addSprite and rewrites positions with // setSprite from onFrame - signals carry structure, per-frame motion goes // straight to the layer (the same split as @solidrt/3d). Whatever moves, the // tree holds ONE texture leaf; each setSprite writes the sprite's arena // node, and the core flush publishes every moved pose as one coalesced // instance-buffer write per frame. // // The atlas is a real image (core's logo) sliced 2x2 by grid(): four frames, // each sprite drawing one quarter. Pixels built in code go to createAtlas as // they are; this path decodes PNG bytes imported with { type: "binary" } // first. import { decodeImage, onFrame, render } from "@solidrt/core" import { addSprite, createAtlas, createSpriteLayer, grid, setSprite } from "@solidrt/2d" import logoBytes from "./logo.png" with { type: "binary" } const COUNT = 500 const W = 720 const H = 720 const SPRITE = 48 function App() { let atlas = createAtlas(decodeImage(logoBytes), { label: "logo-atlas" }) let frames = grid(atlas, 2, 2) let layer = createSpriteLayer([atlas], { capacity: COUNT, label: "bounce" }) let view = layer.createView({ width: W, height: H, clearColor: [0.05, 0.05, 0.09, 1] }) // Simulation state lives in plain arrays; the layer holds the published // snapshot of it. let x = new Float32Array(COUNT) let y = new Float32Array(COUNT) let vx = new Float32Array(COUNT) let vy = new Float32Array(COUNT) let sprites = new Array(COUNT) for (let i = 0; i < COUNT; i++) { x[i] = SPRITE / 2 + Math.random() * (W - SPRITE) y[i] = SPRITE / 2 + Math.random() * (H - SPRITE) vx[i] = (Math.random() * 2 - 1) * 3 vy[i] = (Math.random() * 2 - 1) * 3 sprites[i] = addSprite(layer, { x: x[i], y: y[i], w: SPRITE, h: SPRITE, frame: frames[i % 4], rotation: Math.random() * Math.PI * 2, // A sprite faces its motion: mirrored on the UV side, so the flip is // free of the pose and instant under any transition. flipX: vx[i]! < 0, flipY: vy[i]! < 0, }) } onFrame(tick => { for (let i = 0; i < COUNT; i++) { let nx = x[i]! + vx[i]! let ny = y[i]! + vy[i]! if (nx < SPRITE / 2 || nx > W - SPRITE / 2) { vx[i] = -vx[i]! setSprite(sprites[i]!, { flipX: vx[i]! < 0 }) } else x[i] = nx if (ny < SPRITE / 2 || ny > H - SPRITE / 2) { vy[i] = -vy[i]! setSprite(sprites[i]!, { flipY: vy[i]! < 0 }) } else y[i] = ny setSprite(sprites[i]!, { x: x[i]!, y: y[i]!, rotation: tick / 1000 + i }) } }) return ( ) } render(() => )