Skip to content
Snippets Groups Projects
Unverified Commit 71bc1bca authored by Jamie Temple's avatar Jamie Temple
Browse files

enhance: clean up of code

parent dcfcf75c
Branches
No related tags found
No related merge requests found
import * as CG from "./uitls/Rendering";
import { UI } from "./uitls/UI";
import { Curve2d } from "./uitls/Curve2d";
import { AmbientLight, DirectionalLight } from "three";
import { AmbientLight, GridHelper, PointLight, PointLightHelper } from "three";
import * as THREE from "three";
import { RotationObject } from "./uitls/RotationObject";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import { colorprime } from "./uitls/globals";
function demo1() {
function demo1(render: CG.RenderManager, ui: UI) {
const curve = new Curve2d();
curve.objects().forEach(obj => { render.add(obj); });
ui.addModifiable(curve);
}
function demo2() {
function demo2(render: CG.RenderManager, ui: UI) {
const rot = new RotationObject();
const grid = new GridHelper(10,10);
grid.renderOrder = -1
render.add(grid)
const loader = new OBJLoader();
loader.setPath("models/");
loader.load(
"Suzanne.obj",
"Arrow.obj",
function (obj) {
let tmp = obj.children[0] as THREE.Mesh;
tmp.material = new THREE.MeshLambertMaterial({ wireframe: true, side: 0 });
tmp.material = new THREE.MeshLambertMaterial({ wireframe: false, color: 0xaaaaaa });
rot.setMesh(tmp);
render.add(tmp);
}
......@@ -32,35 +38,33 @@ function demo2() {
const ambient = new AmbientLight(0x404040);
render.add(ambient);
const directionalright = new DirectionalLight(0xFFFFFF, 1.5);
render.add(directionalright);
directionalright.translateX(3)
const plightx = new PointLight(0xffffff, 1, 100);
plightx.position.set(5, 0, 0);
render.add(plightx);
const directionalleft = new DirectionalLight(0xFFFFFF, 1.5);
render.add(directionalleft);
directionalleft.translateX(-3)
const plighty = new PointLight(0xffffff, 1, 100);
plighty.position.set(0, 5, 0);
render.add(plighty);
const directional = new DirectionalLight(0xFFFFFF, 0.5);
render.add(directional);
directional.translateZ(3);
const plightz = new PointLight(0xffffff, 1, 100);
plightz.position.set(0, 0, 5);
render.add(plightz);
rot.objects().forEach(obj => {
render.add(obj);
});
render.render();
ui.addModifiable(rot);
}
const ui = new UI();
const render = new CG.RenderManager('#canvas', { near: 0.1, far: 1000, fov: 45, height: 1 });
const render = new CG.RenderManager('#canvas', { near: 0.01, far: 1000, fov: 45, height: 1 });
ui.addModifiable(render);
// demo1();
demo2();
// demo1(render, ui);
demo2(render, ui);
render.render();
......
......@@ -6,81 +6,92 @@ import { Matrix4 } from "three";
* @class Quaternion
* @description Class for quaternion. Inspired by prbt book.
* https://pbr-book.org/3ed-2018/Geometry_and_Transformations/Animating_Transformations#Quaternions
* @static all static methods are returning a copy. Passed quaternion are handled as immutable.
*/
export class Quaternion {
public _x: number;
public _y: number;
public _z: number;
public _w: number;
private _mode: string = "normal";
private _keepUnitQuaternion: boolean;
constructor(x: number = 0, y: number = 0, z: number = 0, w: number = 1, keepUnitQuaternion: boolean = true) {
constructor(x: number = 0, y: number = 0, z: number = 0, w: number = 1, keepUnitQuaternion: boolean = true, mode: string = "normal") {
this._x = x;
this._y = y;
this._z = z;
this._w = w;
this._mode = mode;
this._keepUnitQuaternion = keepUnitQuaternion;
}
public get x(): number { return this._x; }
public get y(): number { return this._y; }
public get z(): number { return this._z; }
public get w(): number { return this._w; }
public get mode(): string { return this._mode; }
public get keepUnitQuaternion(): boolean { return this._keepUnitQuaternion; }
public set x(x: number) { this.set(x, this.y, this.z, this.w) }
public set y(y: number) { this.set(this.x, y, this.z, this.w) }
public set z(z: number) { this.set(this.x, this.y, z, this.w) }
public set w(w: number) { this.set(this.x, this.y, this.z, w) }
public set mode(mode: string) { this._mode = mode; }
public set keepUnitQuaternion(keepUnitQuaternion: boolean) { this._keepUnitQuaternion = keepUnitQuaternion; }
public static setX(q: Quaternion, x: number): void { q.x = x; }
public static setY(q: Quaternion, y: number): void { q.y = y; }
public static setZ(q: Quaternion, z: number): void { q.z = z; }
public static setW(q: Quaternion, w: number): void { q.w = w; }
/**
* Setter which is set values and if keepUnitQuaternion is true, it will keep values for unit quaternion.
* @param x Quaternion i component
* @param y Quaternion j component
* @param z Quaternion k component
* @param w Quaternion s component
* @returns itself
*/
public set(x: number, y: number, z: number, w: number): Quaternion {
if (this._keepUnitQuaternion) {
const length = Math.sqrt(x * x + y * y + z * z + w * w);
this._x = x;
this._y = y;
this._z = z;
this._w = w;
if (length === 0) {
this._x = 0;
this._y = 0;
this._z = 0;
this._w = 1;
} else {
const length = Math.sqrt(x * x + y * y + z * z + w * w);
if (this._keepUnitQuaternion && length !== 0) {
this._x = x / length;
this._y = y / length;
this._z = z / length;
this._w = w / length;
}
return this
}
this._x = x;
this._y = y;
this._z = z;
this._w = w;
return this;
}
/**
* In js there is call by reference, so we need to clone the quaternion.
* @returns clone of itself
*/
public clone(): Quaternion {
return new Quaternion(this.x, this.y, this.z, this.w);
return new Quaternion(this.x, this.y, this.z, this.w,
this.keepUnitQuaternion, this.mode);
}
/**
* deep copy of quaternion
* @param q Quaternion to be copied
* @returns itself
*/
public copy(q: Quaternion): Quaternion {
this._x = q.w;
this._y = q.x;
this._z = q.y;
this._w = q.z;
this._mode = q.mode;
this._keepUnitQuaternion = q.keepUnitQuaternion;
return this;
}
/**
* Transforms quaternion to matrix
*/
public toMatrix(): Matrix4 {
let copy = this.clone();
......@@ -115,16 +126,28 @@ export class Quaternion {
return matrix;
}
// dot product
public static dot(q1: Quaternion, q2: Quaternion): number {
return q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
}
// normalize quaternion
public static normalize(q: Quaternion): Quaternion {
const length = Math.sqrt(Quaternion.dot(q, q));
if (length === 0) return new Quaternion();
return new Quaternion(q.x / length, q.y / length, q.z / length, q.w / length);
}
/**
* Multiply two quaternions or a quaternion and scalar. Function handles both because
* it is neater.
* @param q Quaternion to be multiplied
* @param other is another quaternion or number
* @returns
*/
public static multiply(q: Quaternion, other: Quaternion | number): Quaternion {
if (typeof other === "number")
......@@ -138,18 +161,26 @@ export class Quaternion {
);
}
// addition of two quaternions
public static add(q1: Quaternion, q2: Quaternion): Quaternion {
return new Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w);
}
// subtraction of two quaternions
public static subtract(q1: Quaternion, q2: Quaternion): Quaternion {
return Quaternion.add(q1, Quaternion.multiply(q2, -1));
}
// inverse of quaternion ! real part is not changed
public static inverse(q: Quaternion): Quaternion {
return new Quaternion(-q.x, -q.y, -q.z, q.w);
}
// spherical linear interpolation
public static slerp(q1: Quaternion, q2: Quaternion, t: number): Quaternion {
let cq1 = q1.clone();
......@@ -182,6 +213,14 @@ export class Quaternion {
Quaternion.multiply(cq1, s0),
Quaternion.multiply(cq2, s1)
)
}
public _x: number;
public _y: number;
public _z: number;
public _w: number;
private _mode: string;
private _keepUnitQuaternion: boolean;
}
\ No newline at end of file
import { GUI } from "dat.gui";
import { BufferGeometry, Color, Group, Line, LineBasicMaterial, Mesh, MeshBasicMaterial, Object3D, SphereBufferGeometry, Vector3 } from "three";
import { Quaternion } from "./Quaternion";
import {
SphereBufferGeometry, MeshBasicMaterial, LineBasicMaterial, BufferGeometry,
Object3D, Vector3, Group, Line, Mesh
} from "three";
import { Updatable, Modifiable } from "./Interfaces";
import { colorprime, colorsec } from "./globals";
import { Quaternion } from "./Quaternion";
import { GUI } from "dat.gui";
export class RotationObject implements Updatable, Modifiable {
public SLERP: boolean = false;
public MODE: string = "Single";
public qa: Quaternion = new Quaternion();;
public qb: Quaternion = new Quaternion();;
public t: number = 0;
private _qworking: Quaternion = new Quaternion();;
// private _orientation: Group = new Group();
private _coordinate: Group = new Group();
private _rotationAxis: Group = new Group();
private _mesh?: Mesh;
constructor() {
this.createReference();
this.createRotationAxis();
}
/**
* Object which should be displayed by the renderer
*/
objects(): Object3D[] {
return [this._coordinate, this._rotationAxis];
}
update(): void {
if (this.SLERP) {
this._qworking = Quaternion.slerp(this.qa, this.qb, this.t);
} else {
if (this.MODE === "Single")
this._qworking = this.qa.clone();
else
this._qworking = Quaternion.multiply(this.qa, this.qb);
/**
* Updates the qworking quaternion which is applied to the mesh
* qa and qb are used to calculate the quaternion and left in their original state
*/
update(): void {
}
if (this.SLERP) this.qworking = Quaternion.slerp(this.qa, this.qb, this.t);
else if (this.MODE === "Single") this.qworking = this.qa.clone();
else this.qworking = Quaternion.multiply(this.qa, this.qb);
if (this._mesh) {
this._mesh.setRotationFromMatrix(this._qworking.toMatrix());
this._mesh.setRotationFromMatrix(this.qworking.toMatrix());
this._coordinate.setRotationFromMatrix(this.qworking.toMatrix());
this.updateRotationAxis();
}
}
/**
* Resets the two quaternions to their default values
*/
resetQuaternions(): void {
this.qa.set(0, 0, 0, 1);
this.qb.set(0, 0, 0, 1);
this.update();
}
clampQuaternions(): void {
const E = 1e-2;
if (Math.abs(this.qa.x) < E) this.qa.x = 0;
if (Math.abs(this.qa.y) < E) this.qa.y = 0;
if (Math.abs(this.qa.z) < E) this.qa.z = 0;
if (Math.abs(this.qa.w) < E) this.qa.w = 0;
if (Math.abs(this.qb.x) < E) this.qb.x = 0;
if (Math.abs(this.qb.y) < E) this.qb.y = 0;
if (Math.abs(this.qb.z) < E) this.qb.z = 0;
if (Math.abs(this.qb.w) < E) this.qb.w = 0;
}
/**
* Create the UI element of this object
*/
createElement(gui: GUI): void {
let folder = gui.__folders["Rotation Object"];
if (folder) gui.removeFolder(folder);
......@@ -89,10 +79,6 @@ export class RotationObject implements Updatable, Modifiable {
this.createElement(gui);
});
folder.add(this, "clampQuaternions").name("CLAMP").onFinishChange(() => {
this.createElement(gui);
});
const range: number = 1;
const step: number = .01;
......@@ -102,10 +88,11 @@ export class RotationObject implements Updatable, Modifiable {
const fqa = folder.addFolder(this.MODE === "Single" ? "Quaternion" : "Quaternion A");
fqa.open();
fqa.add(this.qa, "x", -range, range, step).listen().onChange(func);
fqa.add(this.qa, "y", -range, range, step).listen().onChange(func);
fqa.add(this.qa, "z", -range, range, step).listen().onChange(func);
fqa.add(this.qa, "w", -range, range, step).listen().onChange(func);
fqa.add(this.qa, "keepUnitQuaternion").onChange(func);
fqa.add(this.qa, "x", -range, range, step).onChange(func);
fqa.add(this.qa, "y", -range, range, step).onChange(func);
fqa.add(this.qa, "z", -range, range, step).onChange(func);
fqa.add(this.qa, "w", -range, range, step).onChange(func);
}
......@@ -113,25 +100,25 @@ export class RotationObject implements Updatable, Modifiable {
const fqb = folder.addFolder("Quaternion B");
fqb.open();
fqb.add(this.qb, "x", -range, range, step).listen().onChange(func);
fqb.add(this.qb, "y", -range, range, step).listen().onChange(func);
fqb.add(this.qb, "z", -range, range, step).listen().onChange(func);
fqb.add(this.qb, "w", -range, range, step).listen().onChange(func);
fqb.add(this.qb, "keepUnitQuaternion").onChange(func);
fqb.add(this.qb, "x", -range, range, step).onChange(func);
fqb.add(this.qb, "y", -range, range, step).onChange(func);
fqb.add(this.qb, "z", -range, range, step).onChange(func);
fqb.add(this.qb, "w", -range, range, step).onChange(func);
}
}
createReference(): void {
const colors: Color[] = [
new Color(1, 0, 0),
new Color(0, 1, 0),
new Color(0, 0, 1)
];
/**
* this creates the local coordinate system of the object
*/
createReference(): void {
const scale = 3;
const positions: Vector3[] = [
new Vector3(5, 0, 0),
new Vector3(0, 5, 0),
new Vector3(0, 0, 5)
new Vector3(scale, 0, 0),
new Vector3(0, scale, 0),
new Vector3(0, 0, scale)
];
let lines: Line[] = [];
......@@ -140,14 +127,14 @@ export class RotationObject implements Updatable, Modifiable {
for (let i = 0; i < 3; i++) {
const line = new Line(
new BufferGeometry().setFromPoints([positions[i].clone().multiplyScalar(-1), positions[i]]),
new LineBasicMaterial({ color: colors[i].getHex() })
new LineBasicMaterial({ color: colorprime[i] })
);
line.name = "line" + i;
lines.push(line);
const sphere = new Mesh(
new SphereBufferGeometry(.05, 32, 32),
new MeshBasicMaterial({ color: colors[i].getHex() })
new MeshBasicMaterial({ color: colorprime[i] })
);
sphere.name = "sphere" + i;
sphere.position.copy(positions[i]);
......@@ -158,12 +145,16 @@ export class RotationObject implements Updatable, Modifiable {
this._coordinate.add(...spheres);
}
/**
* Visualisation of the "quaterions" more the resulting rotation axis
*/
updateRotationAxis(): void {
const suffix: string[] = ["A", "B", "C"];
const ref: Quaternion[] = [this.qa, this.qb, this._qworking];
const ref: Quaternion[] = [this.qa, this.qb, this.qworking];
for (let i = 0; i < suffix.length; i++) {
for (let i = 0; i < ref.length; i++) {
const line = this._rotationAxis.getObjectByName("rotationAxisLine" + suffix[i]) as Line;
const point = this._rotationAxis.getObjectByName("rotationAxisPoint" + suffix[i]) as Mesh;
......@@ -176,30 +167,34 @@ export class RotationObject implements Updatable, Modifiable {
)
const origin = new Vector3(0, 0, 0);
line.geometry = new BufferGeometry().setFromPoints([origin, dir.normalize().multiplyScalar(2)]);
if (suffix[i] === "B" && !this.SLERP) dir.applyMatrix4(this.qa.toMatrix());
let factor: number = 3.5;
if (suffix[i] === "C") factor = 4;
line.geometry = new BufferGeometry().setFromPoints([origin, dir.normalize().multiplyScalar(factor)]);
point.position.copy(dir);
}
}
/**
* Initializes the rotation axis lines
*/
createRotationAxis(): void {
const colors: Color[] = [
new Color(0xff8844),
new Color(0x44ff88),
new Color(0x884488)
]
const suffix: string[] = ["A", "B", "C"];
for (let i = 0; i < suffix.length; i++) {
const line = new Line(
undefined,
new LineBasicMaterial({ color: colors[i].getHex() })
new LineBasicMaterial({ color: colorsec[i] })
);
line.name = "rotationAxisLine" + suffix[i];
this._rotationAxis.add(line);
const point = new Mesh(
new SphereBufferGeometry(.05, 32, 32),
new MeshBasicMaterial({ color: colors[i].getHex() })
new MeshBasicMaterial({ color: colorsec[i] })
);
point.name = "rotationAxisPoint" + suffix[i];
this._rotationAxis.add(point);
......@@ -208,8 +203,25 @@ export class RotationObject implements Updatable, Modifiable {
this.updateRotationAxis();
}
/**
* required to load mesh from main, because the objloader is weird
*/
setMesh(mesh: Mesh): void {
this._mesh = mesh;
this.update()
}
public SLERP: boolean = false;
public MODE: string = "Single";
public qa: Quaternion = new Quaternion();;
public qb: Quaternion = new Quaternion();;
public qworking: Quaternion = new Quaternion();;
public t: number = 0;
private _coordinate: Group = new Group();
private _rotationAxis: Group = new Group();
private _mesh?: Mesh;
}
\ No newline at end of file
export const colorprime: [number, number, number] = [
0xF55600, // red
0x5DFF70, // green
0x123FB3 // blue
]
export const colorsec: [number, number, number] = [
0xF500EF, // pink
0xFFD500, // yellow
0x77F1FF // turquoise
]
\ No newline at end of file
......@@ -34,17 +34,14 @@ class CameraManager {
];
this.active = this.cameras[1];
this.active.position.set(0, -3, 5);
this.active.position.set(0, 0, 10);
}
switchCamera(): void {
if (this.active == this.cameras[0]) {
this.active = this.cameras[1];
this.active.position.set(0, 0, 5);
}
else {
this.active = this.cameras[0];
}
const pos = this.active.position.clone();
if (this.active == this.cameras[0]) this.active = this.cameras[1];
else this.active = this.cameras[0];
this.active.position.copy(pos);
}
camera(): Camera {
......@@ -83,20 +80,21 @@ export class RenderManager implements Modifiable {
}
/**
* Create the UI element for dat.gui
* @param gui
*/
createElement(gui: GUI): void {
const rendering = gui.addFolder('Rendering');
rendering.open();
rendering.add(this, '_fps', 10, 60).step(1).name('FPS');
rendering.add(this._cameraManager, 'switchCamera').name('Switch camera').onFinishChange(() => {
if (this._cameraManager.camera().type == 'PerspectiveCamera' ? true : false)
this._controls.enableRotate = true;
else
this._controls.enableRotate = false;
this._controls.object = this._cameraManager.camera();
this._controls.reset();
});
}
/**
* Render routine (called by the main loop)
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment