mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-15 02:59:55 +08:00
Merge pull request #61 from LucasDower/schem-exporter
Added .schem exporter, closes #60
This commit is contained in:
commit
3377872cd4
65
package-lock.json
generated
65
package-lock.json
generated
@ -15,7 +15,8 @@
|
||||
"jpeg-js": "^0.4.4",
|
||||
"pngjs": "^6.0.0",
|
||||
"prismarine-nbt": "^1.6.0",
|
||||
"twgl.js": "^4.19.1"
|
||||
"twgl.js": "^4.19.1",
|
||||
"varint-array": "^0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.4.1",
|
||||
@ -23,6 +24,7 @@
|
||||
"@types/obj-file-parser": "^0.5.0",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"@types/prompt": "^1.1.2",
|
||||
"@types/varint": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"adm-zip": "^0.5.9",
|
||||
@ -1435,6 +1437,15 @@
|
||||
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/varint": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/varint/-/varint-6.0.0.tgz",
|
||||
"integrity": "sha512-2jBazyxGl4644tvu3VAez8UA/AtrcEetT9HOeAbqZ/vAcRVL/ZDFQjSS7rkWusU5cyONQVUz+nwwrNZdMva4ow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "16.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
|
||||
@ -6638,6 +6649,14 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/struct": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/struct/-/struct-0.0.11.tgz",
|
||||
"integrity": "sha512-zhCBDK3POhX5y1IUTr5JXY7l9Esjdi1WkhZBjZEJU8ewpwsa95l+PtLCMrYIu8VxDFCb7vr2CnuQDxDEj2K63g==",
|
||||
"engines": {
|
||||
"node": ">0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sumchecker": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
@ -7105,6 +7124,20 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/varint-array": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/varint-array/-/varint-array-0.0.0.tgz",
|
||||
"integrity": "sha512-mUtmdj8Ic9LO6Vu+AgRtPP8tPBmnliJ7xbRRabwvW+qRVkOYlp/7o3Ga27Wq7xr8KcR6W1GDe+gmm/5hCtqEMQ==",
|
||||
"dependencies": {
|
||||
"struct": "0.0.11",
|
||||
"varint": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/varint-array/node_modules/varint": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz",
|
||||
"integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow=="
|
||||
},
|
||||
"node_modules/w3c-hr-time": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
@ -8489,6 +8522,15 @@
|
||||
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/varint": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/varint/-/varint-6.0.0.tgz",
|
||||
"integrity": "sha512-2jBazyxGl4644tvu3VAez8UA/AtrcEetT9HOeAbqZ/vAcRVL/ZDFQjSS7rkWusU5cyONQVUz+nwwrNZdMva4ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "16.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
|
||||
@ -12486,6 +12528,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"struct": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/struct/-/struct-0.0.11.tgz",
|
||||
"integrity": "sha512-zhCBDK3POhX5y1IUTr5JXY7l9Esjdi1WkhZBjZEJU8ewpwsa95l+PtLCMrYIu8VxDFCb7vr2CnuQDxDEj2K63g=="
|
||||
},
|
||||
"sumchecker": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
@ -12818,6 +12865,22 @@
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"varint-array": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/varint-array/-/varint-array-0.0.0.tgz",
|
||||
"integrity": "sha512-mUtmdj8Ic9LO6Vu+AgRtPP8tPBmnliJ7xbRRabwvW+qRVkOYlp/7o3Ga27Wq7xr8KcR6W1GDe+gmm/5hCtqEMQ==",
|
||||
"requires": {
|
||||
"struct": "0.0.11",
|
||||
"varint": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"varint": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz",
|
||||
"integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"w3c-hr-time": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
|
@ -36,6 +36,7 @@
|
||||
"@types/obj-file-parser": "^0.5.0",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"@types/prompt": "^1.1.2",
|
||||
"@types/varint": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"adm-zip": "^0.5.9",
|
||||
@ -59,6 +60,7 @@
|
||||
"jpeg-js": "^0.4.4",
|
||||
"pngjs": "^6.0.0",
|
||||
"prismarine-nbt": "^1.6.0",
|
||||
"twgl.js": "^4.19.1"
|
||||
"twgl.js": "^4.19.1",
|
||||
"varint-array": "^0.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ import { Schematic } from './schematic_exporter';
|
||||
import { Litematic } from './litematic_exporter';
|
||||
import { ASSERT } from '../util';
|
||||
import { ObjExporter } from './obj_exporter';
|
||||
import { SchemExporter } from './schem_exporter';
|
||||
|
||||
export type TExporters = 'schematic' | 'litematic' | 'obj';
|
||||
export type TExporters = 'schematic' | 'litematic' | 'obj' | 'schem';
|
||||
|
||||
export class ExporterFactory {
|
||||
public static GetExporter(voxeliser: TExporters): IExporter {
|
||||
@ -15,6 +16,8 @@ export class ExporterFactory {
|
||||
return new Litematic();
|
||||
case 'obj':
|
||||
return new ObjExporter();
|
||||
case 'schem':
|
||||
return new SchemExporter();
|
||||
default:
|
||||
ASSERT(false);
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { BlockMesh } from '../block_mesh';
|
||||
import { Vector3 } from '../vector';
|
||||
import { IExporter } from './base_exporter';
|
||||
import { saveNBT } from '../util/nbt_util';
|
||||
|
||||
import fs from 'fs';
|
||||
import { NBT, TagType, writeUncompressed } from 'prismarine-nbt';
|
||||
import * as zlib from 'zlib';
|
||||
import { NBT, TagType } from 'prismarine-nbt';
|
||||
|
||||
type BlockID = number;
|
||||
type long = [number, number];
|
||||
@ -187,17 +186,7 @@ export class Litematic extends IExporter {
|
||||
this._sizeVector = Vector3.sub(bounds.max, bounds.min).add(1);
|
||||
|
||||
const nbt = this._convertToNBT(blockMesh);
|
||||
|
||||
const outBuffer = fs.createWriteStream(filePath);
|
||||
const newBuffer = writeUncompressed(nbt, 'big');
|
||||
|
||||
zlib.gzip(newBuffer, (err, buffer) => {
|
||||
if (!err) {
|
||||
outBuffer.write(buffer);
|
||||
outBuffer.end();
|
||||
}
|
||||
return err;
|
||||
});
|
||||
saveNBT(nbt, filePath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
82
src/exporters/schem_exporter.ts
Normal file
82
src/exporters/schem_exporter.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { NBT, TagType } from 'prismarine-nbt';
|
||||
import { LOG } from '../util';
|
||||
import { Vector3 } from '../vector';
|
||||
import { BlockMesh } from '../block_mesh';
|
||||
import { IExporter } from './base_exporter';
|
||||
import { saveNBT } from '../util/nbt_util';
|
||||
|
||||
const varintarray = require('varint-array');
|
||||
|
||||
export class SchemExporter extends IExporter {
|
||||
public override getFormatFilter() {
|
||||
return {
|
||||
name: this.getFormatName(),
|
||||
extensions: ['schem'],
|
||||
};
|
||||
}
|
||||
|
||||
public override getFormatName() {
|
||||
return 'Sponge Schematic';
|
||||
}
|
||||
|
||||
public override getFileExtension(): string {
|
||||
return 'schem';
|
||||
}
|
||||
|
||||
private static SCHEMA_VERSION = 2;
|
||||
private static DATA_VERSION = 3105; // 3105 => 1.19, TODO: Remove hardcoded value
|
||||
|
||||
public override export(blockMesh: BlockMesh, filePath: string): boolean {
|
||||
const bounds = blockMesh.getVoxelMesh().getBounds();
|
||||
const sizeVector = bounds.getDimensions().add(1);
|
||||
|
||||
// https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-3.md#paletteObject
|
||||
// const blockMapping: BlockMapping = {};
|
||||
const test: {[name: string]: { type: TagType, value: any }} = {
|
||||
'minecraft:air': { type: TagType.Int, value: 0 },
|
||||
};
|
||||
|
||||
let blockIndex = 1;
|
||||
for (const blockName of blockMesh.getBlockPalette()) {
|
||||
const namespacedBlockName = 'minecraft:' + blockName; // FIXME: All block names should be namespaced on import
|
||||
// ASSERT(!(namespacedBlockName in blockMapping));
|
||||
// blockMapping[namespacedBlockName] = blockIndex;
|
||||
test[namespacedBlockName] = { type: TagType.Int, value: blockIndex };
|
||||
++blockIndex;
|
||||
}
|
||||
LOG(test);
|
||||
|
||||
// const paletteObject = SchemExporter._createBlockStatePalette(blockMapping);
|
||||
const blockData = new Array<number>(sizeVector.x * sizeVector.y * sizeVector.z).fill(0);
|
||||
for (const block of blockMesh.getBlocks()) {
|
||||
const indexVector = Vector3.sub(block.voxel.position, bounds.min);
|
||||
const bufferIndex = SchemExporter._getBufferIndex(sizeVector, indexVector);
|
||||
const namespacedBlockName = 'minecraft:' + block.blockInfo.name;
|
||||
blockData[bufferIndex] = test[namespacedBlockName].value;
|
||||
}
|
||||
|
||||
const nbt: NBT = {
|
||||
type: TagType.Compound,
|
||||
name: 'Schematic',
|
||||
value: {
|
||||
Version: { type: TagType.Int, value: SchemExporter.SCHEMA_VERSION },
|
||||
DataVersion: { type: TagType.Int, value: SchemExporter.DATA_VERSION },
|
||||
Width: { type: TagType.Short, value: sizeVector.x },
|
||||
Height: { type: TagType.Short, value: sizeVector.y },
|
||||
Length: { type: TagType.Short, value: sizeVector.z },
|
||||
PaletteMax: { type: TagType.Int, value: blockIndex },
|
||||
Palette: { type: TagType.Compound, value: test },
|
||||
BlockData: { type: TagType.ByteArray, value: varintarray.encode(blockData) },
|
||||
},
|
||||
};
|
||||
|
||||
LOG(nbt);
|
||||
saveNBT(nbt, filePath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static _getBufferIndex(dimensions: Vector3, vec: Vector3) {
|
||||
return vec.x + (vec.z * dimensions.x) + (vec.y * dimensions.x * dimensions.z);
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ import { StatusHandler } from '../status';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { NBT, TagType, writeUncompressed } from 'prismarine-nbt';
|
||||
import * as zlib from 'zlib';
|
||||
import { NBT, TagType } from 'prismarine-nbt';
|
||||
import { saveNBT } from '../util/nbt_util';
|
||||
|
||||
export class Schematic extends IExporter {
|
||||
private _convertToNBT(blockMesh: BlockMesh) {
|
||||
@ -92,17 +92,7 @@ export class Schematic extends IExporter {
|
||||
this._sizeVector = Vector3.sub(bounds.max, bounds.min).add(1);
|
||||
|
||||
const nbt = this._convertToNBT(blockMesh);
|
||||
|
||||
const outBuffer = fs.createWriteStream(filePath);
|
||||
const newBuffer = writeUncompressed(nbt, 'big');
|
||||
|
||||
zlib.gzip(newBuffer, (err, buffer) => {
|
||||
if (!err) {
|
||||
outBuffer.write(buffer);
|
||||
outBuffer.end();
|
||||
}
|
||||
return err;
|
||||
});
|
||||
saveNBT(nbt, filePath);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -171,9 +171,10 @@ export class UI {
|
||||
label: 'Export',
|
||||
elements: {
|
||||
'export': new ComboBoxElement<TExporters>('File format', [
|
||||
{ id: 'litematic', displayText: 'Litematic' },
|
||||
{ id: 'schematic', displayText: 'Schematic' },
|
||||
{ id: 'obj', displayText: 'Wavefront OBJ' },
|
||||
{ id: 'litematic', displayText: 'Litematic (.litematic)' },
|
||||
{ id: 'schematic', displayText: 'Schematic (.schematic)' },
|
||||
{ id: 'obj', displayText: 'Wavefront OBJ (.obj)' },
|
||||
{ id: 'schem', displayText: 'Sponge Schematic (.schem)' },
|
||||
]),
|
||||
},
|
||||
elementsOrder: ['export'],
|
||||
|
@ -21,6 +21,12 @@ export class UIMessageBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public addItem(...messages: string[]) {
|
||||
for (const message of messages) {
|
||||
this._messages.push('• ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this._messages.join('<br>');
|
||||
}
|
||||
|
21
src/util/nbt_util.ts
Normal file
21
src/util/nbt_util.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { ASSERT } from '../util';
|
||||
|
||||
import path from 'path';
|
||||
import { NBT, writeUncompressed } from 'prismarine-nbt';
|
||||
import zlib from 'zlib';
|
||||
import fs from 'fs';
|
||||
|
||||
export function saveNBT(nbt: NBT, filepath: string) {
|
||||
ASSERT(path.isAbsolute(filepath), '[saveNBT]: filepath is not absolute');
|
||||
|
||||
const outBuffer = fs.createWriteStream(filepath);
|
||||
const newBuffer = writeUncompressed(nbt, 'big');
|
||||
|
||||
zlib.gzip(newBuffer, (err, buffer) => {
|
||||
if (!err) {
|
||||
outBuffer.write(buffer);
|
||||
outBuffer.end();
|
||||
}
|
||||
return err;
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user