Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active September 11, 2016 16:02
Show Gist options
  • Save mathdoodle/9490a592182176417957a41468a94a11 to your computer and use it in GitHub Desktop.
Save mathdoodle/9490a592182176417957a41468a94a11 to your computer and use it in GitHub Desktop.
WireCube Scaffold

WireCube Scaffold

Overview

  1. Start from a working WebGL library implementation scaffold (EIGHT).
  2. Replace parts of library (Geometry, Camera, Material, Geometric3, Matrix4).
  3. Restart from a working native WebGL implementation scaffold.
  4. Refactor into a modular API (library).
  5. Capstone project is to use own WebGL library in an application.

Notes

/**
* A camera scaffold.
*/
export default class Camera implements EIGHT.Facet {
//
// Implementing the Facet interface provides uniform values.
// Implementing eye, look, up enables trackball controls.
//
/**
* The position of the camera.
*/
eye = {x:0, y:0, z:0};
/**
* A point along the direction in which te camera is looking.
*/
look = {x:0, y:0, z:0};
/**
* The approximate up direction.
*/
up = {x:0, y:0, z:0};
/**
* The projection transformation.
*/
private uProjection: EIGHT.Matrix4 = EIGHT.Matrix4.one();
/**
* The view transformation.
*/
private uView: EIGHT.Matrix4 = EIGHT.Matrix4.one();
/**
*
*/
constructor() {
// Parameters may be required according to the type of projection.
}
setUniforms(visitor: EIGHT.FacetVisitor) {
console.log(`eye => ${this.eye}`);
console.log(`look => ${this.look}`);
console.log(`up => ${this.up}`);
// The matrics must be updated (efficiently) based upon eye, look, up.
visitor.matrix4fv('uProjection', this.uProjection.elements, false);
visitor.matrix4fv('uView', this.uView.elements, false);
}
}
/**
* Displays an exception by writing it to a <pre> element.
*/
export default function displayError(e: any) {
const stderr = <HTMLPreElement>document.getElementById('errors')
stderr.style.color = "#FF0000"
stderr.innerHTML = `${e}`
}
/**
* vec(x, y, z) corresponds to x * e1 + y * e2 + z * e3
*/
export function vec(x: number, y: number, z: number): EIGHT.VectorE3 {
const that: EIGHT.VectorE3 = {
get x(): number {
return x;
},
get y(): number {
return y;
},
get z(): number {
return z;
}
};
return that;
}
/**
* The magnitude of the vector argument.
*/
export function magnitude(v: EIGHT.VectorE3): number {
// Exercise: Complete this function.
// Hint: Use Math.sqrt
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
/**
* The direction of the vector argument.
*/
export function direction(v: EIGHT.VectorE3): EIGHT.VectorE3 {
// Exercise: Complete this function.
const m = magnitude(v);
const x = v.x / m;
const y = v.y / m;
const z = v.z / m;
return vec(x, y, z);
}
/**
* The projection of vector b onto the line given by vector a.
* This is defined to be the closest point on the line to the point
* with coordinates [b.x, b.y. b.z]
*/
export function proj(b: EIGHT.VectorE3, a: EIGHT.VectorE3): EIGHT.VectorE3 {
// Exercise, using the definition given, compute the projection vector.
// You may assume that the space is Euclidean.
a = direction(a);
const dot = a.x * b.x + a.y * b.y + a.z * b.z;
const denom = a.x * a.x + a.y * a.y + a.z * a.z;
const f = dot /denom;
// It would be nice if we could scale ate the vector level of abstraction.
const x = f * a.x;
const y = f * a.y;
const z = f * a.z;
return vec(x, y, z);
}
export function reflect(a: EIGHT.VectorE3, n: EIGHT.VectorE3): EIGHT.VectorE3 {
const p = proj(a, n);
// It would be nice if we could subtract at the vector level of abstraction.
const x = a.x - 2 * p.x;
const y = a.y - 2 * p.y;
const z = a.z - 2 * p.z;
return vec(x, y, z);
}
export function normalize(v: EIGHT.VectorE3): EIGHT.VectorE3 {
// Exercise: Complete this function.
const x = 0;
const y = 0;
const z = 0;
return vec(x, y, z);
}
export function random(): EIGHT.VectorE3 {
// Exercise: Complete this function.
// Hint: Use Math.random
const x = 0;
const y = 0;
const z = 0;
return vec(x, y, z);
}
export function wedge(...vs: EIGHT.VectorE3[]): any {
}
wedge(void 0, void 0)
function makeColorChangeHandler(color: EIGHT.Color) {
return function(value: number[]) {
color.r = value[0] / 255;
color.g = value[1] / 255;
color.b = value[2] / 255;
}
}
export default class GUI extends dat.GUI {
showA = true;
showB = true;
showC = false;
showAxes = false;
showBox = false;
showComponents = false;
showDirections = false;
showProjection = false;
showReflection = false;
showUnitSphere = false;
constructor() {
super();
this.add(this, 'showA');
this.add(this, 'showB');
this.add(this, 'showC');
this.add(this, 'showAxes');
this.add(this, 'showBox');
// this.add(this, 'showComponents');
this.add(this, 'showDirections');
this.add(this, 'showProjection');
this.add(this, 'showReflection');
this.add(this, 'showUnitSphere');
}
}
<!doctype html>
<html>
<head>
<style>
/* STYLE-MARKER */
</style>
<script src="https://jspm.io/system.js"></script>
<!-- SHADERS-MARKER -->
<!-- SCRIPTS-MARKER -->
</head>
<body>
<canvas id='canvas'></canvas>
<pre id='errors'></pre>
<script>
// CODE-MARKER
</script>
<script>
try {
System.import('./index.js');
}
catch(e) {
alert(`${e}`)
}
</script>
</body>
</html>
import Camera from './Camera';
import {e1, e2, e3} from './math';
import GUI from './GUI';
import {proj, reflect, direction} from './Geometric3';
import requestFrame from './requestFrame';
import WireCube from './WireCube';
const a = e1.clone().scale(1);
const aColor = EIGHT.Color.red.clone();
const b = (1.5 * e1 + e2).scale(1);
const bColor = EIGHT.Color.magenta.clone();
const c = EIGHT.Geometric3.random().grade(1).normalize();
const cColor = EIGHT.Color.yellow.clone();
/**
* Wrapper around the WebGLRenderingContext providing the ContextManager interface.
*/
const engine = new EIGHT.Engine('canvas')
.size(500, 500)
.clearColor(0.1, 0.1, 0.1, 1.0)
.enable(EIGHT.Capability.DEPTH_TEST)
/**
* A collection of objects that can be rendered with a single draw method call.
*/
const scene = new EIGHT.Scene(engine)
/**
* Rendering information that applies to all objects.
*/
const ambients: EIGHT.Facet[] = []
/**
* Provides the viewing point and perspective transformation.
*/
const camera = new EIGHT.PerspectiveCamera()
camera.eye.copyVector(e1).addVector(e2, 0.8).addVector(e3, 2).normalize().scale(5)
ambients.push(camera)
/**
* Provides a light color and direction for Lambert shading.
*/
const dirLight = new EIGHT.DirectionalLight()
ambients.push(dirLight)
/**
* Controls the camera by accumulating mouse movements then moving and rotating the camera.
*/
const trackball = new EIGHT.TrackballControls(camera, window)
trackball.subscribe(engine.canvas)
trackball.noPan = true;
const geometry = new WireCube(engine)
const material = new EIGHT.HTMLScriptsMaterial(['line-vs', 'line-fs'], document, [], engine)
const box = new EIGHT.Mesh(geometry, material, engine)
const basisPos = new EIGHT.Basis({engine});
basisPos.a.scale(10)
basisPos.b.scale(10)
basisPos.c.scale(10)
const basisNeg = new EIGHT.Basis({engine});
basisNeg.a.copy(basisPos.a).neg();
basisNeg.colorA.scale(0.3)
basisNeg.b.copy(basisPos.b).neg();
basisNeg.colorB.scale(0.3)
basisNeg.c.copy(basisPos.c).neg();
basisNeg.colorC.scale(0.3)
const gridXY = new EIGHT.GridXY({engine});
// scene.add(gridXY)
const gridYZ = new EIGHT.GridYZ({engine, yMin:-5, yMax: +5, zMin:-5, zMax: +5});
// scene.add(gridYZ)
const arrow = new EIGHT.Arrow({engine});
arrow.color = EIGHT.Color.yellow
const sphere = new EIGHT.Sphere({engine, k: 1, azimuthSegments: 36, elevationSegments:18})
const gui = new GUI();
/**
* Animates the scene.
*/
const animate = function(timestamp: number) {
engine.clear()
trackball.update()
dirLight.direction.copy(camera.look).sub(camera.eye)
if (gui.showA) {
arrow.h.copyVector(a);
arrow.color.copy(aColor);
arrow.render(ambients)
if (gui.showDirections) {
arrow.h.copyVector(direction(a));
arrow.color.copy(aColor);
arrow.render(ambients);
}
}
if (gui.showB) {
arrow.h.copyVector(b);
arrow.color.copy(bColor);
arrow.render(ambients)
if (gui.showDirections) {
arrow.h.copyVector(direction(b));
arrow.color.copy(bColor);
arrow.render(ambients);
}
}
if (gui.showC) {
// Reflection onto e3
arrow.h.copyVector(c)
arrow.color.copy(cColor);
arrow.render(ambients)
}
if (gui.showAxes) {
basisPos.render(ambients)
basisNeg.render(ambients)
}
if (gui.showBox) {
box.render(ambients)
}
if (gui.showComponents) {
// Projection onto e1
arrow.h.copyVector(proj(a, e1))
arrow.color = EIGHT.Color.red
arrow.render(ambients)
// Projection onto e2
arrow.h.copyVector(proj(a, e2))
arrow.color = EIGHT.Color.green
arrow.render(ambients)
// Projection onto e3
arrow.h.copyVector(proj(a, e3))
arrow.color = EIGHT.Color.blue
arrow.render(ambients)
}
if (gui.showProjection) {
arrow.h.copyVector(proj(b, a))
arrow.color.copy(bColor)
arrow.render(ambients)
if (gui.showDirections) {
arrow.h.copyVector(proj(direction(b), a))
arrow.color.copy(bColor)
arrow.render(ambients)
}
if (gui.showA) {
arrow.h.copyVector(proj(a, b))
arrow.color.copy(aColor)
arrow.render(ambients)
}
gridYZ.X.copyVector(proj(b,a))
gridYZ.color.copy(bColor)
gridYZ.render(ambients);
}
if (gui.showReflection) {
// Reflection onto e3
arrow.h.copyVector(reflect(a, c))
arrow.color.copy(bColor)
arrow.render(ambients)
}
if (gui.showUnitSphere) {
sphere.render(ambients)
}
scene.draw(ambients)
requestFrame(animate)
}
requestFrame(animate)
varying highp vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
attribute vec3 aPosition;
uniform vec3 uColor;
uniform float uOpacity;
uniform mat4 uModel;
uniform mat4 uProjection;
uniform mat4 uView;
varying highp vec4 vColor;
void main(void) {
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
vColor = vec4(uColor, uOpacity);
}
export const e1 = EIGHT.Geometric3.e1();
export const e2 = EIGHT.Geometric3.e2();
export const e3 = EIGHT.Geometric3.e3();
varying highp vec4 vColor;
varying highp vec3 vLight;
void main(void) {
gl_FragColor = vec4(vColor.xyz * vLight, vColor.a);
}
attribute vec3 aPosition;
attribute vec3 aNormal;
uniform vec3 uColor;
uniform float uOpacity;
uniform mat4 uModel;
uniform mat3 uNormal;
uniform mat4 uProjection;
uniform mat4 uView;
uniform vec3 uAmbientLight;
uniform vec3 uDirectionalLightColor;
uniform vec3 uDirectionalLightDirection;
varying highp vec4 vColor;
varying highp vec3 vLight;
void main(void) {
gl_Position = uProjection * uView * uModel * vec4(aPosition, 1.0);
vColor = vec4(uColor, uOpacity);
vec3 L = normalize(uDirectionalLightDirection);
vec3 N = normalize(uNormal * aNormal);
// The minus sign arises because L is the light direction, so we need dot(N, -L) = -dot(N, L)
float directionalLightCosineFactor = max(-dot(N, L), 0.0);
vLight = uAmbientLight + directionalLightCosineFactor * uDirectionalLightColor;
}
{
"description": "WireCube Scaffold",
"dependencies": {
"davinci-eight": "2.307.0",
"dat-gui": "0.5.0"
},
"operatorOverloading": true,
"name": "copy-of-eight-starter-template",
"version": "0.1.0",
"keywords": [
"EIGHT",
"EightJS",
"WebGL",
"Getting Started",
"Engine",
"Scene",
"PerspectiveCamera",
"DirectionalLight",
"TrackballControls",
"DomReady",
"HTMLCanvasElement",
"requestAnimationFrame",
"Geometric3"
],
"author": "David Geo Holmes"
}
import displayError from './displayError'
/**
* Catches exceptions thrown in the animation callback and displays them.
*/
export default function requestFrame(callback: FrameRequestCallback): number {
const wrapper: FrameRequestCallback = function(time: number) {
try {
callback(time)
}
catch(e) {
displayError(e)
}
}
return window.requestAnimationFrame(wrapper)
}
body { margin: 0; }
canvas { width: 500px; height: 500px }
#stats { position: absolute; top: 0; left: 0; }
/**
* A Geometry for rendering a cube made from lines.
*/
export default class WireCube implements EIGHT.Geometry {
private buffer: WebGLBuffer;
public data: Float32Array;
/**
*
*/
public invalid = true;
constructor(private engine: EIGHT.Engine) {
const gl = engine.gl;
const size = 1;
const L = size / 2;
this.data = new Float32Array([
-L, -L, -L, +L, -L, -L,
-L, +L, -L, +L, +L, -L,
-L, -L, +L, +L, -L, +L,
-L, +L, +L, +L, +L, +L,
-L, +L, +L, -L, +L, -L,
+L, +L, +L, +L, +L, -L,
-L, -L, +L, -L, -L, -L,
+L, -L, +L, +L, -L, -L,
-L, -L, -L, -L, +L, -L,
+L, -L, -L, +L, +L, -L,
-L, -L, +L, -L, +L, +L,
+L, -L, +L, +L, +L, +L
]);
this.buffer = gl.createBuffer();
}
bind(material: EIGHT.Material): void {
const gl = this.engine.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
}
unbind(material: EIGHT.Material): void {
const gl = this.engine.gl;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
draw(material: EIGHT.Material): void {
const gl = this.engine.gl;
const aPosition = material.getAttribLocation('aPosition');
if (this.invalid) {
gl.bufferData(gl.ARRAY_BUFFER, this.data, EIGHT.Usage.STATIC_DRAW);
this.invalid = false;
}
gl.vertexAttribPointer(aPosition, 3, EIGHT.DataType.FLOAT, true, 0, 0);
gl.enableVertexAttribArray(aPosition);
gl.drawArrays(EIGHT.BeginMode.LINES, 0, 24);
gl.disableVertexAttribArray(aPosition);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment