diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 2370160..3ed52f1 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -1,7 +1,26 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -18,6 +37,9 @@
+
+
+
@@ -69,6 +91,8 @@
+
+
@@ -88,13 +112,20 @@
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/bundle.js b/build/bundle.js
index e5e6c5e..a535078 100644
--- a/build/bundle.js
+++ b/build/bundle.js
@@ -1 +1 @@
-!function(e){var t=window.webpackHotUpdate;window.webpackHotUpdate=function(e,r){!function(e,t){if(!O[e]||!w[e])return;for(var r in w[e]=!1,t)Object.prototype.hasOwnProperty.call(t,r)&&(d[r]=t[r]);0==--m&&0===g&&S()}(e,r),t&&t(e,r)};var r,n=!0,o="f2c88108809235be8897",i={},a=[],u=[];function c(e){var t=j[e];if(!t)return R;var n=function(n){return t.hot.active?(j[n]?-1===j[n].parents.indexOf(e)&&j[n].parents.push(e):(a=[e],r=n),-1===t.children.indexOf(n)&&t.children.push(n)):(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),a=[]),R(n)},o=function(e){return{configurable:!0,enumerable:!0,get:function(){return R[e]},set:function(t){R[e]=t}}};for(var i in R)Object.prototype.hasOwnProperty.call(R,i)&&"e"!==i&&"t"!==i&&Object.defineProperty(n,i,o(i));return n.e=function(e){return"ready"===f&&p("prepare"),g++,R.e(e).then(t,(function(e){throw t(),e}));function t(){g--,"prepare"===f&&(b[e]||P(e),0===g&&0===m&&S())}},n.t=function(e,t){return 1&t&&(e=n(e)),R.t(e,-2&t)},n}function l(t){var n={_acceptedDependencies:{},_declinedDependencies:{},_selfAccepted:!1,_selfDeclined:!1,_selfInvalidated:!1,_disposeHandlers:[],_main:r!==t,active:!0,accept:function(e,t){if(void 0===e)n._selfAccepted=!0;else if("function"==typeof e)n._selfAccepted=e;else if("object"==typeof e)for(var r=0;r=0&&n._disposeHandlers.splice(t,1)},invalidate:function(){switch(this._selfInvalidated=!0,f){case"idle":(d={})[t]=e[t],p("ready");break;case"ready":x(t);break;case"prepare":case"check":case"dispose":case"apply":(v=v||[]).push(t)}},check:k,apply:E,status:function(e){if(!e)return f;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var t=s.indexOf(e);t>=0&&s.splice(t,1)},data:i[t]};return r=void 0,n}var s=[],f="idle";function p(e){f=e;for(var t=0;t0;){var o=n.pop(),i=o.id,a=o.chain;if((s=j[i])&&(!s.hot._selfAccepted||s.hot._selfInvalidated)){if(s.hot._selfDeclined)return{type:"self-declined",chain:a,moduleId:i};if(s.hot._main)return{type:"unaccepted",chain:a,moduleId:i};for(var u=0;u ")),S.type){case"self-declined":n.onDeclined&&n.onDeclined(S),n.ignoreDeclined||(E=new Error("Aborted because of self decline: "+S.moduleId+M));break;case"declined":n.onDeclined&&n.onDeclined(S),n.ignoreDeclined||(E=new Error("Aborted because of declined dependency: "+S.moduleId+" in "+S.parentId+M));break;case"unaccepted":n.onUnaccepted&&n.onUnaccepted(S),n.ignoreUnaccepted||(E=new Error("Aborted because "+f+" is not accepted"+M));break;case"accepted":n.onAccepted&&n.onAccepted(S),x=!0;break;case"disposed":n.onDisposed&&n.onDisposed(S),T=!0;break;default:throw new Error("Unexception type "+S.type)}if(E)return p("abort"),Promise.reject(E);if(x)for(f in w[f]=d[f],m(b,S.outdatedModules),S.outdatedDependencies)Object.prototype.hasOwnProperty.call(S.outdatedDependencies,f)&&(g[f]||(g[f]=[]),m(g[f],S.outdatedDependencies[f]));T&&(m(b,[S.moduleId]),w[f]=k)}var I,D=[];for(c=0;c0;)if(f=F.pop(),s=j[f]){var B={},U=s.hot._disposeHandlers;for(l=0;l=0&&H.parents.splice(I,1))}}for(f in g)if(Object.prototype.hasOwnProperty.call(g,f)&&(s=j[f]))for(L=g[f],l=0;l=0&&s.children.splice(I,1);p("apply"),void 0!==y&&(o=y,y=void 0);for(f in d=void 0,w)Object.prototype.hasOwnProperty.call(w,f)&&(e[f]=w[f]);var V=null;for(f in g)if(Object.prototype.hasOwnProperty.call(g,f)&&(s=j[f])){L=g[f];var q=[];for(c=0;ce.length)&&(t=e.length);for(var r=0,n=new Array(t);re.length)&&(t=e.length);for(var r=0,n=new Array(t);r=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}function q(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r=this.tris.length&&(this.sinceLastTri+=e,this.sinceLastTri>100&&(this.tris.push(this.createRandomTriangle()),this.sinceLastTri=0));var t,r=V(this.tris);try{for(r.s();!(t=r.n()).done;){var n=t.value;this.renderTriangle(n)}}catch(e){r.e(e)}finally{r.f()}}},{key:"animate",value:function(e){var t=this,r=Date.now(),n=r-e;this.animateRandomTrisPopin(n),window.requestAnimationFrame((function(){return t.animate(r)}))}},{key:"run",value:function(){var e=this;this.initViewport(),this.useDefaultProgram(),window.requestAnimationFrame((function(){return e.animate(Date.now())}))}}])&&G(t.prototype,r),n&&G(t,n),e}();function W(e,t){var r=document.createElement("div");r.id=e.id,r.innerText=t,e.replaceWith(r)}window.onload=function(){var e=function(e,t){var r=document.querySelector("#root"),n=document.createElement("canvas");return n.id="main-stage",n.width=e,n.height=t,r.appendChild(n),n}(1024,720),t=e.getContext("webgl");if(t){var r=new z(t,e);r.onShutdown((function(t){return function(e,t){t===B.QUIT?W(e,"We're done here."):t===B.ERROR&&W(e,"Something unexpected happened and forced the program to shut down.")}(e,t)})),r.run()}else W(e,"Unable to initialize WebGL. Your browser or machine may not support it.")}}]);
\ No newline at end of file
+!function(e){var t=window.webpackHotUpdate;window.webpackHotUpdate=function(e,n){!function(e,t){if(!_[e]||!w[e])return;for(var n in w[e]=!1,t)Object.prototype.hasOwnProperty.call(t,n)&&(d[n]=t[n]);0==--g&&0===m&&S()}(e,n),t&&t(e,n)};var n,r=!0,o="96d6984be2e376a14bf8",i={},a=[],u=[];function c(e){var t=j[e];if(!t)return B;var r=function(r){return t.hot.active?(j[r]?-1===j[r].parents.indexOf(e)&&j[r].parents.push(e):(a=[e],n=r),-1===t.children.indexOf(r)&&t.children.push(r)):(console.warn("[HMR] unexpected require("+r+") from disposed module "+e),a=[]),B(r)},o=function(e){return{configurable:!0,enumerable:!0,get:function(){return B[e]},set:function(t){B[e]=t}}};for(var i in B)Object.prototype.hasOwnProperty.call(B,i)&&"e"!==i&&"t"!==i&&Object.defineProperty(r,i,o(i));return r.e=function(e){return"ready"===f&&h("prepare"),m++,B.e(e).then(t,(function(e){throw t(),e}));function t(){m--,"prepare"===f&&(b[e]||E(e),0===m&&0===g&&S())}},r.t=function(e,t){return 1&t&&(e=r(e)),B.t(e,-2&t)},r}function l(t){var r={_acceptedDependencies:{},_declinedDependencies:{},_selfAccepted:!1,_selfDeclined:!1,_selfInvalidated:!1,_disposeHandlers:[],_main:n!==t,active:!0,accept:function(e,t){if(void 0===e)r._selfAccepted=!0;else if("function"==typeof e)r._selfAccepted=e;else if("object"==typeof e)for(var n=0;n=0&&r._disposeHandlers.splice(t,1)},invalidate:function(){switch(this._selfInvalidated=!0,f){case"idle":(d={})[t]=e[t],h("ready");break;case"ready":A(t);break;case"prepare":case"check":case"dispose":case"apply":(v=v||[]).push(t)}},check:k,apply:P,status:function(e){if(!e)return f;s.push(e)},addStatusHandler:function(e){s.push(e)},removeStatusHandler:function(e){var t=s.indexOf(e);t>=0&&s.splice(t,1)},data:i[t]};return n=void 0,r}var s=[],f="idle";function h(e){f=e;for(var t=0;t0;){var o=r.pop(),i=o.id,a=o.chain;if((s=j[i])&&(!s.hot._selfAccepted||s.hot._selfInvalidated)){if(s.hot._selfDeclined)return{type:"self-declined",chain:a,moduleId:i};if(s.hot._main)return{type:"unaccepted",chain:a,moduleId:i};for(var u=0;u ")),S.type){case"self-declined":r.onDeclined&&r.onDeclined(S),r.ignoreDeclined||(P=new Error("Aborted because of self decline: "+S.moduleId+T));break;case"declined":r.onDeclined&&r.onDeclined(S),r.ignoreDeclined||(P=new Error("Aborted because of declined dependency: "+S.moduleId+" in "+S.parentId+T));break;case"unaccepted":r.onUnaccepted&&r.onUnaccepted(S),r.ignoreUnaccepted||(P=new Error("Aborted because "+f+" is not accepted"+T));break;case"accepted":r.onAccepted&&r.onAccepted(S),A=!0;break;case"disposed":r.onDisposed&&r.onDisposed(S),R=!0;break;default:throw new Error("Unexception type "+S.type)}if(P)return h("abort"),Promise.reject(P);if(A)for(f in w[f]=d[f],g(b,S.outdatedModules),S.outdatedDependencies)Object.prototype.hasOwnProperty.call(S.outdatedDependencies,f)&&(m[f]||(m[f]=[]),g(m[f],S.outdatedDependencies[f]));R&&(g(b,[S.moduleId]),w[f]=k)}var D,M=[];for(c=0;c0;)if(f=F.pop(),s=j[f]){var U={},L=s.hot._disposeHandlers;for(l=0;l=0&&H.parents.splice(D,1))}}for(f in m)if(Object.prototype.hasOwnProperty.call(m,f)&&(s=j[f]))for(C=m[f],l=0;l=0&&s.children.splice(D,1);h("apply"),void 0!==y&&(o=y,y=void 0);for(f in d=void 0,w)Object.prototype.hasOwnProperty.call(w,f)&&(e[f]=w[f]);var z=null;for(f in m)if(Object.prototype.hasOwnProperty.call(m,f)&&(s=j[f])){C=m[f];var G=[];for(c=0;c1e-5?this.scaled(1/e):c.Zeros()}},{key:"scaled",value:function(e){return new c(this.x()*e,this.y()*e,this.z()*e)}},{key:"length",value:function(){return this.lengthCacheDirty&&(this.len=Math.sqrt(this.dot(this)),this.lengthCacheDirty=!1),this.len}}])&&i(t.prototype,n),r&&i(t,r),c}(r);function p(e){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function d(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function y(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw i}}}}function he(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n any = () => {};
constructor(gl: WebGLRenderingContext, canvas: HTMLCanvasElement) {
this.gl = gl;
this.stage = canvas;
this.currentProgram = this.createDefaultProgram();
- this.perspMat = perspMat(90, this.stage.width / this.stage.height, 1.0, 2000.0);
this.setupGl();
}
private setupGl(): void {
this.gl.enable(this.gl.DEPTH_TEST);
- this.gl.enable(this.gl.CULL_FACE);
+ //this.gl.enable(this.gl.CULL_FACE);
+ //this.gl.frontFace(this.gl.CCW);
}
onShutdown(callback: (exitCode: ExitCode) => any): void {
@@ -45,6 +55,7 @@ class WebGlApp {
}
private shutdown(exitCode: ExitCode) {
+ this.running = false;
this.onShutdownCallback(exitCode);
}
@@ -52,13 +63,14 @@ class WebGlApp {
const program = this.createProgram(
this.createShaderFromString(this.gl.VERTEX_SHADER, vs_src),
this.createShaderFromString(this.gl.FRAGMENT_SHADER, fs_src));
- this.positionAttributeLocation = this.gl.getAttribLocation(program, "a_position");
+ this.a_position = this.gl.getAttribLocation(program, "a_position");
+ this.a_normal = this.gl.getAttribLocation(program, "a_normal");
this.u_colorLoc = this.gl.getUniformLocation(program, "u_color") as number;
this.u_trsMatrixLoc = this.gl.getUniformLocation(program, "u_trsMatrix") as number;
return program;
}
- private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader) {
+ private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {
const program: WebGLProgram = this.gl.createProgram() as WebGLProgram;
this.gl.attachShader(program, vertexShader);
this.gl.attachShader(program, fragmentShader);
@@ -89,12 +101,27 @@ class WebGlApp {
}
}
- private renderTriangle(triangle: Triangle2D) {
- this.gl.bufferData(this.gl.ARRAY_BUFFER, triangle.pointBuffer(), this.gl.STATIC_DRAW);
- this.gl.uniform4fv(this.u_colorLoc, triangle.colorVec());
+ private renderObject(object: Object) {
+ this.gl.enableVertexAttribArray(this.a_position);
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.pointBuffer!);
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, object.pointBuffer(), this.gl.STATIC_DRAW);
+ this.gl.vertexAttribPointer(
+ this.a_position, 3, this.gl.FLOAT, false, 0, 0);
+
+ this.gl.enableVertexAttribArray(this.a_normal);
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.normalBuffer!);
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, object.normalBuffer(), this.gl.STATIC_DRAW);
+ this.gl.vertexAttribPointer(
+ this.a_normal, 3, this.gl.FLOAT, false, 0, 0);
+
+ this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.elementBuffer!);
+ this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, object.elementBuffer(), this.gl.STATIC_DRAW);
+
+ this.gl.uniform4fv(this.u_colorLoc, object.getColor().vec());
this.gl.uniformMatrix4fv(this.u_trsMatrixLoc, false,
- triangle.getTrsMatrix().buffer());
- this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
+ this.useCamera(object.getTrsMatrix()).buffer());
+
+ this.gl.drawElements(this.gl.TRIANGLES, object.elementBuffer().length, this.gl.UNSIGNED_SHORT, 0);
}
private initViewport(): void {
@@ -105,70 +132,53 @@ class WebGlApp {
private useDefaultProgram(): void {
this.gl.useProgram(this.currentProgram);
- const positionBuffer = this.gl.createBuffer();
- this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
- this.gl.enableVertexAttribArray(this.positionAttributeLocation);
- this.gl.vertexAttribPointer(
- this.positionAttributeLocation, 3, this.gl.FLOAT, false, 0, 0);
+ this.pointBuffer = this.gl.createBuffer()!;
+ this.elementBuffer = this.gl.createBuffer()!;
+ this.normalBuffer = this.gl.createBuffer()!;
}
- private createRandomTriangle(): Triangle2D {
- return new Triangle2D(
- this.rand2DClipspacePoint(),
- this.rand2DClipspacePoint(),
- this.rand2DClipspacePoint(),
- this.randTrsMat(),
- this.randBasicColor()
- )
+ private useCamera(inputTrs: TrsMatrix): MatrixFour {
+ const projectionMatrix = MatrixFour.Perspective(
+ Math.PI,
+ this.stage.clientWidth / this.stage.clientHeight,
+ 1,
+ 2000);
+ const viewProjectionMatrix = projectionMatrix.times(this.camera.viewMat());
+ return viewProjectionMatrix.times(inputTrs);
}
- private randTrsMat(): TrsMatrix {
- return new TrsMatrix()
- .translateBy(this.randClipspaceInt() / 2, this.randClipspaceInt() / 2, this.randClipspaceInt() / 2)
- .rotateBy(this.randAngle(), this.randAngle(), this.randAngle())
- }
-
- private rand2DClipspacePoint(): Point2D {
- return new Point2D(this.randClipspaceInt() / 2, this.randClipspaceInt() / 2);
- }
-
- private randBasicColor(): BasicColor {
- return new BasicColor(Math.random(), Math.random(), Math.random());
- }
-
- private randAngle(): number {
- return Math.random() * Math.PI / 10;
- }
-
- private randClipspaceInt(): number {
- return Math.random() * 2 - 1;
- }
-
- private animateRandomTrisPopin(delta: number): void {
- if (this.maxTris >= this.tris.length) {
- this.sinceLastTri += delta;
- if (this.sinceLastTri > 100) {
- this.tris.push(this.createRandomTriangle());
- this.sinceLastTri = 0;
- }
+ private mainLoop(delta: number): void {
+ for (const object of this.objects) {
+ object.update(delta);
}
- for (const tri of this.tris) {
- this.renderTriangle(tri);
+ for (const object of this.objects) {
+ this.renderObject(object);
}
}
- private animate(then: number): void {
+ private frame(then: number): void {
const now = Date.now();
const delta = now - then;
- this.animateRandomTrisPopin(delta);
- window.requestAnimationFrame(() => this.animate(now));
+ this.mainLoop(delta);
+ if (this.running) {
+ window.requestAnimationFrame(() => this.frame(now));
+ }
+ }
+
+ private setupScene(): void {
+ const cone = new Cone(4, 100);
+ cone.setColor(new BasicColor(1.0, 0, 0, 1.0));
+ cone.scaleBy(100);
+ this.objects.push(cone);
+ this.camera.translateBy(100, 100, 100);
+ console.log(this.useCamera(cone.getTrsMatrix()));
}
run() {
this.initViewport();
this.useDefaultProgram();
- window.requestAnimationFrame(() => this.animate(Date.now()));
- //this.shutdown(ExitCode.QUIT);
+ this.setupScene();
+ window.requestAnimationFrame(() => this.frame(Date.now()));
}
}
diff --git a/src/BasicColor.ts b/src/BasicColor.ts
index fa6d199..681f579 100644
--- a/src/BasicColor.ts
+++ b/src/BasicColor.ts
@@ -1,9 +1,14 @@
+import {VectorSizeError} from "./Errors";
+import {Color, Vector} from "./types";
+
class BasicColor implements Vector, Color {
- private rep: Float32Array;
- constructor(r: number, g: number, b: number, a?: number) {
- this.rep = new Float32Array([r, g, b, a ?? 1.0]);
+ private readonly rep: Float32Array;
+ constructor();
+ constructor(r: number, g: number, b: number, a?: number);
+ constructor(...args: any[]) {
+ this.rep = new Float32Array([args[0] ?? 0.5, args[1] ?? 0.5, args[2] ?? 0.5, args[3] ?? 1.0]);
}
- static fromVec(vec: Float32Array): Color {
+ static fromVec(vec: ArrayLike): Color {
if (vec.length === 4) {
return new BasicColor(vec[0], vec[1], vec[2], vec[3]);
}
diff --git a/src/Camera.ts b/src/Camera.ts
new file mode 100644
index 0000000..941c53f
--- /dev/null
+++ b/src/Camera.ts
@@ -0,0 +1,66 @@
+import TrsMatrix from "./TrsMatrix";
+import Point3D from "./Point3D";
+import {Transformable} from "./types";
+import MatrixFour from "./MatrixFour";
+
+class Camera implements Transformable {
+ private trs: TrsMatrix;
+ constructor();
+ constructor(position: Point3D, lookAt: Point3D, up: Point3D);
+ constructor(radius: number, rotX: number, rotY: number, rotZ: number);
+ constructor(...args: any[]) {
+ this.trs = new TrsMatrix();
+ if (args[0] !== undefined) {
+ if (args[0] instanceof Point3D) {
+ this.trs.setPosition(args[0]);
+ this.lookAt(args[1], args[2]);
+ }
+ else {
+ this.rotateBy(args[1], args[2], args[3]);
+ this.translateBy(0, 0, args[0]);
+ }
+ }
+ }
+
+ translateBy(tX: number, tY: number, tZ: number): Camera {
+ this.trs.translateBy(tX, tY, tZ);
+ return this;
+ }
+
+ rotateBy(degX: number, degY: number, degZ: number): Camera {
+ this.trs.rotateBy(degX, degY, degZ);
+ return this;
+ }
+
+ scaleBy(sX: number, sY: number, sZ: number): Camera {
+ this.trs.scaleBy(sX, sY, sZ);
+ return this;
+ }
+
+ getTrsMatrix(): TrsMatrix {
+ return this.trs;
+ }
+
+ position(): Point3D {
+ return this.trs.position();
+ };
+
+ lookAt(other: Point3D, up: Point3D) {
+ const selfPosition = this.position();
+ const zAxis = selfPosition.minus(other).normalised();
+ const xAxis = up.cross(zAxis).normalised();
+ const yAxis = zAxis.cross(xAxis).normalised();
+ this.trs.setSlice([
+ xAxis.x(), xAxis.y(), xAxis.z(), 0,
+ yAxis.x(), yAxis.y(), yAxis.z(), 0,
+ zAxis.x(), zAxis.y(), zAxis.z(), 0,
+ selfPosition.x(), selfPosition.y(), selfPosition.z(), 1,
+ ], 0);
+ }
+
+ viewMat(): MatrixFour {
+ return this.trs.inverse();
+ }
+}
+
+export default Camera;
\ No newline at end of file
diff --git a/src/Cone.ts b/src/Cone.ts
new file mode 100644
index 0000000..27740f3
--- /dev/null
+++ b/src/Cone.ts
@@ -0,0 +1,106 @@
+import TrsMatrix from "./TrsMatrix";
+import BasicColor from "./BasicColor";
+import TransformableGeometry from "./TransformableGeometry";
+import {triNormal} from "./utils";
+import Point3D from "./Point3D";
+import {Color, Colored, GeometryParams, Transformable} from "./types";
+import {Updatable} from "./App";
+
+class Cone extends TransformableGeometry implements Transformable, Colored, Updatable {
+ private color: Color;
+ private height?: number;
+ private basePoints?: number;
+ private static DEFAULT_BASE_POINTS: number = 10;
+ private static DEFAULT_HEIGHT: number = 1;
+ constructor(geometry: GeometryParams, trsMatrix?: TrsMatrix, color?: Color);
+ constructor(height?: number, basePoints?: number, trsMatrix?: TrsMatrix, color?: Color | ArrayLike);
+ constructor(...args: any[]) {
+ let color;
+ if (typeof args[0] !== "number") {
+ super(args[0], args[1]);
+ color = args[2];
+ }
+ else {
+ super(Cone.generateGeometry(args[0], args[1]), args[2]);
+ this.height = args[0] ?? Cone.DEFAULT_HEIGHT;
+ this.basePoints = args[1] ?? Cone.DEFAULT_BASE_POINTS;
+ color = args[4] ?? new BasicColor();
+ }
+ if (color.hasOwnProperty("length")) {
+ this.color = BasicColor.fromVec(color as ArrayLike);
+ }
+ else {
+ this.color = color as Color;
+ }
+ }
+
+ private static generateGeometry(height: number, basePoints: number): GeometryParams {
+ basePoints = Math.abs(Math.ceil(basePoints));
+ const basePointsAngle = 2 * Math.PI / basePoints;
+ const pointBuffer = [
+ 0.0, 0.0, 0.0,
+ 0.0, 0.0, height,
+ 0.0, 1.0, 0.0
+ ];
+ const normalBuffer = [];
+ const elementBuffer = [];
+ for (let i = 1; i < basePoints; i++) {
+ pointBuffer.push(
+ Math.sin(basePointsAngle * i),
+ Math.cos(basePointsAngle * i),
+ 0.0
+ );
+ elementBuffer.push(0, 2 + i, 1 + i);
+ normalBuffer.push(
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+ 0.0, 0.0, -1.0,
+ );
+ elementBuffer.push(1, 1 + i, 2 + i);
+ normalBuffer.push(
+ ...triNormal(
+ new Point3D(pointBuffer.slice(1, 4)),
+ new Point3D(pointBuffer.slice(1 + i, 4 + i)),
+ new Point3D(pointBuffer.slice(2 + i, 5 + i)),
+ ).vec()
+ );
+ }
+ elementBuffer.push(0, 2, 1 + basePoints);
+ elementBuffer.push(1, 1 + basePoints, 2);
+ return {
+ pointBuffer: new Float32Array(pointBuffer),
+ normalBuffer: new Float32Array(normalBuffer),
+ elementBuffer: new Uint16Array(elementBuffer)
+ };
+ }
+
+ update(delta: number): void {
+ const secs = delta / 1000.0;
+ this.rotateBy(0, 0.05 * secs, 0);
+ }
+
+ setHeight(height: number) {
+ this.changeHeight(height - (this.height ?? 0));
+ }
+
+ changeHeight(deltaHeight: number) {
+ this.height = this.height ?? Cone.DEFAULT_HEIGHT + deltaHeight;
+ this.geometry = Cone.generateGeometry(this.height, this.basePoints ?? Cone.DEFAULT_BASE_POINTS);
+ }
+
+ setBasePoints(basePoints: number) {
+ this.height = basePoints;
+ this.geometry = Cone.generateGeometry(this.height, this.basePoints ?? Cone.DEFAULT_BASE_POINTS);
+ }
+
+ setColor(color: Color) {
+ this.color = color;
+ }
+
+ getColor(): Color {
+ return this.color;
+ }
+}
+
+
+export default Cone;
\ No newline at end of file
diff --git a/src/Matrix4.ts b/src/Matrix4.ts
deleted file mode 100644
index 236e8d9..0000000
--- a/src/Matrix4.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import {identityMatrix} from "./utils";
-
-class Matrix4 {
- protected readonly matrix: Float32Array;
- constructor(matrix: Float32Array | ArrayLike);
- constructor(...args: any[]) {
- if (args[0].length === 16) {
- if (!(args[0] instanceof Float32Array)) {
- args[0] = new Float32Array(args[0]);
- }
- this.matrix = args[0];
- }
- else {
- throw new VectorSizeError(16, args[0].length);
- }
- }
- times(other: Matrix4) {
- const result = new Matrix4(Array(16));
- const a = this.matrix;
- const b = other.matrix;
- const c = result.matrix;
- c[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
- c[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
- c[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
- c[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
- c[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
- c[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
- c[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
- c[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
- c[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
- c[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
- c[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
- c[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
- c[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
- c[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
- c[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
- c[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
- return result;
- }
- buffer(): Float32Array {
- return this.matrix.slice();
- }
- set(m: number, n: number, val: number): void {
- this.matrix[m * 4 + n] = val;
- }
- setRel(m: number, n: number, val: number): void {
- this.matrix[m * 4 + n] += val;
- }
- static Identity(): Matrix4 {
- return new Matrix4(identityMatrix(4));
- }
-}
-
-export default Matrix4;
\ No newline at end of file
diff --git a/src/MatrixFour.ts b/src/MatrixFour.ts
new file mode 100644
index 0000000..4607d9f
--- /dev/null
+++ b/src/MatrixFour.ts
@@ -0,0 +1,155 @@
+import {identityMatrix} from "./utils";
+import {VectorSizeError} from "./Errors";
+
+class MatrixFour {
+ protected matrix: Float32Array;
+ constructor();
+ constructor(matrix: Float32Array | ArrayLike);
+ constructor(...args: any[]) {
+ if (args[0]) {
+ if (args[0].length === 16) {
+ if (!(args[0] instanceof Float32Array)) {
+ args[0] = new Float32Array(args[0]);
+ }
+ this.matrix = args[0];
+ }
+ else {
+ throw new VectorSizeError(16, args[0].length);
+ }
+ }
+ else {
+ this.matrix = identityMatrix(4);
+ }
+ }
+
+ times(other: MatrixFour) {
+ const result = new MatrixFour(Array(16));
+ const a = this.matrix;
+ const b = other.matrix;
+ const c = result.matrix;
+ c[0] = a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12];
+ c[1] = a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13];
+ c[2] = a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14];
+ c[3] = a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15];
+ c[4] = a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12];
+ c[5] = a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13];
+ c[6] = a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14];
+ c[7] = a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15];
+ c[8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12];
+ c[9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13];
+ c[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14];
+ c[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15];
+ c[12] = a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12];
+ c[13] = a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13];
+ c[14] = a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14];
+ c[15] = a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15];
+ return result;
+ }
+
+ buffer(start?: number, stop?: number): Float32Array {
+ return this.matrix.slice(start, stop);
+ }
+
+ set(m: number, n: number, val: number): void {
+ this.matrix[m * 4 + n] = val;
+ }
+
+ setSlice(vals: number[], offset: number): void {
+ this.matrix.set(vals, offset);
+ }
+
+ setRel(m: number, n: number, val: number): void {
+ this.matrix[m * 4 + n] += val;
+ }
+
+ inverse(): MatrixFour {
+ const m = this.matrix;
+ const m00 = m[0];
+ const m01 = m[1];
+ const m02 = m[2];
+ const m03 = m[3];
+ const m10 = m[4];
+ const m11 = m[5];
+ const m12 = m[6];
+ const m13 = m[7];
+ const m20 = m[8];
+ const m21 = m[9];
+ const m22 = m[10];
+ const m23 = m[11];
+ const m30 = m[12];
+ const m31 = m[13];
+ const m32 = m[14];
+ const m33 = m[15];
+ const tmp_0 = m22 * m33;
+ const tmp_1 = m32 * m23;
+ const tmp_2 = m12 * m33;
+ const tmp_3 = m32 * m13;
+ const tmp_4 = m12 * m23;
+ const tmp_5 = m22 * m13;
+ const tmp_6 = m02 * m33;
+ const tmp_7 = m32 * m03;
+ const tmp_8 = m02 * m23;
+ const tmp_9 = m22 * m03;
+ const tmp_10 = m02 * m13;
+ const tmp_11 = m12 * m03;
+ const tmp_12 = m20 * m31;
+ const tmp_13 = m30 * m21;
+ const tmp_14 = m10 * m31;
+ const tmp_15 = m30 * m11;
+ const tmp_16 = m10 * m21;
+ const tmp_17 = m20 * m11;
+ const tmp_18 = m00 * m31;
+ const tmp_19 = m30 * m01;
+ const tmp_20 = m00 * m21;
+ const tmp_21 = m20 * m01;
+ const tmp_22 = m00 * m11;
+ const tmp_23 = m10 * m01;
+
+ const t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
+ const t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
+ const t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
+ const t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
+
+ const d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
+
+ const result = new MatrixFour([
+ d * t0,
+ d * t1,
+ d * t2,
+ d * t3,
+
+ d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
+ d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
+ d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
+ d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
+
+ d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
+ d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
+ d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
+ d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
+
+ d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
+ d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
+ d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
+ d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
+ ]);
+ return result;
+ }
+
+ static Perspective(fov: number, aspect: number, near: number, far: number): MatrixFour {
+ const f = Math.tan(Math.PI / 2 - fov / 2);
+ const rangeInv = 1.0 / (near - far);
+ return new MatrixFour(new Float32Array([
+ f / aspect, 0, 0, 0,
+ 0, f, 0, 0,
+ 0, 0, (near + far) * rangeInv, -1,
+ 0, 0, near * far * rangeInv * 2, 0
+ ]));
+ }
+
+ static Identity(): MatrixFour {
+ return new MatrixFour(identityMatrix(4));
+ }
+}
+
+export default MatrixFour;
\ No newline at end of file
diff --git a/src/Point2D.ts b/src/Point2D.ts
index b80bb8e..f4743bd 100644
--- a/src/Point2D.ts
+++ b/src/Point2D.ts
@@ -1,4 +1,5 @@
import PointVec from "./PointVec";
+import {VectorSizeError} from "./Errors";
class Point2D extends PointVec {
constructor(vec: Float32Array | ArrayLike);
diff --git a/src/Point3D.ts b/src/Point3D.ts
index f699da1..6c78240 100644
--- a/src/Point3D.ts
+++ b/src/Point3D.ts
@@ -1,32 +1,102 @@
import PointVec from "./PointVec";
+import {VectorSizeError} from "./Errors";
class Point3D extends PointVec {
+ private len: number;
+ private lengthCacheDirty: boolean;
constructor(vec: Float32Array | ArrayLike);
constructor(x: number, y: number, z: number);
- constructor(first: any, second?: any, third?: any) {
- super(first, second, third);
+ constructor(...args: any[]) {
+ if (typeof args[0] === "number") {
+ super(args[0], args[1], args[2]);
+ }
+ else {
+ if (args[0].length !== 3) {
+ throw new VectorSizeError(3, args[0].length);
+ }
+ else {
+ super(args[0][0], args[0][1], args[0][2]);
+ }
+ }
+ this.len = 0;
+ this.lengthCacheDirty = true;
}
- xOrd(): number {
+ x(): number {
return this.rep[0];
}
- yOrd(): number {
+ y(): number {
return this.rep[1];
}
- zOrd(): number {
+ z(): number {
return this.rep[2];
}
setX(x: number): void {
this.rep[0] = x;
+ this.lengthCacheDirty = false;
}
setY(y: number): void {
this.rep[1] = y;
+ this.lengthCacheDirty = false;
}
setZ(z: number): void {
this.rep[2] = z;
+ this.lengthCacheDirty = false;
+ }
+ set(point: Point3D): void {
+ this.setX(point.x());
+ this.setY(point.y());
+ this.setZ(point.z());
}
vec(): Float32Array {
return this.rep.slice();
}
+ cross(other: Point3D): Point3D {
+ return new Point3D(
+ this.y() * other.z() - this.z() * other.y(),
+ this.z() * other.x() - this.x() * other.z(),
+ this.x() * other.y() - this.y() * other.x(),
+ );
+ }
+ dot(other: Point3D): number {
+ return this.x() * other.x() + this.y() * other.y() + this.z() * other.z();
+ }
+ minus(other: Point3D): Point3D {
+ return new Point3D(
+ this.x() - other.x(),
+ this.y() - other.y(),
+ this.z() - other.z(),
+ )
+ }
+ plus(other: Point3D): Point3D {
+ return new Point3D(
+ this.x() + other.x(),
+ this.y() + other.y(),
+ this.z() + other.z(),
+ );
+ }
+ normalised(): Point3D {
+ const len = this.length();
+ if (len > 0.00001) {
+ return this.scaled(1 / len);
+ }
+ else {
+ return Point3D.Zeros();
+ }
+ }
+ scaled(scalar: number) {
+ return new Point3D(
+ this.x() * scalar,
+ this.y() * scalar,
+ this.z() * scalar,
+ );
+ }
+ length(): number {
+ if (this.lengthCacheDirty) {
+ this.len = Math.sqrt(this.dot(this));
+ this.lengthCacheDirty = false;
+ }
+ return this.len;
+ }
static Zeros(): Point3D {
return new Point3D(0.0, 0.0, 0.0);
}
diff --git a/src/PointVec.ts b/src/PointVec.ts
index 8668df0..2f258b7 100644
--- a/src/PointVec.ts
+++ b/src/PointVec.ts
@@ -1,22 +1,12 @@
+import {Vector} from "./types";
+
abstract class PointVec implements Vector {
protected rep: Float32Array;
- protected constructor(vec: Float32Array | ArrayLike);
- protected constructor(x: number, y: number, z: number);
- protected constructor(first: number | Float32Array | ArrayLike, second?: number, third?: number) {
- if (typeof first === "number") {
- if (typeof second !== "number" || typeof third !== "number"){
- throw new TypeError("Incorrect constructor call for Point3D");
- }
- else {
- this.rep = new Float32Array(3);
- this.rep[0] = first;
- this.rep[1] = second;
- this.rep[2] = third;
- }
- }
- else {
- this.rep = new Float32Array(first[2]);
- }
+ protected constructor(first: number, second: number, third: number) {
+ this.rep = new Float32Array(3);
+ this.rep[0] = first;
+ this.rep[1] = second;
+ this.rep[2] = third;
}
abstract vec(): Float32Array;
}
diff --git a/src/TransformableGeometry.ts b/src/TransformableGeometry.ts
new file mode 100644
index 0000000..fbc01f0
--- /dev/null
+++ b/src/TransformableGeometry.ts
@@ -0,0 +1,76 @@
+import TrsMatrix from "./TrsMatrix";
+import {VectorSizeError} from "./Errors";
+import {Geometry, GeometryParams, Transformable} from "./types";
+import Point3D from "./Point3D";
+
+class TransformableGeometry implements Transformable, Geometry {
+ protected geometry: GeometryParams;
+ protected readonly trsMatrix: TrsMatrix;
+ constructor(geometry: {
+ pointBuffer: Float32Array,
+ normalBuffer: Float32Array,
+ elementBuffer?: ArrayLike,
+ }, trsMatrix?: TrsMatrix) {
+ let elBuffer;
+ if (geometry.elementBuffer) {
+ elBuffer = new Uint16Array(geometry.elementBuffer);
+ }
+ else {
+ if (geometry.pointBuffer.length % 3 !== 0) {
+ throw new VectorSizeError("Multiple of 3 (no vertexOrder supplied)",
+ geometry.pointBuffer.length);
+ }
+ else {
+ elBuffer = new Uint16Array(Array(geometry.pointBuffer.length / 3).keys());
+ }
+ }
+ this.geometry = {
+ pointBuffer: geometry.pointBuffer,
+ normalBuffer: geometry.normalBuffer,
+ elementBuffer: elBuffer,
+ };
+ this.trsMatrix = trsMatrix ?? new TrsMatrix();
+ }
+
+ getTrsMatrix(): TrsMatrix {
+ return this.trsMatrix;
+ }
+
+ pointBuffer(): Float32Array {
+ return this.geometry.pointBuffer;
+ }
+
+ normalBuffer(): Float32Array {
+ return this.geometry.normalBuffer;
+ }
+
+ elementBuffer(): Uint16Array {
+ return this.geometry.elementBuffer;
+ }
+
+ position(): Point3D {
+ return this.trsMatrix.position();
+ }
+
+ translateBy(tX: number, tY: number, tZ: number): this {
+ this.trsMatrix.translateBy(tX, tY, tZ);
+ return this;
+ }
+
+ rotateBy(degX: number, degY: number, degZ: number): this {
+ this.trsMatrix.rotateBy(degX, degY, degZ);
+ return this;
+ }
+
+ scaleBy(xOrAll: number, sY?: number, sZ?: number): this {
+ if (sY && sZ) {
+ this.trsMatrix.scaleBy(xOrAll, sY, sZ);
+ }
+ else {
+ this.trsMatrix.scaleBy(xOrAll);
+ }
+ return this;
+ }
+}
+
+export default TransformableGeometry;
\ No newline at end of file
diff --git a/src/Triangle2D.ts b/src/Triangle2D.ts
index fff3494..ab20087 100644
--- a/src/Triangle2D.ts
+++ b/src/Triangle2D.ts
@@ -1,39 +1,56 @@
import TrsMatrix from "./TrsMatrix";
import Point2D from "./Point2D";
+import BasicColor from "./BasicColor";
+import {VectorSizeError} from "./Errors";
+import TransformableGeometry from "./TransformableGeometry";
+import {triNormal} from "./utils";
+import Point3D from "./Point3D";
+import {Color, Colored, Transformable} from "./types";
-class Triangle2D implements Transformable{
- private geometryRep: Float32Array;
+class Triangle2D extends TransformableGeometry implements Transformable, Colored {
private color: Color;
- private readonly trsMatrix: TrsMatrix;
- constructor(vec: Float32Array, trsMatrix?: TrsMatrix, color?: Color);
- constructor(p1: Point2D, p2: Point2D, p3: Point2D, trsMatrix?: TrsMatrix, color?: Color);
+ constructor(pointBuffer: Float32Array, trsMatrix?: TrsMatrix, color?: Color);
+ constructor(p1: Point2D, p2: Point2D, p3: Point2D, trsMatrix?: TrsMatrix, color?: Color | ArrayLike);
constructor(...args: any[]) {
+ let trsMatrix: TrsMatrix, pointBuff: Float32Array, color: Color | ArrayLike;
if (args[0] instanceof Float32Array) {
if (args[0].length !== 6 && args[0].length !== 8) {
throw new VectorSizeError("6 or 9", args[0].length)
}
else if (args[0].length === 6) {
- this.geometryRep = new Float32Array([
+ pointBuff = new Float32Array([
...args[0].slice(0, 2), 0.0,
...args[0].slice(2, 4), 0.0,
...args[0].slice(4, 6), 0.0
]);
}
else {
- this.geometryRep = args[0];
+ pointBuff = args[0];
}
- this.trsMatrix = args[1] ?? new TrsMatrix();
- this.color = args[2] ?? new BasicColor(0, 0, 0, 1.0);
+ trsMatrix = args[1] ?? new TrsMatrix();
+ color = args[2] ?? new BasicColor();
}
else {
- this.geometryRep = new Float32Array([...args[0].vec(), ...args[1].vec(), ...args[2].vec()]);
- this.trsMatrix = args[3] ?? new TrsMatrix();
- this.color = args[4] ?? new BasicColor(0, 0, 0, 1.0);
+ pointBuff = new Float32Array([...args[0].vec(), ...args[1].vec(), ...args[2].vec()]);
+ trsMatrix = args[3] ?? new TrsMatrix();
+ color = args[4] ?? new BasicColor();
+ }
+ super({
+ pointBuffer: pointBuff,
+ normalBuffer: new Float32Array(
+ triNormal(
+ new Point3D(pointBuff.slice(0, 3)),
+ new Point3D(pointBuff.slice(3, 6)),
+ new Point3D(pointBuff.slice(6, 9))
+ ).vec()
+ ),
+ }, trsMatrix);
+ if (color.hasOwnProperty("length")) {
+ this.color = BasicColor.fromVec(color as ArrayLike);
+ }
+ else {
+ this.color = color as Color;
}
- }
-
- colorVec(): Float32Array {
- return this.color.vec();
}
setColor(color: Color) {
@@ -44,18 +61,14 @@ class Triangle2D implements Transformable{
return this.color;
}
- getTrsMatrix(): TrsMatrix {
- return this.trsMatrix;
- }
-
private mapPointTo(point: Point2D | ArrayLike, x: number) {
if (point instanceof Point2D) {
- this.geometryRep[x] = point.xOrd();
- this.geometryRep[x + 1] = point.yOrd();
+ this.geometry.pointBuffer[x] = point.xOrd();
+ this.geometry.pointBuffer[x + 1] = point.yOrd();
}
else {
- this.geometryRep[x] = point[0];
- this.geometryRep[x + 1] = point[1];
+ this.geometry.pointBuffer[x] = point[0];
+ this.geometry.pointBuffer[x + 1] = point[1];
}
}
@@ -72,34 +85,6 @@ class Triangle2D implements Transformable{
this.mapPointTo(p3, 6);
}
}
-
- updatePointsFromVec(vec: Float32Array) {
- if (vec.length === 6) {
- this.geometryRep = vec;
- }
- else {
- throw new VectorSizeError(6, vec.length);
- }
- }
-
- pointBuffer(): Float32Array {
- return this.geometryRep;
- }
-
- translateBy(tX: number, tY: number, tZ: number): Triangle2D {
- this.trsMatrix.translateBy(tX, tY, tZ);
- return this;
- }
-
- rotateBy(degX: number, degY: number, degZ: number): Triangle2D {
- this.trsMatrix.rotateBy(degX, degY, degZ);
- return this;
- }
-
- scaleBy(sX: number, sY: number, sZ: number): Triangle2D {
- this.trsMatrix.scaleBy(sX, sY, sZ);
- return this;
- }
}
export default Triangle2D;
\ No newline at end of file
diff --git a/src/TrsMatrix.ts b/src/TrsMatrix.ts
index a89a0f5..1f71115 100644
--- a/src/TrsMatrix.ts
+++ b/src/TrsMatrix.ts
@@ -1,34 +1,44 @@
-import Matrix4 from "./Matrix4";
+import MatrixFour from "./MatrixFour";
+import Point3D from "./Point3D";
-class TrsMatrix implements Transformable {
- protected trs: Matrix4 = Matrix4.Identity();
+class TrsMatrix extends MatrixFour {
constructor();
- constructor(translation: Matrix4, rotX: Matrix4, rotY: Matrix4, rotZ: Matrix4, scale: Matrix4);
+ constructor(translation: MatrixFour, rotX: MatrixFour, rotY: MatrixFour, rotZ: MatrixFour, scale: MatrixFour);
constructor(mat: TrsMatrix);
constructor(...args: any[]) {
+ super();
if (args[0]) {
if (args[0] instanceof TrsMatrix) {
- this.trs = new Matrix4(args[0].buffer());
+ this.matrix = args[0].buffer();
} else {
- this.trs = args[0].times(args[1]).times(args[2]).times(args[3]).times(args[4])
+ this.matrix = args[0].times(args[1]).times(args[2]).times(args[3]).times(args[4])
}
}
}
- toMatrix4(): Matrix4 {
- return this.trs;
+
+ position(): Point3D {
+ return new Point3D(this.buffer(12, 15));
}
- buffer(): Float32Array {
- return this.trs.buffer();
+
+ setPosition(xOrPoint: number | Point3D, y?: number, z?: number): void {
+ if (xOrPoint instanceof Point3D) {
+ this.setSlice([xOrPoint.x(), xOrPoint.y(), xOrPoint.z()], 12);
+ }
+ else {
+ this.setSlice([xOrPoint, y ?? 0, z ?? 0], 12);
+ }
}
+
translateBy(tX: number, tY: number, tZ: number): TrsMatrix {
- this.trs = this.trs.times(new Matrix4([
+ this.matrix = this.times(new MatrixFour([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tX, tY, tZ, 1,
- ]));
+ ])).buffer();
return this;
}
+
rotateBy(degX: number, degY: number, degZ: number): TrsMatrix {
const sx = Math.sin(degX);
const cx = Math.cos(degX);
@@ -36,34 +46,39 @@ class TrsMatrix implements Transformable {
const cy = Math.cos(degY);
const sz = Math.sin(degZ);
const cz = Math.cos(degZ);
- this.trs = this.trs
- .times(new Matrix4([
+ this.matrix = this
+ .times(new MatrixFour([
1, 0, 0, 0,
0, cx, sx, 0,
0, -sx, cx, 0,
0, 0, 0, 1,
]))
- .times(new Matrix4([
+ .times(new MatrixFour([
cy, 0, -sy, 0,
0, 1, 0, 0,
sy, 0, cy, 0,
0, 0, 0, 1,
]))
- .times(new Matrix4([
+ .times(new MatrixFour([
cz, sz, 0, 0,
-sz, cz, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
- ]));
+ ])).buffer();
return this;
}
- scaleBy(sX: number, sY: number, sZ: number): TrsMatrix {
- this.trs = this.trs.times(new Matrix4([
- sX, 0, 0, 0,
- 0, sY, 0, 0,
- 0, 0, sZ, 0,
- 0, 0, 0, 1,
- ]));
+
+ scaleBy(xOrAll: number, sY?: number, sZ?: number): TrsMatrix {
+ if (sY === undefined || sZ === undefined) {
+ sY = xOrAll;
+ sZ = xOrAll;
+ }
+ this.matrix = this.times(new MatrixFour([
+ xOrAll, 0, 0, 0,
+ 0, sY, 0, 0,
+ 0, 0, sZ, 0,
+ 0, 0, 0, 1,
+ ])).buffer();
return this;
}
}
diff --git a/src/fragmentShader.glsl b/src/fragmentShader.glsl
index f79676f..93e3b1b 100644
--- a/src/fragmentShader.glsl
+++ b/src/fragmentShader.glsl
@@ -1,9 +1,12 @@
precision mediump float;
uniform vec4 u_color;
-
varying float v_depth;
+varying vec3 v_light;
+varying vec3 v_normal;
void main() {
- gl_FragColor = vec4(exp(v_depth), exp(v_depth), exp(v_depth), 1.0);
+ vec3 normal = normalize(v_normal);
+ float light = dot(normal, v_light);
+ gl_FragColor = u_color * (1.0 + light - light);
}
\ No newline at end of file
diff --git a/src/types.d.ts b/src/types.d.ts
index c61691f..e7bb457 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -1,3 +1,6 @@
+import TrsMatrix from "./TrsMatrix";
+import Point3D from "./Point3D";
+
declare module '*.glsl' {
const value: string;
export = value;
@@ -8,9 +11,16 @@ declare interface Vector {
}
declare interface Transformable {
- translateBy(tX: number, tY: number, tZ: number): any;
- rotateBy(degX: number, degY: number, degZ: number): any;
- scaleBy(sX: number, sY: number, sZ: number): any;
+ translateBy(tX: number, tY: number, tZ: number): Transformable;
+ rotateBy(degX: number, degY: number, degZ: number): Transformable;
+ scaleBy(sX: number, sY: number, sZ: number): Transformable;
+ getTrsMatrix(): TrsMatrix;
+ position(): Point3D;
+}
+
+declare interface Colored {
+ setColor(color: Color);
+ getColor(): Color;
}
declare interface Color extends Vector {
@@ -18,4 +28,16 @@ declare interface Color extends Vector {
green(): number;
blue(): number;
alpha(): number;
+}
+
+declare interface GeometryParams {
+ pointBuffer: Float32Array;
+ normalBuffer: Float32Array;
+ elementBuffer: Uint16Array;
+}
+
+declare interface Geometry {
+ pointBuffer(): Float32Array;
+ normalBuffer(): Float32Array;
+ elementBuffer(): Uint16Array;
}
\ No newline at end of file
diff --git a/src/utils.ts b/src/utils.ts
index 05da464..a6cc604 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,4 +1,9 @@
-import Matrix4 from "./Matrix4";
+import MatrixFour from "./MatrixFour";
+import Triangle2D from "./Triangle2D";
+import TrsMatrix from "./TrsMatrix";
+import Point2D from "./Point2D";
+import BasicColor from "./BasicColor";
+import Point3D from "./Point3D";
export function identityMatrix(size: number): Float32Array {
const mat = Array(size*size).fill(0);
@@ -8,14 +13,6 @@ export function identityMatrix(size: number): Float32Array {
));
}
-export function perspMat(fovDeg: number, aspect: number, near: number, far: number): Matrix4 {
- const f = Math.tan(Math.PI * 0.5 - 0.5 * fovDeg);
- const rangeInv = 1.0 / (near - far);
-
- return new Matrix4([
- f / aspect, 0, 0, 0,
- 0, f, 0, 0,
- 0, 0, (near + far) * rangeInv, -1,
- 0, 0, near * far * rangeInv * 2, 0
- ]);
+export function triNormal(p1: Point3D, p2: Point3D, p3: Point3D): Point3D {
+ return p2.minus(p1).cross(p3.minus(p1));
}
\ No newline at end of file
diff --git a/src/vertexShader.glsl b/src/vertexShader.glsl
index 5ca5b62..9110ced 100644
--- a/src/vertexShader.glsl
+++ b/src/vertexShader.glsl
@@ -1,11 +1,14 @@
attribute vec4 a_position;
-
+attribute vec4 a_normal;
uniform mat4 u_trsMatrix;
+varying vec3 v_light;
varying float v_depth;
-
+varying vec3 v_normal;
void main() {
+ v_light = vec3(0.0, 0.0, 1.0);
+ v_normal = (u_trsMatrix * a_normal).xyz;
vec4 pos = u_trsMatrix * a_position;
v_depth = pos.z;
gl_Position = pos;