scene/SkyBox.tstypescript
import * as shaders from "../shaders/skybox";
import { Scene } from "./Scene";
import type { WebGLBufferExt, WebGLTextureExt, Texture3DParams } from "../webgl/Handler";
import { Mat4 } from "../math/Mat4";
import { RADIANS_HALF } from "../math";

class SkyBox extends Scene {
    public params: Texture3DParams;
    public size: number;
    public vertexPositionBuffer: WebGLBufferExt | null;
    public texture: WebGLTextureExt | null;

    constructor(params: Texture3DParams, size: number = 10000) {
        super("skybox");
        this.params = params;
        this.size = size;
        this.vertexPositionBuffer = null;
        this.texture = null;
    }

    static createDefault(RESOURCES_URL: string) {
        return new SkyBox({
            nx: RESOURCES_URL + "skybox/nx.webp",
            px: RESOURCES_URL + "skybox/px.webp",
            py: RESOURCES_URL + "skybox/py.webp",
            ny: RESOURCES_URL + "skybox/ny.webp",
            pz: RESOURCES_URL + "skybox/pz.webp",
            nz: RESOURCES_URL + "skybox/nz.webp"
        });
    }

    public override init() {
        const h = this.renderer!.handler;
        const gl = h.gl!;
        h.addProgram(shaders.skybox());
        this.texture = h.loadCubeMapTexture(this.params, gl.SRGB8_ALPHA8, gl.LINEAR);
        this._createBuffers();
        this.drawMode = gl.TRIANGLES;
        this.renderer!.events.on("predraw", this._onPredraw, this);
    }

    public override onremove() {
        this.renderer?.events.off("predraw", this._onPredraw);
    }

    public setSize(size: number) {
        this.size = size;
        if (this.renderer && this.vertexPositionBuffer) {
            this.renderer.handler.gl!.deleteBuffer(this.vertexPositionBuffer);
            this._createBuffers();
        }
    }

    protected _onPredraw = () => {
        let h = this.renderer!.handler;
        let gl = h.gl!;
        let cam = this.renderer!.activeCamera!;
        gl.disable(gl.DEPTH_TEST);

        h.programs.skybox.activate();
        let sh = h.programs.skybox;
        let shu = sh.uniforms;
        if (cam.isOrthographic) {
            const near = cam.frustum.near;
            const far = cam.frustum.far;
            const aspect = cam.getAspectRatio();
            const top = near * Math.tan(cam.viewAngle * RADIANS_HALF);
            const right = top * aspect;
            const proj = new Mat4().setPerspective(-right, right, -top, top, near, far);
            const view = new Mat4().set(cam.getViewMatrix());
            view._m[12] = 0.0;
            view._m[13] = 0.0;
            view._m[14] = 0.0;
            gl.uniformMatrix4fv(shu.projectionViewMatrix, false, proj.mul(view)._m);
        } else {
            gl.uniformMatrix4fv(shu.projectionViewMatrix, false, cam.getProjectionViewRTEMatrix());
        }
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.texture!);
        gl.uniform1i(shu.uSampler, 0);
        let buf = this.vertexPositionBuffer!;
        gl.bindBuffer(gl.ARRAY_BUFFER, buf);
        gl.vertexAttribPointer(sh.attributes.aVertexPosition, buf.itemSize, gl.FLOAT, false, 0, 0);

        gl.drawArrays(this.drawMode, 0, buf.numItems);
        gl.enable(gl.DEPTH_TEST);
    };

    protected _createBuffers() {
        const size = this.size;
        const vertices = new Float32Array([
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,

            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,

            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,

            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,

            -1 * size,
            1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size,
            -1 * size,

            -1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            -1 * size,
            1 * size,
            1 * size,
            -1 * size,
            1 * size
        ]);

        this.vertexPositionBuffer = this.renderer!.handler.createArrayBuffer(vertices, 3, vertices.length / 3);
    }
}

export { SkyBox };