aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2024-05-06 22:28:48 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2024-05-06 22:28:48 +0200
commit5f6ef99eae91f53239f08143cead1249893fef81 (patch)
treefac495ea31140060ccd0b916897bbd7adf72fc2f /lib
Initial commit
Diffstat (limited to 'lib')
-rw-r--r--lib/components/color.js14
-rw-r--r--lib/components/triple_frequency.js12
-rw-r--r--lib/config.js3
-rw-r--r--lib/factories/curves.js14
-rw-r--r--lib/main.js62
-rw-r--r--lib/nodes/drawable.js9
-rw-r--r--lib/nodes/lissajous_curve.js9
-rw-r--r--lib/systems/lissajous_position_updater.js44
-rw-r--r--lib/systems/webgl_renderer.js149
-rw-r--r--lib/webgl_utils.js51
10 files changed, 367 insertions, 0 deletions
diff --git a/lib/components/color.js b/lib/components/color.js
new file mode 100644
index 0000000..a9877bf
--- /dev/null
+++ b/lib/components/color.js
@@ -0,0 +1,14 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class Color extends Component {
+ constructor(config) {
+
+ super(config);
+
+ this.r = Math.random();
+ this.g = Math.random();
+ this.b = Math.random();
+ this.a = 1.0;
+ }
+};
+
diff --git a/lib/components/triple_frequency.js b/lib/components/triple_frequency.js
new file mode 100644
index 0000000..aa768e3
--- /dev/null
+++ b/lib/components/triple_frequency.js
@@ -0,0 +1,12 @@
+import { Component } from '@serpentity/serpentity';
+
+export default class TripleFrequency extends Component {
+ constructor(config) {
+
+ super(config);
+
+ this.a = Math.random();
+ this.b = Math.random();
+ this.c = Math.random();
+ }
+};
diff --git a/lib/config.js b/lib/config.js
new file mode 100644
index 0000000..bb8e11e
--- /dev/null
+++ b/lib/config.js
@@ -0,0 +1,3 @@
+export const canvasId = 'lissajous';
+
+export const fps = 30;
diff --git a/lib/factories/curves.js b/lib/factories/curves.js
new file mode 100644
index 0000000..d9f0f47
--- /dev/null
+++ b/lib/factories/curves.js
@@ -0,0 +1,14 @@
+import { Entity } from '@serpentity/serpentity';
+import Position from '@serpentity/components.position';
+import TripleFrequency from '../components/triple_frequency';
+import Color from '../components/color';
+
+export function lissajousCurve() {
+
+ const entity = new Entity();
+ entity.addComponent(new Position());
+ entity.addComponent(new TripleFrequency());
+ entity.addComponent(new Color());
+
+ return entity;
+}
diff --git a/lib/main.js b/lib/main.js
new file mode 100644
index 0000000..6a2ea8f
--- /dev/null
+++ b/lib/main.js
@@ -0,0 +1,62 @@
+import Serpentity from '@serpentity/serpentity';
+import WebGLRenderer from './systems/webgl_renderer';
+import LissajousPositionUpdater from './systems/lissajous_position_updater';
+import { lissajousCurve } from './factories/curves';
+import { canvasId, fps } from './config';
+
+const internals = {
+
+ // State variables
+
+ started: false,
+ currentTime: 0,
+ engine: null,
+
+ load() {
+ // Create entities
+ const engine = new Serpentity();
+
+ // Add Systems
+ engine.addSystem(new LissajousPositionUpdater(canvasId));
+ engine.addSystem(new WebGLRenderer(canvasId));
+
+ // Add Entities
+ engine.addEntity(lissajousCurve());
+
+ internals.engine = engine;
+ },
+
+ unload() {
+ internals.engine = null;
+ },
+
+ run(time) {
+
+ const delta = time - internals.currentTime;
+ const interval = 1000 / fps;
+
+ if (delta >= interval) {
+ internals.engine.update(delta);
+ internals.currentTime = time;
+ }
+
+ internals.started && window.requestAnimationFrame(internals.run);
+ },
+
+ start() {
+ if (!internals.started) {
+ internals.started = true;
+ internals.load();
+ window.requestAnimationFrame(internals.run);
+ }
+ },
+
+ stop() {
+ if (internals.started) {
+ internals.started = false;
+ internals.unload();
+ }
+ }
+};
+
+internals.start();
diff --git a/lib/nodes/drawable.js b/lib/nodes/drawable.js
new file mode 100644
index 0000000..77fd969
--- /dev/null
+++ b/lib/nodes/drawable.js
@@ -0,0 +1,9 @@
+import Position from '@serpentity/components.position';
+import Color from '../components/color';
+import { Node } from '@serpentity/serpentity';
+
+export default class Drawable extends Node {};
+Drawable.types = {
+ position: Position,
+ color: Color
+}
diff --git a/lib/nodes/lissajous_curve.js b/lib/nodes/lissajous_curve.js
new file mode 100644
index 0000000..402aea6
--- /dev/null
+++ b/lib/nodes/lissajous_curve.js
@@ -0,0 +1,9 @@
+import Position from '@serpentity/components.position';
+import TripleFrequency from '../components/triple_frequency';
+import { Node } from '@serpentity/serpentity';
+
+export default class LissajousCurve extends Node {};
+LissajousCurve.types = {
+ position: Position,
+ frequency: TripleFrequency
+}
diff --git a/lib/systems/lissajous_position_updater.js b/lib/systems/lissajous_position_updater.js
new file mode 100644
index 0000000..0f5792d
--- /dev/null
+++ b/lib/systems/lissajous_position_updater.js
@@ -0,0 +1,44 @@
+import { System } from '@serpentity/serpentity';
+import LissajousCurve from '../nodes/lissajous_curve';
+
+const internals = {
+ kAmplitude: 0.8,
+ kPeriod: Math.PI * 12000000
+};
+
+export default class WebGLRenderer extends System {
+
+ constructor() {
+
+ super();
+ }
+
+ added(engine){
+
+ this.curves = engine.getNodes(LissajousCurve);
+ this.time = 0;
+ }
+
+ removed(){
+
+ this.curves = undefined;
+ this.time = undefined;
+ }
+
+ update(dt){
+
+ 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);
+ }
+ console.log('UP');
+ }
+
+ _getPosition(amplitude, frequency, time, phaseShift) {
+
+ return amplitude * Math.sin(frequency * time + phaseShift);
+ }
+};
diff --git a/lib/systems/webgl_renderer.js b/lib/systems/webgl_renderer.js
new file mode 100644
index 0000000..8535fde
--- /dev/null
+++ b/lib/systems/webgl_renderer.js
@@ -0,0 +1,149 @@
+import { System } from '@serpentity/serpentity';
+import Drawable from '../nodes/drawable';
+import { initializeShaderProgram, initializeBuffers } from '../webgl_utils';
+
+const internals = {
+ kWidthRatio: 2.76,
+ kHeightRatio: 1,
+ kTargetVerticalResolution: 32 + Math.round(Math.random() * 1024),
+ kVertexShader: `
+
+ attribute vec3 aVertexPosition;
+ void main() {
+ gl_Position = vec4(aVertexPosition.xy, 0.0, 1.0);
+ gl_PointSize = 10.0; // Set the point size
+ }
+ `,
+
+ kFragmentShader: `
+
+ precision mediump float;
+ uniform vec4 uColor;
+ void main() {
+ gl_FragColor = uColor;
+ }
+ `
+};
+
+export default class WebGLRenderer extends System {
+
+ constructor(canvasId) {
+
+ super();
+ this.canvasId = canvasId;
+ }
+
+ added(engine){
+
+ // Set up canvas
+ const canvas = document.getElementById(this.canvasId);
+ window.addEventListener('resize', () => this._resizeCanvas(canvas));
+
+ // Set up WebGL
+ const gl = canvas.getContext('webgl', {
+ preserveDrawingBuffer: true
+ });
+ this.gl = gl;
+
+ const shaderProgram = initializeShaderProgram(
+ gl,
+ internals.kVertexShader,
+ internals.kFragmentShader
+ );
+
+ this.programInfo = {
+ program: shaderProgram,
+ attribLocations: {
+ vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition')
+ },
+ uniformLocations: {
+ color: gl.getUniformLocation(shaderProgram, 'uColor')
+ }
+ };
+
+ this.buffers = initializeBuffers(gl);
+ this._resizeCanvas(canvas);
+
+ this.points = engine.getNodes(Drawable);
+ }
+
+ removed(engine){
+
+ this.points = undefined;
+ }
+
+ update(){
+
+ const {gl, programInfo, buffers} = this;
+
+ gl.useProgram(programInfo.program);
+
+ {
+ const color = [1, 1, 1, 1];
+ gl.uniform4fv(programInfo.uniformLocations.color, color);
+ }
+
+ const positions = [];
+ for (const point of this.points) {
+ positions.push(point.position.x, point.position.y, point.position.z);
+ positions.push(
+ point.position.prevX || point.position.x,
+ point.position.prevY || point.position.y,
+ point.position.prevZ || point.position.z
+ );
+ point.position.prevX = point.position.x;
+ point.position.prevY = point.position.y;
+ point.position.prevZ = point.position.z;
+ }
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
+ gl.bufferData(gl.ARRAY_BUFFER,
+ new Float32Array(positions),
+ gl.STATIC_DRAW);
+
+ {
+ const numberOfComponents = 3;
+ const type = gl.FLOAT;
+ const normalize = false;
+ const stride = 0;
+ const offset = 0;
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
+ gl.vertexAttribPointer(
+ programInfo.attribLocations.vertexPosition,
+ numberOfComponents,
+ type,
+ normalize,
+ stride,
+ offset
+ );
+ gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
+ }
+
+ {
+ gl.lineWidth(2);
+ gl.drawArrays(gl.LINES, 0, this.points.nodes.length * 2);
+ }
+ }
+
+ _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`;
+ canvas.style.height = `${height}px`;
+
+ canvas.width = internals.kTargetVerticalResolution * internals.kWidthRatio;
+ canvas.height = internals.kTargetVerticalResolution;
+
+ this.gl.viewport(0, 0, canvas.width, canvas.height);
+ }
+};
diff --git a/lib/webgl_utils.js b/lib/webgl_utils.js
new file mode 100644
index 0000000..1386bf7
--- /dev/null
+++ b/lib/webgl_utils.js
@@ -0,0 +1,51 @@
+/**
+ * Initializes the WebGL Shader program
+ */
+export function initializeShaderProgram(gl, vertexShaderSource, fragmentShaderSource) {
+
+ const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
+ const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
+
+ const shaderProgram = gl.createProgram();
+ gl.attachShader(shaderProgram, vertexShader);
+ gl.attachShader(shaderProgram, fragmentShader);
+
+ gl.linkProgram(shaderProgram);
+
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ console.error(`Unable to initialize shader program: ${gl.getProgramInfoLog(shaderProgram)}`);
+ return null;
+ }
+
+ return shaderProgram;
+}
+
+/**
+ * Initializes the buffers
+ */
+export function initializeBuffers(gl) {
+
+ const positionBuffer = gl.createBuffer();
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+
+ return {
+ position: positionBuffer
+ };
+ }
+
+
+function loadShader(gl, type, source) {
+
+ const shader = gl.createShader(type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ console.error(`Unable to compile shader: ${gl.getShaderInfoLog(shader)}`);
+ gl.deleteShader(shader);
+ return null;
+ }
+
+ return shader;
+}