Initial commit: AtomicEngine v0.2
- Rust/WASM physics engine (grid, material, physics, render_buffer) - 16 material types (stone, sand, water, wood, fire, lava, glass, ice, oil, acid, steam, spark, smoke + player placeholder) - Fire system (fuel-based, flammability-scaled consumption, spread, smoke/sparks) - Lighting system (emission, flood-fill propagation, glow, reflection) - Per-material pixel texturing (wood grain, dirt specks, stone noise, flame variation) - WebGL2 renderer with camera (WASD move, mouse wheel zoom) - Native x86 debug binary (identical dimensions, FPS counter) - Large world 1024x768 with active-region simulation (camera + 200px margin) - UI material palette + FPS display
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
import { createEngine, M, MATERIAL_NAMES, MATERIAL_COLORS } from "./engine";
|
||||
import { Renderer } from "./renderer";
|
||||
import { Input } from "./input";
|
||||
import { Camera } from "./camera";
|
||||
|
||||
const GRID_WIDTH = 1024;
|
||||
const GRID_HEIGHT = 768;
|
||||
|
||||
const PAINT_MATERIALS = [M.SAND, M.WATER, M.LAVA, M.WOOD, M.FIRE, M.ICE, M.OIL, M.ACID, M.STONE, M.DIRT, M.GLASS];
|
||||
|
||||
async function main() {
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||
const renderer = new Renderer(canvas);
|
||||
const input = new Input();
|
||||
const camera = new Camera();
|
||||
camera.x = GRID_WIDTH / 2;
|
||||
camera.y = GRID_HEIGHT / 2;
|
||||
camera.zoom = 3;
|
||||
|
||||
const engine = await createEngine(GRID_WIDTH, GRID_HEIGHT);
|
||||
|
||||
engine.fillRect(0, 740, 1024, 28, M.STONE);
|
||||
engine.fillRect(440, 720, 144, 20, M.WOOD);
|
||||
engine.fillRect(460, 700, 104, 20, M.DIRT);
|
||||
engine.fillCircle(490, 560, 25, M.SAND);
|
||||
engine.fillCircle(520, 550, 20, M.SAND);
|
||||
engine.fillRect(430, 650, 40, 30, M.WATER);
|
||||
engine.fillRect(550, 650, 40, 25, M.LAVA);
|
||||
engine.fillRect(505, 640, 10, 16, M.ICE);
|
||||
engine.fillCircle(620, 580, 20, M.OIL);
|
||||
engine.fillRect(480, 620, 64, 30, M.WOOD);
|
||||
engine.fillRect(480, 600, 64, 20, M.FIRE);
|
||||
engine.fillRect(300, 720, 40, 20, M.GLASS);
|
||||
engine.fillCircle(200, 680, 18, M.SAND);
|
||||
engine.fillRect(100, 700, 30, 40, M.WOOD);
|
||||
engine.fillRect(750, 680, 60, 60, M.STONE);
|
||||
engine.fillCircle(780, 660, 15, M.LAVA);
|
||||
engine.fillRect(700, 720, 30, 20, M.ICE);
|
||||
|
||||
const rw = engine.renderWidth();
|
||||
const rh = engine.renderHeight();
|
||||
|
||||
camera.x = 512;
|
||||
camera.y = 600;
|
||||
|
||||
function resize() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
renderer.resize(window.innerWidth, window.innerHeight, dpr);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
let paintMat: number = M.SAND;
|
||||
|
||||
function screenToGrid(sx: number, sy: number) {
|
||||
const mx = (sx / window.innerWidth - 0.5) * rw / camera.zoom + camera.x;
|
||||
const my = (sy / window.innerHeight - 0.5) * rh / camera.zoom + camera.y;
|
||||
return { x: Math.floor(mx), y: Math.floor(my) };
|
||||
}
|
||||
|
||||
const panel = document.getElementById("panel")!;
|
||||
function buildPanel() {
|
||||
panel.innerHTML = "";
|
||||
PAINT_MATERIALS.forEach((mat, idx) => {
|
||||
const div = document.createElement("div");
|
||||
div.className = "mat" + (mat === paintMat ? " active" : "");
|
||||
div.dataset.mat = String(mat);
|
||||
div.innerHTML = `<div class="swatch" style="background:${MATERIAL_COLORS[mat]}"></div><div class="label"><span class="key">${idx}</span> ${MATERIAL_NAMES[mat]}</div>`;
|
||||
div.addEventListener("click", () => selectMat(mat));
|
||||
panel.appendChild(div);
|
||||
});
|
||||
}
|
||||
function selectMat(mat: number) {
|
||||
paintMat = mat;
|
||||
document.querySelectorAll(".mat").forEach((el) => {
|
||||
el.classList.toggle("active", (el as HTMLElement).dataset.mat === String(mat));
|
||||
});
|
||||
}
|
||||
buildPanel();
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
const idx = parseInt(e.key);
|
||||
if (idx >= 0 && idx < PAINT_MATERIALS.length) {
|
||||
selectMat(PAINT_MATERIALS[idx]);
|
||||
}
|
||||
});
|
||||
|
||||
let lastTime = performance.now();
|
||||
let simAccum = 0;
|
||||
const simStep = 1 / 60;
|
||||
let fpsFrames = 0;
|
||||
let fpsLast = performance.now();
|
||||
const fpsEl = document.getElementById("fps")!;
|
||||
|
||||
function loop(now: number) {
|
||||
const dt = Math.min((now - lastTime) / 1000, 0.1);
|
||||
lastTime = now;
|
||||
|
||||
fpsFrames++;
|
||||
if (now - fpsLast >= 500) {
|
||||
const fps = Math.round(fpsFrames / ((now - fpsLast) / 1000));
|
||||
fpsEl.textContent = `${fps} FPS`;
|
||||
fpsFrames = 0;
|
||||
fpsLast = now;
|
||||
}
|
||||
|
||||
simAccum += dt;
|
||||
while (simAccum >= simStep) {
|
||||
simAccum -= simStep;
|
||||
engine.simulate(
|
||||
Math.round(camera.x),
|
||||
Math.round(camera.y),
|
||||
camera.zoom,
|
||||
);
|
||||
}
|
||||
|
||||
const speed = 200;
|
||||
if (input.isDown("KeyA") || input.isDown("ArrowLeft")) camera.x -= speed * dt;
|
||||
if (input.isDown("KeyD") || input.isDown("ArrowRight")) camera.x += speed * dt;
|
||||
if (input.isDown("KeyW") || input.isDown("ArrowUp")) camera.y -= speed * dt;
|
||||
if (input.isDown("KeyS") || input.isDown("ArrowDown")) camera.y += speed * dt;
|
||||
if (input.isDown("Equal")) camera.setZoom(camera.zoom + 2 * dt);
|
||||
if (input.isDown("Minus")) camera.setZoom(camera.zoom - 2 * dt);
|
||||
if (input.wheel !== 0) {
|
||||
camera.setZoom(camera.zoom + input.wheel * 0.5);
|
||||
input.wheel = 0;
|
||||
}
|
||||
|
||||
if (input.mouseDown) {
|
||||
const g = screenToGrid(input.mouseX, input.mouseY);
|
||||
engine.fillCircle(g.x, g.y, 6, paintMat);
|
||||
}
|
||||
|
||||
camera.update(dt);
|
||||
engine.updateRender(
|
||||
Math.round(camera.x),
|
||||
Math.round(camera.y),
|
||||
camera.zoom,
|
||||
);
|
||||
|
||||
const ptr = engine.renderBufferPtr();
|
||||
const len = engine.renderBufferLen();
|
||||
const buf = new Uint8Array(engine.memory.buffer, ptr, len).slice(0);
|
||||
renderer.uploadTexture(buf, rw, rh);
|
||||
renderer.render();
|
||||
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user