forked from mirror/ObjToSchematic
Added prototype .litematic support
This commit is contained in:
parent
bb607353e5
commit
b9aa86d59d
@ -2,7 +2,7 @@ import { Renderer } from "./renderer";
|
||||
import { Mesh } from "./mesh";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { Schematic } from "./schematic";
|
||||
import { Schematic, Litematic } from "./schematic";
|
||||
//const dialog = from 'electron').remote.dialog;
|
||||
import {remote} from 'electron';
|
||||
import * as bootstrap from "bootstrap";
|
||||
@ -153,7 +153,7 @@ export class AppContext {
|
||||
}
|
||||
|
||||
try {
|
||||
const schematic = new Schematic(this._voxelManager);
|
||||
const schematic = new Litematic(this._voxelManager);
|
||||
schematic.exportSchematic(filePath);
|
||||
} catch (err) {
|
||||
this._showToast("Failed to export schematic", ToastColour.RED);
|
||||
|
@ -5,7 +5,7 @@ import { UV, RGB } from "./util";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
interface BlockInfo {
|
||||
export interface BlockInfo {
|
||||
name: string;
|
||||
colour: RGB;
|
||||
texcoord: UV
|
||||
@ -37,12 +37,8 @@ export class BlockAtlas {
|
||||
this._blocks = blocksJSON;
|
||||
}
|
||||
|
||||
public getTexcoord(voxelColour: RGB) {
|
||||
const block = this._getBlock(voxelColour);
|
||||
return block.texcoord;
|
||||
}
|
||||
|
||||
private _getBlock(voxelColour: RGB): BlockInfo {
|
||||
public getBlock(voxelColour: RGB): BlockInfo {
|
||||
const voxelColourVector = new Vector3(voxelColour.r, voxelColour.g, voxelColour.b);
|
||||
|
||||
let cachedBlockIndex = this._cachedBlocks.get(voxelColourVector);
|
||||
|
@ -153,7 +153,7 @@ export class Renderer {
|
||||
|
||||
if (this._debug) {
|
||||
voxelManager.voxels.forEach((voxel) => {
|
||||
this.registerBox(voxel);
|
||||
this.registerBox(voxel.position);
|
||||
});
|
||||
} else {
|
||||
// Setup arrays for calculating voxel ambient occlusion
|
||||
@ -162,7 +162,7 @@ export class Renderer {
|
||||
const voxel = voxelManager.voxels[i];
|
||||
//const colour = voxelManager.voxelColours[i];
|
||||
const texcoord = voxelManager.voxelTexcoords[i];
|
||||
this._registerVoxel(voxel, voxelManager, texcoord);
|
||||
this._registerVoxel(voxel.position, voxelManager, texcoord);
|
||||
}
|
||||
/*
|
||||
voxelManager.voxels.forEach((voxel) => {
|
||||
|
210
src/schematic.ts
210
src/schematic.ts
@ -1,23 +1,10 @@
|
||||
import zlib from "zlib";
|
||||
import fs from "fs";
|
||||
import { byte, NBT, parse, short, TagType, writeUncompressed } from "prismarine-nbt";
|
||||
import { NBT, TagType, writeUncompressed } from "prismarine-nbt";
|
||||
import { Vector3 } from "./vector";
|
||||
import { remote } from "electron"
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Block } from "./block_atlas";
|
||||
|
||||
//const zlib = require("zlib");
|
||||
//const fs = require('fs');
|
||||
//const { parse, writeUncompressed } = require('prismarine-nbt');
|
||||
//const { Vector3 } = require('./vector.js');
|
||||
//const dialog = require('electron').remote.dialog;
|
||||
|
||||
/*
|
||||
interface SchematicValue<T> {
|
||||
type: string,
|
||||
value: T
|
||||
}
|
||||
*/
|
||||
import { powerMonitor } from "electron";
|
||||
|
||||
|
||||
export class Schematic {
|
||||
@ -26,20 +13,16 @@ export class Schematic {
|
||||
private _schematic: NBT;
|
||||
|
||||
constructor(voxelManager: VoxelManager) {
|
||||
//const minPos = voxelManager._voxelCentreToPosition(new Vector3(voxelManager.minX, voxelManager.minY, voxelManager.minZ));
|
||||
//const maxPos = voxelManager._voxelCentreToPosition(new Vector3(voxelManager.maxX, voxelManager.maxY, voxelManager.maxZ));
|
||||
//console.log("SAMPLE", voxelManager.voxels[0]);
|
||||
|
||||
const minPos = new Vector3(voxelManager.minX, voxelManager.minY, voxelManager.minZ);
|
||||
const maxPos = new Vector3(voxelManager.maxX, voxelManager.maxY, voxelManager.maxZ);
|
||||
|
||||
|
||||
this._sizeVector = Vector3.addScalar(Vector3.sub(maxPos, minPos), 1);
|
||||
const bufferSize = this._sizeVector.x * this._sizeVector.y * this._sizeVector.z;
|
||||
|
||||
let blocksData = Array<number>(bufferSize);
|
||||
voxelManager.voxels.forEach(voxel => {
|
||||
//console.log(voxel);
|
||||
const indexVector = Vector3.sub(voxel, minPos);
|
||||
const indexVector = Vector3.sub(voxel.position, minPos);
|
||||
const index = this._getBufferIndex(indexVector);
|
||||
//this._schematic.value.Blocks.value[index] = 1;
|
||||
blocksData[index] = Block.Stone;
|
||||
@ -49,27 +32,16 @@ export class Schematic {
|
||||
type: TagType.Compound,
|
||||
name: 'Schematic',
|
||||
value: {
|
||||
Width: { type: TagType.Short, value: this._sizeVector.x },
|
||||
Height: { type: TagType.Short, value: this._sizeVector.y },
|
||||
Length: { type: TagType.Short, value: this._sizeVector.z },
|
||||
Materials: { type: TagType.String, value: 'Alpha' },
|
||||
Blocks: { type: TagType.ByteArray, value: blocksData },
|
||||
Data: { type: TagType.ByteArray, value: new Array<number>(bufferSize).fill(0) },
|
||||
Entities: { type: TagType.List, value: {type: TagType.Int, value: Array(0)} },
|
||||
TileEntities: { type: TagType.List, value: {type: TagType.Int, value: Array(0)} }
|
||||
Width: { type: TagType.Short, value: this._sizeVector.x },
|
||||
Height: { type: TagType.Short, value: this._sizeVector.y },
|
||||
Length: { type: TagType.Short, value: this._sizeVector.z },
|
||||
Materials: { type: TagType.String, value: 'Alpha' },
|
||||
Blocks: { type: TagType.ByteArray, value: blocksData },
|
||||
Data: { type: TagType.ByteArray, value: new Array<number>(bufferSize).fill(0) },
|
||||
Entities: { type: TagType.List, value: { type: TagType.Int, value: Array(0) } },
|
||||
TileEntities: { type: TagType.List, value: { type: TagType.Int, value: Array(0) } }
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
for (let i = 0; i < voxelManager.voxels.length; ++i) {
|
||||
const voxel = voxelManager.voxels[i];
|
||||
const pos = voxelManager._voxelCentreToPosition(voxel);
|
||||
|
||||
const indexVector = Vector3.sub(pos, minPos);
|
||||
const index = this._getBufferIndex(indexVector);
|
||||
this.schematic.value.Blocks.value[index] = 1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
_getBufferIndex(vec: Vector3) {
|
||||
@ -79,12 +51,166 @@ export class Schematic {
|
||||
exportSchematic(filePath: string) {
|
||||
const outBuffer = fs.createWriteStream(filePath);
|
||||
const newBuffer = writeUncompressed(this._schematic, "big");
|
||||
|
||||
|
||||
zlib.gzip(newBuffer, (err, buffer) => {
|
||||
if (!err) {
|
||||
outBuffer.write(buffer);
|
||||
outBuffer.end(() => console.log('Written!'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class Litematic {
|
||||
|
||||
private _sizeVector: Vector3;
|
||||
private _litematic: NBT;
|
||||
|
||||
// XZY
|
||||
_getBufferIndex(vec: Vector3) {
|
||||
return (this._sizeVector.z * this._sizeVector.x * vec.y) + (this._sizeVector.x * vec.z) + vec.x;
|
||||
}
|
||||
|
||||
constructor(voxelManager: VoxelManager) {
|
||||
const minPos = new Vector3(voxelManager.minX, voxelManager.minY, voxelManager.minZ);
|
||||
const maxPos = new Vector3(voxelManager.maxX, voxelManager.maxY, voxelManager.maxZ);
|
||||
this._sizeVector = Vector3.sub(maxPos, minPos).addScalar(1);
|
||||
console.log("sizeVector", this._sizeVector);
|
||||
|
||||
const bufferSize = this._sizeVector.x * this._sizeVector.y * this._sizeVector.z;
|
||||
let buffer = Array<number>(bufferSize);
|
||||
for (let i = 0; i < bufferSize; ++i) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
|
||||
const blockPalette = voxelManager.blockPalette;
|
||||
let blockMapping: { [name: string]: number } = {"air": 0};
|
||||
for (let i = 0; i < blockPalette.length; ++i) {
|
||||
const blockName = blockPalette[i];
|
||||
blockMapping[blockName] = i + 1; // Ensure 0 maps to air
|
||||
}
|
||||
console.log(blockMapping);
|
||||
|
||||
const paletteSize = blockPalette.length + 1;
|
||||
const stride = (paletteSize - 1).toString(2).length;
|
||||
const numBits = stride * bufferSize;
|
||||
const numLongs = Math.ceil(numBits / 64);
|
||||
console.log("numLongs", numLongs);
|
||||
console.log("stride", stride);
|
||||
|
||||
voxelManager.voxels.forEach(voxel => {
|
||||
const indexVector = Vector3.sub(voxel.position, minPos);
|
||||
const index = this._getBufferIndex(indexVector);
|
||||
buffer[index] = blockMapping[voxel.block];
|
||||
});
|
||||
|
||||
let blockStates: [number, number][] = [];
|
||||
for (let i = 0; i < numLongs; ++i) {
|
||||
blockStates.push([0, 0]);
|
||||
}
|
||||
|
||||
let str = "";
|
||||
for (let i = 0; i < bufferSize; ++i) {
|
||||
str = buffer[i].toString(2).padStart(stride, "0") + str;
|
||||
}
|
||||
let a = Math.ceil(str.length / 64) * 64;
|
||||
str = str.padStart(a, "0");
|
||||
|
||||
let j = 0;
|
||||
for (let i = str.length; i > 0; i -= 64) {
|
||||
let right = parseInt(str.substring(i-32, i), 2);
|
||||
let left = parseInt(str.substring(i-64, i-32), 2);
|
||||
if (right > Math.pow(2, 30)) {
|
||||
right = -((right << 1) >> 1);
|
||||
}
|
||||
if (left > Math.pow(2, 30)) {
|
||||
left = -((left << 1) >> 1);
|
||||
}
|
||||
blockStates[j] = [left, right];
|
||||
++j;
|
||||
}
|
||||
console.log(blockStates);
|
||||
|
||||
let blockStatePalette = Array(paletteSize);
|
||||
for (const block of blockPalette) {
|
||||
let index = blockMapping[block];
|
||||
let blockName = "minecraft:" + block;
|
||||
blockStatePalette[index] = { Name: { type: TagType.String, value: blockName } };
|
||||
}
|
||||
blockStatePalette[0] = { Name: { type: TagType.String, value: "minecraft:air" } };
|
||||
|
||||
this._litematic = {
|
||||
type: TagType.Compound,
|
||||
name: 'Litematic',
|
||||
value: {
|
||||
Metadata: {
|
||||
type: TagType.Compound, value: {
|
||||
Author: { type: TagType.String, value: "" },
|
||||
Description: { type: TagType.String, value: "" },
|
||||
Size: {
|
||||
type: TagType.Compound, value: {
|
||||
x: { type: TagType.Int, value: this._sizeVector.x },
|
||||
y: { type: TagType.Int, value: this._sizeVector.y },
|
||||
z: { type: TagType.Int, value: this._sizeVector.z },
|
||||
}
|
||||
},
|
||||
Name: { type: TagType.String, value: "" },
|
||||
RegionCount: { type: TagType.Int, value: 1 },
|
||||
TimeCreated: { type: TagType.Long, value: [0, 0] },
|
||||
TimeModified: { type: TagType.Long, value: [0, 0] },
|
||||
TotalBlocks: { type: TagType.Int, value: voxelManager.voxels.length },
|
||||
TotalVolume: { type: TagType.Int, value: bufferSize },
|
||||
},
|
||||
},
|
||||
Regions: {
|
||||
type: TagType.Compound, value: {
|
||||
Unnamed: {
|
||||
type: TagType.Compound, value: {
|
||||
BlockStates: { type: TagType.LongArray, value: blockStates },
|
||||
PendingBlockTicks: { type: TagType.List, value: { type: TagType.Int, value: [] } },
|
||||
Position: {
|
||||
type: TagType.Compound, value: {
|
||||
x: { type: TagType.Int, value: 0 },
|
||||
y: { type: TagType.Int, value: 0 },
|
||||
z: { type: TagType.Int, value: 0 },
|
||||
}
|
||||
},
|
||||
BlockStatePalette: { type: TagType.List, value: { type: TagType.Compound, value: blockStatePalette } },
|
||||
Size: {
|
||||
type: TagType.Compound, value: {
|
||||
x: { type: TagType.Int, value: this._sizeVector.x },
|
||||
y: { type: TagType.Int, value: this._sizeVector.y },
|
||||
z: { type: TagType.Int, value: this._sizeVector.z },
|
||||
}
|
||||
},
|
||||
PendingFluidTicks: { type: TagType.List, value: { type: TagType.Int, value: [] } },
|
||||
TileEntities: { type: TagType.List, value: { type: TagType.Int, value: [] } },
|
||||
Entities: { type: TagType.List, value: { type: TagType.Int, value: [] } }
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
MinecraftDataVersion: { type: TagType.Int, value: 2730 },
|
||||
Version: { type: TagType.Int, value: 5 }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exportSchematic(filePath: string) {
|
||||
const outBuffer = fs.createWriteStream(filePath);
|
||||
const newBuffer = writeUncompressed(this._litematic, "big");
|
||||
|
||||
zlib.gzip(newBuffer, (err, buffer) => {
|
||||
if (!err) {
|
||||
outBuffer.write(buffer);
|
||||
outBuffer.end(() => console.log('Written!'));
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
|
@ -40,6 +40,13 @@ export class Vector3 extends Hashable {
|
||||
);
|
||||
}
|
||||
|
||||
addScalar(scalar: number) {
|
||||
this.x += scalar;
|
||||
this.y += scalar;
|
||||
this.z += scalar;
|
||||
return this;
|
||||
}
|
||||
|
||||
static sub(vecA: Vector3, vecB: Vector3) {
|
||||
return new Vector3(
|
||||
vecA.x - vecB.x,
|
||||
|
@ -2,11 +2,16 @@ import { CubeAABB } from "./aabb";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { HashSet } from "./hash_map";
|
||||
import { Texture } from "./texture";
|
||||
import { BlockAtlas } from "./block_atlas";
|
||||
import { BlockAtlas, BlockInfo } from "./block_atlas";
|
||||
import { UV, RGB } from "./util";
|
||||
import { Triangle } from "./triangle";
|
||||
import { Mesh, MaterialType } from "./mesh";
|
||||
|
||||
interface Block {
|
||||
position: Vector3;
|
||||
block: string
|
||||
}
|
||||
|
||||
|
||||
interface TriangleCubeAABBs {
|
||||
triangle: Triangle;
|
||||
@ -15,7 +20,7 @@ interface TriangleCubeAABBs {
|
||||
|
||||
export class VoxelManager {
|
||||
|
||||
public voxels: Array<Vector3>;
|
||||
public voxels: Array<Block>;
|
||||
public voxelTexcoords: Array<UV>;
|
||||
public triangleAABBs: Array<TriangleCubeAABBs>;
|
||||
public _voxelSize: number;
|
||||
@ -25,6 +30,7 @@ export class VoxelManager {
|
||||
private _blockMode!: MaterialType;
|
||||
private _currentTexture!: Texture;
|
||||
private _currentColour!: RGB;
|
||||
public blockPalette: Array<string>;
|
||||
|
||||
public minX = Infinity; public maxX = -Infinity;
|
||||
public minY = Infinity; public maxY = -Infinity;
|
||||
@ -38,6 +44,7 @@ export class VoxelManager {
|
||||
|
||||
this.voxelsHash = new HashSet(2048);
|
||||
this.blockAtlas = new BlockAtlas();
|
||||
this.blockPalette = [];
|
||||
}
|
||||
|
||||
public setVoxelSize(voxelSize: number) {
|
||||
@ -47,6 +54,7 @@ export class VoxelManager {
|
||||
private _clearVoxels() {
|
||||
this.voxels = [];
|
||||
this.voxelTexcoords = [];
|
||||
this.blockPalette = []
|
||||
|
||||
this.minX = Infinity;
|
||||
this.minY = Infinity;
|
||||
@ -99,7 +107,7 @@ export class VoxelManager {
|
||||
return this.voxelsHash.contains(pos);
|
||||
}
|
||||
|
||||
addVoxel(vec: Vector3, blockTexcoord: UV) {
|
||||
addVoxel(vec: Vector3, block: BlockInfo) {
|
||||
|
||||
// (0.5, 0.5, 0.5) -> (0, 0, 0);
|
||||
//console.log(vec);
|
||||
@ -118,8 +126,12 @@ export class VoxelManager {
|
||||
if (this.voxelsHash.contains(pos)) {
|
||||
return;
|
||||
}
|
||||
this.voxels.push(pos);
|
||||
this.voxelTexcoords.push(blockTexcoord);
|
||||
|
||||
if (!this.blockPalette.includes(block.name)) {
|
||||
this.blockPalette.push(block.name);
|
||||
}
|
||||
this.voxels.push({position: pos, block: block.name});
|
||||
this.voxelTexcoords.push(block.texcoord);
|
||||
this.voxelsHash.add(pos);
|
||||
|
||||
this.minX = Math.min(this.minX, pos.x);
|
||||
@ -253,9 +265,9 @@ export class VoxelManager {
|
||||
for (const sub of AABB.subdivide()) {
|
||||
if (triangle.intersectAABB(sub)) {
|
||||
const voxelColour = this._getVoxelColour(triangle, sub.centre);
|
||||
const blockTexcoord = this.blockAtlas.getTexcoord(voxelColour);
|
||||
const block = this.blockAtlas.getBlock(voxelColour);
|
||||
|
||||
this.addVoxel(sub.centre, blockTexcoord);
|
||||
this.addVoxel(sub.centre, block);
|
||||
triangleAABBs.push(sub);
|
||||
}
|
||||
}
|
||||
@ -344,7 +356,7 @@ export class VoxelManager {
|
||||
} else {
|
||||
// We've reached the voxel level, stop
|
||||
const voxelColour = this._getVoxelColour(triangle, aabb.centre);
|
||||
const blockTexcoord = this.blockAtlas.getTexcoord(voxelColour);
|
||||
const blockTexcoord = this.blockAtlas.getBlock(voxelColour);
|
||||
|
||||
this.addVoxel(aabb.centre, blockTexcoord);
|
||||
triangleAABBs.push(aabb);
|
||||
@ -371,6 +383,7 @@ export class VoxelManager {
|
||||
this.voxeliseTriangle(triangle);
|
||||
}
|
||||
}
|
||||
console.log(this.blockPalette);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user