forked from mirror/ObjToSchematic
Glass redesign
This commit is contained in:
parent
e904fdef7d
commit
8c71dbb14b
124
index.html
124
index.html
@ -5,92 +5,70 @@
|
||||
<meta charset="utf8">
|
||||
<title>ObjToSchematic</title>
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
<link rel="stylesheet" href="./src/vendor/bootstrap.css">
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="c"></canvas>
|
||||
<div class="toolbar">
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<span id="buttonChooseFile" class="input-group-addon button pill-left">
|
||||
Choose file
|
||||
</span><input id="inputFile" class="text-field pill-right" style="cursor: pointer" type="text" value="">
|
||||
<div id="main">
|
||||
<canvas id="c"></canvas>
|
||||
<div class="toolbar-container">
|
||||
<div class="toolbar-container glass pill-left pill-right">
|
||||
<div class="toolbar-item pill-left toolbar-item-1">
|
||||
<div class="input-group">
|
||||
<button id="buttonChooseFile" class="input-group-item pill-left">
|
||||
Choose file
|
||||
</button>
|
||||
<input id="inputFile" type="text"
|
||||
class="input-group-item input-group-item-dominant pill-right text-field cursor-pointer" value=""
|
||||
readonly></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon button pill-left">
|
||||
Voxel size
|
||||
</span><input class="text-field" type="number" value="0.1">
|
||||
<button class="button pill-right bg-blue">Voxelise</button>
|
||||
<div class="toolbar-item toolbar-item-2">
|
||||
<div id="groupVoxel" class="input-group transparent">
|
||||
<div class="input-group-item pill-left">Voxel size</div>
|
||||
<input id="inputVoxelSize" type="number" class="input-group-item input-group-item-dominant text-field"
|
||||
value="0.1" disabled>
|
||||
</input>
|
||||
<button id="buttonVoxelise" class="input-group-item pill-right bg-primary cursor-pointer" disabled>
|
||||
Voxelise
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<button class="button bg-red pill-left half-width">Export .schematic</button><button class="button bg-red pill-right half-width">Export .litematic</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-div">Model loaded successfully</div>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="toast canvas-toast align-items-center text-white bg-danger border-0" role="alert"
|
||||
aria-live="assertive" aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div id="toastText" class="toast-body ms-2">
|
||||
toastText
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- EXPORT DISCLAIMER -->
|
||||
<div id="modalExport" class="modal custom-modal-background fade" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div id="modalExportText" class="modal-body">
|
||||
<p>modalText</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button id="exportBtn" type="button" class="btn btn-danger">
|
||||
<img src="./resources/svg/save.svg" alt="">
|
||||
Export
|
||||
</button>
|
||||
<div class="toolbar-item pill-right toolbar-item-3">
|
||||
<div id="groupExport" class="input-group transparent">
|
||||
<button id="buttonSchematic" class="input-group-item input-group-item-dominant pill-left bg-secondary"
|
||||
disabled>
|
||||
Export .schematic
|
||||
</button>
|
||||
<button id="buttonLitematic" class="input-group-item input-group-item-dominant pill-right bg-secondary"
|
||||
disabled>
|
||||
Export .litematic
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GENERAL DISCLAIMER -->
|
||||
<div id="modalGeneral" class="modal custom-modal-background fade" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div id="modalGeneralText" class="modal-body">
|
||||
<p>modalText</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Okay</button>
|
||||
</div>
|
||||
<div id="toast" class="status-container pill-left pill-right glass toast hide"></div>
|
||||
|
||||
<div id="modal" class="pill-left pill-right glass modal" hidden>
|
||||
<div class="content">
|
||||
<div id="textModal" class="modalTop"></div>
|
||||
<div class="modalBottom">
|
||||
<button id="buttonModalAction" class="button pill bg-secondary"></button>
|
||||
<button id="buttonModalClose" class="button pill bg-primary">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script src="./src/vendor/jquery-3.6.0.min.js"></script>
|
||||
<script src="./src/vendor/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="./src/vendor/popper.min.js"></script>
|
||||
<script src="./src/vendor/bootstrap.min.js"></script>
|
||||
<script>
|
||||
require("./dist/client.js")
|
||||
</script>
|
||||
</body>
|
||||
|
||||
<script src="./src/vendor/jquery-3.6.0.min.js"></script>
|
||||
<script src="./src/vendor/jquery-3.3.1.slim.min.js"></script>
|
||||
<script src="./src/vendor/popper.min.js"></script>
|
||||
<script>
|
||||
require("./dist/client.js");
|
||||
</script>
|
||||
|
||||
</html>
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "npm run build && electron ./dist/main.js",
|
||||
"start": "npm run-script build && electron ./dist/main.js --enable-logging",
|
||||
"export": "electron-packager . ObjToSchematic --platform=win32 --arch=x64"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Renderer } from "./renderer";
|
||||
import { Mesh } from "./mesh";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { Schematic, Litematic, Exporter } from "./schematic";
|
||||
//const dialog = from 'electron').remote.dialog;
|
||||
import {remote} from 'electron';
|
||||
import * as bootstrap from "bootstrap";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Renderer } from "./renderer";
|
||||
import { Toast, ToastStyle } from "./ui/toast";
|
||||
import { Modal } from "./ui/modal";
|
||||
import { Mesh } from "./mesh";
|
||||
|
||||
import { remote } from 'electron';
|
||||
import path from "path";
|
||||
|
||||
enum ToastColour {
|
||||
RED = "bg-danger",
|
||||
@ -13,11 +14,6 @@ enum ToastColour {
|
||||
GREEN = "bg-success"
|
||||
}
|
||||
|
||||
export enum ExportFormat {
|
||||
SCHEMATIC = "schematic",
|
||||
LITEMATIC = "litematic"
|
||||
}
|
||||
|
||||
|
||||
export class AppContext {
|
||||
|
||||
@ -27,12 +23,6 @@ export class AppContext {
|
||||
private _voxelManager: VoxelManager;
|
||||
private _loadedMesh?: Mesh;
|
||||
|
||||
private _toast: bootstrap.Toast;
|
||||
private _modalExport: bootstrap.Modal;
|
||||
//private _modalVoxelise: bootstrap.Modal;
|
||||
private _modalGeneral: bootstrap.Modal;
|
||||
private _cachedFormat?: ExportFormat;
|
||||
|
||||
|
||||
constructor() {
|
||||
this._voxelSize = $("#voxelInput").prop("value");
|
||||
@ -46,71 +36,49 @@ export class AppContext {
|
||||
this._renderer = new Renderer(this._gl);
|
||||
this._voxelManager = new VoxelManager(this._voxelSize);
|
||||
|
||||
this._toast = new bootstrap.Toast(<HTMLElement>document.getElementById('toast'), {delay: 3000});
|
||||
this._modalExport = new bootstrap.Modal(<HTMLElement>document.getElementById('modalExport'), {});
|
||||
//this._modalVoxelise = new bootstrap.Modal(<HTMLElement>document.getElementById('modalVoxelise'), {});
|
||||
this._modalGeneral = new bootstrap.Modal(<HTMLElement>document.getElementById('modalGeneral'), {});
|
||||
|
||||
//this._showModalGeneral("Note that in this current version, all blocks in the schematic will be exported as Stone blocks. This will be changed in version 0.4.");
|
||||
}
|
||||
|
||||
public load() {
|
||||
const files = $("#fileInput").prop("files");
|
||||
|
||||
|
||||
public load(files: Array<string>) {
|
||||
if (files.length != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const file = files[0];
|
||||
if (!file.name.endsWith(".obj") && !file.name.endsWith(".OBJ")) {
|
||||
this._showToast("Files must be .obj format", ToastColour.RED);
|
||||
if (!file.endsWith(".obj") && !file.endsWith(".OBJ")) {
|
||||
Toast.show("Files must be .obj format", ToastStyle.Failure);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const parsedPath = path.parse(file);
|
||||
|
||||
try {
|
||||
this._loadedMesh = new Mesh(files[0].path, this._gl);
|
||||
this._loadedMesh = new Mesh(file, this._gl);
|
||||
} catch (err: any) {
|
||||
this._showToast(err.message, ToastColour.RED);
|
||||
console.log(err);
|
||||
Toast.show("Could not load mesh", ToastStyle.Failure);
|
||||
return;
|
||||
}
|
||||
|
||||
this._renderer.clear();
|
||||
this._renderer.registerMesh(this._loadedMesh);
|
||||
this._renderer.compile();
|
||||
|
||||
$('#voxelInput').prop('disabled', false);
|
||||
$('#voxelBtn').prop('disabled', false);
|
||||
$('#splitBtn').prop('disabled', true);
|
||||
|
||||
$('#exportSchematic').prop('disabled', true);
|
||||
$('#exportLitematic').prop('disabled', true);
|
||||
|
||||
this._showToast(`Successfully loaded ${file.name}`, ToastColour.GREEN);
|
||||
}
|
||||
|
||||
/*
|
||||
split() {
|
||||
this.voxelSize /= 2;
|
||||
$("#voxelInput").prop('value', this.voxelSize);
|
||||
|
||||
this.voxelManager.splitVoxels();
|
||||
|
||||
this.renderer.clear();
|
||||
this.renderer.registerVoxelMesh(this.voxelManager);
|
||||
this.renderer.compile();
|
||||
}
|
||||
*/
|
||||
|
||||
public voxeliseDisclaimer() {
|
||||
//this._modalVoxelise.show();
|
||||
this.voxelise();
|
||||
|
||||
$('#inputFile').prop("value", parsedPath.base);
|
||||
$('#groupVoxel').removeClass("transparent");
|
||||
|
||||
$('#inputVoxelSize').prop('disabled', false);
|
||||
$('#buttonVoxelise').prop('disabled', false);
|
||||
|
||||
$('#buttonSchematic').prop('disabled', true);
|
||||
$('#buttonLitematic').prop('disabled', true);
|
||||
|
||||
Toast.show("Loaded successfully", ToastStyle.Success);
|
||||
}
|
||||
|
||||
public voxelise() {
|
||||
const newVoxelSize = $("#voxelInput").prop('value');
|
||||
const newVoxelSize = $("#inputVoxelSize").prop('value');
|
||||
if (newVoxelSize < 0.001) {
|
||||
this._showToast("Voxel size must be at least 0.001", ToastColour.RED);
|
||||
Toast.show("Voxel size must be at least 0.001", ToastStyle.Failure);
|
||||
return;
|
||||
}
|
||||
this._voxelSize = newVoxelSize;
|
||||
@ -128,49 +96,43 @@ export class AppContext {
|
||||
this._renderer.registerVoxelMesh(this._voxelManager);
|
||||
this._renderer.compile();
|
||||
} catch (err: any) {
|
||||
this._showToast(err.message, ToastColour.RED);
|
||||
Toast.show(err.message, ToastStyle.Failure);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#exportSchematic').prop('disabled', false);
|
||||
$('#exportLitematic').prop('disabled', false);
|
||||
$('#groupExport').removeClass("transparent");
|
||||
$('#buttonSchematic').prop('disabled', false);
|
||||
$('#buttonLitematic').prop('disabled', false);
|
||||
|
||||
this._showToast("Voxelised successfully", ToastColour.GREEN);
|
||||
Toast.show("Voxelised successfully", ToastStyle.Success);
|
||||
}
|
||||
|
||||
public exportDisclaimer(exportFormat: ExportFormat) {
|
||||
public exportDisclaimer(exporter: Exporter) {
|
||||
const schematicHeight = Math.ceil(this._voxelManager.maxZ - this._voxelManager.minZ);
|
||||
|
||||
let message = "";
|
||||
if (schematicHeight > 320) {
|
||||
message += `Note, this structure is <b>${schematicHeight}</b> blocks tall, this is larger than the height of a Minecraft world (320 in 1.17, 256 in <=1.16). `;
|
||||
}
|
||||
if (exportFormat == ExportFormat.SCHEMATIC) {
|
||||
message += "Schematic files only support pre-1.13 blocks. As a result, all blocks will be exported as Stone. To export the blocks use the .litematic format with the Litematica mod.";
|
||||
|
||||
const formatDisclaimer = exporter.getFormatDisclaimer();
|
||||
if (formatDisclaimer) {
|
||||
message += "\n" + formatDisclaimer;
|
||||
}
|
||||
|
||||
this._cachedFormat = exportFormat;
|
||||
|
||||
if (message.length == 0) {
|
||||
this.export();
|
||||
this.export(exporter);
|
||||
} else {
|
||||
this._showModalExport(message);
|
||||
Modal.setButton("Export", () => { this.export(exporter); });
|
||||
Modal.show(message);
|
||||
}
|
||||
}
|
||||
|
||||
public export() {
|
||||
this._modalExport.hide();
|
||||
|
||||
public export(exporter: Exporter) {
|
||||
const filePath = remote.dialog.showSaveDialogSync({
|
||||
title: "Save structure",
|
||||
buttonLabel: "Save",
|
||||
filters: this._cachedFormat == ExportFormat.SCHEMATIC ? [{
|
||||
name: 'Schematic',
|
||||
extensions: ['schematic']
|
||||
}] : [{
|
||||
name: 'Litematic',
|
||||
extensions: ['litematic']
|
||||
}]
|
||||
filters: [ exporter.getFormatFilter() ]
|
||||
});
|
||||
|
||||
if (filePath === undefined) {
|
||||
@ -179,20 +141,14 @@ export class AppContext {
|
||||
}
|
||||
|
||||
try {
|
||||
let exporter: Exporter;
|
||||
if (this._cachedFormat == ExportFormat.SCHEMATIC) {
|
||||
exporter = new Schematic(this._voxelManager);
|
||||
} else {
|
||||
exporter = new Litematic(this._voxelManager);
|
||||
}
|
||||
exporter.export(filePath);
|
||||
exporter.export(filePath, this._voxelManager);
|
||||
} catch (err) {
|
||||
this._showToast("Failed to export", ToastColour.RED);
|
||||
console.error(err);
|
||||
Toast.show("Failed to export", ToastStyle.Failure)
|
||||
return;
|
||||
}
|
||||
|
||||
this._showToast("Successfully saved", ToastColour.GREEN);
|
||||
|
||||
Toast.show("Successfully exported", ToastStyle.Success);
|
||||
}
|
||||
|
||||
|
||||
@ -200,29 +156,4 @@ export class AppContext {
|
||||
this._renderer.draw(this._voxelManager._voxelSize);
|
||||
}
|
||||
|
||||
|
||||
private _showToast(text: string, colour: ToastColour) {
|
||||
$("#toast").removeClass(ToastColour.RED);
|
||||
$("#toast").removeClass(ToastColour.ORANGE);
|
||||
$("#toast").removeClass(ToastColour.GREEN);
|
||||
$("#toast").addClass(colour);
|
||||
|
||||
$("#toastText").html(text);
|
||||
//$("#toast").toast({ delay: 3000 });
|
||||
//$("#toast").toast('show');
|
||||
this._toast.show();
|
||||
}
|
||||
|
||||
private _showModalExport(text: string) {
|
||||
$("#modalExportText").html(text);
|
||||
this._modalExport.show();
|
||||
}
|
||||
|
||||
private _showModalGeneral(text: string) {
|
||||
$("#modalGeneralText").html(text);
|
||||
this._modalGeneral.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.AppContext = AppContext;
|
||||
}
|
@ -1,37 +1,43 @@
|
||||
import { AppContext } from "./app_context";
|
||||
import { ExportFormat } from "./app_context";
|
||||
import { Schematic, Litematic } from "./schematic";
|
||||
import { remote } from "electron";
|
||||
|
||||
const context = new AppContext();
|
||||
|
||||
function handleFileLoad() {
|
||||
const files = remote.dialog.showOpenDialogSync({
|
||||
title: "Load Waveform .obj file",
|
||||
buttonLabel: "Load",
|
||||
filters: [{
|
||||
name: 'Waveform obj file',
|
||||
extensions: ['obj']
|
||||
}]
|
||||
});
|
||||
|
||||
$("#loadBtn").on("click", () => {
|
||||
context.load();
|
||||
if (files) {
|
||||
context.load(files);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$("#buttonChooseFile").on("click", () => {
|
||||
handleFileLoad();
|
||||
});
|
||||
|
||||
|
||||
$("#voxelBtn").on("click", () => {
|
||||
context.voxeliseDisclaimer();
|
||||
$("#inputFile").on("click", () => {
|
||||
handleFileLoad();
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
$("#splitBtn").on("click", () => {
|
||||
context.split();
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
$("#exportBtn").on("click", async () => {
|
||||
context.export();
|
||||
$("#buttonVoxelise").on("click", () => {
|
||||
context.voxelise();
|
||||
});
|
||||
|
||||
|
||||
$("#exportSchematic").on("click", async () => {
|
||||
context.exportDisclaimer(ExportFormat.SCHEMATIC);
|
||||
$("#buttonSchematic").on("click", async () => {
|
||||
context.exportDisclaimer(new Schematic());
|
||||
});
|
||||
|
||||
$("#exportLitematic").on("click", async () => {
|
||||
context.exportDisclaimer(ExportFormat.LITEMATIC);
|
||||
$("#buttonLitematic").on("click", async () => {
|
||||
context.exportDisclaimer(new Litematic());
|
||||
});
|
||||
|
||||
|
||||
|
@ -14,8 +14,10 @@ function createWindow () {
|
||||
|
||||
//const appIcon = new Tray("../resources/icon.png");
|
||||
mainWindow = new BrowserWindow({
|
||||
width,
|
||||
height,
|
||||
width: width,
|
||||
height: height,
|
||||
minWidth: 1280,
|
||||
minHeight: 720,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
|
@ -5,26 +5,28 @@ import { Vector3 } from "./vector";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
import { Block } from "./block_atlas";
|
||||
|
||||
|
||||
export abstract class Exporter {
|
||||
|
||||
protected _voxelManager: VoxelManager
|
||||
protected _minPos: Vector3
|
||||
protected _maxPos: Vector3
|
||||
protected _sizeVector: Vector3
|
||||
protected _voxelManager!: VoxelManager
|
||||
protected _minPos!: Vector3
|
||||
protected _maxPos!: Vector3
|
||||
protected _sizeVector!: Vector3
|
||||
|
||||
constructor(voxelManager: VoxelManager) {
|
||||
abstract convertToNBT(): NBT
|
||||
abstract getFormatFilter(): Electron.FileFilter;
|
||||
abstract getFormatName(): string;
|
||||
|
||||
getFormatDisclaimer(): string | undefined {
|
||||
return;
|
||||
}
|
||||
|
||||
export(filePath: string, voxelManager: VoxelManager): boolean {
|
||||
this._voxelManager = voxelManager;
|
||||
|
||||
this._minPos = new Vector3(voxelManager.minX, voxelManager.minY, voxelManager.minZ);
|
||||
this._maxPos = new Vector3(voxelManager.maxX, voxelManager.maxY, voxelManager.maxZ);
|
||||
this._sizeVector = Vector3.sub(this._maxPos, this._minPos).addScalar(1);
|
||||
console.log(this._minPos, this._maxPos);
|
||||
}
|
||||
|
||||
abstract convertToNBT(): NBT
|
||||
|
||||
export(filePath: string): boolean {
|
||||
const nbt = this.convertToNBT();
|
||||
|
||||
const outBuffer = fs.createWriteStream(filePath);
|
||||
@ -79,6 +81,21 @@ export class Schematic extends Exporter {
|
||||
return (sizeVector.z * sizeVector.x * vec.y) + (sizeVector.x * vec.z) + vec.x;
|
||||
}
|
||||
|
||||
getFormatFilter() {
|
||||
return {
|
||||
name: this.getFormatName(),
|
||||
extensions: ['schematic']
|
||||
}
|
||||
}
|
||||
|
||||
getFormatName() {
|
||||
return "Schematic";
|
||||
}
|
||||
|
||||
getFormatDisclaimer() {
|
||||
return "Schematic files only support pre-1.13 blocks. As a result, all blocks will be exported as Stone. To export the blocks, use the .litematic format with the Litematica mod.";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type BlockID = number;
|
||||
@ -243,5 +260,16 @@ export class Litematic extends Exporter {
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
getFormatFilter() {
|
||||
return {
|
||||
name: this.getFormatName(),
|
||||
extensions: ['litematic']
|
||||
}
|
||||
}
|
||||
|
||||
getFormatName() {
|
||||
return "Litematic";
|
||||
}
|
||||
|
||||
}
|
26
src/ui/modal.ts
Normal file
26
src/ui/modal.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export class Modal {
|
||||
|
||||
public static show(text: string) {
|
||||
this._setText(text);
|
||||
this._show();
|
||||
}
|
||||
|
||||
public static setButton(text: string, onClick: (() => void)) {
|
||||
$("#buttonModalAction").html(text);
|
||||
$("#buttonModalAction").on("click", () => { this._hide(); onClick(); } );
|
||||
$("#buttonModalClose").on("click", () => { this._hide(); });
|
||||
}
|
||||
|
||||
private static _setText(text: string) {
|
||||
$("#textModal").html(text);
|
||||
}
|
||||
|
||||
private static _show() {
|
||||
$("#modal").show()
|
||||
}
|
||||
|
||||
private static _hide() {
|
||||
$("#modal").hide();
|
||||
}
|
||||
|
||||
}
|
36
src/ui/toast.ts
Normal file
36
src/ui/toast.ts
Normal file
@ -0,0 +1,36 @@
|
||||
export enum ToastStyle {
|
||||
Success = "bg-success",
|
||||
Failure = "bg-failure"
|
||||
}
|
||||
|
||||
export class Toast {
|
||||
|
||||
private static current: ToastStyle = ToastStyle.Success;
|
||||
private static autoHideDelay: number = 4000;
|
||||
|
||||
public static show(text: string, style: ToastStyle) {
|
||||
this._setText(text);
|
||||
this._setStyle(style);
|
||||
this._show();
|
||||
setTimeout(() => { this._hide(); }, this.autoHideDelay);
|
||||
}
|
||||
|
||||
private static _setText(text: string) {
|
||||
$("#toast").html(text);
|
||||
}
|
||||
|
||||
private static _setStyle(style: ToastStyle) {
|
||||
$("#toast").removeClass(Toast.current);
|
||||
$("#toast").addClass(style);
|
||||
Toast.current = style;
|
||||
}
|
||||
|
||||
private static _show() {
|
||||
$("#toast").removeClass("hide");
|
||||
}
|
||||
|
||||
private static _hide() {
|
||||
$("#toast").addClass("hide");
|
||||
}
|
||||
|
||||
}
|
@ -6,10 +6,12 @@ import { BlockAtlas, BlockInfo } from "./block_atlas";
|
||||
import { UV, RGB } from "./util";
|
||||
import { Triangle } from "./triangle";
|
||||
import { Mesh, MaterialType } from "./mesh";
|
||||
import fs from "fs";
|
||||
|
||||
interface Block {
|
||||
position: Vector3;
|
||||
colours: Array<RGB>;
|
||||
colours?: Array<RGB>;
|
||||
colour?: RGB;
|
||||
block?: string
|
||||
}
|
||||
|
||||
@ -112,15 +114,18 @@ export class VoxelManager {
|
||||
this.blockPalette = [];
|
||||
|
||||
for (let i = 0; i < this.voxels.length; ++i) {
|
||||
let averageColour = this.voxels[i].colours.reduce((a, c) => {return {r: a.r + c.r, g: a.g + c.g, b: a.b + c.b}})
|
||||
let n = this.voxels[i].colours.length;
|
||||
let averageColour = this.voxels[i].colours!.reduce((a, c) => {return {r: a.r + c.r, g: a.g + c.g, b: a.b + c.b}})
|
||||
let n = this.voxels[i].colours!.length;
|
||||
averageColour.r /= n;
|
||||
averageColour.g /= n;
|
||||
averageColour.b /= n;
|
||||
const block = this.blockAtlas.getBlock(averageColour);
|
||||
this.voxels[i].block = block.name;
|
||||
this.voxels[i].colour = averageColour;
|
||||
delete this.voxels[i].colours;
|
||||
this.voxelTexcoords.push(block.texcoord);
|
||||
|
||||
|
||||
if (!this.blockPalette.includes(block.name)) {
|
||||
this.blockPalette.push(block.name);
|
||||
}
|
||||
@ -146,7 +151,7 @@ export class VoxelManager {
|
||||
if (this.voxelsHash.contains(pos)) {
|
||||
for (let i = 0; i < this.voxels.length; ++i) {
|
||||
if (this.voxels[i].position.equals(pos)) {
|
||||
this.voxels[i].colours.push(block.colour);
|
||||
this.voxels[i].colours!.push(block.colour);
|
||||
//console.log("Overlap");
|
||||
return;
|
||||
}
|
||||
@ -414,7 +419,14 @@ export class VoxelManager {
|
||||
}
|
||||
}
|
||||
this.assignBlocks();
|
||||
console.log(this.blockPalette);
|
||||
|
||||
this._dumpVoxelsToFile();
|
||||
}
|
||||
|
||||
private _dumpVoxelsToFile() {
|
||||
const json = JSON.stringify(this.voxels);
|
||||
console.log(this.voxels.length);
|
||||
fs.writeFileSync('block_dump.json', json);
|
||||
}
|
||||
|
||||
}
|
289
styles.css
289
styles.css
@ -1,144 +1,237 @@
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@300;400&display=swap');
|
||||
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #101214;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
position: absolute;
|
||||
margin-left: 20%;
|
||||
margin-top: 30px;
|
||||
padding: 10px;
|
||||
padding-top: 8.5px;
|
||||
width: 60%;
|
||||
height: 60px;
|
||||
body {
|
||||
margin: 0px;
|
||||
background-color: rgb(25, 25, 25);
|
||||
background-size: 100%;
|
||||
font-family: 'Lexend', sans-serif;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: 'Lexend', sans-serif;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
input:enabled {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: 'Lexend', sans-serif;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
button:hover:enabled {
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: default;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.toolbar-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
padding: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.status-container {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 25px;
|
||||
transform: translate(-50%, -50%);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.glass {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 100px;
|
||||
border: solid;
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
border-width: 1.5px;
|
||||
border-style: solid;
|
||||
border-color: rgba(255, 255, 255, 0.03);
|
||||
box-shadow: 0 1rem 2rem rgba(0, 0, 0, .175);
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.canvas-toast {
|
||||
position:absolute;
|
||||
left:10px;
|
||||
top:68px;
|
||||
.toast {
|
||||
padding: 10px 50px !important;
|
||||
font-weight: 300;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.custom-modal-background {
|
||||
background-color: rgba(0, 0, 0, .25);
|
||||
backdrop-filter: blur(5px);
|
||||
.hide {
|
||||
opacity: 0;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: rgba(40, 167, 69, 0.5) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.bg-failure {
|
||||
background-color: rgba(220, 53, 69, 0.5) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.transparent {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.toolbar-item {
|
||||
padding: 10px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-content: stretch;
|
||||
}
|
||||
|
||||
.input-group > input {
|
||||
flex: 1 0 auto;
|
||||
.input-group-item {
|
||||
background-color: #EFEFEF;
|
||||
padding: 10px 14px;
|
||||
border: solid;
|
||||
border-color: rgba(0, 0, 0, 0.25);
|
||||
border-width: 1.5px 0px;
|
||||
}
|
||||
|
||||
|
||||
.input-group-addon {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.bg-blue {
|
||||
background-color: #0D6EFD !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.bg-red {
|
||||
background-color: #BB2D3B !important;
|
||||
color: white !important;
|
||||
.input-group-item-dominant {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.pill-left {
|
||||
border-top-left-radius: 20px;
|
||||
border-bottom-left-radius: 20px;
|
||||
padding-left: 20px;
|
||||
border-top-left-radius: 32px;
|
||||
border-bottom-left-radius: 32px;
|
||||
border-left-width: 1.5px;
|
||||
border-right-width: 0px;
|
||||
}
|
||||
|
||||
.pill-right {
|
||||
border-top-right-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 40px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
font-size: 15px;
|
||||
font-family: 'Lexend', sans-serif;
|
||||
font-weight: 400;
|
||||
border: none;
|
||||
color: #33393F;
|
||||
background: #EFEFEF;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
cursor: pointer;
|
||||
border-top-right-radius: 32px;
|
||||
border-bottom-right-radius: 32px;
|
||||
border-right-width: 1.5px;
|
||||
border-left-width: 0px;
|
||||
}
|
||||
|
||||
.text-field {
|
||||
background-color: white;
|
||||
height: 40px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
font-family: 'Lexend', sans-serif;
|
||||
font-size: 15px;
|
||||
color: gray;
|
||||
font-weight: 300;
|
||||
border: none;
|
||||
cursor: text;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
border: none;
|
||||
.text-field:read-only {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.half-width {
|
||||
width: 50%;
|
||||
.text-field:disabled {
|
||||
background-color: #EFEFEF;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.box-div {
|
||||
position: absolute;
|
||||
margin-top: 280px;
|
||||
height: 40px;
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
background-color: #28a746c4;
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
padding-top: 6px;
|
||||
.bg-primary {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bg-secondary {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bg-primary:disabled {
|
||||
background-color: #5AAAFF;
|
||||
}
|
||||
|
||||
.bg-secondary:disabled {
|
||||
background-color: #E87C87;
|
||||
}
|
||||
|
||||
.bg-primary:hover:enabled {
|
||||
background-color: #0069D9;
|
||||
}
|
||||
|
||||
.bg-secondary:hover:enabled {
|
||||
background-color: #C82333;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.toolbar-container {
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.input-group-item-dominant {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.toolbar-item-1 {
|
||||
border-top-right-radius: 200px;
|
||||
border-bottom-right-radius: 200px;
|
||||
border-right-width: 1.5px;
|
||||
border-left-width: 0px;
|
||||
}
|
||||
.toolbar-item-2 {
|
||||
border-top-left-radius: 200px;
|
||||
border-bottom-left-radius: 200px;
|
||||
border-top-right-radius: 200px;
|
||||
border-bottom-right-radius: 200px;
|
||||
border-right-width: 1.5px;
|
||||
border-left-width: 1.5px;
|
||||
}
|
||||
.toolbar-item-3 {
|
||||
border-top-left-radius: 200px;
|
||||
border-bottom-left-radius: 200px;
|
||||
border-right-width: 0px;
|
||||
border-left-width: 1.5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.pill {
|
||||
border-radius: 32px;
|
||||
border-width: 1.5px;
|
||||
border-style: solid;
|
||||
border-color: rgba(255, 255, 255, 0.03);
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: absolute;
|
||||
padding: 40px;
|
||||
margin: 0;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
div.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modalTop {
|
||||
color: white;
|
||||
flex: 1;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.modalBottom {
|
||||
flex: none;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 8px 40px;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
//"resolveJsonModule": true,
|
||||
|
Loading…
Reference in New Issue
Block a user