multiview. camera controls

This commit is contained in:
David Allemang
2026-07-04 10:43:11 -04:00
parent b4029d1bdf
commit 8249c4dd52
5 changed files with 352 additions and 141 deletions

View File

@@ -7,127 +7,127 @@
body {
background: #222;
color: #eee;
font-family: monospace;
padding: 20px;
font-family: sans-serif;
}
#canvas-container {
display: flex;
gap: 20px;
margin-top: 20px;
main {
width: 80ch;
margin-inline: auto;
}
canvas {
border: 1px solid #444;
}
#webgl-canvas {
width: 512px;
height: 512px;
.figure {
width: 100%;
height: 20em;
}
</style>
</head>
<body>
<h2>Renderer Data Pipeline Prototype</h2>
<div id="output">Loading and building geometry...</div>
<main>
<h1>Visualizer</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper ut mauris sed tempor. Nunc
vehicula tempor purus, non vestibulum libero ornare non. Morbi fringilla sapien diam, sed rhoncus lacus egestas
vel.
</p>
<div class="figure" id="demo-1"></div>
<p>
Curabitur vestibulum vitae orci ac laoreet. Donec vel imperdiet tortor. Vestibulum lobortis aliquam tellus,
vitae viverra nisi porttitor quis. Pellentesque id efficitur arcu. Nunc laoreet pulvinar ligula eu maximus.
Mauris ullamcorper accumsan dui vel pulvinar.
</p>
<div class="figure" id="demo-2"></div>
<p>
Curabitur tincidunt sapien et diam fringilla, eu accumsan odio faucibus. Orci varius natoque penatibus et magnis
dis parturient montes, nascetur ridiculus mus.
</p>
<div id="canvas-container">
<canvas id="webgl-canvas" width="1024" height="1024"></canvas>
<div id="atlas-container"></div>
</div>
</main>
<script type="module">
import {World} from './world.js';
import {Renderer} from './renderer.js';
import {Engine} from './engine.js';
import {Diorama} from './diorama.js';
async function testPipeline() {
async function initDocument() {
try {
const webglCanvas = document.getElementById('webgl-canvas');
const renderer = new Renderer(webglCanvas);
// Adjusted camera slightly to see the block underneath
renderer.setCamera(-3, 3, -3, 3, 5, 4, 5, 0, 0, 0);
// 1. Initialize the global background renderer
const engine = new Engine();
document.getElementById('atlas-container').appendChild(renderer.atlas.canvas);
// Append the texture atlas to the bottom for debugging
document.getElementById('atlas-container').appendChild(engine.atlas.canvas);
const world = new World();
// 2. Setup Figure 1 (Shared: Wire & Repeater | Unique: Trapdoor)
const demo1 = new Diorama('demo-1', engine);
// Adjust camera to fit the small viewport
demo1.camera.radius = 2;
demo1.camera.phi = 10
demo1.camera.theta = -5
// Initial State: Closed trapdoor, no redstone block
world.setBlock('minecraft:oak_trapdoor', 0, 0, 0, {
facing: 'east',
open: 'false',
half: 'bottom'
demo1.world.setBlock('minecraft:redstone_wire', 0, 0, 0, {
east: 'side',
west: 'none',
north: 'none',
south: 'none',
power: '15'
});
demo1.world.setBlock('minecraft:repeater', 1, 0, 0, {
facing: 'east',
delay: '1',
locked: 'false',
powered: 'true'
});
demo1.world.setBlock('minecraft:oak_trapdoor', 2, 0, 0, {facing: 'east', half: 'bottom', open: 'true'});
world.setBlock('minecraft:coal_block', 0, -1, -3); // north -z
world.setBlock('minecraft:diamond_block', 3, -1, 0); // east +x
world.setBlock('minecraft:gold_block', 0, -1, 3); // south +z
world.setBlock('minecraft:iron_block', -3, -1, 0); // west -x
demo1.centerView()
// NOTE the trapdoor should open AWAY from the diamond block
// the repeaters should point "inward"
// the pistons should extend "outward"
engine.addDiorama(demo1);
// world.setBlock('minecraft:glass', 1, 0, 0);
// world.setBlock('minecraft:glass', 0, 0, 1);
world.setBlock('minecraft:coal_block', 0, -1, 0);
world.setBlock('minecraft:repeater', 1, -1, 0, {facing: 'west', delay: 1, locked: false, powered: false});
world.setBlock('minecraft:comparator', 0, -1, 1, {facing: 'north', mode: 'compare', powered: false});
// 3. Setup Figure 2 (Shared: Wire & Repeater | Unique: Piston & Redstone Block)
const demo2 = new Diorama('demo-2', engine);
// Adjust camera to fit the small viewport
demo2.camera.radius = 3;
// View from a slightly different angle
world.setBlock('minecraft:piston', 0, 0, -3, {facing: "north", extended: false})
world.setBlock('minecraft:piston', 3, 0, 0, {facing: "east", extended: false})
world.setBlock('minecraft:piston', 0, 0, 3, {facing: "south", extended: false})
world.setBlock('minecraft:piston', -3, 0, 0, {facing: "west", extended: false})
demo2.world.setBlock('minecraft:redstone_wire', 0, 0, 0, {
east: 'side',
west: 'none',
north: 'none',
south: 'none',
power: '0'
});
demo2.world.setBlock('minecraft:repeater', 1, 0, 0, {
facing: 'east',
delay: '4',
locked: 'false',
powered: 'false'
});
demo2.world.setBlock('minecraft:piston', 2, 0, 0, {facing: 'east', extended: 'false'});
demo2.world.setBlock('minecraft:piston', 2, 0, 1, {facing: 'east', extended: 'true'});
demo2.world.setBlock('minecraft:piston', 2, 0, 2, {facing: 'east', extended: 'true'});
demo2.world.setBlock('minecraft:piston_head', 2.5, 0, 1, {facing: 'east', short: 'true',type: 'sticky'});
demo2.world.setBlock('minecraft:piston_head', 3, 0, 2, {facing: 'east', short: 'false',type: 'normal'});
demo2.world.setBlock('minecraft:redstone_block', 3, 0, 0);
demo2.world.setBlock('minecraft:redstone_block', 3.5, 0, 1);
demo2.world.setBlock('minecraft:redstone_block', 4, 0, 2);
world.setBlock('minecraft:repeater', 0, 1, -3, {facing: "north", delay: 1, locked: true, powered: false})
world.setBlock('minecraft:repeater', 3, 1, 0, {facing: "east", delay: 2, locked: true, powered: false})
world.setBlock('minecraft:repeater', 0, 1, 3, {facing: "south", delay: 3, locked: true, powered: false})
world.setBlock('minecraft:repeater', -3, 1, 0, {facing: "west", delay: 4, locked: true, powered: false})
demo2.centerView()
engine.addDiorama(demo2);
await renderer.updateWorld(world);
// Render Loop (Static, just drawing the current buffers)
function animate() {
renderer.render();
requestAnimationFrame(animate);
}
animate();
// --- THE STATE MACHINE LOOP ---
let isOpen = false;
setInterval(async () => {
isOpen = !isOpen;
// 1. Mutate the world data
world.updateBlock(0, 0, 0, {open: isOpen.toString()});
if (isOpen) {
world.setBlock('minecraft:redstone_block', 0, -1, 0);
world.updateBlock(1, -1, 0, {powered: true})
world.updateBlock(0, -1, 1, {powered: true})
} else {
world.removeBlock(0, -1, 0);
world.updateBlock(1, -1, 0, {powered: false})
world.updateBlock(0, -1, 1, {mode: 'subtract', powered: false})
}
// 2. Tell the renderer to sync with the new world state
await renderer.updateWorld(world);
document.getElementById('output').innerHTML = `<strong>State Demo:</strong> Trapdoor is ${isOpen ? 'OPEN' : 'CLOSED'}`;
// await return new Promise((resolve) => setTimeout(resolve, time));
}, 1000);
// 4. Build geometries and draw
// We await these so the meshes are fully baked and the atlas is populated
await demo1.update();
await demo2.update();
} catch (err) {
console.error(err);
document.getElementById('output').textContent = 'Error: ' + err.message;
console.error("Renderer Initialization Failed:", err);
}
}
testPipeline();
initDocument();
</script>
</body>
</html>