block object
This commit is contained in:
@@ -37,8 +37,6 @@ export class Blockstate {
|
||||
this.variants = json.variants;
|
||||
this.multipart = json.multipart;
|
||||
|
||||
// Memoize evaluated property combinations to prevent console spam
|
||||
// and speed up Sweep 2 recalculations.
|
||||
this._resolutionCache = new Map();
|
||||
}
|
||||
|
||||
@@ -95,7 +93,6 @@ export class Blockstate {
|
||||
}
|
||||
|
||||
resolveParts(properties) {
|
||||
// Create a deterministic cache key from the properties object
|
||||
const cacheKey = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(',');
|
||||
|
||||
if (this._resolutionCache.has(cacheKey)) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export class ResourceCache {
|
||||
export class Cache {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.handlers = new Map();
|
||||
12
diorama.js
12
diorama.js
@@ -80,12 +80,12 @@ export class Diorama {
|
||||
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
||||
|
||||
for (const block of this.world.blocks.values()) {
|
||||
minX = Math.min(minX, block.x);
|
||||
minY = Math.min(minY, block.y);
|
||||
minZ = Math.min(minZ, block.z);
|
||||
maxX = Math.max(maxX, block.x + 1);
|
||||
maxY = Math.max(maxY, block.y + 1);
|
||||
maxZ = Math.max(maxZ, block.z + 1);
|
||||
minX = Math.min(minX, block.pos[0]);
|
||||
minY = Math.min(minY, block.pos[1]);
|
||||
minZ = Math.min(minZ, block.pos[2]);
|
||||
maxX = Math.max(maxX, block.pos[0] + 1);
|
||||
maxY = Math.max(maxY, block.pos[1] + 1);
|
||||
maxZ = Math.max(maxZ, block.pos[2] + 1);
|
||||
}
|
||||
|
||||
this.target = [(minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2];
|
||||
|
||||
14
engine.js
14
engine.js
@@ -1,6 +1,6 @@
|
||||
// engine.js
|
||||
import {mat4Identity, mat4Translate} from './math.js';
|
||||
import {ResourceCache} from "./resource-cache.js";
|
||||
import {Cache} from "./cache.js";
|
||||
import {Blockstate, BlockstateHandler} from "./blockstate.js";
|
||||
import {ModelHandler} from "./model.js";
|
||||
import {TextureHandler} from "./texture.js";
|
||||
@@ -94,7 +94,7 @@ export class Engine {
|
||||
this.atlasTexture = gl.createTexture();
|
||||
|
||||
// --- Initialize the new Resource Pipeline ---
|
||||
this.cache = new ResourceCache('assets');
|
||||
this.cache = new Cache('assets');
|
||||
this.atlas = new TextureHandler(256);
|
||||
this.cache.register('blockstates', new BlockstateHandler());
|
||||
this.cache.register('models', new ModelHandler());
|
||||
@@ -143,7 +143,7 @@ export class Engine {
|
||||
|
||||
addDiorama(diorama) {
|
||||
this.dioramas.push(diorama);
|
||||
if (!this.observedWorlds.has(diorama.world)) {
|
||||
if (!this.observedWorlds.has(diorama.worlds)) {
|
||||
this.observedWorlds.add(diorama.world);
|
||||
diorama.world.subscribe(() => this.requestUpdate());
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export class Engine {
|
||||
const parsedBlocks = [];
|
||||
for (const block of world.blocks.values()) {
|
||||
const state = await this.cache.get(block.id, 'blockstates');
|
||||
const parts = state.resolveParts(block.props);
|
||||
const parts = state.resolveParts(block.state);
|
||||
parsedBlocks.push({block, parts});
|
||||
for (const p of parts) uniqueModelIds.add(p.model);
|
||||
}
|
||||
@@ -190,12 +190,12 @@ export class Engine {
|
||||
}
|
||||
|
||||
const matrix = mat4Identity(new Float32Array(16));
|
||||
mat4Translate(matrix, matrix, [block.x, block.y, block.z]);
|
||||
mat4Translate(matrix, matrix, block.pos);
|
||||
const pool = instancePool.get(hash);
|
||||
pool.matrices.push(...matrix);
|
||||
|
||||
if (block.props.power) {
|
||||
const p = parseInt(block.props.power, 10);
|
||||
if (block.state.power) {
|
||||
const p = parseInt(block.state.power, 10);
|
||||
pool.colors.push((0x4B + (p * 12)) / 255, 0.0, 0.0);
|
||||
} else {
|
||||
pool.colors.push(1.0, 1.0, 1.0);
|
||||
|
||||
104
index.html
104
index.html
@@ -25,16 +25,20 @@
|
||||
<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
|
||||
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.
|
||||
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>
|
||||
|
||||
@@ -44,11 +48,13 @@
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Curabitur tincidunt sapien et diam fringilla, eu accumsan odio faucibus. Orci varius natoque penatibus et magnis
|
||||
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="atlas-container" style="position: fixed; left: 0; top: 0; border: 1px solid white;"></div>
|
||||
<div id="atlas-container"
|
||||
style="position: fixed; left: 0; top: 0; border: 1px solid white;"></div>
|
||||
</main>
|
||||
|
||||
<script type="module">
|
||||
@@ -62,45 +68,57 @@
|
||||
document.getElementById('atlas-container')?.appendChild(engine.atlas.canvas);
|
||||
|
||||
const world1 = new World();
|
||||
world1.setBlock('minecraft:redstone_wire', -1, 0, 0, {
|
||||
world1.set('minecraft:redstone_wire', -1, 0, 0, {
|
||||
east: 'side',
|
||||
west: 'none',
|
||||
north: 'none',
|
||||
south: 'none',
|
||||
power: '15'
|
||||
});
|
||||
world1.setBlock('minecraft:repeater', 0, 0, 0, {
|
||||
world1.set('minecraft:repeater', 0, 0, 0, {
|
||||
facing: 'east',
|
||||
delay: '1',
|
||||
locked: 'false',
|
||||
powered: 'true'
|
||||
});
|
||||
world1.setBlock('minecraft:oak_trapdoor', 2, 0, 0, {facing: 'east', half: 'bottom', open: 'true'});
|
||||
world1.setBlock('lodestone', 0, -1, 0);
|
||||
world1.setBlock('piston', 0, -2, 0, {facing: 'north', extended: 'false'});
|
||||
world1.set('minecraft:oak_trapdoor', 2, 0, 0, {
|
||||
facing: 'east',
|
||||
half: 'bottom',
|
||||
open: 'true'
|
||||
});
|
||||
world1.set('lodestone', 0, -1, 0);
|
||||
world1.set('piston', 0, -2, 0, {facing: 'north', extended: 'false'});
|
||||
|
||||
const world2 = new World();
|
||||
world2.setBlock('minecraft:redstone_wire', 0, 0, 0, {
|
||||
world2.set('minecraft:redstone_wire', 0, 0, 0, {
|
||||
east: 'side',
|
||||
west: 'none',
|
||||
north: 'none',
|
||||
south: 'none',
|
||||
power: '0'
|
||||
});
|
||||
world2.setBlock('minecraft:repeater', 1, 0, 0, {
|
||||
world2.set('minecraft:repeater', 1, 0, 0, {
|
||||
facing: 'east',
|
||||
delay: '4',
|
||||
locked: 'false',
|
||||
powered: 'false'
|
||||
});
|
||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 0, {facing: 'east', extended: 'false'});
|
||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 1, {facing: 'east', extended: 'true'});
|
||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
||||
world2.setBlock('minecraft:piston_head', 2.5, 0, 1, {facing: 'east', short: 'true', type: 'sticky'});
|
||||
world2.setBlock('minecraft:piston_head', 3, 0, 2, {facing: 'east', short: 'false', type: 'sticky'});
|
||||
world2.setBlock('minecraft:glass', 3, 0, 0);
|
||||
world2.setBlock('minecraft:observer', 3.5, 0, 1, {facing: 'up', powered: 'true'});
|
||||
world2.setBlock('minecraft:redstone_block', 4, 0, 2);
|
||||
world2.set('minecraft:sticky_piston', 2, 0, 0, {facing: 'east', extended: 'false'});
|
||||
world2.set('minecraft:sticky_piston', 2, 0, 1, {facing: 'east', extended: 'true'});
|
||||
world2.set('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
||||
world2.set('minecraft:piston_head', 2.5, 0, 1, {
|
||||
facing: 'east',
|
||||
short: 'true',
|
||||
type: 'sticky'
|
||||
});
|
||||
world2.set('minecraft:piston_head', 3, 0, 2, {
|
||||
facing: 'east',
|
||||
short: 'false',
|
||||
type: 'sticky'
|
||||
});
|
||||
world2.set('minecraft:glass', 3, 0, 0);
|
||||
world2.set('minecraft:observer', 3.5, 0, 1, {facing: 'up', powered: 'true'});
|
||||
world2.set('minecraft:redstone_block', 4, 0, 2);
|
||||
|
||||
const demo1 = new Diorama('demo-1', world1, () => engine.requestRender())
|
||||
demo1.radius = 2;
|
||||
@@ -130,34 +148,46 @@
|
||||
await engine.updateAll();
|
||||
|
||||
function set0() {
|
||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'false'});
|
||||
world2.setBlock('minecraft:redstone_block', 3, 0, 2);
|
||||
world2.removeBlock(2.5, 0, 2);
|
||||
world2.removeBlock(3.5, 0, 2);
|
||||
world2.set('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'false'});
|
||||
world2.set('minecraft:redstone_block', 3, 0, 2);
|
||||
world2.del(2.5, 0, 2);
|
||||
world2.del(3.5, 0, 2);
|
||||
setTimeout(set1, 250)
|
||||
}
|
||||
|
||||
function set1() {
|
||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
||||
world2.removeBlock(3, 0, 2)
|
||||
world2.setBlock('minecraft:piston_head', 2.5, 0, 2, {facing: 'east', short: 'true', type: 'sticky'});
|
||||
world2.setBlock('minecraft:redstone_block', 3.5, 0, 2);
|
||||
world2.set('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
||||
world2.del(3, 0, 2)
|
||||
world2.set('minecraft:piston_head', 2.5, 0, 2, {
|
||||
facing: 'east',
|
||||
short: 'true',
|
||||
type: 'sticky'
|
||||
});
|
||||
world2.set('minecraft:redstone_block', 3.5, 0, 2);
|
||||
setTimeout(set2, 250)
|
||||
}
|
||||
|
||||
function set2() {
|
||||
world2.setBlock('minecraft:piston_head', 3, 0, 2, {facing: 'east', short: 'false', type: 'sticky'});
|
||||
world2.removeBlock(2.5, 0, 2);
|
||||
world2.removeBlock(3.5, 0, 2);
|
||||
world2.setBlock('minecraft:redstone_block', 4, 0, 2);
|
||||
world2.set('minecraft:piston_head', 3, 0, 2, {
|
||||
facing: 'east',
|
||||
short: 'false',
|
||||
type: 'sticky'
|
||||
});
|
||||
world2.del(2.5, 0, 2);
|
||||
world2.del(3.5, 0, 2);
|
||||
world2.set('minecraft:redstone_block', 4, 0, 2);
|
||||
setTimeout(set3, 250)
|
||||
}
|
||||
|
||||
function set3() {
|
||||
world2.removeBlock(4, 0, 2)
|
||||
world2.removeBlock(3, 0, 2)
|
||||
world2.setBlock('minecraft:piston_head', 2.5, 0, 2, {facing: 'east', short: 'true', type: 'sticky'});
|
||||
world2.setBlock('minecraft:redstone_block', 3.5, 0, 2);
|
||||
world2.del(4, 0, 2)
|
||||
world2.del(3, 0, 2)
|
||||
world2.set('minecraft:piston_head', 2.5, 0, 2, {
|
||||
facing: 'east',
|
||||
short: 'true',
|
||||
type: 'sticky'
|
||||
});
|
||||
world2.set('minecraft:redstone_block', 3.5, 0, 2);
|
||||
setTimeout(set0, 250)
|
||||
}
|
||||
|
||||
|
||||
71
world.js
71
world.js
@@ -1,42 +1,81 @@
|
||||
// world.js
|
||||
export class Block {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {[number, number, number]} pos
|
||||
* @param {Object} state
|
||||
* @param {Array<World>} world
|
||||
*/
|
||||
constructor(id, pos, state, world = []) {
|
||||
this.id = id;
|
||||
this.pos = pos;
|
||||
this.state = state;
|
||||
this.worlds = world;
|
||||
}
|
||||
|
||||
delete() {
|
||||
for (let world of this.worlds) {
|
||||
world.blocks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class World {
|
||||
constructor() {
|
||||
this.blocks = new Map();
|
||||
this.blocks = new Map();
|
||||
this.listeners = new Set();
|
||||
}
|
||||
|
||||
// Subscribe to changes (returns an unsubscribe function)
|
||||
/**
|
||||
* Subscribe to modify events.
|
||||
* @param {function()} callback. Called when this world is modified.
|
||||
* @returns {function()} Unsubscribe function.
|
||||
*/
|
||||
subscribe(callback) {
|
||||
this.listeners.add(callback);
|
||||
return () => this.listeners.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all listeners of a modification.
|
||||
*/
|
||||
notify() {
|
||||
for (const listener of this.listeners) listener();
|
||||
}
|
||||
|
||||
setBlock(id, x, y, z, props = {}) {
|
||||
/**
|
||||
* Set a block and state at a position.
|
||||
* @param {string} id
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} z
|
||||
* @param {Object} state
|
||||
*/
|
||||
set(id, x, y, z, state = {}) {
|
||||
const key = `${x},${y},${z}`;
|
||||
this.blocks.set(key, { id, x, y, z, props });
|
||||
this.blocks.set(key, new Block(id, [x, y, z], state));
|
||||
this.notify();
|
||||
}
|
||||
|
||||
removeBlock(x, y, z) {
|
||||
/**
|
||||
* Remove a block at a position.
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} z
|
||||
*/
|
||||
del(x, y, z) {
|
||||
if (this.blocks.delete(`${x},${y},${z}`)) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
updateBlock(x, y, z, newProps) {
|
||||
const key = `${x},${y},${z}`;
|
||||
const block = this.blocks.get(key);
|
||||
if (block) {
|
||||
block.props = { ...block.props, ...newProps };
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
getBlock(x, y, z) {
|
||||
/**
|
||||
* Get a block at a position.
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} z
|
||||
* @returns {any}
|
||||
*/
|
||||
get(x, y, z) {
|
||||
return this.blocks.get(`${x},${y},${z}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user