WebController - Enter フレーム

Enter フレームセクションには、各フレームまたは同様の方法でトリガされるメソッドが含まれています。

onNewFrame

onNewFrame(frameSource)

onNewFrame メソッドは、常に更新が必要な様々なタスクを実行します。作成した Genvid クライアント用に、フレームごとに呼び出します。

  1. まず、 updateOverlays(compositionData) を用いてオーバーレイを更新し、ビデオストリームの構成に合わせてオーバーレイを更新します。ピクチャモードでピクチャ内のオーバーレイをクロップすることができます。
        updateOverlays(compositionData) {
            let multipleSourceVideoComposition = false;
            let hideOverlay = false;
            // Do we have multiple sources?
            if (compositionData && compositionData.length > 1) {
                // Assuming the second element to be the foreground frame.
                if (compositionData[1].type == "PipVideoLayout") {
                    // Prevent the 3d overlay to overlay the secondary screen
                    this.pipFrameDiv.style.display = "block";
                    hideOverlay = true;
                    multipleSourceVideoComposition = true;
                    // set up the main canvas to pip affine transform matrix
                    const pipMat = genvidMath.mat3FromArray(compositionData[1].affineMatrix);
                    this.updateDomRect(this.pipFrameDiv, this.videoOverlay, pipMat);
                    this.updateDomClipping(this.canvas3d, pipMat);
                } else if (compositionData[1].type == "ChromaKeyVideoLayout") {
                    multipleSourceVideoComposition = true;
                }
            } 
            if (!hideOverlay) {
                this.pipFrameDiv.style.display = "none";
                if (this.canvas3d) this.canvas3d.style.removeProperty("clip-path");
            }
            return multipleSourceVideoComposition;
        }
    
  2. アノテーションデータを取得します。有効かどうかを確認し、その後コンソールログを表示します。

  3. 動画が操作を実行できる状態になっているかどうかを確認します。

  4. ビデオの準備ができたら、さまざまな処理を行う前に、ゲームデータを取得してから、そのデータが有効であることを確認します。

  5. データの妥当性が確認できたら、データ (キューブ) からオブジェクトリストを取得します。

  6. 次のカメラとシーンまでの残り時間のテキストフィールドを更新します。

  7. プレイヤーテーブルをセットアップしたかどうかを確認します。設定されていない場合、 initPlayerTable を呼び出します。

  8. オブジェクト配列を使用して、WebGL で輪を作るループ処理を作成します。

  9. 同じループで、各オブジェクトの popularity 値と位置を更新します。

  10. ループ処理の終了後、WebGL の輪を描画します。

            if (this.videoReady) {
                if (gameDataFrame && gameDataFrame.user) {
                    
                    const gameData = gameDataFrame.user;
                    this.lastGameData = gameData;
                    const cubes = gameData.cubes;
    
                    // Lets update the time of camera and scene. Only for the Unity sample.
                    if (gameData.camera && gameData.scene) {
                        this.timeCamSceneDiv.textContent = ` Time until next camera: ${gameData.camera.toFixed(2)} seconds
                    Time until next scene: ${gameData.scene.toFixed(2)} seconds`;
                    }
    
    
                    // Performs the webgl update process -- Update 3d
                    if (cubes) {
                        // Sets up the player table once the game data is ready
                        if (this.cubePanelDiv.length === 0) {
                            this.initPlayerTable(cubes);
                        }
    
                        const vertices = [];
                        const projViewMatrix = this.convertMatrix(gameData.MatProjView);
                        for (const cube of cubes) {
                            const m = cube.mat;
                            const p = genvidMath.vec3(m[12], m[13], m[14]);
                            const r = this.circleRadius;
                            let c = genvidMath.vec4(1, 0, 0, 1);
                            if (cube.color) {
                                c = genvidMath.vec4(cube.color[0], cube.color[1], cube.color[2], cube.color[3]);
                            }
                            if (this.isSelected(cube.name)) {
                                c = genvidMath.muls4D(c, 2.0); // brighten selected color intensity
                            } else {
                                c = genvidMath.muls4D(c, 0.8); // dim unselected color intensity
                            }
                            this.makeCircleZ(vertices, p.x, p.y, p.z, r, c);
    
                            // Makes the name tag of the cube follow the cube
                            const tag = this.findOrCreateTagDiv(cube);
                            const pos2d = genvidMath.projectPosition(projViewMatrix, p);
                            this.centerAt(tag, pos2d, genvidMath.vec2(0, -75));
    
                            // Modifies the popularity with the latest value
                            if (this.latestPopularity) {
                                const pCube = this.latestPopularity.find((c) => {
                                    return c.name === cube.name;
                                });
                                if (pCube) {
                                    cube.popularity = pCube.popularity;
                                }
                            }
    
                            let cubePopSpan = document.querySelector(`#${cube.name} .cheer_value`);
                            cubePopSpan.textContent = this.popularityToText(cube.popularity);
    
                            let cubePosXSpan = document.querySelector(`#${cube.name} .position_x`);
                            cubePosXSpan.textContent = cube.mat[12].toFixed(2);
    
                            let cubePosYSpan = document.querySelector(`#${cube.name} .position_y`);
                            cubePosYSpan.textContent = cube.mat[13].toFixed(2);
    
                            let cubePosZSpan = document.querySelector(`#${cube.name} .position_z`);
                            cubePosZSpan.textContent = cube.mat[14].toFixed(2);
                        }
    
                        let numQuads = vertices.length / (4 * 9);
    
                        this.gfxCmdCubes.vtx = this.genvidWebGL.createBuffer(new Float32Array(vertices));
                        this.gfxCmdCubes.idx = this.genvidWebGL.createIndexBufferForQuads(numQuads);
                    }
    
                    this.gfxProgDataViewproj = gameData.MatProjView;
    
                    this.gfxDraw3D();
                }
    
  11. その後、Genvid オーバーレイの値とプロンプトオーバーレイの可視性 (ユーザーの変更をサウンドに表示するために使用) を更新します。

                // Updates the Genvid information overlay
                let width = 18; // Width of the content of every line (without label).
                this.timeLocalDiv.textContent = `Local: ${this.preN(this.msToDuration(Date.now()), width, " ")}`;
    
                this.timeVideoDiv.textContent = `Est. Video: ${this.preN(this.msToDuration(Math.round(this.genvidClient.videoTimeMS)), width, " ")}`;
    
                this.timeComposeLastDiv.textContent = `Stream received: ${this.preN(this.msToDuration(Math.round(this.genvidClient.lastComposeTimeMS)), width, " ")}`;
    
                this.timeStreamDiv.textContent = `Stream played: ${this.preN(this.msToDuration(Math.round(this.genvidClient.streamTimeMS)), width, " ")}`;
    
                this.latencyDiv.textContent = `Latency: ${this.preN(this.genvidClient.streamLatencyMS.toFixed(0), width - 3, " ")} ms`;
    
                this.delayOffsetDiv.textContent = `DelayOffset: ${this.preN(this.genvidClient.delayOffset.toFixed(0), width - 3, " ")} ms`;
    
                // Updates the visibility on the overlay when using key press
                if (this.volumeDisplay.style.visibility === "visible" && this.volumeInfoDisplayCount < this.volumeInfoDisplayUntil) {
                    this.volumeInfoDisplayCount++;
                } else if (this.volumeDisplay.style.visibility === "visible" && this.volumeInfoDisplayCount >= this.volumeInfoDisplayUntil) {
                    this.volumeDisplay.style.visibility = "hidden";
                }
            }
    

initPlayerTable

initPlayerTable(cubeData)

initPlayerTable は、各オブジェクトのストリームウィンドウの下にテーブルを作成します。

  1. 受信したゲームデータに基づいて、各オブジェクトの情報について繰り返すループ処理から始めます。

  2. あとで HTML タグに追加するノードを複製します。

  3. 次に、テーブル、応援、リセットのクリック機能のイベントリスナーを追加します。

  4. 最後に、使用可能な色のループ処理を行い、クリックイベントリスナーを追加します。

        // Method used to display the appropriate number of players with their proper buttons
        initPlayerTable(cubeData) {
            let cubePanel = document.getElementById("cube_panel_prototype");
    
            for (let cube of cubeData) {
                let cubePanelClone = cubePanel.cloneNode(true);
                cubePanelClone.id = cube.name;
                cubePanelClone.getElementsByClassName("cube_name")[0].innerText = cube.name;
    
                let cheerButton = cubePanelClone.querySelector(".cheer");
                cheerButton.addEventListener("click", () => this.onCheer(cube.name), false);
    
                let cubeDiv = cubePanelClone.querySelector(".cube");
                cubeDiv.addEventListener("click", () => this.onSelect(cube.name), false);
                this.cubePanelDiv.push(cubeDiv); // will stop triggering initPlayerTable from onNewFrame() indefinitely
    
                let resetButton = cubePanelClone.querySelector(".reset");
                resetButton.addEventListener("click", () => this.onReset(cube.name), false);
    
                for (let colorSelect of this.tableColor) {
                    let colorButton = cubePanelClone.querySelector("." + colorSelect[0]);
                    colorButton.addEventListener("click", () => this.onColorChange(cube.name, colorSelect[1]), false);
                }
                document.querySelector(".gameControlsDiv").append(cubePanelClone);
            }
            cubePanel.remove();
        }
    

onStreamsReceived

onStreamsReceived(dataStreams)

onStreamsReceived メソッドはループを使ってゲームデータを取得します。最初のループで受け取ったフォーマットが JSON の場合、データを JavaScript に変換します。フォーマットが UTF8 で ID が GameCopyright の場合は、 console.log で表示します。2 つ目のループでは、アノテーションの JSON データを JavaScript に変換します。Web サイトがストリームを受信したときに onStreamsReceived を呼び出します。

    // Upon receving the stream, gets the data
    onStreamsReceived(dataStreams) {
        for (let stream of dataStreams.streams) {
            // Using switch...case because different operations can be made depending on the stream ID.
            switch (this.streamIdToFormat[stream.id]) {
                case "JSON": {
                    // Parse the JSON from each elements
                    for (let frame of stream.frames) {
                        frame.user = JSON.parse(frame.data);
                    }
                    break;
                }
                case "UTF8": {
                    // Code handling UTF8 data format.
                    for (let frame of stream.frames) {
                        if (stream.id == "GameCopyright") genvid.log(frame.data);
                    }
                    break;
                }
                default:
                    break;
            }
        }
        for (let annotation of dataStreams.annotations) {
            // Using switch...case because different operations can be made depending on the stream ID.
            switch (this.annotationIdToFormat[annotation.id]) {
                case "JSON": {
                    // Parse the JSON from each elements
                    for (let frame of annotation.frames) {
                        frame.user = JSON.parse(frame.data);
                    }
                    break;
                }
                default:
                    break;
            }
        }
    }

onNotificationsReceived

onNotificationsReceived(message)

onNotificationsReceived` メソッドは通知の ID が Popularity であるかどうかを調べ、その際に JSON データを JavaScript に変換します。また、Web サイトが通知を受信したときにこのメソッドを呼び出します。

    // Upon receiving a notification, gets the notification content
    onNotificationsReceived(message) {
        for (let notification of message.notifications) {
            if (notification.id === "Popularity") {
                this.latestPopularity = JSON.parse(notification.data).cubes;
            }
        }
    }