diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2024-05-06 22:28:48 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2024-05-06 22:28:48 +0200 |
| commit | 5f6ef99eae91f53239f08143cead1249893fef81 (patch) | |
| tree | fac495ea31140060ccd0b916897bbd7adf72fc2f /lib | |
Initial commit
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/components/color.js | 14 | ||||
| -rw-r--r-- | lib/components/triple_frequency.js | 12 | ||||
| -rw-r--r-- | lib/config.js | 3 | ||||
| -rw-r--r-- | lib/factories/curves.js | 14 | ||||
| -rw-r--r-- | lib/main.js | 62 | ||||
| -rw-r--r-- | lib/nodes/drawable.js | 9 | ||||
| -rw-r--r-- | lib/nodes/lissajous_curve.js | 9 | ||||
| -rw-r--r-- | lib/systems/lissajous_position_updater.js | 44 | ||||
| -rw-r--r-- | lib/systems/webgl_renderer.js | 149 | ||||
| -rw-r--r-- | lib/webgl_utils.js | 51 |
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; +} |