]> git.r.bdr.sh - rbdr/lissajous/blob - lib/systems/webgl_renderer.js
Update mobile CSS
[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 export default class WebGLRenderer extends System {
49
50 constructor(canvas) {
51
52 super();
53 this.canvas = canvas;
54 }
55
56 added(engine){
57
58 // Set up canvas
59 const { canvas } = this;
60 window.addEventListener('resize', () => this._resizeCanvas(canvas));
61
62 // Set up WebGL
63 const gl = canvas.getContext('webgl', {
64 preserveDrawingBuffer: true
65 });
66 this.gl = gl;
67
68 gl.clearColor(0.05882, 0.14902, 0.12157, 1);
69 gl.enable(gl.DEPTH_TEST);
70 gl.depthFunc(gl.LEQUAL);
71
72 this.colorBuffer = gl.createBuffer();
73
74 const shaderProgram = initializeShaderProgram(
75 gl,
76 internals.kVertexShader,
77 internals.kFragmentShader
78 );
79
80 this.programInfo = {
81 program: shaderProgram,
82 attribLocations: {
83 vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
84 vertexColor: gl.getAttribLocation(shaderProgram, 'aColor')
85 },
86 uniformLocations: {
87 projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
88 viewMatrix: gl.getUniformLocation(shaderProgram, 'uViewMatrix')
89 }
90 };
91
92 this.buffers = initializeBuffers(gl);
93 this._resizeCanvas(canvas);
94
95 this.points = engine.getNodes(Drawable);
96 this.positions = [];
97 this.colors = [];
98
99 this.configurations = engine.getNodes(Configurable);
100 this.cameras = engine.getNodes(Cameras);
101 }
102
103 removed(engine){
104
105 delete this.gl;
106 delete this.points;
107 delete this.colorBuffer;
108 delete this.buffers;
109 delete this.positions;
110 delete this.colors;
111 delete this.configurations;
112 delete this.cameras;
113 }
114
115 update(){
116
117 const {gl, programInfo, buffers} = this;
118
119 gl.useProgram(programInfo.program);
120
121 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
122
123 const fieldOfView = internals.kFieldOfView * Math.PI / 180;
124 const aspectRatio = internals.kWidthRatio / internals.kHeightRatio;
125 const projectionMatrix = mat4.create();
126
127 mat4.perspective(
128 projectionMatrix,
129 fieldOfView,
130 aspectRatio,
131 internals.kNearLimit,
132 internals.kFarLimit
133 );
134
135 gl.uniformMatrix4fv(
136 programInfo.uniformLocations.projectionMatrix,
137 false,
138 projectionMatrix
139 );
140
141 // We only support one camera for now.
142 const camera = this.cameras.nodes[0];
143 if (camera != undefined) {
144 const eye = vec3.fromValues(camera.position.x, camera.position.y, camera.position.z);
145 const center = vec3.fromValues(0, 0, 0);
146 const up = vec3.fromValues(camera.up.x, camera.up.y, camera.up.z);
147 const viewMatrix = mat4.create();
148 mat4.lookAt(viewMatrix, eye, center, up);
149 gl.uniformMatrix4fv(
150 programInfo.uniformLocations.viewMatrix,
151 false,
152 viewMatrix
153 );
154 }
155
156 let i = 0;
157 for (const point of this.points) {
158 this.positions[i] = this.positions[i] || [];
159 this.positions[i].push(point.position.x, point.position.y, point.position.z, 1);
160 this.positions[i].push(
161 point.position.prevX || point.position.x,
162 point.position.prevY || point.position.y,
163 point.position.prevZ || point.position.z,
164 1
165 );
166 point.position.prevX = point.position.x;
167 point.position.prevY = point.position.y;
168 point.position.prevZ = point.position.z;
169
170 this.colors[i] = this.colors[i] || [];
171 this.colors[i].push(
172 0.5 + point.position.z / 4 + point.position.x / 4,
173 0.5 + point.position.z / 4 + point.position.y / 4,
174 0.75 + point.position.z / 4, 1,
175 0.5 + point.position.prevZ / 4 + point.position.prevX / 4,
176 0.5 + point.position.prevZ / 4 + point.position.prevY / 4,
177 0.75 + point.position.prevZ / 4,1);
178
179 ++i;
180 }
181
182 gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
183 const colors = this.colors.flat();
184 gl.bufferData(gl.ARRAY_BUFFER,
185 new Float32Array(colors),
186 gl.STATIC_DRAW);
187
188 {
189 const numberOfComponents = 4;
190 const type = gl.FLOAT;
191 const normalize = false;
192 const stride = 0;
193 const offset = 0;
194
195 gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
196 gl.vertexAttribPointer(
197 programInfo.attribLocations.vertexColor,
198 numberOfComponents,
199 type,
200 normalize,
201 stride,
202 offset
203 );
204 gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
205 }
206
207 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
208 const positions = this.positions.flat();
209 gl.bufferData(gl.ARRAY_BUFFER,
210 new Float32Array(positions),
211 gl.STATIC_DRAW);
212
213 {
214 const numberOfComponents = 4;
215 const type = gl.FLOAT;
216 const normalize = false;
217 const stride = 0;
218 const offset = 0;
219
220 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
221 gl.vertexAttribPointer(
222 programInfo.attribLocations.vertexPosition,
223 numberOfComponents,
224 type,
225 normalize,
226 stride,
227 offset
228 );
229 gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
230 }
231
232 {
233 gl.lineWidth(2);
234 gl.drawArrays(gl.LINES, 0, positions.length / 4);
235 }
236
237 this._cullLines();
238 }
239
240 _resizeCanvas(canvas) {
241
242 let width = window.innerWidth;
243 let height = Math.round(width * internals.kHeightRatio / internals.kWidthRatio);
244
245 if (window.innerHeight < height) {
246 height = window.innerHeight;
247 width = Math.round(height * internals.kWidthRatio / internals.kHeightRatio);
248 }
249
250 canvas.style.width = `${width}px`;
251 canvas.style.height = `${height}px`;
252
253 canvas.width = internals.kTargetVerticalResolution * internals.kWidthRatio;
254 canvas.height = internals.kTargetVerticalResolution;
255
256 this.gl.viewport(0, 0, canvas.width, canvas.height);
257 }
258
259 _cullLines() {
260 const lineLength = this.configurations.nodes[0]?.configuration.lineLength || internals.kDefaultLineLength;
261
262 for (const [i, position] of Object.entries(this.positions)) {
263 this.positions[i] = position.slice(-lineLength * 8);
264 }
265
266 for (const [i, color] of Object.entries(this.colors)) {
267 this.colors[i] = color.slice(-lineLength * 8);
268 }
269 }
270 };