]> git.r.bdr.sh - rbdr/lissajous/blob - lib/systems/webgl_renderer.js
b677c6c19456625c4d56757c71d4ba61c39624b9
[rbdr/lissajous] / lib / systems / webgl_renderer.js
1 import { mat4, vec3 } from 'gl-matrix';
2 import { System } from '@serpentity/serpentity';
3 import Drawable from '../nodes/drawable';
4 import Configurable from '../nodes/configurable';
5 import Cameras from '../nodes/cameras';
6 import { initializeShaderProgram, initializeBuffers } from '../webgl_utils';
7
8 const internals = {
9 kDefaultLineLength: 1000,
10 kCameraRadius: 5,
11 kCameraAngularVelocity: Math.PI / 180,
12 kFieldOfView: 45, // degrees
13 kNearLimit: 0.1,
14 kFarLimit: 100,
15 kWidthRatio: 1.5,
16 kHeightRatio: 1,
17 kTargetVerticalResolution: 1024,
18 kVertexShader: `
19
20 attribute vec4 aVertexPosition;
21 attribute vec4 aColor;
22
23 varying vec4 vColor;
24
25 uniform mat4 uViewMatrix;
26 uniform mat4 uProjectionMatrix;
27
28 void main() {
29
30 gl_Position = uProjectionMatrix * uViewMatrix * aVertexPosition;
31 vColor = aColor;
32 gl_PointSize = 10.0; // Set the point size
33 }
34 `,
35
36 kFragmentShader: `
37
38 precision mediump float;
39 varying vec4 vColor;
40
41 void main() {
42
43 gl_FragColor = vColor;
44 }
45 `
46 };
47
48 /**
49 * Does all the WebGL rendering. I'm not super familiar with WebGL so I need
50 * to revisit this in a while and see how I would restructure this.
51 */
52 export default class WebGLRenderer extends System {
53
54 constructor(canvas) {
55
56 super();
57 this.canvas = canvas;
58 }
59
60 added(engine){
61
62 // Set up canvas
63 const { canvas } = this;
64 window.addEventListener('resize', () => this._resizeCanvas(canvas));
65
66 // Set up WebGL
67 const gl = canvas.getContext('webgl', {
68 preserveDrawingBuffer: true
69 });
70 this.gl = gl;
71
72 gl.clearColor(0.05882, 0.14902, 0.12157, 1);
73 gl.enable(gl.DEPTH_TEST);
74 gl.depthFunc(gl.LEQUAL);
75
76 this.colorBuffer = gl.createBuffer();
77
78 const shaderProgram = initializeShaderProgram(
79 gl,
80 internals.kVertexShader,
81 internals.kFragmentShader
82 );
83
84 this.programInfo = {
85 program: shaderProgram,
86 attribLocations: {
87 vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
88 vertexColor: gl.getAttribLocation(shaderProgram, 'aColor')
89 },
90 uniformLocations: {
91 projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
92 viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix')
93 }
94 };
95
96 this.buffers = initializeBuffers(gl);
97 this._resizeCanvas(canvas);
98
99 this.points = engine.getNodes(Drawable);
100 this.positions = [];
101 this.colors = [];
102
103 this.configurations = engine.getNodes(Configurable);
104 this.cameras = engine.getNodes(Cameras);
105 }
106
107 removed(engine){
108
109 delete this.gl;
110 delete this.points;
111 delete this.colorBuffer;
112 delete this.buffers;
113 delete this.positions;
114 delete this.colors;
115 delete this.configurations;
116 delete this.cameras;
117 }
118
119 update(){
120
121 const {gl, programInfo, buffers} = this;
122
123 gl.useProgram(programInfo.program);
124
125 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
126
127 const fieldOfView = internals.kFieldOfView * Math.PI / 180;
128 const aspectRatio = internals.kWidthRatio / internals.kHeightRatio;
129 const projectionMatrix = mat4.create();
130
131 mat4.perspective(
132 projectionMatrix,
133 fieldOfView,
134 aspectRatio,
135 internals.kNearLimit,
136 internals.kFarLimit
137 );
138
139 gl.uniformMatrix4fv(
140 programInfo.uniformLocations.projectionMatrix,
141 false,
142 projectionMatrix
143 );
144
145 // We only support one camera for now.
146 const camera = this.cameras.nodes[0];
147 if (camera != undefined) {
148 const eye = vec3.fromValues(camera.position.x, camera.position.y, camera.position.z);
149 const center = vec3.fromValues(0, 0, 0);
150 const up = vec3.fromValues(camera.up.x, camera.up.y, camera.up.z);
151 const viewMatrix = mat4.create();
152 mat4.lookAt(viewMatrix, eye, center, up);
153 gl.uniformMatrix4fv(
154 programInfo.uniformLocations.viewMatrix,
155 false,
156 viewMatrix
157 );
158 }
159
160 let i = 0;
161 for (const point of this.points) {
162 this.positions[i] = this.positions[i] || [];
163 this.positions[i].push(point.position.x, point.position.y, point.position.z, 1);
164 this.positions[i].push(
165 point.position.prevX || point.position.x,
166 point.position.prevY || point.position.y,
167 point.position.prevZ || point.position.z,
168 1
169 );
170 point.position.prevX = point.position.x;
171 point.position.prevY = point.position.y;
172 point.position.prevZ = point.position.z;
173
174 this.colors[i] = this.colors[i] || [];
175 this.colors[i].push(
176 0.5 + point.position.z / 4 + point.position.x / 4,
177 0.5 + point.position.z / 4 + point.position.y / 4,
178 0.75 + point.position.z / 4, 1,
179 0.5 + point.position.prevZ / 4 + point.position.prevX / 4,
180 0.5 + point.position.prevZ / 4 + point.position.prevY / 4,
181 0.75 + point.position.prevZ / 4,1);
182
183 ++i;
184 }
185
186 gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
187 const colors = this.colors.flat();
188 gl.bufferData(gl.ARRAY_BUFFER,
189 new Float32Array(colors),
190 gl.STATIC_DRAW);
191
192 {
193 const numberOfComponents = 4;
194 const type = gl.FLOAT;
195 const normalize = false;
196 const stride = 0;
197 const offset = 0;
198
199 gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
200 gl.vertexAttribPointer(
201 programInfo.attribLocations.vertexColor,
202 numberOfComponents,
203 type,
204 normalize,
205 stride,
206 offset
207 );
208 gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
209 }
210
211 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
212 const positions = this.positions.flat();
213 gl.bufferData(gl.ARRAY_BUFFER,
214 new Float32Array(positions),
215 gl.STATIC_DRAW);
216
217 {
218 const numberOfComponents = 4;
219 const type = gl.FLOAT;
220 const normalize = false;
221 const stride = 0;
222 const offset = 0;
223
224 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
225 gl.vertexAttribPointer(
226 programInfo.attribLocations.vertexPosition,
227 numberOfComponents,
228 type,
229 normalize,
230 stride,
231 offset
232 );
233 gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
234 }
235
236 {
237 gl.lineWidth(2);
238 gl.drawArrays(gl.LINES, 0, positions.length / 4);
239 }
240
241 this._cullLines();
242 }
243
244 _resizeCanvas(canvas) {
245
246 let width = window.innerWidth;
247 let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio);
248
249 if (window.innerHeight < height) {
250 height = window.innerHeight;
251 width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio);
252 }
253
254 canvas.style.width = `${width}px`;
255 canvas.style.height = `${height}px`;
256
257 canvas.width = internals.kTargetVerticalResolution * internals.kWidthRatio;
258 canvas.height = internals.kTargetVerticalResolution;
259
260 this.gl.viewport(0, 0, canvas.width, canvas.height);
261 }
262
263 _cullLines() {
264 const lineLength = this.configurations.nodes[0]?.configuration.lineLength || internals.kDefaultLineLength;
265
266 for (const [i, position] of Object.entries(this.positions)) {
267 this.positions[i] = position.slice(-lineLength * 8);
268 }
269
270 for (const [i, color] of Object.entries(this.colors)) {
271 this.colors[i] = color.slice(-lineLength * 8);
272 }
273 }
274 };