mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2025-03-07 14:06:41 +08:00
Merge branch '0.6' into atlas-palette
This commit is contained in:
commit
25423b2d95
6
res/static/magnet.svg
Normal file
6
res/static/magnet.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-magnet" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#00abfb" fill="none" stroke-linecap="round" stroke-linejoin="round" id="magnet-svg">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M4 13v-8a2 2 0 0 1 2 -2h1a2 2 0 0 1 2 2v8a2 2 0 0 0 6 0v-8a2 2 0 0 1 2 -2h1a2 2 0 0 1 2 2v8a8 8 0 0 1 -16 0" />
|
||||||
|
<line x1="4" y1="8" x2="9" y2="8" />
|
||||||
|
<line x1="15" y1="8" x2="19" y2="8" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 502 B |
4
res/static/orthographic.svg
Normal file
4
res/static/orthographic.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-rectangle" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#00abfb" fill="none" stroke-linecap="round" stroke-linejoin="round" id="orthographic-svg">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<rect x="3" y="5" width="18" height="14" rx="2" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 361 B |
4
res/static/perspective.svg
Normal file
4
res/static/perspective.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-perspective" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#00abfb" fill="none" stroke-linecap="round" stroke-linejoin="round" id="perspective-svg">
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M6.141 4.163l12 1.714a1 1 0 0 1 .859 .99v10.266a1 1 0 0 1 -.859 .99l-12 1.714a1 1 0 0 1 -1.141 -.99v-13.694a1 1 0 0 1 1.141 -.99z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 454 B |
@ -17,6 +17,7 @@ import { TVoxelisers, VoxeliseParams, VoxeliserFactory } from './voxelisers/voxe
|
|||||||
import { ExporterFactory, TExporters } from './exporters/exporters';
|
import { ExporterFactory, TExporters } from './exporters/exporters';
|
||||||
import { Atlas } from './atlas';
|
import { Atlas } from './atlas';
|
||||||
import { Palette } from './palette';
|
import { Palette } from './palette';
|
||||||
|
import { ArcballCamera } from './camera';
|
||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export enum EAction {
|
export enum EAction {
|
||||||
@ -83,8 +84,9 @@ export class AppContext {
|
|||||||
|
|
||||||
this._ui.disable(EAction.Simplify);
|
this._ui.disable(EAction.Simplify);
|
||||||
|
|
||||||
Renderer.Get.toggleIsGridEnabled();
|
|
||||||
Renderer.Get.toggleIsAxesEnabled();
|
Renderer.Get.toggleIsAxesEnabled();
|
||||||
|
ArcballCamera.Get.setCameraMode('perspective');
|
||||||
|
ArcballCamera.Get.toggleAngleSnap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public do(action: EAction) {
|
public do(action: EAction) {
|
||||||
@ -234,6 +236,7 @@ export class AppContext {
|
|||||||
|
|
||||||
public draw() {
|
public draw() {
|
||||||
Renderer.Get.update();
|
Renderer.Get.update();
|
||||||
|
this._ui.tick();
|
||||||
Renderer.Get.draw();
|
Renderer.Get.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
160
src/camera.ts
160
src/camera.ts
@ -1,9 +1,10 @@
|
|||||||
import { m4, v3 } from 'twgl.js';
|
import { m4, v3 } from 'twgl.js';
|
||||||
import { MouseManager } from './mouse';
|
import { MouseManager } from './mouse';
|
||||||
import { degreesToRadians } from './math';
|
import { AppMath, between, clamp, degreesToRadians, roundToNearest } from './math';
|
||||||
import { Renderer } from './renderer';
|
import { Renderer } from './renderer';
|
||||||
import { SmoothVariable, SmoothVectorVariable } from './util';
|
import { LOG, SmoothVariable, SmoothVectorVariable } from './util';
|
||||||
import { Vector3 } from './vector';
|
import { Vector3 } from './vector';
|
||||||
|
import { AppConfig } from './config';
|
||||||
|
|
||||||
export class ArcballCamera {
|
export class ArcballCamera {
|
||||||
public isUserRotating = false;
|
public isUserRotating = false;
|
||||||
@ -13,6 +14,8 @@ export class ArcballCamera {
|
|||||||
private readonly zNear: number;
|
private readonly zNear: number;
|
||||||
private readonly zFar: number;
|
private readonly zFar: number;
|
||||||
public aspect: number;
|
public aspect: number;
|
||||||
|
|
||||||
|
private _isPerspective: boolean = true;
|
||||||
|
|
||||||
private readonly _defaultDistance = 18.0;
|
private readonly _defaultDistance = 18.0;
|
||||||
private readonly _defaultAzimuth = -1.0;
|
private readonly _defaultAzimuth = -1.0;
|
||||||
@ -26,6 +29,10 @@ export class ArcballCamera {
|
|||||||
private readonly up: v3.Vec3 = [0, 1, 0];
|
private readonly up: v3.Vec3 = [0, 1, 0];
|
||||||
private eye: v3.Vec3 = [0, 0, 0];
|
private eye: v3.Vec3 = [0, 0, 0];
|
||||||
|
|
||||||
|
private _azimuthRelief = 0.0;
|
||||||
|
private _elevationRelief = 0.0;
|
||||||
|
private _isAngleSnapped = false;
|
||||||
|
|
||||||
private mouseSensitivity = 0.005;
|
private mouseSensitivity = 0.005;
|
||||||
private scrollSensitivity = 0.005;
|
private scrollSensitivity = 0.005;
|
||||||
|
|
||||||
@ -43,21 +50,66 @@ export class ArcballCamera {
|
|||||||
this.gl = Renderer.Get._gl;
|
this.gl = Renderer.Get._gl;
|
||||||
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
|
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
|
||||||
|
|
||||||
this._elevation.setClamp(0.01, Math.PI - 0.01);
|
this._elevation.setClamp(0.001, Math.PI - 0.001);
|
||||||
this._distance.setClamp(1.0, 100.0);
|
this._distance.setClamp(1.0, 100.0);
|
||||||
|
|
||||||
|
this.setCameraMode('perspective');
|
||||||
|
}
|
||||||
|
|
||||||
|
public isPerspective() {
|
||||||
|
return this._isPerspective;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isOrthographic() {
|
||||||
|
return !this._isPerspective;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAlignedWithAxis(axis: 'x' | 'y' | 'z'): boolean {
|
||||||
|
const azimuth = Math.abs(this._azimuth.getTarget() % (Math.PI * 2));
|
||||||
|
const elevation = this._elevation.getTarget();
|
||||||
|
|
||||||
|
switch (axis) {
|
||||||
|
case 'x':
|
||||||
|
return AppMath.nearlyEqual(azimuth, AppMath.RADIANS_0) || AppMath.nearlyEqual(azimuth, AppMath.RADIANS_180);
|
||||||
|
case 'y':
|
||||||
|
return AppMath.nearlyEqual(elevation, AppMath.RADIANS_0, 0.002) || AppMath.nearlyEqual(elevation, AppMath.RADIANS_180, 0.002);
|
||||||
|
case 'z':
|
||||||
|
return AppMath.nearlyEqual(azimuth, AppMath.RADIANS_90) || AppMath.nearlyEqual(azimuth, AppMath.RADIANS_270);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setCameraMode(mode: 'perspective' | 'orthographic') {
|
||||||
|
this._isPerspective = mode === 'perspective';
|
||||||
|
}
|
||||||
|
|
||||||
|
private _angleSnap = false;
|
||||||
|
public toggleAngleSnap() {
|
||||||
|
this._angleSnap = !this._angleSnap;
|
||||||
|
|
||||||
|
if (!this._angleSnap) {
|
||||||
|
this._isAngleSnapped = false;
|
||||||
|
this._azimuthRelief = 0.0;
|
||||||
|
this._elevationRelief = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public isAngleSnapEnabled() {
|
||||||
|
return this._angleSnap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateCamera() {
|
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();
|
const mouseDelta = MouseManager.Get.getMouseDelta();
|
||||||
|
mouseDelta.dx *= this.mouseSensitivity;
|
||||||
|
mouseDelta.dy *= this.mouseSensitivity;
|
||||||
|
|
||||||
if (this.isUserRotating) {
|
if (this.isUserRotating) {
|
||||||
this._azimuth.addToTarget(mouseDelta.dx * this.mouseSensitivity);
|
this._azimuth.addToTarget(mouseDelta.dx);
|
||||||
this._elevation.addToTarget(mouseDelta.dy * this.mouseSensitivity);
|
this._elevation.addToTarget(mouseDelta.dy);
|
||||||
}
|
}
|
||||||
if (this.isUserTranslating) {
|
if (this.isUserTranslating) {
|
||||||
const my = mouseDelta.dy * this.mouseSensitivity;
|
const my = mouseDelta.dy;
|
||||||
const mx = mouseDelta.dx * this.mouseSensitivity;
|
const mx = mouseDelta.dx;
|
||||||
// Up-down
|
// Up-down
|
||||||
const dy = -Math.cos(this._elevation.getTarget() - Math.PI/2);
|
const dy = -Math.cos(this._elevation.getTarget() - Math.PI/2);
|
||||||
const df = Math.sin(this._elevation.getTarget() - Math.PI/2);
|
const df = Math.sin(this._elevation.getTarget() - Math.PI/2);
|
||||||
@ -72,6 +124,89 @@ export class ArcballCamera {
|
|||||||
this._target.addToTarget(new Vector3(dx * mx, 0.0, dz * mx));
|
this._target.addToTarget(new Vector3(dx * mx, 0.0, dz * mx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const axisSnapRadius = clamp(AppConfig.ANGLE_SNAP_RADIUS_DEGREES, 0.0, 90.0) * degreesToRadians;
|
||||||
|
|
||||||
|
if (this._shouldSnapCameraAngle()) {
|
||||||
|
let shouldSnapToAzimuth = false;
|
||||||
|
let shouldSnapToElevation = false;
|
||||||
|
let snapAngleAzimuth = 0.0;
|
||||||
|
let snapAngleElevation = 0.0;
|
||||||
|
|
||||||
|
const azimuth = this._azimuth.getTarget();
|
||||||
|
const elevation = this._elevation.getTarget();
|
||||||
|
|
||||||
|
const modAzimuth = Math.abs(azimuth % (90 * degreesToRadians));
|
||||||
|
|
||||||
|
if (modAzimuth < axisSnapRadius || modAzimuth > (90*degreesToRadians - axisSnapRadius)) {
|
||||||
|
shouldSnapToAzimuth = true;
|
||||||
|
snapAngleAzimuth = roundToNearest(azimuth, 90 * degreesToRadians);
|
||||||
|
}
|
||||||
|
|
||||||
|
const elevationSnapPoints = [0, 90, 180].map((x) => x * degreesToRadians);
|
||||||
|
for (const elevationSnapPoint of elevationSnapPoints) {
|
||||||
|
if (elevationSnapPoint - axisSnapRadius <= elevation && elevation <= elevationSnapPoint + axisSnapRadius) {
|
||||||
|
shouldSnapToElevation = true;
|
||||||
|
snapAngleElevation = elevationSnapPoint;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSnapToAzimuth && shouldSnapToElevation) {
|
||||||
|
this._azimuth.setTarget(snapAngleAzimuth);
|
||||||
|
this._elevation.setTarget(snapAngleElevation);
|
||||||
|
this._isAngleSnapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (this.isOrthographic()) {
|
||||||
|
const azimuth0 = between(this._azimuth.getTarget(), 0.0 - axisSnapRadius, 0.0 + axisSnapRadius);
|
||||||
|
const azimuth90 = between(this._azimuth.getTarget(), Math.PI/2 - axisSnapRadius, Math.PI/2 + axisSnapRadius);
|
||||||
|
const azimuth180 = between(this._azimuth.getTarget(), Math.PI - axisSnapRadius, Math.PI + axisSnapRadius);
|
||||||
|
const azimuth270 = between(this._azimuth.getTarget(), 3*Math.PI/2 - axisSnapRadius, 3*Math.PI/2 + axisSnapRadius);
|
||||||
|
|
||||||
|
const elevationTop = between(this._elevation.getTarget(), 0.0 - axisSnapRadius, 0.0 + axisSnapRadius);
|
||||||
|
const elevationMiddle = between(this._elevation.getTarget(), Math.PI/2 - axisSnapRadius, Math.PI/2 + axisSnapRadius);
|
||||||
|
const elevationBottom = between(this._elevation.getTarget(), Math.PI - axisSnapRadius, Math.PI + axisSnapRadius);
|
||||||
|
|
||||||
|
if (elevationMiddle) {
|
||||||
|
if (azimuth0) {
|
||||||
|
this._azimuth.setTarget(0);
|
||||||
|
this._elevation.setTarget(Math.PI/2);
|
||||||
|
this._isAngleSnapped = true;
|
||||||
|
} else if (azimuth90) {
|
||||||
|
this._azimuth.setTarget(90);
|
||||||
|
this._elevation.setTarget(Math.PI/2);
|
||||||
|
this._isAngleSnapped = true;
|
||||||
|
} else if (azimuth180) {
|
||||||
|
this._azimuth.setTarget(180);
|
||||||
|
this._elevation.setTarget(Math.PI/2);
|
||||||
|
this._isAngleSnapped = true;
|
||||||
|
} else if (azimuth270) {
|
||||||
|
this._azimuth.setTarget(270);
|
||||||
|
this._elevation.setTarget(Math.PI/2);
|
||||||
|
this._isAngleSnapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (this._isAngleSnapped && this.isUserRotating) {
|
||||||
|
this._azimuthRelief += mouseDelta.dx;
|
||||||
|
this._elevationRelief += mouseDelta.dy;
|
||||||
|
|
||||||
|
if (!between(this._azimuthRelief, -axisSnapRadius, axisSnapRadius) || !between(this._elevationRelief, -axisSnapRadius, axisSnapRadius)) {
|
||||||
|
this._azimuth.setTarget(this._azimuth.getTarget() + this._azimuthRelief * 2);
|
||||||
|
this._elevation.setTarget(this._elevation.getTarget() + this._elevationRelief * 2);
|
||||||
|
this._isAngleSnapped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._isAngleSnapped) {
|
||||||
|
this._azimuthRelief = 0.0;
|
||||||
|
this._elevationRelief = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
// Move camera towards target location
|
// Move camera towards target location
|
||||||
this._distance.tick();
|
this._distance.tick();
|
||||||
this._azimuth.tick();
|
this._azimuth.tick();
|
||||||
@ -86,6 +221,10 @@ export class ArcballCamera {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _shouldSnapCameraAngle() {
|
||||||
|
return this.isOrthographic() && this._angleSnap;
|
||||||
|
}
|
||||||
|
|
||||||
getCameraPosition(azimuthOffset: number, elevationOffset: number) {
|
getCameraPosition(azimuthOffset: number, elevationOffset: number) {
|
||||||
const azimuth = this._azimuth.getActual() + azimuthOffset;
|
const azimuth = this._azimuth.getActual() + azimuthOffset;
|
||||||
const elevation = this._elevation.getActual() + elevationOffset;
|
const elevation = this._elevation.getActual() + elevationOffset;
|
||||||
@ -114,7 +253,12 @@ export class ArcballCamera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getProjectionMatrix() {
|
public getProjectionMatrix() {
|
||||||
return m4.perspective(this.fov, this.aspect, this.zNear, this.zFar);
|
if (this._isPerspective) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCameraMatrix() {
|
public getCameraMatrix() {
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
// TODO: Replace with UI options
|
// TODO: Replace with UI options
|
||||||
|
|
||||||
export namespace AppConfig {
|
export namespace AppConfig {
|
||||||
/** Darkens corner even if corner block does not exist, recommended */
|
/** Darkens corner even if corner block does not exist, recommended. */
|
||||||
export const AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
|
export const AMBIENT_OCCLUSION_OVERRIDE_CORNER = true;
|
||||||
|
|
||||||
/** Enable logging to the console */
|
/** Enable logging to the console. */
|
||||||
export const LOGGING_ENABLED = true;
|
export const LOGGING_ENABLED = true;
|
||||||
|
|
||||||
/** Enables runtime assertions, useful for debugging */
|
/** Enables runtime assertions, useful for debugging. */
|
||||||
export const ASSERTIONS_ENABLED = true;
|
export const ASSERTIONS_ENABLED = true;
|
||||||
|
|
||||||
/** Optimises rendering by not rendering triangles facing away from camera's view direction */
|
/** Optimises rendering by not rendering triangles facing away from camera's view direction. */
|
||||||
export const FACE_CULLING = false;
|
export const FACE_CULLING = false;
|
||||||
|
|
||||||
/** Enables extra runtimes checks that slow execution */
|
/** Enables extra runtimes checks that slow execution. */
|
||||||
export const DEBUG_ENABLED = true;
|
export const DEBUG_ENABLED = true;
|
||||||
|
|
||||||
/** The number of samples used when sampling a voxel's colour from a textured material */
|
/** The number of samples used when sampling a voxel's colour from a textured material. */
|
||||||
export const MULTISAMPLE_COUNT = 16;
|
export const MULTISAMPLE_COUNT = 16;
|
||||||
|
|
||||||
/** Max size of Node's old space in MBs */
|
/** Max size of Node's old space in MBs. */
|
||||||
export const OLD_SPACE_SIZE = 2048;
|
export const OLD_SPACE_SIZE = 2048;
|
||||||
|
|
||||||
/** This value determines how much more important it is to closely match a block's transparency value than it's colour */
|
/** 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;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,6 @@ import { ASSERT, LOG } from './util';
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export enum EAppEvent {
|
export enum EAppEvent {
|
||||||
onModelActiveChanged,
|
|
||||||
onModelAvailableChanged,
|
|
||||||
onGridEnabledChanged,
|
|
||||||
onAxesEnabledChanged,
|
|
||||||
onWireframeEnabledChanged,
|
|
||||||
onNormalsEnabledChanged,
|
|
||||||
onDevViewEnabledChanged,
|
|
||||||
}
|
}
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
|
123
src/geometry.ts
123
src/geometry.ts
@ -219,36 +219,89 @@ export class DebugGeometryTemplates {
|
|||||||
return MergeAttributeData(line, cone);
|
return MergeAttributeData(line, cone);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static grid(dimensions: Vector3, spacing?: number): RenderBuffer {
|
public static COLOUR_MINOR: RGBA = { r: 0.5, g: 0.5, b: 0.5, a: 0.3 };
|
||||||
|
public static COLOUR_MAJOR: RGBA = { r: 1.0, g: 1.0, b: 1.0, a: 0.3 };
|
||||||
|
|
||||||
|
public static gridX(dimensions: Vector3, spacing?: number): RenderBuffer {
|
||||||
|
const buffer = new RenderBuffer([
|
||||||
|
{ name: 'position', numComponents: 3 },
|
||||||
|
{ name: 'colour', numComponents: 4 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, -dimensions.y / 2, -dimensions.z / 2),
|
||||||
|
new Vector3(0, -dimensions.y / 2, dimensions.z / 2),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, dimensions.y / 2, -dimensions.z / 2),
|
||||||
|
new Vector3(0, dimensions.y / 2, dimensions.z / 2),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, -dimensions.y / 2, -dimensions.z / 2),
|
||||||
|
new Vector3(0, dimensions.y / 2, -dimensions.z / 2),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, -dimensions.y / 2, dimensions.z / 2),
|
||||||
|
new Vector3(0, dimensions.y / 2, dimensions.z / 2),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (spacing) {
|
||||||
|
ASSERT(spacing > 0.0);
|
||||||
|
for (let y = -dimensions.y / 2; y < dimensions.y / 2; y += spacing) {
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, y, -dimensions.z / 2),
|
||||||
|
new Vector3(0, y, dimensions.z / 2),
|
||||||
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let z = -dimensions.z / 2; z < dimensions.z / 2; z += spacing) {
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(0, -dimensions.y / 2, z),
|
||||||
|
new Vector3(0, dimensions.y / 2, z),
|
||||||
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static gridY(dimensions: Vector3, spacing?: number): RenderBuffer {
|
||||||
const buffer = new RenderBuffer([
|
const buffer = new RenderBuffer([
|
||||||
{ name: 'position', numComponents: 3 },
|
{ name: 'position', numComponents: 3 },
|
||||||
{ name: 'colour', numComponents: 4 },
|
{ name: 'colour', numComponents: 4 },
|
||||||
]);
|
]);
|
||||||
const COLOUR_MINOR: RGBA = { r: 0.5, g: 0.5, b: 0.5, a: 0.3 };
|
|
||||||
const COLOUR_MAJOR: RGBA = { r: 1.0, g: 1.0, b: 1.0, a: 0.3 };
|
|
||||||
|
|
||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(-dimensions.x / 2, 0, -dimensions.z / 2),
|
new Vector3(-dimensions.x / 2, 0, -dimensions.z / 2),
|
||||||
new Vector3(-dimensions.x / 2, 0, dimensions.z / 2),
|
new Vector3(-dimensions.x / 2, 0, dimensions.z / 2),
|
||||||
COLOUR_MAJOR,
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
));
|
));
|
||||||
|
|
||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(dimensions.x / 2, 0, -dimensions.z / 2),
|
new Vector3(dimensions.x / 2, 0, -dimensions.z / 2),
|
||||||
new Vector3(dimensions.x / 2, 0, dimensions.z / 2),
|
new Vector3(dimensions.x / 2, 0, dimensions.z / 2),
|
||||||
COLOUR_MAJOR,
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
));
|
));
|
||||||
|
|
||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(-dimensions.x / 2, 0, -dimensions.z / 2),
|
new Vector3(-dimensions.x / 2, 0, -dimensions.z / 2),
|
||||||
new Vector3(dimensions.x / 2, 0, -dimensions.z / 2),
|
new Vector3(dimensions.x / 2, 0, -dimensions.z / 2),
|
||||||
COLOUR_MAJOR,
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
));
|
));
|
||||||
|
|
||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(-dimensions.x / 2, 0, dimensions.z / 2),
|
new Vector3(-dimensions.x / 2, 0, dimensions.z / 2),
|
||||||
new Vector3(dimensions.x / 2, 0, dimensions.z / 2),
|
new Vector3(dimensions.x / 2, 0, dimensions.z / 2),
|
||||||
COLOUR_MAJOR,
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
));
|
));
|
||||||
|
|
||||||
if (spacing) {
|
if (spacing) {
|
||||||
@ -257,7 +310,7 @@ export class DebugGeometryTemplates {
|
|||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(x, 0, -dimensions.z / 2),
|
new Vector3(x, 0, -dimensions.z / 2),
|
||||||
new Vector3(x, 0, dimensions.z / 2),
|
new Vector3(x, 0, dimensions.z / 2),
|
||||||
COLOUR_MINOR,
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +318,59 @@ export class DebugGeometryTemplates {
|
|||||||
buffer.add(DebugGeometryTemplates.line(
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
new Vector3(-dimensions.x / 2, 0, z),
|
new Vector3(-dimensions.x / 2, 0, z),
|
||||||
new Vector3(dimensions.x / 2, 0, z),
|
new Vector3(dimensions.x / 2, 0, z),
|
||||||
COLOUR_MINOR,
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static gridZ(dimensions: Vector3, spacing?: number): RenderBuffer {
|
||||||
|
const buffer = new RenderBuffer([
|
||||||
|
{ name: 'position', numComponents: 3 },
|
||||||
|
{ name: 'colour', numComponents: 4 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(-dimensions.x / 2, -dimensions.y / 2, 0),
|
||||||
|
new Vector3(-dimensions.x / 2, dimensions.y / 2, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(dimensions.x / 2, -dimensions.y / 2, 0),
|
||||||
|
new Vector3(dimensions.x / 2, dimensions.y / 2, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(-dimensions.x / 2, -dimensions.y / 2, 0),
|
||||||
|
new Vector3(dimensions.x / 2, -dimensions.y / 2, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(-dimensions.x / 2, dimensions.y / 2, 0),
|
||||||
|
new Vector3(dimensions.x / 2, dimensions.y / 2, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MAJOR,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (spacing) {
|
||||||
|
ASSERT(spacing > 0.0);
|
||||||
|
for (let x = -dimensions.x / 2; x < dimensions.x / 2; x += spacing) {
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(x, -dimensions.y / 2, 0),
|
||||||
|
new Vector3(x, dimensions.y / 2, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let y = -dimensions.y / 2; y < dimensions.y / 2; y += spacing) {
|
||||||
|
buffer.add(DebugGeometryTemplates.line(
|
||||||
|
new Vector3(-dimensions.x / 2, y, 0),
|
||||||
|
new Vector3(dimensions.x / 2, y, 0),
|
||||||
|
DebugGeometryTemplates.COLOUR_MINOR,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
src/math.ts
18
src/math.ts
@ -1,6 +1,20 @@
|
|||||||
import { AppError, LOG_ERROR } from './util';
|
import { AppError, LOG_ERROR } from './util';
|
||||||
import { Vector3 } from './vector';
|
import { Vector3 } from './vector';
|
||||||
|
|
||||||
|
export namespace AppMath {
|
||||||
|
export const RADIANS_0 = degreesToRadians(0.0);
|
||||||
|
export const RADIANS_90 = degreesToRadians(90.0);
|
||||||
|
export const RADIANS_180 = degreesToRadians(180.0);
|
||||||
|
export const RADIANS_270 = degreesToRadians(270.0);
|
||||||
|
|
||||||
|
export function nearlyEqual(a: number, b: number, tolerance: number = 0.0001) {
|
||||||
|
return Math.abs(a - b) < tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function degreesToRadians(degrees: number) {
|
||||||
|
return degrees * (Math.PI / 180.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const argMax = (array: [number]) => {
|
export const argMax = (array: [number]) => {
|
||||||
return array.map((x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
|
return array.map((x, i) => [x, i]).reduce((r, a) => (a[0] > r[0] ? a : r))[1];
|
||||||
@ -34,6 +48,10 @@ export const roundToNearest = (value: number, base: number) => {
|
|||||||
return Math.round(value / base) * base;
|
return Math.round(value / base) * base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const between = (value: number, min: number, max: number) => {
|
||||||
|
return min <= value && value <= max;
|
||||||
|
};
|
||||||
|
|
||||||
export const mapRange = (value: number, fromMin: number, fromMax: number, toMin: number, toMax: number) => {
|
export const mapRange = (value: number, fromMin: number, fromMax: number, toMin: number, toMax: number) => {
|
||||||
return (value - fromMin)/(fromMax - fromMin) * (toMax - toMin) + toMin;
|
return (value - fromMin)/(fromMax - fromMin) * (toMax - toMin) + toMin;
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,6 @@ import { VoxelMesh } from './voxel_mesh';
|
|||||||
import { BlockMesh } from './block_mesh';
|
import { BlockMesh } from './block_mesh';
|
||||||
|
|
||||||
import * as twgl from 'twgl.js';
|
import * as twgl from 'twgl.js';
|
||||||
import { EAppEvent, EventManager } from './event';
|
|
||||||
import { RGBA, RGBAUtil } from './colour';
|
import { RGBA, RGBAUtil } from './colour';
|
||||||
import { Texture } from './texture';
|
import { Texture } from './texture';
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ export enum MeshType {
|
|||||||
|
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
enum EDebugBufferComponents {
|
enum EDebugBufferComponents {
|
||||||
Grid,
|
|
||||||
Wireframe,
|
Wireframe,
|
||||||
Normals,
|
Normals,
|
||||||
Bounds,
|
Bounds,
|
||||||
@ -59,6 +57,13 @@ export class Renderer {
|
|||||||
private _isGridComponentEnabled: { [bufferComponent: string]: boolean };
|
private _isGridComponentEnabled: { [bufferComponent: string]: boolean };
|
||||||
private _axesEnabled: boolean;
|
private _axesEnabled: boolean;
|
||||||
|
|
||||||
|
private _gridBuffers: {
|
||||||
|
x: { [meshType: string]: RenderBuffer};
|
||||||
|
y: { [meshType: string]: RenderBuffer};
|
||||||
|
z: { [meshType: string]: RenderBuffer};
|
||||||
|
};
|
||||||
|
private _gridEnabled: boolean;
|
||||||
|
|
||||||
private static _instance: Renderer;
|
private static _instance: Renderer;
|
||||||
public static get Get() {
|
public static get Get() {
|
||||||
return this._instance || (this._instance = new this());
|
return this._instance || (this._instance = new this());
|
||||||
@ -73,6 +78,9 @@ export class Renderer {
|
|||||||
this._modelsAvailable = 0;
|
this._modelsAvailable = 0;
|
||||||
this._materialBuffers = [];
|
this._materialBuffers = [];
|
||||||
|
|
||||||
|
this._gridBuffers = { x: {}, y: {}, z: {} };
|
||||||
|
this._gridEnabled = true;
|
||||||
|
|
||||||
this._debugBuffers = {};
|
this._debugBuffers = {};
|
||||||
this._debugBuffers[MeshType.None] = {};
|
this._debugBuffers[MeshType.None] = {};
|
||||||
this._debugBuffers[MeshType.TriangleMesh] = {};
|
this._debugBuffers[MeshType.TriangleMesh] = {};
|
||||||
@ -80,7 +88,6 @@ export class Renderer {
|
|||||||
this._debugBuffers[MeshType.BlockMesh] = {};
|
this._debugBuffers[MeshType.BlockMesh] = {};
|
||||||
|
|
||||||
this._isGridComponentEnabled = {};
|
this._isGridComponentEnabled = {};
|
||||||
this._isGridComponentEnabled[EDebugBufferComponents.Grid] = false;
|
|
||||||
this._axesEnabled = false;
|
this._axesEnabled = false;
|
||||||
|
|
||||||
this._axisBuffer = new RenderBuffer([
|
this._axisBuffer = new RenderBuffer([
|
||||||
@ -117,50 +124,44 @@ export class Renderer {
|
|||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public toggleIsGridEnabled() {
|
public toggleIsGridEnabled() {
|
||||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Grid];
|
this._gridEnabled = !this._gridEnabled;
|
||||||
this._isGridComponentEnabled[EDebugBufferComponents.Grid] = isEnabled;
|
}
|
||||||
EventManager.Get.broadcast(EAppEvent.onGridEnabledChanged, isEnabled);
|
|
||||||
|
public isGridEnabled() {
|
||||||
|
return this._gridEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAxesEnabled() {
|
||||||
|
return this._axesEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleIsAxesEnabled() {
|
public toggleIsAxesEnabled() {
|
||||||
this._axesEnabled = !this._axesEnabled;
|
this._axesEnabled = !this._axesEnabled;
|
||||||
EventManager.Get.broadcast(EAppEvent.onAxesEnabledChanged, this._axesEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleIsWireframeEnabled() {
|
public toggleIsWireframeEnabled() {
|
||||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Wireframe];
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Wireframe];
|
||||||
this._isGridComponentEnabled[EDebugBufferComponents.Wireframe] = isEnabled;
|
this._isGridComponentEnabled[EDebugBufferComponents.Wireframe] = isEnabled;
|
||||||
EventManager.Get.broadcast(EAppEvent.onWireframeEnabledChanged, isEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleIsNormalsEnabled() {
|
public toggleIsNormalsEnabled() {
|
||||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Normals];
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Normals];
|
||||||
this._isGridComponentEnabled[EDebugBufferComponents.Normals] = isEnabled;
|
this._isGridComponentEnabled[EDebugBufferComponents.Normals] = isEnabled;
|
||||||
EventManager.Get.broadcast(EAppEvent.onNormalsEnabledChanged, isEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleIsDevDebugEnabled() {
|
public toggleIsDevDebugEnabled() {
|
||||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Dev];
|
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Dev];
|
||||||
this._isGridComponentEnabled[EDebugBufferComponents.Dev] = isEnabled;
|
this._isGridComponentEnabled[EDebugBufferComponents.Dev] = isEnabled;
|
||||||
EventManager.Get.broadcast(EAppEvent.onDevViewEnabledChanged, isEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearMesh() {
|
public clearMesh() {
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.TriangleMesh, false);
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.VoxelMesh, false);
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, false);
|
|
||||||
|
|
||||||
this._materialBuffers = [];
|
this._materialBuffers = [];
|
||||||
|
|
||||||
this._modelsAvailable = 0;
|
this._modelsAvailable = 0;
|
||||||
this.setModelToUse(MeshType.None);
|
this.setModelToUse(MeshType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public useMesh(mesh: Mesh) {
|
public useMesh(mesh: Mesh) {
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.TriangleMesh, false);
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.VoxelMesh, false);
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, false);
|
|
||||||
|
|
||||||
LOG('Using mesh');
|
LOG('Using mesh');
|
||||||
this._materialBuffers = [];
|
this._materialBuffers = [];
|
||||||
|
|
||||||
@ -255,21 +256,16 @@ export class Renderer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const dimensions = mesh.getBounds().getDimensions();
|
const dimensions = mesh.getBounds().getDimensions();
|
||||||
this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Grid] = DebugGeometryTemplates.grid(dimensions);
|
|
||||||
// this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.meshWireframe(mesh, new RGB(0.18, 0.52, 0.89).toRGBA());
|
this._gridBuffers.x[MeshType.TriangleMesh] = DebugGeometryTemplates.gridX(dimensions);
|
||||||
// this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Normals] = DebugGeometryTemplates.meshNormals(mesh, new RGB(0.89, 0.52, 0.18).toRGBA());
|
this._gridBuffers.y[MeshType.TriangleMesh] = DebugGeometryTemplates.gridY(dimensions);
|
||||||
// delete this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Dev];
|
this._gridBuffers.z[MeshType.TriangleMesh] = DebugGeometryTemplates.gridZ(dimensions);
|
||||||
|
|
||||||
this._modelsAvailable = 1;
|
this._modelsAvailable = 1;
|
||||||
this.setModelToUse(MeshType.TriangleMesh);
|
this.setModelToUse(MeshType.TriangleMesh);
|
||||||
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.TriangleMesh, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public useVoxelMesh(voxelMesh: VoxelMesh, voxelSize: number, ambientOcclusionEnabled: boolean) {
|
public useVoxelMesh(voxelMesh: VoxelMesh, voxelSize: number, ambientOcclusionEnabled: boolean) {
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.VoxelMesh, false);
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, false);
|
|
||||||
|
|
||||||
LOG('Using voxel mesh');
|
LOG('Using voxel mesh');
|
||||||
LOG(voxelMesh);
|
LOG(voxelMesh);
|
||||||
|
|
||||||
@ -285,18 +281,15 @@ export class Renderer {
|
|||||||
);
|
);
|
||||||
dimensions.add(1);
|
dimensions.add(1);
|
||||||
|
|
||||||
this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Grid] = DebugGeometryTemplates.grid(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
this._gridBuffers.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||||
// this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.voxelMeshWireframe(voxelMesh, new RGB(0.18, 0.52, 0.89).toRGBA(), this._voxelSize);
|
this._gridBuffers.y[MeshType.VoxelMesh] = DebugGeometryTemplates.gridY(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||||
|
this._gridBuffers.z[MeshType.VoxelMesh] = DebugGeometryTemplates.gridZ(Vector3.mulScalar(dimensions, voxelSize), voxelSize);
|
||||||
|
|
||||||
this._modelsAvailable = 2;
|
this._modelsAvailable = 2;
|
||||||
this.setModelToUse(MeshType.VoxelMesh);
|
this.setModelToUse(MeshType.VoxelMesh);
|
||||||
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.VoxelMesh, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public useBlockMesh(blockMesh: BlockMesh) {
|
public useBlockMesh(blockMesh: BlockMesh) {
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, false);
|
|
||||||
|
|
||||||
LOG('Using block mesh');
|
LOG('Using block mesh');
|
||||||
LOG(blockMesh);
|
LOG(blockMesh);
|
||||||
this._blockBuffer = twgl.createBufferInfoFromArrays(this._gl, blockMesh.createBuffer());
|
this._blockBuffer = twgl.createBufferInfoFromArrays(this._gl, blockMesh.createBuffer());
|
||||||
@ -308,18 +301,17 @@ export class Renderer {
|
|||||||
|
|
||||||
this._atlasSize = blockMesh.getAtlas().getAtlasSize();
|
this._atlasSize = blockMesh.getAtlas().getAtlasSize();
|
||||||
|
|
||||||
this._debugBuffers[MeshType.BlockMesh][EDebugBufferComponents.Grid] = this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Grid];
|
this._gridBuffers.y[MeshType.BlockMesh] = this._gridBuffers.y[MeshType.VoxelMesh];
|
||||||
|
|
||||||
this._modelsAvailable = 3;
|
this._modelsAvailable = 3;
|
||||||
this.setModelToUse(MeshType.BlockMesh);
|
this.setModelToUse(MeshType.BlockMesh);
|
||||||
|
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private _drawDebug() {
|
private _drawDebug() {
|
||||||
const debugComponents = [EDebugBufferComponents.Grid];
|
/*
|
||||||
|
const debugComponents = [EDebugBufferComponents.GridY];
|
||||||
for (const debugComp of debugComponents) {
|
for (const debugComp of debugComponents) {
|
||||||
if (this._isGridComponentEnabled[debugComp]) {
|
if (this._isGridComponentEnabled[debugComp]) {
|
||||||
ASSERT(this._debugBuffers[this._meshToUse]);
|
ASSERT(this._debugBuffers[this._meshToUse]);
|
||||||
@ -328,6 +320,9 @@ export class Renderer {
|
|||||||
if (debugComp === EDebugBufferComponents.Dev) {
|
if (debugComp === EDebugBufferComponents.Dev) {
|
||||||
this._gl.disable(this._gl.DEPTH_TEST);
|
this._gl.disable(this._gl.DEPTH_TEST);
|
||||||
}
|
}
|
||||||
|
if (debugComp === EDebugBufferComponents.GridY && !ArcballCamera.Get.isAlignedWithAxis('y')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
this._drawBuffer(this._gl.LINES, buffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
this._drawBuffer(this._gl.LINES, buffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
||||||
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||||
});
|
});
|
||||||
@ -335,6 +330,33 @@ export class Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
// Draw grid
|
||||||
|
if (this._gridEnabled) {
|
||||||
|
if (ArcballCamera.Get.isAlignedWithAxis('x') && !ArcballCamera.Get.isAlignedWithAxis('y') && !ArcballCamera.Get.isUserRotating) {
|
||||||
|
const gridBuffer = this._gridBuffers.x[this._meshToUse];
|
||||||
|
if (gridBuffer !== undefined) {
|
||||||
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
||||||
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (ArcballCamera.Get.isAlignedWithAxis('z') && !ArcballCamera.Get.isAlignedWithAxis('y') && !ArcballCamera.Get.isUserRotating) {
|
||||||
|
const gridBuffer = this._gridBuffers.z[this._meshToUse];
|
||||||
|
if (gridBuffer !== undefined) {
|
||||||
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
||||||
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const gridBuffer = this._gridBuffers.y[this._meshToUse];
|
||||||
|
if (gridBuffer !== undefined) {
|
||||||
|
this._drawBuffer(this._gl.LINES, gridBuffer.getWebGLBuffer(), ShaderManager.Get.debugProgram, {
|
||||||
|
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw axis
|
// Draw axis
|
||||||
if (this._axesEnabled) {
|
if (this._axesEnabled) {
|
||||||
this._gl.disable(this._gl.DEPTH_TEST);
|
this._gl.disable(this._gl.DEPTH_TEST);
|
||||||
@ -413,7 +435,6 @@ export class Renderer {
|
|||||||
const isModelAvailable = this._modelsAvailable >= meshType;
|
const isModelAvailable = this._modelsAvailable >= meshType;
|
||||||
if (isModelAvailable) {
|
if (isModelAvailable) {
|
||||||
this._meshToUse = meshType;
|
this._meshToUse = meshType;
|
||||||
EventManager.Get.broadcast(EAppEvent.onModelActiveChanged, meshType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,12 @@ import { ASSERT, getRandomID, STATIC_DIR } from '../../util';
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { EAppEvent, EventManager } from '../../event';
|
|
||||||
|
export type TToolbarBooleanProperty = 'enabled' | 'active';
|
||||||
|
|
||||||
|
export type TToolbarItemParams = {
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class ToolbarItemElement {
|
export class ToolbarItemElement {
|
||||||
private _id: string;
|
private _id: string;
|
||||||
@ -10,36 +15,44 @@ export class ToolbarItemElement {
|
|||||||
private _iconPath: string;
|
private _iconPath: string;
|
||||||
private _isEnabled: boolean;
|
private _isEnabled: boolean;
|
||||||
private _isActive: boolean;
|
private _isActive: boolean;
|
||||||
private _onClick: () => void;
|
private _onClick?: () => void;
|
||||||
|
|
||||||
public constructor(iconName: string, onClick: () => void,
|
|
||||||
_activeChangedEvent?: EAppEvent, _activeChangedDelegate?: (...args: any[]) => boolean,
|
|
||||||
_enableChangedEvent?: EAppEvent, _enableChangedDelegate?: (...args: any[]) => boolean,
|
|
||||||
) {
|
|
||||||
this._id = getRandomID();
|
|
||||||
this._iconName = iconName;
|
|
||||||
this._iconPath = path.join(STATIC_DIR, iconName + '.svg');
|
|
||||||
this._isEnabled = false;
|
|
||||||
this._isActive = false;
|
|
||||||
this._onClick = onClick;
|
|
||||||
|
|
||||||
// Enabled/Disabled Event
|
public constructor(params: TToolbarItemParams) {
|
||||||
if (_enableChangedEvent !== undefined && _enableChangedDelegate) {
|
this._id = getRandomID();
|
||||||
EventManager.Get.add(_enableChangedEvent, (...args: any[]) => {
|
|
||||||
const isEnabled = _enableChangedDelegate(args);
|
|
||||||
this.setEnabled(isEnabled);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._isEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Active/Inactive Event
|
this._iconName = params.icon;
|
||||||
if (_activeChangedEvent !== undefined && _activeChangedDelegate) {
|
this._iconPath = path.join(STATIC_DIR, params.icon + '.svg');
|
||||||
EventManager.Get.add(_activeChangedEvent, (...args: any[]) => {
|
|
||||||
const isActive = _activeChangedDelegate(args);
|
this._isEnabled = true;
|
||||||
this.setActive(isActive);
|
this._isActive = false;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
public tick() {
|
||||||
|
if (this._isEnabledDelegate !== undefined) {
|
||||||
|
this.setEnabled(this._isEnabledDelegate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._isActiveDelegate !== undefined) {
|
||||||
|
this.setActive(this._isActiveDelegate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isActiveDelegate?: () => boolean;
|
||||||
|
public isActive(delegate: () => boolean) {
|
||||||
|
this._isActiveDelegate = delegate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isEnabledDelegate?: () => boolean;
|
||||||
|
public isEnabled(delegate: () => boolean) {
|
||||||
|
this._isEnabledDelegate = delegate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onClick(delegate: () => void) {
|
||||||
|
this._onClick = delegate;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateHTML() {
|
public generateHTML() {
|
||||||
@ -56,7 +69,7 @@ export class ToolbarItemElement {
|
|||||||
ASSERT(element !== null);
|
ASSERT(element !== null);
|
||||||
|
|
||||||
element.addEventListener('click', () => {
|
element.addEventListener('click', () => {
|
||||||
if (this._isEnabled) {
|
if (this._isEnabled && this._onClick) {
|
||||||
this._onClick();
|
this._onClick();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
207
src/ui/layout.ts
207
src/ui/layout.ts
@ -9,7 +9,6 @@ import { ASSERT, ATLASES_DIR, LOG } from '../util';
|
|||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { ToolbarItemElement } from './elements/toolbar_item';
|
import { ToolbarItemElement } from './elements/toolbar_item';
|
||||||
import { EAppEvent } from '../event';
|
|
||||||
import { MeshType, Renderer } from '../renderer';
|
import { MeshType, Renderer } from '../renderer';
|
||||||
import { ArcballCamera } from '../camera';
|
import { ArcballCamera } from '../camera';
|
||||||
import { TVoxelisers } from '../voxelisers/voxelisers';
|
import { TVoxelisers } from '../voxelisers/voxelisers';
|
||||||
@ -189,119 +188,117 @@ export class UI {
|
|||||||
groups: {
|
groups: {
|
||||||
'viewmode': {
|
'viewmode': {
|
||||||
elements: {
|
elements: {
|
||||||
'mesh': new ToolbarItemElement('mesh', () => {
|
'mesh': new ToolbarItemElement({ icon: 'mesh' })
|
||||||
Renderer.Get.setModelToUse(MeshType.TriangleMesh);
|
.onClick(() => {
|
||||||
},
|
Renderer.Get.setModelToUse(MeshType.TriangleMesh);
|
||||||
EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
})
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
.isActive(() => {
|
||||||
return modelUsed === MeshType.TriangleMesh;
|
return Renderer.Get.getActiveMeshType() === MeshType.TriangleMesh;
|
||||||
},
|
})
|
||||||
EAppEvent.onModelAvailableChanged, (...args: any[]) => {
|
.isEnabled(() => {
|
||||||
const modelType = args[0][0][0] as MeshType;
|
return Renderer.Get.getModelsAvailable() >= MeshType.TriangleMesh;
|
||||||
const isCached = args[0][0][1] as boolean;
|
}),
|
||||||
return modelType >= MeshType.TriangleMesh && isCached;
|
'voxelMesh': new ToolbarItemElement({ icon: 'voxel' })
|
||||||
}),
|
.onClick(() => {
|
||||||
|
Renderer.Get.setModelToUse(MeshType.VoxelMesh);
|
||||||
'voxelMesh': new ToolbarItemElement('voxel', () => {
|
})
|
||||||
Renderer.Get.setModelToUse(MeshType.VoxelMesh);
|
.isActive(() => {
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
return Renderer.Get.getActiveMeshType() === MeshType.VoxelMesh;
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
})
|
||||||
return modelUsed === MeshType.VoxelMesh;
|
.isEnabled(() => {
|
||||||
}, EAppEvent.onModelAvailableChanged, (...args: any[]) => {
|
return Renderer.Get.getModelsAvailable() >= MeshType.VoxelMesh;
|
||||||
const modelType = args[0][0][0] as MeshType;
|
}),
|
||||||
const isCached = args[0][0][1] as boolean;
|
'blockMesh': new ToolbarItemElement({ icon: 'block' })
|
||||||
return modelType >= MeshType.VoxelMesh && isCached;
|
.onClick(() => {
|
||||||
}),
|
Renderer.Get.setModelToUse(MeshType.BlockMesh);
|
||||||
|
})
|
||||||
'blockMesh': new ToolbarItemElement('block', () => {
|
.isActive(() => {
|
||||||
Renderer.Get.setModelToUse(MeshType.BlockMesh);
|
return Renderer.Get.getActiveMeshType() === MeshType.BlockMesh;
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
})
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
.isEnabled(() => {
|
||||||
return modelUsed === MeshType.BlockMesh;
|
return Renderer.Get.getModelsAvailable() >= MeshType.BlockMesh;
|
||||||
}, EAppEvent.onModelAvailableChanged, (...args: any[]) => {
|
}),
|
||||||
const modelType = args[0][0][0] as MeshType;
|
|
||||||
const isCached = args[0][0][1] as boolean;
|
|
||||||
return modelType >= MeshType.BlockMesh && isCached;
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
elementsOrder: ['mesh', 'voxelMesh', 'blockMesh'],
|
elementsOrder: ['mesh', 'voxelMesh', 'blockMesh'],
|
||||||
},
|
},
|
||||||
'zoom': {
|
|
||||||
elements: {
|
|
||||||
'zoomOut': new ToolbarItemElement('minus', () => {
|
|
||||||
ArcballCamera.Get.onZoomOut();
|
|
||||||
}),
|
|
||||||
'zoomIn': new ToolbarItemElement('plus', () => {
|
|
||||||
ArcballCamera.Get.onZoomIn();
|
|
||||||
}),
|
|
||||||
'centre': new ToolbarItemElement('centre', () => {
|
|
||||||
ArcballCamera.Get.reset();
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
elementsOrder: ['zoomOut', 'zoomIn', 'centre'],
|
|
||||||
},
|
|
||||||
'debug': {
|
'debug': {
|
||||||
elements: {
|
elements: {
|
||||||
'grid': new ToolbarItemElement('grid', () => {
|
'grid': new ToolbarItemElement({ icon: 'grid' })
|
||||||
Renderer.Get.toggleIsGridEnabled();
|
.onClick(() => {
|
||||||
}, EAppEvent.onGridEnabledChanged, (...args: any[]) => {
|
Renderer.Get.toggleIsGridEnabled();
|
||||||
const isEnabled = args[0][0][0] as boolean;
|
})
|
||||||
return isEnabled;
|
.isActive(() => {
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
return Renderer.Get.isGridEnabled();
|
||||||
return Renderer.Get.getActiveMeshType() !== MeshType.None;
|
})
|
||||||
}),
|
.isEnabled(() => {
|
||||||
'axes': new ToolbarItemElement('axes', () => {
|
return Renderer.Get.getActiveMeshType() !== MeshType.None;
|
||||||
Renderer.Get.toggleIsAxesEnabled();
|
}),
|
||||||
}, EAppEvent.onAxesEnabledChanged, (...args: any[]) => {
|
'axes': new ToolbarItemElement({ icon: 'axes' })
|
||||||
const isEnabled = args[0][0][0] as boolean;
|
.onClick(() => {
|
||||||
return isEnabled;
|
Renderer.Get.toggleIsAxesEnabled();
|
||||||
}),
|
})
|
||||||
|
.isActive(() => {
|
||||||
|
return Renderer.Get.isAxesEnabled();
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
elementsOrder: ['grid', 'axes'],
|
elementsOrder: ['grid', 'axes'],
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
groupsOrder: ['viewmode', 'zoom', 'debug'],
|
groupsOrder: ['viewmode', 'debug'],
|
||||||
};
|
};
|
||||||
|
|
||||||
private _toolbarRight = {
|
private _toolbarRight = {
|
||||||
groups: {
|
groups: {
|
||||||
'debug': {
|
'zoom': {
|
||||||
elements: {
|
elements: {
|
||||||
/*
|
'zoomOut': new ToolbarItemElement({ icon: 'minus' })
|
||||||
'wireframe': new ToolbarItemElement('wireframe', () => {
|
.onClick(() => {
|
||||||
Renderer.Get.toggleIsWireframeEnabled();
|
ArcballCamera.Get.onZoomOut();
|
||||||
}, EAppEvent.onWireframeEnabledChanged, (...args: any[]) => {
|
}),
|
||||||
const isEnabled = args[0][0][0] as boolean;
|
'zoomIn': new ToolbarItemElement({ icon: 'plus' })
|
||||||
return isEnabled;
|
.onClick(() => {
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
ArcballCamera.Get.onZoomIn();
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
}),
|
||||||
return modelUsed === MeshType.TriangleMesh || modelUsed === MeshType.VoxelMesh;
|
'reset': new ToolbarItemElement({ icon: 'centre' })
|
||||||
}),
|
.onClick(() => {
|
||||||
'normals': new ToolbarItemElement('normal', () => {
|
ArcballCamera.Get.reset();
|
||||||
Renderer.Get.toggleIsNormalsEnabled();
|
}),
|
||||||
}, EAppEvent.onNormalsEnabledChanged, (...args: any[]) => {
|
|
||||||
const isEnabled = args[0][0][0] as boolean;
|
|
||||||
return isEnabled;
|
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
|
||||||
return modelUsed === MeshType.TriangleMesh;
|
|
||||||
}),
|
|
||||||
'dev': new ToolbarItemElement('debug', () => {
|
|
||||||
Renderer.Get.toggleIsDevDebugEnabled();
|
|
||||||
}, EAppEvent.onDevViewEnabledChanged, (...args: any[]) => {
|
|
||||||
const isEnabled = args[0][0][0] as boolean;
|
|
||||||
return isEnabled;
|
|
||||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
|
||||||
const modelUsed = args[0][0][0] as MeshType;
|
|
||||||
const devBufferAvailable = Renderer.Get.getModelsAvailable() >= 2;
|
|
||||||
return modelUsed === MeshType.TriangleMesh && devBufferAvailable;
|
|
||||||
}),
|
|
||||||
*/
|
|
||||||
},
|
},
|
||||||
elementsOrder: [], // ['wireframe', 'normals', 'dev'],
|
elementsOrder: ['zoomOut', 'zoomIn', 'reset'],
|
||||||
|
},
|
||||||
|
'camera': {
|
||||||
|
elements: {
|
||||||
|
'perspective': new ToolbarItemElement({ icon: 'perspective' })
|
||||||
|
.onClick(() => {
|
||||||
|
ArcballCamera.Get.setCameraMode('perspective');
|
||||||
|
})
|
||||||
|
.isActive(() => {
|
||||||
|
return ArcballCamera.Get.isPerspective();
|
||||||
|
}),
|
||||||
|
'orthographic': new ToolbarItemElement({ icon: 'orthographic' })
|
||||||
|
.onClick(() => {
|
||||||
|
ArcballCamera.Get.setCameraMode('orthographic');
|
||||||
|
})
|
||||||
|
.isActive(() => {
|
||||||
|
return ArcballCamera.Get.isOrthographic();
|
||||||
|
}),
|
||||||
|
'angleSnap': new ToolbarItemElement({ icon: 'magnet' })
|
||||||
|
.onClick(() => {
|
||||||
|
ArcballCamera.Get.toggleAngleSnap();
|
||||||
|
})
|
||||||
|
.isActive(() => {
|
||||||
|
return ArcballCamera.Get.isAngleSnapEnabled();
|
||||||
|
})
|
||||||
|
.isEnabled(() => {
|
||||||
|
return ArcballCamera.Get.isOrthographic();
|
||||||
|
}),
|
||||||
|
|
||||||
|
},
|
||||||
|
elementsOrder: ['perspective', 'orthographic', 'angleSnap'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
groupsOrder: ['debug'],
|
groupsOrder: ['camera', 'zoom'],
|
||||||
};
|
};
|
||||||
|
|
||||||
private _uiDull: { [key: string]: Group } = this._ui;
|
private _uiDull: { [key: string]: Group } = this._ui;
|
||||||
@ -317,6 +314,22 @@ export class UI {
|
|||||||
this._ui.assign.elements.fallable.addDescription('Read tooltips for more info');
|
this._ui.assign.elements.fallable.addDescription('Read tooltips for more info');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public tick() {
|
||||||
|
for (const groupName in this._toolbarLeftDull) {
|
||||||
|
const toolbarGroup = this._toolbarLeftDull[groupName];
|
||||||
|
for (const toolbarItem of toolbarGroup.elementsOrder) {
|
||||||
|
toolbarGroup.elements[toolbarItem].tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const groupName in this._toolbarRightDull) {
|
||||||
|
const toolbarGroup = this._toolbarRightDull[groupName];
|
||||||
|
for (const toolbarItem of toolbarGroup.elementsOrder) {
|
||||||
|
toolbarGroup.elements[toolbarItem].tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public build() {
|
public build() {
|
||||||
const groupHTML: { [key: string]: string } = {};
|
const groupHTML: { [key: string]: string } = {};
|
||||||
for (const groupName in this._ui) {
|
for (const groupName in this._ui) {
|
||||||
|
@ -231,7 +231,7 @@ export class SmoothVariable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setTarget(target: number) {
|
public setTarget(target: number) {
|
||||||
this._target = target;
|
this._target = clamp(target, this._min, this._max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActual(actual: number) {
|
public setActual(actual: number) {
|
||||||
|
Loading…
Reference in New Issue
Block a user