webgl/variableHandlers.tstypescript
/**
 * @module og/webgl/variableHandlers
 */

import { types } from "./types";
import { ShaderProgram } from "./ShaderProgram";
import type { WebGLBufferExt } from "./Handler";
import type { WebGLTextureExt } from "./Handler";

export type VariableHandler = {
    u: { [id: number]: Function };
    a: { [id: number]: Function };
};

export type ProgramVariable = {
    type: string | number;
    func: Function;
    _pName: WebGLUniformLocation | number;
    value: number | Float32Array | Int32Array | WebGLBufferExt | WebGLTextureExt | WebGLTextureExt[];
    itemType: string | number;
    normalized: boolean;
    divisor: number;
};

/*=========================
   Uniforms callbacks
 =========================*/
export const variableHandlers: VariableHandler = {
    u: [],
    a: []
};

variableHandlers.u[types.MAT4] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniformMatrix4fv(variable._pName, false, variable.value as Float32Array);
};

variableHandlers.u[types.MAT3] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniformMatrix3fv(variable._pName, false, variable.value as Float32Array);
};

variableHandlers.u[types.FLOAT] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform1f(variable._pName, variable.value as number);
};

variableHandlers.u[types.INT] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform1i(variable._pName, variable.value as number);
};

variableHandlers.u[types.VEC2] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform2fv(variable._pName, variable.value as Float32Array);
};

variableHandlers.u[types.VEC3] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform3fv(variable._pName, variable.value as Float32Array);
};

variableHandlers.u[types.VEC4] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform4fv(variable._pName, variable.value as Float32Array);
};

variableHandlers.u[types.SAMPLER2D] = function (program: ShaderProgram, variable: ProgramVariable) {
    let pgl = program.gl!;
    pgl.activeTexture(pgl.TEXTURE0 + program._textureID);
    pgl.bindTexture(pgl.TEXTURE_2D, variable.value);
    pgl.uniform1i(variable._pName, program._textureID);
    program._textureID++;
};

variableHandlers.u[types.SAMPLERCUBE] = function (program: ShaderProgram, variable: ProgramVariable) {
    let pgl = program.gl!;
    pgl.activeTexture(pgl.TEXTURE0 + program._textureID);
    pgl.bindTexture(pgl.TEXTURE_CUBE_MAP, variable.value);
    pgl.uniform1i(variable._pName, program._textureID);
    program._textureID++;
};

variableHandlers.u[types.SAMPLER3D] = function (program: ShaderProgram, variable: ProgramVariable) {
    let pgl = program.gl!;
    pgl.activeTexture(pgl.TEXTURE0 + program._textureID);
    pgl.bindTexture(pgl.TEXTURE_3D, variable.value);
    pgl.uniform1i(variable._pName, program._textureID);
    program._textureID++;
};

variableHandlers.u[types.SAMPLER2DARRAY] = function (program: ShaderProgram, variable: ProgramVariable) {
    const value = variable.value as any;

    // Legacy mode for array uniforms declared as sampler2D[N]
    if (Array.isArray(value) || ArrayBuffer.isView(value)) {
        variableHandlers.u[types.SAMPLER2DARRAYLEGACY](program, variable);
        return;
    }

    // Real sampler2DArray texture (TEXTURE_2D_ARRAY)
    let pgl = program.gl!;
    pgl.activeTexture(pgl.TEXTURE0 + program._textureID);
    pgl.bindTexture(pgl.TEXTURE_2D_ARRAY, value as WebGLTextureExt);
    pgl.uniform1i(variable._pName, program._textureID);
    program._textureID++;
};

variableHandlers.u[types.SAMPLER2DARRAYLEGACY] = function (program: ShaderProgram, variable: ProgramVariable) {
    let pgl = program.gl!;
    let value = variable.value as Int32Array | WebGLTextureExt[];

    if (!value || value.length === 0) {
        pgl.uniform1iv(variable._pName, new Int32Array(0));
        return;
    }

    // Pre-bound texture units path.
    if (typeof value[0] === "number") {
        pgl.uniform1iv(variable._pName, value as Int32Array);
        return;
    }

    // Legacy sampler2D array path: bind one texture per texture unit.
    let size = value.length;
    let samplerArr = new Int32Array(size);
    for (let i = 0; i < size; i++) {
        let textureUnit = program._textureID + i;
        pgl.activeTexture(pgl.TEXTURE0 + textureUnit);
        pgl.bindTexture(pgl.TEXTURE_2D, value[i] as WebGLTextureExt);
        samplerArr[i] = textureUnit;
    }
    pgl.uniform1iv(variable._pName, samplerArr);
    program._textureID += size;
};

variableHandlers.u[types.INTXX] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform1iv(variable._pName, variable.value as Int32Array);
};

variableHandlers.u[types.FLOATXX] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.uniform1fv(variable._pName, variable.value as Float32Array);
};

/*========================
   Attributes callbacks
 ========================*/
variableHandlers.a[types.FLOAT] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.vertexAttrib1f(variable._pName as number, variable.value as number);
};

variableHandlers.a[types.VEC2] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.vertexAttrib2fv(variable._pName as number, variable.value as Float32Array);
};

variableHandlers.a[types.VEC3] = function (program: ShaderProgram, variable: ProgramVariable) {
    program.gl!.vertexAttrib3fv(variable._pName as number, variable.value as Float32Array);
};

//VariableHandlers.a[types.VEC4] = function (program, variable) {
//    program.gl.vertexAttrib4fv(variable._pName, variable.value);
//};