forked from mirror/ObjToSchematic
commit
76b364385e
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 |
@ -15,6 +15,7 @@ import { OutputStyle } from './ui/elements/output';
|
||||
import { IExporter } from './exporters/base_exporter';
|
||||
import { TVoxelisers, VoxeliseParams, VoxeliserFactory } from './voxelisers/voxelisers';
|
||||
import { ExporterFactory, TExporters } from './exporters/exporters';
|
||||
import { ArcballCamera } from './camera';
|
||||
|
||||
/* eslint-disable */
|
||||
export enum EAction {
|
||||
@ -81,8 +82,9 @@ export class AppContext {
|
||||
|
||||
this._ui.disable(EAction.Simplify);
|
||||
|
||||
Renderer.Get.toggleIsGridEnabled();
|
||||
Renderer.Get.toggleIsAxesEnabled();
|
||||
ArcballCamera.Get.setCameraMode('perspective');
|
||||
ArcballCamera.Get.toggleAngleSnap();
|
||||
}
|
||||
|
||||
public do(action: EAction) {
|
||||
@ -223,6 +225,7 @@ export class AppContext {
|
||||
|
||||
public draw() {
|
||||
Renderer.Get.update();
|
||||
this._ui.tick();
|
||||
Renderer.Get.draw();
|
||||
}
|
||||
|
||||
|
160
src/camera.ts
160
src/camera.ts
@ -1,9 +1,10 @@
|
||||
import { m4, v3 } from 'twgl.js';
|
||||
import { MouseManager } from './mouse';
|
||||
import { degreesToRadians } from './math';
|
||||
import { AppMath, between, clamp, degreesToRadians, roundToNearest } from './math';
|
||||
import { Renderer } from './renderer';
|
||||
import { SmoothVariable, SmoothVectorVariable } from './util';
|
||||
import { LOG, SmoothVariable, SmoothVectorVariable } from './util';
|
||||
import { Vector3 } from './vector';
|
||||
import { AppConfig } from './config';
|
||||
|
||||
export class ArcballCamera {
|
||||
public isUserRotating = false;
|
||||
@ -13,6 +14,8 @@ export class ArcballCamera {
|
||||
private readonly zNear: number;
|
||||
private readonly zFar: number;
|
||||
public aspect: number;
|
||||
|
||||
private _isPerspective: boolean = true;
|
||||
|
||||
private readonly _defaultDistance = 18.0;
|
||||
private readonly _defaultAzimuth = -1.0;
|
||||
@ -26,6 +29,10 @@ export class ArcballCamera {
|
||||
private readonly up: v3.Vec3 = [0, 1, 0];
|
||||
private eye: v3.Vec3 = [0, 0, 0];
|
||||
|
||||
private _azimuthRelief = 0.0;
|
||||
private _elevationRelief = 0.0;
|
||||
private _isAngleSnapped = false;
|
||||
|
||||
private mouseSensitivity = 0.005;
|
||||
private scrollSensitivity = 0.005;
|
||||
|
||||
@ -43,21 +50,66 @@ export class ArcballCamera {
|
||||
this.gl = Renderer.Get._gl;
|
||||
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.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() {
|
||||
this.aspect = this.gl.canvas.width / this.gl.canvas.height;
|
||||
|
||||
const mouseDelta = MouseManager.Get.getMouseDelta();
|
||||
mouseDelta.dx *= this.mouseSensitivity;
|
||||
mouseDelta.dy *= this.mouseSensitivity;
|
||||
|
||||
if (this.isUserRotating) {
|
||||
this._azimuth.addToTarget(mouseDelta.dx * this.mouseSensitivity);
|
||||
this._elevation.addToTarget(mouseDelta.dy * this.mouseSensitivity);
|
||||
this._azimuth.addToTarget(mouseDelta.dx);
|
||||
this._elevation.addToTarget(mouseDelta.dy);
|
||||
}
|
||||
if (this.isUserTranslating) {
|
||||
const my = mouseDelta.dy * this.mouseSensitivity;
|
||||
const mx = mouseDelta.dx * this.mouseSensitivity;
|
||||
const my = mouseDelta.dy;
|
||||
const mx = mouseDelta.dx;
|
||||
// Up-down
|
||||
const dy = -Math.cos(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));
|
||||
}
|
||||
|
||||
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
|
||||
this._distance.tick();
|
||||
this._azimuth.tick();
|
||||
@ -86,6 +221,10 @@ export class ArcballCamera {
|
||||
];
|
||||
}
|
||||
|
||||
private _shouldSnapCameraAngle() {
|
||||
return this.isOrthographic() && this._angleSnap;
|
||||
}
|
||||
|
||||
getCameraPosition(azimuthOffset: number, elevationOffset: number) {
|
||||
const azimuth = this._azimuth.getActual() + azimuthOffset;
|
||||
const elevation = this._elevation.getActual() + elevationOffset;
|
||||
@ -114,7 +253,12 @@ export class ArcballCamera {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -1,27 +1,30 @@
|
||||
// TODO: Replace with UI options
|
||||
|
||||
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;
|
||||
|
||||
/** Enable logging to the console */
|
||||
/** Enable logging to the console. */
|
||||
export const LOGGING_ENABLED = true;
|
||||
|
||||
/** Enables runtime assertions, useful for debugging */
|
||||
/** Enables runtime assertions, useful for debugging. */
|
||||
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;
|
||||
|
||||
/** Enables extra runtimes checks that slow execution */
|
||||
/** Enables extra runtimes checks that slow execution. */
|
||||
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;
|
||||
|
||||
/** Max size of Node's old space in MBs */
|
||||
/** Max size of Node's old space in MBs. */
|
||||
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;
|
||||
|
||||
/** 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 */
|
||||
export enum EAppEvent {
|
||||
onModelActiveChanged,
|
||||
onModelAvailableChanged,
|
||||
onGridEnabledChanged,
|
||||
onAxesEnabledChanged,
|
||||
onWireframeEnabledChanged,
|
||||
onNormalsEnabledChanged,
|
||||
onDevViewEnabledChanged,
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
|
123
src/geometry.ts
123
src/geometry.ts
@ -219,36 +219,89 @@ export class DebugGeometryTemplates {
|
||||
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([
|
||||
{ name: 'position', numComponents: 3 },
|
||||
{ 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(
|
||||
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(
|
||||
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(
|
||||
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(
|
||||
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) {
|
||||
@ -257,7 +310,7 @@ export class DebugGeometryTemplates {
|
||||
buffer.add(DebugGeometryTemplates.line(
|
||||
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(
|
||||
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 { 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]) => {
|
||||
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;
|
||||
};
|
||||
|
||||
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) => {
|
||||
return (value - fromMin)/(fromMax - fromMin) * (toMax - toMin) + toMin;
|
||||
};
|
||||
|
@ -10,7 +10,6 @@ import { VoxelMesh } from './voxel_mesh';
|
||||
import { BlockMesh } from './block_mesh';
|
||||
|
||||
import * as twgl from 'twgl.js';
|
||||
import { EAppEvent, EventManager } from './event';
|
||||
import { RGBA, RGBAUtil } from './colour';
|
||||
import { Texture } from './texture';
|
||||
|
||||
@ -25,7 +24,6 @@ export enum MeshType {
|
||||
|
||||
/* eslint-disable */
|
||||
enum EDebugBufferComponents {
|
||||
Grid,
|
||||
Wireframe,
|
||||
Normals,
|
||||
Bounds,
|
||||
@ -59,6 +57,13 @@ export class Renderer {
|
||||
private _isGridComponentEnabled: { [bufferComponent: string]: 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;
|
||||
public static get Get() {
|
||||
return this._instance || (this._instance = new this());
|
||||
@ -73,6 +78,9 @@ export class Renderer {
|
||||
this._modelsAvailable = 0;
|
||||
this._materialBuffers = [];
|
||||
|
||||
this._gridBuffers = { x: {}, y: {}, z: {} };
|
||||
this._gridEnabled = true;
|
||||
|
||||
this._debugBuffers = {};
|
||||
this._debugBuffers[MeshType.None] = {};
|
||||
this._debugBuffers[MeshType.TriangleMesh] = {};
|
||||
@ -80,7 +88,6 @@ export class Renderer {
|
||||
this._debugBuffers[MeshType.BlockMesh] = {};
|
||||
|
||||
this._isGridComponentEnabled = {};
|
||||
this._isGridComponentEnabled[EDebugBufferComponents.Grid] = false;
|
||||
this._axesEnabled = false;
|
||||
|
||||
this._axisBuffer = new RenderBuffer([
|
||||
@ -117,50 +124,44 @@ export class Renderer {
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public toggleIsGridEnabled() {
|
||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Grid];
|
||||
this._isGridComponentEnabled[EDebugBufferComponents.Grid] = isEnabled;
|
||||
EventManager.Get.broadcast(EAppEvent.onGridEnabledChanged, isEnabled);
|
||||
this._gridEnabled = !this._gridEnabled;
|
||||
}
|
||||
|
||||
public isGridEnabled() {
|
||||
return this._gridEnabled;
|
||||
}
|
||||
|
||||
public isAxesEnabled() {
|
||||
return this._axesEnabled;
|
||||
}
|
||||
|
||||
public toggleIsAxesEnabled() {
|
||||
this._axesEnabled = !this._axesEnabled;
|
||||
EventManager.Get.broadcast(EAppEvent.onAxesEnabledChanged, this._axesEnabled);
|
||||
}
|
||||
|
||||
public toggleIsWireframeEnabled() {
|
||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Wireframe];
|
||||
this._isGridComponentEnabled[EDebugBufferComponents.Wireframe] = isEnabled;
|
||||
EventManager.Get.broadcast(EAppEvent.onWireframeEnabledChanged, isEnabled);
|
||||
}
|
||||
|
||||
public toggleIsNormalsEnabled() {
|
||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Normals];
|
||||
this._isGridComponentEnabled[EDebugBufferComponents.Normals] = isEnabled;
|
||||
EventManager.Get.broadcast(EAppEvent.onNormalsEnabledChanged, isEnabled);
|
||||
}
|
||||
|
||||
public toggleIsDevDebugEnabled() {
|
||||
const isEnabled = !this._isGridComponentEnabled[EDebugBufferComponents.Dev];
|
||||
this._isGridComponentEnabled[EDebugBufferComponents.Dev] = isEnabled;
|
||||
EventManager.Get.broadcast(EAppEvent.onDevViewEnabledChanged, isEnabled);
|
||||
}
|
||||
|
||||
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._modelsAvailable = 0;
|
||||
this.setModelToUse(MeshType.None);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
public useMesh(mesh: Mesh) {
|
||||
LOG('Using mesh');
|
||||
this._materialBuffers = [];
|
||||
|
||||
@ -255,21 +256,16 @@ export class Renderer {
|
||||
});
|
||||
|
||||
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._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Normals] = DebugGeometryTemplates.meshNormals(mesh, new RGB(0.89, 0.52, 0.18).toRGBA());
|
||||
// delete this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Dev];
|
||||
|
||||
this._gridBuffers.x[MeshType.TriangleMesh] = DebugGeometryTemplates.gridX(dimensions);
|
||||
this._gridBuffers.y[MeshType.TriangleMesh] = DebugGeometryTemplates.gridY(dimensions);
|
||||
this._gridBuffers.z[MeshType.TriangleMesh] = DebugGeometryTemplates.gridZ(dimensions);
|
||||
|
||||
this._modelsAvailable = 1;
|
||||
this.setModelToUse(MeshType.TriangleMesh);
|
||||
|
||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.TriangleMesh, true);
|
||||
}
|
||||
|
||||
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(voxelMesh);
|
||||
|
||||
@ -285,18 +281,15 @@ export class Renderer {
|
||||
);
|
||||
dimensions.add(1);
|
||||
|
||||
this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Grid] = DebugGeometryTemplates.grid(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.x[MeshType.VoxelMesh] = DebugGeometryTemplates.gridX(Vector3.mulScalar(dimensions, voxelSize), 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.setModelToUse(MeshType.VoxelMesh);
|
||||
|
||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.VoxelMesh, true);
|
||||
}
|
||||
|
||||
public useBlockMesh(blockMesh: BlockMesh) {
|
||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, false);
|
||||
|
||||
LOG('Using block mesh');
|
||||
LOG(blockMesh);
|
||||
this._blockBuffer = twgl.createBufferInfoFromArrays(this._gl, blockMesh.createBuffer());
|
||||
@ -306,18 +299,17 @@ export class Renderer {
|
||||
mag: this._gl.NEAREST,
|
||||
});
|
||||
|
||||
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.setModelToUse(MeshType.BlockMesh);
|
||||
|
||||
EventManager.Get.broadcast(EAppEvent.onModelAvailableChanged, MeshType.BlockMesh, true);
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private _drawDebug() {
|
||||
const debugComponents = [EDebugBufferComponents.Grid];
|
||||
/*
|
||||
const debugComponents = [EDebugBufferComponents.GridY];
|
||||
for (const debugComp of debugComponents) {
|
||||
if (this._isGridComponentEnabled[debugComp]) {
|
||||
ASSERT(this._debugBuffers[this._meshToUse]);
|
||||
@ -326,6 +318,9 @@ export class Renderer {
|
||||
if (debugComp === EDebugBufferComponents.Dev) {
|
||||
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, {
|
||||
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||
});
|
||||
@ -333,6 +328,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
|
||||
if (this._axesEnabled) {
|
||||
this._gl.disable(this._gl.DEPTH_TEST);
|
||||
@ -411,7 +433,6 @@ export class Renderer {
|
||||
const isModelAvailable = this._modelsAvailable >= meshType;
|
||||
if (isModelAvailable) {
|
||||
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 fs from 'fs';
|
||||
import { EAppEvent, EventManager } from '../../event';
|
||||
|
||||
export type TToolbarBooleanProperty = 'enabled' | 'active';
|
||||
|
||||
export type TToolbarItemParams = {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export class ToolbarItemElement {
|
||||
private _id: string;
|
||||
@ -10,36 +15,44 @@ export class ToolbarItemElement {
|
||||
private _iconPath: string;
|
||||
private _isEnabled: boolean;
|
||||
private _isActive: boolean;
|
||||
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;
|
||||
private _onClick?: () => void;
|
||||
|
||||
// Enabled/Disabled Event
|
||||
if (_enableChangedEvent !== undefined && _enableChangedDelegate) {
|
||||
EventManager.Get.add(_enableChangedEvent, (...args: any[]) => {
|
||||
const isEnabled = _enableChangedDelegate(args);
|
||||
this.setEnabled(isEnabled);
|
||||
});
|
||||
} else {
|
||||
this._isEnabled = true;
|
||||
}
|
||||
public constructor(params: TToolbarItemParams) {
|
||||
this._id = getRandomID();
|
||||
|
||||
// Active/Inactive Event
|
||||
if (_activeChangedEvent !== undefined && _activeChangedDelegate) {
|
||||
EventManager.Get.add(_activeChangedEvent, (...args: any[]) => {
|
||||
const isActive = _activeChangedDelegate(args);
|
||||
this.setActive(isActive);
|
||||
});
|
||||
this._iconName = params.icon;
|
||||
this._iconPath = path.join(STATIC_DIR, params.icon + '.svg');
|
||||
|
||||
this._isEnabled = true;
|
||||
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() {
|
||||
@ -56,7 +69,7 @@ export class ToolbarItemElement {
|
||||
ASSERT(element !== null);
|
||||
|
||||
element.addEventListener('click', () => {
|
||||
if (this._isEnabled) {
|
||||
if (this._isEnabled && this._onClick) {
|
||||
this._onClick();
|
||||
}
|
||||
});
|
||||
|
207
src/ui/layout.ts
207
src/ui/layout.ts
@ -9,7 +9,6 @@ import { ASSERT, ATLASES_DIR, LOG, PALETTES_DIR } from '../util';
|
||||
|
||||
import fs from 'fs';
|
||||
import { ToolbarItemElement } from './elements/toolbar_item';
|
||||
import { EAppEvent } from '../event';
|
||||
import { MeshType, Renderer } from '../renderer';
|
||||
import { ArcballCamera } from '../camera';
|
||||
import { TVoxelisers } from '../voxelisers/voxelisers';
|
||||
@ -192,119 +191,117 @@ export class UI {
|
||||
groups: {
|
||||
'viewmode': {
|
||||
elements: {
|
||||
'mesh': new ToolbarItemElement('mesh', () => {
|
||||
Renderer.Get.setModelToUse(MeshType.TriangleMesh);
|
||||
},
|
||||
EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
||||
const modelUsed = args[0][0][0] as MeshType;
|
||||
return modelUsed === MeshType.TriangleMesh;
|
||||
},
|
||||
EAppEvent.onModelAvailableChanged, (...args: any[]) => {
|
||||
const modelType = args[0][0][0] as MeshType;
|
||||
const isCached = args[0][0][1] as boolean;
|
||||
return modelType >= MeshType.TriangleMesh && isCached;
|
||||
}),
|
||||
|
||||
'voxelMesh': new ToolbarItemElement('voxel', () => {
|
||||
Renderer.Get.setModelToUse(MeshType.VoxelMesh);
|
||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
||||
const modelUsed = args[0][0][0] as MeshType;
|
||||
return modelUsed === MeshType.VoxelMesh;
|
||||
}, EAppEvent.onModelAvailableChanged, (...args: any[]) => {
|
||||
const modelType = args[0][0][0] as MeshType;
|
||||
const isCached = args[0][0][1] as boolean;
|
||||
return modelType >= MeshType.VoxelMesh && isCached;
|
||||
}),
|
||||
|
||||
'blockMesh': new ToolbarItemElement('block', () => {
|
||||
Renderer.Get.setModelToUse(MeshType.BlockMesh);
|
||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
||||
const modelUsed = args[0][0][0] as MeshType;
|
||||
return modelUsed === 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;
|
||||
}),
|
||||
'mesh': new ToolbarItemElement({ icon: 'mesh' })
|
||||
.onClick(() => {
|
||||
Renderer.Get.setModelToUse(MeshType.TriangleMesh);
|
||||
})
|
||||
.isActive(() => {
|
||||
return Renderer.Get.getActiveMeshType() === MeshType.TriangleMesh;
|
||||
})
|
||||
.isEnabled(() => {
|
||||
return Renderer.Get.getModelsAvailable() >= MeshType.TriangleMesh;
|
||||
}),
|
||||
'voxelMesh': new ToolbarItemElement({ icon: 'voxel' })
|
||||
.onClick(() => {
|
||||
Renderer.Get.setModelToUse(MeshType.VoxelMesh);
|
||||
})
|
||||
.isActive(() => {
|
||||
return Renderer.Get.getActiveMeshType() === MeshType.VoxelMesh;
|
||||
})
|
||||
.isEnabled(() => {
|
||||
return Renderer.Get.getModelsAvailable() >= MeshType.VoxelMesh;
|
||||
}),
|
||||
'blockMesh': new ToolbarItemElement({ icon: 'block' })
|
||||
.onClick(() => {
|
||||
Renderer.Get.setModelToUse(MeshType.BlockMesh);
|
||||
})
|
||||
.isActive(() => {
|
||||
return Renderer.Get.getActiveMeshType() === MeshType.BlockMesh;
|
||||
})
|
||||
.isEnabled(() => {
|
||||
return Renderer.Get.getModelsAvailable() >= MeshType.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': {
|
||||
elements: {
|
||||
'grid': new ToolbarItemElement('grid', () => {
|
||||
Renderer.Get.toggleIsGridEnabled();
|
||||
}, EAppEvent.onGridEnabledChanged, (...args: any[]) => {
|
||||
const isEnabled = args[0][0][0] as boolean;
|
||||
return isEnabled;
|
||||
}, EAppEvent.onModelActiveChanged, (...args: any[]) => {
|
||||
return Renderer.Get.getActiveMeshType() !== MeshType.None;
|
||||
}),
|
||||
'axes': new ToolbarItemElement('axes', () => {
|
||||
Renderer.Get.toggleIsAxesEnabled();
|
||||
}, EAppEvent.onAxesEnabledChanged, (...args: any[]) => {
|
||||
const isEnabled = args[0][0][0] as boolean;
|
||||
return isEnabled;
|
||||
}),
|
||||
'grid': new ToolbarItemElement({ icon: 'grid' })
|
||||
.onClick(() => {
|
||||
Renderer.Get.toggleIsGridEnabled();
|
||||
})
|
||||
.isActive(() => {
|
||||
return Renderer.Get.isGridEnabled();
|
||||
})
|
||||
.isEnabled(() => {
|
||||
return Renderer.Get.getActiveMeshType() !== MeshType.None;
|
||||
}),
|
||||
'axes': new ToolbarItemElement({ icon: 'axes' })
|
||||
.onClick(() => {
|
||||
Renderer.Get.toggleIsAxesEnabled();
|
||||
})
|
||||
.isActive(() => {
|
||||
return Renderer.Get.isAxesEnabled();
|
||||
}),
|
||||
},
|
||||
elementsOrder: ['grid', 'axes'],
|
||||
},
|
||||
|
||||
},
|
||||
groupsOrder: ['viewmode', 'zoom', 'debug'],
|
||||
groupsOrder: ['viewmode', 'debug'],
|
||||
};
|
||||
|
||||
private _toolbarRight = {
|
||||
groups: {
|
||||
'debug': {
|
||||
'zoom': {
|
||||
elements: {
|
||||
/*
|
||||
'wireframe': new ToolbarItemElement('wireframe', () => {
|
||||
Renderer.Get.toggleIsWireframeEnabled();
|
||||
}, EAppEvent.onWireframeEnabledChanged, (...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 || modelUsed === MeshType.VoxelMesh;
|
||||
}),
|
||||
'normals': new ToolbarItemElement('normal', () => {
|
||||
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;
|
||||
}),
|
||||
*/
|
||||
'zoomOut': new ToolbarItemElement({ icon: 'minus' })
|
||||
.onClick(() => {
|
||||
ArcballCamera.Get.onZoomOut();
|
||||
}),
|
||||
'zoomIn': new ToolbarItemElement({ icon: 'plus' })
|
||||
.onClick(() => {
|
||||
ArcballCamera.Get.onZoomIn();
|
||||
}),
|
||||
'reset': new ToolbarItemElement({ icon: 'centre' })
|
||||
.onClick(() => {
|
||||
ArcballCamera.Get.reset();
|
||||
}),
|
||||
},
|
||||
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;
|
||||
@ -320,6 +317,22 @@ export class UI {
|
||||
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() {
|
||||
const groupHTML: { [key: string]: string } = {};
|
||||
for (const groupName in this._ui) {
|
||||
|
@ -210,7 +210,7 @@ export class SmoothVariable {
|
||||
}
|
||||
|
||||
public setTarget(target: number) {
|
||||
this._target = target;
|
||||
this._target = clamp(target, this._min, this._max);
|
||||
}
|
||||
|
||||
public setActual(actual: number) {
|
||||
|
Loading…
Reference in New Issue
Block a user