Let's invite A(R)lice from Wonderland to our browsers

Let's invite
A(R)lice from Wonderland
to our browsers

Paweł Kamiński
twitter logo( kamyk_pl )

About me

  • Paweł Kamiński
  • 10+ around IT
  • before Java Full-Stack
  • now more JavaScript and front-ends
  • twitter logokamyk_pllinkedin logoLinkedIn
  • @iteratec
authors-img

iteratec logo

iteratec blocks iteratec blocks

Link to presentation

Agenda

agenda
  • Theory
  • Theory
  • Examples, Examples, Examples

Glossary

can i use webxr screenshot

AR - Augmented Reality

  • real world
  • extend by computer generated contentheadset is required
  • headset or mobile device requiredgoogle glass

Why

why

Been there done that

  • Pokemon Go

    Pokemon Go
  • Mozzies

    Symbian Mozzies
    Symbian OS ~ 2003

web-xr-logo

WebXR

  • The first proposed standard of  Immersive Web  working group at W3C
  • Mission:
    We help bring high-performance Virtual Reality (VR) and Augmented Reality (AR) (collectively known as XR) to the open Web via APIs to interact with XR devices and sensors in browsers.
  • low level JavaScript API which exposes the VR/AR platform capabilities

WebXR W3C Status

  • W3C receives a Submission
  • W3C publishes a Note
  • W3C creates a Working Group
  • W3C publishes a Working Draft

Can I use WebXR

can i use webxr screenshot

Status for 2019-10-28

Can I use WebXR

can i use webxr screenshot

Status for 2021-05-11

The best way to explain it is to do it

why

Disclaimer

why

Follow me

WebXR Requirements

WebXR Requirements

Pure JS AR Example

  • qr code with link to first example
  • qr code with link to first example

Request AR session


 function initXR() {
         if (!window.isSecureContext) {
             alert("AR experience requires https connection")
         }
         if (navigator.xr) {
             xrButton.addEventListener('click', onButtonClicked);
             navigator.xr.addEventListener('devicechange',
                                                checkSupportedState);
             checkSupportedState();
         }
     }
  function checkSupportedState() {
         navigator.xr.isSessionSupported('immersive-ar')
            .then((supported) => {
                 if (supported) {
                     xrButton.innerHTML = 'Enter AR';
              } else {
                     xrButton.innerHTML = 'AR not found';
                 }
                 xrButton.disabled = !supported;
            });
     }

Request AR session



     function onButtonClicked() {
       if (!xrSession) {
           navigator.xr.requestSession('immersive-ar', {})
              .then(onSessionStarted, () => {
                   alert("Failed to start immersive AR session.");
                   console.error(ex.message);
           });
       }
     }
     function onSessionStarted(session) {
       xrSession = session;
       session.addEventListener('end', onSessionEnded);
       let canvas = document.createElement('canvas');
       gl = canvas.getContext('webgl', {
           xrCompatible: true
       });
       session.updateRenderState({baseLayer: new XRWebGLLayer(session,gl)});
       session.requestReferenceSpace('local').then((refSpace) => {
           xrRefSpace = refSpace;
           session.requestAnimationFrame(onXRFrame);
       });
     }

WebGl to draw rectangle


const width = session.renderState.baseLayer.framebufferWidth;
const height = session.renderState.baseLayer.framebufferHeight;
gl.enable(gl.SCISSOR_TEST);
gl.bindFramebuffer(gl.FRAMEBUFFER,
                           session.renderState.baseLayer.framebuffer);
gl.scissor(width / 4, height / 4, width / 2, height / 2);
gl.clearColor(0, 255, 0, 0.5);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

Three js logo

Code overview


import ...

let camera, scene, renderer;

init();
animate();

function init() {
 ...
 drawScene()
 ...
}

function animate() {...}

function render() {...}

function drawScene() {...}

import


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                          /jsm/webxr/ARButton.js';

    let camera, scene, renderer;

    init();
    animate();


Three.js global objects


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                          /jsm/webxr/ARButton.js';

    let camera, scene, renderer;

    init();
    animate();


calling function


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                          /jsm/webxr/ARButton.js';

    let camera, scene, renderer;

    init();
    animate();


initialize scene and camera


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 drawScene();

 window.addEventListener('resize', ()=> {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
}
              |

initialize renderer


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 drawScene();

 window.addEventListener('resize', ()=> {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
}
              |

add ARButton


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 drawScene();

 window.addEventListener('resize', ()=> {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
}
              |

call drawScene


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 drawScene();

 window.addEventListener('resize', ()=> {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
}
              |

window resize handler


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 drawScene();

 window.addEventListener('resize', ()=> {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
}
              |

Code animate


function animate() {
   renderer.setAnimationLoop(render);
}

Code render


    function render() {
         renderer.render(scene, camera);
     }

Code drawScene


        function drawScene() {
             const materials = [
                 new THREE.MeshBasicMaterial({color: 0xff0000}),
                 new THREE.MeshBasicMaterial({color: 0x0000ff}),
                 new THREE.MeshBasicMaterial({color: 0x00ff00}),
                 new THREE.MeshBasicMaterial({color: 0xff00ff}),
                 new THREE.MeshBasicMaterial({color: 0x00ffff}),
                 new THREE.MeshBasicMaterial({color: 0xffff00})
             ];


             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3);
             scene.add(cube);
         }

Code drawScene


        function drawScene() {
             const materials = [
                 new THREE.MeshBasicMaterial({color: 0xff0000}),
                 new THREE.MeshBasicMaterial({color: 0x0000ff}),
                 new THREE.MeshBasicMaterial({color: 0x00ff00}),
                 new THREE.MeshBasicMaterial({color: 0xff00ff}),
                 new THREE.MeshBasicMaterial({color: 0x00ffff}),
                 new THREE.MeshBasicMaterial({color: 0xffff00})
             ];


             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3);
             scene.add(cube);
         }

Code html


<div id="info">
    <p>My first Three.js AR example</p>
    <a href="/">Back</a>
</div>

First Three.js example

  • qr code with link to second example
  • link to second example

add light


function init() {
 const container = document.createElement('div');
 document.body.appendChild(container);
 scene = new THREE.Scene();
 camera = new THREE.PerspectiveCamera(70, window.innerWidth
                               / window.innerHeight, 0.01, 20);

 renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
 renderer.setPixelRatio(window.devicePixelRatio);
 renderer.setSize(window.innerWidth, window.innerHeight);
 renderer.xr.enabled = true;
 container.appendChild(renderer.domElement);
 document.body.appendChild(ARButton.createButton(renderer, {}));

 const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
 light.position.set(0.5, 1, 0.25);
 scene.add(light);

 drawScene();

...

Use images


        function drawScene() {
             const materials = [
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/pers.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/dev.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/4dev.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ff00}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/elo.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ffff}),
             ];

             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3);
             scene.add(cube);
         }

Three.js example with images

  • qr code with link to third example
  • link to third example

add controller


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                          /jsm/webxr/ARButton.js';

    let camera, scene, renderer, controller;

    init();
    animate();


add onSelect


function init() {
 ...

 const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
 light.position.set(0.5, 1, 0.25);
 scene.add(light);

 controller = renderer.xr.getController(0);
 controller.addEventListener('select', onSelect);
 scene.add(controller);

 function onSelect() {
       drawScene();
  }

...

Use images


        function drawScene() {
             const materials = [
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/pers.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/dev.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/4dev.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ff00}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/elo.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ffff}),
             ];

             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3).applyMatrix4(controller.matrixWorld);
             scene.add(cube);
         }

Three.js example with interaction

  • qr code with link to fourth example
  • link to fourth example

cubes arrays


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                          /jsm/webxr/ARButton.js';

    let camera, scene, renderer, controller;
    const cubes = [];

    init();
    animate();


Use images


        function drawScene() {
             const materials = [
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/pers.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/dev.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/4dev.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ff00}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/elo.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ffff}),
             ];

             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3).applyMatrix4(controller.matrixWorld);
             cubes.push(cube);
             scene.add(cube);
         }

Code render


    function render() {
       cubes.forEach((c, i) => {
         if (i % 3 == 0) {
             c.rotation.x =
                    c.rotation.x - Math.PI / 300
         } else if (i % 3 == 1) {
             c.rotation.y =
                    c.rotation.y - Math.PI / 300
         } else if (i % 3 == 2) {
             c.rotation.z =
                    c.rotation.z - Math.PI / 300
         }
      });

      renderer.render(scene, camera);
     }

Three.js example with animation

  • qr code with link to fifth example
  • link to fifth example

Video html element


  <video id="video"
         loop
         muted
         crossOrigin="anonymous"
         playsinline
         style="display:none">
      <source src="res/video.mp4">
  </video>

  <div id="info">
      <p>Three.js example with video</p>
      <a href="/">Back</a>
  </div>

Use video


        function drawScene() {
             const video = document.getElementById('video');
             video.play();
             const materials = [
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/pers.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/dev.png')}),
               new THREE.MeshPhongMaterial({map: new THREE.VideoTexture(video)}),
               new THREE.MeshBasicMaterial({color: 0x00ff00}),
               new THREE.MeshPhongMaterial({map: new THREE.TextureLoader()
                                                              .load('res/elo.png')}),
               new THREE.MeshBasicMaterial({color: 0x00ffff}),
             ];
             const cube = new THREE.Mesh(
                             new THREE.BoxBufferGeometry(0.05, 0.05, 0.05)
                               , materials);
             cube.position.set(0, 0, -0.3).applyMatrix4(controller.matrixWorld);
             cubes.push(cube);
             scene.add(cube);
         }

Three.js example with video

  • qr code with link to sixth example
  • link to sixth example

GLTFLoader import


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                        /jsm/webxr/ARButton.js';

    import {GLTFLoader} from './three/examples/
                       /jsm/loaders/GLTFLoader.js';

    let camera, scene, renderer, controller;

    init();
    animate();


onSelect


function init() {
 ...

 const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
 light.position.set(0.5, 1, 0.25);
 scene.add(light);

 controller = renderer.xr.getController(0);
 controller.addEventListener('select', onSelect);
 scene.add(controller);

 function onSelect() {
       drawScene();
  }

...

Gltf loader

   function drawScene() {
      const loader = new GLTFLoader();
      loader.load('./models/dodo/scene.gltf',
           (gltf) => {
               const model = gltf.scene;
               model.position.set(0, 0, -0.3)
                                .applyMatrix4(controller.matrixWorld);
               model.scale.set(0.01, 0.01, 0.01)
               scene.add(model);
           },
           (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
           (e) => console.error(e)
        );
 }

Gltf loader success

   function drawScene() {
      const loader = new GLTFLoader();
      loader.load('./models/dodo/scene.gltf',
           (gltf) => {
               const model = gltf.scene;
               model.position.set(0, 0, -0.3)
                                .applyMatrix4(controller.matrixWorld);
               model.scale.set(0.01, 0.01, 0.01)
               scene.add(model);
           },
           (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
           (e) => console.error(e)
        );
 }

Gltf loader progress

   function drawScene() {
      const loader = new GLTFLoader();
      loader.load('./models/dodo/scene.gltf',
           (gltf) => {
               const model = gltf.scene;
               model.position.set(0, 0, -0.3)
                                .applyMatrix4(controller.matrixWorld);
               model.scale.set(0.01, 0.01, 0.01)
               scene.add(model);
           },
           (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
           (e) => console.error(e)
        );
 }

Gltf loader failure

   function drawScene() {
      const loader = new GLTFLoader();
      loader.load('./models/dodo/scene.gltf',
           (gltf) => {
               const model = gltf.scene;
               model.position.set(0, 0, -0.3)
                                .applyMatrix4(controller.matrixWorld);
               model.scale.set(0.01, 0.01, 0.01)
               scene.add(model);
           },
           (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
           (e) => console.error(e)
        );
 }

Three.js model

  • qr code with link to seventh example
  • link to seventh example

mixer and clock


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                        /jsm/webxr/ARButton.js';

    import {GLTFLoader} from './three/examples/
                       /jsm/loaders/GLTFLoader.js';

    let camera, scene, renderer, controller, mixer, clock;


    init();
    animate();


clock


function init() {
 ...

 const light = new THREE.HemisphereLight(0xffffff, 0xbbbbff, 1);
 light.position.set(0.5, 1, 0.25);
 scene.add(light);

 controller = renderer.xr.getController(0);
 controller.addEventListener('select', onSelect);
 scene.add(controller);

 clock = new THREE.Clock();

 function onSelect() {
       drawScene();
  }

...

mixer update


    function render() {
       if (mixer) {
            mixer.update(clock.getDelta());
        }

        renderer.render(scene, camera);
     }

play-animation

   function drawScene() {
      const loader = new GLTFLoader();
      loader.load('./models/dodo/scene.gltf',
           (gltf) => {
               const model = gltf.scene;
               model.position.set(0, 0, -0.3)
                                .applyMatrix4(controller.matrixWorld);
               model.scale.set(0.01, 0.01, 0.01)
               scene.add(model);

               mixer = new THREE.AnimationMixer(model);
               const animation = gltf.animations[0];
               mixer.clipAction(animation).play();
           },
           (xhr) => console.log((xhr.loaded / xhr.total * 100) + '% loaded'),
           (e) => console.error(e)
        );
 }

Three.js model animation

  • qr code with link to eighth example
  • link to eighth example

reticle


    import * as THREE from 'three';
    import {ARButton}
             from './three/examples
                        /jsm/webxr/ARButton.js';

    import {GLTFLoader} from './three/examples/
                       /jsm/loaders/GLTFLoader.js';

    let camera, scene, renderer, controller, mixer, clock;

    let reticle;
    let hitTestSource = null;
    let hitTestSourceRequested = false

    init();
    animate();


request hit-test


function init() {
 ...
 document.body.appendChild(ARButton.createButton(renderer,
                                        {requiredFeatures: ['hit-test']}));
 ...
 clock = new THREE.Clock();

 reticle = new THREE.Mesh(
      new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2),
      new THREE.MeshBasicMaterial()
  );
 reticle.matrixAutoUpdate = false;
 reticle.visible = false;
 scene.add(reticle);

 function onSelect() {
       if(reticle.visible){
             drawScene();
       }
  }
 ...

build rectile


function init() {
 ...
 document.body.appendChild(ARButton.createButton(renderer,
                                        {requiredFeatures: ['hit-test']}));
 ...
 clock = new THREE.Clock();

 reticle = new THREE.Mesh(
      new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2),
      new THREE.MeshBasicMaterial()
  );
 reticle.matrixAutoUpdate = false;
 reticle.visible = false;
 scene.add(reticle);

 function onSelect() {
       if(reticle.visible){
             drawScene();
       }
  }
 ...

only if reticle.visible


function init() {
 ...
 document.body.appendChild(ARButton.createButton(renderer,
                                        {requiredFeatures: ['hit-test']}));
 ...
 clock = new THREE.Clock();

 reticle = new THREE.Mesh(
      new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2),
      new THREE.MeshBasicMaterial()
  );
 reticle.matrixAutoUpdate = false;
 reticle.visible = false;
 scene.add(reticle);

 function onSelect() {
       if(reticle.visible){
             drawScene();
       }
  }
 ...

request ReferenceSpace


    function render(_, frame) {
       if (mixer) {
            mixer.update(clock.getDelta());
        }
       if (frame) {
            const referenceSpace = renderer.xr.getReferenceSpace();
            const session = renderer.xr.getSession();
            if (hitTestSourceRequested === false) {
                session.requestReferenceSpace('viewer')
                                       .then((referenceSpace) => {
                    session.requestHitTestSource({space: referenceSpace})
                                        .then((source)=> {
                        hitTestSource = source;
                    });
                });
                session.addEventListener('end', function () {
                    hitTestSourceRequested = false;
                    hitTestSource = null;
                });
                hitTestSourceRequested = true;
            }
           ...

reticle visibility


    function render(_, frame) {
           ...
            if (hitTestSource) {
                const hitTestResults = frame.getHitTestResults(hitTestSource);
                if (hitTestResults.length) {
                    const hit = hitTestResults[0];
                    reticle.visible = true;
                    reticle.matrix.fromArray(hit.getPose(referenceSpace)
                                                           .transform.matrix);
                } else {
                    reticle.visible = false;
                }
            }
        }
        renderer.render(scene, camera);
     }

Three.js surface detection

  • qr code with link to ninth example
  • link to ninth example

Model Viewer

Three js logo

model viewer import


<html lang="en">
<head>
    <title>Model Viewer</title>
    <script type="module" src="https://unpkg.com/@google/model-viewer/
                                          dist/model-viewer.min.js"></script>
    <link type="text/css" rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="info">
    <p>Model Viewer</p>
    <a href="/">Back</a>
</div>
<div style="width: 800px; height: 600px">
    <model-viewer src="/models/dodo/scene.gltf"
                  alt="A 3D model of dodo bird"
                  autoplay auto-rotate camera-controls
                  ar ar-scale="auto"
                  ar-modes="webxr scene-viewer quick-look"
                  style="width: 100%; height: 100%" ;
    >
    </model-viewer>

model viewer usage


<html lang="en">
<head>
    <title>Model Viewer</title>
    <script type="module" src="https://unpkg.com/@google/model-viewer/
                                          dist/model-viewer.min.js"></script>
    <link type="text/css" rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="info">
    <p>Model Viewer</p>
    <a href="/">Back</a>
</div>
<div style="width: 800px; height: 600px">
    <model-viewer src="/models/dodo/scene.gltf"
                  alt="A 3D model of dodo bird"
                  autoplay auto-rotate camera-controls
                  ar ar-scale="auto"
                  ar-modes="webxr scene-viewer quick-look"
                  style="width: 100%; height: 100%" ;
    >
    </model-viewer>

model viewer usage


    <model-viewer src="models/chair/chair.glb"
                  autoplay auto-rotate camera-controls
                  alt="A 3D model of a chair"
                  ar ar-scale="auto"
                  ar-modes="webxr scene-viewer quick-look"
                  style="width: 100%; height: 100%" ;
    >
    </model-viewer>
</div>
</body>
</html>

Model Viewer

  • qr code with link to tenth     example
  • link to tenth example

Alice

  • qr code with link to eleventh  example
  • link to eleventh example

web vs native apps

pros

  • no installation
  • no mobile stores restrictions
  • fast implementation (one app all platforms *)
  • low entry threshold
  • instant updates
vs native

web vs native apps

pros

  • security
  • extend current websites
  • it is our playground with our tools
    (Dev Tools, Npm, VisualCode, ...)
vs natuve

web vs native apps

cons

  • it is not yet production ready
  • offline resources
  • performance
  • web limitation (saving files, offline mode, ...)
vs native

Challenges

  • lack of 3D models

  • difficult editing of 3D models

challanges

Challenges

  • size of models => Dodo weigth ~13MB

  • lack of UI guidelines and good practices

challanges

Try that at home

why

Chess

  • qr code with link to twelfth example
  • link to twelfth example