Merge branch '0.6' into 0.6-chunks

This commit is contained in:
Lucas Dower 2022-10-02 15:57:15 +01:00 committed by GitHub
commit c3588ca802
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 167 additions and 95 deletions

17
res/config.json Normal file
View File

@ -0,0 +1,17 @@
{
"AMBIENT_OCCLUSION_OVERRIDE_CORNER": true,
"LOG_TO_FILE": true,
"USE_WORKER_THREAD": true,
"MULTISAMPLE_COUNT": 16,
"OLD_SPACE_SIZE_MB": 8192,
"ALPHA_BIAS": 1.0,
"ANGLE_SNAP_RADIUS_DEGREES": 10.0,
"RENDER_TRIANGLE_THRESHOLD": 1000000,
"MAXIMUM_IMAGE_MEM_ALLOC": 2048,
"CAMERA_FOV_DEGREES": 30.0,
"CAMERA_DEFAULT_DISTANCE_UNITS": 18.0,
"CAMERA_DEFAULT_AZIMUTH_RADIANS": -1.0,
"CAMERA_DEFAULT_ELEVATION_RADIANS": 1.3,
"CAMERA_SENSITIVITY_ROTATION": 0.005,
"CAMERA_SENSITIVITY_ZOOM": 0.005
}

View File

@ -31,6 +31,8 @@ export class AppContext {
Logger.Get.enableLOGMAJOR();
Logger.Get.enableLOGWARN();
AppConfig.Get.dumpConfig();
const gl = (<HTMLCanvasElement>document.getElementById('canvas')).getContext('webgl');
if (!gl) {
throw Error('Could not load WebGL context');
@ -181,11 +183,11 @@ export class AppContext {
ASSERT(payload.action === 'Import');
const outputElement = this._ui.getActionOutput(EAction.Import);
if (payload.result.triangleCount < AppConfig.RENDER_TRIANGLE_THRESHOLD) {
if (payload.result.triangleCount < AppConfig.Get.RENDER_TRIANGLE_THRESHOLD) {
outputElement.setTaskInProgress('render', '[Renderer]: Processing...');
this._workerController.addJob(this._renderMesh());
} else {
const message = `Will not render mesh as its over ${AppConfig.RENDER_TRIANGLE_THRESHOLD.toLocaleString()} triangles.`;
const message = `Will not render mesh as its over ${AppConfig.Get.RENDER_TRIANGLE_THRESHOLD.toLocaleString()} triangles.`;
outputElement.setTaskComplete('render', '[Renderer]: Stopped', [message], 'warning');
}
};

View File

@ -6,7 +6,6 @@ import { Mesh, SolidMaterial, TexturedMaterial } from './mesh';
import { OcclusionManager } from './occlusion';
import { ProgressManager } from './progress';
import { AttributeData } from './render_buffer';
import { ASSERT } from './util/error_util';
import { Vector3 } from './vector';
import { VoxelMesh } from './voxel_mesh';
import { RenderNextVoxelMeshChunkParams } from './worker_types';

View File

@ -10,21 +10,16 @@ export class ArcballCamera {
public isUserRotating = false;
public isUserTranslating = false;
private readonly fov: number;
private readonly zNear: number;
private readonly zFar: number;
public aspect: number;
private _isPerspective: boolean;
private _fov: number;
private _zNear: number;
private _zFar: number;
private _aspect: number;
private _isPerspective: boolean = true;
private readonly _defaultDistance = 18.0;
private readonly _defaultAzimuth = -1.0;
private readonly _defaultElevation = 1.3;
private _distance = new SmoothVariable(this._defaultDistance, 0.025);
private _azimuth = new SmoothVariable(this._defaultAzimuth, 0.025);
private _elevation = new SmoothVariable(this._defaultElevation, 0.025);
private _target = new SmoothVectorVariable(new Vector3(0, 0, 0), 0.025);
private _distance: SmoothVariable;// = new SmoothVariable(this._defaultDistance, 0.025);
private _azimuth: SmoothVariable;// = new SmoothVariable(this._defaultAzimuth, 0.025);
private _elevation: SmoothVariable;// = new SmoothVariable(this._defaultElevation, 0.025);
private _target: SmoothVectorVariable;// = new SmoothVectorVariable(new Vector3(0, 0, 0), 0.025);
private readonly up: v3.Vec3 = [0, 1, 0];
private eye: v3.Vec3 = [0, 0, 0];
@ -36,7 +31,7 @@ export class ArcballCamera {
private mouseSensitivity = 0.005;
private scrollSensitivity = 0.005;
private gl: WebGLRenderingContext;
private _gl: WebGLRenderingContext;
private static _instance: ArcballCamera;
public static get Get() {
@ -44,16 +39,22 @@ export class ArcballCamera {
}
private constructor() {
this.fov = 30 * degreesToRadians;
this.zNear = 0.5;
this.zFar = 100.0;
this.gl = Renderer.Get._gl;
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
this._gl = Renderer.Get._gl;
this._isPerspective = true;
this._fov = AppConfig.Get.CAMERA_FOV_DEGREES * degreesToRadians;
this._zNear = 0.5;
this._zFar = 100.0;
this._aspect = this._gl.canvas.width / this._gl.canvas.height;
this._distance = new SmoothVariable(AppConfig.Get.CAMERA_DEFAULT_DISTANCE_UNITS, 0.025);
this._azimuth = new SmoothVariable(AppConfig.Get.CAMERA_DEFAULT_AZIMUTH_RADIANS, 0.025);
this._elevation = new SmoothVariable(AppConfig.Get.CAMERA_DEFAULT_ELEVATION_RADIANS, 0.025);
this._target = new SmoothVectorVariable(new Vector3(0, 0, 0), 0.025);
this._elevation.setClamp(0.001, Math.PI - 0.001);
this._distance.setClamp(1.0, 100.0);
this.setCameraMode('perspective');
this.setCameraMode(this._isPerspective ? 'perspective' : 'orthographic');
}
public isPerspective() {
@ -97,7 +98,7 @@ export class ArcballCamera {
}
public updateCamera() {
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
this._aspect = this._gl.canvas.width / this._gl.canvas.height;
const mouseDelta = MouseManager.Get.getMouseDelta();
mouseDelta.dx *= this.mouseSensitivity;
@ -124,7 +125,7 @@ export class ArcballCamera {
this._target.addToTarget(new Vector3(dx * mx, 0.0, dz * mx));
}
const axisSnapRadius = clamp(AppConfig.ANGLE_SNAP_RADIUS_DEGREES, 0.0, 90.0) * degreesToRadians;
const axisSnapRadius = clamp(AppConfig.Get.ANGLE_SNAP_RADIUS_DEGREES, 0.0, 90.0) * degreesToRadians;
if (this._shouldSnapCameraAngle()) {
let shouldSnapToAzimuth = false;
@ -254,10 +255,10 @@ export class ArcballCamera {
public getProjectionMatrix() {
if (this._isPerspective) {
return m4.perspective(this.fov, this.aspect, this.zNear, this.zFar);
return m4.perspective(this._fov, this._aspect, this._zNear, this._zFar);
} else {
const zoom = this._distance.getActual() / 3.6;
return m4.ortho(-zoom * this.aspect, zoom * this.aspect, -zoom, zoom, -1000, 1000);
return m4.ortho(-zoom * this._aspect, zoom * this._aspect, -zoom, zoom, -1000, 1000);
}
}
@ -299,18 +300,26 @@ export class ArcballCamera {
public reset() {
this._target.setTarget(new Vector3(0, 0, 0));
this._distance.setTarget(this._defaultDistance);
this._azimuth.setTarget(this._defaultAzimuth);
this._elevation.setTarget(this._defaultElevation);
this._distance.setTarget(AppConfig.Get.CAMERA_DEFAULT_DISTANCE_UNITS);
this._azimuth.setTarget(AppConfig.Get.CAMERA_DEFAULT_AZIMUTH_RADIANS);
this._elevation.setTarget(AppConfig.Get.CAMERA_DEFAULT_ELEVATION_RADIANS);
while (this._azimuth.getActual() < this._defaultAzimuth - Math.PI) {
while (this._azimuth.getActual() < AppConfig.Get.CAMERA_DEFAULT_AZIMUTH_RADIANS - Math.PI) {
this._azimuth.setActual(this._azimuth.getActual() + Math.PI * 2);
}
while (this._azimuth.getActual() > this._defaultAzimuth + Math.PI) {
while (this._azimuth.getActual() > AppConfig.Get.CAMERA_DEFAULT_ELEVATION_RADIANS + Math.PI) {
this._azimuth.setActual(this._azimuth.getActual() - Math.PI * 2);
}
}
public getAspect() {
return this._aspect;
}
public setAspect(aspect: number) {
this._aspect = aspect;
}
/*
public getMouseRay() {
const mousePos = this.mouseManager.getMousePosNorm();

View File

@ -40,7 +40,7 @@ export namespace RGBAUtil {
squaredDistance += (a.r - b.r) * (a.r - b.r);
squaredDistance += (a.g - b.g) * (a.g - b.g);
squaredDistance += (a.b - b.b) * (a.b - b.b);
squaredDistance += (a.a - b.a) * (a.a - b.a) * AppConfig.ALPHA_BIAS;
squaredDistance += (a.a - b.a) * (a.a - b.a) * AppConfig.Get.ALPHA_BIAS;
return squaredDistance;
}

View File

@ -1,45 +1,60 @@
// TODO: Replace with UI options
import fs from 'fs';
export namespace AppConfig {
/** Darkens corner even if corner block does not exist, recommended. */
export const AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
import { LOG } from './util/log_util';
import { AppPaths, PathUtil } from './util/path_util';
/** Enable logging to the console. */
export const LOGGING_ENABLED = true;
export class AppConfig {
/* Singleton */
private static _instance: AppConfig;
public static get Get() {
return this._instance || (this._instance = new this());
}
/** Enables runtime assertions, useful for debugging. */
export const ASSERTIONS_ENABLED = true;
public readonly RELEASE_MODE: boolean;
public readonly VOXEL_BUFFER_CHUNK_SIZE: number;
/** Optimises rendering by not rendering triangles facing away from camera's view direction. */
export const FACE_CULLING = false;
// Loaded from .json
public readonly AMBIENT_OCCLUSION_OVERRIDE_CORNER: boolean;
public readonly LOG_TO_FILE: boolean;
public readonly USE_WORKER_THREAD: boolean;
public readonly MULTISAMPLE_COUNT: number;
public readonly OLD_SPACE_SIZE_MB: number;
public readonly ALPHA_BIAS: number;
public readonly ANGLE_SNAP_RADIUS_DEGREES: number;
public readonly RENDER_TRIANGLE_THRESHOLD: number;
public readonly MAXIMUM_IMAGE_MEM_ALLOC: number;
public readonly CAMERA_FOV_DEGREES: number;
public readonly CAMERA_DEFAULT_DISTANCE_UNITS: number;
public readonly CAMERA_DEFAULT_AZIMUTH_RADIANS: number;
public readonly CAMERA_DEFAULT_ELEVATION_RADIANS: number;
public readonly CAMERA_SENSITIVITY_ROTATION: number;
public readonly CAMERA_SENSITIVITY_ZOOM: number;
/** Enables extra runtimes checks that slow execution. */
export const DEBUG_ENABLED = true;
private constructor() {
this.RELEASE_MODE = false;
this.VOXEL_BUFFER_CHUNK_SIZE = 5_000;
/** The number of samples used when sampling a voxel's colour from a textured material. */
export const MULTISAMPLE_COUNT = 16;
const configFile = fs.readFileSync(PathUtil.join(AppPaths.Get.resources, 'config.json'), 'utf8');
const configJSON = JSON.parse(configFile);
/** Max size of Node's old space in MBs. */
export const OLD_SPACE_SIZE = 8192;
this.AMBIENT_OCCLUSION_OVERRIDE_CORNER = configJSON.AMBIENT_OCCLUSION_OVERRIDE_CORNER;
this.LOG_TO_FILE = configJSON.LOG_TO_FILE;
this.USE_WORKER_THREAD = configJSON.USE_WORKER_THREAD;
this.MULTISAMPLE_COUNT = configJSON.MULTISAMPLE_COUNT;
this.OLD_SPACE_SIZE_MB = configJSON.OLD_SPACE_SIZE_MB;
this.ALPHA_BIAS = configJSON.ALPHA_BIAS;
this.ANGLE_SNAP_RADIUS_DEGREES = configJSON.ANGLE_SNAP_RADIUS_DEGREES;
this.RENDER_TRIANGLE_THRESHOLD = configJSON.RENDER_TRIANGLE_THRESHOLD;
this.MAXIMUM_IMAGE_MEM_ALLOC = configJSON.MAXIMUM_IMAGE_MEM_ALLOC;
this.CAMERA_FOV_DEGREES = configJSON.CAMERA_FOV_DEGREES;
this.CAMERA_DEFAULT_DISTANCE_UNITS = configJSON.CAMERA_DEFAULT_DISTANCE_UNITS;
this.CAMERA_DEFAULT_AZIMUTH_RADIANS = configJSON.CAMERA_DEFAULT_AZIMUTH_RADIANS;
this.CAMERA_DEFAULT_ELEVATION_RADIANS = configJSON.CAMERA_DEFAULT_ELEVATION_RADIANS;
this.CAMERA_SENSITIVITY_ROTATION = configJSON.CAMERA_SENSITIVITY_ROTATION;
this.CAMERA_SENSITIVITY_ZOOM = configJSON.CAMERA_SENSITIVITY_ZOOM;
}
/** This value determines how much more important it is to closely match a block's transparency value than its colour. */
export const ALPHA_BIAS = 1.0;
/** The angle radius (in degrees) around a snapping point the viewport camera must be within to snap. Must be between 0.0 and 90.0 */
export const ANGLE_SNAP_RADIUS_DEGREES = 10.0;
/** If the loaded mesh exceeds this number of triangles, the renderer will not attempt to draw it. */
export const RENDER_TRIANGLE_THRESHOLD = 1_000_000;
/** The maximum memory that should be allocated when attempting to decode JPEG images. 512MB is usually sufficient for 4k images, however this will need to be increased for 8k images */
export const MAXIMUM_IMAGE_MEM_ALLOC = 512;
/** Whether of not all logs should be saved to a file */
export const LOG_TO_FILE = true;
/** Whether or not to use a worker thread. You should probably never disable this unless debugging. */
export const USE_WORKER_THREAD = true;
/** The number of voxels that're packed into a single buffer */
export const VOXEL_BUFFER_CHUNK_SIZE = 5_000;
public dumpConfig() {
LOG(this);
}
}

View File

@ -2,13 +2,13 @@ import { NBT, TagType } from 'prismarine-nbt';
import { BlockMesh } from '../block_mesh';
import { AppConstants } from '../constants';
import { AppUtil } from '../util';
import { LOG } from '../util/log_util';
import { MathUtil } from '../util/math_util';
import { saveNBT } from '../util/nbt_util';
import { Vector3 } from '../vector';
import { IExporter } from './base_exporter';
const varintarray = require('varint-array');
export class SchemExporter extends IExporter {
public override getFormatFilter() {
return {
@ -33,27 +33,41 @@ export class SchemExporter extends IExporter {
// https://github.com/SpongePowered/Schematic-Specification/blob/master/versions/schematic-3.md#paletteObject
// const blockMapping: BlockMapping = {};
const test: {[name: string]: { type: TagType, value: any }} = {
const blockMapping: {[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 };
const namespacedBlockName = AppUtil.Text.namespaceBlock(blockName);
blockMapping[namespacedBlockName] = { type: TagType.Int, value: blockIndex };
++blockIndex;
}
LOG(test);
LOG(blockMapping);
// 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 namespacedBlockName = AppUtil.Text.namespaceBlock(block.blockInfo.name);
blockData[bufferIndex] = blockMapping[namespacedBlockName].value;
}
const blockEncoding: number[] = [];
for (let i = 0; i < blockData.length; ++i) {
let id = blockData[i];
while ((id & -128) != 0) {
blockEncoding.push(id & 127 | 128);
id >>>= 7;
}
blockEncoding.push(id);
}
for (let i = 0; i < blockEncoding.length; ++i) {
blockEncoding[i] = MathUtil.int8(blockEncoding[i]);
}
const nbt: NBT = {
@ -66,8 +80,8 @@ export class SchemExporter extends IExporter {
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) },
Palette: { type: TagType.Compound, value: blockMapping },
BlockData: { type: TagType.ByteArray, value: blockEncoding },
},
};

View File

@ -20,7 +20,7 @@ import url from 'url';
import { AppConfig } from './config';
import { AppPaths, PathUtil } from './util/path_util';
app.commandLine.appendSwitch('js-flags', `--max-old-space-size=${AppConfig.OLD_SPACE_SIZE}`);
app.commandLine.appendSwitch('js-flags', `--max-old-space-size=${AppConfig.Get.OLD_SPACE_SIZE_MB}`);
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@ -46,7 +46,7 @@ function createWindow() {
enableRemoteModule: true,
},
});
if (!AppConfig.DEBUG_ENABLED) {
if (AppConfig.Get.RELEASE_MODE) {
mainWindow.removeMenu();
}

View File

@ -51,7 +51,7 @@ export class OcclusionManager {
// If both edge blocks along this vertex exist,
// assume corner exists (even if it doesnt)
// (This is a stylistic choice)
if (numNeighbours == 2 && AppConfig.AMBIENT_OCCLUSION_OVERRIDE_CORNER) {
if (numNeighbours == 2 && AppConfig.Get.AMBIENT_OCCLUSION_OVERRIDE_CORNER) {
++numNeighbours;
} else {
const neighbourIndex = this._occlusionNeighboursIndices[f][v][2];

View File

@ -419,7 +419,7 @@ export class Renderer {
private _setupScene() {
twgl.resizeCanvasToDisplaySize(<HTMLCanvasElement>this._gl.canvas);
this._gl.viewport(0, 0, this._gl.canvas.width, this._gl.canvas.height);
ArcballCamera.Get.aspect = this._gl.canvas.width / this._gl.canvas.height;
ArcballCamera.Get.setAspect(this._gl.canvas.width / this._gl.canvas.height);
this._gl.blendFuncSeparate(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ONE, this._gl.ONE_MINUS_SRC_ALPHA);
this._gl.enable(this._gl.DEPTH_TEST);

View File

@ -54,7 +54,7 @@ export class Texture {
} else if (['.jpg', '.jpeg'].includes(filePath.ext.toLowerCase())) {
this._useAlphaChannelValue = false;
return jpeg.decode(data, {
maxMemoryUsageInMB: AppConfig.MAXIMUM_IMAGE_MEM_ALLOC,
maxMemoryUsageInMB: AppConfig.Get.MAXIMUM_IMAGE_MEM_ALLOC,
});
}
ASSERT(false);

View File

@ -1,5 +1,3 @@
import { AppConfig } from '../config';
export class AppError extends Error {
constructor(msg: string) {
super(msg);
@ -8,7 +6,7 @@ export class AppError extends Error {
}
export function ASSERT(condition: any, errorMessage = 'Assertion Failed'): asserts condition {
if (AppConfig.ASSERTIONS_ENABLED && !condition) {
if (!condition) {
Error(errorMessage);
throw Error(errorMessage);
}

View File

@ -198,7 +198,7 @@ export class Logger {
* Should be called before `initLogFile`
*/
public enableLogToFile() {
if (AppConfig.LOG_TO_FILE && this._enabledLogToFile === undefined) {
if (AppConfig.Get.LOG_TO_FILE && this._enabledLogToFile === undefined) {
this._enabledLogToFile = true;
}
}

18
src/util/math_util.ts Normal file
View File

@ -0,0 +1,18 @@
export namespace MathUtil {
export function uint8(x: number) {
return x & 0xFF;
}
export function int8(x: number) {
return uint8(x + 0x80) - 0x80;
}
export function uint32(x: number) {
return x >>> 0;
}
export function int32(x: number) {
return uint32(x + 0x80000000) - 0x80000000;
}
}

View File

@ -87,7 +87,7 @@ export class NormalCorrectedRayVoxeliser extends IVoxeliser {
let voxelColour: RGBA;
if (this._voxeliseParams!.useMultisampleColouring) {
const samples: RGBA[] = [];
for (let i = 0; i < AppConfig.MULTISAMPLE_COUNT; ++i) {
for (let i = 0; i < AppConfig.Get.MULTISAMPLE_COUNT; ++i) {
const samplePosition = Vector3.add(voxelPosition, Vector3.random().add(-0.5));
samples.push(this.__getVoxelColour(triangle, materialName, samplePosition));
}

View File

@ -71,7 +71,7 @@ export class RayVoxeliser extends IVoxeliser {
let voxelColour: RGBA;
if (this._voxeliseParams!.useMultisampleColouring) {
const samples: RGBA[] = [];
for (let i = 0; i < AppConfig.MULTISAMPLE_COUNT; ++i) {
for (let i = 0; i < AppConfig.Get.MULTISAMPLE_COUNT; ++i) {
const samplePosition = Vector3.add(voxelPosition, Vector3.random().add(-0.5));
samples.push(this.__getVoxelColour(triangle, materialName, samplePosition));
}

View File

@ -70,7 +70,7 @@ export class WorkerController {
}
private _tryStartNextJob() {
if (this.isBusy() && AppConfig.USE_WORKER_THREAD) {
if (this.isBusy() && AppConfig.Get.USE_WORKER_THREAD) {
return;
}
@ -81,7 +81,7 @@ export class WorkerController {
LOG('[WorkerController]: Starting Job', this._jobPending.id, `(${this._jobQueue.length} remaining)`);
TIME_START(this._jobPending.id);
if (AppConfig.USE_WORKER_THREAD) {
if (AppConfig.Get.USE_WORKER_THREAD) {
this._worker.postMessage(this._jobPending.payload);
} else {
const result = doWork(this._jobPending.payload);