mpetroff / pannellum

Pannellum is a lightweight, free, and open source panorama viewer for the web.
https://pannellum.org/
MIT License
4.21k stars 717 forks source link

Change of Scene hangs ( dynamicUpdate vs dynamic only and viewer.setUpdate() ) #836

Open martinlombana opened 4 years ago

martinlombana commented 4 years ago

If I have multiple scenes wher I have passed canvases (instead of images), isn't it more efficient to set dynamic=true and leave dynamicUpdate=false, and call viewer.setUpdate(true);, update the canvas, and then call viewer.setUpdate(false); ?

The reasons why I am asking is are:

  1. Only when I set dynamicUpdate = true, I am really able to load a different scene after the 1st one has been loaded.

  2. If I I set dynamicUpdate = false;, even if I set the viewer.setUpdate(true) before changing the scene and after doing it, viewer.setUpdate(false), it simply doesn't work and it gets hung forever in the "loading..." screen.

I suspect it has something to do with line 445 in panellum.js

https://github.com/mpetroff/pannellum/blob/15e0d09fec8d7ea56adb67f30736c81d46f1aa88/src/js/pannellum.js#L445

It seems related to this one:

https://github.com/mpetroff/pannellum/issues/686

martinlombana commented 4 years ago

This is a sample testing code that does the following:

1) Creates 2 dynamic scenes based on 2 canvases and scene hot-spots that link to the other scene. 2) Loads scene "s2" 3) After 3 seconds, draws something different in the assciated canvas 4) After 5 seconds, tries to load scene 1.

Also, if you comment the point 4 and try to navigate with the custom spots, it doesn't work either (I added a custom handler function also, to force the update, but it doesn't work).

<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Tour</title>
    <link href="pannellum.css" rel="stylesheet" />
    <script src="pannellum.js" type="text/javascript"></script>
    <script src="libpannellum.js" type="text/javascript"></script>
    <style>
        #panorama {
            width: 800px;
            height: 800px;
        }

        .hidden-canvas {
            display: none;
        }
    </style>
</head>

<body>

    <div id="panorama"></div>

    <script>

        var canvas1;
        var canvas2;
        var viewer;

        createCanvases();
        createViewer();

        // Does not work either, even if I setUpdate(true);
        setTimeout(() => {
            viewer.loadScene("s2", 45.06449476984468, -103.99684074728088, 100);
            viewer.setUpdate(true);
            viewer.setUpdate(false);
        }, 1000);

        setTimeout(() => {
            // viewer.loadScene("s1", 45.06449476984468, -103.99684074728088, 100);

            viewer.setUpdate(true);
            c = document.getElementById("canvas2");
            var ctx = c.getContext("2d");
            ctx.fillStyle = "rgba(255, 0, 0, 0.2)";
            ctx.fillRect(100, 100, 200, 200);
            ctx.fillStyle = "rgba(0, 255, 0, 0.2)";
            ctx.fillRect(150, 150, 200, 200);
            ctx.fillStyle = "rgba(0, 0, 255, 0.2)";
            ctx.fillRect(200, 50, 200, 200);

            viewer.setUpdate(false);

        }, 3000);

        setTimeout(() => {
            viewer.setUpdate(true);
            viewer.loadScene("s1", 45.06449476984468, -103.99684074728088, 100);
            viewer.setUpdate(false);
        }, 5000); 

         window.addEventListener('contextmenu', () => {
            console.log(this.viewer.getPitch(), this.viewer.getYaw(), this.viewer.getHfov())
        }) 

        function createViewer() {
            var config = {};
            config.scenes = {}
            config.type = 'equirectangular';
            config.dynamic = true;
            config.dynamicUpdate = false;
            config.sceneFadeDuration = 800;
            config.disableKeyboardCtrl = false;
            config.mouseZoom = true;

            viewer = pannellum.viewer('panorama', config);

            viewer.addScene('s1', {
                'type': 'equirectangular',
                'panorama': canvas1
            });

            viewer.addScene('s2', {
                'type': 'equirectangular',
                'panorama': canvas2
            });

            var onHotSpotClick = (event, args) => {
                console.log('onHotSpotClick')
                this.viewer.setUpdate(true);
                this.viewer.loadScene(args["target_scene_id"]);
                this.viewer.setUpdate(false);
            }

            var hs1 = {
                pitch: 45,
                yaw: -103,
                type: 'scene',
                id: '01',
                targetPitch: 45,
                targetYaw: -103,
                targetHfov: 100,
                clickHandlerFunc: onHotSpotClick,
                clickHandlerArgs: { target_scene_id: 's2' }
            };

            viewer.addHotSpot(hs1, "s1")

            var hs2 = {
                pitch: 25,
                yaw: -83,
                type: 'scene',
                id: '02',
                targetPitch: 45,
                targetYaw: -93,
                targetHfov: 80,
                clickHandlerFunc: onHotSpotClick,
                clickHandlerArgs: { target_scene_id: 's1' }
            };
            viewer.addHotSpot(hs2, "s2")

        }

        function createCanvases() {
            canvas1 = document.createElement('canvas');
            canvas1.id = "canvas1";
            canvas1.width = 1224;
            canvas1.height = 768;
            canvas1.style.zIndex = 8;
            canvas1.style.position = "absolute";
            canvas1.style.border = "1px solid";
            var body = document.getElementsByTagName("body")[0];
            canvas1.classList.add('hidden-canvas');
            body.appendChild(canvas1);

            canvas2 = document.createElement('canvas');
            canvas2.id = "canvas2";
            canvas2.width = 1224;
            canvas2.height = 768;
            canvas2.style.zIndex = 8;
            canvas2.style.position = "absolute";
            canvas2.style.border = "1px solid";
            canvas2.style.left = "444px";

            var body = document.getElementsByTagName("body")[0];
            canvas2.classList.add('hidden-canvas');
            body.appendChild(canvas2);

            c = document.getElementById("canvas1");
            var ctx = c.getContext("2d");
            ctx.fillStyle = "rgba(255, 0, 0, 0.2)";
            ctx.fillRect(100, 100, 200, 200);
            ctx.fillStyle = "rgba(0, 255, 0, 0.2)";
            ctx.fillRect(150, 150, 200, 200);
            ctx.fillStyle = "rgba(0, 0, 255, 0.2)";
            ctx.fillRect(200, 50, 200, 200);

            c = document.getElementById("canvas2");
            var ctx = c.getContext("2d");
            ctx.fillStyle = "rgba(255, 32, 0, 0.2)";
            ctx.fillRect(100, 100, 1200, 700);

        }

    </script>

</body>

</html>

Please, find attatched a ready to test zip with this use-case. testpan.zip

martinlombana commented 4 years ago

Also, I don't know. Maybe just letting dynamicUpdate = true, forever, causes no issues whatsoever, but I have the feeling it would be more appropiate to just set the viewer.setUpdate(true) before doing changes and setting it back to false after modifying the canvas. Right?

mpetroff commented 4 years ago

As <canvas> elements do not have onload events, dynamicUpdate must be set to true to avoid waiting for the non-existent events to (never) fire. To maximize efficiency, you should add a listener for Pannellum's load event to run viewer.setUpdate(false) as soon as the scene is loaded; otherwise, the viewer will continue to run the render loop even if there's no movement.

martinlombana commented 4 years ago

Ok. So I leave, in the starting config: dynamic = true; dynamicUpdate = true;

And then, when I have loded the canvas, drawn in to it my image, I should call viewer.setUpdate(false);. And everytime I want to update the canvas "on the fly" if there are dynamic actions going on, I call viewer.setUpdate(true); before and after that is done. Correct?

mpetroff commented 4 years ago

Correct