WebController - Utility Methods

This section includes all the functions that are useful to the sample, but don’t involve any functions from the Genvid API.

popularityToText

popularityToText(popularity)

The popularityToText method converts a popularity value to a text value.

    // Converts popularity value to popularity text
    popularityToText(popularity) {
        const hearts = ["💔", "♡", "♥", "💕"];
        const levels = [0.1, 1.0, 5.0];
        for (let i = 0; i < hearts.length; ++i) {
            if (popularity < levels[i]) {
                return hearts[i];
            }
        }
        return hearts[levels.length];
    }

makeCircleZ

makeCircleZ(dst, x, y, z, r, c)

The makeCircleZ method prepares a circle on the XY plane centered at coordinates x,y,z of radius r and color c. All these values are sent as parameters to the method. We use it to determine the correct positions for each WebGL circle.

    // Utility routine preparing a circle on the XY-plane
    // centered at {x,y,z}, of radius r and color c.
    makeCircleZ(dst, x, y, z, r, c) {
        dst.push(
            // X   Y   Z     U    V      R    G    B    A
            x - r, y - r, z, 0.0, 0.0, c.x, c.y, c.z, c.w,
            x + r, y - r, z, 1.0, 0.0, c.x, c.y, c.z, c.w,
            x + r, y + r, z, 1.0, 1.0, c.x, c.y, c.z, c.w,
            x - r, y + r, z, 0.0, 1.0, c.x, c.y, c.z, c.w
        );
    }

projectWithRadius

projectWithRadius(mat, pos3d, rad3d)

The projectWithRadius method converts a 3D radius around a 3D position using the viewProjection matrix. We use it to determine a hitbox for each circle when trying to click on it.

    // Converts a @rad3d around a 3D position @pos3d using the viewProjection matrix @mat.
    // Returns an array [pos2d, rad2d].
    projectWithRadius(mat, pos3d, rad3d) {
        // There might be a more mathematically sound solution to this,
        // but I've decided to use the shotgun approach and just project
        // 8 positions (add/sub radius to every dimension), and keep
        // the one which yields the largest 2D distance.
        let pos2d = genvidMath.projectPosition(mat, pos3d);
        let radSq2d = 0;
        let offsets = [
            genvidMath.vec3(rad3d, 0, 0),
            genvidMath.vec3(-rad3d, 0, 0),
            genvidMath.vec3(0, rad3d, 0),
            genvidMath.vec3(0, -rad3d, 0),
            genvidMath.vec3(0, 0, rad3d),
            genvidMath.vec3(0, 0, -rad3d),
        ];
        for (let i = 0; i < offsets.length; ++i) {
            let off = offsets[i];
            let nPos3d = genvidMath.add3D(pos3d, off);
            let nPos2d = genvidMath.projectPosition(mat, nPos3d);
            let nRadSq2d = genvidMath.lengthSq2D(genvidMath.sub2D(pos2d, nPos2d));
            radSq2d = Math.max(radSq2d, nRadSq2d);
        }
        return [pos2d, Math.sqrt(radSq2d)];
    }

centerAt

centerAt(htmlElement, pos_2d, offsetPixels)

The centerAt method modifies an HTML element position to be centered at the 2D position sent. We use it to move the name div-element displayed above each object.

    // Changes the HTML element position to be at the center of the pos 2d sent
    centerAt(htmlElement, pos2d, offsetPixels) {

        // Converts from [-1, 1] range to [0, 1].
        let vh = genvidMath.vec2(0.5, 0.5);
        let pos2dN = genvidMath.mad2D(pos2d, vh, vh);

        // Converts from [0, 1] range to [0, w].
        let p = htmlElement.parentElement;
        let pSize = genvidMath.vec2(p.clientWidth, p.clientHeight);
        let posInParent = genvidMath.mul2D(pos2dN, pSize);

        // Adjusts for centering element.
        let eSize = genvidMath.vec2(htmlElement.clientWidth, htmlElement.clientHeight);
        let eOffset = genvidMath.muls2D(eSize, -0.5);
        let posCentered = genvidMath.add2D(posInParent, eOffset);

        // Applies user offset.
        const posFinal = genvidMath.sub2D(posCentered, offsetPixels);
        Object.assign(htmlElement.style, {
            left: posFinal.x + "px",
            bottom: posFinal.y + "px",
            position: "absolute",
            zIndex: "1100"
        });

    }

preN

preN(str, n, c)

The preN method adds empty spaces to a string. We use it to correctly display the data on the Genvid overlay.

    // Widens a string to at least n characters, prefixing with spaces.
    preN(str, n, c) {
        let s = str.length;
        if (s < n) {
            str = c.repeat(n - s) + str;
        }
        return str;
    }

convertMatrix

convertMatrix(rawmat)

The convertMatrix method converts an array of numbers into a Genvid Math 4x4 Matrix. We use it when doing operations that need to use the proper matrix-format, since we recieve the data sent from the game as an array of numbers.

    // Converts a 4x4 matrix from 16 labeled scalars into a GenvidMath.mat4 instance.
    convertMatrix(rawmat) {
        return genvidMath.mat4(
            genvidMath.vec4(rawmat[0], rawmat[4], rawmat[8], rawmat[12]),
            genvidMath.vec4(rawmat[1], rawmat[5], rawmat[9], rawmat[13]),
            genvidMath.vec4(rawmat[2], rawmat[6], rawmat[10], rawmat[14]),
            genvidMath.vec4(rawmat[3], rawmat[7], rawmat[11], rawmat[15]));
    }

findOrCreateTagDiv

findOrCreateTagDiv(cube)

The findOrCreateTagDiv method finds or creates the div that displays the name of the object that moves with the object.

    // Finds or creates the div needed to display the name of the cube on the cube
    findOrCreateTagDiv(cube) {
        let elemId = "cube_tag_" + cube.name;
        let elem = document.querySelector("#" + elemId);
        if (elem == null) {
            elem = document.createElement("div");
            elem.id = elemId;
            elem.textContent = cube.name;
            elem.classList.add("tag");

            let parent = document.querySelector("#video_overlay");
            parent.appendChild(elem);

            elem.addEventListener("click", () => this.onSelect(elem.textContent), false);
            this.cubeTagElements.push(elem);
        }
        elem.style.display = "block";
        return elem;
    }

msToDuration

msToDuration(duration)

The msToDuration method converts milliseconds into days, hours, minutes, and seconds.

    // Method used to convert ms to specific duration
    msToDuration(duration) {
        const msInADay = 1000 * 60 * 60 * 24;
        const date = new Date(duration);
        const days = (duration - (duration % msInADay)) / msInADay;
        return `${days ? `${days}:` : ""}${date.toLocaleTimeString(undefined, {hour12: false, timeZone: "GMT"})}:${this.preN(date.getMilliseconds().toFixed(0), 3, "0")}`;
    }

checkFullScreen

checkFullScreen()

The checkFullScreen method returns the current fullscreen status.

    checkFullScreen() {
        return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
    }