From fc62362c185f36095461de20122ffb386ebc0edb Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Tue, 23 Jun 2020 18:39:39 -0400 Subject: [PATCH] feat: orthographic example, fix perspective camera sign bug, improve uniformity of maker-functions. --- .../gettingstarted/8_orthographic/index.html | 8 + .../gettingstarted/4_lambertCube/index.ts | 6 +- .../5_reflectivePolyhedral/index.ts | 9 +- .../7_metallicRoughness/index.ts | 6 +- .../8_orthographic/fragment.glsl | 18 +++ .../gettingstarted/8_orthographic/index.ts | 59 ++++++++ .../gettingstarted/8_orthographic/vertex.glsl | 20 +++ src/lib/math/Euler.Functions.ts | 15 +- src/lib/math/Matrix3.ts | 4 - src/lib/math/Matrix4.Functions.ts | 88 ++++++++--- src/lib/math/Quaternion.Functions.ts | 141 ++++++++---------- src/lib/math/Quaternion.ts | 13 +- src/lib/math/Vector2.Functions.ts | 7 +- src/lib/math/Vector3.Functions.ts | 12 +- src/lib/nodes/Node.ts | 5 +- src/lib/nodes/cameras/OrthographicCamera.ts | 22 +-- src/lib/nodes/cameras/PerspectiveCamera.ts | 22 ++- src/lib/nodes/lights/Direction.ts | 12 +- .../webgl/framebuffers/CanvasFramebuffer.ts | 4 + 19 files changed, 311 insertions(+), 160 deletions(-) create mode 100644 examples/gettingstarted/8_orthographic/index.html create mode 100644 src/examples/gettingstarted/8_orthographic/fragment.glsl create mode 100644 src/examples/gettingstarted/8_orthographic/index.ts create mode 100644 src/examples/gettingstarted/8_orthographic/vertex.glsl diff --git a/examples/gettingstarted/8_orthographic/index.html b/examples/gettingstarted/8_orthographic/index.html new file mode 100644 index 00000000..5fc629ef --- /dev/null +++ b/examples/gettingstarted/8_orthographic/index.html @@ -0,0 +1,8 @@ + + + + Threeify - Getting Started - 8 Orrthographic + + + + diff --git a/src/examples/gettingstarted/4_lambertCube/index.ts b/src/examples/gettingstarted/4_lambertCube/index.ts index dbd52e58..b652d165 100644 --- a/src/examples/gettingstarted/4_lambertCube/index.ts +++ b/src/examples/gettingstarted/4_lambertCube/index.ts @@ -31,8 +31,8 @@ async function init(): Promise { const program = makeProgramFromShaderMaterial(context, material); const uniforms = { localToWorld: new Matrix4(), - worldToView: makeMatrix4Translation(new Matrix4(), new Vector3(0, 0, -1)), - viewToScreen: makeMatrix4Perspective(new Matrix4(), -0.25, 0.25, 0.25, -0.25, 0.1, 4.0), + worldToView: makeMatrix4Translation(new Vector3(0, 0, -1)), + viewToScreen: makeMatrix4Perspective(-0.25, 0.25, 0.25, -0.25, 0.1, 4.0), viewLightPosition: new Vector3(0, 0, 0), map: makeTexImage2DFromTexture(context, texture), }; @@ -44,8 +44,8 @@ async function init(): Promise { const now = Date.now(); uniforms.localToWorld = makeMatrix4RotationFromEuler( - uniforms.localToWorld, new Euler(now * 0.001, now * 0.0033, now * 0.00077), + uniforms.localToWorld, ); canvasFramebuffer.renderBufferGeometry(program, uniforms, bufferGeometry, depthTestState); } diff --git a/src/examples/gettingstarted/5_reflectivePolyhedral/index.ts b/src/examples/gettingstarted/5_reflectivePolyhedral/index.ts index 468fc6b4..e512edc1 100644 --- a/src/examples/gettingstarted/5_reflectivePolyhedral/index.ts +++ b/src/examples/gettingstarted/5_reflectivePolyhedral/index.ts @@ -4,7 +4,7 @@ import { ShaderMaterial } from "../../../lib/materials/ShaderMaterial"; import { Euler } from "../../../lib/math/Euler"; import { Matrix4 } from "../../../lib/math/Matrix4"; import { - makeMatrix4Perspective, + makeMatrix4PerspectiveFov, makeMatrix4RotationFromEuler, makeMatrix4Translation, } from "../../../lib/math/Matrix4.Functions"; @@ -38,8 +38,9 @@ async function init(): Promise { const program = makeProgramFromShaderMaterial(context, material); const uniforms = { localToWorld: new Matrix4(), - worldToView: makeMatrix4Translation(new Matrix4(), new Vector3(0, 0, -1)), - viewToScreen: makeMatrix4Perspective(new Matrix4(), -0.25, 0.25, 0.25, -0.25, 0.1, 4.0), + worldToView: makeMatrix4Translation(new Vector3(0, 0, -1)), + // viewToScreen: makeMatrix4Perspective(-0.25, 0.25, 0.25, -0.25, 0.1, 4.0), + viewToScreen: makeMatrix4PerspectiveFov(45, 0.1, 4.0, 1.0, canvasFramebuffer.aspectRatio), cubeMap: makeTexImage2DFromCubeTexture(context, cubeTexture), }; const bufferGeometry = makeBufferGeometryFromGeometry(context, geometry); @@ -50,8 +51,8 @@ async function init(): Promise { const now = Date.now(); uniforms.localToWorld = makeMatrix4RotationFromEuler( - uniforms.localToWorld, new Euler(now * 0.0001, now * 0.00033, now * 0.000077), + uniforms.localToWorld, ); canvasFramebuffer.renderBufferGeometry(program, uniforms, bufferGeometry, depthTestState); } diff --git a/src/examples/gettingstarted/7_metallicRoughness/index.ts b/src/examples/gettingstarted/7_metallicRoughness/index.ts index 8d7a3398..9b2563e9 100644 --- a/src/examples/gettingstarted/7_metallicRoughness/index.ts +++ b/src/examples/gettingstarted/7_metallicRoughness/index.ts @@ -39,8 +39,8 @@ async function init(): Promise { const program = makeProgramFromShaderMaterial(context, material); const uniforms = { localToWorld: new Matrix4(), - worldToView: makeMatrix4Translation(new Matrix4(), new Vector3(0, 0, -1)), - viewToScreen: makeMatrix4Perspective(new Matrix4(), -0.25, 0.25, 0.25, -0.25, 0.1, 4.0), + worldToView: makeMatrix4Translation(new Vector3(0, 0, -1)), + viewToScreen: makeMatrix4Perspective(-0.25, 0.25, 0.25, -0.25, 0.1, 4.0), roughnessFactor: 0, cubeMap: makeTexImage2DFromCubeTexture(context, cubeTexture), }; @@ -52,8 +52,8 @@ async function init(): Promise { const now = Date.now(); uniforms.localToWorld = makeMatrix4RotationFromEuler( - uniforms.localToWorld, new Euler(now * 0.0001, now * 0.00033, now * 0.000077), + uniforms.localToWorld, ); uniforms.roughnessFactor = Math.sin(now * 0.0001) * 0.5 + 0.5; canvasFramebuffer.renderBufferGeometry(program, uniforms, bufferGeometry, depthTestState); diff --git a/src/examples/gettingstarted/8_orthographic/fragment.glsl b/src/examples/gettingstarted/8_orthographic/fragment.glsl new file mode 100644 index 00000000..e52cca4c --- /dev/null +++ b/src/examples/gettingstarted/8_orthographic/fragment.glsl @@ -0,0 +1,18 @@ +precision highp float; + +varying vec3 v_viewPosition; +varying vec3 v_viewNormal; +varying vec2 v_uv; + +uniform sampler2D map; +uniform vec3 viewLightPosition; + +void main() { + + vec3 albedo = texture2D(map, v_uv).xyz; + vec3 directionToLight = normalize( viewLightPosition - v_viewPosition ); + float lambertianIntensity = dot( directionToLight, v_viewNormal ); + + gl_FragColor = vec4( albedo * lambertianIntensity, 1.0 ); + +} diff --git a/src/examples/gettingstarted/8_orthographic/index.ts b/src/examples/gettingstarted/8_orthographic/index.ts new file mode 100644 index 00000000..75f6770e --- /dev/null +++ b/src/examples/gettingstarted/8_orthographic/index.ts @@ -0,0 +1,59 @@ +import { box } from "../../../lib/geometry/primitives/Box"; +import { fetchImage } from "../../../lib/io/loaders/Image"; +import { ShaderMaterial } from "../../../lib/materials/ShaderMaterial"; +import { Euler } from "../../../lib/math/Euler"; +import { Matrix4 } from "../../../lib/math/Matrix4"; +import { + makeMatrix4OrthographicSimple, + makeMatrix4RotationFromEuler, + makeMatrix4Translation, +} from "../../../lib/math/Matrix4.Functions"; +import { Vector2 } from "../../../lib/math/Vector2"; +import { Vector3 } from "../../../lib/math/Vector3"; +import { makeBufferGeometryFromGeometry } from "../../../lib/renderers/webgl/buffers/BufferGeometry"; +import { DepthTestFunc, DepthTestState } from "../../../lib/renderers/webgl/DepthTestState"; +import { makeProgramFromShaderMaterial } from "../../../lib/renderers/webgl/programs/Program"; +import { RenderingContext } from "../../../lib/renderers/webgl/RenderingContext"; +import { makeTexImage2DFromTexture } from "../../../lib/renderers/webgl/textures/TexImage2D"; +import { Texture } from "../../../lib/textures/Texture"; +import fragmentSourceCode from "./fragment.glsl"; +import vertexSourceCode from "./vertex.glsl"; + +async function init(): Promise { + const geometry = box(0.75, 0.75, 0.75); + const material = new ShaderMaterial(vertexSourceCode, fragmentSourceCode); + const texture = new Texture(await fetchImage("/assets/textures/uv_grid_opengl.jpg")); + + const context = new RenderingContext(); + const canvasFramebuffer = context.canvasFramebuffer; + if (canvasFramebuffer.canvas instanceof HTMLCanvasElement) { + document.body.appendChild(canvasFramebuffer.canvas); + } + const program = makeProgramFromShaderMaterial(context, material); + const uniforms = { + localToWorld: new Matrix4(), + worldToView: makeMatrix4Translation(new Vector3(0, 0, -1)), + viewToScreen: makeMatrix4OrthographicSimple(1.5, new Vector2(), 0.1, 4.0, 1.0, canvasFramebuffer.aspectRatio), + viewLightPosition: new Vector3(0, 0, 0), + map: makeTexImage2DFromTexture(context, texture), + }; + const bufferGeometry = makeBufferGeometryFromGeometry(context, geometry); + const depthTestState = new DepthTestState(true, DepthTestFunc.Less); + + function animate(): void { + requestAnimationFrame(animate); + + const now = Date.now(); + uniforms.localToWorld = makeMatrix4RotationFromEuler( + new Euler(now * 0.001, now * 0.0033, now * 0.00077), + uniforms.localToWorld, + ); + canvasFramebuffer.renderBufferGeometry(program, uniforms, bufferGeometry, depthTestState); + } + + animate(); + + return null; +} + +init(); diff --git a/src/examples/gettingstarted/8_orthographic/vertex.glsl b/src/examples/gettingstarted/8_orthographic/vertex.glsl new file mode 100644 index 00000000..c92d069e --- /dev/null +++ b/src/examples/gettingstarted/8_orthographic/vertex.glsl @@ -0,0 +1,20 @@ +attribute vec3 position; +attribute vec3 normal; +attribute vec2 uv; + +uniform mat4 localToWorld; +uniform mat4 worldToView; +uniform mat4 viewToScreen; + +varying vec3 v_viewPosition; +varying vec3 v_viewNormal; +varying vec2 v_uv; + +void main() { + + v_viewNormal = normalize( ( worldToView * localToWorld * vec4( normal, 0.0 ) ).xyz ); + v_viewPosition = ( worldToView * localToWorld * vec4( position, 1.0 ) ).xyz; + v_uv = uv; + gl_Position = viewToScreen * vec4( v_viewPosition, 1.0 ); + +} diff --git a/src/lib/math/Euler.Functions.ts b/src/lib/math/Euler.Functions.ts index 3bb3af56..fb2a3a25 100644 --- a/src/lib/math/Euler.Functions.ts +++ b/src/lib/math/Euler.Functions.ts @@ -4,7 +4,11 @@ import { Matrix4 } from "./Matrix4"; import { makeMatrix4RotationFromQuaternion } from "./Matrix4.Functions"; import { Quaternion } from "./Quaternion"; -export function makeEulerFromRotationMatrix4(e: Euler, m: Matrix4, order: EulerOrder = EulerOrder.Default): Euler { +export function makeEulerFromRotationMatrix4( + m: Matrix4, + order: EulerOrder = EulerOrder.Default, + result = new Euler(), +): Euler { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; @@ -102,11 +106,10 @@ export function makeEulerFromRotationMatrix4(e: Euler, m: Matrix4, order: EulerO break; } - return e.set(x, y, z, order); + return result.set(x, y, z, order); } -export function makeEulerFromQuaternion(e: Euler, q: Quaternion, order: EulerOrder): Euler { - const m = makeMatrix4RotationFromQuaternion(new Matrix4(), q); - - return makeEulerFromRotationMatrix4(e, m, order); +export function makeEulerFromQuaternion(q: Quaternion, order: EulerOrder, result = new Euler()): Euler { + const m = makeMatrix4RotationFromQuaternion(q); + return makeEulerFromRotationMatrix4(m, order, result); } diff --git a/src/lib/math/Matrix3.ts b/src/lib/math/Matrix3.ts index d7e90d97..7335fd1e 100644 --- a/src/lib/math/Matrix3.ts +++ b/src/lib/math/Matrix3.ts @@ -75,10 +75,6 @@ export class Matrix3 implements IPrimitive { return this; } - numComponents(): 9 { - return 9; - } - multiplyByScalar(s: number): this { const te = this.elements; diff --git a/src/lib/math/Matrix4.Functions.ts b/src/lib/math/Matrix4.Functions.ts index 136282b5..c8651c82 100644 --- a/src/lib/math/Matrix4.Functions.ts +++ b/src/lib/math/Matrix4.Functions.ts @@ -2,6 +2,7 @@ import { Euler, EulerOrder } from "./Euler"; import { Matrix4 } from "./Matrix4"; import { Quaternion } from "./Quaternion"; import { makeQuaternionFromRotationMatrix4 } from "./Quaternion.Functions"; +import { Vector2 } from "./Vector2"; import { Vector3 } from "./Vector3"; export function makeMatrix4Concatenation(a: Matrix4, b: Matrix4, result = new Matrix4()): Matrix4 { @@ -203,11 +204,11 @@ export function makeMatrix4Inverse(m: Matrix4, result = new Matrix4()): Matrix4 return result; } -export function makeMatrix4Translation(m: Matrix4, t: Vector3): Matrix4 { - return m.set(1, 0, 0, t.x, 0, 1, 0, t.y, 0, 0, 1, t.z, 0, 0, 0, 1); +export function makeMatrix4Translation(t: Vector3, result = new Matrix4()): Matrix4 { + return result.set(1, 0, 0, t.x, 0, 1, 0, t.y, 0, 0, 1, t.z, 0, 0, 0, 1); } -export function makeMatrix4RotationFromAngleAxis(m: Matrix4, axis: Vector3, angle: number): Matrix4 { +export function makeMatrix4RotationFromAngleAxis(axis: Vector3, angle: number, result = new Matrix4()): Matrix4 { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); @@ -219,7 +220,7 @@ export function makeMatrix4RotationFromAngleAxis(m: Matrix4, axis: Vector3, angl const tx = t * x, ty = t * y; - return m.set( + return result.set( tx * x + c, tx * y - s * z, tx * z + s * y, @@ -239,8 +240,8 @@ export function makeMatrix4RotationFromAngleAxis(m: Matrix4, axis: Vector3, angl ); } -export function makeMatrix4RotationFromEuler(m: Matrix4, euler: Euler): Matrix4 { - const te = m.elements; +export function makeMatrix4RotationFromEuler(euler: Euler, result = new Matrix4()): Matrix4 { + const te = result.elements; const x = euler.x, y = euler.y, @@ -368,19 +369,19 @@ export function makeMatrix4RotationFromEuler(m: Matrix4, euler: Euler): Matrix4 te[14] = 0; te[15] = 1; - return m; + return result; } -export function makeMatrix4RotationFromQuaternion(m: Matrix4, q: Quaternion): Matrix4 { - return composeMatrix4(m, new Vector3(), q, new Vector3(1, 1, 1)); +export function makeMatrix4RotationFromQuaternion(q: Quaternion, result = new Matrix4()): Matrix4 { + return composeMatrix4(new Vector3(), q, new Vector3(1, 1, 1), result); } -export function makeMatrix4Scale(m: Matrix4, s: Vector3): Matrix4 { - return m.set(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1); +export function makeMatrix4Scale(s: Vector3, result = new Matrix4()): Matrix4 { + return result.set(s.x, 0, 0, 0, 0, s.y, 0, 0, 0, 0, s.z, 0, 0, 0, 0, 1); } -export function makeMatrix4Shear(m: Matrix4, x: number, y: number, z: number): Matrix4 { - return m.set(1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1); +export function makeMatrix4Shear(x: number, y: number, z: number, result = new Matrix4()): Matrix4 { + return result.set(1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1); } export function getMaxScaleOnAxis(m: Matrix4): number { @@ -393,7 +394,12 @@ export function getMaxScaleOnAxis(m: Matrix4): number { return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } -export function composeMatrix4(m: Matrix4, position: Vector3, rotation: Quaternion, scale: Vector3): Matrix4 { +export function composeMatrix4( + position: Vector3, + rotation: Quaternion, + scale: Vector3, + result = new Matrix4(), +): Matrix4 { const x = rotation.x, y = rotation.y, z = rotation.z, @@ -415,7 +421,7 @@ export function composeMatrix4(m: Matrix4, position: Vector3, rotation: Quaterni sy = scale.y, sz = scale.z; - return m.set( + return result.set( // TODO: Replace with set (1 - (yy + zz)) * sx, (xy - wz) * sy, @@ -472,7 +478,7 @@ export function decomposeMatrix4(m: Matrix4, position: Vector3, rotation: Quater m2.elements[9] *= invSZ; m2.elements[10] *= invSZ; - makeQuaternionFromRotationMatrix4(rotation, m); + makeQuaternionFromRotationMatrix4(m2, rotation); scale.x = sx; scale.y = sy; @@ -483,13 +489,13 @@ export function decomposeMatrix4(m: Matrix4, position: Vector3, rotation: Quater // TODO: Replace with a Box2 export function makeMatrix4Perspective( - m: Matrix4, left: number, right: number, top: number, bottom: number, near: number, far: number, + result = new Matrix4(), ): Matrix4 { const x = (2 * near) / (right - left); const y = (2 * near) / (top - bottom); @@ -499,18 +505,40 @@ export function makeMatrix4Perspective( const c = -(far + near) / (far - near); const d = (-2 * far * near) / (far - near); - return m.set(x, 0, a, 0, 0, y, b, 0, 0, 0, c, d, 0, 0, -1, 0); + return result.set(x, 0, a, 0, 0, y, b, 0, 0, 0, c, d, 0, 0, -1, 0); +} + +export function makeMatrix4PerspectiveFov( + verticalFov: number, + near: number, + far: number, + zoom: number, + aspectRatio: number, + result = new Matrix4(), +): Matrix4 { + const height = (2.0 * near * Math.tan((verticalFov * Math.PI) / 180.0)) / zoom; + const width = height * aspectRatio; + + // NOTE: OpenGL screen coordinates are -bottomt to +top, -left to +right. + + const right = width * 0.5; + const left = right - width; + + const top = height * 0.5; + const bottom = top - height; + + return makeMatrix4Perspective(left, right, top, bottom, near, far, result); } // TODO: Replace with a Box3? export function makeMatrix4Orthographic( - m: Matrix4, left: number, right: number, top: number, bottom: number, near: number, far: number, + result = new Matrix4(), ): Matrix4 { const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); @@ -520,5 +548,25 @@ export function makeMatrix4Orthographic( const y = (top + bottom) * h; const z = (far + near) * p; - return m.set(2 * w, 0, 0, -x, 0, 2 * h, 0, -y, 0, 0, -2 * p, -z, 0, 0, 0, 1); + return result.set(2 * w, 0, 0, -x, 0, 2 * h, 0, -y, 0, 0, -2 * p, -z, 0, 0, 0, 1); +} + +export function makeMatrix4OrthographicSimple( + height: number, + center: Vector2, + near: number, + far: number, + zoom: number, + aspectRatio = 1.0, + result = new Matrix4(), +): Matrix4 { + const width = (height * aspectRatio) / zoom; + + const left = -width * 0.5 + center.x; + const right = left + width; + + const top = -height * 0.5 + center.y; + const bottom = top + height; + + return makeMatrix4Orthographic(left, right, top, bottom, near, far, result); } diff --git a/src/lib/math/Quaternion.Functions.ts b/src/lib/math/Quaternion.Functions.ts index dac6fe48..e23db123 100644 --- a/src/lib/math/Quaternion.Functions.ts +++ b/src/lib/math/Quaternion.Functions.ts @@ -3,7 +3,7 @@ import { Matrix4 } from "./Matrix4"; import { Quaternion } from "./Quaternion"; import { Vector3 } from "./Vector3"; -export function makeQuaternionFromEuler(q: Quaternion, e: Euler): Quaternion { +export function makeQuaternionFromEuler(e: Euler, result = new Quaternion()): Quaternion { const x = e.x, y = e.y, z = e.z, @@ -22,52 +22,59 @@ export function makeQuaternionFromEuler(q: Quaternion, e: Euler): Quaternion { switch (order) { case EulerOrder.XYZ: - q.x = s1 * c2 * c3 + c1 * s2 * s3; - q.y = c1 * s2 * c3 - s1 * c2 * s3; - q.z = c1 * c2 * s3 + s1 * s2 * c3; - q.w = c1 * c2 * c3 - s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 + c1 * s2 * s3, + c1 * s2 * c3 - s1 * c2 * s3, + c1 * c2 * s3 + s1 * s2 * c3, + c1 * c2 * c3 - s1 * s2 * s3, + ); case EulerOrder.YXZ: - q.x = s1 * c2 * c3 + c1 * s2 * s3; - q.y = c1 * s2 * c3 - s1 * c2 * s3; - q.z = c1 * c2 * s3 - s1 * s2 * c3; - q.w = c1 * c2 * c3 + s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 + c1 * s2 * s3, + c1 * s2 * c3 - s1 * c2 * s3, + c1 * c2 * s3 - s1 * s2 * c3, + c1 * c2 * c3 + s1 * s2 * s3, + ); case EulerOrder.ZXY: - q.x = s1 * c2 * c3 - c1 * s2 * s3; - q.y = c1 * s2 * c3 + s1 * c2 * s3; - q.z = c1 * c2 * s3 + s1 * s2 * c3; - q.w = c1 * c2 * c3 - s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 - c1 * s2 * s3, + c1 * s2 * c3 + s1 * c2 * s3, + c1 * c2 * s3 + s1 * s2 * c3, + c1 * c2 * c3 - s1 * s2 * s3, + ); case EulerOrder.ZYX: - q.x = s1 * c2 * c3 - c1 * s2 * s3; - q.y = c1 * s2 * c3 + s1 * c2 * s3; - q.z = c1 * c2 * s3 - s1 * s2 * c3; - q.w = c1 * c2 * c3 + s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 - c1 * s2 * s3, + c1 * s2 * c3 + s1 * c2 * s3, + c1 * c2 * s3 - s1 * s2 * c3, + c1 * c2 * c3 + s1 * s2 * s3, + ); case EulerOrder.YZX: - q.x = s1 * c2 * c3 + c1 * s2 * s3; - q.y = c1 * s2 * c3 + s1 * c2 * s3; - q.z = c1 * c2 * s3 - s1 * s2 * c3; - q.w = c1 * c2 * c3 - s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 + c1 * s2 * s3, + c1 * s2 * c3 + s1 * c2 * s3, + c1 * c2 * s3 - s1 * s2 * c3, + c1 * c2 * c3 - s1 * s2 * s3, + ); case EulerOrder.XZY: - q.x = s1 * c2 * c3 - c1 * s2 * s3; - q.y = c1 * s2 * c3 - s1 * c2 * s3; - q.z = c1 * c2 * s3 + s1 * s2 * c3; - q.w = c1 * c2 * c3 + s1 * s2 * s3; - break; + return result.set( + s1 * c2 * c3 - c1 * s2 * s3, + c1 * s2 * c3 - s1 * c2 * s3, + c1 * c2 * s3 + s1 * s2 * c3, + c1 * c2 * c3 + s1 * s2 * s3, + ); + + default: + throw new Error("unsupported euler order"); } - - return q; } -export function makeQuaternionFromRotationMatrix4(q: Quaternion, m: Matrix4): Quaternion { +export function makeQuaternionFromRotationMatrix4(m: Matrix4, result = new Quaternion()): Quaternion { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) @@ -85,42 +92,29 @@ export function makeQuaternionFromRotationMatrix4(q: Quaternion, m: Matrix4): Qu m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; - let s; if (trace > 0) { - s = 0.5 / Math.sqrt(trace + 1.0); - - q.w = 0.25 / s; - q.x = (m32 - m23) * s; - q.y = (m13 - m31) * s; - q.z = (m21 - m12) * s; - } else if (m11 > m22 && m11 > m33) { - s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); - - q.w = (m32 - m23) / s; - q.x = 0.25 * s; - q.y = (m12 + m21) / s; - q.z = (m13 + m31) / s; - } else if (m22 > m33) { - s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); - - q.w = (m13 - m31) / s; - q.x = (m12 + m21) / s; - q.y = 0.25 * s; - q.z = (m23 + m32) / s; - } else { - s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); - - q.w = (m21 - m12) / s; - q.x = (m13 + m31) / s; - q.y = (m23 + m32) / s; - q.z = 0.25 * s; + const s = 0.5 / Math.sqrt(trace + 1.0); + + return result.set((m32 - m23) * s, (m13 - m31) * s, (m21 - m12) * s, 0.25 / s); + } + if (m11 > m22 && m11 > m33) { + const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); + + return result.set(0.25 * s, (m12 + m21) / s, (m13 + m31) / s, (m32 - m23) / s); } + if (m22 > m33) { + const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); - return q; + return result.set((m12 + m21) / s, 0.25 * s, (m23 + m32) / s, (m13 - m31) / s); + } + + const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); + + return result.set((m13 + m31) / s, (m23 + m32) / s, 0.25 * s, (m21 - m12) / s); } -export function makeQuaternionFromAxisAngle(q: Quaternion, axis: Vector3, angle: number): Quaternion { +export function makeQuaternionFromAxisAngle(axis: Vector3, angle: number, result = new Quaternion()): Quaternion { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized @@ -128,12 +122,7 @@ export function makeQuaternionFromAxisAngle(q: Quaternion, axis: Vector3, angle: const halfAngle = angle / 2, s = Math.sin(halfAngle); - q.x = axis.x * s; - q.y = axis.y * s; - q.z = axis.z * s; - q.w = Math.cos(halfAngle); - - return q; + return result.set(axis.x * s, axis.y * s, axis.z * s, Math.cos(halfAngle)); } export function makeQuaternionFromBaryCoordWeights( @@ -143,9 +132,11 @@ export function makeQuaternionFromBaryCoordWeights( c: Quaternion, result = new Quaternion(), ): Quaternion { - result.x = a.x * baryCoord.x + b.x * baryCoord.y + c.x * baryCoord.z; - result.y = a.y * baryCoord.x + b.y * baryCoord.y + c.y * baryCoord.z; - result.z = a.z * baryCoord.x + b.z * baryCoord.y + c.z * baryCoord.z; - result.w = a.w * baryCoord.x + b.w * baryCoord.y + c.w * baryCoord.z; - return result; + const v = baryCoord; + return result.set( + a.x * v.x + b.x * v.y + c.x * v.z, + a.y * v.x + b.y * v.y + c.y * v.z, + a.z * v.x + b.z * v.y + c.z * v.z, + a.w * v.x + b.w * v.y + c.w * v.z, + ); } diff --git a/src/lib/math/Quaternion.ts b/src/lib/math/Quaternion.ts index 6ef57310..0ca30d41 100644 --- a/src/lib/math/Quaternion.ts +++ b/src/lib/math/Quaternion.ts @@ -15,6 +15,15 @@ export class Quaternion implements IPrimitive { return hashFloat4(this.x, this.y, this.z, this.w); } + set(x: number, y: number, z: number, w: number): this { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + } + clone(): Quaternion { return new Quaternion().copy(this); } @@ -82,10 +91,6 @@ export class Quaternion implements IPrimitive { return this; } - numComponents(): 4 { - return 4; - } - multiply(q: Quaternion): this { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm diff --git a/src/lib/math/Vector2.Functions.ts b/src/lib/math/Vector2.Functions.ts index a554e118..eeb40d6c 100644 --- a/src/lib/math/Vector2.Functions.ts +++ b/src/lib/math/Vector2.Functions.ts @@ -2,13 +2,12 @@ import { Vector2 } from "./Vector2"; import { Vector3 } from "./Vector3"; export function makeVector2FromBaryCoordWeights( - BaryCoord: Vector3, + baryCoord: Vector3, a: Vector2, b: Vector2, c: Vector2, result = new Vector2(), ): Vector2 { - result.x = a.x * BaryCoord.x + b.x * BaryCoord.y + c.x * BaryCoord.z; - result.y = a.y * BaryCoord.x + b.y * BaryCoord.y + c.y * BaryCoord.z; - return result; + const v = baryCoord; + return result.set(a.x * v.x + b.x * v.y + c.x * v.z, a.y * v.x + b.y * v.y + c.y * v.z); } diff --git a/src/lib/math/Vector3.Functions.ts b/src/lib/math/Vector3.Functions.ts index f04a99be..03926b28 100644 --- a/src/lib/math/Vector3.Functions.ts +++ b/src/lib/math/Vector3.Functions.ts @@ -42,14 +42,16 @@ export function pointToBaryCoords(a: Vector3, b: Vector3, c: Vector3, point: Vec } export function makeVector3FromBaryCoordWeights( - BaryCoord: Vector3, + baryCoord: Vector3, a: Vector3, b: Vector3, c: Vector3, result = new Vector3(), ): Vector3 { - result.x = a.x * BaryCoord.x + b.x * BaryCoord.y + c.x * BaryCoord.z; - result.y = a.y * BaryCoord.x + b.y * BaryCoord.y + c.y * BaryCoord.z; - result.z = a.z * BaryCoord.x + b.z * BaryCoord.y + c.z * BaryCoord.z; - return result; + const v = baryCoord; + return result.set( + a.x * v.x + b.x * v.y + c.x * v.z, + a.y * v.x + b.y * v.y + c.y * v.z, + a.z * v.x + b.z * v.y + c.z * v.z, + ); } diff --git a/src/lib/nodes/Node.ts b/src/lib/nodes/Node.ts index 5d9aa3fb..a22ff990 100644 --- a/src/lib/nodes/Node.ts +++ b/src/lib/nodes/Node.ts @@ -10,7 +10,6 @@ import { IDisposable, IIdentifiable, IVersionable } from "../core/types"; import { Euler } from "../math/Euler"; import { Matrix4 } from "../math/Matrix4"; import { composeMatrix4, makeMatrix4Inverse } from "../math/Matrix4.Functions"; -import { Quaternion } from "../math/Quaternion"; import { makeQuaternionFromEuler } from "../math/Quaternion.Functions"; import { Vector3 } from "../math/Vector3"; import { NodeCollection } from "./NodeCollection"; @@ -54,10 +53,10 @@ export class Node implements IIdentifiable, IVersionable, IDisposable { get localToParentTransform(): Matrix4 { if (this.#parentToLocalVersion !== this.version) { this.#localToParent = composeMatrix4( - this.#localToParent, this.position, - makeQuaternionFromEuler(new Quaternion(), this.rotation), + makeQuaternionFromEuler(this.rotation), this.scale, + this.#localToParent, ); this.#parentToLocalVersion = this.version; } diff --git a/src/lib/nodes/cameras/OrthographicCamera.ts b/src/lib/nodes/cameras/OrthographicCamera.ts index 8597d5a0..3413b8dc 100644 --- a/src/lib/nodes/cameras/OrthographicCamera.ts +++ b/src/lib/nodes/cameras/OrthographicCamera.ts @@ -6,7 +6,7 @@ // import { Matrix4 } from "../../math/Matrix4"; -import { makeMatrix4Orthographic } from "../../math/Matrix4.Functions"; +import { makeMatrix4OrthographicSimple } from "../../math/Matrix4.Functions"; import { Vector2 } from "../../math/Vector2"; import { Camera } from "./Camera"; @@ -21,15 +21,15 @@ export class OrthographicCamera extends Camera { super(); } - getProjection(viewAspectRatio = 1.0): Matrix4 { - const width = (this.height * viewAspectRatio * this.pixelAspectRatio) / this.zoom; - - const left = -width * 0.5 + this.center.x; - const right = left + width; - - const top = -this.height * 0.5 + this.center.y; - const bottom = top + this.height; - - return makeMatrix4Orthographic(new Matrix4(), left, right, top, bottom, this.near, this.far); + getProjection(viewAspectRatio = 1.0, result = new Matrix4()): Matrix4 { + return makeMatrix4OrthographicSimple( + this.height, + this.center, + this.near, + this.far, + this.zoom, + viewAspectRatio * this.pixelAspectRatio, + result, + ); } } diff --git a/src/lib/nodes/cameras/PerspectiveCamera.ts b/src/lib/nodes/cameras/PerspectiveCamera.ts index d59f597a..5395d1e9 100644 --- a/src/lib/nodes/cameras/PerspectiveCamera.ts +++ b/src/lib/nodes/cameras/PerspectiveCamera.ts @@ -6,7 +6,7 @@ // import { Matrix4 } from "../../math/Matrix4"; -import { makeMatrix4Perspective } from "../../math/Matrix4.Functions"; +import { makeMatrix4PerspectiveFov } from "../../math/Matrix4.Functions"; import { Camera } from "./Camera"; export class PerspectiveCamera extends Camera { @@ -14,16 +14,14 @@ export class PerspectiveCamera extends Camera { super(); } - getProjection(viewAspectRatio = 1.0): Matrix4 { - const height = (2.0 * this.near * Math.tan((this.verticalFov * Math.PI) / 180.0)) / this.zoom; - const width = height * this.pixelAspectRatio * viewAspectRatio; - - const left = -width * 0.5; - const right = left + width; - - const top = -height * 0.5; - const bottom = -top + height; - - return makeMatrix4Perspective(new Matrix4(), left, right, top, bottom, this.near, this.far); + getProjection(viewAspectRatio = 1.0, result = new Matrix4()): Matrix4 { + return makeMatrix4PerspectiveFov( + this.verticalFov, + this.near, + this.far, + this.zoom, + this.pixelAspectRatio * viewAspectRatio, + result, + ); } } diff --git a/src/lib/nodes/lights/Direction.ts b/src/lib/nodes/lights/Direction.ts index 3df3c60b..0a8ab601 100644 --- a/src/lib/nodes/lights/Direction.ts +++ b/src/lib/nodes/lights/Direction.ts @@ -1,10 +1,10 @@ -import { Euler } from "../../math/Euler"; +import { Euler, EulerOrder } from "../../math/Euler"; import { makeEulerFromRotationMatrix4 } from "../../math/Euler.Functions"; import { Matrix4 } from "../../math/Matrix4"; import { makeMatrix4RotationFromEuler } from "../../math/Matrix4.Functions"; import { Vector3 } from "../../math/Vector3"; -export function negativeZDirectionToEuler(d: Vector3): Euler { +export function negativeZDirectionToEuler(d: Vector3, result = new Euler()): Euler { // NOTE: This has never been tested. It may not work. // found on stackoverflow. console.warn("This has never been tested."); @@ -38,12 +38,12 @@ export function negativeZDirectionToEuler(d: Vector3): Euler { te[11] = 0; te[15] = 1; - return makeEulerFromRotationMatrix4(new Euler(), m); + return makeEulerFromRotationMatrix4(m, EulerOrder.Default, result); } -export function eulerToNegativeZDirection(e: Euler): Vector3 { +export function eulerToNegativeZDirection(e: Euler, result = new Vector3()): Vector3 { console.warn("This has never been tested."); - const m = makeMatrix4RotationFromEuler(new Matrix4(), e); + const m = makeMatrix4RotationFromEuler(e); const te = m.elements; - return new Vector3(te[2], te[6], te[10]); + return result.set(te[2], te[6], te[10]); } diff --git a/src/lib/renderers/webgl/framebuffers/CanvasFramebuffer.ts b/src/lib/renderers/webgl/framebuffers/CanvasFramebuffer.ts index fcd9559f..2bcc7e92 100644 --- a/src/lib/renderers/webgl/framebuffers/CanvasFramebuffer.ts +++ b/src/lib/renderers/webgl/framebuffers/CanvasFramebuffer.ts @@ -31,6 +31,10 @@ export class CanvasFramebuffer extends VirtualFramebuffer { return new Vector2(this.context.gl.drawingBufferWidth, this.context.gl.drawingBufferHeight); } + get aspectRatio(): number { + return this.context.gl.drawingBufferWidth / this.context.gl.drawingBufferHeight; + } + // eslint-disable-next-line @typescript-eslint/no-empty-function dispose(): void {}