block object
This commit is contained in:
@@ -37,8 +37,6 @@ export class Blockstate {
|
|||||||
this.variants = json.variants;
|
this.variants = json.variants;
|
||||||
this.multipart = json.multipart;
|
this.multipart = json.multipart;
|
||||||
|
|
||||||
// Memoize evaluated property combinations to prevent console spam
|
|
||||||
// and speed up Sweep 2 recalculations.
|
|
||||||
this._resolutionCache = new Map();
|
this._resolutionCache = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +93,6 @@ export class Blockstate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolveParts(properties) {
|
resolveParts(properties) {
|
||||||
// Create a deterministic cache key from the properties object
|
|
||||||
const cacheKey = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(',');
|
const cacheKey = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(',');
|
||||||
|
|
||||||
if (this._resolutionCache.has(cacheKey)) {
|
if (this._resolutionCache.has(cacheKey)) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export class ResourceCache {
|
export class Cache {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.handlers = new Map();
|
this.handlers = new Map();
|
||||||
12
diorama.js
12
diorama.js
@@ -80,12 +80,12 @@ export class Diorama {
|
|||||||
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
||||||
|
|
||||||
for (const block of this.world.blocks.values()) {
|
for (const block of this.world.blocks.values()) {
|
||||||
minX = Math.min(minX, block.x);
|
minX = Math.min(minX, block.pos[0]);
|
||||||
minY = Math.min(minY, block.y);
|
minY = Math.min(minY, block.pos[1]);
|
||||||
minZ = Math.min(minZ, block.z);
|
minZ = Math.min(minZ, block.pos[2]);
|
||||||
maxX = Math.max(maxX, block.x + 1);
|
maxX = Math.max(maxX, block.pos[0] + 1);
|
||||||
maxY = Math.max(maxY, block.y + 1);
|
maxY = Math.max(maxY, block.pos[1] + 1);
|
||||||
maxZ = Math.max(maxZ, block.z + 1);
|
maxZ = Math.max(maxZ, block.pos[2] + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.target = [(minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2];
|
this.target = [(minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2];
|
||||||
|
|||||||
14
engine.js
14
engine.js
@@ -1,6 +1,6 @@
|
|||||||
// engine.js
|
// engine.js
|
||||||
import {mat4Identity, mat4Translate} from './math.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 {Blockstate, BlockstateHandler} from "./blockstate.js";
|
||||||
import {ModelHandler} from "./model.js";
|
import {ModelHandler} from "./model.js";
|
||||||
import {TextureHandler} from "./texture.js";
|
import {TextureHandler} from "./texture.js";
|
||||||
@@ -94,7 +94,7 @@ export class Engine {
|
|||||||
this.atlasTexture = gl.createTexture();
|
this.atlasTexture = gl.createTexture();
|
||||||
|
|
||||||
// --- Initialize the new Resource Pipeline ---
|
// --- Initialize the new Resource Pipeline ---
|
||||||
this.cache = new ResourceCache('assets');
|
this.cache = new Cache('assets');
|
||||||
this.atlas = new TextureHandler(256);
|
this.atlas = new TextureHandler(256);
|
||||||
this.cache.register('blockstates', new BlockstateHandler());
|
this.cache.register('blockstates', new BlockstateHandler());
|
||||||
this.cache.register('models', new ModelHandler());
|
this.cache.register('models', new ModelHandler());
|
||||||
@@ -143,7 +143,7 @@ export class Engine {
|
|||||||
|
|
||||||
addDiorama(diorama) {
|
addDiorama(diorama) {
|
||||||
this.dioramas.push(diorama);
|
this.dioramas.push(diorama);
|
||||||
if (!this.observedWorlds.has(diorama.world)) {
|
if (!this.observedWorlds.has(diorama.worlds)) {
|
||||||
this.observedWorlds.add(diorama.world);
|
this.observedWorlds.add(diorama.world);
|
||||||
diorama.world.subscribe(() => this.requestUpdate());
|
diorama.world.subscribe(() => this.requestUpdate());
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ export class Engine {
|
|||||||
const parsedBlocks = [];
|
const parsedBlocks = [];
|
||||||
for (const block of world.blocks.values()) {
|
for (const block of world.blocks.values()) {
|
||||||
const state = await this.cache.get(block.id, 'blockstates');
|
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});
|
parsedBlocks.push({block, parts});
|
||||||
for (const p of parts) uniqueModelIds.add(p.model);
|
for (const p of parts) uniqueModelIds.add(p.model);
|
||||||
}
|
}
|
||||||
@@ -190,12 +190,12 @@ export class Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const matrix = mat4Identity(new Float32Array(16));
|
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);
|
const pool = instancePool.get(hash);
|
||||||
pool.matrices.push(...matrix);
|
pool.matrices.push(...matrix);
|
||||||
|
|
||||||
if (block.props.power) {
|
if (block.state.power) {
|
||||||
const p = parseInt(block.props.power, 10);
|
const p = parseInt(block.state.power, 10);
|
||||||
pool.colors.push((0x4B + (p * 12)) / 255, 0.0, 0.0);
|
pool.colors.push((0x4B + (p * 12)) / 255, 0.0, 0.0);
|
||||||
} else {
|
} else {
|
||||||
pool.colors.push(1.0, 1.0, 1.0);
|
pool.colors.push(1.0, 1.0, 1.0);
|
||||||
|
|||||||
104
index.html
104
index.html
@@ -25,16 +25,20 @@
|
|||||||
<main>
|
<main>
|
||||||
<h1>Visualizer</h1>
|
<h1>Visualizer</h1>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper ut mauris sed tempor. Nunc
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ullamcorper ut mauris sed
|
||||||
vehicula tempor purus, non vestibulum libero ornare non. Morbi fringilla sapien diam, sed rhoncus lacus egestas
|
tempor. Nunc
|
||||||
|
vehicula tempor purus, non vestibulum libero ornare non. Morbi fringilla sapien diam, sed
|
||||||
|
rhoncus lacus egestas
|
||||||
vel.
|
vel.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="figure" id="demo-1"></div>
|
<div class="figure" id="demo-1"></div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Curabitur vestibulum vitae orci ac laoreet. Donec vel imperdiet tortor. Vestibulum lobortis aliquam tellus,
|
Curabitur vestibulum vitae orci ac laoreet. Donec vel imperdiet tortor. Vestibulum lobortis
|
||||||
vitae viverra nisi porttitor quis. Pellentesque id efficitur arcu. Nunc laoreet pulvinar ligula eu maximus.
|
aliquam tellus,
|
||||||
|
vitae viverra nisi porttitor quis. Pellentesque id efficitur arcu. Nunc laoreet pulvinar
|
||||||
|
ligula eu maximus.
|
||||||
Mauris ullamcorper accumsan dui vel pulvinar.
|
Mauris ullamcorper accumsan dui vel pulvinar.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -44,11 +48,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<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.
|
dis parturient montes, nascetur ridiculus mus.
|
||||||
</p>
|
</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>
|
</main>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -62,45 +68,57 @@
|
|||||||
document.getElementById('atlas-container')?.appendChild(engine.atlas.canvas);
|
document.getElementById('atlas-container')?.appendChild(engine.atlas.canvas);
|
||||||
|
|
||||||
const world1 = new World();
|
const world1 = new World();
|
||||||
world1.setBlock('minecraft:redstone_wire', -1, 0, 0, {
|
world1.set('minecraft:redstone_wire', -1, 0, 0, {
|
||||||
east: 'side',
|
east: 'side',
|
||||||
west: 'none',
|
west: 'none',
|
||||||
north: 'none',
|
north: 'none',
|
||||||
south: 'none',
|
south: 'none',
|
||||||
power: '15'
|
power: '15'
|
||||||
});
|
});
|
||||||
world1.setBlock('minecraft:repeater', 0, 0, 0, {
|
world1.set('minecraft:repeater', 0, 0, 0, {
|
||||||
facing: 'east',
|
facing: 'east',
|
||||||
delay: '1',
|
delay: '1',
|
||||||
locked: 'false',
|
locked: 'false',
|
||||||
powered: 'true'
|
powered: 'true'
|
||||||
});
|
});
|
||||||
world1.setBlock('minecraft:oak_trapdoor', 2, 0, 0, {facing: 'east', half: 'bottom', open: 'true'});
|
world1.set('minecraft:oak_trapdoor', 2, 0, 0, {
|
||||||
world1.setBlock('lodestone', 0, -1, 0);
|
facing: 'east',
|
||||||
world1.setBlock('piston', 0, -2, 0, {facing: 'north', extended: 'false'});
|
half: 'bottom',
|
||||||
|
open: 'true'
|
||||||
|
});
|
||||||
|
world1.set('lodestone', 0, -1, 0);
|
||||||
|
world1.set('piston', 0, -2, 0, {facing: 'north', extended: 'false'});
|
||||||
|
|
||||||
const world2 = new World();
|
const world2 = new World();
|
||||||
world2.setBlock('minecraft:redstone_wire', 0, 0, 0, {
|
world2.set('minecraft:redstone_wire', 0, 0, 0, {
|
||||||
east: 'side',
|
east: 'side',
|
||||||
west: 'none',
|
west: 'none',
|
||||||
north: 'none',
|
north: 'none',
|
||||||
south: 'none',
|
south: 'none',
|
||||||
power: '0'
|
power: '0'
|
||||||
});
|
});
|
||||||
world2.setBlock('minecraft:repeater', 1, 0, 0, {
|
world2.set('minecraft:repeater', 1, 0, 0, {
|
||||||
facing: 'east',
|
facing: 'east',
|
||||||
delay: '4',
|
delay: '4',
|
||||||
locked: 'false',
|
locked: 'false',
|
||||||
powered: 'false'
|
powered: 'false'
|
||||||
});
|
});
|
||||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 0, {facing: 'east', extended: 'false'});
|
world2.set('minecraft:sticky_piston', 2, 0, 0, {facing: 'east', extended: 'false'});
|
||||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 1, {facing: 'east', extended: 'true'});
|
world2.set('minecraft:sticky_piston', 2, 0, 1, {facing: 'east', extended: 'true'});
|
||||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
world2.set('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.set('minecraft:piston_head', 2.5, 0, 1, {
|
||||||
world2.setBlock('minecraft:piston_head', 3, 0, 2, {facing: 'east', short: 'false', type: 'sticky'});
|
facing: 'east',
|
||||||
world2.setBlock('minecraft:glass', 3, 0, 0);
|
short: 'true',
|
||||||
world2.setBlock('minecraft:observer', 3.5, 0, 1, {facing: 'up', powered: 'true'});
|
type: 'sticky'
|
||||||
world2.setBlock('minecraft:redstone_block', 4, 0, 2);
|
});
|
||||||
|
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())
|
const demo1 = new Diorama('demo-1', world1, () => engine.requestRender())
|
||||||
demo1.radius = 2;
|
demo1.radius = 2;
|
||||||
@@ -130,34 +148,46 @@
|
|||||||
await engine.updateAll();
|
await engine.updateAll();
|
||||||
|
|
||||||
function set0() {
|
function set0() {
|
||||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'false'});
|
world2.set('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'false'});
|
||||||
world2.setBlock('minecraft:redstone_block', 3, 0, 2);
|
world2.set('minecraft:redstone_block', 3, 0, 2);
|
||||||
world2.removeBlock(2.5, 0, 2);
|
world2.del(2.5, 0, 2);
|
||||||
world2.removeBlock(3.5, 0, 2);
|
world2.del(3.5, 0, 2);
|
||||||
setTimeout(set1, 250)
|
setTimeout(set1, 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
function set1() {
|
function set1() {
|
||||||
world2.setBlock('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
world2.set('minecraft:sticky_piston', 2, 0, 2, {facing: 'east', extended: 'true'});
|
||||||
world2.removeBlock(3, 0, 2)
|
world2.del(3, 0, 2)
|
||||||
world2.setBlock('minecraft:piston_head', 2.5, 0, 2, {facing: 'east', short: 'true', type: 'sticky'});
|
world2.set('minecraft:piston_head', 2.5, 0, 2, {
|
||||||
world2.setBlock('minecraft:redstone_block', 3.5, 0, 2);
|
facing: 'east',
|
||||||
|
short: 'true',
|
||||||
|
type: 'sticky'
|
||||||
|
});
|
||||||
|
world2.set('minecraft:redstone_block', 3.5, 0, 2);
|
||||||
setTimeout(set2, 250)
|
setTimeout(set2, 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
function set2() {
|
function set2() {
|
||||||
world2.setBlock('minecraft:piston_head', 3, 0, 2, {facing: 'east', short: 'false', type: 'sticky'});
|
world2.set('minecraft:piston_head', 3, 0, 2, {
|
||||||
world2.removeBlock(2.5, 0, 2);
|
facing: 'east',
|
||||||
world2.removeBlock(3.5, 0, 2);
|
short: 'false',
|
||||||
world2.setBlock('minecraft:redstone_block', 4, 0, 2);
|
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)
|
setTimeout(set3, 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
function set3() {
|
function set3() {
|
||||||
world2.removeBlock(4, 0, 2)
|
world2.del(4, 0, 2)
|
||||||
world2.removeBlock(3, 0, 2)
|
world2.del(3, 0, 2)
|
||||||
world2.setBlock('minecraft:piston_head', 2.5, 0, 2, {facing: 'east', short: 'true', type: 'sticky'});
|
world2.set('minecraft:piston_head', 2.5, 0, 2, {
|
||||||
world2.setBlock('minecraft:redstone_block', 3.5, 0, 2);
|
facing: 'east',
|
||||||
|
short: 'true',
|
||||||
|
type: 'sticky'
|
||||||
|
});
|
||||||
|
world2.set('minecraft:redstone_block', 3.5, 0, 2);
|
||||||
setTimeout(set0, 250)
|
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 {
|
export class World {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.blocks = new Map();
|
this.blocks = new Map();
|
||||||
this.listeners = new Set();
|
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) {
|
subscribe(callback) {
|
||||||
this.listeners.add(callback);
|
this.listeners.add(callback);
|
||||||
return () => this.listeners.delete(callback);
|
return () => this.listeners.delete(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify all listeners of a modification.
|
||||||
|
*/
|
||||||
notify() {
|
notify() {
|
||||||
for (const listener of this.listeners) listener();
|
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}`;
|
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();
|
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}`)) {
|
if (this.blocks.delete(`${x},${y},${z}`)) {
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBlock(x, y, z, newProps) {
|
/**
|
||||||
const key = `${x},${y},${z}`;
|
* Get a block at a position.
|
||||||
const block = this.blocks.get(key);
|
* @param {number} x
|
||||||
if (block) {
|
* @param {number} y
|
||||||
block.props = { ...block.props, ...newProps };
|
* @param {number} z
|
||||||
this.notify();
|
* @returns {any}
|
||||||
}
|
*/
|
||||||
}
|
get(x, y, z) {
|
||||||
|
|
||||||
getBlock(x, y, z) {
|
|
||||||
return this.blocks.get(`${x},${y},${z}`);
|
return this.blocks.get(`${x},${y},${z}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user