socorro
Version:
Siervos de la Obra del Socorro (S.O.S)
413 lines (362 loc) • 14.2 kB
JavaScript
/*
* =============================================================================
*
* M Ó D U L O E S C E N A
*
* =============================================================================
*/
import CONFIG from './config';
import Esquema from './esquema';
/**
* Escena
* Entidad principal de la Obra que articula la reproducción
* de contenidos en el lienzo del navegador (el "canvas").
* La Obra puede estar compuesta por una o múltiples escenas.
*
* A cada escena se le asigna un "orquestador" encargado de
* instrumentar los tres actos en los que ésta se divide:
*
* - ACTO 1 ("Preparación"): Cargar archivos que van a utilizarse.
* - ACTO 2 ("Iniciación") : Configuración y armado inicial de la escena.
* - ACTO 3 ("Ejecución") : Despliegue (cuadro a cuadro) de la escena.
*
* NOTA 1: La escena es la entidad que permite encapsular el uso de las
* librerías para la generación de gráficos (p5js o Three.js).
* NOTA 2: La escena hereda las funciones del esquema.
*
*/
function Escena(sos) {
const S = sos.socorrista();
let _contenedor;
// Shaders
let _vertexShader, _fragmentShader;
let _p5Shader;
// Definición e inicialización de variables "uniform"
const _uniforms = {};
let _nombreUniformTiempo = CONFIG.UNIFORM_TIEMPO;
let _nombreUniformResolucion = CONFIG.UNIFORM_RESOLUCION;
let _nombreUniformMouse = CONFIG.UNIFORM_MOUSE;
// Variables Three.js
let camera, scene;
let rendererTHREE;
let rendererP5;
// =====================================================================
//
// DEFINICIÓN DE LA "FUNCION ACTUARIA" (LOS TRES ACTOS)
//
// =====================================================================
/**
* functionActuaria
* Definición dinámica de las funciones a ser invocadas
* para cada uno de los tres actos de la escena.
*/
function functionActuaria() {
const _FUNCION = {};
/**
* ACTO DE PREPARACIÓN (método "preload" de p5js)
* Función estándar que se ejecuta una vez, al inicio, y se
* utiliza para cargar archivos como shaders, imágenes, etc.
*/
_FUNCION[CONFIG.ACTO_PREPARACION] = () => {
};
/**
* ACTO DE INICIACIÓN (método "setup" de p5js)
* Función estándar que se ejecuta una vez, al inicio y justo
* después de que haya finalizado el "Acto de Preparación".
* Se utiliza para configurar la escena (por ejemplo, sus
* dimensiones) y para definir las variables "uniform".
*/
_FUNCION[CONFIG.ACTO_INICIACION] = () => {
};
/**
* ACTO DE EJECUCIÓN (método "draw" de p5js)
* Función estándar que se ejecuta indefinidamente "en bucle"
* y se encarga de desplegar la escena (cuadro a cuadro).
*/
_FUNCION[CONFIG.ACTO_EJECUCION] = () => {
if (rendererTHREE) {
rendererTHREE.render(scene, camera);
}
if (rendererP5) {
S.O.S.P5.push();
S.O.S.P5.noStroke();
S.O.S.P5.shader(_p5Shader);
S.O.S.P5.plane(_contenedor.geometria.ancho, _contenedor.geometria.alto);
S.O.S.P5.pop();
}
};
return _FUNCION;
}
// =====================================================================
//
// FUNCIONES PARA EL MANEJO DE LOS "SHADERS" DE LA OBRA
//
// =====================================================================
function vertexShader(shader) {
if (shader !== undefined) {
_vertexShader = shader;
}
return _vertexShader;
}
function fragmentShader(shader) {
if (shader !== undefined) {
_fragmentShader = shader;
}
return _fragmentShader;
}
// =====================================================================
//
// FUNCIONES PARA LA MANIPULACIÓN DE LAS VARIABLES "UNIFORM"
//
// =====================================================================
function uniformTiempo(nombre) {
if (nombre !== undefined) {
_nombreUniformTiempo = nombre;
uniform(nombre, 1.0);
}
return uniform(nombre);
}
function uniformResolucion(nombre) {
if (nombre !== undefined) {
_nombreUniformResolucion = nombre;
uniform(nombre, new S.O.S.THREE.Vector2());
}
return uniform(nombre);
}
function uniformMouse(nombre) {
if (nombre !== undefined) {
_nombreUniformMouse = nombre;
uniform(nombre, new S.O.S.THREE.Vector2());
}
return uniform(nombre);
}
function uniform(nombre, valor, valor2) {
if (valor2 !== undefined) {
let v = new S.O.S.THREE.Vector2(valor, valor2);
return uniform(nombre, v);
}
if (valor !== undefined) {
if (!_uniforms.hasOwnProperty(nombre))
_uniforms[nombre] = {};
_uniforms[nombre][CONFIG.UNIFORM_VALOR] = valor;
uniformP5(nombre, valor);
}
else {
if (!_uniforms.hasOwnProperty(nombre) || !_uniforms[nombre].hasOwnProperty(CONFIG.UNIFORM_VALOR)) {
return undefined;
}
}
return _uniforms[nombre];
}
function uniformP5(nombre, valor) {
if (valor !== null && _p5Shader) {
if (typeof valor === 'object' && !Array.isArray(valor)) {
if (valor.hasOwnProperty('x') && valor.hasOwnProperty('y') && valor.hasOwnProperty('z')) {
_p5Shader.setUniform(nombre, [valor.x, valor.y, valor.z]);
}
else if (valor.hasOwnProperty('x') && valor.hasOwnProperty('y')) {
_p5Shader.setUniform(nombre, [valor.x, valor.y]);
}
else if (valor.hasOwnProperty('contenido')) {
_p5Shader.setUniform(nombre, valor.contenido());
}
else {
_p5Shader.setUniform(nombre, valor);
}
}
else {
_p5Shader.setUniform(nombre, valor);
}
}
}
function uniformTiempoP5(valor) {
uniformP5(_nombreUniformTiempo, valor);
}
function uniformResolucionP5(valor) {
uniformP5(_nombreUniformResolucion, valor);
}
function uniformMouseP5(valor) {
uniformP5(_nombreUniformMouse, valor);
}
// =====================================================================
//
// FUNCIONES PARA EMPLAZAR y MANIPULAR EL "LIENZO" DE LA ESCENA
//
// =====================================================================
/**
* ancho
* Devuelve el ancho de la escena, o sea, la anchura
* del lienzo donde se realiza el "render".
*/
function ancho() {
if (_contenedor) {
return _contenedor.geometria.ancho;
}
return 0;
}
/**
* alto
* Devuelve el alto de la escena, o sea, la altura
* del lienzo donde se realiza el "render".
*/
function alto() {
if (_contenedor) {
return _contenedor.geometria.alto;
}
return 0;
}
/**
* dimensionar
* Establece el ancho y alto del lienzo donde se
* representa la escena (el canvas del renderer).
*/
function dimensionar(ancho, alto) {
if (rendererTHREE) {
rendererTHREE.setSize(ancho, alto);
}
if (rendererP5) {
S.O.S.P5.resizeCanvas(ancho, alto);
S.O.S.P5.ortho(-ancho / 2, ancho / 2, -alto / 2, alto / 2);
}
}
/**
* lienzo
* Devuelve el lienzo (el "canvas" HTML) que se utiliza
* para llevar a cabo el "render" de la escena.
*/
function lienzo() {
_contenedor.lienzo();
}
/**
* emplazar
* Función principal que encapsula los llamados a las librerías necesarias
* para construir el "canvas" de la escena en la página HTML.
* El emplazamiento tiene lugar una vez que finalizaron los 2 primeros actos
* y justo antes de iniciar el bucle de ejecución.
*/
function emplazar(contenedor) {
_contenedor = contenedor;
if (!S.O.S.P5) {
_emplazarLienzoTHREE();
}
else {
_emplazarLienzoP5();
}
}
/**
* _emplazarLienzoTHREE
* Realiza el emplazamiento del "canvas" únicamente a través de la
* librería Three.js
*/
function _emplazarLienzoTHREE() {
// 1. CREACIÓN DEL LIENZO (CANVAS)
// La creación del lienzo o canvas es diferida hasta el momento del
// emplazamiento de la escena en la página. Se supone que en este
// punto el contenedor (elemento HTML) ya fue creado y sus dimensiones
// fueron ajustadas por el navegador en función del tamaño del viewport.
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
rendererTHREE = new S.O.S.THREE.WebGLRenderer();
rendererTHREE.setPixelRatio(window.devicePixelRatio);
_contenedor.lienzo(rendererTHREE.domElement);
// 2. CREACIÓN DE VERDADERA ESCENA
// Una vez que el lienzo fue creado y emplazado en la página, se procede
// con la creación de la escena mediante la libreraí Three.js
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
scene = new S.O.S.THREE.Scene();
camera = new S.O.S.THREE.Camera();
camera.position.z = 1;
// 3. DEFINICIÓN DE LOS SHADERS
// Por último, se obtienen los "shaders" a emplear para la escena, se
// definen las variables "uniform" y se invocan las funciones de la
// librería Three.js para ejecutar los "shaders" de la escena.
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
let _vshader = _vertexShader && _vertexShader.contenido() ? _vertexShader.contenido() : CONFIG.VERTEX_SHADER_THREE;
let atributos = {
uniforms : _uniforms,
vertexShader : _vshader,
fragmentShader : _fragmentShader.contenido(),
};
let material = new S.O.S.THREE.ShaderMaterial(atributos);
let geometry = new S.O.S.THREE.PlaneGeometry( 2, 2 );
let mesh = new S.O.S.THREE.Mesh( geometry, material );
scene.add( mesh );
}
/**
* _emplazarLienzoP5
* Realiza el emplazamiento del "canvas" únicamente a través de la
* librería p5js
*/
function _emplazarLienzoP5() {
rendererP5 = S.O.S.P5.createCanvas(_contenedor.geometria.ancho, _contenedor.geometria.alto, S.O.S.P5.WEBGL);
_contenedor.lienzo(rendererP5.canvas);
// Se verifica si se indicó algún shader
if ((_vertexShader && _vertexShader.contenido()) ||
(_fragmentShader && _fragmentShader.contenido())) {
let _vshader = _vertexShader && _vertexShader.contenido() ? _vertexShader.contenido() : CONFIG.VERTEX_SHADER_P5;
_p5Shader = S.O.S.P5.createShader(_vshader, _fragmentShader.contenido());
// Se definen los valores iniciales de los "uniforms"
// que hayan sido creados hasta el momento.
for (const [uNombre, uValor] of Object.entries(_uniforms)) {
if (uValor.hasOwnProperty(CONFIG.UNIFORM_VALOR)) {
uniformP5(uNombre, uValor[CONFIG.UNIFORM_VALOR]);
}
}
}
}
// =====================================================================
//
// FUNCIONES MISCELÁNEAS DE INICIALIZACIÓN Y MANTENIMIENTO
//
// =====================================================================
/**
* inicializar
* Función que crea la "Definición" del esquema asociado a la escena.
* Los atributos que se inicialicen en este punto podrán ser importados,
* exportados y/o manipulados desde la GUI del "Panel Conrolador".
* >>> PENDIENTE DE IMPLEMENTACIÓN
*/
function inicializar() {
}
/**
* asociar
* Asocia componentes como parte del socorrista designado.
* Se exponen las librerías de p5js y Three.js a través del
* socorrista creado para atender a la instancia de la escena.
*/
function asociar(nombre, componente) {
if (nombre == 'THREE') {
S.O.S.THREE = componente;
}
else if (nombre == 'P5') {
S.O.S.P5 = componente;
}
else {
S.O.S[nombre] = componente;
}
}
// ===============================================================
// ===> Se exponen únicamente las funciones públicas de la escena
// ==> ("Revealing Module Pattern") y se implementa la herencia.
// ===============================================================
return S.O.S.revelar({vertexShader,
fragmentShader,
uniformTiempo,
uniformResolucion,
uniformMouse,
uniform,
uniformP5,
uniformTiempoP5,
uniformResolucionP5,
uniformMouseP5,
ancho,
alto,
dimensionar,
lienzo,
emplazar,
asociar,
inicializar
},
functionActuaria(), // Se adicionan los métodos de la "Función Actuaria"
Esquema(S.O.S, CONFIG.NOMBRE_ESCENA)); // Se heredan las funciones públicas del "Esquema"
}
export default Escena;