]> git.r.bdr.sh - rbdr/lissajous/blobdiff - lib/systems/webgl_renderer.js
Make 3D
[rbdr/lissajous] / lib / systems / webgl_renderer.js
index 8535fde64e6d073ec72d04f1f3d4e2c6bb7047a8..3a92552f0dc5e5d91ef7afd6d4b86ee737f0ba20 100644 (file)
@@ -1,16 +1,34 @@
+import { mat4, vec3 } from 'gl-matrix';
 import { System } from '@serpentity/serpentity';
 import Drawable from '../nodes/drawable';
+import Configurable from '../nodes/configurable';
+import Cameras from '../nodes/cameras';
 import { initializeShaderProgram, initializeBuffers } from '../webgl_utils';
 
 const internals = {
-  kWidthRatio: 2.76,
+  kDefaultLineLength: 1000,
+  kCameraRadius: 5,
+  kCameraAngularVelocity: Math.PI / 180,
+  kFieldOfView: 45, // degrees
+  kNearLimit: 0.1,
+  kFarLimit: 100,
+  kWidthRatio: 1.5,
   kHeightRatio: 1,
-  kTargetVerticalResolution: 32 + Math.round(Math.random() * 1024),
+  kTargetVerticalResolution: 1024,
   kVertexShader: `
 
-    attribute vec3 aVertexPosition;
+    attribute vec4 aVertexPosition;
+    attribute vec4 aColor;
+
+    varying vec4 vColor;
+
+    uniform mat4 uViewMatrix;
+    uniform mat4 uProjectionMatrix;
+
     void main() {
-        gl_Position = vec4(aVertexPosition.xy, 0.0, 1.0);
+
+        gl_Position = uProjectionMatrix * uViewMatrix * aVertexPosition;
+        vColor = aColor;
         gl_PointSize = 10.0; // Set the point size
     }
   `,
@@ -18,25 +36,27 @@ const internals = {
   kFragmentShader: `
 
     precision mediump float;
-    uniform vec4 uColor;
+    varying vec4 vColor;
+
     void main() {
-        gl_FragColor = uColor;
+
+        gl_FragColor = vColor;
     }
   `
 };
 
 export default class WebGLRenderer extends System {
 
-  constructor(canvasId) {
+  constructor(canvas) {
 
     super();
-    this.canvasId = canvasId;
+    this.canvas = canvas;
   }
 
   added(engine){
 
     // Set up canvas
-    const canvas = document.getElementById(this.canvasId);
+    const { canvas } = this;
     window.addEventListener('resize', () => this._resizeCanvas(canvas));
 
     // Set up WebGL
@@ -45,6 +65,12 @@ export default class WebGLRenderer extends System {
     });
     this.gl = gl;
 
+    gl.clearColor(0.05882, 0.14902, 0.12157, 1);
+    gl.enable(gl.DEPTH_TEST);
+    gl.depthFunc(gl.LEQUAL);
+
+    this.colorBuffer = gl.createBuffer();
+
     const shaderProgram = initializeShaderProgram(
       gl,
       internals.kVertexShader,
@@ -54,10 +80,12 @@ export default class WebGLRenderer extends System {
     this.programInfo = {
       program: shaderProgram,
       attribLocations: {
-        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition')
+        vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
+        vertexColor: gl.getAttribLocation(shaderProgram, 'aColor')
       },
       uniformLocations: {
-        color: gl.getUniformLocation(shaderProgram, 'uColor')
+        projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
+        viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix')
       }
     };
 
@@ -65,11 +93,23 @@ export default class WebGLRenderer extends System {
     this._resizeCanvas(canvas);
 
     this.points = engine.getNodes(Drawable);
+    this.positions = [];
+    this.colors = [];
+
+    this.configurations = engine.getNodes(Configurable);
+    this.cameras = engine.getNodes(Cameras);
   }
 
   removed(engine){
 
-    this.points = undefined;
+    delete this.gl;
+    delete this.points;
+    delete this.colorBuffer;
+    delete this.buffers;
+    delete this.positions;
+    delete this.colors;
+    delete this.configurations;
+    delete this.cameras;
   }
 
   update(){
@@ -78,31 +118,100 @@ export default class WebGLRenderer extends System {
 
     gl.useProgram(programInfo.program);
 
-    {
-      const color = [1, 1, 1, 1];
-      gl.uniform4fv(programInfo.uniformLocations.color, color);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+    const fieldOfView = internals.kFieldOfView * Math.PI / 180;
+    const aspectRatio = internals.kWidthRatio / internals.kHeightRatio;
+    const projectionMatrix = mat4.create();
+
+    mat4.perspective(
+      projectionMatrix,
+      fieldOfView,
+      aspectRatio,
+      internals.kNearLimit,
+      internals.kFarLimit
+    );
+
+    gl.uniformMatrix4fv(
+      programInfo.uniformLocations.projectionMatrix,
+      false,
+      projectionMatrix
+    );
+
+    // We only support one camera for now.
+    const camera = this.cameras.nodes[0];
+    if (camera != undefined) {
+      const eye = vec3.fromValues(camera.position.x, camera.position.y, camera.position.z);
+      const center = vec3.fromValues(0, 0, 0);
+      const up = vec3.fromValues(camera.up.x, camera.up.y, camera.up.z);
+      const viewMatrix = mat4.create();
+      mat4.lookAt(viewMatrix, eye, center, up);
+      gl.uniformMatrix4fv(
+        programInfo.uniformLocations.viewMatrix,
+        false,
+        viewMatrix
+      );
     }
 
-    const positions = [];
+    let i = 0;
     for (const point of this.points) {
-      positions.push(point.position.x, point.position.y, point.position.z);
-      positions.push(
+      this.positions[i] = this.positions[i] || [];
+      this.positions[i].push(point.position.x, point.position.y, point.position.z, 1);
+      this.positions[i].push(
         point.position.prevX || point.position.x,
         point.position.prevY || point.position.y,
-        point.position.prevZ || point.position.z
+        point.position.prevZ || point.position.z,
+        1
       );
       point.position.prevX = point.position.x;
       point.position.prevY = point.position.y;
       point.position.prevZ = point.position.z;
+
+      this.colors[i] = this.colors[i] || [];
+      this.colors[i].push(
+        0.5 + point.position.z / 4 + point.position.x / 4,
+        0.5 + point.position.z / 4 + point.position.y / 4,
+        0.75 + point.position.z / 4, 1,
+        0.5 + point.position.prevZ / 4 + point.position.prevX / 4,
+        0.5 + point.position.prevZ / 4 + point.position.prevY / 4,
+        0.75 + point.position.prevZ / 4,1);
+
+      ++i;
+    }
+
+    gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+    const colors = this.colors.flat();
+    gl.bufferData(gl.ARRAY_BUFFER,
+      new Float32Array(colors),
+      gl.STATIC_DRAW);
+
+    {
+      const numberOfComponents = 4;
+      const type = gl.FLOAT;
+      const normalize = false;
+      const stride = 0;
+      const offset = 0;
+
+      gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+      gl.vertexAttribPointer(
+        programInfo.attribLocations.vertexColor,
+        numberOfComponents,
+        type,
+        normalize,
+        stride,
+        offset
+      );
+      gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
     }
 
     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
+    const positions = this.positions.flat();
     gl.bufferData(gl.ARRAY_BUFFER,
       new Float32Array(positions),
       gl.STATIC_DRAW);
 
     {
-      const numberOfComponents = 3;
+      const numberOfComponents = 4;
       const type = gl.FLOAT;
       const normalize = false;
       const stride = 0;
@@ -122,20 +231,20 @@ export default class WebGLRenderer extends System {
 
     {
       gl.lineWidth(2);
-      gl.drawArrays(gl.LINES, 0, this.points.nodes.length * 2);
+      gl.drawArrays(gl.LINES, 0, positions.length / 4);
     }
+
+    this._cullLines();
   }
 
   _resizeCanvas(canvas) {
 
-    console.log('Resizing Canvas');
     let width = window.innerWidth;
     let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio);
 
     if (window.innerHeight < height) {
       height = window.innerHeight;
       width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio);
-      console.log(width, height);
     }
 
     canvas.style.width = `${width}px`;
@@ -146,4 +255,16 @@ export default class WebGLRenderer extends System {
 
     this.gl.viewport(0, 0, canvas.width, canvas.height);
   }
+
+  _cullLines() {
+    const lineLength = this.configurations.nodes[0]?.configuration.lineLength || internals.kDefaultLineLength;
+
+    for (const [i, position] of Object.entries(this.positions)) {
+      this.positions[i] = position.slice(-lineLength * 8);
+    }
+
+    for (const [i, color] of Object.entries(this.colors)) {
+      this.colors[i] = color.slice(-lineLength * 8);
+    }
+  }
 };