Big update

This commit is contained in:
Daniel Ledda
2020-08-06 16:55:53 +02:00
parent d8a21ca50e
commit 32a62f6d98
19 changed files with 736 additions and 249 deletions

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

45
.idea/workspace.xml generated
View File

@@ -1,7 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="dc704aa3-69bc-4c4e-bfa3-31f248741e3c" name="Default Changelist" comment="" /> <list default="true" id="dc704aa3-69bc-4c4e-bfa3-31f248741e3c" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/Camera.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/Cone.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/MatrixFour.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/TransformableGeometry.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/bundle.js" beforeDir="false" afterPath="$PROJECT_DIR$/build/bundle.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/App.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/BasicColor.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/BasicColor.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Matrix4.ts" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/Point2D.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/Point2D.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Point3D.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/Point3D.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/PointVec.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/PointVec.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Triangle2D.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/Triangle2D.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/TrsMatrix.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/TrsMatrix.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/fragmentShader.glsl" beforeDir="false" afterPath="$PROJECT_DIR$/src/fragmentShader.glsl" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/types.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/types.d.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/utils.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/utils.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/vertexShader.glsl" beforeDir="false" afterPath="$PROJECT_DIR$/src/vertexShader.glsl" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -18,6 +37,9 @@
</list> </list>
</option> </option>
</component> </component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="1dJrR6XMWeePLne62dJk97OPzOb" /> <component name="ProjectId" id="1dJrR6XMWeePLne62dJk97OPzOb" />
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
@@ -69,6 +91,8 @@
<workItem from="1592157594053" duration="10162000" /> <workItem from="1592157594053" duration="10162000" />
<workItem from="1592213404810" duration="33877000" /> <workItem from="1592213404810" duration="33877000" />
<workItem from="1592384389528" duration="612000" /> <workItem from="1592384389528" duration="612000" />
<workItem from="1593163095834" duration="18917000" />
<workItem from="1593248452910" duration="13264000" />
</task> </task>
<servers /> <servers />
</component> </component>
@@ -88,13 +112,20 @@
<screen x="0" y="540" width="1080" height="1380" /> <screen x="0" y="540" width="1080" height="1380" />
</state> </state>
<state x="0" y="721" key="SettingsEditor/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592157574185" /> <state x="0" y="721" key="SettingsEditor/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592157574185" />
<state x="204" y="456" width="672" height="677" key="run.anything.popup" timestamp="1592164107251"> <state x="608" y="250" width="704" height="578" key="find.popup" timestamp="1593260457178">
<screen x="0" y="540" width="1080" height="1380" />
</state>
<state x="204" y="456" width="672" height="677" key="run.anything.popup/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592164107251" />
<state x="992" y="237" width="672" height="678" key="search.everywhere.popup" timestamp="1592225020545">
<screen x="0" y="27" width="1920" height="1053" /> <screen x="0" y="27" width="1920" height="1053" />
</state> </state>
<state x="992" y="237" width="672" height="678" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1592225020545" /> <state x="608" y="250" width="704" height="578" key="find.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1593260457178" />
<state x="363" y="-37" width="1195" height="517" key="run.anything.popup" timestamp="1593262773357">
<screen x="0" y="27" width="1920" height="1053" />
</state>
<state x="204" y="456" width="672" height="677" key="run.anything.popup/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592164107251" />
<state x="363" y="-37" width="1195" height="517" key="run.anything.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1593262773357" />
<state x="2283" y="-66" width="1195" height="531" key="run.anything.popup/1920.0.1920.1080/0.27.1920.1053@1920.0.1920.1080" timestamp="1593189438638" />
<state x="2912" y="215" width="672" height="696" key="search.everywhere.popup" timestamp="1593184656351">
<screen x="1920" y="0" width="1920" height="1080" />
</state>
<state x="992" y="237" width="672" height="678" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1593175537121" />
<state x="2912" y="215" width="672" height="696" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@1920.0.1920.1080" timestamp="1593184656351" />
</component> </component>
</project> </project>

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,14 @@
import vs_src from "./vertexShader.glsl"; import vs_src from "./vertexShader.glsl";
import fs_src from "./fragmentShader.glsl"; import fs_src from "./fragmentShader.glsl";
import Matrix4 from "./Matrix4"; import {Color, Colored, Geometry, Transformable} from "./types";
import {perspMat} from "./utils"; import MatrixFour from "./MatrixFour";
import Triangle2D from "./Triangle2D"; import Triangle2D from "./Triangle2D";
import TrsMatrix from "./TrsMatrix";
import Point2D from "./Point2D";
import BasicColor from "./BasicColor"; import BasicColor from "./BasicColor";
import {ProgramLinkingError, ShaderCompilationError} from "./Errors"; import {ProgramLinkingError, ShaderCompilationError} from "./Errors";
import Cone from "./Cone";
import TrsMatrix from "./TrsMatrix";
import Camera from "./Camera";
import Point3D from "./Point3D";
export enum ExitCode { export enum ExitCode {
ERROR, ERROR,
@@ -15,29 +17,37 @@ export enum ExitCode {
WEBGL_ERROR WEBGL_ERROR
} }
export interface Updatable {
update(delta: number): void;
}
type Object = Transformable & Geometry & Colored & Updatable;
class WebGlApp { class WebGlApp {
private gl: WebGLRenderingContext; private gl: WebGLRenderingContext;
private stage: HTMLCanvasElement; private stage: HTMLCanvasElement;
private perspMat: Matrix4; private normalBuffer?: WebGLBuffer;
private pointBuffer?: WebGLBuffer;
private elementBuffer?: WebGLBuffer;
private currentProgram: WebGLProgram | null; private currentProgram: WebGLProgram | null;
private positionAttributeLocation: number = 0; private a_position: number = 0;
private a_normal: number = 0;
private u_colorLoc: number = 0; private u_colorLoc: number = 0;
private u_trsMatrixLoc: number = 0; private u_trsMatrixLoc: number = 0;
private maxTris: number = 1000; private objects: Object[] = [];
private tris: Triangle2D[] = []; private camera: Camera = new Camera();
private sinceLastTri: number = 0; private running: boolean = true;
private onShutdownCallback: (exitCode: ExitCode) => any = () => {}; private onShutdownCallback: (exitCode: ExitCode) => any = () => {};
constructor(gl: WebGLRenderingContext, canvas: HTMLCanvasElement) { constructor(gl: WebGLRenderingContext, canvas: HTMLCanvasElement) {
this.gl = gl; this.gl = gl;
this.stage = canvas; this.stage = canvas;
this.currentProgram = this.createDefaultProgram(); this.currentProgram = this.createDefaultProgram();
this.perspMat = perspMat(90, this.stage.width / this.stage.height, 1.0, 2000.0);
this.setupGl(); this.setupGl();
} }
private setupGl(): void { private setupGl(): void {
this.gl.enable(this.gl.DEPTH_TEST); 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 { onShutdown(callback: (exitCode: ExitCode) => any): void {
@@ -45,6 +55,7 @@ class WebGlApp {
} }
private shutdown(exitCode: ExitCode) { private shutdown(exitCode: ExitCode) {
this.running = false;
this.onShutdownCallback(exitCode); this.onShutdownCallback(exitCode);
} }
@@ -52,13 +63,14 @@ class WebGlApp {
const program = this.createProgram( const program = this.createProgram(
this.createShaderFromString(this.gl.VERTEX_SHADER, vs_src), this.createShaderFromString(this.gl.VERTEX_SHADER, vs_src),
this.createShaderFromString(this.gl.FRAGMENT_SHADER, fs_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_colorLoc = this.gl.getUniformLocation(program, "u_color") as number;
this.u_trsMatrixLoc = this.gl.getUniformLocation(program, "u_trsMatrix") as number; this.u_trsMatrixLoc = this.gl.getUniformLocation(program, "u_trsMatrix") as number;
return program; return program;
} }
private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader) { private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram {
const program: WebGLProgram = this.gl.createProgram() as WebGLProgram; const program: WebGLProgram = this.gl.createProgram() as WebGLProgram;
this.gl.attachShader(program, vertexShader); this.gl.attachShader(program, vertexShader);
this.gl.attachShader(program, fragmentShader); this.gl.attachShader(program, fragmentShader);
@@ -89,12 +101,27 @@ class WebGlApp {
} }
} }
private renderTriangle(triangle: Triangle2D) { private renderObject(object: Object) {
this.gl.bufferData(this.gl.ARRAY_BUFFER, triangle.pointBuffer(), this.gl.STATIC_DRAW); this.gl.enableVertexAttribArray(this.a_position);
this.gl.uniform4fv(this.u_colorLoc, triangle.colorVec()); 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, this.gl.uniformMatrix4fv(this.u_trsMatrixLoc, false,
triangle.getTrsMatrix().buffer()); this.useCamera(object.getTrsMatrix()).buffer());
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
this.gl.drawElements(this.gl.TRIANGLES, object.elementBuffer().length, this.gl.UNSIGNED_SHORT, 0);
} }
private initViewport(): void { private initViewport(): void {
@@ -105,70 +132,53 @@ class WebGlApp {
private useDefaultProgram(): void { private useDefaultProgram(): void {
this.gl.useProgram(this.currentProgram); this.gl.useProgram(this.currentProgram);
const positionBuffer = this.gl.createBuffer(); this.pointBuffer = this.gl.createBuffer()!;
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer); this.elementBuffer = this.gl.createBuffer()!;
this.gl.enableVertexAttribArray(this.positionAttributeLocation); this.normalBuffer = this.gl.createBuffer()!;
this.gl.vertexAttribPointer(
this.positionAttributeLocation, 3, this.gl.FLOAT, false, 0, 0);
} }
private createRandomTriangle(): Triangle2D { private useCamera(inputTrs: TrsMatrix): MatrixFour {
return new Triangle2D( const projectionMatrix = MatrixFour.Perspective(
this.rand2DClipspacePoint(), Math.PI,
this.rand2DClipspacePoint(), this.stage.clientWidth / this.stage.clientHeight,
this.rand2DClipspacePoint(), 1,
this.randTrsMat(), 2000);
this.randBasicColor() const viewProjectionMatrix = projectionMatrix.times(this.camera.viewMat());
) return viewProjectionMatrix.times(inputTrs);
} }
private randTrsMat(): TrsMatrix { private mainLoop(delta: number): void {
return new TrsMatrix() for (const object of this.objects) {
.translateBy(this.randClipspaceInt() / 2, this.randClipspaceInt() / 2, this.randClipspaceInt() / 2) object.update(delta);
.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;
}
} }
for (const tri of this.tris) { for (const object of this.objects) {
this.renderTriangle(tri); this.renderObject(object);
} }
} }
private animate(then: number): void { private frame(then: number): void {
const now = Date.now(); const now = Date.now();
const delta = now - then; const delta = now - then;
this.animateRandomTrisPopin(delta); this.mainLoop(delta);
window.requestAnimationFrame(() => this.animate(now)); 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() { run() {
this.initViewport(); this.initViewport();
this.useDefaultProgram(); this.useDefaultProgram();
window.requestAnimationFrame(() => this.animate(Date.now())); this.setupScene();
//this.shutdown(ExitCode.QUIT); window.requestAnimationFrame(() => this.frame(Date.now()));
} }
} }

View File

@@ -1,9 +1,14 @@
import {VectorSizeError} from "./Errors";
import {Color, Vector} from "./types";
class BasicColor implements Vector, Color { class BasicColor implements Vector, Color {
private rep: Float32Array; private readonly rep: Float32Array;
constructor(r: number, g: number, b: number, a?: number) { constructor();
this.rep = new Float32Array([r, g, b, a ?? 1.0]); 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<number>): Color {
if (vec.length === 4) { if (vec.length === 4) {
return new BasicColor(vec[0], vec[1], vec[2], vec[3]); return new BasicColor(vec[0], vec[1], vec[2], vec[3]);
} }

66
src/Camera.ts Normal file
View File

@@ -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;

106
src/Cone.ts Normal file
View File

@@ -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<number>);
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<number>);
}
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;

View File

@@ -1,54 +0,0 @@
import {identityMatrix} from "./utils";
class Matrix4 {
protected readonly matrix: Float32Array;
constructor(matrix: Float32Array | ArrayLike<number>);
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<number>(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;

155
src/MatrixFour.ts Normal file
View File

@@ -0,0 +1,155 @@
import {identityMatrix} from "./utils";
import {VectorSizeError} from "./Errors";
class MatrixFour {
protected matrix: Float32Array;
constructor();
constructor(matrix: Float32Array | ArrayLike<number>);
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<number>(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;

View File

@@ -1,4 +1,5 @@
import PointVec from "./PointVec"; import PointVec from "./PointVec";
import {VectorSizeError} from "./Errors";
class Point2D extends PointVec { class Point2D extends PointVec {
constructor(vec: Float32Array | ArrayLike<number>); constructor(vec: Float32Array | ArrayLike<number>);

View File

@@ -1,32 +1,102 @@
import PointVec from "./PointVec"; import PointVec from "./PointVec";
import {VectorSizeError} from "./Errors";
class Point3D extends PointVec { class Point3D extends PointVec {
private len: number;
private lengthCacheDirty: boolean;
constructor(vec: Float32Array | ArrayLike<number>); constructor(vec: Float32Array | ArrayLike<number>);
constructor(x: number, y: number, z: number); constructor(x: number, y: number, z: number);
constructor(first: any, second?: any, third?: any) { constructor(...args: any[]) {
super(first, second, third); 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]; return this.rep[0];
} }
yOrd(): number { y(): number {
return this.rep[1]; return this.rep[1];
} }
zOrd(): number { z(): number {
return this.rep[2]; return this.rep[2];
} }
setX(x: number): void { setX(x: number): void {
this.rep[0] = x; this.rep[0] = x;
this.lengthCacheDirty = false;
} }
setY(y: number): void { setY(y: number): void {
this.rep[1] = y; this.rep[1] = y;
this.lengthCacheDirty = false;
} }
setZ(z: number): void { setZ(z: number): void {
this.rep[2] = z; 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 { vec(): Float32Array {
return this.rep.slice(); 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 { static Zeros(): Point3D {
return new Point3D(0.0, 0.0, 0.0); return new Point3D(0.0, 0.0, 0.0);
} }

View File

@@ -1,22 +1,12 @@
import {Vector} from "./types";
abstract class PointVec implements Vector { abstract class PointVec implements Vector {
protected rep: Float32Array; protected rep: Float32Array;
protected constructor(vec: Float32Array | ArrayLike<number>); protected constructor(first: number, second: number, third: number) {
protected constructor(x: number, y: number, z: number); this.rep = new Float32Array(3);
protected constructor(first: number | Float32Array | ArrayLike<number>, second?: number, third?: number) { this.rep[0] = first;
if (typeof first === "number") { this.rep[1] = second;
if (typeof second !== "number" || typeof third !== "number"){ this.rep[2] = third;
throw new TypeError("Incorrect constructor call for Point3D");
}
else {
this.rep = new Float32Array(3);
this.rep[0] = first;
this.rep[1] = <number>second;
this.rep[2] = <number>third;
}
}
else {
this.rep = new Float32Array(first[2]);
}
} }
abstract vec(): Float32Array; abstract vec(): Float32Array;
} }

View File

@@ -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<number>,
}, 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;

View File

@@ -1,39 +1,56 @@
import TrsMatrix from "./TrsMatrix"; import TrsMatrix from "./TrsMatrix";
import Point2D from "./Point2D"; 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{ class Triangle2D extends TransformableGeometry implements Transformable, Colored {
private geometryRep: Float32Array;
private color: Color; private color: Color;
private readonly trsMatrix: TrsMatrix; constructor(pointBuffer: Float32Array, trsMatrix?: TrsMatrix, color?: Color);
constructor(vec: Float32Array, trsMatrix?: TrsMatrix, color?: Color); constructor(p1: Point2D, p2: Point2D, p3: Point2D, trsMatrix?: TrsMatrix, color?: Color | ArrayLike<any>);
constructor(p1: Point2D, p2: Point2D, p3: Point2D, trsMatrix?: TrsMatrix, color?: Color);
constructor(...args: any[]) { constructor(...args: any[]) {
let trsMatrix: TrsMatrix, pointBuff: Float32Array, color: Color | ArrayLike<number>;
if (args[0] instanceof Float32Array) { if (args[0] instanceof Float32Array) {
if (args[0].length !== 6 && args[0].length !== 8) { if (args[0].length !== 6 && args[0].length !== 8) {
throw new VectorSizeError("6 or 9", args[0].length) throw new VectorSizeError("6 or 9", args[0].length)
} }
else if (args[0].length === 6) { else if (args[0].length === 6) {
this.geometryRep = new Float32Array([ pointBuff = new Float32Array([
...args[0].slice(0, 2), 0.0, ...args[0].slice(0, 2), 0.0,
...args[0].slice(2, 4), 0.0, ...args[0].slice(2, 4), 0.0,
...args[0].slice(4, 6), 0.0 ...args[0].slice(4, 6), 0.0
]); ]);
} }
else { else {
this.geometryRep = args[0]; pointBuff = args[0];
} }
this.trsMatrix = args[1] ?? new TrsMatrix(); trsMatrix = args[1] ?? new TrsMatrix();
this.color = args[2] ?? new BasicColor(0, 0, 0, 1.0); color = args[2] ?? new BasicColor();
} }
else { else {
this.geometryRep = new Float32Array([...args[0].vec(), ...args[1].vec(), ...args[2].vec()]); pointBuff = new Float32Array([...args[0].vec(), ...args[1].vec(), ...args[2].vec()]);
this.trsMatrix = args[3] ?? new TrsMatrix(); trsMatrix = args[3] ?? new TrsMatrix();
this.color = args[4] ?? new BasicColor(0, 0, 0, 1.0); 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<number>);
}
else {
this.color = color as Color;
} }
}
colorVec(): Float32Array {
return this.color.vec();
} }
setColor(color: Color) { setColor(color: Color) {
@@ -44,18 +61,14 @@ class Triangle2D implements Transformable{
return this.color; return this.color;
} }
getTrsMatrix(): TrsMatrix {
return this.trsMatrix;
}
private mapPointTo(point: Point2D | ArrayLike<number>, x: number) { private mapPointTo(point: Point2D | ArrayLike<number>, x: number) {
if (point instanceof Point2D) { if (point instanceof Point2D) {
this.geometryRep[x] = point.xOrd(); this.geometry.pointBuffer[x] = point.xOrd();
this.geometryRep[x + 1] = point.yOrd(); this.geometry.pointBuffer[x + 1] = point.yOrd();
} }
else { else {
this.geometryRep[x] = point[0]; this.geometry.pointBuffer[x] = point[0];
this.geometryRep[x + 1] = point[1]; this.geometry.pointBuffer[x + 1] = point[1];
} }
} }
@@ -72,34 +85,6 @@ class Triangle2D implements Transformable{
this.mapPointTo(p3, 6); 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; export default Triangle2D;

View File

@@ -1,34 +1,44 @@
import Matrix4 from "./Matrix4"; import MatrixFour from "./MatrixFour";
import Point3D from "./Point3D";
class TrsMatrix implements Transformable { class TrsMatrix extends MatrixFour {
protected trs: Matrix4 = Matrix4.Identity();
constructor(); 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(mat: TrsMatrix);
constructor(...args: any[]) { constructor(...args: any[]) {
super();
if (args[0]) { if (args[0]) {
if (args[0] instanceof TrsMatrix) { if (args[0] instanceof TrsMatrix) {
this.trs = new Matrix4(args[0].buffer()); this.matrix = args[0].buffer();
} else { } 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 { 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, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
tX, tY, tZ, 1, tX, tY, tZ, 1,
])); ])).buffer();
return this; return this;
} }
rotateBy(degX: number, degY: number, degZ: number): TrsMatrix { rotateBy(degX: number, degY: number, degZ: number): TrsMatrix {
const sx = Math.sin(degX); const sx = Math.sin(degX);
const cx = Math.cos(degX); const cx = Math.cos(degX);
@@ -36,34 +46,39 @@ class TrsMatrix implements Transformable {
const cy = Math.cos(degY); const cy = Math.cos(degY);
const sz = Math.sin(degZ); const sz = Math.sin(degZ);
const cz = Math.cos(degZ); const cz = Math.cos(degZ);
this.trs = this.trs this.matrix = this
.times(new Matrix4([ .times(new MatrixFour([
1, 0, 0, 0, 1, 0, 0, 0,
0, cx, sx, 0, 0, cx, sx, 0,
0, -sx, cx, 0, 0, -sx, cx, 0,
0, 0, 0, 1, 0, 0, 0, 1,
])) ]))
.times(new Matrix4([ .times(new MatrixFour([
cy, 0, -sy, 0, cy, 0, -sy, 0,
0, 1, 0, 0, 0, 1, 0, 0,
sy, 0, cy, 0, sy, 0, cy, 0,
0, 0, 0, 1, 0, 0, 0, 1,
])) ]))
.times(new Matrix4([ .times(new MatrixFour([
cz, sz, 0, 0, cz, sz, 0, 0,
-sz, cz, 0, 0, -sz, cz, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 1, 0, 0, 0, 1,
])); ])).buffer();
return this; return this;
} }
scaleBy(sX: number, sY: number, sZ: number): TrsMatrix {
this.trs = this.trs.times(new Matrix4([ scaleBy(xOrAll: number, sY?: number, sZ?: number): TrsMatrix {
sX, 0, 0, 0, if (sY === undefined || sZ === undefined) {
0, sY, 0, 0, sY = xOrAll;
0, 0, sZ, 0, sZ = xOrAll;
0, 0, 0, 1, }
])); 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; return this;
} }
} }

View File

@@ -1,9 +1,12 @@
precision mediump float; precision mediump float;
uniform vec4 u_color; uniform vec4 u_color;
varying float v_depth; varying float v_depth;
varying vec3 v_light;
varying vec3 v_normal;
void main() { 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);
} }

28
src/types.d.ts vendored
View File

@@ -1,3 +1,6 @@
import TrsMatrix from "./TrsMatrix";
import Point3D from "./Point3D";
declare module '*.glsl' { declare module '*.glsl' {
const value: string; const value: string;
export = value; export = value;
@@ -8,9 +11,16 @@ declare interface Vector {
} }
declare interface Transformable { declare interface Transformable {
translateBy(tX: number, tY: number, tZ: number): any; translateBy(tX: number, tY: number, tZ: number): Transformable;
rotateBy(degX: number, degY: number, degZ: number): any; rotateBy(degX: number, degY: number, degZ: number): Transformable;
scaleBy(sX: number, sY: number, sZ: number): any; 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 { declare interface Color extends Vector {
@@ -18,4 +28,16 @@ declare interface Color extends Vector {
green(): number; green(): number;
blue(): number; blue(): number;
alpha(): number; alpha(): number;
}
declare interface GeometryParams {
pointBuffer: Float32Array;
normalBuffer: Float32Array;
elementBuffer: Uint16Array;
}
declare interface Geometry {
pointBuffer(): Float32Array;
normalBuffer(): Float32Array;
elementBuffer(): Uint16Array;
} }

View File

@@ -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 { export function identityMatrix(size: number): Float32Array {
const mat = Array<number>(size*size).fill(0); const mat = Array<number>(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 { export function triNormal(p1: Point3D, p2: Point3D, p3: Point3D): Point3D {
const f = Math.tan(Math.PI * 0.5 - 0.5 * fovDeg); return p2.minus(p1).cross(p3.minus(p1));
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
]);
} }

View File

@@ -1,11 +1,14 @@
attribute vec4 a_position; attribute vec4 a_position;
attribute vec4 a_normal;
uniform mat4 u_trsMatrix; uniform mat4 u_trsMatrix;
varying vec3 v_light;
varying float v_depth; varying float v_depth;
varying vec3 v_normal;
void main() { void main() {
v_light = vec3(0.0, 0.0, 1.0);
v_normal = (u_trsMatrix * a_normal).xyz;
vec4 pos = u_trsMatrix * a_position; vec4 pos = u_trsMatrix * a_position;
v_depth = pos.z; v_depth = pos.z;
gl_Position = pos; gl_Position = pos;