Search code examples
javascriptwebgl2twgl.js

TWGL Camera rotation not expected


I'm so confused,

I am trying to use mouse and keyboard controls (With WebGL2), and I'm trying to rotate and move the camera with these four functions:

m4.rotateX(m, angleInRadians, dst);
m4.rotateY(m, angleInRadians, dst);
m4.rotateX(m, angleInRadians, dst);
m4.translate(m, v, dst);

(Provided by twgl) And it works fine with X, and Y (When seperate), but when added it provides weird output (As of a gyroscope) Heres what I use to rotate the camera and create the scene:

(() => {
    class Camera {
        static setPosRot(x, y, z, cx, cy, cz, camera) {
/*
x,y,z = Players X,Y,Z Position
cx,cy,cz = Players X,Y,Z Rotation
*/
            m4.identity(camera);
            m4.translate(camera, [
                x,
                y,
                z
            ], camera);
            m4.rotateX(camera, ((cx - 90) * Math.PI) / 180, camera);
            m4.rotateY(camera, (cy * Math.PI) / 180, camera);
            m4.rotateZ(camera, (cz * Math.PI) / 180, camera);
            return camera;
        }
    }

    class Block {
        #texture;
        #x;
        #y;
        #z;

        set texture(texture) {
            this.#texture = texture;
        }

        get getTexture() {
            return texture;
        }

        set x(x = 0) {
            x = parseInt(x);
            if (isNaN(x)) {
                console.warn(`Invalid X pos, exiting`);
                return;
            }
            this.#x = x;
            return;
        }

        set y(y = 0) {
            y = parseInt(y);
            if (isNaN(y)) {
                console.warn(`Invalid Y pos, exiting`);
                return;
            }
            this.#y = y;
            return;
        }

        set z(z = 0) {
            z = parseInt(z);
            if (isNaN(z)) {
                console.warn(`Invalid Z pos, exiting`);
                return;
            }
            this.#z = z;
            return;
        }

        get getX() {
            return this.#x;
        }

        get getY() {
            return this.#y;
        }

        get getZ() {
            return this.#z;
        }

        set pos(pos = [
            0,
            0,
            0
        ]) {
            var x = parseInt(pos[0]);
            var y = parseInt(pos[1]);
            var z = parseInt(pos[2]);
            if (isNaN(x)) {
                console.warn(`Invalid X pos, setting to default.`);
                x = 0;
            }
            if (isNaN(y)) {
                console.warn(`Invalid Y pos, setting to default.`);
                y = 0;
            }
            if (isNaN(z)) {
                console.warn(`Invalid Z pos, setting to default.`);
                z = 0;
            }
            this.#x = x;
            this.#y = y;
            this.#z = z;
            return;
        }

        get getPos() {
            return [
                this.#x,
                this.#y,
                this.#z
            ]
        }

        render() {
            m4.identity(Render.world);
            m4.translate(Render.world, [
                this.#x * 3,
                this.#y * 3,
                this.#z * 3
            ], Render.world);

            m4.multiply(Render.viewProjection, Render.world, Render.worldViewProjection);
            m4.inverse(Render.world, Render.worldInverse);
            m4.transpose(Render.worldInverse, Render.worldInverseTranspose);

            twgl.setBuffersAndAttributes(Render.gl, Render.pinfo, Render.binfo);
            twgl.setUniforms(Render.pinfo, {
                u_worldViewProjection: Render.worldViewProjection,
                u_worldInverseTranspose: Render.worldInverseTranspose,
                u_color: [
                    (this.#x + 2) / 3,
                    (this.#y + 2) / 3,
                    (this.#z + 2) / 3,
                    1
                ],
                u_lightDir: Render.lightDir,
            });
            twgl.drawBufferInfo(Render.gl, Render.binfo);
        }

        constructor(x = 0, y = 0, z = 0) {
            var x = parseInt(x);
            var y = parseInt(y);
            var z = parseInt(z);
            if (isNaN(x)) {
                console.warn(`Invalid X pos, setting to default.`);
                x = 0;
            }
            if (isNaN(y)) {
                console.warn(`Invalid Y pos, setting to default.`);
                y = 0;
            }
            if (isNaN(z)) {
                console.warn(`Invalid Z pos, setting to default.`);
                z = 0;
            }
            this.#x = x;
            this.#y = y;
            this.#z = z;
            return;
        }
    }

    class Render {
        static vertexShader = [
`precision highp float;`,
`uniform mat4 u_worldViewProjection;`,
`uniform mat4 u_worldInverseTranspose;`,
`attribute vec4 position;`,
`attribute vec3 normal;`,
`varying vec3 v_normal;`,
`void main() {`,
    `gl_Position = u_worldViewProjection * position;`,
    `v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;`,
`}`
        ].join(`\n`);

        static fragmentShader = [
`precision highp float;`,
`varying vec3 v_normal;`,
`uniform vec3 u_lightDir;`,
`uniform vec4 u_color;`,
`void main() {`,
`vec3 norm = normalize(v_normal);`,
    `float light = dot(u_lightDir, norm) * .5 + .5;`,
    `gl_FragColor = vec4(u_color.rgb * light, u_color.a);`,
`}`
        ].join(`\n`);

        static canvas;
        static gl;
        static gl2d;
        static program;

        static width = 1920;
        static height = 1080;

        static pinfo;
        static binfo;

        static #cx = 90;
        static #cy = 0;
        static #cz = 0;

        static #x = 0.0;
        static #y = 0.0;
        static #z = 0.0;

        static webgloptions = {
            powerPreference: `default`,
            antialias: true,
            depth: true
        };

        static load() {
this.width = document.documentElement.clientWidth;
            this.height = document.documentElement.clientHeight;
            this.canvas = document.createElement(`canvas`);
            this.canvas.id = `root`;
            this.canvas.width = this.width;
            this.canvas.height = this.height;
            this.canvas.style.width = this.width;
            this.canvas.style.height = this.height;
            document.body.appendChild(this.canvas);
            this.canvas = document.getElementById(`root`);
            this.canvas.addEventListener(`contextmenu`, event => event.preventDefault());
            this.gl = this.canvas.getContext(`webgl2`, this.webgloptions);
            this.gl2d = this.canvas.getContext(`2d`, this.webgloptions); // Rendering UI
            if (this.gl == null || this.gl == undefined || !this.gl) {
                console.warn(`WebGL2 not supported, attempting to use WebGL, expect bugs.`);
                this.gl = this.canvas.getContext(`webgl`, this.webgloptions) || this.canvas.getContext(`experimental-webgl`, this.webgloptions);
                if (this.gl == null || this.gl == undefined || !this.gl) {
                    console.error(`Neither WebGL nor WebGL2 is supported, exiting`);
                    return true;
                }
            }
            this.pinfo = twgl.createProgramInfo(this.gl, [
                this.vertexShader,
                this.fragmentShader
            ]);
            this.binfo = twgl.primitives.createCubeBufferInfo(this.gl, 1);
            this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
            return false;
        }

        static projection = m4.identity();
        static camera = m4.identity();
        static view = m4.identity();
        static viewProjection = m4.identity();
        static world = m4.identity();
        static worldViewProjection = m4.identity();
        static worldInverse = m4.identity();
        static worldInverseTranspose = m4.identity();
        static lightDir = v3.normalize([1, 2, 3]);

        static render() {
            requestAnimationFrame(this.render.bind(this));
            var now = Date.now() * 0.001;
            this.#deltaTime = now - this.#then;
            this.#then = now;
            this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
            this.gl.clearDepth(1.0);
            this.gl.viewport(0, 0, this.width, this.height);
            this.gl.enable(this.gl.DEPTH_TEST);
            this.gl.enable(this.gl.CULL_FACE);
            this.gl.depthFunc(this.gl.LEQUAL);
            this.gl.useProgram(this.pinfo.program);
            this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
            const fov = (this.#fov * Math.PI) / 180;
            const aspect = this.gl.canvas.clientWidth / this.gl.canvas.clientHeight;
            m4.perspective(fov, aspect, 0.0, 100.0, this.projection);
            /*
            m4.axisRotate(this.camera, m4.getAxis(this.camera, 0), (this.#cx - 90) * Math.PI / 180, this.camera);
            m4.axisRotate(this.camera, m4.getAxis(this.camera, 1), this.#cy * Math.PI / 180, this.camera);
            m4.axisRotate(this.camera, m4.getAxis(this.camera, 2), this.#cz * Math.PI / 180, this.camera);
            */
            /*
            m4.rotateX(this.camera, ((this.#cx - 90) * Math.PI) / 180, this.camera);
            m4.rotateY(this.camera, (this.#cy * Math.PI) / 180, this.camera);
            m4.rotateZ(this.camera, (this.#cz * Math.PI) / 180, this.camera);
            */

            this.camera = Camera.setPosRot(this.#x, this.#y, this.#z, this.#cx, this.#cy, this.#cz, this.camera);

            m4.inverse(this.camera, this.view);

            m4.multiply(this.projection, this.view, this.viewProjection);

            for (let z = -1; z <= 1; ++z) {
                for (let y = -1; y <= 1; ++y) {
                    for (let x = -1; x <= 1; ++x) {
                        if (x === 0 && y === 0 && z === 0) {
                            continue;
                        }

                        const block = new Block();
                        block.pos = [
                            x,
                            y,
                            z
                        ];
                        Render.addObj(block);
                    }
                }
            }
        }
    }
})();

(Taken from this website (Runnable))


Solution

  • Twgl does not rotate stuff like mat4 example: Twgl:

    function rotateX(m, angleInRadians, dst) {
      dst = dst || new MatType(16);
      const m10 = m[4];
      const m11 = m[5];
      const m12 = m[6];
      const m13 = m[7];
      const m20 = m[8];
      const m21 = m[9];
      const m22 = m[10];
      const m23 = m[11];
      const c = Math.cos(angleInRadians);
      const s = Math.sin(angleInRadians);
      dst[4] = c * m10 + s * m20;
      dst[5] = c * m11 + s * m21;
      dst[6] = c * m12 + s * m22;
      dst[7] = c * m13 + s * m23;
      dst[8] = c * m20 - s * m10;
      dst[9] = c * m21 - s * m11;
      dst[10] = c * m22 - s * m12;
      dst[11] = c * m23 - s * m13;
      if (m !== dst) {
        dst[0] = m[0];
        dst[1] = m[1];
        dst[2] = m[2];
        dst[3] = m[3];
        dst[12] = m[12];
        dst[13] = m[13];
        dst[14] = m[14];
        dst[15] = m[15];
      }
      return dst;
    }
    
    function rotateY(m, angleInRadians, dst) {
      dst = dst || new MatType(16);
      const m00 = m[0 * 4 + 0];
      const m01 = m[0 * 4 + 1];
      const m02 = m[0 * 4 + 2];
      const m03 = m[0 * 4 + 3];
      const m20 = m[2 * 4 + 0];
      const m21 = m[2 * 4 + 1];
      const m22 = m[2 * 4 + 2];
      const m23 = m[2 * 4 + 3];
      const c = Math.cos(angleInRadians);
      const s = Math.sin(angleInRadians);
      dst[0] = c * m00 - s * m20;
      dst[1] = c * m01 - s * m21;
      dst[2] = c * m02 - s * m22;
      dst[3] = c * m03 - s * m23;
      dst[8] = c * m20 + s * m00;
      dst[9] = c * m21 + s * m01;
      dst[10] = c * m22 + s * m02;
      dst[11] = c * m23 + s * m03;
      if (m !== dst) {
        dst[4] = m[4];
        dst[5] = m[5];
        dst[6] = m[6];
        dst[7] = m[7];
        dst[12] = m[12];
        dst[13] = m[13];
        dst[14] = m[14];
        dst[15] = m[15];
      }
      return dst;
    }
    
    function rotateZ(m, angleInRadians, dst) {
      dst = dst || new MatType(16);
      const m00 = m[0 * 4 + 0];
      const m01 = m[0 * 4 + 1];
      const m02 = m[0 * 4 + 2];
      const m03 = m[0 * 4 + 3];
      const m10 = m[1 * 4 + 0];
      const m11 = m[1 * 4 + 1];
      const m12 = m[1 * 4 + 2];
      const m13 = m[1 * 4 + 3];
      const c = Math.cos(angleInRadians);
      const s = Math.sin(angleInRadians);
      dst[0] = c * m00 + s * m10;
      dst[1] = c * m01 + s * m11;
      dst[2] = c * m02 + s * m12;
      dst[3] = c * m03 + s * m13;
      dst[4] = c * m10 - s * m00;
      dst[5] = c * m11 - s * m01;
      dst[6] = c * m12 - s * m02;
      dst[7] = c * m13 - s * m03;
      if (m !== dst) {
        dst[8] = m[8];
        dst[9] = m[9];
        dst[10] = m[10];
        dst[11] = m[11];
        dst[12] = m[12];
        dst[13] = m[13];
        dst[14] = m[14];
        dst[15] = m[15];
      }
      return dst;
    }
    

    Mat4 (What im looking for):

    function rotateX(out, a, rad) {
      let s = Math.sin(rad);
      let c = Math.cos(rad);
      let a10 = a[4];
      let a11 = a[5];
      let a12 = a[6];
      let a13 = a[7];
      let a20 = a[8];
      let a21 = a[9];
      let a22 = a[10];
      let a23 = a[11];
      if (a !== out) {
        out[0] = a[0];
        out[1] = a[1];
        out[2] = a[2];
        out[3] = a[3];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
      }
      out[4] = a10 * c + a20 * s;
      out[5] = a11 * c + a21 * s;
      out[6] = a12 * c + a22 * s;
      out[7] = a13 * c + a23 * s;
      out[8] = a20 * c - a10 * s;
      out[9] = a21 * c - a11 * s;
      out[10] = a22 * c - a12 * s;
      out[11] = a23 * c - a13 * s;
      return out;
    }
    
    function rotateY(out, a, rad) {
      let s = Math.sin(rad);
      let c = Math.cos(rad);
      let a00 = a[0];
      let a01 = a[1];
      let a02 = a[2];
      let a03 = a[3];
      let a20 = a[8];
      let a21 = a[9];
      let a22 = a[10];
      let a23 = a[11];
      if (a !== out) {
        out[4] = a[4];
        out[5] = a[5];
        out[6] = a[6];
        out[7] = a[7];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
      }
      out[0] = a00 * c - a20 * s;
      out[1] = a01 * c - a21 * s;
      out[2] = a02 * c - a22 * s;
      out[3] = a03 * c - a23 * s;
      out[8] = a00 * s + a20 * c;
      out[9] = a01 * s + a21 * c;
      out[10] = a02 * s + a22 * c;
      out[11] = a03 * s + a23 * c;
      return out;
    }
    
    function rotateZ(out, a, rad) {
      let s = Math.sin(rad);
      let c = Math.cos(rad);
      let a00 = a[0];
      let a01 = a[1];
      let a02 = a[2];
      let a03 = a[3];
      let a10 = a[4];
      let a11 = a[5];
      let a12 = a[6];
      let a13 = a[7];
      if (a !== out) {
        out[8] = a[8];
        out[9] = a[9];
        out[10] = a[10];
        out[11] = a[11];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
      }
      out[0] = a00 * c + a10 * s;
      out[1] = a01 * c + a11 * s;
      out[2] = a02 * c + a12 * s;
      out[3] = a03 * c + a13 * s;
      out[4] = a10 * c - a00 * s;
      out[5] = a11 * c - a01 * s;
      out[6] = a12 * c - a02 * s;
      out[7] = a13 * c - a03 * s;
      return out;
    }