aboutsummaryrefslogtreecommitdiff
path: root/lib
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
parent5f6ef99eae91f53239f08143cead1249893fef81 (diff)
Make 3D
Diffstat (limited to 'lib')
-rw-r--r--lib/components/configuration.js9
-rw-r--r--lib/components/radius.js9
-rw-r--r--lib/components/triple_amplitude.js12
-rw-r--r--lib/components/triple_frequency.js6
-rw-r--r--lib/components/up.js12
-rw-r--r--lib/config.js1
-rw-r--r--lib/factories/curves.js2
-rw-r--r--lib/factories/global.js33
-rw-r--r--lib/factories/ui.js28
-rw-r--r--lib/main.js34
-rw-r--r--lib/nodes/ample.js7
-rw-r--r--lib/nodes/cameras.js15
-rw-r--r--lib/nodes/configurable.js7
-rw-r--r--lib/nodes/frequent.js7
-rw-r--r--lib/nodes/lissajous_curve.js4
-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
-rw-r--r--lib/webgl_utils.js12
23 files changed, 641 insertions, 45 deletions
diff --git a/lib/components/configuration.js b/lib/components/configuration.js
new file mode 100644
index 0000000..ddb4a27
--- /dev/null
+++ b/lib/components/configuration.js
@@ -0,0 +1,9 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Configuration extends Component {
+ constructor(config) {
+
+ super(config);
+ this.lineLength = this.lineLength || 1000;
+ }
+};
diff --git a/lib/components/radius.js b/lib/components/radius.js
new file mode 100644
index 0000000..2aa29a8
--- /dev/null
+++ b/lib/components/radius.js
@@ -0,0 +1,9 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Radius extends Component {
+ constructor(config) {
+
+ super(config);
+ this.radius = this.radius || 5;
+ }
+};
diff --git a/lib/components/triple_amplitude.js b/lib/components/triple_amplitude.js
new file mode 100644
index 0000000..376c321
--- /dev/null
+++ b/lib/components/triple_amplitude.js
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class TripleAmplitude extends Component {
+ constructor(config) {
+
+ super(config);
+
+ this.a = this.a || Math.random();
+ this.b = this.b || Math.random();
+ this.c = this.c || Math.random();
+ }
+};
diff --git a/lib/components/triple_frequency.js b/lib/components/triple_frequency.js
index aa768e3..1f118b9 100644
--- a/lib/components/triple_frequency.js
+++ b/lib/components/triple_frequency.js
@@ -5,8 +5,8 @@ export default class TripleFrequency extends Component {
super(config);
- this.a = Math.random();
- this.b = Math.random();
- this.c = Math.random();
+ this.a = this.a || Math.random();
+ this.b = this.b || Math.random();
+ this.c = this.c || Math.random();
}
};
diff --git a/lib/components/up.js b/lib/components/up.js
new file mode 100644
index 0000000..f28cc20
--- /dev/null
+++ b/lib/components/up.js
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Up extends Component {
+ constructor(config) {
+
+ super(config);
+
+ this.x = this.x || 0;
+ this.y = this.y || 1;
+ this.z = this.z || 0;
+ }
+};
diff --git a/lib/config.js b/lib/config.js
index bb8e11e..e8fa3c9 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -1,3 +1,4 @@
export const canvasId = 'lissajous';
+export const settingsId = 'settings';
export const fps = 30;
diff --git a/lib/factories/curves.js b/lib/factories/curves.js
index d9f0f47..99bc588 100644
--- a/lib/factories/curves.js
+++ b/lib/factories/curves.js
@@ -1,6 +1,7 @@
import { Entity } from '@serpentity/serpentity';
import Position from '@serpentity/components.position';
import TripleFrequency from '../components/triple_frequency';
+import TripleAmplitude from '../components/triple_amplitude';
import Color from '../components/color';
export function lissajousCurve() {
@@ -8,6 +9,7 @@ export function lissajousCurve() {
const entity = new Entity();
entity.addComponent(new Position());
entity.addComponent(new TripleFrequency());
+ entity.addComponent(new TripleAmplitude());
entity.addComponent(new Color());
return entity;
diff --git a/lib/factories/global.js b/lib/factories/global.js
new file mode 100644
index 0000000..c601bdb
--- /dev/null
+++ b/lib/factories/global.js
@@ -0,0 +1,33 @@
+import { Entity } from '@serpentity/serpentity';
+import Position from '@serpentity/components.position';
+import EulerAngle from '@serpentity/components.euler_angle';
+import Velocity from '@serpentity/components.velocity';
+import Radius from '../components/radius';
+import Up from '../components/up';
+import Configuration from '../components/configuration';
+
+export function configuration() {
+
+ const entity = new Entity();
+ entity.addComponent(new Configuration());
+
+ return entity;
+}
+
+export function camera() {
+
+ const entity = new Entity();
+ entity.addComponent(new Position());
+ entity.addComponent(new EulerAngle());
+ entity.addComponent(new Velocity({
+ x: 0,
+ y: Math.PI / 180,
+ z: 0,
+ }));
+ entity.addComponent(new Radius({
+ radius: 5
+ }));
+ entity.addComponent(new Up());
+
+ return entity;
+}
diff --git a/lib/factories/ui.js b/lib/factories/ui.js
new file mode 100644
index 0000000..27ff6eb
--- /dev/null
+++ b/lib/factories/ui.js
@@ -0,0 +1,28 @@
+export function settingsContainer({id, label, level=2}) {
+
+ const container = document.createElement('div');
+ container.id = id;
+ container.innerHTML = `<h${level}>${label}</h${level}>`;
+ return container;
+};
+
+export function slider({min, max, step, get, set, label = '', className}) {
+
+ const sliderContainer = document.createElement('div');
+ sliderContainer.classList.add('slider');
+ sliderContainer.classList.add(className);
+ const labelElement = document.createElement('label');
+ labelElement.innerHTML = label;
+ const slider = document.createElement('input');
+ slider.type = 'range';
+ slider.min = min;
+ slider.max = max;
+ slider.step = step;
+ slider.value = get().toString();
+
+ slider.addEventListener('input', () => set(slider.value));
+
+ sliderContainer.appendChild(labelElement);
+ sliderContainer.appendChild(slider);
+ return sliderContainer;
+}
diff --git a/lib/main.js b/lib/main.js
index 6a2ea8f..6018957 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -1,8 +1,18 @@
import Serpentity from '@serpentity/serpentity';
+import { canvasId, settingsId, fps } from './config';
+
+// Systems
+import CameraRotator from './systems/camera_rotator';
import WebGLRenderer from './systems/webgl_renderer';
import LissajousPositionUpdater from './systems/lissajous_position_updater';
+import CameraAdjuster from './systems/camera_adjuster';
+import FrequencyAdjuster from './systems/frequency_adjuster';
+import AmplitudeAdjuster from './systems/amplitude_adjuster';
+import GlobalAdjuster from './systems/global_adjuster';
+
+// Factories
import { lissajousCurve } from './factories/curves';
-import { canvasId, fps } from './config';
+import { camera, configuration } from './factories/global';
const internals = {
@@ -16,12 +26,26 @@ const internals = {
// Create entities
const engine = new Serpentity();
- // Add Systems
- engine.addSystem(new LissajousPositionUpdater(canvasId));
- engine.addSystem(new WebGLRenderer(canvasId));
-
// Add Entities
+ engine.addEntity(camera());
engine.addEntity(lissajousCurve());
+ engine.addEntity(configuration());
+
+ // Select elements
+ const canvas = document.getElementById(canvasId);
+ const settings = document.getElementById(settingsId);
+
+ // Add Systems
+ engine.addSystem(new CameraRotator());
+ engine.addSystem(new LissajousPositionUpdater());
+ engine.addSystem(new WebGLRenderer(canvas));
+
+ // IMPROVEMENT NOTE: I believe adjusters can be generalized further.
+ // We could potentially have a factory for this.
+ engine.addSystem(new GlobalAdjuster(settings));
+ engine.addSystem(new CameraAdjuster(settings));
+ engine.addSystem(new FrequencyAdjuster(settings));
+ engine.addSystem(new AmplitudeAdjuster(settings));
internals.engine = engine;
},
diff --git a/lib/nodes/ample.js b/lib/nodes/ample.js
new file mode 100644
index 0000000..e48d250
--- /dev/null
+++ b/lib/nodes/ample.js
@@ -0,0 +1,7 @@
+import TripleAmplitude from '../components/triple_amplitude';
+import { Node } from '@serpentity/serpentity';
+
+export default class Ample extends Node {};
+Ample.types = {
+ amplitude: TripleAmplitude
+}
diff --git a/lib/nodes/cameras.js b/lib/nodes/cameras.js
new file mode 100644
index 0000000..56072d3
--- /dev/null
+++ b/lib/nodes/cameras.js
@@ -0,0 +1,15 @@
+import Position from '@serpentity/components.position';
+import EulerAngle from '@serpentity/components.euler_angle';
+import Velocity from '@serpentity/components.velocity';
+import Radius from '../components/radius';
+import Up from '../components/up';
+import { Node } from '@serpentity/serpentity';
+
+export default class Cameras extends Node {};
+Cameras.types = {
+ position: Position,
+ angle: EulerAngle,
+ velocity: Velocity,
+ radius: Radius,
+ up: Up
+}
diff --git a/lib/nodes/configurable.js b/lib/nodes/configurable.js
new file mode 100644
index 0000000..650f131
--- /dev/null
+++ b/lib/nodes/configurable.js
@@ -0,0 +1,7 @@
+import Configuration from '../components/configuration';
+import { Node } from '@serpentity/serpentity';
+
+export default class Configurable extends Node {};
+Configurable.types = {
+ configuration: Configuration,
+}
diff --git a/lib/nodes/frequent.js b/lib/nodes/frequent.js
new file mode 100644
index 0000000..de8de92
--- /dev/null
+++ b/lib/nodes/frequent.js
@@ -0,0 +1,7 @@
+import TripleFrequency from '../components/triple_frequency';
+import { Node } from '@serpentity/serpentity';
+
+export default class Frequent extends Node {};
+Frequent.types = {
+ frequency: TripleFrequency
+}
diff --git a/lib/nodes/lissajous_curve.js b/lib/nodes/lissajous_curve.js
index 402aea6..55179e8 100644
--- a/lib/nodes/lissajous_curve.js
+++ b/lib/nodes/lissajous_curve.js
@@ -1,9 +1,11 @@
import Position from '@serpentity/components.position';
import TripleFrequency from '../components/triple_frequency';
+import TripleAmplitude from '../components/triple_amplitude';
import { Node } from '@serpentity/serpentity';
export default class LissajousCurve extends Node {};
LissajousCurve.types = {
position: Position,
- frequency: TripleFrequency
+ frequency: TripleFrequency,
+ amplitude: TripleAmplitude
}
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);
+ }
+ }
};
diff --git a/lib/webgl_utils.js b/lib/webgl_utils.js
index 1386bf7..42bd8b6 100644
--- a/lib/webgl_utils.js
+++ b/lib/webgl_utils.js
@@ -25,14 +25,14 @@ export function initializeShaderProgram(gl, vertexShaderSource, fragmentShaderSo
*/
export function initializeBuffers(gl) {
- const positionBuffer = gl.createBuffer();
+ const positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- return {
- position: positionBuffer
- };
- }
+ return {
+ position: positionBuffer
+ };
+}
function loadShader(gl, type, source) {