-
-
Save EndingCredits/9891678fccbf51636b243787afee8b91 to your computer and use it in GitHub Desktop.
Demo using deepslate to render litematic files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://unpkg.com/deepslate@0.9.0"></script> | |
<script src="https://unpkg.com/gl-matrix@3.3.0/gl-matrix-min.js"></script> | |
<!--<script src="./assets.js"></script> | |
<script src="./opaque.js"></script>--> | |
<script src="https://rawcdn.githack.com/misode/deepslate-demo/023617d9bac846b3f1ad02fc52123df3ddc623aa/assets.js"></script> | |
<script src="https://rawcdn.githack.com/misode/deepslate-demo/023617d9bac846b3f1ad02fc52123df3ddc623aa/opaque.js"></script> | |
</head> | |
<body> | |
<script> | |
function readFile(input) { | |
let file = input.files[0]; | |
let reader = new FileReader(); | |
reader.readAsArrayBuffer(file); | |
reader.onload = function(evt) { | |
const nbtdata = deepslate.read(new Uint8Array(reader.result)).result; // Don't care about .compressed | |
//console.log(nbtdata); | |
console.log(nbtdata.value); | |
readLitematic(nbtdata); | |
}; | |
reader.onerror = function() { | |
console.log(reader.error); | |
}; | |
} | |
function readLitematic(nbtdata) { | |
var regions = nbtdata.value.Regions.value; | |
for (let regionName in regions) { | |
var region = regions[regionName].value; | |
var blockPalette = region.BlockStatePalette.value.value; | |
//nbits = max(ceil(log(len(palette), 2)), 2) | |
nbits = Math.ceil(Math.log2(blockPalette.length)); | |
width = region.Size.value.x.value; | |
height = region.Size.value.y.value; | |
depth = region.Size.value.z.value; | |
var blockData = region.BlockStates.value; | |
var blocks = processBlockData(blockData, nbits, width, height, depth); | |
//console.log(blocks); | |
renderStructure(blocks, blockPalette); | |
} | |
} | |
function processBlockData(blockData, nbits, width, height, depth) { | |
mask = (1 << nbits) - 1; | |
y_shift = Math.abs(width * depth); | |
z_shift = Math.abs(width); | |
var blocks = new Array(); | |
for (let x=0; x < Math.abs(width); x++) { | |
blocks[x] = new Array(); | |
for (let y=0; y < Math.abs(height); y++) { | |
blocks[x][y] = new Array(); | |
for (let z=0; z < Math.abs(depth); z++) { | |
index = y * y_shift + z * z_shift + x; | |
start_offset = index * nbits; | |
start_arr_index = start_offset >>> 5; /// divide by 32 | |
end_arr_index = ((index + 1) * nbits - 1) >>> 5; | |
start_bit_offset = start_offset & 0x1F; // % 32 | |
half_ind = start_arr_index >>> 1; | |
if ((start_arr_index & 0x1) == 0) { | |
blockStart = blockData[half_ind][1]; | |
blockEnd = blockData[half_ind][0]; | |
} else { | |
blockStart = blockData[half_ind][0]; | |
blockEnd = blockData[half_ind+1][1]; | |
} | |
if (start_arr_index == end_arr_index) { | |
blocks[x][y][z] = (blockStart >>> start_bit_offset) & mask; | |
} else { | |
end_offset = 32 - start_bit_offset; // num curtailed bits | |
val = ((blockStart >>> start_bit_offset) & mask) | ((blockEnd << end_offset) & mask); | |
blocks[x][y][z] = val;// & mask; | |
} | |
} | |
} | |
} | |
return blocks; | |
} | |
function __stripNBTTyping(nbtData) { | |
if (nbtData.hasOwnProperty("type")) { | |
switch(nbtData.type) { | |
case "compound": | |
var newDict = {} | |
for (const [k, v] of Object.entries(nbtData.value)) { | |
newDict[k] = __stripNBTTyping(v); | |
} | |
return newDict; | |
break; | |
case "list": | |
var newList = []; | |
for (const [k, v] of Object.entries(nbtData.value.value)) { | |
newList[k] = __stripNBTTyping(v); | |
} | |
return newList; | |
break; | |
default: | |
return nbtData.value; | |
} | |
} else { | |
return nbtData; | |
} | |
} | |
function renderStructure(blocks, palette) { | |
console.log( blocks ); | |
width = blocks.length; | |
height = blocks[0].length; | |
depth = blocks[0][0].length; | |
console.log(width, height, depth); | |
const structure = new deepslate.Structure([width, height, depth]); | |
const size = structure.getSize(); | |
structure.addBlock([1, 0, 0], "minecraft:stone") | |
structure.addBlock([2, 0, 0], "minecraft:grass_block", { "snowy": "false" }); | |
structure.addBlock([1, 1, 0], "minecraft:cake", { "bites": "3" }) | |
structure.addBlock([0, 0, 0], "minecraft:wall_torch", { "facing": "west" }); | |
structure.addBlock([2, 1, 0], "minecraft:scaffolding", { "bottom": "false", "waterlogged": "false", "distance": "0" }); | |
var blockCount = 0 | |
console.log("Building blocks..."); | |
for (let x=0; x < width; x++) { | |
for (let y=0; y < height; y++) { | |
for (let z=0; z < depth; z++) { | |
blockID = blocks[x][y][z]; | |
if (blockID > 0) { | |
if(blockID < palette.length) { | |
blockInfo = palette[blockID]; | |
blockName = blockInfo.Name.value; | |
blockCount++; | |
if (blockInfo.hasOwnProperty("Properties")) { | |
blockProperties = __stripNBTTyping(blockInfo.Properties); | |
structure.addBlock([x, y, z], blockName, blockProperties); | |
} else { | |
structure.addBlock([x, y, z], blockName); | |
} | |
} else { | |
structure.addBlock([x, y, z], "minecraft:stone") | |
} | |
} | |
} | |
} | |
} | |
console.log("Done!", blockCount); | |
const canvas = document.getElementById('canvas'); | |
const gl = canvas.getContext('webgl'); | |
const renderer = new deepslate.StructureRenderer(gl, structure, deepslateResources); | |
let viewDist = 4; | |
let xRotation = 0.8; | |
let yRotation = 0.5; | |
function render() { | |
yRotation = yRotation % (Math.PI * 2); | |
xRotation = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, xRotation)); | |
viewDist = Math.max(1, Math.min(20, viewDist)); | |
const view = mat4.create(); | |
mat4.translate(view, view, [0, 0, -viewDist]); | |
mat4.rotate(view, view, xRotation, [1, 0, 0]); | |
mat4.rotate(view, view, yRotation, [0, 1, 0]); | |
mat4.translate(view, view, [-size[0] / 2, -size[1] / 2, -size[2] / 2]); | |
renderer.drawStructure(view); | |
} | |
requestAnimationFrame(render); | |
let dragPos = null; | |
canvas.addEventListener('mousedown', evt => { | |
if (evt.button === 0) { | |
dragPos = [evt.clientX, evt.clientY]; | |
} | |
}) | |
canvas.addEventListener('mousemove', evt => { | |
if (dragPos) { | |
yRotation += (evt.clientX - dragPos[0]) / 100; | |
xRotation += (evt.clientY - dragPos[1]) / 100; | |
dragPos = [evt.clientX, evt.clientY]; | |
requestAnimationFrame(render); | |
} | |
}) | |
canvas.addEventListener('mouseup', () => { | |
dragPos = null; | |
}) | |
canvas.addEventListener('wheel', evt => { | |
evt.preventDefault(); | |
viewDist += evt.deltaY / 100; | |
requestAnimationFrame(render); | |
}) | |
} | |
/*fetch('./Dark_Scaffolding_Shulker_Farm_v1.litematic') | |
.then(res => res.arrayBuffer()) | |
.then(data => { | |
console.log(new Uint8Array(data)); | |
const { result, compressed } = deepslate.read(new Uint8Array(data)) | |
console.log(result, compressed) | |
//deepslate.write(result, compressed) | |
})*/ //Turn off security.fileuri.strict_origin_policy in about:config | |
</script> | |
<canvas id="canvas" width="800" height="600"></canvas> | |
<img id="atlas" src="https://raw.githubusercontent.com/misode/deepslate-demo/main/atlas.png" alt="Texture atlas" crossorigin="anonymous" hidden> | |
<script> | |
var deepslateResources; | |
const { mat4 } = glMatrix; | |
const image = document.getElementById('atlas'); | |
if (image.complete) { | |
loadResources(); | |
} else { | |
image.addEventListener('load', loadResources); | |
} | |
function loadResources() { | |
const blockDefinitions = {}; | |
Object.keys(assets.blockstates).forEach(id => { | |
blockDefinitions['minecraft:' + id] = deepslate.BlockDefinition.fromJson(id, assets.blockstates[id]); | |
}) | |
const blockModels = {}; | |
Object.keys(assets.models).forEach(id => { | |
blockModels['minecraft:' + id] = deepslate.BlockModel.fromJson(id, assets.models[id]); | |
}) | |
Object.values(blockModels).forEach(m => m.flatten({ getBlockModel: id => blockModels[id] })); | |
const atlasCanvas = document.createElement('canvas'); | |
atlasCanvas.width = image.width; | |
atlasCanvas.height = image.height; | |
const atlasCtx = atlasCanvas.getContext('2d'); | |
atlasCtx.drawImage(image, 0, 0); | |
const atlasData = atlasCtx.getImageData(0, 0, atlasCanvas.width, atlasCanvas.height); | |
const part = 16 / atlasData.width; | |
const idMap = {}; | |
Object.keys(assets.textures).forEach(id => { | |
const [u, v] = assets.textures[id]; | |
idMap['minecraft:' + id] = [u, v, u + part, v + part]; | |
}) | |
const textureAtlas = new deepslate.TextureAtlas(atlasData, idMap); | |
deepslateResources = { | |
getBlockDefinition(id) { return blockDefinitions[id] }, | |
getBlockModel(id) { return blockModels[id] }, | |
getTextureUV(id) { return textureAtlas.getTextureUV(id) }, | |
getTextureAtlas() { return textureAtlas.getTextureAtlas() }, | |
getBlockFlags(id) { return { opaque: opaqueBlocks.has(id) } }, | |
getBlockProperties(id) { return null }, | |
getDefaultBlockProperties(id) { return null }, | |
} | |
} | |
</script> | |
<input type="file" onchange="readFile(this)"> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment