First commit
This commit is contained in:
7
.babelrc
Normal file
7
.babelrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/typescript",
|
||||
"@babel/env"
|
||||
],
|
||||
"plugins": ["@babel/proposal-class-properties"]
|
||||
}
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/webgl.iml" filepath="$PROJECT_DIR$/.idea/webgl.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/webgl.iml
generated
Normal file
8
.idea/webgl.iml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
100
.idea/workspace.xml
generated
Normal file
100
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="dc704aa3-69bc-4c4e-bfa3-31f248741e3c" name="Default Changelist" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="package.json" />
|
||||
<option value="HTML File" />
|
||||
<option value="CSS File" />
|
||||
<option value="GLSL Shader" />
|
||||
<option value="TypeScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectId" id="1dJrR6XMWeePLne62dJk97OPzOb" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showExcludedFiles" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="DefaultHtmlFileTemplate" value="HTML File" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="list.type.of.created.stylesheet" value="CSS" />
|
||||
<property name="nodejs_package_manager_path" value="npm" />
|
||||
<property name="settings.editor.selected.configurable" value="preferences.pluginManager" />
|
||||
<property name="ts.external.directory.path" value="$PROJECT_DIR$/node_modules/typescript/lib" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/build" />
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
</key>
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="index.html" type="JavascriptDebugType" temporary="true" nameIsGenerated="true" uri="http://localhost:63342/webgl/build/index.html" useBuiltInWebServerPort="true">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="JavaScript Debug.index.html" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="dc704aa3-69bc-4c4e-bfa3-31f248741e3c" name="Default Changelist" comment="" />
|
||||
<created>1592154025642</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1592154025642</updated>
|
||||
<workItem from="1592154027024" duration="169000" />
|
||||
<workItem from="1592154212967" duration="2262000" />
|
||||
<workItem from="1592156543248" duration="1031000" />
|
||||
<workItem from="1592157594053" duration="10162000" />
|
||||
<workItem from="1592213404810" duration="33877000" />
|
||||
<workItem from="1592384389528" duration="612000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="1" />
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state x="208" y="764" width="654" height="390" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1592157547420">
|
||||
<screen x="0" y="540" width="1080" height="1380" />
|
||||
</state>
|
||||
<state x="208" y="764" width="654" height="390" key="#com.intellij.fileTypes.FileTypeChooser/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592157547420" />
|
||||
<state x="161" y="728" width="747" height="462" key="#com.intellij.ide.fileTemplates.ui.ConfigureTemplatesDialog" timestamp="1592157672793">
|
||||
<screen x="0" y="540" width="1080" height="1380" />
|
||||
</state>
|
||||
<state x="161" y="728" width="747" height="462" key="#com.intellij.ide.fileTemplates.ui.ConfigureTemplatesDialog/1080.513.1920.1080/0.540.1080.1380@0.540.1080.1380" timestamp="1592157672793" />
|
||||
<state x="0" y="721" key="SettingsEditor" timestamp="1592157574185">
|
||||
<screen x="0" y="540" width="1080" height="1380" />
|
||||
</state>
|
||||
<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">
|
||||
<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" />
|
||||
</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" />
|
||||
</component>
|
||||
</project>
|
||||
14
build/index.html
Normal file
14
build/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Boilerplate Typescript Single Page App</title>
|
||||
<link rel="stylesheet" href="main.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<noscript>Enable JavaScript, you fuck!</noscript>
|
||||
</div>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
22
build/main.css
Normal file
22
build/main.css
Normal file
@@ -0,0 +1,22 @@
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#main-stage {
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
box-shadow: 3px 3px 5px 0px rgba(0.0, 0.0, 0.0, 0.5);
|
||||
}
|
||||
|
||||
div#main-stage {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
1
dist/bundle.js
vendored
Normal file
1
dist/bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7393
package-lock.json
generated
Normal file
7393
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
package.json
Normal file
36
package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "webgl-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"license": "ISC",
|
||||
"author": "Daniel Ledda",
|
||||
"scripts": {
|
||||
"build-dev": "webpack --mode development && npm postbuild",
|
||||
"build": "webpack --mode production",
|
||||
"start": "webpack-dev-server --mode development",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@types/node": "^13.11.1",
|
||||
"babel-loader": "^8.1.0",
|
||||
"css-loader": "^3.5.3",
|
||||
"file-loader": "^6.0.0",
|
||||
"font-loader": "^0.1.2",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"raw-loader": "^4.0.1",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^7.0.3",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint-react": "^4.2.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
175
src/App.ts
Normal file
175
src/App.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import vs_src from "./vertexShader.glsl";
|
||||
import fs_src from "./fragmentShader.glsl";
|
||||
import Matrix4 from "./Matrix4";
|
||||
import {perspMat} from "./utils";
|
||||
import Triangle2D from "./Triangle2D";
|
||||
import TrsMatrix from "./TrsMatrix";
|
||||
import Point2D from "./Point2D";
|
||||
import BasicColor from "./BasicColor";
|
||||
import {ProgramLinkingError, ShaderCompilationError} from "./Errors";
|
||||
|
||||
export enum ExitCode {
|
||||
ERROR,
|
||||
QUIT,
|
||||
CONTINUE,
|
||||
WEBGL_ERROR
|
||||
}
|
||||
|
||||
class WebGlApp {
|
||||
private gl: WebGLRenderingContext;
|
||||
private stage: HTMLCanvasElement;
|
||||
private perspMat: Matrix4;
|
||||
private currentProgram: WebGLProgram | null;
|
||||
private positionAttributeLocation: number = 0;
|
||||
private u_colorLoc: number = 0;
|
||||
private u_trsMatrixLoc: number = 0;
|
||||
private maxTris: number = 1000;
|
||||
private tris: Triangle2D[] = [];
|
||||
private sinceLastTri: number = 0;
|
||||
private onShutdownCallback: (exitCode: ExitCode) => 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);
|
||||
}
|
||||
|
||||
onShutdown(callback: (exitCode: ExitCode) => any): void {
|
||||
this.onShutdownCallback = callback;
|
||||
}
|
||||
|
||||
private shutdown(exitCode: ExitCode) {
|
||||
this.onShutdownCallback(exitCode);
|
||||
}
|
||||
|
||||
private createDefaultProgram(): WebGLProgram {
|
||||
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.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) {
|
||||
const program: WebGLProgram = this.gl.createProgram() as WebGLProgram;
|
||||
this.gl.attachShader(program, vertexShader);
|
||||
this.gl.attachShader(program, fragmentShader);
|
||||
this.gl.linkProgram(program);
|
||||
const success = this.gl.getProgramParameter(program, this.gl.LINK_STATUS);
|
||||
if (success) {
|
||||
return program;
|
||||
}
|
||||
else {
|
||||
const log = this.gl.getProgramInfoLog(program);
|
||||
this.gl.deleteProgram(program);
|
||||
throw new ProgramLinkingError(log ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
private createShaderFromString(type: GLenum, source: string): WebGLShader {
|
||||
const shader: WebGLShader = this.gl.createShader(type) as WebGLShader;
|
||||
this.gl.shaderSource(shader, source);
|
||||
this.gl.compileShader(shader);
|
||||
const success = this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS);
|
||||
if (success) {
|
||||
return shader;
|
||||
}
|
||||
else {
|
||||
const log = this.gl.getShaderInfoLog(shader)?.toString();
|
||||
this.gl.deleteShader(shader);
|
||||
throw new ShaderCompilationError(log ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
this.gl.uniformMatrix4fv(this.u_trsMatrixLoc, false,
|
||||
triangle.getTrsMatrix().buffer());
|
||||
this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
private initViewport(): void {
|
||||
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
|
||||
this.gl.clearColor(0, 0, 0, 0);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private createRandomTriangle(): Triangle2D {
|
||||
return new Triangle2D(
|
||||
this.rand2DClipspacePoint(),
|
||||
this.rand2DClipspacePoint(),
|
||||
this.rand2DClipspacePoint(),
|
||||
this.randTrsMat(),
|
||||
this.randBasicColor()
|
||||
)
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (const tri of this.tris) {
|
||||
this.renderTriangle(tri);
|
||||
}
|
||||
}
|
||||
|
||||
private animate(then: number): void {
|
||||
const now = Date.now();
|
||||
const delta = now - then;
|
||||
this.animateRandomTrisPopin(delta);
|
||||
window.requestAnimationFrame(() => this.animate(now));
|
||||
}
|
||||
|
||||
run() {
|
||||
this.initViewport();
|
||||
this.useDefaultProgram();
|
||||
window.requestAnimationFrame(() => this.animate(Date.now()));
|
||||
//this.shutdown(ExitCode.QUIT);
|
||||
}
|
||||
}
|
||||
|
||||
export default WebGlApp;
|
||||
46
src/BasicColor.ts
Normal file
46
src/BasicColor.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
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]);
|
||||
}
|
||||
static fromVec(vec: Float32Array): Color {
|
||||
if (vec.length === 4) {
|
||||
return new BasicColor(vec[0], vec[1], vec[2], vec[3]);
|
||||
}
|
||||
else if (vec.length === 3) {
|
||||
return new BasicColor(vec[0], vec[1], vec[2]);
|
||||
}
|
||||
else {
|
||||
throw new VectorSizeError("3 to 4", vec.length);
|
||||
}
|
||||
}
|
||||
red(): number {
|
||||
return this.rep[0];
|
||||
}
|
||||
green(): number {
|
||||
return this.rep[1];
|
||||
}
|
||||
blue(): number {
|
||||
return this.rep[2];
|
||||
}
|
||||
alpha(): number {
|
||||
return this.rep[3];
|
||||
}
|
||||
setRed(r: number): void {
|
||||
this.rep[0] = r;
|
||||
}
|
||||
setGreen(g: number): void {
|
||||
this.rep[1] = g;
|
||||
}
|
||||
setBlue(b: number): void {
|
||||
this.rep[2] = b;
|
||||
}
|
||||
setAlpha(a: number): void {
|
||||
this.rep[3] = a;
|
||||
}
|
||||
vec(): Float32Array {
|
||||
return this.rep;
|
||||
}
|
||||
}
|
||||
|
||||
export default BasicColor;
|
||||
20
src/Errors.ts
Normal file
20
src/Errors.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export class ShaderCompilationError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message);
|
||||
this.name = "ShaderCompilationError";
|
||||
}
|
||||
}
|
||||
|
||||
export class ProgramLinkingError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message);
|
||||
this.name = "ProgramLinkingError";
|
||||
}
|
||||
}
|
||||
|
||||
export class VectorSizeError extends Error {
|
||||
constructor(expected: number | string, received: number) {
|
||||
super("Expected vector of at least size " + expected + ", but got one of size " + received + "!");
|
||||
this.name = "VectorSizeError";
|
||||
}
|
||||
}
|
||||
54
src/Matrix4.ts
Normal file
54
src/Matrix4.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
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;
|
||||
37
src/Point2D.ts
Normal file
37
src/Point2D.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import PointVec from "./PointVec";
|
||||
|
||||
class Point2D extends PointVec {
|
||||
constructor(vec: Float32Array | ArrayLike<number>);
|
||||
constructor(x: number, y: number);
|
||||
constructor(first: any, second?: any) {
|
||||
if (typeof first === "number") {
|
||||
super(first, second, 0.0);
|
||||
}
|
||||
else if (first.length !== 2) {
|
||||
throw new VectorSizeError(2, first.length);
|
||||
}
|
||||
else {
|
||||
super([...first, 0]);
|
||||
}
|
||||
}
|
||||
xOrd(): number {
|
||||
return this.rep[0];
|
||||
}
|
||||
yOrd(): number {
|
||||
return this.rep[1];
|
||||
}
|
||||
setX(x: number): void {
|
||||
this.rep[0] = x;
|
||||
}
|
||||
setY(y: number): void {
|
||||
this.rep[1] = y;
|
||||
}
|
||||
vec(): Float32Array {
|
||||
return new Float32Array([...this.rep]);
|
||||
}
|
||||
static Zeros(): Point2D {
|
||||
return new Point2D(0.0, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
export default Point2D;
|
||||
35
src/Point3D.ts
Normal file
35
src/Point3D.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import PointVec from "./PointVec";
|
||||
|
||||
class Point3D extends PointVec {
|
||||
constructor(vec: Float32Array | ArrayLike<number>);
|
||||
constructor(x: number, y: number, z: number);
|
||||
constructor(first: any, second?: any, third?: any) {
|
||||
super(first, second, third);
|
||||
}
|
||||
xOrd(): number {
|
||||
return this.rep[0];
|
||||
}
|
||||
yOrd(): number {
|
||||
return this.rep[1];
|
||||
}
|
||||
zOrd(): number {
|
||||
return this.rep[2];
|
||||
}
|
||||
setX(x: number): void {
|
||||
this.rep[0] = x;
|
||||
}
|
||||
setY(y: number): void {
|
||||
this.rep[1] = y;
|
||||
}
|
||||
setZ(z: number): void {
|
||||
this.rep[2] = z;
|
||||
}
|
||||
vec(): Float32Array {
|
||||
return this.rep.slice();
|
||||
}
|
||||
static Zeros(): Point3D {
|
||||
return new Point3D(0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
export default Point3D;
|
||||
24
src/PointVec.ts
Normal file
24
src/PointVec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
abstract class PointVec implements Vector {
|
||||
protected rep: Float32Array;
|
||||
protected constructor(vec: Float32Array | ArrayLike<number>);
|
||||
protected constructor(x: number, y: number, z: number);
|
||||
protected constructor(first: number | Float32Array | ArrayLike<number>, 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] = <number>second;
|
||||
this.rep[2] = <number>third;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.rep = new Float32Array(first[2]);
|
||||
}
|
||||
}
|
||||
abstract vec(): Float32Array;
|
||||
}
|
||||
|
||||
export default PointVec;
|
||||
105
src/Triangle2D.ts
Normal file
105
src/Triangle2D.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import TrsMatrix from "./TrsMatrix";
|
||||
import Point2D from "./Point2D";
|
||||
|
||||
class Triangle2D implements Transformable{
|
||||
private geometryRep: Float32Array;
|
||||
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(...args: any[]) {
|
||||
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([
|
||||
...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];
|
||||
}
|
||||
this.trsMatrix = args[1] ?? new TrsMatrix();
|
||||
this.color = args[2] ?? new BasicColor(0, 0, 0, 1.0);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
colorVec(): Float32Array {
|
||||
return this.color.vec();
|
||||
}
|
||||
|
||||
setColor(color: Color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
getColor(): Color {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
getTrsMatrix(): TrsMatrix {
|
||||
return this.trsMatrix;
|
||||
}
|
||||
|
||||
private mapPointTo(point: Point2D | ArrayLike<number>, x: number) {
|
||||
if (point instanceof Point2D) {
|
||||
this.geometryRep[x] = point.xOrd();
|
||||
this.geometryRep[x + 1] = point.yOrd();
|
||||
}
|
||||
else {
|
||||
this.geometryRep[x] = point[0];
|
||||
this.geometryRep[x + 1] = point[1];
|
||||
}
|
||||
}
|
||||
|
||||
updatePoints(p1: Point2D | ArrayLike<number> | null,
|
||||
p2: Point2D | ArrayLike<number> | null,
|
||||
p3: Point2D | ArrayLike<number> | null) {
|
||||
if (p1) {
|
||||
this.mapPointTo(p1, 0);
|
||||
}
|
||||
if (p2) {
|
||||
this.mapPointTo(p2, 3)
|
||||
}
|
||||
if (p3) {
|
||||
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;
|
||||
71
src/TrsMatrix.ts
Normal file
71
src/TrsMatrix.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import Matrix4 from "./Matrix4";
|
||||
|
||||
class TrsMatrix implements Transformable {
|
||||
protected trs: Matrix4 = Matrix4.Identity();
|
||||
constructor();
|
||||
constructor(translation: Matrix4, rotX: Matrix4, rotY: Matrix4, rotZ: Matrix4, scale: Matrix4);
|
||||
constructor(mat: TrsMatrix);
|
||||
constructor(...args: any[]) {
|
||||
if (args[0]) {
|
||||
if (args[0] instanceof TrsMatrix) {
|
||||
this.trs = new Matrix4(args[0].buffer());
|
||||
} else {
|
||||
this.trs = args[0].times(args[1]).times(args[2]).times(args[3]).times(args[4])
|
||||
}
|
||||
}
|
||||
}
|
||||
toMatrix4(): Matrix4 {
|
||||
return this.trs;
|
||||
}
|
||||
buffer(): Float32Array {
|
||||
return this.trs.buffer();
|
||||
}
|
||||
translateBy(tX: number, tY: number, tZ: number): TrsMatrix {
|
||||
this.trs = this.trs.times(new Matrix4([
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
tX, tY, tZ, 1,
|
||||
]));
|
||||
return this;
|
||||
}
|
||||
rotateBy(degX: number, degY: number, degZ: number): TrsMatrix {
|
||||
const sx = Math.sin(degX);
|
||||
const cx = Math.cos(degX);
|
||||
const sy = Math.sin(degY);
|
||||
const cy = Math.cos(degY);
|
||||
const sz = Math.sin(degZ);
|
||||
const cz = Math.cos(degZ);
|
||||
this.trs = this.trs
|
||||
.times(new Matrix4([
|
||||
1, 0, 0, 0,
|
||||
0, cx, sx, 0,
|
||||
0, -sx, cx, 0,
|
||||
0, 0, 0, 1,
|
||||
]))
|
||||
.times(new Matrix4([
|
||||
cy, 0, -sy, 0,
|
||||
0, 1, 0, 0,
|
||||
sy, 0, cy, 0,
|
||||
0, 0, 0, 1,
|
||||
]))
|
||||
.times(new Matrix4([
|
||||
cz, sz, 0, 0,
|
||||
-sz, cz, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
]));
|
||||
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,
|
||||
]));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default TrsMatrix;
|
||||
9
src/fragmentShader.glsl
Normal file
9
src/fragmentShader.glsl
Normal file
@@ -0,0 +1,9 @@
|
||||
precision mediump float;
|
||||
|
||||
uniform vec4 u_color;
|
||||
|
||||
varying float v_depth;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(exp(v_depth), exp(v_depth), exp(v_depth), 1.0);
|
||||
}
|
||||
47
src/main.ts
Normal file
47
src/main.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import App, {ExitCode as AppExitCode} from "./App";
|
||||
|
||||
const STAGE_ELEMENT_ID: string = "main-stage";
|
||||
const WIDTH: number = 1024;
|
||||
const HEIGHT: number = 720;
|
||||
|
||||
function bootstrap() {
|
||||
const stage = setupStage(WIDTH, HEIGHT);
|
||||
const gl = stage.getContext("webgl");
|
||||
if (gl) {
|
||||
const app = new App(gl, stage);
|
||||
app.onShutdown((exitCode) => shutdown(stage, exitCode));
|
||||
app.run();
|
||||
}
|
||||
else {
|
||||
replaceStageWithMessage(stage,
|
||||
"Unable to initialize WebGL. Your browser or machine may not support it.");
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown(stage: HTMLCanvasElement, exitCode: AppExitCode): void {
|
||||
if (exitCode === AppExitCode.QUIT) {
|
||||
replaceStageWithMessage(stage, "We're done here.");
|
||||
}
|
||||
else if (exitCode === AppExitCode.ERROR) {
|
||||
replaceStageWithMessage(stage, "Something unexpected happened and forced the program to shut down.");
|
||||
}
|
||||
}
|
||||
|
||||
function replaceStageWithMessage(stage: HTMLCanvasElement, message: string): void {
|
||||
const div = document.createElement("div");
|
||||
div.id = stage.id;
|
||||
div.innerText = message;
|
||||
stage.replaceWith(div);
|
||||
}
|
||||
|
||||
function setupStage(width: number, height: number): HTMLCanvasElement {
|
||||
const root: HTMLCanvasElement = document.querySelector("#root") as HTMLCanvasElement;
|
||||
const stage = document.createElement("canvas");
|
||||
stage.id = STAGE_ELEMENT_ID;
|
||||
stage.width = width;
|
||||
stage.height = height;
|
||||
root.appendChild(stage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
window.onload = bootstrap;
|
||||
21
src/types.d.ts
vendored
Normal file
21
src/types.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
declare module '*.glsl' {
|
||||
const value: string;
|
||||
export = value;
|
||||
}
|
||||
|
||||
declare interface Vector {
|
||||
vec(): Float32Array;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
declare interface Color extends Vector {
|
||||
red(): number;
|
||||
green(): number;
|
||||
blue(): number;
|
||||
alpha(): number;
|
||||
}
|
||||
21
src/utils.ts
Normal file
21
src/utils.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import Matrix4 from "./Matrix4";
|
||||
|
||||
export function identityMatrix(size: number): Float32Array {
|
||||
const mat = Array<number>(size*size).fill(0);
|
||||
return new Float32Array(
|
||||
mat.map((_, index) =>
|
||||
index % 5 === 0 ? 1 : 0
|
||||
));
|
||||
}
|
||||
|
||||
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
|
||||
]);
|
||||
}
|
||||
12
src/vertexShader.glsl
Normal file
12
src/vertexShader.glsl
Normal file
@@ -0,0 +1,12 @@
|
||||
attribute vec4 a_position;
|
||||
|
||||
uniform mat4 u_trsMatrix;
|
||||
|
||||
varying float v_depth;
|
||||
|
||||
|
||||
void main() {
|
||||
vec4 pos = u_trsMatrix * a_position;
|
||||
v_depth = pos.z;
|
||||
gl_Position = pos;
|
||||
}
|
||||
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./build/",
|
||||
"noImplicitAny": true,
|
||||
"module": "esnext",
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
]
|
||||
}
|
||||
32
tslint.json
Normal file
32
tslint.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint-react"
|
||||
],
|
||||
"jsRules": {
|
||||
},
|
||||
"rules": {
|
||||
"jsx-no-multiline-js": false,
|
||||
"member-access": false,
|
||||
"prefer-for-of": true,
|
||||
"prefer-const": true,
|
||||
"prefer-readonly": true,
|
||||
"typedef": [
|
||||
true,
|
||||
"call-signature",
|
||||
"property-declaration"
|
||||
],
|
||||
"ordered-imports": false,
|
||||
"quotemark": false,
|
||||
"no-console": false,
|
||||
"jsx-no-lambda": false
|
||||
},
|
||||
"rulesDirectory": [
|
||||
],
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"build/**/*.js",
|
||||
"node_modules/**/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
56
webpack.config.js
Normal file
56
webpack.config.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
|
||||
module.exports = {
|
||||
entry: ["./src/main.ts"],
|
||||
mode: "production",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
exclude: /(node_modules|bower_components|\.d\.ts$)/,
|
||||
use: [
|
||||
"babel-loader",
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.d\.ts$/,
|
||||
loader: 'ignore-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"]
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.glsl$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'raw-loader',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: { extensions: [".tsx", ".ts", ".js", "*"] },
|
||||
output: {
|
||||
path: path.resolve(__dirname, "dist/"),
|
||||
publicPath: "/",
|
||||
filename: "bundle.js"
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.join(__dirname, "build/"),
|
||||
contentBasePublicPath: "/",
|
||||
port: 3000,
|
||||
publicPath: "http://localhost:3000/",
|
||||
hotOnly: true
|
||||
},
|
||||
plugins: [new webpack.HotModuleReplacementPlugin()]
|
||||
};
|
||||
Reference in New Issue
Block a user