Created
April 18, 2020 11:47
-
-
Save ckiee/a07af38a609889f043e2bdc0d6886ba8 to your computer and use it in GitHub Desktop.
patched version of the structure_importer plugin for blockbench that works well on linux
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
var plugin_data = { | |
id: "structure_importer2", | |
title: "Structure Importer (patched)", | |
icon: "restore_from_trash", //Material icon name | |
author: "JannisX11 & Krozi", | |
description: "Import structure files", | |
version: "2.0.2", //Plugin version | |
variant: "both", // 'both', 'web', 'desktop' | |
}; | |
var nbt = new (function () { | |
"use strict"; | |
if (typeof ArrayBuffer === "undefined") { | |
throw new Error("Missing required type ArrayBuffer"); | |
} | |
if (typeof DataView === "undefined") { | |
throw new Error("Missing required type DataView"); | |
} | |
if (typeof Uint8Array === "undefined") { | |
throw new Error("Missing required type Uint8Array"); | |
} | |
/** @exports nbt */ | |
var nbt = this; | |
//var zlib = typeof require !== 'undefined' ? require('zlib') : window.zlib; | |
/** | |
* A mapping from type names to NBT type numbers. | |
* {@link module:nbt.Writer} and {@link module:nbt.Reader} | |
* have correspoding methods (e.g. {@link module:nbt.Writer#int}) | |
* for every type. | |
* | |
* @type Object<string, number> | |
* @see module:nbt.tagTypeNames */ | |
nbt.tagTypes = { | |
end: 0, | |
byte: 1, | |
short: 2, | |
int: 3, | |
long: 4, | |
float: 5, | |
double: 6, | |
byteArray: 7, | |
string: 8, | |
list: 9, | |
compound: 10, | |
intArray: 11, | |
}; | |
/** | |
* A mapping from NBT type numbers to type names. | |
* | |
* @type Object<number, string> | |
* @see module:nbt.tagTypes */ | |
nbt.tagTypeNames = {}; | |
(function () { | |
for (var typeName in nbt.tagTypes) { | |
if (nbt.tagTypes.hasOwnProperty(typeName)) { | |
nbt.tagTypeNames[nbt.tagTypes[typeName]] = typeName; | |
} | |
} | |
})(); | |
function hasGzipHeader(data) { | |
var head = new Uint8Array(data.slice(0, 2)); | |
return head.length === 2 && head[0] === 0x1f && head[1] === 0x8b; | |
} | |
function encodeUTF8(str) { | |
var array = [], | |
i, | |
c; | |
for (i = 0; i < str.length; i++) { | |
c = str.charCodeAt(i); | |
if (c < 0x80) { | |
array.push(c); | |
} else if (c < 0x800) { | |
array.push(0xc0 | (c >> 6)); | |
array.push(0x80 | (c & 0x3f)); | |
} else if (c < 0x10000) { | |
array.push(0xe0 | (c >> 12)); | |
array.push(0x80 | ((c >> 6) & 0x3f)); | |
array.push(0x80 | (c & 0x3f)); | |
} else { | |
array.push(0xf0 | ((c >> 18) & 0x07)); | |
array.push(0x80 | ((c >> 12) & 0x3f)); | |
array.push(0x80 | ((c >> 6) & 0x3f)); | |
array.push(0x80 | (c & 0x3f)); | |
} | |
} | |
return array; | |
} | |
function decodeUTF8(array) { | |
var codepoints = [], | |
i; | |
for (i = 0; i < array.length; i++) { | |
if ((array[i] & 0x80) === 0) { | |
codepoints.push(array[i] & 0x7f); | |
} else if ( | |
i + 1 < array.length && | |
(array[i] & 0xe0) === 0xc0 && | |
(array[i + 1] & 0xc0) === 0x80 | |
) { | |
codepoints.push( | |
((array[i] & 0x1f) << 6) | (array[i + 1] & 0x3f) | |
); | |
} else if ( | |
i + 2 < array.length && | |
(array[i] & 0xf0) === 0xe0 && | |
(array[i + 1] & 0xc0) === 0x80 && | |
(array[i + 2] & 0xc0) === 0x80 | |
) { | |
codepoints.push( | |
((array[i] & 0x0f) << 12) | | |
((array[i + 1] & 0x3f) << 6) | | |
(array[i + 2] & 0x3f) | |
); | |
} else if ( | |
i + 3 < array.length && | |
(array[i] & 0xf8) === 0xf0 && | |
(array[i + 1] & 0xc0) === 0x80 && | |
(array[i + 2] & 0xc0) === 0x80 && | |
(array[i + 3] & 0xc0) === 0x80 | |
) { | |
codepoints.push( | |
((array[i] & 0x07) << 18) | | |
((array[i + 1] & 0x3f) << 12) | | |
((array[i + 2] & 0x3f) << 6) | | |
(array[i + 3] & 0x3f) | |
); | |
} | |
} | |
return String.fromCharCode.apply(null, codepoints); | |
} | |
/* Not all environments, in particular PhantomJS, supply | |
Uint8Array.slice() */ | |
function sliceUint8Array(array, begin, end) { | |
if ("slice" in array) { | |
return array.slice(begin, end); | |
} else { | |
return new Uint8Array([].slice.call(array, begin, end)); | |
} | |
} | |
/** | |
* In addition to the named writing methods documented below, | |
* the same methods are indexed by the NBT type number as well, | |
* as shown in the example below. | |
* | |
* @constructor | |
* @see module:nbt.Reader | |
* | |
* @example | |
* var writer = new nbt.Writer(); | |
* | |
* // all equivalent | |
* writer.int(42); | |
* writer[3](42); | |
* writer(nbt.tagTypes.int)(42); | |
* | |
* // overwrite the second int | |
* writer.offset = 0; | |
* writer.int(999); | |
* | |
* return writer.buffer; */ | |
nbt.Writer = function () { | |
var self = this; | |
/* Will be resized (x2) on write if necessary. */ | |
var buffer = new ArrayBuffer(1024); | |
/* These are recreated when the buffer is */ | |
var dataView = new DataView(buffer); | |
var arrayView = new Uint8Array(buffer); | |
/** | |
* The location in the buffer where bytes are written or read. | |
* This increases after every write, but can be freely changed. | |
* The buffer will be resized when necessary. | |
* | |
* @type number */ | |
this.offset = 0; | |
// Ensures that the buffer is large enough to write `size` bytes | |
// at the current `self.offset`. | |
function accommodate(size) { | |
var requiredLength = self.offset + size; | |
if (buffer.byteLength >= requiredLength) { | |
return; | |
} | |
var newLength = buffer.byteLength; | |
while (newLength < requiredLength) { | |
newLength *= 2; | |
} | |
var newBuffer = new ArrayBuffer(newLength); | |
var newArrayView = new Uint8Array(newBuffer); | |
newArrayView.set(arrayView); | |
// If there's a gap between the end of the old buffer | |
// and the start of the new one, we need to zero it out | |
if (self.offset > buffer.byteLength) { | |
newArrayView.fill(0, buffer.byteLength, self.offset); | |
} | |
buffer = newBuffer; | |
dataView = new DataView(newBuffer); | |
arrayView = newArrayView; | |
} | |
function write(dataType, size, value) { | |
accommodate(size); | |
dataView["set" + dataType](self.offset, value); | |
self.offset += size; | |
return self; | |
} | |
/** | |
* Returns the writen data as a slice from the internal buffer, | |
* cutting off any padding at the end. | |
* | |
* @returns {ArrayBuffer} a [0, offset] slice of the interal buffer */ | |
this.getData = function () { | |
accommodate(0); /* make sure the offset is inside the buffer */ | |
return buffer.slice(0, self.offset); | |
}; | |
/** | |
* @method module:nbt.Writer#byte | |
* @param {number} value - a signed byte | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.byte] = write.bind(this, "Int8", 1); | |
/** | |
* @method module:nbt.Writer#ubyte | |
* @param {number} value - an unsigned byte | |
* @returns {module:nbt.Writer} itself */ | |
this.ubyte = write.bind(this, "Uint8", 1); | |
/** | |
* @method module:nbt.Writer#short | |
* @param {number} value - a signed 16-bit integer | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.short] = write.bind(this, "Int16", 2); | |
/** | |
* @method module:nbt.Writer#int | |
* @param {number} value - a signed 32-bit integer | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.int] = write.bind(this, "Int32", 4); | |
/** | |
* @method module:nbt.Writer#float | |
* @param {number} value - a signed 32-bit float | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.float] = write.bind(this, "Float32", 4); | |
/** | |
* @method module:nbt.Writer#float | |
* @param {number} value - a signed 64-bit float | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.double] = write.bind(this, "Float64", 8); | |
/** | |
* As JavaScript does not support 64-bit integers natively, this | |
* method takes an array of two 32-bit integers that make up the | |
* upper and lower halves of the long. | |
* | |
* @method module:nbt.Writer#long | |
* @param {Array.<number>} value - [upper, lower] | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.long] = function (value) { | |
self.int(value[0]); | |
self.int(value[1]); | |
return self; | |
}; | |
/** | |
* @method module:nbt.Writer#byteArray | |
* @param {Array.<number>|Uint8Array|Buffer} value | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.byteArray] = function (value) { | |
this.int(value.length); | |
accommodate(value.length); | |
arrayView.set(value, this.offset); | |
this.offset += value.length; | |
return this; | |
}; | |
/** | |
* @method module:nbt.Writer#intArray | |
* @param {Array.<number>} value | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.intArray] = function (value) { | |
this.int(value.length); | |
var i; | |
for (i = 0; i < value.length; i++) { | |
this.int(value[i]); | |
} | |
return this; | |
}; | |
/** | |
* @method module:nbt.Writer#string | |
* @param {string} value | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.string] = function (value) { | |
var bytes = encodeUTF8(value); | |
this.short(bytes.length); | |
accommodate(bytes.length); | |
arrayView.set(bytes, this.offset); | |
this.offset += bytes.length; | |
return this; | |
}; | |
/** | |
* @method module:nbt.Writer#list | |
* @param {Object} value | |
* @param {number} value.type - the NBT type number | |
* @param {Array} value.value - an array of values | |
* @returns {module:nbt.Writer} itself */ | |
this[nbt.tagTypes.list] = function (value) { | |
this.byte(nbt.tagTypes[value.type]); | |
this.int(value.value.length); | |
var i; | |
for (i = 0; i < value.value.length; i++) { | |
this[value.type](value.value[i]); | |
} | |
return this; | |
}; | |
/** | |
* @method module:nbt.Writer#compound | |
* @param {Object} value - a key/value map | |
* @param {Object} value.KEY | |
* @param {string} value.KEY.type - the NBT type number | |
* @param {Object} value.KEY.value - a value matching the type | |
* @returns {module:nbt.Writer} itself | |
* | |
* @example | |
* writer.compound({ | |
* foo: { type: 'int', value: 12 }, | |
* bar: { type: 'string', value: 'Hello, World!' } | |
* }); */ | |
this[nbt.tagTypes.compound] = function (value) { | |
var self = this; | |
Object.keys(value).map(function (key) { | |
self.byte(nbt.tagTypes[value[key].type]); | |
self.string(key); | |
self[value[key].type](value[key].value); | |
}); | |
this.byte(nbt.tagTypes.end); | |
return this; | |
}; | |
var typeName; | |
for (typeName in nbt.tagTypes) { | |
if (nbt.tagTypes.hasOwnProperty(typeName)) { | |
this[typeName] = this[nbt.tagTypes[typeName]]; | |
} | |
} | |
}; | |
/** | |
* In addition to the named writing methods documented below, | |
* the same methods are indexed by the NBT type number as well, | |
* as shown in the example below. | |
* | |
* @constructor | |
* @see module:nbt.Writer | |
* | |
* @example | |
* var reader = new nbt.Reader(buf); | |
* int x = reader.int(); | |
* int y = reader[3](); | |
* int z = reader[nbt.tagTypes.int](); */ | |
nbt.Reader = function (buffer) { | |
if (!buffer) { | |
throw new Error('Argument "buffer" is falsy'); | |
} | |
var self = this; | |
/** | |
* The current location in the buffer. Can be freely changed | |
* within the bounds of the buffer. | |
* | |
* @type number */ | |
this.offset = 0; | |
var arrayView = new Uint8Array(buffer); | |
var dataView = new DataView(arrayView.buffer); | |
function read(dataType, size) { | |
var val = dataView["get" + dataType](self.offset); | |
self.offset += size; | |
return val; | |
} | |
/** | |
* @method module:nbt.Reader#byte | |
* @returns {number} the read byte */ | |
this[nbt.tagTypes.byte] = read.bind(this, "Int8", 1); | |
/** | |
* @method module:nbt.Reader#byte | |
* @returns {number} the read unsigned byte */ | |
this.ubyte = read.bind(this, "Uint8", 1); | |
/** | |
* @method module:nbt.Reader#short | |
* @returns {number} the read signed 16-bit short */ | |
this[nbt.tagTypes.short] = read.bind(this, "Int16", 2); | |
/** | |
* @method module:nbt.Reader#int | |
* @returns {number} the read signed 32-bit integer */ | |
this[nbt.tagTypes.int] = read.bind(this, "Int32", 4); | |
/** | |
* @method module:nbt.Reader#float | |
* @returns {number} the read signed 32-bit float */ | |
this[nbt.tagTypes.float] = read.bind(this, "Float32", 4); | |
/** | |
* @method module:nbt.Reader#double | |
* @returns {number} the read signed 64-bit float */ | |
this[nbt.tagTypes.double] = read.bind(this, "Float64", 8); | |
/** | |
* As JavaScript does not not natively support 64-bit | |
* integers, the value is returned as an array of two | |
* 32-bit integers, the upper and the lower. | |
* | |
* @method module:nbt.Reader#long | |
* @returns {Array.<number>} [upper, lower] */ | |
this[nbt.tagTypes.long] = function () { | |
return [this.int(), this.int()]; | |
}; | |
/** | |
* @method module:nbt.Reader#byteArray | |
* @returns {Array.<number>} the read array */ | |
this[nbt.tagTypes.byteArray] = function () { | |
var length = this.int(); | |
var bytes = []; | |
var i; | |
for (i = 0; i < length; i++) { | |
bytes.push(this.byte()); | |
} | |
return bytes; | |
}; | |
/** | |
* @method module:nbt.Reader#intArray | |
* @returns {Array.<number>} the read array of 32-bit ints */ | |
this[nbt.tagTypes.intArray] = function () { | |
var length = this.int(); | |
var ints = []; | |
var i; | |
for (i = 0; i < length; i++) { | |
ints.push(this.int()); | |
} | |
return ints; | |
}; | |
/** | |
* @method module:nbt.Reader#string | |
* @returns {string} the read string */ | |
this[nbt.tagTypes.string] = function () { | |
var length = this.short(); | |
var slice = sliceUint8Array( | |
arrayView, | |
this.offset, | |
this.offset + length | |
); | |
this.offset += length; | |
return decodeUTF8(slice); | |
}; | |
/** | |
* @method module:nbt.Reader#list | |
* @returns {{type: string, value: Array}} | |
* | |
* @example | |
* reader.list(); | |
* // -> { type: 'string', values: ['foo', 'bar'] } */ | |
this[nbt.tagTypes.list] = function () { | |
var type = this.byte(); | |
var length = this.int(); | |
var values = []; | |
var i; | |
for (i = 0; i < length; i++) { | |
values.push(this[type]()); | |
} | |
return { type: nbt.tagTypeNames[type], value: values }; | |
}; | |
/** | |
* @method module:nbt.Reader#compound | |
* @returns {Object.<string, { type: string, value }>} | |
* | |
* @example | |
* reader.compound(); | |
* // -> { foo: { type: int, value: 42 }, | |
* // bar: { type: string, value: 'Hello! }} */ | |
this[nbt.tagTypes.compound] = function () { | |
var values = {}; | |
while (true) { | |
var type = this.byte(); | |
if (type === nbt.tagTypes.end) { | |
break; | |
} | |
var name = this.string(); | |
var value = this[type](); | |
values[name] = { type: nbt.tagTypeNames[type], value: value }; | |
} | |
return values; | |
}; | |
var typeName; | |
for (typeName in nbt.tagTypes) { | |
if (nbt.tagTypes.hasOwnProperty(typeName)) { | |
this[typeName] = this[nbt.tagTypes[typeName]]; | |
} | |
} | |
}; | |
/** | |
* @param {Object} value - a named compound | |
* @param {string} value.name - the top-level name | |
* @param {Object} value.value - a compound | |
* @returns {ArrayBuffer} | |
* | |
* @see module:nbt.parseUncompressed | |
* @see module:nbt.Writer#compound | |
* | |
* @example | |
* nbt.writeUncompressed({ | |
* name: 'My Level', | |
* value: { | |
* foo: { type: int, value: 42 }, | |
* bar: { type: string, value: 'Hi!' } | |
* } | |
* }); */ | |
nbt.writeUncompressed = function (value) { | |
if (!value) { | |
throw new Error('Argument "value" is falsy'); | |
} | |
var writer = new nbt.Writer(); | |
writer.byte(nbt.tagTypes.compound); | |
writer.string(value.name); | |
writer.compound(value.value); | |
return writer.getData(); | |
}; | |
/** | |
* @param {ArrayBuffer|Buffer} data - an uncompressed NBT archive | |
* @returns {{name: string, value: Object.<string, Object>}} | |
* a named compound | |
* | |
* @see module:nbt.parse | |
* @see module:nbt.writeUncompressed | |
* | |
* @example | |
* nbt.readUncompressed(buf); | |
* // -> { name: 'My Level', | |
* // value: { foo: { type: int, value: 42 }, | |
* // bar: { type: string, value: 'Hi!' }}} */ | |
nbt.parseUncompressed = function (data) { | |
if (!data) { | |
throw new Error('Argument "data" is falsy'); | |
} | |
var reader = new nbt.Reader(data); | |
var type = reader.byte(); | |
if (type !== nbt.tagTypes.compound) { | |
throw new Error("Top tag should be a compound"); | |
} | |
return { | |
name: reader.string(), | |
value: reader.compound(), | |
}; | |
}; | |
/** | |
* @callback parseCallback | |
* @param {Object} error | |
* @param {Object} result - a named compound | |
* @param {string} result.name - the top-level name | |
* @param {Object} result.value - the top-level compound */ | |
/** | |
* This accepts both gzipped and uncompressd NBT archives. | |
* If the archive is uncompressed, the callback will be | |
* called directly from this method. For gzipped files, the | |
* callback is async. | |
* | |
* For use in the browser, window.zlib must be defined to decode | |
* compressed archives. It will be passed a Buffer if the type is | |
* available, or an Uint8Array otherwise. | |
* | |
* @param {ArrayBuffer|Buffer} data - gzipped or uncompressed data | |
* @param {parseCallback} callback | |
* | |
* @see module:nbt.parseUncompressed | |
* @see module:nbt.Reader#compound | |
* | |
* @example | |
* nbt.parse(buf, function(error, results) { | |
* if (error) { | |
* throw error; | |
* } | |
* console.log(result.name); | |
* console.log(result.value.foo); | |
* }); */ | |
nbt.parse = function (data, callback) { | |
if (!data) { | |
throw new Error('Argument "data" is falsy'); | |
} | |
var self = this; | |
if (!hasGzipHeader(data)) { | |
callback(null, self.parseUncompressed(data)); | |
} else if (!zlib) { | |
console.log(zlib); | |
callback( | |
new Error( | |
"NBT archive is compressed but zlib is not " + "available" | |
), | |
null | |
); | |
} else { | |
/* zlib.gunzip take a Buffer, at least in Node, so try to convert | |
if possible. */ | |
var buffer; | |
if (data.length) { | |
buffer = data; | |
} else if (typeof Buffer !== "undefined") { | |
buffer = new Buffer(data); | |
} else { | |
/* In the browser? Unknown zlib library. Let's settle for | |
Uint8Array and see what happens. */ | |
buffer = new Uint8Array(data); | |
} | |
zlib.gunzip(buffer, function (error, uncompressed) { | |
if (error) { | |
callback(error, null); | |
} else { | |
callback(null, self.parseUncompressed(uncompressed)); | |
} | |
}); | |
} | |
}; | |
})(); | |
Blockbench.nbt_lib = nbt; | |
if (!Blockbench.isWeb) { | |
window.zlib = require("zlib"); | |
} else { | |
$.getScript( | |
"https://rawgit.com/nodeca/pako/master/dist/pako.js", | |
function () { | |
window.zlib = pako; | |
} | |
); | |
} | |
var structure_importer_resourepackCount = 0; | |
async function structure_importer_selectResourcePath(button) { | |
buttonNumber = button.id.split("-")[1]; | |
var path = await electron.dialog.showOpenDialog({ | |
properties: ["openDirectory"], | |
}); | |
document.getElementById( | |
"structure_importer_path_input-" + String(buttonNumber) | |
).value = path.filePaths[0]; | |
} | |
function structure_importer_addResourcePack() { | |
structure_importer_resourepackCount += 1; | |
var node = document.createElement("P"); | |
node.id = | |
"structure_importer_path-" + | |
String(structure_importer_resourepackCount); | |
var input = document.createElement("INPUT"); | |
input.type = "text"; | |
input.value = "Enter path to assets folder here"; | |
input.id = | |
"structure_importer_path_input-" + | |
String(structure_importer_resourepackCount); | |
var button = document.createElement("BUTTON"); | |
button.innerHTML = "Browse"; | |
button.id = | |
"structure_importer_path_button-" + | |
String(structure_importer_resourepackCount); | |
button.onclick = function () { | |
structure_importer_selectResourcePath(this); | |
}; | |
var removeButton = document.createElement("BUTTON"); | |
removeButton.innerHTML = "Remove"; | |
removeButton.id = | |
"structure_importer_path_remove-" + | |
String(structure_importer_resourepackCount); | |
removeButton.onclick = function () { | |
buttonNumber = this.id.split("-")[1]; | |
document | |
.getElementById("structure_importer_options") | |
.removeChild( | |
document.getElementById( | |
"structure_importer_path-" + String(buttonNumber) | |
) | |
); | |
}; | |
node.appendChild(document.createTextNode("Resource Pack: ")); | |
node.appendChild(input); | |
node.appendChild(button); | |
node.appendChild(removeButton); | |
var addButton = document.getElementById("structure_importer_add"); | |
document | |
.getElementById("structure_importer_options") | |
.insertBefore(node, addButton); | |
} | |
function structure_importer_updateSize() { | |
var filePath = document.getElementById("file").files[0].path; | |
var data = zlib.gunzipSync(fs.readFileSync(filePath)); | |
nbt.parse(data, function (a, b) { | |
var sizeNBT = b.value.size.value.value; | |
var size = document.getElementById("structure_importer_size"); | |
size.innerHTML = | |
"Size: " + | |
String(sizeNBT[0]) + | |
"x" + | |
String(sizeNBT[1]) + | |
"x" + | |
String(sizeNBT[2]); | |
var scale = document.getElementById("scale"); | |
scale.value = | |
2 ** | |
Math.ceil(Math.log2(Math.max(sizeNBT[0], sizeNBT[1], sizeNBT[2]))); | |
}); | |
} | |
Plugin.register("structure_importer2", { | |
author: "Krozi", | |
icon: "restore_from_trash", | |
version: "2.0.2", | |
description: "Import structure files on Linux too", | |
onload() { | |
//Adds an entry to the plugin menu | |
MenuBar.addAction( | |
new Action({ | |
id: "structure_importer", | |
name: "Structure file", | |
icon: "account_balance", | |
category: "filter", | |
click: function (ev) { | |
function importStructureFile(cb) { | |
if (Blockbench.isWeb) { | |
fileLoaderLoad(".nbt", false, function () { | |
hideDialog(); | |
var file = $("#file_upload").get(0).files[0]; | |
var reader = new FileReader(); | |
reader.onload = function () { | |
var data = zlib.ungzip(this.result); | |
nbt.parse(data, function (error, data) { | |
if (error) throw error; | |
legacyStructureImporter(data); | |
}); | |
}; | |
if (file) { | |
reader.readAsArrayBuffer(file); | |
} | |
}); | |
$("#file_folder").val(""); | |
} else { | |
structure_importer_resourepackCount = 0; | |
var dialog = new Dialog({ | |
title: "Import Structure", | |
id: "structure_importer_options", | |
lines: [ | |
'<p>File: <input type="file" id="file" accept=".nbt" oninput="structure_importer_updateSize()"></p>', | |
'<p id="structure_importer_size"></p>', | |
'<p>Scale: <input type="number" id="scale" value=16></p>', | |
'<p>Use legacy structure importer: <input type="checkbox" id="structure_importer_legacy"></p>', | |
'<p>Vanilla assets: <input type="text" value="Enter path to assets folder here" id="structure_importer_path_input-0"><button id="structure_importer_path_button-0" onclick="structure_importer_selectResourcePath(this)">Browse</button></p>', | |
'<button onclick="structure_importer_addResourcePack()" id="structure_importer_add">Add resourcepack', | |
], | |
onConfirm: function (data) { | |
dialog.hide(); | |
var filePath = $("#file")[0].files[0].path; | |
var paths = []; | |
for ( | |
var i = 0; | |
i <= | |
structure_importer_resourepackCount; | |
i++ | |
) { | |
var path = $( | |
"#structure_importer_path_input-" + | |
String(i) | |
); | |
if (path.length >= 1) { | |
paths.push(path[0].value); | |
} | |
} | |
var structureBuilder = new StructureBuilder(); | |
structureBuilder.scale = $( | |
"#scale" | |
)[0].value; | |
structureBuilder.assetPaths = paths; | |
var data = zlib.gunzipSync( | |
fs.readFileSync(filePath) | |
); | |
if ( | |
!$("#structure_importer_legacy")[0] | |
.checked | |
) { | |
nbt.parse( | |
data, | |
function (a, b) { | |
this.buildStructure(b); | |
}.bind(structureBuilder) | |
); | |
} else { | |
nbt.parse(data, function (a, b) { | |
legacyStructureImporter(b); | |
}); | |
} | |
}, | |
}); | |
dialog.show(); | |
} | |
} | |
importStructureFile(); | |
function legacyStructureImporter(file) { | |
file = file.value; | |
var group = new Group("structure").addTo(); | |
var blocks = file.blocks.value.value; | |
var palette = file.palette.value.value; | |
var calculated_blocks = []; | |
var max_size = 16; | |
var i = 0; | |
while (i < blocks.length) { | |
var blockType = | |
palette[blocks[i].state.value].Name.value; | |
if ( | |
blocks[i].pos && | |
blockType !== "minecraft:air" && | |
blockType !== "minecraft:tallgrass" && | |
blockType !== "minecraft:double_plant" | |
) { | |
var size = [0, 0, 0, 1, 1, 1]; | |
if ( | |
blockType === "minecraft:carpet" || | |
blockType.includes("repeater") || | |
blockType.includes("comparator") | |
) { | |
//clearFaces(blocks[i].pos, ['down']) | |
size = [0, 0, 0, 1, 0.0625, 1]; | |
} else if (blockType === "minecraft:chest") { | |
size = [ | |
0.0625, | |
0, | |
0.0625, | |
0.9375, | |
0.9375, | |
0.9375, | |
]; | |
} else if (blockType.includes("torch")) { | |
size = [0.4, 0, 0.4, 0.6, 0.8, 0.6]; | |
} else if (blockType.includes("flower")) { | |
size = [0.4, 0, 0.4, 0.6, 0.5, 0.6]; | |
} else if (blockType.includes("slab")) { | |
var half; | |
if ( | |
palette[blocks[i].state.value] && | |
palette[blocks[i].state.value] | |
.Properties && | |
palette[blocks[i].state.value] | |
.Properties.value.half | |
) { | |
half = | |
palette[blocks[i].state.value] | |
.Properties.value.half.value; | |
} | |
if (half === "top") { | |
//clearFaces(blocks[i].pos, ['up']) | |
size = [0, 0.5, 0, 1, 1, 1]; | |
} else if (half === "bottom") { | |
//clearFaces(blocks[i].pos, ['down']) | |
size = [0, 0, 0, 1, 0.5, 1]; | |
} else { | |
//clearFaces(blocks[i].pos, ['north', 'east', 'south', 'west', 'up', 'down']) | |
size = [0, 0, 0, 1, 1, 1]; | |
} | |
} else { | |
//clearFaces(blocks[i].pos, ['north', 'east', 'south', 'west', 'up', 'down']) | |
size = [0, 0, 0, 1, 1, 1]; | |
} | |
//Add Block Position Offset | |
for (si = 0; si < 6; si++) { | |
size[si] += | |
blocks[i].pos.value.value[si % 3]; | |
} | |
calculated_blocks.push({ | |
pos: size, | |
name: blockType.replace("minecraft:", ""), | |
}); | |
max_size = Math.max( | |
max_size, | |
size[0], | |
size[3], | |
size[1], | |
size[4], | |
size[2], | |
size[5] | |
); | |
} | |
i++; | |
} | |
//Print Into Canvas | |
var size_multiplier = 1; | |
max_size = max_size / 16; | |
if (max_size > 1) { | |
if (max_size <= 2) { | |
size_multiplier = 0.5; | |
} else if (max_size <= 4) { | |
size_multiplier = 0.25; | |
} else if (max_size <= 8) { | |
size_multiplier = 0.125; | |
} else { | |
size_multiplier = 0.0625; | |
} | |
//Adapt Grid | |
settings.edit_size.value = 16 / size_multiplier; | |
saveSettings(); | |
} | |
calculated_blocks.forEach(function (cbl) { | |
if (size_multiplier !== 1) { | |
cbl.pos.forEach(function (p, ip) { | |
cbl.pos[ip] = p * size_multiplier; | |
}); | |
} | |
var cube = new Cube() | |
.extend({ | |
from: [cbl.pos[0], cbl.pos[1], cbl.pos[2]], | |
to: [cbl.pos[3], cbl.pos[4], cbl.pos[5]], | |
name: cbl.name, | |
display: { | |
autouv: false, | |
}, | |
}) | |
.addTo(group); | |
for (var face in cube.faces) { | |
if (cube.faces.hasOwnProperty(face)) { | |
cube.faces[face].uv = [0, 0, 16, 16]; | |
} | |
} | |
// elements.push(cube) | |
cube.init(); | |
i++; | |
}); | |
Canvas.updateAll(); | |
} | |
}, | |
}), | |
"file.import" | |
); | |
}, | |
onunload() { | |
MenuBar.removeAction("file.import.structure_importer"); | |
MenuBar.removeAction("structure_importer"); | |
delete StructureBuilder; | |
}, | |
}); | |
var StructureBuilder = class { | |
constructor() { | |
this.assetPaths = []; | |
this.allFaces = ["north", "east", "south", "west", "up", "down"]; | |
this.textureVariables = {}; | |
this.palette = []; | |
this.paletteCulling = []; | |
this.group = new Group("structure").addTo(); | |
this.texturesAdded = []; | |
this.culling = []; | |
this.scale = 16; | |
this.solid = [ | |
"stone", | |
"granite", | |
"polished_granite", | |
"diorite", | |
"polished_diorite", | |
"andesite", | |
"polished_andesite", | |
"grass_block", | |
"dirt", | |
"coarse_dirt", | |
"podzol", | |
"cobblestone", | |
"oak_planks", | |
"spruce_planks", | |
"birch_planks", | |
"jungle_planks", | |
"acacia_planks", | |
"dark_oak_planks", | |
"bedrock", | |
"sand", | |
"red_sand", | |
"gravel", | |
"gold_ore", | |
"iron_ore", | |
"coal_ore", | |
"oak_log", | |
"spruce_log", | |
"birch_log", | |
"jungle_log", | |
"acacia_log", | |
"dark_oak_log", | |
"stripped_spruce_log", | |
"stripped_birch_log", | |
"stripped_jungle_log", | |
"stripped_acacia_log", | |
"stripped_dark_oak_log", | |
"stripped_oak_log", | |
"oak_wood", | |
"spruce_wood", | |
"birch_wood", | |
"jungle_wood", | |
"acacia_wood", | |
"dark_oak_wood", | |
"stripped_oak_wood", | |
"stripped_spruce_wood", | |
"stripped_birch_wood", | |
"stripped_jungle_wood", | |
"stripped_acacia_wood", | |
"stripped_dark_oak_wood", | |
"sponge", | |
"wet_sponge", | |
"lapis_ore", | |
"lapis_block", | |
"dispenser", | |
"sandstone", | |
"chiseled_sandstone", | |
"cut_sandstone", | |
"note_block", | |
"sticky_piston", | |
"white_wool", | |
"orange_wool", | |
"magenta_wool", | |
"light_blue_wool", | |
"yellow_wool", | |
"lime_wool", | |
"pink_wool", | |
"gray_wool", | |
"light_gray_wool", | |
"cyan_wool", | |
"purple_wool", | |
"blue_wool", | |
"brown_wool", | |
"green_wool", | |
"red_wool", | |
"black_wool", | |
"gold_block", | |
"iron_block", | |
"bricks", | |
"tnt", | |
"bookshelf", | |
"mossy_cobblestone", | |
"obsidian", | |
"diamond_ore", | |
"diamond_block", | |
"crafting_table", | |
"furnace", | |
"redstone_ore", | |
"snow_block", | |
"clay", | |
"jukebox", | |
"pumpkin", | |
"netherrack", | |
"soul_sand", | |
"glowstone", | |
"carved_pumpkin", | |
"jack_o_lantern", | |
"infested_stone", | |
"infested_cobblestone", | |
"infested_stone_bricks", | |
"infested_mossy_stone_bricks", | |
"infested_cracked_stone_bricks", | |
"infested_chiseled_stone_bricks", | |
"stone_bricks", | |
"mossy_stone_bricks", | |
"cracked_stone_bricks", | |
"chiseled_stone_bricks", | |
"brown_mushroom_block", | |
"red_mushroom_block", | |
"melon", | |
"mycelium", | |
"nether_bricks", | |
"end_stone", | |
"redstone_lamp", | |
"emerald_ore", | |
"emerald_block", | |
"command_block", | |
"redstone_block", | |
"nether_quartz_ore", | |
"quartz_block", | |
"chiseled_quartz_block", | |
"quartz_pillar", | |
"dropper", | |
"white_terracotta", | |
"orange_terracotta", | |
"magenta_terracotta", | |
"light_blue_terracotta", | |
"yellow_terracotta", | |
"lime_terracotta", | |
"pink_terracotta", | |
"gray_terracotta", | |
"light_gray_terracotta", | |
"cyan_terracotta", | |
"purple_terracotta", | |
"blue_terracotta", | |
"brown_terracotta", | |
"green_terracotta", | |
"red_terracotta", | |
"black_terracotta", | |
"prismarine", | |
"prismarine_bricks", | |
"dark_prismarine", | |
"sea_lantern", | |
"hay_block", | |
"terracotta", | |
"coal_block", | |
"packed_ice", | |
"red_sandstone", | |
"chiseled_red_sandstone", | |
"cut_red_sandstone", | |
"smooth_stone", | |
"smooth_sandstone", | |
"smooth_quartz", | |
"smooth_red_sandstone", | |
"purpur_block", | |
"purpur_pillar", | |
"end_stone_bricks", | |
"repeating_command_block", | |
"chain_command_block", | |
"magma_block", | |
"nether_wart_block", | |
"red_nether_bricks", | |
"bone_block", | |
"observer", | |
"white_glazed_terracotta", | |
"orange_glazed_terracotta", | |
"magenta_glazed_terracotta", | |
"light_blue_glazed_terracotta", | |
"barrel", | |
"blast_furnace", | |
"cartography_table", | |
"fletching_table", | |
"jigsaw", | |
"loom", | |
"smithing_table", | |
"smoker", | |
]; | |
this.cullSelf = [ | |
"glass", | |
"glass_pane", | |
"white_stained_glass", | |
"orange_stained_glass", | |
"magenta_stained_glass", | |
"light_blue_stained_glass", | |
"yellow_stained_glass", | |
"lime_stained_glass", | |
"pink_stained_glass", | |
"gray_stained_glass", | |
"light_gray_stained_glass", | |
"cyan_stained_glass", | |
"purple_stained_glass", | |
"blue_stained_glass", | |
"brown_stained_glass", | |
"green_stained_glass", | |
"red_stained_glass", | |
"black_stained_glass", | |
"white_stained_glass_pane", | |
"orange_stained_glass_pane", | |
"magenta_stained_glass_pane", | |
"light_blue_stained_glass_pane", | |
"yellow_stained_glass_pane", | |
"lime_stained_glass_pane", | |
"pink_stained_glass_pane", | |
"gray_stained_glass_pane", | |
"light_gray_stained_glass_pane", | |
"cyan_stained_glass_pane", | |
"purple_stained_glass_pane", | |
"blue_stained_glass_pane", | |
"brown_stained_glass_pane", | |
"green_stained_glass_pane", | |
"red_stained_glass_pane", | |
"black_stained_glass_pane", | |
"ice", | |
"iron_bars", | |
"slime_block", | |
]; | |
this.cullSelfGroup = [ | |
[ | |
"oak_fence", | |
"spruce_fence", | |
"birch_fence", | |
"jungle_fence", | |
"acacia_fence", | |
"dark_oak_fence", | |
], | |
[ | |
"cobblestone_wall", | |
"mossy_cobblestone_wall", | |
"brick_wall", | |
"prismarine_wall", | |
"red_sandstone_wall", | |
"mossy_stone_brick_wall", | |
"granite_wall", | |
"stone_brick_wall", | |
"nether_brick_wall", | |
"andesite_wall", | |
"red_nether_brick_wall", | |
"sandstone_wall", | |
"end_stone_brick_wall", | |
"diorite_wall", | |
], | |
]; | |
this.slabs = [ | |
"stone_slab", | |
"smooth_stone_slab", | |
"stone_brick_slab", | |
"sandstone_slab", | |
"acacia_slab", | |
"birch_slab", | |
"dark_oak_slab", | |
"jungle_slab", | |
"oak_slab", | |
"spruce_slab", | |
"purpur_slab", | |
"quartz_slab", | |
"red_sandstone_slab", | |
"brick_slab", | |
"cobblestone_slab", | |
"nether_brick_slab", | |
"petrified_oak_slab", | |
"prismarine_slab", | |
"prismarine_brick_slab", | |
"dark_prismarine_slab", | |
"polished_granite_slab", | |
"smooth_red_sandstone_slab", | |
"mossy_stone_brick_slab", | |
"polished_diorite_slab", | |
"mossy_cobblestone_slab", | |
"end_stone_brick_slab", | |
"smooth_sandstone_slab", | |
"smooth_quartz_slab", | |
"granite_slab", | |
"andesite_slab", | |
"red_nether_brick_slab", | |
"polished_andesite_slab", | |
"diorite_slab", | |
"cut_red_sandstone_slab", | |
"cut_sandstone_slab", | |
]; | |
this.flatBlocks = { | |
farmland: 15, | |
daylight_detector: 6, | |
grass_path: 15, | |
white_carpet: 1, | |
orange_carpet: 1, | |
magenta_carpet: 1, | |
light_blue_carpet: 1, | |
yellow_carpet: 1, | |
lime_carpet: 1, | |
pink_carpet: 1, | |
gray_carpet: 1, | |
light_gray_carpet: 1, | |
cyan_carpet: 1, | |
purple_carpet: 1, | |
blue_carpet: 1, | |
brown_carpet: 1, | |
green_carpet: 1, | |
red_carpet: 1, | |
black_carpet: 1, | |
repeater: 2, | |
comparator: 2, | |
enchanting_table: 12, | |
end_portal_frame: 13, | |
lectern: 2, | |
}; | |
} | |
buildStructure(file) { | |
Undo.initEdit({ elements: [], uv_only: false, textures: [] }); | |
for (var i = 0; i < this.assetPaths.length; i++) { | |
this.assetPaths[i] = | |
this.getAssetsPath(this.assetPaths[i]) + "/minecraft"; | |
} | |
file = file.value; | |
var palette = file.palette.value.value; | |
var blocks = file.blocks.value.value; | |
for (var i = 0; i < palette.length; i++) { | |
var blockId = palette[i].Name.value.slice( | |
palette[i].Name.value.indexOf(":") + 1 | |
); | |
var blockstates = {}; | |
if (Object.keys(palette[i]).indexOf("Properties") >= 0) { | |
var properties = Object.keys(palette[i].Properties.value); | |
for (var j = 0; j < properties.length; j++) { | |
blockstates[properties[j]] = | |
palette[i].Properties.value[properties[j]].value; | |
} | |
} | |
this.loadBlockstate(blockId, blockstates); | |
this.paletteCulling.push(this.getCullingType(blockId, blockstates)); | |
} | |
var size = file.size.value.value; | |
for (var x = 0; x < size[0]; x++) { | |
var plane = []; | |
for (var y = 0; y < size[1]; y++) { | |
plane.push(Array(size[2])); | |
} | |
this.culling.push(plane); | |
} | |
for (var i = 0; i < blocks.length; i++) { | |
this.culling[blocks[i].pos.value.value[0]][ | |
blocks[i].pos.value.value[1] | |
][blocks[i].pos.value.value[2]] = this.paletteCulling[ | |
blocks[i].state.value | |
]; | |
} | |
for (var i = 0; i < blocks.length; i++) { | |
var x = blocks[i].pos.value.value[0]; | |
var y = blocks[i].pos.value.value[1]; | |
var z = blocks[i].pos.value.value[2]; | |
var cullingData = this.culling[x][y][z]; | |
var cullSides = { | |
west: false, | |
east: false, | |
down: false, | |
up: false, | |
north: false, | |
south: false, | |
}; | |
if (x > 0) | |
cullSides.west = this.doCulling( | |
cullingData, | |
this.culling[x - 1][y][z], | |
"west" | |
); | |
if (x < size[0] - 1) | |
cullSides.east = this.doCulling( | |
cullingData, | |
this.culling[x + 1][y][z], | |
"east" | |
); | |
if (y > 0) | |
cullSides.down = this.doCulling( | |
cullingData, | |
this.culling[x][y - 1][z], | |
"down" | |
); | |
if (y < size[1] - 1) | |
cullSides.up = this.doCulling( | |
cullingData, | |
this.culling[x][y + 1][z], | |
"up" | |
); | |
if (z > 0) | |
cullSides.north = this.doCulling( | |
cullingData, | |
this.culling[x][y][z - 1], | |
"north" | |
); | |
if (z < size[2] - 1) | |
cullSides.south = this.doCulling( | |
cullingData, | |
this.culling[x][y][z + 1], | |
"south" | |
); | |
this.addModel( | |
blocks[i].state.value, | |
cullSides, | |
[ | |
(x * 16) / this.scale, | |
(y * 16) / this.scale, | |
(z * 16) / this.scale, | |
], | |
1 / this.scale | |
); | |
} | |
Canvas.updateAll(); | |
Undo.finishEdit("structure_importer", { | |
elements: this.group.children, | |
uv_only: false, | |
textures: this.texturesAdded, | |
}); | |
} | |
getAssetsPath(path) { | |
// path = path.replace("/", "\\"); | |
if (path.endsWith("/assets")) return path; | |
if (fs.existsSync(path + "/assets")) return path + "/assets"; | |
if (path.indexOf("/assets/") >= 0) | |
return path.slice(0, path.indexOf("/assets/") + 7); | |
return path; | |
} | |
getCullingType(blockId, blockstates) { | |
if (this.solid.indexOf(blockId) >= 0) { | |
return { type: "solid" }; | |
} | |
if (Object.keys(this.flatBlocks).indexOf(blockId) >= 0) { | |
return { type: "slab", height: this.flatBlocks[blockId] }; | |
} | |
if (this.slabs.indexOf(blockId) >= 0) { | |
if (blockstates.type == "bottom") { | |
return { type: "slab", height: 8 }; | |
} | |
if (blockstates.type == "top") { | |
return { type: "inverse_slab", height: 8 }; | |
} | |
if (blockstates.type == "double") { | |
return { type: "solid" }; | |
} | |
} | |
if (blockId == "snow") { | |
if (blockstates.layers == 8) { | |
return { type: "solid" }; | |
} | |
return { type: "slab", height: blockstates.layers * 2 }; | |
} | |
if (this.cullSelf.indexOf(blockId) >= 0) { | |
return { type: "selfcull", id: blockId }; | |
} | |
for (var i = 0; i < this.cullSelfGroup.length; i++) { | |
if (this.cullSelfGroup[i].indexOf(blockId) >= 0) { | |
return { type: "selfcull", id: "GROUP" + String(i) }; | |
} | |
} | |
return { type: "none" }; | |
} | |
doCulling(tile, neighbour, side) { | |
if (neighbour == undefined || neighbour.type == "none") { | |
return false; | |
} | |
if (neighbour.type == "solid") { | |
return true; | |
} | |
if ( | |
neighbour.type == "selfcull" && | |
tile.type == "selfcull" && | |
tile.id == neighbour.id | |
) { | |
return true; | |
} | |
if (neighbour.type == "slab") { | |
if (side == "up") { | |
return true; | |
} | |
if ( | |
tile.type == "slab" && | |
side != "down" && | |
tile.height <= neighbour.height | |
) { | |
return true; | |
} | |
} | |
if (neighbour.type == "inverse_slab") { | |
if (side == "down") { | |
return true; | |
} | |
if ( | |
tile.type == "inverse_slab" && | |
side != "up" && | |
tile.height <= neighbour.height | |
) { | |
return true; | |
} | |
} | |
return false; | |
} | |
getResourcePath(localPath) { | |
for (var i = this.assetPaths.length - 1; i >= 0; i--) { | |
var fullPath = this.assetPaths[i] + "/" + localPath; | |
console.log({ fullPath }); | |
if (fs.existsSync(fullPath)) { | |
return fullPath; | |
} | |
} | |
Blockbench.showMessage("Error: File not found: " + localPath, "center"); | |
throw "Error: File not found: " + localPath; | |
} | |
loadBlockstate(blockId, blockstates) { | |
var fullPath = this.getResourcePath("blockstates/" + blockId + ".json"); | |
var data = JSON.parse(fs.readFileSync(fullPath, "utf8")); | |
if (Object.keys(data).indexOf("variants") >= 0) { | |
var variants = Object.keys(data.variants); | |
for (var i = 0; i < variants.length; i++) { | |
var variant = variants[i]; | |
if (variant == "") { | |
this.palette.push(this.loadRotatedModel(data.variants[""])); | |
} | |
var repeat = true; | |
var start = 0; | |
while (repeat) { | |
var equalSign = variant.indexOf("=", start); | |
var comma = variant.indexOf(",", start); | |
if (comma == -1) { | |
comma = variant.length; | |
repeat = false; | |
} | |
var state = variant.slice(start, equalSign); | |
var value = variant.slice(equalSign + 1, comma); | |
if (blockstates[state] != value) { | |
repeat = false; | |
} else if (!repeat) { | |
this.palette.push( | |
this.loadRotatedModel(data.variants[variant]) | |
); | |
} else { | |
start = comma + 1; | |
} | |
} | |
} | |
} | |
if (Object.keys(data).indexOf("multipart") >= 0) { | |
var model = []; | |
for (var i = 0; i < data.multipart.length; i++) { | |
var part = data.multipart[i]; | |
if (Object.keys(part).indexOf("when") == -1) { | |
model = model.concat(this.loadRotatedModel(part.apply)); | |
} else { | |
var match; | |
if (Object.keys(part.when).indexOf("OR") >= 0) { | |
match = false; | |
for (var j = 0; j < part.when.OR.length; j++) { | |
match = | |
match || | |
this.matchBlockstates( | |
part.when.OR[j], | |
blockstates | |
); | |
} | |
} else { | |
match = this.matchBlockstates(part.when, blockstates); | |
} | |
if (match) { | |
model = model.concat(this.loadRotatedModel(part.apply)); | |
} | |
} | |
} | |
this.palette.push(model); | |
} | |
} | |
matchBlockstates(when, blockstates) { | |
var states = Object.keys(when); | |
var match = true; | |
for (var j = 0; j < states.length; j++) { | |
var values = String(when[states[j]]).split("|"); | |
if (values.indexOf(blockstates[states[j]]) == -1) { | |
match = false; | |
} | |
} | |
return match; | |
} | |
loadRotatedModel(data) { | |
if (data.model == undefined) { | |
return this.loadRotatedModel(data[0]); | |
} | |
var uvlock = typeof data.uvlock != "undefined" && data.uvlock; | |
var cubes = this.loadModel(data.model); | |
if (data.x) { | |
this.rollCubes(cubes, 0, -data.x, uvlock); | |
} | |
if (data.y) { | |
this.rollCubes(cubes, 1, data.y, uvlock); | |
} | |
return cubes; | |
} | |
rollCubes(cubes, axis, angle, uvlock) { | |
// Taken and modified from https://github.com/JannisX11/blockbench/blob/master/js/elements.js | |
function rotateCoord(array) { | |
var a, b; | |
array.forEach(function (s, i) { | |
if (i == axis) { | |
} else { | |
if (a == undefined) { | |
a = s - 8; | |
b = i; | |
} else { | |
array[b] = s - 8; | |
array[b] = 8 - array[b]; | |
array[i] = 8 + a; | |
} | |
} | |
}); | |
return array; | |
} | |
function rotateUVFace(number, iterations) { | |
if (!number) number = 0; | |
number += iterations * 90; | |
return number % 360; | |
} | |
function rotateUV(uv, times) { | |
for (var i = 0; i < times; i++) { | |
var temp = uv[0]; | |
uv[0] = uv[1]; | |
uv[1] = 16 - uv[2]; | |
uv[2] = uv[3]; | |
uv[3] = 16 - temp; | |
} | |
} | |
var steps = (angle % 360) / 90; | |
if (steps < 0) { | |
steps = steps + 4; | |
} | |
for (var cubeIndex = 0; cubeIndex < cubes.length; cubeIndex++) { | |
var cube = cubes[cubeIndex]; | |
for (var repeat = 0; repeat < steps; repeat++) { | |
//Swap coordinate thingy | |
[cube.from[2], cube.to[2]] = [cube.to[2], cube.from[2]]; | |
cube.from = rotateCoord(cube.from, 1); | |
cube.to = rotateCoord(cube.to, 1); | |
if (cube.origin && [8, 8, 8] != cube.origin) { | |
cube.origin = rotateCoord(cube.origin, 1); | |
} | |
if (axis === 0) { | |
if (uvlock) { | |
rotateUV(cube.faces.west.uv, 3); | |
rotateUV(cube.faces.east.uv, 1); | |
rotateUV(cube.faces.north.uv, 2); | |
rotateUV(cube.faces.down.uv, 2); | |
} else { | |
cube.faces.west.rotation = rotateUVFace( | |
cube.faces.west.rotation, | |
1 | |
); | |
cube.faces.east.rotation = rotateUVFace( | |
cube.faces.east.rotation, | |
3 | |
); | |
cube.faces.north.rotation = rotateUVFace( | |
cube.faces.north.rotation, | |
2 | |
); | |
cube.faces.down.rotation = rotateUVFace( | |
cube.faces.down.rotation, | |
2 | |
); | |
} | |
var temp = cube.faces.north; | |
cube.faces.north = cube.faces.down; | |
cube.faces.down = cube.faces.south; | |
cube.faces.south = cube.faces.up; | |
cube.faces.up = temp; | |
for (var i = 0; i < 6; i++) { | |
cube.faces[this.allFaces[i]].cullface = { | |
down: "north", | |
south: "down", | |
up: "south", | |
north: "up", | |
}[cube.faces[this.allFaces[i]].cullface]; | |
} | |
} else if (axis === 1) { | |
if (uvlock) { | |
rotateUV(cube.faces.up.uv, 3); | |
rotateUV(cube.faces.down.uv, 1); | |
} else { | |
cube.faces.up.rotation = rotateUVFace( | |
cube.faces.up.rotation, | |
1 | |
); | |
cube.faces.down.rotation = rotateUVFace( | |
cube.faces.down.rotation, | |
3 | |
); | |
} | |
var temp = cube.faces.north; | |
cube.faces.north = cube.faces.west; | |
cube.faces.west = cube.faces.south; | |
cube.faces.south = cube.faces.east; | |
cube.faces.east = temp; | |
for (var i = 0; i < 6; i++) { | |
cube.faces[this.allFaces[i]].cullface = { | |
west: "north", | |
south: "west", | |
east: "south", | |
north: "east", | |
}[cube.faces[this.allFaces[i]].cullface]; | |
} | |
} | |
if (cube.rotation) { | |
//Fine Rotations | |
var i = 0; | |
var temp_rot = undefined; | |
var temp_i = undefined; | |
while (i < 3) { | |
if (i !== axis) { | |
if (temp_rot === undefined) { | |
temp_rot = cube.rotation[i]; | |
temp_i = i; | |
} else { | |
cube.rotation[temp_i] = -cube.rotation[i]; | |
cube.rotation[i] = temp_rot; | |
} | |
} | |
i++; | |
} | |
} | |
} | |
} | |
} | |
loadModel(model, textures = {}) { | |
var cubes = []; | |
var path = this.getResourcePath("models/" + model + ".json"); | |
var data = JSON.parse(fs.readFileSync(path, "utf8")); | |
if (Object.keys(data).indexOf("textures") >= 0) { | |
var newTextureList = Object.keys(data.textures); | |
var oldTextureList = Object.keys(textures); | |
for (var i = 0; i < newTextureList.length; i++) { | |
textures[newTextureList[i]] = data.textures[newTextureList[i]]; | |
for (var j = 0; j < oldTextureList.length; j++) { | |
if ( | |
data.textures[newTextureList[i]] == | |
"#" + oldTextureList[j] | |
) { | |
textures[newTextureList[i]] = | |
textures[oldTextureList[j]]; | |
break; | |
} | |
} | |
} | |
} | |
if (Object.keys(data).indexOf("elements") >= 0) { | |
for (var i = 0; i < data.elements.length; i++) { | |
var element = data.elements[i]; | |
var cube = { | |
faces: { | |
north: {}, | |
east: {}, | |
south: {}, | |
west: {}, | |
up: {}, | |
down: {}, | |
}, | |
}; | |
cube.from = element.from; | |
cube.to = element.to; | |
if (element.rotation && element.rotation.axis != 0) { | |
cube.rotation = [0, 0, 0]; | |
cube.rotation[{ x: 0, y: 1, z: 2 }[element.rotation.axis]] = | |
element.rotation.angle; | |
cube.origin = element.rotation.origin; | |
cube.rescale = element.rotation.rescale; | |
} | |
cube.shade = element.shade; | |
for ( | |
var faceIndex = 0; | |
faceIndex < this.allFaces.length; | |
faceIndex++ | |
) { | |
var face = this.allFaces[faceIndex]; | |
if (face in element.faces) { | |
cube.faces[face].texture = this.getTextureVariable( | |
textures[ | |
this.removeHashtag(element.faces[face].texture) | |
] | |
); | |
cube.faces[face].rotation = | |
element.faces[face].rotation; | |
cube.faces[face].cullface = | |
element.faces[face].cullface; | |
cube.faces[face].tint = | |
element.faces[face].tintindex != null; | |
if ("uv" in element.faces[face]) { | |
cube.faces[face].uv = element.faces[face].uv; | |
} else { | |
switch (faceIndex) { | |
case 0: | |
cube.faces[face].uv = [ | |
16 - cube.to[0], | |
16 - cube.to[1], | |
16 - cube.from[0], | |
16 - cube.from[1], | |
]; | |
break; | |
case 1: | |
cube.faces[face].uv = [ | |
16 - cube.to[2], | |
16 - cube.to[1], | |
16 - cube.from[2], | |
16 - cube.from[1], | |
]; | |
break; | |
case 2: | |
cube.faces[face].uv = [ | |
cube.from[0], | |
16 - cube.to[1], | |
cube.to[0], | |
16 - cube.from[1], | |
]; | |
break; | |
case 3: | |
cube.faces[face].uv = [ | |
cube.from[2], | |
16 - cube.to[1], | |
cube.to[2], | |
16 - cube.from[1], | |
]; | |
break; | |
case 4: | |
cube.faces[face].uv = [ | |
cube.from[0], | |
cube.from[2], | |
cube.to[0], | |
cube.to[2], | |
]; | |
break; | |
case 5: | |
cube.faces[face].uv = [ | |
cube.from[0], | |
16 - cube.to[2], | |
cube.to[0], | |
16 - cube.from[2], | |
]; | |
break; | |
} | |
} | |
} else { | |
cube.faces[face].texture = null; | |
cube.faces[face].uv = [0, 0, 16, 16]; | |
} | |
} | |
cubes.push(cube); | |
} | |
} else if (Object.keys(data).indexOf("parent") >= 0) { | |
return this.loadModel(data.parent, textures); | |
} | |
return cubes; | |
} | |
getTextureVariable(path) { | |
if (path in this.textureVariables) { | |
return this.textureVariables[path]; | |
} else { | |
var texture = new Texture() | |
.fromPath(this.getResourcePath("/textures/" + path + ".png")) | |
.add(false); | |
this.texturesAdded.push(texture); | |
var variable = texture.uuid; | |
this.textureVariables[path] = variable; | |
return variable; | |
} | |
} | |
removeHashtag(texture) { | |
if (texture.charAt(0) == "#") { | |
return texture.slice(1); | |
} | |
return texture; | |
} | |
addModel(state, cullsides, position, scale) { | |
var cubes = this.palette[state]; | |
for (var i = 0; i < cubes.length; i++) { | |
var cube = new Cube(); | |
cube.autouv = 0; | |
if (cubes[i].rotation) { | |
cube.rotation = cubes[i].rotation; | |
cube.rescale = cubes[i].rescale; | |
} | |
for (var j = 0; j < 3; j++) { | |
cube.from[j] = cubes[i].from[j] * scale + position[j]; | |
cube.to[j] = cubes[i].to[j] * scale + position[j]; | |
if (cubes[i].origin) { | |
cube.origin[j] = cubes[i].origin[j] * scale + position[j]; | |
} | |
} | |
cube.shade = cubes[i].shade; | |
var cubeVisible = false; | |
for ( | |
var faceIndex = 0; | |
faceIndex < this.allFaces.length; | |
faceIndex++ | |
) { | |
var face = this.allFaces[faceIndex]; | |
if ( | |
!cullsides[cubes[i].faces[face].cullface] && | |
cubes[i].faces[face].texture != null | |
) { | |
cube.faces[face].texture = cubes[i].faces[face].texture; | |
cube.faces[face].uv = cubes[i].faces[face].uv; | |
cube.faces[face].rotation = cubes[i].faces[face].rotation; | |
cube.faces[face].tint = cubes[i].faces[face].tint; | |
cubeVisible = true; | |
} else { | |
cube.faces[face].texture = null; | |
} | |
} | |
if (cubeVisible) { | |
cube.init(); | |
// elements.push(cube) | |
cube.addTo(this.group); | |
} | |
} | |
} | |
}; | |
//Called when the user uninstalls the plugin |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment