Created
July 3, 2021 02:06
-
-
Save jassmith/915ec5a4d2b87a3a06046a8d1857e63b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { assert } from "@glideapps/ts-necessities"; | |
import { nonNull } from "common/Support"; | |
import * as React from "react"; | |
// A react component which draws a webgl canvas | |
export class CubeComponent extends React.Component<{ | |
width?: number; | |
height?: number; | |
}> { | |
public width: number = 1000; | |
public height: number = 1000; | |
public canvas: HTMLCanvasElement | undefined = undefined; | |
public gl: WebGLRenderingContext | null = null; | |
private rotX = 0; | |
private rotY = 0; | |
private rotZ = 0; | |
//cube vertices | |
private vertices = [ | |
// Front face | |
-1.0, | |
-1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
1.0, | |
// Back face | |
-1.0, | |
-1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
-1.0, | |
// Top face | |
-1.0, | |
1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
-1.0, | |
// Bottom face | |
-1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
// Right face | |
1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
// Left face | |
-1.0, | |
-1.0, | |
-1.0, | |
-1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
1.0, | |
1.0, | |
-1.0, | |
1.0, | |
-1.0, | |
]; | |
//cube indices | |
private indices = [ | |
0, | |
1, | |
2, | |
0, | |
2, | |
3, // front | |
4, | |
5, | |
6, | |
4, | |
6, | |
7, // back | |
8, | |
9, | |
10, | |
8, | |
10, | |
11, // top | |
12, | |
13, | |
14, | |
12, | |
14, | |
15, // bottom | |
16, | |
17, | |
18, | |
16, | |
18, | |
19, // right | |
20, | |
21, | |
22, | |
20, | |
22, | |
23, // left | |
]; | |
//cube colors | |
private colors = [ | |
// Front face | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
// Back face | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
// Top face | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
1.0, | |
// Bottom face | |
1.0, | |
1.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
0.0, | |
0.0, | |
0.0, | |
0.0, | |
0.0, | |
1.0, | |
1.0, | |
// Right face | |
0.0, | |
1.0, | |
1.0, | |
0.0, | |
1.0, | |
1.0, | |
0.0, | |
1.0, | |
1.0, | |
0.0, | |
1.0, | |
1.0, | |
0.0, | |
// Left face | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
]; | |
private cubeVertexShader = ` | |
attribute vec3 aVertexPosition; | |
attribute vec3 aVertexColor; | |
uniform mat4 uMVMatrix; | |
uniform mat4 uPMatrix; | |
varying vec3 vColor; | |
void main(void) { | |
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0); | |
vColor = aVertexColor; | |
} | |
`; | |
private cubeFragmentShader = ` | |
precision mediump float; | |
varying vec3 vColor; | |
void main(void) { | |
gl_FragColor = vec4(vColor, 1.0); | |
} | |
`; | |
private makeShaderFromString(input: string, shaderType: number) { | |
assert(this.gl !== null); | |
const shader = this.gl.createShader(shaderType); | |
assert(shader !== null); | |
this.gl.shaderSource(shader, input); | |
this.gl.compileShader(shader); | |
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { | |
// eslint-disable-next-line no-console | |
console.error(this.gl.getShaderInfoLog(shader)); | |
return null; | |
} | |
return shader; | |
} | |
private makeBuffer(gl: WebGLRenderingContext, vals: number[], bufferTarget: number) { | |
const buffer = gl.createBuffer(); | |
assert(buffer !== null); | |
gl.bindBuffer(bufferTarget, buffer); | |
gl.bufferData(bufferTarget, new Float32Array(vals), gl.STATIC_DRAW); | |
return buffer; | |
} | |
private identity: Float32List = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]; | |
private scaleMatrix(input: Float32List, amount: number): Float32List { | |
const output = []; | |
output[0] = input[0] * amount; | |
output[1] = input[1] * amount; | |
output[2] = input[2] * amount; | |
output[3] = input[3]; | |
output[4] = input[4] * amount; | |
output[5] = input[5] * amount; | |
output[6] = input[6] * amount; | |
output[7] = input[7]; | |
output[8] = input[8] * amount; | |
output[9] = input[9] * amount; | |
output[10] = input[10] * amount; | |
output[11] = input[11]; | |
output[12] = input[12] * amount; | |
output[13] = input[13] * amount; | |
output[14] = input[14] * amount; | |
output[15] = input[15]; | |
return output; | |
} | |
private rotateMatrix(rotXDeg: number, rotYDeg: number, rotZDeg: number): Float32List { | |
const output = []; | |
const radX = (rotXDeg * Math.PI) / 180; | |
const radY = (rotYDeg * Math.PI) / 180; | |
const radZ = (rotZDeg * Math.PI) / 180; | |
const cosX = Math.cos(radX); | |
const sinX = Math.sin(radX); | |
const cosY = Math.cos(radY); | |
const sinY = Math.sin(radY); | |
const cosZ = Math.cos(radZ); | |
const sinZ = Math.sin(radZ); | |
output[0] = cosZ * cosY; | |
output[1] = cosZ * sinY; | |
output[2] = -sinZ; | |
output[3] = 0.0; | |
output[4] = -cosY * sinX * cosZ - sinY * sinZ; | |
output[5] = cosY * cosX; | |
output[6] = cosY * sinX * sinZ + sinY * cosZ; | |
output[7] = 0.0; | |
output[8] = cosX * sinY * cosZ + sinX * sinZ; | |
output[9] = -cosX * cosY; | |
output[10] = sinX * sinY * sinZ + cosX * cosZ; | |
output[11] = 0.0; | |
output[12] = 0.0; | |
output[13] = 0.0; | |
output[14] = 0.0; | |
output[15] = 1.0; | |
return output; | |
} | |
private renderCube(gl: WebGLRenderingContext, vertices: number[], indices: number[], colors: number[]) { | |
// renders a cube using webgl | |
// gl: the WebGL context | |
// vertices: the vertices of the cube | |
// indices: the indices of the cube | |
// colors: the colors of the cube | |
assert(vertices !== null); | |
assert(indices !== null); | |
assert(colors !== null); | |
const vertexShader = this.makeShaderFromString(this.cubeVertexShader, gl.VERTEX_SHADER); | |
const fragmentShader = this.makeShaderFromString(this.cubeFragmentShader, gl.FRAGMENT_SHADER); | |
const program = gl.createProgram(); | |
assert(program !== null); | |
assert(vertexShader !== null); | |
assert(fragmentShader !== null); | |
gl.attachShader(program, vertexShader); | |
gl.attachShader(program, fragmentShader); | |
gl.linkProgram(program); | |
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | |
// eslint-disable-next-line no-console | |
console.error(gl.getProgramInfoLog(program)); | |
return; | |
} | |
// use shader program here | |
gl.useProgram(program); | |
// Set the background color to black | |
gl.clearColor(1.0, 1.0, 1.0, 1.0); | |
// Clear the color buffer | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
const positionAttributeLocation = gl.getAttribLocation(program, "aVertexPosition"); | |
const colorAttributeLocation = gl.getAttribLocation(program, "aVertexColor"); | |
this.makeBuffer(gl, vertices, gl.ARRAY_BUFFER); | |
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0); | |
this.makeBuffer(gl, colors, gl.ARRAY_BUFFER); | |
gl.vertexAttribPointer(colorAttributeLocation, 3, gl.FLOAT, false, 0, 0); | |
const buffer = gl.createBuffer(); | |
assert(buffer !== null); | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); | |
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); | |
gl.enableVertexAttribArray(positionAttributeLocation); | |
gl.enableVertexAttribArray(colorAttributeLocation); | |
// Set uMVMatrix | |
const uMVMatrix = gl.getUniformLocation(program, "uMVMatrix"); | |
assert(uMVMatrix !== null); | |
gl.uniformMatrix4fv(uMVMatrix, false, this.scaleMatrix(this.identity, 0.2)); | |
// Set uPMatrix | |
const uPMatrix = gl.getUniformLocation(program, "uPMatrix"); | |
assert(uPMatrix !== null); | |
gl.uniformMatrix4fv(uPMatrix, false, this.rotateMatrix(this.rotX, this.rotY, this.rotZ)); | |
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); | |
} | |
public componentDidMount(): void { | |
assert(this.canvas !== undefined); | |
this.gl = this.canvas.getContext("webgl") as WebGLRenderingContext; | |
// create requestAnimationFrame loop | |
const render = () => { | |
this.rotX += 0.1; | |
this.rotY += 0.22; | |
this.rotZ += 0.25; | |
this.renderCube(nonNull(this.gl), this.vertices, this.indices, this.colors); | |
requestAnimationFrame(render); | |
}; | |
requestAnimationFrame(render); | |
} | |
public componentWillUnmount(): void { | |
this.gl = null; | |
} | |
public render(): JSX.Element { | |
return ( | |
<canvas | |
width={this.width} | |
height={this.height} | |
ref={(canvas: HTMLCanvasElement) => { | |
this.canvas = canvas; | |
}} | |
/> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment