CFDG 3D

HTML
<iframe src = "https://s.codepen.io/ge1doot/debug/LdjRqg"></iframe>
CSS
html, body { overflow: hidden; touch-action: none; position: absolute; margin: 0; padding: 0; width: 100%; height: 100%; background: #000; } iframe { position: absolute; border: none; top:0; left: 0; width: 100%; height: 100%; }
JAVASCRIPT
"use strict"; // CFDG 3D library [instanced geometry version] // ge1doot - last modified 25 March 2018 // https://codepen.io/ge1doot/pen/rdwPLr/ const cfdg = function() { // state variables let camera, buffers, geometry, width = 0, height = 0, minSize = 0, seed = 0, fov = 60, camDist = 0, running = false, mustResize = true, offsetY = 0, rotY = 0, background; const stack = [], shapes = []; // WebGL context const canvas = document.querySelector("canvas"); const gl = canvas.getContext("webgl", { alpha: false }) || canvas.getContext("experimental-webgl", { alpha: false }); if (!gl) return false; gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); // instanced geometry extension const ext = gl.getExtension("ANGLE_instanced_arrays"); if (!ext) throw new Error("ANGLE_instanced_arrays not supported"); // init engine const init = (p) => { let program; // hex to rgb const hex2rgb = hex => { return hex .replace( /^#?([a-f\d])([a-f\d])([a-f\d])$/i, (m, r, g, b) => "#" + r + r + g + g + b + b ) .substring(1) .match(/.{2}/g) .map(x => parseInt(x, 16) / 256); }; // Compile Shaders const compileShaders = (specular) => { const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource( vertexShader, ` precision mediump float; uniform mat4 camProj, camView; attribute vec3 aPosition, aNormal, aCol; attribute vec4 aC0, aC1, aC2, aC3; varying vec4 vPosition; varying vec3 vNormal; varying vec3 vColor; vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } void main() { mat4 uMatrix = mat4(aC0, aC1, aC2, aC3); vPosition = camView * uMatrix * vec4(aPosition, 1.0); gl_Position = camProj * vPosition; vNormal = normalize(vec3(camView * uMatrix * vec4(aNormal, 0.0))); vColor = hsv2rgb(aCol); }` ); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); let fragment = ` precision mediump float; uniform vec3 uPointLightingLocation; uniform vec3 uAmbientColor; `; if (specular) fragment += ` uniform vec3 uPointLightingSpecularColor; uniform float uShininess; `; fragment += ` uniform vec3 uPointLightingDiffuseColor; varying vec4 vPosition; varying vec3 vColor; varying vec3 vNormal; void main() { vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz); float diffuseLightWeighting = max(dot(vNormal, lightDirection), 0.0); vec3 lightWeighting = uAmbientColor + uPointLightingDiffuseColor * diffuseLightWeighting; `; if (specular) fragment += ` vec3 eyeDirection = normalize(-vPosition.xyz); vec3 reflectionDirection = reflect(-lightDirection, vNormal); float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), uShininess); lightWeighting += uPointLightingSpecularColor * specularLightWeighting; `; fragment += ` vec3 col = vColor.rgb * lightWeighting; gl_FragColor = vec4(col, 1.0); } `; gl.shaderSource(fragmentShader, fragment); gl.compileShader(fragmentShader); // create program const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); gl.useProgram(program); return program; }; // set view matrix const Camera = () => { const camProj = gl.getUniformLocation(program, "camProj"); const mView = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]); const camView = gl.getUniformLocation(program, "camView"); return { move(rx, ry, y, z) { mView.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, y, z, 1]); if (rx) transforms.rx(mView, rx); transforms.ry(mView, ry); gl.uniformMatrix4fv(camView, false, mView); }, projection(fov) { const near = 0.01; const far = 100; const top = near * Math.tan(fov * Math.PI / 360); const right = top * (gl.drawingBufferWidth / gl.drawingBufferHeight); const left = -right; const bottom = -top; gl.uniformMatrix4fv(camProj, false, [ 2 * near / (right - left), 0, 0, 0, 0, 2 * near / (top - bottom), 0, 0, (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1, 0, 0, 0, 1 ]); } }; }; // GPU buffers const Buffers = () => { let vertices = [], normals = [], vOffset = 0; let c0 = [], c1 = [], c2 = [], c3 = [], col = []; const geometry = []; // buffer data const bufferData = (buffer, data) => { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); data.length = 0; }; // init attribute const attrib = name => { const buffer = gl.createBuffer(); const index = gl.getAttribLocation(program, name); gl.enableVertexAttribArray(index); return [buffer, index]; }; // create GPU static buffers const [positionBuffer, aPosition] = attrib("aPosition"); const [normalBuffer, aNormal] = attrib("aNormal"); const [c0Buffer, aC0] = attrib("aC0"); const [c1Buffer, aC1] = attrib("aC1"); const [c2Buffer, aC2] = attrib("aC2"); const [c3Buffer, aC3] = attrib("aC3"); const [colBuffer, aCol] = attrib("aCol"); // Mesh class const Mesh = class { constructor(v, n) { this.vNum = v.length / 3; this.vOffset = vOffset; this.mOffset = 0; this.cOffset = 0; this.iNum = 0; this.c0 = []; this.c1 = []; this.c2 = []; this.c3 = []; this.col = []; vertices = vertices.concat(v); normals = normals.concat(n ? n : this.normals(v)); vOffset += v.length * Float32Array.BYTES_PER_ELEMENT; } normals (v) { const n = []; for (let i = 0; i < v.length; i += 9) { const p1x = v[i + 3] - v[i]; const p1y = v[i + 4] - v[i + 1]; const p1z = v[i + 5] - v[i + 2]; const p2x = v[i + 6] - v[i]; const p2y = v[i + 7] - v[i + 1]; const p2z = v[i + 8] - v[i + 2]; const p3x = p1y * p2z - p1z * p2y; const p3y = -(p1x * p2z - p1z * p2x); const p3z = p1x * p2y - p1y * p2x; const mag = Math.sqrt(p3x * p3x + p3y * p3y + p3z * p3z); if (mag === 0) { n.push(0, 0, 0, 0, 0, 0, 0, 0, 0); } else { n.push(p3x / mag, p3y / mag, p3z / mag); n.push(p3x / mag, p3y / mag, p3z / mag); n.push(p3x / mag, p3y / mag, p3z / mag); } } return n; } // reset mesh buffers reset() { this.iNum = 0; this.c0.length = 0; this.c1.length = 0; this.c2.length = 0; this.c3.length = 0; this.col.length = 0; } // push matrix transform push(m) { this.iNum++; this.c0.push(m[0], m[1], m[2], m[3]); this.c1.push(m[4], m[5], m[6], m[7]); this.c2.push(m[8], m[9], m[10], m[11]); this.c3.push(m[12], m[13], m[14], m[15]); this.col.push(m[16] / 360, m[17], m[18]); } bind(buffer, size, index, offset, div) { gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.vertexAttribPointer(index, size, gl.FLOAT, false, 0, offset); if (div) ext.vertexAttribDivisorANGLE(index, 1); } drawInstance() { // bind geometry buffers this.bind(positionBuffer, 3, aPosition, this.vOffset, false); this.bind(normalBuffer, 3, aNormal, this.vOffset, false); // bind instanced buffers this.bind(c0Buffer, 4, aC0, this.mOffset, true); this.bind(c1Buffer, 4, aC1, this.mOffset, true); this.bind(c2Buffer, 4, aC2, this.mOffset, true); this.bind(c3Buffer, 4, aC3, this.mOffset, true); this.bind(colBuffer, 3, aCol, this.cOffset, true); // Draw the instanced meshes ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, this.vNum, this.iNum); } }; // Cube geometry[0] = new Mesh([ -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5 ]); // Plane geometry[1] = new Mesh([ -0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, 0.5, 0.5, 0, -0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, -0.5 ]); // tube (() => { const res = 22; let angle = 0; const alpha = 2 * Math.PI / res; const v = [], n = []; for ( let i = 0; i < res; i++) { const c0 = Math.cos(angle); const s0 = Math.sin(angle); const c1 = Math.cos(angle + alpha); const s1 = Math.sin(angle + alpha); v.push( c0 * 0.5, -0.5, s0 * 0.5, c0 * 0.5, 0.5, s0 * 0.5, c1 * 0.5, -0.5, s1 * 0.5, c0 * 0.5, 0.5, s0 * 0.5, c1 * 0.5, 0.5, s1 * 0.5, c1 * 0.5, -0.5, s1 * 0.5 ); n.push( c0, 1, s0, c0, 1, s0, c1, 1, s1, c0, 1, s0, c1, 1, s1, c1, 1, s1 ); angle += alpha; } geometry[2] = new Mesh(v, n); })(); // cylinder (() => { const res = 22; let angle = 0; const alpha = 2 * Math.PI / res; const v = [], n = []; for ( let i = 0; i < res; i++) { const c0 = Math.cos(angle); const s0 = Math.sin(angle); const c1 = Math.cos(angle + alpha); const s1 = Math.sin(angle + alpha); v.push( c0 * 0.5, -0.5, s0 * 0.5, c0 * 0.5, 0.5, s0 * 0.5, c1 * 0.5, -0.5, s1 * 0.5, c0 * 0.5, 0.5, s0 * 0.5, c1 * 0.5, 0.5, s1 * 0.5, c1 * 0.5, -0.5, s1 * 0.5, c1 * 0.5, -0.5, s1 * 0.5, 0, -0.5, 0, c0 * 0.5, -0.5, s0 * 0.5, c0 * 0.5, 0.5, s0 * 0.5, 0, -0.5, 0, c1 * 0.5, 0.5, s1 * 0.5 ); n.push( c0, 1, s0, c0, 1, s0, c1, 1, s1, c0, 1, s0, c1, 1, s1, c1, 1, s1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ); angle += alpha; } geometry[3] = new Mesh(v, n); })(); // sphere (() => { const res = 22; const nx = [], ny = [], nz = [], v = [], n = []; for (let i = 0; i <= res; i++) { const theta = i * Math.PI / res; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); for (let j = 0; j <= res; j++) { const phi = -j * 2 * Math.PI / res; nx.push(Math.cos(phi) * sinTheta) ny.push(cosTheta); nz.push(Math.sin(phi) * sinTheta); } } for (let i = 0; i < res; i++) { for (let j = 0; j < res; j++) { const first = (i * (res + 1)) + j; const second = first + res + 1; v.push( nx[first] * 0.5, ny[first] * 0.5, nz[first] * 0.5, nx[second] * 0.5, ny[second] * 0.5, nz[second] * 0.5, nx[first + 1] * 0.5, ny[first + 1] * 0.5, nz[first + 1] * 0.5, nx[second] * 0.5, ny[second] * 0.5, nz[second] * 0.5, nx[second + 1] * 0.5, ny[second + 1] * 0.5, nz[second + 1] * 0.5, nx[first + 1] * 0.5, ny[first + 1] * 0.5, nz[first + 1] * 0.5 ); n.push( nx[first], ny[first], nz[first], nx[second], ny[second], nz[second], nx[first + 1], ny[first + 1], nz[first + 1], nx[second], ny[second], nz[second], nx[second + 1], ny[second + 1], nz[second + 1], nx[first + 1], ny[first + 1], nz[first + 1] ); } } geometry[4] = new Mesh(v, n); })(); // Pyramid geometry[5] = new Mesh([ -0.5, -0.5, -0.5, 0, 0.5, 0, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0, 0.5, 0, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0, 0.5, 0, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0, 0.5, 0, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5 ]); // load meshes into GPU bufferData(positionBuffer, vertices); bufferData(normalBuffer, normals); // Interface return { geometry: geometry, reset() { c0.length = 0; c1.length = 0; c2.length = 0; c3.length = 0; col.length = 0; for (const mesh of geometry) mesh.reset(); }, loadInstances() { let mOffset = 0, cOffset = 0; for (const mesh of geometry) { if (mesh.iNum) { // merge buffers c0 = c0.concat(mesh.c0); c1 = c1.concat(mesh.c1); c2 = c2.concat(mesh.c2); c3 = c3.concat(mesh.c3); col = col.concat(mesh.col); // free up memory mesh.c0.length = 0; mesh.c1.length = 0; mesh.c2.length = 0; mesh.c3.length = 0; mesh.col.length = 0; // compute offsets mesh.mOffset = mOffset; mesh.cOffset = cOffset; mOffset += (4 * mesh.iNum * Float32Array.BYTES_PER_ELEMENT); cOffset += (3 * mesh.iNum * Float32Array.BYTES_PER_ELEMENT); } } // load buffers into GPU bufferData(c0Buffer, c0); bufferData(c1Buffer, c1); bufferData(c2Buffer, c2); bufferData(c3Buffer, c3); bufferData(colBuffer, col); }, // draw the scene render() { for (const mesh of geometry) { if (mesh.iNum) { mesh.drawInstance(); } } } }; }; // setup minSize = (p.minSize || 0.1) / 100; offsetY = p.offsetY || 0.0; fov = p.fov || 60; rotY = p.rotY === undefined ? -0.15 : p.rotY; camDist = p.camDist === undefined ? -10 : -p.camDist; const specular = p.specularColor ? true : false; // compile shaders if (!program) { program = compileShaders(specular); camera = Camera(); buffers = Buffers(); geometry = buffers.geometry; } // init lightning const light = (name, p) => { const x = p ? p[0] : 0; const y = p ? p[1] : 0; const z = p ? p[2] : 0; gl.uniform3f(gl.getUniformLocation(program, name), x, y, z); }; light("uPointLightingLocation", p.lightPosition); light("uAmbientColor", p.ambientColor); light("uPointLightingDiffuseColor", p.diffuseColor); if (specular) { light("uPointLightingSpecularColor", p.specularColor); gl.uniform1f( gl.getUniformLocation(program, "uShininess"), p.shininess ? p.shininess : 6.0 ); } background = hex2rgb(p.background || "#000"); if (p.seed) seed = p.seed; else seed = Math.round(Math.random() * 1000000); }; // clear screen const clearScreen = () => { if (mustResize) resize(); gl.clearColor(background[0], background[1], background[2], 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }; // perspective view const resize = () => { width = canvas.width = canvas.offsetWidth; height = canvas.height = canvas.offsetHeight; gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); camera.projection(fov); }; // resize windows event window.addEventListener("resize", () => { mustResize = true; }); // Context Free adjustments (3D) const transforms = { x(m, v) { m[12] += m[0] * v; m[13] += m[1] * v; m[14] += m[2] * v; m[15] += m[3] * v; }, y(m, v) { m[12] += m[4] * v; m[13] += m[5] * v; m[14] += m[6] * v; m[15] += m[7] * v; }, z(m, v) { m[12] += m[8] * v; m[13] += m[9] * v; m[14] += m[10] * v; m[15] += m[11] * v; }, s(m, v) { const a = Array.isArray(v); const x = a ? v[0] : v; const y = a ? v[1] : x; const z = a ? v[2] : x; m[0] *= x; m[1] *= x; m[2] *= x; m[3] *= x; m[4] *= y; m[5] *= y; m[6] *= y; m[7] *= y; m[8] *= z; m[9] *= z; m[10] *= z; m[11] *= z; }, rx(m, v) { const rad = Math.PI * (v / 180); const s = Math.sin(rad); const c = Math.cos(rad); const a10 = m[4]; const a11 = m[5]; const a12 = m[6]; const a13 = m[7]; const a20 = m[8]; const a21 = m[9]; const a22 = m[10]; const a23 = m[11]; m[4] = a10 * c + a20 * s; m[5] = a11 * c + a21 * s; m[6] = a12 * c + a22 * s; m[7] = a13 * c + a23 * s; m[8] = a10 * -s + a20 * c; m[9] = a11 * -s + a21 * c; m[10] = a12 * -s + a22 * c; m[11] = a13 * -s + a23 * c; }, ry(m, v) { const rad = Math.PI * (v / 180); const s = Math.sin(rad); const c = Math.cos(rad); const a00 = m[0]; const a01 = m[1]; const a02 = m[2]; const a03 = m[3]; const a20 = m[8]; const a21 = m[9]; const a22 = m[10]; const a23 = m[11]; m[0] = a00 * c + a20 * -s; m[1] = a01 * c + a21 * -s; m[2] = a02 * c + a22 * -s; m[3] = a03 * c + a23 * -s; m[8] = a00 * s + a20 * c; m[9] = a01 * s + a21 * c; m[10] = a02 * s + a22 * c; m[11] = a03 * s + a23 * c; }, rz(m, v) { const rad = Math.PI * (v / 180); const s = Math.sin(rad); const c = Math.cos(rad); const a00 = m[0]; const a01 = m[1]; const a02 = m[2]; const a03 = m[3]; const a10 = m[4]; const a11 = m[5]; const a12 = m[6]; const a13 = m[7]; m[0] = a00 * c + a10 * s; m[1] = a01 * c + a11 * s; m[2] = a02 * c + a12 * s; m[3] = a03 * c + a13 * s; m[4] = a00 * -s + a10 * c; m[5] = a01 * -s + a11 * c; m[6] = a02 * -s + a12 * c; m[7] = a03 * -s + a13 * c; }, h(m, v) { m[16] += v; m[16] %= 360; }, sat(m, v) { this.col(m, v, 17); }, b(m, v) { this.col(m, v, 18); }, col(m, v, p) { if (v > 0) { m[p] += v * (1 - m[p]); } else { m[p] += v * m[p]; } } }; transforms.hue = transforms.h; transforms.scale = transforms.s; // execute context free adjustments const transform = (s, p, mintest) => { let m = copy(s); for (const c in p) { if (transforms[c]) transforms[c](m, p[c]); else console.log("error: " + c); } if (mintest) { const x = m[0] * m[0] + m[1] * m[1] + m[2] * m[2]; const y = m[4] * m[4] + m[5] * m[5] + m[6] * m[6]; const z = m[8] * m[8] + m[9] * m[9] + m[10] * m[10]; return x < minSize || y < minSize || z < minSize ? false : m; } return m; }; // execute loop const loop = (n, s, t, f) => { let ls = copy(s); for (let i = 0; i < n; i++) { f(ls, i); ls = transform(ls, t, true); } }; // random seedable function const random = _ => { seed = (seed * 16807) % 2147483647; return (seed - 1) / 2147483646; }; // random integers const randint = (s, e = 0) => { if (e === 0) { e = s; s = 0; } return Math.floor(s + random() * (e - s + 1)); }; // return random position in array const randpos = a => { return a[Math.floor(random() * a.length)]; }; // copy state const copy = s => { return [ s[0], // a00 1 s[1], // a01 0 s[2], // a02 0 s[3], // a03 0 s[4], // a10 0 s[5], // a11 1 s[6], // a12 0 s[7], // a13 0 s[8], // a20 0 s[9], // a21 0 s[10], // a22 1 s[11], // a23 0 s[12], // a30 0 s[13], // a31 0 s[14], // a32 0 s[15], // a33 1 s[16], // hue s[17], // saturation s[18], // brillance s[19] // function ]; }; // return execute function for single rule const singlerule = i => { return (s, t) => { s = transform(s, t, true); if (!s) return; s[19] = i; stack.push(s); }; }; // return execute function for multiple rules const randomrule = (totalWeight, weight, index, len) => { return (s, t) => { s = transform(s, t, true); if (!s) return; let w = 0; const r = random() * totalWeight; for (let i = 0; i < len; i++) { w += weight[i]; if (r <= w) { s[19] = index[i]; stack.push(s); return; } } }; }; // compile JS shapes functions const compileshapes = () => { shapes.length = 0; this.code = this.rules(); for (const namerule in this.code) { const rules = this.code[namerule]; if (Array.isArray(rules)) { let totalWeight = 0; const weight = []; const index = []; for (let i = 0; i < rules.length; i += 2) { totalWeight += rules[i]; shapes.push(rules[i + 1]); weight.push(rules[i]); index.push(shapes.length - 1); } this[namerule] = randomrule(totalWeight, weight, index, index.length); } else { shapes.push(rules); this[namerule] = singlerule(shapes.length - 1); } } }; // flatten out recursive JS calls const runshapes = (start, t, p) => { let minComplexity = p.minComplexity || 100; let comp = 0; do { comp = 0; stack.length = 0; this[start]([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], t); buffers.reset(); do { const s = stack.shift(); shapes[s[19]](s); comp++; } while (stack.length); } while (comp < minComplexity--); // load GPU buffers buffers.loadInstances(); }; // animation loop let ry = 0; const run = () => { clearScreen(); ry += rotY; camera.move(10, ry, offsetY, camDist); buffers.render(); requestAnimationFrame(run); }; // start first shape const render = p => { if (!running) init(p); // run CFDG if (!this.code) compileshapes(); runshapes(p.startShape, p.transform || {}, p); // start animation loop if (!running) { running = true; run(); } }; // push matrices const pushGeometry = (s, t, p) => { s = transform(s, t, false); geometry[p].push(s); }; // primitives this.CUBE = (s, t) => pushGeometry(s, t, 0); this.PLANE = (s, t) => pushGeometry(s, t, 1); this.TUBE = (s, t) => pushGeometry(s, t, 2); this.CYLINDER = (s, t) => pushGeometry(s, t, 3); this.SPHERE = (s, t) => pushGeometry(s, t, 4); this.PYRAMID = (s, t) => pushGeometry(s, t, 5); // public methods this.random = random; this.randint = randint; this.randpos = randpos; this.render = render; this.transform = transform; this.loop = loop; };
Expand for more options Login