Skip to content

Instantly share code, notes, and snippets.

@jassmith
Created July 3, 2021 02:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jassmith/915ec5a4d2b87a3a06046a8d1857e63b to your computer and use it in GitHub Desktop.
Save jassmith/915ec5a4d2b87a3a06046a8d1857e63b to your computer and use it in GitHub Desktop.
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