+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
}
`,
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
});
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,
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')
}
};
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(){
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;
{
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`;
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);
+ }
+ }
};