aboutsummaryrefslogtreecommitdiff
path: root/lib/systems
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2024-05-07 22:37:27 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2024-05-07 22:37:27 +0200
commit362f91160b243453578633e3f9af67ce40179d8c (patch)
treea9ee95e2f8a9c59189ea904ce87bfe54ed5be107 /lib/systems
parent5f6ef99eae91f53239f08143cead1249893fef81 (diff)
Make 3D
Diffstat (limited to 'lib/systems')
-rw-r--r--lib/systems/amplitude_adjuster.js64
-rw-r--r--lib/systems/camera_adjuster.js74
-rw-r--r--lib/systems/camera_rotator.js49
-rw-r--r--lib/systems/frequency_adjuster.js64
-rw-r--r--lib/systems/global_adjuster.js57
-rw-r--r--lib/systems/lissajous_position_updater.js13
-rw-r--r--lib/systems/webgl_renderer.js167
7 files changed, 458 insertions, 30 deletions
diff --git a/lib/systems/amplitude_adjuster.js b/lib/systems/amplitude_adjuster.js
new file mode 100644
index 0000000..71f7277
--- /dev/null
+++ b/lib/systems/amplitude_adjuster.js
@@ -0,0 +1,64 @@
+import { System } from '@serpentity/serpentity';
+import Ample from '../nodes/ample';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+ symbols: {
+ a: '𝛢',
+ b: '𝛣',
+ c: '𝛧'
+ }
+};
+
+export default class AmplitudeAdjuster extends System {
+
+ constructor(container) {
+
+ super();
+ this.container = container;
+ }
+
+ added(engine){
+
+ this.nodes = engine.getNodes(Ample);
+ this.adjusterContainer = settingsContainer({
+ id: 'amplitude-adjuster',
+ label: 'Amplitude'
+ });
+
+ let i = 0;
+ for (const node of this.nodes) {
+ const nodeElement = settingsContainer({
+ id: `amplitude-adjuster-${i}`,
+ label: `ɣ${i + 1}`,
+ level: 3
+ });
+
+ ['a', 'b', 'c'].forEach(key => {
+ nodeElement.appendChild(slider({
+ min: '0',
+ max: '1',
+ step: '0.01',
+ label: internals.symbols[key],
+ className: `amplitude`,
+ get: () => node.amplitude[key].toString(),
+ set: (value) => (node.amplitude[key] = parseFloat(value))
+ }));
+ });
+
+ this.adjusterContainer.appendChild(nodeElement);
+ ++i;
+ }
+
+ this.container.appendChild(this.adjusterContainer);
+ }
+
+ removed(){
+
+ this.container.removeChild(this.adjusterContainer);
+ delete this.adjusterContainer;
+ delete this.nodes;
+ }
+
+ update(){}
+};
diff --git a/lib/systems/camera_adjuster.js b/lib/systems/camera_adjuster.js
new file mode 100644
index 0000000..3b6ea41
--- /dev/null
+++ b/lib/systems/camera_adjuster.js
@@ -0,0 +1,74 @@
+import { System } from '@serpentity/serpentity';
+import Cameras from '../nodes/cameras';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+ symbols: {
+ x: '𝜃',
+ y: '𝜓',
+ z: '𝜙'
+ }
+};
+
+export default class CameraAdjuster extends System {
+
+ constructor(container) {
+
+ super();
+ this.container = container;
+ }
+
+ added(engine){
+
+ this.nodes = engine.getNodes(Cameras);
+ this.adjusterContainer = settingsContainer({
+ id: 'camera-adjuster',
+ label: 'Camera'
+ })
+
+ let i = 0;
+ for (const node of this.nodes) {
+ const nodeElement = settingsContainer({
+ id: `camera-adjuster-${i}`,
+ label: `C${i + 1}`,
+ level: 3
+ });
+
+ nodeElement.appendChild(slider({
+ min: '2',
+ max: '10',
+ step: '0.1',
+ label: '𝑟',
+ className: 'radius',
+ get: () => node.radius.radius.toString(),
+ set: (value) => (node.radius.radius = parseFloat(value))
+ }));
+
+ ['x', 'y', 'z'].forEach(key => {
+ nodeElement.appendChild(slider({
+ min: '0',
+ max: '0.5',
+ step: '0.001',
+ label: internals.symbols[key],
+ className: `rotation-${key}`,
+ get: () => node.velocity[key].toString(),
+ set: (value) => (node.velocity[key] = parseFloat(value))
+ }));
+ });
+
+ this.adjusterContainer.appendChild(nodeElement);
+ ++i;
+ }
+
+ this.container.appendChild(this.adjusterContainer);
+ }
+
+ removed(){
+
+ this.container.removeChild(this.adjusterContainer);
+ delete this.adjusterContainer;
+ delete this.nodes;
+ }
+
+ update(){}
+};
diff --git a/lib/systems/camera_rotator.js b/lib/systems/camera_rotator.js
new file mode 100644
index 0000000..3520f71
--- /dev/null
+++ b/lib/systems/camera_rotator.js
@@ -0,0 +1,49 @@
+import { mat4, vec3 } from 'gl-matrix';
+import { System } from '@serpentity/serpentity';
+import Cameras from '../nodes/cameras';
+
+export default class CameraRotator extends System {
+
+ constructor() {
+
+ super();
+ }
+
+ added(engine){
+
+ this.cameras = engine.getNodes(Cameras);
+ }
+
+ removed(){
+
+ delete this.cameras;
+ }
+
+ update(dt){
+
+ for (const camera of this.cameras) {
+
+ let rotationMatrix = mat4.create();
+ mat4.rotateY(rotationMatrix, rotationMatrix, camera.angle.yaw);
+ mat4.rotateX(rotationMatrix, rotationMatrix, camera.angle.pitch);
+ mat4.rotateZ(rotationMatrix, rotationMatrix, camera.angle.roll);
+
+ let eye = vec3.fromValues(0, 0, camera.radius.radius);
+ vec3.transformMat4(eye, eye, rotationMatrix);
+
+ camera.position.x = eye[0];
+ camera.position.y = eye[1];
+ camera.position.z = eye[2];
+
+ let up = vec3.fromValues(0, 1, 0);
+ vec3.transformMat4(up, up, rotationMatrix);
+ camera.up.x = up[0];
+ camera.up.y = up[1];
+ camera.up.z = up[2];
+
+ camera.angle.pitch = (camera.angle.pitch + camera.velocity.x * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+ camera.angle.yaw = (camera.angle.yaw + camera.velocity.y * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+ camera.angle.roll = (camera.angle.roll + camera.velocity.z * dt / 100 + 2 * Math.PI) % (2 * Math.PI);
+ }
+ }
+};
diff --git a/lib/systems/frequency_adjuster.js b/lib/systems/frequency_adjuster.js
new file mode 100644
index 0000000..6aa11d4
--- /dev/null
+++ b/lib/systems/frequency_adjuster.js
@@ -0,0 +1,64 @@
+import { System } from '@serpentity/serpentity';
+import Frequent from '../nodes/frequent';
+import { settingsContainer, slider } from '../factories/ui';
+
+const internals = {
+ symbols: {
+ a: '𝛼',
+ b: '𝛽',
+ c: '𝜁'
+ }
+};
+
+export default class FrequencyAdjuster extends System {
+
+ constructor(container) {
+
+ super();
+ this.container = container;
+ }
+
+ added(engine){
+
+ this.nodes = engine.getNodes(Frequent);
+ this.adjusterContainer = settingsContainer({
+ id: 'frequency-adjuster',
+ label: 'Frequency'
+ })
+
+ let i = 0;
+ for (const node of this.nodes) {
+ const nodeElement = settingsContainer({
+ id: `frequency-adjuster-${i}`,
+ label: `ɣ${i + 1}`,
+ level: 3
+ });
+
+ ['a', 'b', 'c'].forEach(key => {
+ nodeElement.appendChild(slider({
+ min: '0',
+ max: '1',
+ step: '0.01',
+ label: internals.symbols[key],
+ className: `frequency`,
+ get: () => node.frequency[key].toString(),
+ set: (value) => (node.frequency[key] = parseFloat(value))
+ }));
+ });
+
+ this.adjusterContainer.appendChild(nodeElement);
+ ++i;
+ }
+
+ this.container.appendChild(this.adjusterContainer);
+ }
+
+ removed(){
+
+ this.container.removeChild(this.adjusterContainer);
+ delete this.adjusterContainer;
+ delete this.nodes;
+ }
+
+ update(){}
+};
diff --git a/lib/systems/global_adjuster.js b/lib/systems/global_adjuster.js
new file mode 100644
index 0000000..7f6df91
--- /dev/null
+++ b/lib/systems/global_adjuster.js
@@ -0,0 +1,57 @@
+import { System } from '@serpentity/serpentity';
+import Frequent from '../nodes/configurable';
+import { settingsContainer, slider } from '../factories/ui';
+
+export default class GlobalAdjuster extends System {
+
+ constructor(container) {
+
+ super();
+ this.container = container;
+ }
+
+ added(engine){
+
+ this.nodes = engine.getNodes(Frequent);
+ const container = document.getElementById('settings');
+ this.adjusterContainer = settingsContainer({
+ id: 'global-adjuster',
+ label: 'Global'
+ });
+
+ let i = 0;
+ for (const node of this.nodes) {
+ const nodeElement = settingsContainer({
+ id: `global-adjuster-${i}`,
+ label: 'Line',
+ level: 3
+ });
+
+ nodeElement.appendChild(slider({
+ min: '5',
+ max: '2000',
+ step: '1',
+ label: '𝜆',
+ className: 'lineLength',
+ get: () => node.configuration.lineLength.toString(),
+ set: (value) => (node.configuration.lineLength = parseFloat(value))
+ }));
+
+ this.adjusterContainer.appendChild(nodeElement);
+ ++i;
+ }
+
+ this.container.appendChild(this.adjusterContainer);
+ }
+
+ removed(){
+
+ this.container.removeChild(this.adjusterContainer);
+ delete this.adjusterContainer;
+ delete this.nodes;
+ }
+
+ update(){}
+};
+
+
diff --git a/lib/systems/lissajous_position_updater.js b/lib/systems/lissajous_position_updater.js
index 0f5792d..202ddc7 100644
--- a/lib/systems/lissajous_position_updater.js
+++ b/lib/systems/lissajous_position_updater.js
@@ -2,7 +2,7 @@ import { System } from '@serpentity/serpentity';
import LissajousCurve from '../nodes/lissajous_curve';
const internals = {
- kAmplitude: 0.8,
+ kAmplitude: 1,
kPeriod: Math.PI * 12000000
};
@@ -21,8 +21,8 @@ export default class WebGLRenderer extends System {
removed(){
- this.curves = undefined;
- this.time = undefined;
+ delete this.curves;
+ delete this.time;
}
update(dt){
@@ -30,11 +30,10 @@ export default class WebGLRenderer extends System {
this.time = (this.time + dt / 100) % internals.kPeriod;
for (const curve of this.curves) {
- curve.position.x = this._getPosition(internals.kAmplitude, curve.frequency.a, this.time, 0);
- curve.position.y = this._getPosition(internals.kAmplitude, curve.frequency.b, this.time, 0);
- curve.position.z = this._getPosition(internals.kAmplitude, curve.frequency.c, this.time, 0);
+ curve.position.x = this._getPosition(curve.amplitude.a, curve.frequency.a, this.time, 0);
+ curve.position.y = this._getPosition(curve.amplitude.b, curve.frequency.b, this.time, 0);
+ curve.position.z = this._getPosition(curve.amplitude.c, curve.frequency.c, this.time, 0);
}
- console.log('UP');
}
_getPosition(amplitude, frequency, time, phaseShift) {
diff --git a/lib/systems/webgl_renderer.js b/lib/systems/webgl_renderer.js
index 8535fde..3a92552 100644
--- a/lib/systems/webgl_renderer.js
+++ b/lib/systems/webgl_renderer.js
@@ -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);
+ }
+ }
};