diff --git a/index.html b/index.html index 6cfadf55..2691fc1d 100644 --- a/index.html +++ b/index.html @@ -24,13 +24,16 @@ diff --git a/public/assets/minecraft/models/block/block.json b/public/assets/minecraft/models/block/block.json index 3ca539e9..aebc4bef 100644 --- a/public/assets/minecraft/models/block/block.json +++ b/public/assets/minecraft/models/block/block.json @@ -1 +1,124 @@ -{"gui_light":"side","display":{"gui":{"rotation":[30,225,0],"translation":[0,0,0],"scale":[0.625,0.625,0.625]},"ground":{"rotation":[0,0,0],"translation":[0,3,0],"scale":[0.25,0.25,0.25]},"fixed":{"rotation":[0,0,0],"translation":[0,0,0],"scale":[0.5,0.5,0.5]},"on_shelf":{"rotation":[0,180,0],"translation":[0,0,0],"scale":[1,1,1]},"thirdperson_righthand":{"rotation":[75,45,0],"translation":[0,2.5,0],"scale":[0.375,0.375,0.375]},"firstperson_righthand":{"rotation":[0,45,0],"translation":[0,0,0],"scale":[0.4,0.4,0.4]},"firstperson_lefthand":{"rotation":[0,225,0],"translation":[0,0,0],"scale":[0.4,0.4,0.4]}}} \ No newline at end of file +{ + "gui_light": "side", + "display": { + "gui": { + "rotation": [ + 30, + 225, + 0 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 0.625, + 0.625, + 0.625 + ] + }, + "ground": { + "rotation": [ + 0, + 0, + 0 + ], + "translation": [ + 0, + 3, + 0 + ], + "scale": [ + 0.25, + 0.25, + 0.25 + ] + }, + "fixed": { + "rotation": [ + 0, + 0, + 0 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 0.5, + 0.5, + 0.5 + ] + }, + "on_shelf": { + "rotation": [ + 0, + 180, + 0 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + "thirdperson_righthand": { + "rotation": [ + 75, + 45, + 0 + ], + "translation": [ + 0, + 2.5, + 0 + ], + "scale": [ + 0.375, + 0.375, + 0.375 + ] + }, + "firstperson_righthand": { + "rotation": [ + 0, + 45, + 0 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 0.4, + 0.4, + 0.4 + ] + }, + "firstperson_lefthand": { + "rotation": [ + 0, + 225, + 0 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 0.4, + 0.4, + 0.4 + ] + } + } +} \ No newline at end of file diff --git a/public/assets/minecraft/models/block/cube.json b/public/assets/minecraft/models/block/cube.json index 7b512042..e8ed7953 100644 --- a/public/assets/minecraft/models/block/cube.json +++ b/public/assets/minecraft/models/block/cube.json @@ -1 +1,43 @@ -{"parent":"block/block","elements":[{"from":[0,0,0],"to":[16,16,16],"faces":{"down":{"texture":"#down","cullface":"down"},"up":{"texture":"#up","cullface":"up"},"north":{"texture":"#north","cullface":"north"},"south":{"texture":"#south","cullface":"south"},"west":{"texture":"#west","cullface":"west"},"east":{"texture":"#east","cullface":"east"}}}]} \ No newline at end of file +{ + "parent": "block/block", + "elements": [ + { + "from": [ + 0, + 0, + 0 + ], + "to": [ + 16, + 16, + 16 + ], + "faces": { + "down": { + "texture": "#down", + "cullface": "down" + }, + "up": { + "texture": "#up", + "cullface": "up" + }, + "north": { + "texture": "#north", + "cullface": "north" + }, + "south": { + "texture": "#south", + "cullface": "south" + }, + "west": { + "texture": "#west", + "cullface": "west" + }, + "east": { + "texture": "#east", + "cullface": "east" + } + } + } + ] +} \ No newline at end of file diff --git a/public/assets/minecraft/models/block/cube_all.json b/public/assets/minecraft/models/block/cube_all.json index a638b497..aaa6adbc 100644 --- a/public/assets/minecraft/models/block/cube_all.json +++ b/public/assets/minecraft/models/block/cube_all.json @@ -1 +1,12 @@ -{"parent":"block/cube","textures":{"particle":"#all","down":"#all","up":"#all","north":"#all","east":"#all","south":"#all","west":"#all"}} \ No newline at end of file +{ + "parent": "block/cube", + "textures": { + "particle": "#all", + "down": "#all", + "up": "#all", + "north": "#all", + "east": "#all", + "south": "#all", + "west": "#all" + } +} \ No newline at end of file diff --git a/public/assets/minecraft/models/block/smooth_stone.json b/public/assets/minecraft/models/block/smooth_stone.json index 4f01b2d8..54595f0c 100644 --- a/public/assets/minecraft/models/block/smooth_stone.json +++ b/public/assets/minecraft/models/block/smooth_stone.json @@ -1 +1,6 @@ -{"parent":"minecraft:block/cube_all","textures":{"all":"minecraft:block/smooth_stone"}} \ No newline at end of file +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "minecraft:block/smooth_stone" + } +} \ No newline at end of file diff --git a/src/blockstate.js b/src/blockstate.js index 21d1a4ac..abf334b8 100644 --- a/src/blockstate.js +++ b/src/blockstate.js @@ -174,13 +174,16 @@ export class BlockstateResolver { this.pending.delete(id); if (this.onResolve) { - this.onResolve(id, resolver); + this.onResolve(id); } }) .catch(reason => { this.resolvers.set(id, this.resolvers.get(':invalid')); this.pending.delete(id); - console.log(`failed to resolve blockstate ${id}: ${reason}`); + console.warn(`failed to resolve blockstate ${id}: ${reason}`); + if (this.onResolve) { + this.onResolve(id); + } })); } @@ -189,7 +192,26 @@ export class BlockstateResolver { resolve(id, pos, state) { const resolver = this.getResolver(id); - const hash = pos[0] * 3.236 + pos[1] * 4.854 + pos[2] * 8.090; + + // Read the 3 floats directly as 32-bit integers + let buffer = new Float32Array([pos[0], pos[1], pos[2]]).buffer; + let uview = new Uint32Array(buffer); + + // Mix using large primes + let hash = Math.imul(uview[0], 73856093) ^ + Math.imul(uview[1], 19349663) ^ + Math.imul(uview[2], 83492791); + + // Avalanche step: Force high bits to mix into low bits (fixes the % 8 issue) + hash ^= hash >>> 16; + hash = Math.imul(hash, 0x85ebca6b); + hash ^= hash >>> 13; + hash = Math.imul(hash, 0xc2b2ae35); + hash ^= hash >>> 16; + + // Ensure unsigned 32-bit int + hash >>>= 0; + return resolver(state, hash); } } \ No newline at end of file diff --git a/src/minerama.js b/src/minerama.js index 87773834..8cbf6bed 100644 --- a/src/minerama.js +++ b/src/minerama.js @@ -1,4 +1,5 @@ import {BlockstateResolver} from "./blockstate"; +import {ModelResolver} from "./model"; export class Renderer { constructor() { @@ -12,26 +13,38 @@ export class Renderer { async function parse(src) { const lines = src.replace(/#.*?(?:\n|$)/, '\n').trim().split(/(?:\n[ \t]*)+/); - let count = 0; - const res = new BlockstateResolver(); + const bres = new BlockstateResolver(); + const mres = new ModelResolver(); function ff() { + const drawList = new Map(); + for (const line of lines) { const args = line.split(/[ \t]+/); const cmd = args[0].toLowerCase(); if (cmd === 'p') { - const id = args[1]; - const hash = count; - count += 1; - - console.log(id, JSON.stringify(res.getResolver(id)({}, hash))); + const pos = [args[1], args[2], args[3]]; + const id = args[4]; + const state = Object.fromEntries(args.slice(5).map(it => it.split('='))); + for (const part of bres.resolve(id, pos, state)) { + drawList.set(part.model, null); + } } } + + for (const id of drawList.keys()) { + const data = mres.get(id); + console.log(id, data); + } } - res.onResolve = (id, fn) => { - console.log('resolved', id, 'to', fn) + bres.onResolve = (id) => { + console.log('resolved blockstate', id) + ff() + } + mres.onResolve = (id) => { + console.log('resolved model', id) ff() } diff --git a/src/model.js b/src/model.js new file mode 100644 index 00000000..41afbcf8 --- /dev/null +++ b/src/model.js @@ -0,0 +1,58 @@ +export class ModelResolver { + constructor() { + this.models = new Map(); + this.pending = new Map(); + + this.onResolve = undefined; + + const cuboid = { + "from": [0, 0, 0], + "to": [0, 0, 0], + "faces": { + "down": {"texture": "#all"}, + "up": {"texture": "#all"}, + "north": {"texture": "#all"}, + "south": {"texture": "#all"}, + "west": {"texture": "#all"}, + "east": {"texture": "#all"} + } + }; + + this.models.set(':pending', {"textures": {"all": ":pending"}, "elements": [cuboid]}); + this.models.set(':missing', {"textures": {"all": ":missing"}, "elements": [cuboid]}); + } + + get(id) { + id = id.includes(":") ? id : `minecraft:${id}`; + + if (this.models.has(id)) { + return this.models.get(id); + } + + if (!this.pending.has(id)) { + const [space, name] = id.split(':'); + const url = `/assets/${space}/models/${name}.json`; + + this.pending.set(id, fetch(url) + .then(res => res.json()) + .then(data => { + this.models.set(id, data) + this.pending.delete(id) + + if (this.onResolve) { + this.onResolve(id); + } + }) + .catch(reason => { + this.models.set(id, this.models.get(':invalid')); + this.pending.delete(id); + console.log(`failed to resolve model ${id}: ${reason}`) + if (this.onResolve) { + this.onResolve(id); + } + })); + } + + return this.models.get(':pending'); + } +} \ No newline at end of file