├── .gitignore ├── 360-photos.html ├── LICENSE.md ├── anchors.html ├── ar-barebones.html ├── controller-state.html ├── css ├── common.css ├── pygment_trac.css └── stylesheet.css ├── explainer.html ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── framebuffer-scaling.html ├── hit-test-anchors.html ├── hit-test.html ├── immersive-ar-session.html ├── immersive-hands.html ├── immersive-vr-session.html ├── index.html ├── inline-session.html ├── input-profiles.html ├── input-selection.html ├── input-tracking.html ├── js ├── hit-test.js ├── render │ ├── README.md │ ├── core │ │ ├── material.js │ │ ├── node.js │ │ ├── primitive.js │ │ ├── program.js │ │ ├── renderer.js │ │ └── texture.js │ ├── geometry │ │ ├── box-builder.js │ │ ├── cone-builder.js │ │ └── primitive-stream.js │ ├── loaders │ │ └── gltf2.js │ ├── materials │ │ └── pbr.js │ ├── math │ │ ├── gl-matrix.js │ │ └── ray.js │ ├── nodes │ │ ├── bounds-renderer.js │ │ ├── button.js │ │ ├── cube-sea.js │ │ ├── drop-shadow.js │ │ ├── gltf2.js │ │ ├── input-renderer.js │ │ ├── menu-system.js │ │ ├── quad-texture.js │ │ ├── seven-segment-text.js │ │ ├── skybox.js │ │ ├── stats-viewer.js │ │ └── video.js │ └── scenes │ │ └── scene.js ├── stereo-util.js ├── third-party │ ├── dat.gui.min.js │ ├── gl-matrix │ │ ├── LICENSE.md │ │ └── src │ │ │ ├── gl-matrix.js │ │ │ └── gl-matrix │ │ │ ├── common.js │ │ │ ├── mat2.js │ │ │ ├── mat2d.js │ │ │ ├── mat3.js │ │ │ ├── mat4.js │ │ │ ├── quat.js │ │ │ ├── quat2.js │ │ │ ├── vec2.js │ │ │ ├── vec3.js │ │ │ └── vec4.js │ └── webxr-polyfill │ │ ├── LICENSE │ │ └── build │ │ └── webxr-polyfill.module.js ├── util │ ├── inline-viewer-helper.js │ ├── query-args.js │ ├── texture-loader.js │ └── webxr-button.js ├── webxr-sample-app.js └── wglu │ ├── wglu-program.js │ └── wglu-url.js ├── layers-samples ├── cube-layer.html ├── cyld-layer.html ├── eqrt-layer.html ├── eqrt-video.html ├── index.html ├── media-layer-sample.html ├── proj-layer.html ├── proj-multiview-occlusion.html ├── proj-multiview.html ├── quad-ab-test.html ├── quad-layer.html └── quad-select.html ├── manifest.json ├── media ├── gltf │ ├── camp │ │ ├── ATTRIBUTION.md │ │ ├── camp.bin │ │ ├── camp.blend │ │ └── camp.gltf │ ├── cave │ │ ├── ATTRIBUTION.md │ │ ├── cave.bin │ │ ├── cave.blend │ │ └── cave.gltf │ ├── controller │ │ ├── ATTRIBUTION.md │ │ ├── controller-left.gltf │ │ ├── controller.bin │ │ ├── controller.blend │ │ └── controller.gltf │ ├── cube-room │ │ ├── ATTRIBUTION.md │ │ ├── CubeRoom_BakedDiffuse_2048.png │ │ ├── cube-room.bin │ │ ├── cube-room.blend │ │ └── cube-room.gltf │ ├── garage │ │ ├── ATTRIBUTION.md │ │ ├── garage.bin │ │ ├── garage.blend │ │ └── garage.gltf │ ├── headset │ │ ├── ATTRIBUTION.md │ │ ├── headset.bin │ │ ├── headset.blend │ │ └── headset.gltf │ ├── home-theater │ │ ├── ATTRIBUTION.md │ │ ├── home-theater.bin │ │ ├── home-theater.blend │ │ └── home-theater.gltf │ ├── reticle │ │ ├── reticle.bin │ │ └── reticle.gltf │ ├── space │ │ ├── ATTRIBUTION.md │ │ ├── space.bin │ │ ├── space.blend │ │ └── space.gltf │ ├── sponza │ │ ├── 10381718147657362067.jpg │ │ ├── 10388182081421875623.jpg │ │ ├── 11474523244911310074.jpg │ │ ├── 11490520546946913238.jpg │ │ ├── 11872827283454512094.jpg │ │ ├── 11968150294050148237.jpg │ │ ├── 1219024358953944284.jpg │ │ ├── 12501374198249454378.jpg │ │ ├── 13196865903111448057.jpg │ │ ├── 13824894030729245199.jpg │ │ ├── 13982482287905699490.jpg │ │ ├── 14118779221266351425.jpg │ │ ├── 14170708867020035030.jpg │ │ ├── 14267839433702832875.jpg │ │ ├── 14650633544276105767.jpg │ │ ├── 15295713303328085182.jpg │ │ ├── 15722799267630235092.jpg │ │ ├── 16275776544635328252.png │ │ ├── 16299174074766089871.jpg │ │ ├── 16885566240357350108.jpg │ │ ├── 17556969131407844942.jpg │ │ ├── 17876391417123941155.jpg │ │ ├── 2051777328469649772.jpg │ │ ├── 2185409758123873465.jpg │ │ ├── 2299742237651021498.jpg │ │ ├── 2374361008830720677.jpg │ │ ├── 2411100444841994089.jpg │ │ ├── 2775690330959970771.jpg │ │ ├── 2969916736137545357.jpg │ │ ├── 332936164838540657.jpg │ │ ├── 3371964815757888145.jpg │ │ ├── 3455394979645218238.jpg │ │ ├── 3628158980083700836.jpg │ │ ├── 3827035219084910048.jpg │ │ ├── 4477655471536070370.jpg │ │ ├── 4601176305987539675.jpg │ │ ├── 466164707995436622.jpg │ │ ├── 4675343432951571524.jpg │ │ ├── 4871783166746854860.jpg │ │ ├── 4910669866631290573.jpg │ │ ├── 4975155472559461469.jpg │ │ ├── 5061699253647017043.png │ │ ├── 5792855332885324923.jpg │ │ ├── 5823059166183034438.jpg │ │ ├── 6047387724914829168.jpg │ │ ├── 6151467286084645207.jpg │ │ ├── 6593109234861095314.jpg │ │ ├── 6667038893015345571.jpg │ │ ├── 6772804448157695701.jpg │ │ ├── 7056944414013900257.jpg │ │ ├── 715093869573992647.jpg │ │ ├── 7268504077753552595.jpg │ │ ├── 7441062115984513793.jpg │ │ ├── 755318871556304029.jpg │ │ ├── 759203620573749278.jpg │ │ ├── 7645212358685992005.jpg │ │ ├── 7815564343179553343.jpg │ │ ├── 8006627369776289000.png │ │ ├── 8051790464816141987.jpg │ │ ├── 8114461559286000061.jpg │ │ ├── 8481240838833932244.jpg │ │ ├── 8503262930880235456.jpg │ │ ├── 8747919177698443163.jpg │ │ ├── 8750083169368950601.jpg │ │ ├── 8773302468495022225.jpg │ │ ├── 8783994986360286082.jpg │ │ ├── 9288698199695299068.jpg │ │ ├── 9916269861720640319.jpg │ │ ├── README.md │ │ ├── Sponza.bin │ │ ├── Sponza.gltf │ │ └── white.png │ ├── stereo │ │ ├── ATTRIBUTION.md │ │ ├── stereo.bin │ │ ├── stereo.blend │ │ └── stereo.gltf │ └── sunflower │ │ ├── ATTRIBUTION.md │ │ ├── PUSHILIN_sunflower.png │ │ ├── sunflower.bin │ │ ├── sunflower.blend │ │ └── sunflower.gltf ├── logo │ ├── webxr-logo-mark.svg │ ├── webxr-logo-square.svg │ └── webxr-logo.svg ├── manifest │ ├── icon-192x192-maskable.png │ ├── icon-192x192-regular.png │ ├── icon-512x512-maskable.png │ ├── icon-512x512-regular.png │ ├── screenshot-1.png │ ├── screenshot-2.png │ ├── screenshot-3.png │ ├── screenshot-4.png │ ├── screenshot-5.png │ └── screenshot-6.png ├── sound │ ├── drums.ogg │ ├── guitar.ogg │ └── perc.ogg ├── textures │ ├── ATTRIBUTION.md │ ├── annotation.png │ ├── arrow-up.png │ ├── backward-button.png │ ├── bluetooth-button.png │ ├── camera-button.png │ ├── check-button.png │ ├── chess-pano-4k.png │ ├── chess-pano-4k180.jpg │ ├── cube-sea.png │ ├── cube-sea.psd │ ├── eilenriede-park-2k.png │ ├── forward-button.png │ ├── hid-button.png │ ├── info-button.png │ ├── install-pwa-button.png │ ├── location-button.png │ ├── microphone-button.png │ ├── midi-button.png │ ├── milky-way-2k.png │ ├── milky-way-4k.png │ ├── mono-equirect180.png │ ├── mono_cube_map.png │ ├── mono_equirect_test.png │ ├── notifications-button.png │ ├── pause-button.png │ ├── play-button.png │ ├── rect-mono.png │ ├── rect-stereo-top-down.png │ ├── screenshare-button.png │ ├── serial-button.png │ ├── show-notification-button.png │ ├── stereo_cube_map.png │ ├── transparent.png │ ├── usb-button.png │ └── x-button.png ├── thumbnails │ ├── cave.png │ ├── cube-room.png │ └── space.png └── video │ ├── ATTRIBUTION.md │ └── bbb-sunflower-540p2-1min.webm ├── positional-audio.html ├── proposals ├── index.html ├── mesh-detection.html ├── phone-ar-depth-gpu.html ├── phone-ar-depth.html └── plane-detection.html ├── reduced-bind-rendering.html ├── report └── index.html ├── room-scale.html ├── service-worker.js ├── shaders ├── depth-api-cpu.frag ├── depth-api-gpu.frag └── turbo.glsl ├── spectator-mode.html ├── stereo-video.html ├── teleportation.html ├── tests ├── composed-views.html ├── cube-sea.html ├── exit-button.html ├── index.html ├── interrupted-ar.html ├── offscreen-canvas.html ├── permission-request.html ├── pointer-painter.html ├── ref-space-invert.html ├── show-boundary.html ├── sparse-frames.html ├── sponza.html └── webgl2-multisample.html ├── viewport-scaling.html ├── vr-barebones.html ├── w3c.json └── webgpu ├── ar-barebones.html ├── index.html └── vr-barebones.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.blend1 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Immersive Web Community Group 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /ar-barebones.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Barebones AR 33 | 34 | 35 |
36 |
37 |
38 | Barebones WebXR DOM Overlay 39 |

40 | This sample demonstrates extremely simple use of an "immersive-ar" 41 | session with no library dependencies, with an optional DOM overlay. 42 | It doesn't render anything exciting, just draws a rectangle with a 43 | slowly changing color to prove it's working. 44 | Back 45 |

46 |
47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 |

Click 'Enter AR' to see content

55 |
56 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /css/common.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #F0F0F0; 3 | font: 1rem/1.4 -apple-system, BlinkMacSystemFont, 4 | Segoe UI, Roboto, Oxygen, 5 | Ubuntu, Cantarell, Fira Sans, 6 | Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | header { 10 | position: relative; 11 | z-index: 2; 12 | left: 0px; 13 | text-align: left; 14 | max-width: 420px; 15 | padding: 0.5em; 16 | background-color: rgba(255, 255, 255, 0.90); 17 | margin-bottom: 0.5em; 18 | border-radius: 2px; 19 | } 20 | 21 | details summary { 22 | font-size: 1.0em; 23 | font-weight: bold; 24 | } 25 | 26 | details[open] summary { 27 | font-size: 1.4em; 28 | font-weight: bold; 29 | } 30 | 31 | header h1 { 32 | margin-top: 0px; 33 | } 34 | 35 | canvas { 36 | position: absolute; 37 | z-index: 0; 38 | width: 100%; 39 | height: 100%; 40 | left: 0; 41 | top: 0; 42 | right: 0; 43 | bottom: 0; 44 | margin: 0; 45 | touch-action: none; 46 | } 47 | 48 | .back { 49 | float: right; 50 | text-decoration: none; 51 | } 52 | 53 | .back:hover { 54 | text-decoration: underline; 55 | } 56 | 57 | .back::before { 58 | display: inline-block; 59 | content: attr(data-index) '<'; 60 | font-weight: bold; 61 | white-space: nowrap; 62 | margin-right: 0.2em; 63 | margin-left: 0.2em; 64 | } 65 | 66 | /* Used for the 'barebones' samples */ 67 | .barebones-button { 68 | font-family: "Karla", sans-serif; 69 | border: rgb(80, 168, 252) 2px solid; 70 | border-radius: 2px; 71 | box-sizing: border-box; 72 | background: none; 73 | height: 55px; 74 | min-width: 176px; 75 | display: inline-block; 76 | position: relative; 77 | cursor: pointer; 78 | font-size: 18px; 79 | color: rgb(80, 168, 252); 80 | background-color: rgba(255, 255, 255, 0.7); 81 | } 82 | -------------------------------------------------------------------------------- /css/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight { background: #ffffff; } 2 | .highlight .c { color: #999988; font-style: italic } /* Comment */ 3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 4 | .highlight .k { font-weight: bold } /* Keyword */ 5 | .highlight .o { font-weight: bold } /* Operator */ 6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ 7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ 8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ 9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ 10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ 11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #aa0000 } /* Generic.Error */ 14 | .highlight .gh { color: #999999 } /* Generic.Heading */ 15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ 16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ 17 | .highlight .go { color: #888888 } /* Generic.Output */ 18 | .highlight .gp { color: #555555 } /* Generic.Prompt */ 19 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 20 | .highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ 21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */ 22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */ 23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */ 24 | .highlight .kn { font-weight: bold } /* Keyword.Namespace */ 25 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ 26 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */ 27 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ 28 | .highlight .m { color: #009999 } /* Literal.Number */ 29 | .highlight .s { color: #d14 } /* Literal.String */ 30 | .highlight .na { color: #008080 } /* Name.Attribute */ 31 | .highlight .nb { color: #0086B3 } /* Name.Builtin */ 32 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ 33 | .highlight .no { color: #008080 } /* Name.Constant */ 34 | .highlight .ni { color: #800080 } /* Name.Entity */ 35 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ 37 | .highlight .nn { color: #555555 } /* Name.Namespace */ 38 | .highlight .nt { color: #000080 } /* Name.Tag */ 39 | .highlight .nv { color: #008080 } /* Name.Variable */ 40 | .highlight .ow { font-weight: bold } /* Operator.Word */ 41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 42 | .highlight .mf { color: #009999 } /* Literal.Number.Float */ 43 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */ 44 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */ 45 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */ 46 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */ 47 | .highlight .sc { color: #d14 } /* Literal.String.Char */ 48 | .highlight .sd { color: #d14 } /* Literal.String.Doc */ 49 | .highlight .s2 { color: #d14 } /* Literal.String.Double */ 50 | .highlight .se { color: #d14 } /* Literal.String.Escape */ 51 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */ 52 | .highlight .si { color: #d14 } /* Literal.String.Interpol */ 53 | .highlight .sx { color: #d14 } /* Literal.String.Other */ 54 | .highlight .sr { color: #009926 } /* Literal.String.Regex */ 55 | .highlight .s1 { color: #d14 } /* Literal.String.Single */ 56 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */ 57 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ 58 | .highlight .vc { color: #008080 } /* Name.Variable.Class */ 59 | .highlight .vg { color: #008080 } /* Name.Variable.Global */ 60 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */ 61 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ 62 | 63 | .type-csharp .highlight .k { color: #0000FF } 64 | .type-csharp .highlight .kt { color: #0000FF } 65 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 66 | .type-csharp .highlight .nc { color: #2B91AF } 67 | .type-csharp .highlight .nn { color: #000000 } 68 | .type-csharp .highlight .s { color: #A31515 } 69 | .type-csharp .highlight .sc { color: #A31515 } 70 | -------------------------------------------------------------------------------- /css/stylesheet.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-sizing: border-box; 3 | -webkit-sizing: border-box; 4 | box-sizing: border-box; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | html, 10 | body { 11 | height: 100%; 12 | width: 100%; 13 | } 14 | 15 | html { 16 | font-size: 16px; 17 | } 18 | 19 | body { 20 | background-color: #f1f1f1; 21 | color: #333; 22 | font: 1rem/1.4 -apple-system, BlinkMacSystemFont, 23 | Segoe UI, Roboto, Oxygen, 24 | Ubuntu, Cantarell, Fira Sans, 25 | Droid Sans, Helvetica Neue, sans-serif; 26 | } 27 | 28 | a { 29 | color: #369; 30 | text-decoration: none; 31 | } 32 | 33 | a:hover { 34 | color: #036; 35 | text-decoration: underline; 36 | } 37 | 38 | .header { 39 | padding-bottom: 2rem; 40 | text-align: center; 41 | } 42 | 43 | #nav { 44 | position: absolute; 45 | top: 0; 46 | } 47 | 48 | #nav a { 49 | display: inline-block; 50 | margin-right:20px; 51 | font-size: 15px; 52 | opacity: 0.4; 53 | padding-top: 14px; 54 | color:black; 55 | } 56 | 57 | #nav a.selected{ 58 | border-top: 2px solid black; 59 | opacity: 1.0; 60 | } 61 | 62 | .wordmark { 63 | color: inherit; 64 | display: block; 65 | } 66 | 67 | .wordmark > span { 68 | background: #2ea; 69 | box-shadow: 3px 3px 0 0 rgba(0,0,0,.15); 70 | color: #fff; 71 | display: inline-block; 72 | font-size: 64px; 73 | font-size: calc(16px + 1vh + 3vw); /* Responsive font-size for mobile */ 74 | font-style: italic; 75 | font-weight: 300; 76 | margin-bottom: 15px; 77 | max-width: 100%; 78 | padding: 15px 30px; 79 | transition: .25s box-shadow ease-in-out, .5s width ease-in-out; 80 | } 81 | 82 | .wordmark:hover { 83 | text-decoration: none; 84 | } 85 | 86 | .wordmark:hover > span { 87 | background: #2ea; 88 | box-shadow: 3px 3px 0 0 rgba(0,0,0,.25); 89 | } 90 | 91 | .tagline { 92 | color: #999; 93 | font-size: 1rem; 94 | font-weight: normal; 95 | } 96 | 97 | @media screen and (min-width: 650px) { 98 | .device-col { 99 | width: 48%; 100 | float: left; 101 | } 102 | .device-col:last-of-type{ 103 | margin-left: 4%; 104 | } 105 | } 106 | 107 | code, 108 | pre { 109 | font-family: Consolas, Andale Mono, Monaco, 110 | Lucida Console, Liberation Mono, DejaVu Sans Mono, 111 | Bitstream Vera Sans Mono, Courier New, monospace; 112 | } 113 | 114 | hr { 115 | background-color: rgba(0,0,0,.15); 116 | height: 1px; 117 | padding-bottom: 2px; 118 | } 119 | 120 | .container { 121 | max-width: 740px; 122 | margin: 0 auto; 123 | min-width: 300px; 124 | padding: 2rem; 125 | padding-top: 4rem; 126 | width: 100%; 127 | } 128 | 129 | h3, 130 | h4, 131 | ul, 132 | ol, 133 | dl, 134 | p, 135 | blockquote { 136 | margin: 1.5rem 0; 137 | } 138 | 139 | ul, 140 | ol, 141 | dl { 142 | margin-left: 2.75rem; 143 | } 144 | 145 | ul { 146 | list-style: none; 147 | } 148 | 149 | ul li:before { 150 | content: "•"; 151 | color: #999; 152 | display: inline-block; 153 | margin-left: -1em; 154 | width: 1em; 155 | } 156 | 157 | li + li { 158 | margin-top: .25rem; 159 | } 160 | 161 | h3 { 162 | border-bottom: 1px solid #ddd; 163 | color: #111; 164 | line-height: 1.4; 165 | padding-bottom: .35rem; 166 | } 167 | 168 | h3 + p { 169 | margin-top: 1rem; 170 | } 171 | 172 | p { 173 | font-weight: normal; 174 | } 175 | 176 | iframe { 177 | border: 0; 178 | height: 100%; 179 | width: 100%; 180 | } 181 | 182 | .iframe-wrap { 183 | height: 600px; 184 | } 185 | 186 | .iframe-wrap--chrome { 187 | height: 400px; 188 | } 189 | 190 | .iframe--chrome { 191 | min-width: 400px; 192 | margin-left: -15px; 193 | width: 100%; 194 | width: calc(100% + 35px); 195 | } 196 | 197 | @media only screen and (max-width: 449px) { 198 | .wordmark > span { 199 | display: block; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /explainer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 29 | 32 | WebXR - Samples Explainer 33 | 34 | 35 | 36 |
37 |
38 | 41 | 42 |

WebXR

43 |

Samples Explainer

44 |
45 | 46 |
47 |

What are these?

48 |

These samples have been created to give developers a set of simple, fun, readable apps that demonstrate various aspects of using the WebXR API. They are designed to focus on API use rather than the details of how the WebGL rendering is done.

49 | 50 |

Who are they for?

51 |

The samples will be most useful as a reference for developers that want to use the WebXR API directly in their own project or add XR functionality to their libraries. They are also handy for testing the functionality of WebXR implementations.

52 | 53 |

If you are using a graphics framework in your project you should check its documentation first, as they may already have WebXR support built in! In that case, you should refer to the library documentation for details on how to use XR features.

54 | 55 |

How are they formatted?

56 |

The samples are provided as web pages with heavily commented JavaScript code. Each page covers a single, targeted topic relating to API use. Code that has already been well commented in a previous sample will typically not be commented in subsequent samples in order to make it easier to focus on the newly introduced concepts, so if you see an uncommented code block that you don't understand check the previous samples and you will probably find the explanation that you're looking for.

57 | 58 |

What's the deal with WebGL?

59 |

Today the primary way of drawing XR content is via the WebGL API. WebGL provides an efficient way of rendering 3D graphics in the browser, which is great for XR applications that require high performance.

60 | 61 |

Unfortunately WebGL is fairly verbose and can be hard to follow for developers who aren't already familiar with it. Because of this, these samples generally avoid spending much time on the details of how the WebGL rendering is done. Otherwise they would quickly turn into WebGL tutorials with a little bit of XR on the side. Instead the focus is on how to get the necessary values from the WebXR API to feed into WebGL to ensure that the rendering is done correctly.

62 | 63 |

To Framework or not to Framework?

64 |

Most usage of WebGL today happens via frameworks that significantly simplify the creation of 3D scenes compared to using raw WebGL. Some of the more popular examples are three.js, babylon.js, and PlayCanvas. There's also frameworks that are specifically designed to create XR content on the web, such as A-Frame and ReactXR. These are all fantastic libraries with their own strengths and focuses, and in general it's recommended that you find tools that suit your needs and rely on them rather than trying to build your own rendering systems from scratch.

65 | 66 |

However, most frameworks will also hide away the details of interacting with the WebXR API. That's generally great for users, but not terribly useful when the entire point of your code is to demonstrate how to use the API! At the same time, we don't want the WebXR logic to be obscured by hundreds of lines of WebGL calls. As a result, these samples make use of their own minimalistic rendering library that is specifically designed to highlight use of the WebXR API and de-emphasize the WebGL rendering logic. It is not recommended that you use this library in your own projects, as you will almost certainly be better served by one of the more popular, better established frameworks.

67 |
68 | 69 | 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/favicon-32x32.png -------------------------------------------------------------------------------- /favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/favicon-96x96.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/favicon.ico -------------------------------------------------------------------------------- /js/render/README.md: -------------------------------------------------------------------------------- 1 | WebXR Samples Renderer 2 | ========================================== 3 | A simple WebGL renderering framework optimised for demonstrating WebXR concepts. 4 | 5 | This library does two things well and not much else: 6 | 7 | 1) Loading and Rendering GLTF 2.0 files. 8 | 2) Optimising for WebXR-style rendering. 9 | 10 | However it explicitly goes out of it's way to NOT wrap much, if any, WebXR 11 | functionality. This is because arguably it's sole purpose in life is to enable 12 | the WebXR samples, who's sole purpose in life is to provide easy-to-follow code 13 | snippets demonstrating the API use and thus anything this code did to hide the 14 | WebXR API's direct use is counter productive. 15 | 16 | The only "third party" dependency is gl-matrix. 17 | 18 | Using this renderer for your own projects is very much not recommended, as you will 19 | almost certainly be better served by one of the other more popular frameworks 20 | out there. 21 | 22 | Library previously had a build step but all WebXR-enabled browsers support JS 23 | modules, so it turned out to be more of a nusiance than it was worth. 24 | -------------------------------------------------------------------------------- /js/render/core/primitive.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {vec3} from '../math/gl-matrix.js'; 22 | 23 | export class PrimitiveAttribute { 24 | constructor(name, buffer, componentCount, componentType, stride, byteOffset) { 25 | this.name = name; 26 | this.buffer = buffer; 27 | this.componentCount = componentCount || 3; 28 | this.componentType = componentType || 5126; // gl.FLOAT; 29 | this.stride = stride || 0; 30 | this.byteOffset = byteOffset || 0; 31 | this.normalized = false; 32 | } 33 | } 34 | 35 | export class Primitive { 36 | constructor(attributes, elementCount, mode) { 37 | this.attributes = attributes || []; 38 | this.elementCount = elementCount || 0; 39 | this.mode = mode || 4; // gl.TRIANGLES; 40 | this.indexBuffer = null; 41 | this.indexByteOffset = 0; 42 | this.indexType = 0; 43 | this._min = null; 44 | this._max = null; 45 | } 46 | 47 | setIndexBuffer(indexBuffer, byteOffset, indexType) { 48 | this.indexBuffer = indexBuffer; 49 | this.indexByteOffset = byteOffset || 0; 50 | this.indexType = indexType || 5123; // gl.UNSIGNED_SHORT; 51 | } 52 | 53 | setBounds(min, max) { 54 | this._min = vec3.clone(min); 55 | this._max = vec3.clone(max); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /js/render/core/program.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | export class Program { 22 | constructor(gl, vertSrc, fragSrc, attribMap, defines, extensions, layouts) { 23 | this._gl = gl; 24 | this.program = gl.createProgram(); 25 | this.attrib = null; 26 | this.uniform = null; 27 | this.defines = {}; 28 | this.extensions = []; 29 | this.layouts = []; 30 | 31 | this._firstUse = true; 32 | this._nextUseCallbacks = []; 33 | 34 | let extensionsString = ''; 35 | if (extensions) { 36 | for (let extension of extensions) { 37 | this.extensions.push(extension); 38 | extensionsString += `#extension ${extension} : require\n`; 39 | } 40 | } 41 | let layoutsString = ''; 42 | if (layouts) { 43 | for (let layout of layouts) { 44 | this.layouts.push(layout); 45 | layoutsString += `layout(${layout}) in;\n`; 46 | } 47 | } 48 | let definesString = ''; 49 | if (defines) { 50 | for (let define in defines) { 51 | this.defines[define] = defines[define]; 52 | definesString += `#define ${define} ${defines[define]}\n`; 53 | } 54 | } 55 | 56 | this._vertShader = gl.createShader(gl.VERTEX_SHADER); 57 | gl.attachShader(this.program, this._vertShader); 58 | gl.shaderSource(this._vertShader, '#version 300 es\n' + extensionsString + layoutsString + definesString + vertSrc); 59 | gl.compileShader(this._vertShader); 60 | 61 | this._fragShader = gl.createShader(gl.FRAGMENT_SHADER); 62 | gl.attachShader(this.program, this._fragShader); 63 | gl.shaderSource(this._fragShader, '#version 300 es\n' + extensionsString + definesString + fragSrc); 64 | gl.compileShader(this._fragShader); 65 | 66 | if (attribMap) { 67 | this.attrib = {}; 68 | for (let attribName in attribMap) { 69 | gl.bindAttribLocation(this.program, attribMap[attribName], attribName); 70 | this.attrib[attribName] = attribMap[attribName]; 71 | } 72 | } 73 | 74 | gl.linkProgram(this.program); 75 | } 76 | 77 | onNextUse(callback) { 78 | this._nextUseCallbacks.push(callback); 79 | } 80 | 81 | use() { 82 | let gl = this._gl; 83 | 84 | // If this is the first time the program has been used do all the error checking and 85 | // attrib/uniform querying needed. 86 | if (this._firstUse) { 87 | this._firstUse = false; 88 | if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { 89 | if (!gl.getShaderParameter(this._vertShader, gl.COMPILE_STATUS)) { 90 | console.error('Vertex shader compile error: ' + gl.getShaderInfoLog(this._vertShader)); 91 | } else if (!gl.getShaderParameter(this._fragShader, gl.COMPILE_STATUS)) { 92 | console.error('Fragment shader compile error: ' + gl.getShaderInfoLog(this._fragShader)); 93 | } else { 94 | console.error('Program link error: ' + gl.getProgramInfoLog(this.program)); 95 | } 96 | gl.deleteProgram(this.program); 97 | this.program = null; 98 | } else { 99 | if (!this.attrib) { 100 | this.attrib = {}; 101 | let attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); 102 | for (let i = 0; i < attribCount; i++) { 103 | let attribInfo = gl.getActiveAttrib(this.program, i); 104 | this.attrib[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name); 105 | } 106 | } 107 | 108 | this.uniform = {}; 109 | let uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); 110 | let uniformName = ''; 111 | for (let i = 0; i < uniformCount; i++) { 112 | let uniformInfo = gl.getActiveUniform(this.program, i); 113 | uniformName = uniformInfo.name.replace('[0]', ''); 114 | this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName); 115 | } 116 | } 117 | gl.deleteShader(this._vertShader); 118 | gl.deleteShader(this._fragShader); 119 | } 120 | 121 | gl.useProgram(this.program); 122 | 123 | if (this._nextUseCallbacks.length) { 124 | for (let callback of this._nextUseCallbacks) { 125 | callback(this); 126 | } 127 | this._nextUseCallbacks = []; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /js/render/core/texture.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | const GL = WebGLRenderingContext; // For enums 22 | 23 | let nextDataTextureIndex = 0; 24 | 25 | export class TextureSampler { 26 | constructor() { 27 | this.minFilter = null; 28 | this.magFilter = null; 29 | this.wrapS = null; 30 | this.wrapT = null; 31 | } 32 | } 33 | 34 | export class Texture { 35 | constructor() { 36 | this.sampler = new TextureSampler(); 37 | this.mipmap = true; 38 | // TODO: Anisotropy 39 | } 40 | 41 | get format() { 42 | return GL.RGBA; 43 | } 44 | 45 | get width() { 46 | return 0; 47 | } 48 | 49 | get height() { 50 | return 0; 51 | } 52 | 53 | get textureKey() { 54 | return null; 55 | } 56 | 57 | get isExternal() { 58 | return false; 59 | } 60 | } 61 | 62 | export class ImageTexture extends Texture { 63 | constructor(img) { 64 | super(); 65 | 66 | this._img = img; 67 | this._imgBitmap = null; 68 | this._manualKey = null; 69 | 70 | if (img.src && img.complete) { 71 | if (img.naturalWidth) { 72 | this._promise = this._finishImage(); 73 | } else { 74 | this._promise = Promise.reject('Image provided had failed to load.'); 75 | } 76 | } else { 77 | this._promise = new Promise((resolve, reject) => { 78 | img.addEventListener('load', () => resolve(this._finishImage())); 79 | img.addEventListener('error', reject); 80 | }); 81 | } 82 | } 83 | 84 | _finishImage() { 85 | if (window.createImageBitmap) { 86 | return window.createImageBitmap(this._img).then((imgBitmap) => { 87 | this._imgBitmap = imgBitmap; 88 | return Promise.resolve(this); 89 | }); 90 | } 91 | return Promise.resolve(this); 92 | } 93 | 94 | get format() { 95 | // TODO: Can be RGB in some cases. 96 | return GL.RGBA; 97 | } 98 | 99 | get width() { 100 | return this._img.width; 101 | } 102 | 103 | get height() { 104 | return this._img.height; 105 | } 106 | 107 | waitForComplete() { 108 | return this._promise; 109 | } 110 | 111 | get textureKey() { 112 | return this._manualKey || this._img.src; 113 | } 114 | 115 | get source() { 116 | return this._imgBitmap || this._img; 117 | } 118 | 119 | genDataKey() { 120 | this._manualKey = `DATA_${nextDataTextureIndex}`; 121 | nextDataTextureIndex++; 122 | } 123 | } 124 | 125 | export class UrlTexture extends ImageTexture { 126 | constructor(url) { 127 | let img = new Image(); 128 | super(img); 129 | img.src = url; 130 | } 131 | } 132 | 133 | export class BlobTexture extends ImageTexture { 134 | constructor(blob) { 135 | let img = new Image(); 136 | super(img); 137 | img.src = window.URL.createObjectURL(blob); 138 | } 139 | } 140 | 141 | export class VideoTexture extends Texture { 142 | constructor(video) { 143 | super(); 144 | 145 | this._video = video; 146 | 147 | if (video.readyState >= 2) { 148 | this._promise = Promise.resolve(this); 149 | } else if (video.error) { 150 | this._promise = Promise.reject(video.error); 151 | } else { 152 | this._promise = new Promise((resolve, reject) => { 153 | video.addEventListener('loadeddata', () => resolve(this)); 154 | video.addEventListener('error', reject); 155 | }); 156 | } 157 | } 158 | 159 | get format() { 160 | // TODO: Can be RGB in some cases. 161 | return GL.RGBA; 162 | } 163 | 164 | get width() { 165 | return this._video.videoWidth; 166 | } 167 | 168 | get height() { 169 | return this._video.videoHeight; 170 | } 171 | 172 | waitForComplete() { 173 | return this._promise; 174 | } 175 | 176 | get textureKey() { 177 | return this._video.src; 178 | } 179 | 180 | get source() { 181 | return this._video; 182 | } 183 | } 184 | 185 | export class DataTexture extends Texture { 186 | constructor(data, width, height, format = GL.RGBA, type = GL.UNSIGNED_BYTE) { 187 | super(); 188 | 189 | this._data = data; 190 | this._width = width; 191 | this._height = height; 192 | this._format = format; 193 | this._type = type; 194 | this._key = `DATA_${nextDataTextureIndex}`; 195 | nextDataTextureIndex++; 196 | } 197 | 198 | get format() { 199 | return this._format; 200 | } 201 | 202 | get width() { 203 | return this._width; 204 | } 205 | 206 | get height() { 207 | return this._height; 208 | } 209 | 210 | get textureKey() { 211 | return this._key; 212 | } 213 | } 214 | 215 | export class ExternalTexture extends Texture { 216 | constructor(key) { 217 | super(); 218 | 219 | this._key = key; 220 | } 221 | 222 | get textureKey() { 223 | return this._key; 224 | } 225 | 226 | get isExternal() { 227 | return true; 228 | } 229 | } 230 | 231 | export class ColorTexture extends DataTexture { 232 | constructor(r, g, b, a) { 233 | let colorData = new Uint8Array([r*255.0, g*255.0, b*255.0, a*255.0]); 234 | super(colorData, 1, 1); 235 | 236 | this.mipmap = false; 237 | this._key = `COLOR_${colorData[0]}_${colorData[1]}_${colorData[2]}_${colorData[3]}`; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /js/render/geometry/box-builder.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {GeometryBuilderBase} from './primitive-stream.js'; 22 | 23 | export class BoxBuilder extends GeometryBuilderBase { 24 | pushBox(min, max) { 25 | let stream = this.primitiveStream; 26 | 27 | let w = max[0] - min[0]; 28 | let h = max[1] - min[1]; 29 | let d = max[2] - min[2]; 30 | 31 | let wh = w * 0.5; 32 | let hh = h * 0.5; 33 | let dh = d * 0.5; 34 | 35 | let cx = min[0] + wh; 36 | let cy = min[1] + hh; 37 | let cz = min[2] + dh; 38 | 39 | stream.startGeometry(); 40 | 41 | // Bottom 42 | let idx = stream.nextVertexIndex; 43 | stream.pushTriangle(idx, idx+1, idx+2); 44 | stream.pushTriangle(idx, idx+2, idx+3); 45 | 46 | // X Y Z U V NX NY NZ 47 | stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, 0.0, -1.0, 0.0); 48 | stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 0.0, -1.0, 0.0); 49 | stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 1.0, 0.0, 0.0, -1.0, 0.0); 50 | stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 0.0, 0.0, 0.0, -1.0, 0.0); 51 | 52 | // Top 53 | idx = stream.nextVertexIndex; 54 | stream.pushTriangle(idx, idx+2, idx+1); 55 | stream.pushTriangle(idx, idx+3, idx+2); 56 | 57 | stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, 0.0, 1.0, 0.0); 58 | stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 0.0, 1.0, 0.0); 59 | stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 1.0, 1.0, 0.0, 1.0, 0.0); 60 | stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 0.0, 1.0, 0.0, 1.0, 0.0); 61 | 62 | // Left 63 | idx = stream.nextVertexIndex; 64 | stream.pushTriangle(idx, idx+2, idx+1); 65 | stream.pushTriangle(idx, idx+3, idx+2); 66 | 67 | stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, -1.0, 0.0, 0.0); 68 | stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, -1.0, 0.0, 0.0); 69 | stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 1.0, 0.0, -1.0, 0.0, 0.0); 70 | stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 1.0, 1.0, -1.0, 0.0, 0.0); 71 | 72 | // Right 73 | idx = stream.nextVertexIndex; 74 | stream.pushTriangle(idx, idx+1, idx+2); 75 | stream.pushTriangle(idx, idx+2, idx+3); 76 | 77 | stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 1.0, 0.0, 0.0); 78 | stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 1.0, 0.0, 0.0); 79 | stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 0.0, 0.0, 1.0, 0.0, 0.0); 80 | stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 0.0, 1.0, 1.0, 0.0, 0.0); 81 | 82 | // Back 83 | idx = stream.nextVertexIndex; 84 | stream.pushTriangle(idx, idx+2, idx+1); 85 | stream.pushTriangle(idx, idx+3, idx+2); 86 | 87 | stream.pushVertex(-wh+cx, -hh+cy, -dh+cz, 1.0, 1.0, 0.0, 0.0, -1.0); 88 | stream.pushVertex(+wh+cx, -hh+cy, -dh+cz, 0.0, 1.0, 0.0, 0.0, -1.0); 89 | stream.pushVertex(+wh+cx, +hh+cy, -dh+cz, 0.0, 0.0, 0.0, 0.0, -1.0); 90 | stream.pushVertex(-wh+cx, +hh+cy, -dh+cz, 1.0, 0.0, 0.0, 0.0, -1.0); 91 | 92 | // Front 93 | idx = stream.nextVertexIndex; 94 | stream.pushTriangle(idx, idx+1, idx+2); 95 | stream.pushTriangle(idx, idx+2, idx+3); 96 | 97 | stream.pushVertex(-wh+cx, -hh+cy, +dh+cz, 0.0, 1.0, 0.0, 0.0, 1.0); 98 | stream.pushVertex(+wh+cx, -hh+cy, +dh+cz, 1.0, 1.0, 0.0, 0.0, 1.0); 99 | stream.pushVertex(+wh+cx, +hh+cy, +dh+cz, 1.0, 0.0, 0.0, 0.0, 1.0); 100 | stream.pushVertex(-wh+cx, +hh+cy, +dh+cz, 0.0, 0.0, 0.0, 0.0, 1.0); 101 | 102 | stream.endGeometry(); 103 | } 104 | 105 | pushCube(center = [0, 0, 0], size = 1.0) { 106 | let hs = size * 0.5; 107 | this.pushBox([center[0] - hs, center[1] - hs, center[2] - hs], 108 | [center[0] + hs, center[1] + hs, center[2] + hs]); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /js/render/geometry/cone-builder.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {GeometryBuilderBase} from './primitive-stream.js'; 22 | 23 | export class ConeBuilder extends GeometryBuilderBase { 24 | pushCone(size = 0.5) { 25 | let stream = this.primitiveStream; 26 | let coneSegments = 64; 27 | 28 | stream.startGeometry(); 29 | 30 | // Cone side vertices 31 | for (let i = 0; i < coneSegments; ++i) { 32 | let idx = stream.nextVertexIndex; 33 | 34 | stream.pushTriangle(idx, idx + 1, idx + 2); 35 | 36 | let rad = ((Math.PI * 2) / coneSegments) * i; 37 | let rad2 = ((Math.PI * 2) / coneSegments) * (i + 1); 38 | 39 | stream.pushVertex( 40 | Math.sin(rad) * (size / 2), -size, Math.cos(rad) * (size / 2), 41 | i / coneSegments, 0.0, 42 | Math.sin(rad), 0.25, Math.cos(rad)); 43 | 44 | stream.pushVertex( 45 | Math.sin(rad2) * (size / 2), -size, Math.cos(rad2) * (size / 2), 46 | i / coneSegments, 0.0, 47 | Math.sin(rad2), 0.25, Math.cos(rad2)); 48 | 49 | stream.pushVertex( 50 | 0, size, 0, 51 | i / coneSegments, 1.0, 52 | Math.sin((rad + rad2) / 2), 0.25, Math.cos((rad + rad2) / 2)); 53 | } 54 | 55 | // Base triangles 56 | let baseCenterIndex = stream.nextVertexIndex; 57 | stream.pushVertex( 58 | 0, -size, 0, 59 | 0.5, 0.5, 60 | 0, -1, 0); 61 | for (let i = 0; i < coneSegments; ++i) { 62 | let idx = stream.nextVertexIndex; 63 | stream.pushTriangle(baseCenterIndex, idx, idx + 1); 64 | let rad = ((Math.PI * 2) / coneSegments) * i; 65 | let rad2 = ((Math.PI * 2) / coneSegments) * (i + 1); 66 | stream.pushVertex( 67 | Math.sin(rad2) * (size / 2.0), -size, Math.cos(rad2) * (size / 2.0), 68 | (Math.sin(rad2) + 1.0) * 0.5, (Math.cos(rad2) + 1.0) * 0.5, 69 | 0, -1, 0); 70 | stream.pushVertex( 71 | Math.sin(rad) * (size / 2.0), -size, Math.cos(rad) * (size / 2.0), 72 | (Math.sin(rad) + 1.0) * 0.5, (Math.cos(rad) + 1.0) * 0.5, 73 | 0, -1, 0); 74 | } 75 | 76 | stream.endGeometry(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /js/render/math/gl-matrix.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import * as glMatrix from '../../third-party/gl-matrix/src/gl-matrix/common.js'; 22 | import * as mat2 from '../../third-party/gl-matrix/src/gl-matrix/mat2.js'; 23 | import * as mat2d from '../../third-party/gl-matrix/src/gl-matrix/mat2d.js'; 24 | import * as mat3 from '../../third-party/gl-matrix/src/gl-matrix/mat3.js'; 25 | import * as mat4 from '../../third-party/gl-matrix/src/gl-matrix/mat4.js'; 26 | import * as quat from '../../third-party/gl-matrix/src/gl-matrix/quat.js'; 27 | import * as quat2 from '../../third-party/gl-matrix/src/gl-matrix/quat2.js'; 28 | import * as vec2 from '../../third-party/gl-matrix/src/gl-matrix/vec2.js'; 29 | import * as vec3 from '../../third-party/gl-matrix/src/gl-matrix/vec3.js'; 30 | import * as vec4 from '../../third-party/gl-matrix/src/gl-matrix/vec4.js'; 31 | 32 | export { 33 | glMatrix, 34 | mat2, 35 | mat2d, 36 | mat3, 37 | mat4, 38 | quat, 39 | quat2, 40 | vec2, 41 | vec3, 42 | vec4, 43 | }; 44 | -------------------------------------------------------------------------------- /js/render/math/ray.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {mat3, vec3} from './gl-matrix.js'; 22 | 23 | let normalMat = mat3.create(); 24 | 25 | const RAY_INTERSECTION_OFFSET = 0.02; 26 | 27 | export class Ray { 28 | constructor(matrix = null) { 29 | this.origin = vec3.create(); 30 | 31 | this._dir = vec3.create(); 32 | this._dir[2] = -1.0; 33 | 34 | if (matrix) { 35 | vec3.transformMat4(this.origin, this.origin, matrix); 36 | mat3.fromMat4(normalMat, matrix); 37 | vec3.transformMat3(this._dir, this._dir, normalMat); 38 | } 39 | 40 | // To force the inverse and sign calculations. 41 | this.direction = this._dir; 42 | } 43 | 44 | get direction() { 45 | return this._dir; 46 | } 47 | 48 | set direction(value) { 49 | this._dir = vec3.copy(this._dir, value); 50 | vec3.normalize(this._dir, this._dir); 51 | 52 | this.inv_dir = vec3.fromValues( 53 | 1.0 / this._dir[0], 54 | 1.0 / this._dir[1], 55 | 1.0 / this._dir[2]); 56 | 57 | this.sign = [ 58 | (this.inv_dir[0] < 0) ? 1 : 0, 59 | (this.inv_dir[1] < 0) ? 1 : 0, 60 | (this.inv_dir[2] < 0) ? 1 : 0, 61 | ]; 62 | } 63 | 64 | // Borrowed from: 65 | // eslint-disable-next-line max-len 66 | // https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection 67 | intersectsAABB(min, max) { 68 | let r = this; 69 | 70 | let bounds = [min, max]; 71 | 72 | let tmin = (bounds[r.sign[0]][0] - r.origin[0]) * r.inv_dir[0]; 73 | let tmax = (bounds[1-r.sign[0]][0] - r.origin[0]) * r.inv_dir[0]; 74 | let tymin = (bounds[r.sign[1]][1] - r.origin[1]) * r.inv_dir[1]; 75 | let tymax = (bounds[1-r.sign[1]][1] - r.origin[1]) * r.inv_dir[1]; 76 | 77 | if ((tmin > tymax) || (tymin > tmax)) { 78 | return null; 79 | } 80 | if (tymin > tmin) { 81 | tmin = tymin; 82 | } 83 | if (tymax < tmax) { 84 | tmax = tymax; 85 | } 86 | 87 | let tzmin = (bounds[r.sign[2]][2] - r.origin[2]) * r.inv_dir[2]; 88 | let tzmax = (bounds[1-r.sign[2]][2] - r.origin[2]) * r.inv_dir[2]; 89 | 90 | if ((tmin > tzmax) || (tzmin > tmax)) { 91 | return null; 92 | } 93 | if (tzmin > tmin) { 94 | tmin = tzmin; 95 | } 96 | if (tzmax < tmax) { 97 | tmax = tzmax; 98 | } 99 | 100 | let t = -1; 101 | if (tmin > 0 && tmax > 0) { 102 | t = Math.min(tmin, tmax); 103 | } else if (tmin > 0) { 104 | t = tmin; 105 | } else if (tmax > 0) { 106 | t = tmax; 107 | } else { 108 | // Intersection is behind the ray origin. 109 | return null; 110 | } 111 | 112 | // Push ray intersection point back along the ray a bit so that cursors 113 | // don't accidentally intersect with the hit surface. 114 | t -= RAY_INTERSECTION_OFFSET; 115 | 116 | // Return the point where the ray first intersected with the AABB. 117 | let intersectionPoint = vec3.clone(this._dir); 118 | vec3.scale(intersectionPoint, intersectionPoint, t); 119 | vec3.add(intersectionPoint, intersectionPoint, this.origin); 120 | return intersectionPoint; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /js/render/nodes/bounds-renderer.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | /* 22 | This file renders a passed in XRBoundedReferenceSpace object and attempts 23 | to render geometry on the floor to indicate where the bounds is. 24 | The bounds `geometry` is a series of DOMPointReadOnlys in 25 | clockwise-order. 26 | */ 27 | 28 | import {Material, RENDER_ORDER} from '../core/material.js'; 29 | import {Node} from '../core/node.js'; 30 | import {Primitive, PrimitiveAttribute} from '../core/primitive.js'; 31 | 32 | const GL = WebGLRenderingContext; // For enums 33 | 34 | const BOUNDS_HEIGHT = 0.5; // Meters 35 | 36 | class BoundsMaterial extends Material { 37 | constructor() { 38 | super(); 39 | 40 | this.renderOrder = RENDER_ORDER.ADDITIVE; 41 | this.state.blend = true; 42 | this.state.blendFuncSrc = GL.SRC_ALPHA; 43 | this.state.blendFuncDst = GL.ONE; 44 | this.state.depthTest = false; 45 | this.state.cullFace = false; 46 | } 47 | 48 | get materialName() { 49 | return 'BOUNDS_RENDERER'; 50 | } 51 | 52 | get vertexSource() { 53 | return ` 54 | in vec3 POSITION; 55 | out vec3 v_pos; 56 | vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { 57 | v_pos = POSITION; 58 | return proj * view * model * vec4(POSITION, 1.0); 59 | }`; 60 | } 61 | 62 | get fragmentSource() { 63 | return ` 64 | precision mediump float; 65 | out vec3 v_pos; 66 | vec4 fragment_main() { 67 | return vec4(0.25, 1.0, 0.5, (${BOUNDS_HEIGHT} - v_pos.y) / ${BOUNDS_HEIGHT}); 68 | }`; 69 | } 70 | } 71 | 72 | export class BoundsRenderer extends Node { 73 | constructor(boundedRefSpace) { 74 | super(); 75 | 76 | this._boundedRefSpace = boundedRefSpace; 77 | } 78 | 79 | onRendererChanged(renderer) { 80 | this.boundedRefSpace = this._boundedRefSpace; 81 | } 82 | 83 | get boundedRefSpace() { 84 | return this._boundedRefSpace; 85 | } 86 | 87 | set boundedRefSpace(refSpace) { 88 | if (this._boundedRefSpace) { 89 | this.clearRenderPrimitives(); 90 | } 91 | this._boundedRefSpace = refSpace; 92 | if (!refSpace || refSpace.boundsGeometry.length === 0 || !this._renderer) { 93 | return; 94 | } 95 | 96 | let geometry = refSpace.boundsGeometry; 97 | 98 | let verts = []; 99 | let indices = []; 100 | 101 | // Tessellate the bounding points from XRStageBounds and connect 102 | // each point to a neighbor and 0,0,0. 103 | const pointCount = geometry.length; 104 | let lastIndex = -1; 105 | for (let i = 0; i < pointCount; i++) { 106 | const point = geometry[i]; 107 | verts.push(point.x, 0, point.z); 108 | verts.push(point.x, BOUNDS_HEIGHT, point.z); 109 | 110 | lastIndex += 2; 111 | if (i > 0) { 112 | indices.push(lastIndex, lastIndex-1, lastIndex-2); 113 | indices.push(lastIndex-2, lastIndex-1, lastIndex-3); 114 | } 115 | } 116 | 117 | if (pointCount > 1) { 118 | indices.push(1, 0, lastIndex); 119 | indices.push(lastIndex, 0, lastIndex-1); 120 | } 121 | 122 | let vertexBuffer = this._renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(verts)); 123 | let indexBuffer = this._renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices)); 124 | 125 | let attribs = [ 126 | new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 12, 0), 127 | ]; 128 | 129 | let primitive = new Primitive(attribs, indices.length); 130 | primitive.setIndexBuffer(indexBuffer); 131 | 132 | let renderPrimitive = this._renderer.createRenderPrimitive(primitive, new BoundsMaterial()); 133 | this.addRenderPrimitive(renderPrimitive); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /js/render/nodes/drop-shadow.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {Material} from '../core/material.js'; 22 | import {Node} from '../core/node.js'; 23 | import {PrimitiveStream} from '../geometry/primitive-stream.js'; 24 | 25 | const GL = WebGLRenderingContext; // For enums 26 | 27 | const SHADOW_SEGMENTS = 32; 28 | const SHADOW_GROUND_OFFSET = 0.01; 29 | const SHADOW_CENTER_ALPHA = 0.7; 30 | const SHADOW_INNER_ALPHA = 0.3; 31 | const SHADOW_OUTER_ALPHA = 0.0; 32 | const SHADOW_INNER_RADIUS = 0.6; 33 | const SHADOW_OUTER_RADIUS = 1.0; 34 | 35 | class DropShadowMaterial extends Material { 36 | constructor() { 37 | super(); 38 | 39 | this.state.blend = true; 40 | this.state.blendFuncSrc = GL.ONE; 41 | this.state.blendFuncDst = GL.ONE_MINUS_SRC_ALPHA; 42 | this.state.depthFunc = GL.LEQUAL; 43 | this.state.depthMask = false; 44 | } 45 | 46 | get materialName() { 47 | return 'DROP_SHADOW_MATERIAL'; 48 | } 49 | 50 | get vertexSource() { 51 | return ` 52 | in vec3 POSITION; 53 | in vec2 TEXCOORD_0; 54 | 55 | out float vShadow; 56 | 57 | vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { 58 | vShadow = TEXCOORD_0.x; 59 | return proj * view * model * vec4(POSITION, 1.0); 60 | }`; 61 | } 62 | 63 | get fragmentSource() { 64 | return ` 65 | in float vShadow; 66 | 67 | vec4 fragment_main() { 68 | return vec4(0.0, 0.0, 0.0, vShadow); 69 | }`; 70 | } 71 | } 72 | 73 | export class DropShadowNode extends Node { 74 | constructor(iconTexture, callback) { 75 | super(); 76 | } 77 | 78 | onRendererChanged(renderer) { 79 | let stream = new PrimitiveStream(); 80 | 81 | stream.startGeometry(); 82 | 83 | // Shadow center 84 | stream.pushVertex(0, SHADOW_GROUND_OFFSET, 0, SHADOW_CENTER_ALPHA); 85 | 86 | let segRad = ((Math.PI * 2.0) / SHADOW_SEGMENTS); 87 | 88 | let idx; 89 | for (let i = 0; i < SHADOW_SEGMENTS; ++i) { 90 | idx = stream.nextVertexIndex; 91 | 92 | let rad = i * segRad; 93 | let x = Math.cos(rad); 94 | let y = Math.sin(rad); 95 | stream.pushVertex(x * SHADOW_INNER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_INNER_RADIUS, SHADOW_INNER_ALPHA); 96 | stream.pushVertex(x * SHADOW_OUTER_RADIUS, SHADOW_GROUND_OFFSET, y * SHADOW_OUTER_RADIUS, SHADOW_OUTER_ALPHA); 97 | 98 | if (i > 0) { 99 | // Inner circle 100 | stream.pushTriangle(0, idx, idx-2); 101 | 102 | // Outer circle 103 | stream.pushTriangle(idx, idx+1, idx-1); 104 | stream.pushTriangle(idx, idx-1, idx-2); 105 | } 106 | } 107 | 108 | stream.pushTriangle(0, 1, idx); 109 | 110 | stream.pushTriangle(1, 2, idx+1); 111 | stream.pushTriangle(1, idx+1, idx); 112 | 113 | stream.endGeometry(); 114 | 115 | let shadowPrimitive = stream.finishPrimitive(renderer); 116 | this._shadowRenderPrimitive = renderer.createRenderPrimitive(shadowPrimitive, new DropShadowMaterial()); 117 | this.addRenderPrimitive(this._shadowRenderPrimitive); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /js/render/nodes/gltf2.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import {Node} from '../core/node.js'; 22 | import {Gltf2Loader} from '../loaders/gltf2.js'; 23 | 24 | // Using a weak map here allows us to cache a loader per-renderer without 25 | // modifying the renderer object or leaking memory when it's garbage collected. 26 | let gltfLoaderMap = new WeakMap(); 27 | 28 | export class Gltf2Node extends Node { 29 | constructor(options) { 30 | super(); 31 | this._url = options.url; 32 | 33 | this._promise = null; 34 | this._resolver = null; 35 | this._rejecter = null; 36 | } 37 | 38 | onRendererChanged(renderer) { 39 | let loader = gltfLoaderMap.get(renderer); 40 | if (!loader) { 41 | loader = new Gltf2Loader(renderer); 42 | gltfLoaderMap.set(renderer, loader); 43 | } 44 | 45 | // Do we have a previously resolved promise? If so clear it. 46 | if (!this._resolver && this._promise) { 47 | this._promise = null; 48 | } 49 | 50 | this._ensurePromise(); 51 | 52 | loader.loadFromUrl(this._url).then((sceneNode) => { 53 | this.addNode(sceneNode); 54 | this._resolver(sceneNode.waitForComplete()); 55 | this._resolver = null; 56 | this._rejecter = null; 57 | }).catch((err) => { 58 | if (this._rejecter) { 59 | this._rejecter(err); 60 | } 61 | this._resolver = null; 62 | this._rejecter = null; 63 | }); 64 | } 65 | 66 | _ensurePromise() { 67 | if (!this._promise) { 68 | this._promise = new Promise((resolve, reject) => { 69 | this._resolver = resolve; 70 | this._rejecter = reject; 71 | }); 72 | } 73 | return this._promise; 74 | } 75 | 76 | waitForComplete() { 77 | return this._ensurePromise(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /js/render/nodes/menu-system.js: -------------------------------------------------------------------------------- 1 | import { UrlTexture } from "../core/texture.js"; 2 | import { ButtonNode } from "./button.js"; 3 | import { Node } from "../core/node.js"; 4 | 5 | const BUTTON_INTERVAL = 0.12; 6 | 7 | export class MenuSystem { 8 | constructor(highlightOverwrite = true) { 9 | this.buttons = []; 10 | this.highlightOverwrite = highlightOverwrite; 11 | this.menuBarNode = null; 12 | this.lastFrameButtonsByGamepad = new WeakMap(); 13 | } 14 | 15 | createButton(path, action) { 16 | let button = new ButtonNode(new UrlTexture(path), () => {}); 17 | this.buttons.push({ node: button, action: action, state: null }); 18 | } 19 | 20 | createSwitch(path1, action1, path2, action2) { 21 | let button1 = new ButtonNode(new UrlTexture(path1), () => {}); 22 | let button2 = new ButtonNode(new UrlTexture(path2), () => {}); 23 | this.buttons.push({ 24 | node: [button1, button2], 25 | action: [action1, action2], 26 | state: 0, 27 | }); 28 | } 29 | 30 | getButtons() { 31 | return this.buttons; 32 | } 33 | 34 | getButtonNode(button) { 35 | if (button.state != null) { 36 | return button.node[button.state]; 37 | } else { 38 | return button.node; 39 | } 40 | } 41 | 42 | executeButtonAction(button) { 43 | if (button.state != null) { 44 | button.action[button.state](); 45 | } else { 46 | button.action(); 47 | } 48 | } 49 | 50 | createMenuLayout() { 51 | let numButtons = this.buttons.length; 52 | for (var i = 0; i < numButtons; i++) { 53 | let buttonX = (i + (1 - numButtons) / 2) * BUTTON_INTERVAL; 54 | this.buttons[i]["translation"] = [buttonX, -0.1, -0.1]; 55 | this.buttons[i]["rotation"] = [-0.3826834, 0, 0, 0.9238795]; 56 | } 57 | } 58 | 59 | getMenuBarNode() { 60 | this.menuBarNode = new Node(); 61 | this.createMenuLayout(); 62 | for (let button of this.buttons) { 63 | let buttonNode = this.getButtonNode(button); 64 | buttonNode.translation = button.translation; 65 | buttonNode.rotation = button.rotation; 66 | this.menuBarNode.addNode(buttonNode); 67 | } 68 | this.menuBarNode.translation = [0, -0.4, -0.2]; 69 | 70 | return this.menuBarNode; 71 | } 72 | 73 | processInput(frame, scene, refSpace) { 74 | let hits = []; 75 | for (let source of frame.session.inputSources) { 76 | let gamepad = source.gamepad; 77 | if (gamepad) { 78 | let pose = frame.getPose(source.gripSpace, refSpace); 79 | if (gamepad.buttons[4].pressed) { 80 | if (pose) { 81 | let pos = pose.transform.position; 82 | let rot = pose.transform.orientation; 83 | this.menuBarNode.translation = [pos.x, pos.y, pos.z]; 84 | this.menuBarNode.rotation = [rot.x, rot.y, rot.z, rot.w]; 85 | } 86 | } 87 | let targetRayPose = frame.getPose(source.targetRaySpace, refSpace); 88 | if (!targetRayPose) { 89 | continue; 90 | } 91 | let hitResult = scene.hitTest(targetRayPose.transform); 92 | let lastFrameButtons = this.lastFrameButtonsByGamepad.get(gamepad); 93 | if (!lastFrameButtons) { 94 | lastFrameButtons = new Array(gamepad.buttons.length); 95 | lastFrameButtons.fill(false); 96 | this.lastFrameButtonsByGamepad.set(gamepad, lastFrameButtons); 97 | } 98 | hits.push({ 99 | hitResult: hitResult, 100 | buttonPressed: 101 | 0 < gamepad.buttons.length && 102 | gamepad.buttons[0].pressed && 103 | !lastFrameButtons[0], 104 | }); 105 | for (let i = 0; i < gamepad.buttons.length; i++) { 106 | lastFrameButtons[i] = gamepad.buttons[i].pressed; 107 | } 108 | } 109 | } 110 | 111 | for (let button of this.buttons) { 112 | for (let hit of hits) { 113 | let buttonNode = this.getButtonNode(button); 114 | if (this.highlightOverwrite) { 115 | buttonNode.scale = [1, 1, 1]; 116 | } 117 | if (hit.hitResult && hit.hitResult.node == buttonNode) { 118 | if (this.highlightOverwrite) { 119 | buttonNode.scale = [1.2, 1.2, 1.2]; 120 | } 121 | if (hit.buttonPressed) { 122 | this.executeButtonAction(button); 123 | if (button.state != null) { 124 | this.menuBarNode.removeNode(buttonNode); 125 | button.state = 1 - button.state; 126 | let newButtonNode = this.getButtonNode(button); 127 | newButtonNode.translation = button.translation; 128 | newButtonNode.rotation = button.rotation; 129 | this.menuBarNode.addNode(newButtonNode); 130 | } 131 | } 132 | break; 133 | } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /js/render/nodes/quad-texture.js: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | import { Material } from "../core/material.js"; 22 | import { Node } from "../core/node.js"; 23 | import { PrimitiveStream } from "../geometry/primitive-stream.js"; 24 | import { UrlTexture } from "../core/texture.js"; 25 | 26 | class ButtonIconMaterial extends Material { 27 | constructor() { 28 | super(); 29 | 30 | this.state.blend = true; 31 | this.icon = this.defineSampler("icon"); 32 | } 33 | 34 | get materialName() { 35 | return "BUTTON_ICON_MATERIAL"; 36 | } 37 | 38 | get vertexSource() { 39 | return ` 40 | in vec3 POSITION; 41 | in vec2 TEXCOORD_0; 42 | 43 | out vec2 vTexCoord; 44 | 45 | vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { 46 | vTexCoord = TEXCOORD_0; 47 | vec4 pos = vec4(POSITION.x, POSITION.y, POSITION.z, 1.0); 48 | return proj * view * model * pos; 49 | }`; 50 | } 51 | 52 | get fragmentSource() { 53 | return ` 54 | uniform sampler2D icon; 55 | in vec2 vTexCoord; 56 | 57 | vec4 fragment_main() { 58 | return texture(icon, vTexCoord); 59 | }`; 60 | } 61 | } 62 | 63 | export class QuadNode extends Node { 64 | constructor(texturePath, textureSize, selectable=false) { 65 | super(); 66 | 67 | this.selectable = selectable; 68 | this._textureSize = textureSize; 69 | this._iconTexture = new UrlTexture(texturePath); 70 | } 71 | 72 | onRendererChanged(renderer) { 73 | let stream = new PrimitiveStream(); 74 | let hs = this._textureSize * 0.5; 75 | stream.clear(); 76 | stream.startGeometry(); 77 | 78 | stream.pushVertex(-hs, hs, 0, 0, 0, 0, 0, 1); 79 | stream.pushVertex(-hs, -hs, 0, 0, 1, 0, 0, 1); 80 | stream.pushVertex(hs, -hs, 0, 1, 1, 0, 0, 1); 81 | stream.pushVertex(hs, hs, 0, 1, 0, 0, 0, 1); 82 | 83 | stream.pushTriangle(0, 1, 2); 84 | stream.pushTriangle(0, 2, 3); 85 | 86 | stream.endGeometry(); 87 | 88 | let iconPrimitive = stream.finishPrimitive(renderer); 89 | let iconMaterial = new ButtonIconMaterial(); 90 | iconMaterial.icon.texture = this._iconTexture; 91 | this._iconRenderPrimitive = renderer.createRenderPrimitive( 92 | iconPrimitive, 93 | iconMaterial 94 | ); 95 | this.addRenderPrimitive(this._iconRenderPrimitive); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /js/render/nodes/skybox.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | /* 22 | Node for displaying 360 equirect images as a skybox. 23 | */ 24 | 25 | import {Material, RENDER_ORDER} from '../core/material.js'; 26 | import {Primitive, PrimitiveAttribute} from '../core/primitive.js'; 27 | import {Node} from '../core/node.js'; 28 | import {UrlTexture} from '../core/texture.js'; 29 | 30 | const GL = WebGLRenderingContext; // For enums 31 | 32 | class SkyboxMaterial extends Material { 33 | constructor() { 34 | super(); 35 | this.renderOrder = RENDER_ORDER.SKY; 36 | this.state.depthFunc = GL.LEQUAL; 37 | this.state.depthMask = false; 38 | 39 | this.image = this.defineSampler('diffuse'); 40 | 41 | this.texCoordScaleOffset = this.defineUniform('texCoordScaleOffset', 42 | [1.0, 1.0, 0.0, 0.0, 43 | 1.0, 1.0, 0.0, 0.0], 4); 44 | } 45 | 46 | get materialName() { 47 | return 'SKYBOX'; 48 | } 49 | 50 | get vertexSource() { 51 | return ` 52 | uniform int EYE_INDEX; 53 | uniform vec4 texCoordScaleOffset[2]; 54 | in vec3 POSITION; 55 | in vec2 TEXCOORD_0; 56 | out vec2 vTexCoord; 57 | 58 | vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { 59 | vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX]; 60 | vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw; 61 | // Drop the translation portion of the view matrix 62 | view[3].xyz = vec3(0.0, 0.0, 0.0); 63 | vec4 out_vec = proj * view * model * vec4(POSITION, 1.0); 64 | 65 | // Returning the W component for both Z and W forces the geometry depth to 66 | // the far plane. When combined with a depth func of LEQUAL this makes the 67 | // sky write to any depth fragment that has not been written to yet. 68 | return out_vec.xyww; 69 | }`; 70 | } 71 | 72 | get fragmentSource() { 73 | return ` 74 | uniform sampler2D diffuse; 75 | in vec2 vTexCoord; 76 | 77 | vec4 fragment_main() { 78 | return texture(diffuse, vTexCoord); 79 | }`; 80 | } 81 | } 82 | 83 | export class SkyboxNode extends Node { 84 | constructor(options) { 85 | super(); 86 | 87 | this._url = options.url; 88 | this._displayMode = options.displayMode || 'mono'; 89 | this._rotationY = options.rotationY || 0; 90 | } 91 | 92 | onRendererChanged(renderer) { 93 | let vertices = []; 94 | let indices = []; 95 | 96 | let latSegments = 40; 97 | let lonSegments = 40; 98 | 99 | // Create the vertices/indices 100 | for (let i = 0; i <= latSegments; ++i) { 101 | let theta = i * Math.PI / latSegments; 102 | let sinTheta = Math.sin(theta); 103 | let cosTheta = Math.cos(theta); 104 | 105 | let idxOffsetA = i * (lonSegments+1); 106 | let idxOffsetB = (i+1) * (lonSegments+1); 107 | 108 | for (let j=0; j <= lonSegments; ++j) { 109 | let phi = (j * 2 * Math.PI / lonSegments) + this._rotationY; 110 | let x = Math.sin(phi) * sinTheta; 111 | let y = cosTheta; 112 | let z = -Math.cos(phi) * sinTheta; 113 | let u = (j / lonSegments); 114 | let v = (i / latSegments); 115 | 116 | // Vertex shader will force the geometry to the far plane, so the 117 | // radius of the sphere is immaterial. 118 | vertices.push(x, y, z, u, v); 119 | 120 | if (i < latSegments && j < lonSegments) { 121 | let idxA = idxOffsetA+j; 122 | let idxB = idxOffsetB+j; 123 | 124 | indices.push(idxA, idxB, idxA+1, 125 | idxB, idxB+1, idxA+1); 126 | } 127 | } 128 | } 129 | 130 | let vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices)); 131 | let indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices)); 132 | 133 | let attribs = [ 134 | new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0), 135 | new PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12), 136 | ]; 137 | 138 | let primitive = new Primitive(attribs, indices.length); 139 | primitive.setIndexBuffer(indexBuffer); 140 | 141 | let material = new SkyboxMaterial(); 142 | material.image.texture = new UrlTexture(this._url); 143 | 144 | switch (this._displayMode) { 145 | case 'mono': 146 | material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0, 147 | 1.0, 1.0, 0.0, 0.0]; 148 | break; 149 | case 'stereoTopBottom': 150 | material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0, 151 | 1.0, 0.5, 0.0, 0.5]; 152 | break; 153 | case 'stereoLeftRight': 154 | material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0, 155 | 0.5, 1.0, 0.5, 0.0]; 156 | break; 157 | } 158 | 159 | let renderPrimitive = renderer.createRenderPrimitive(primitive, material); 160 | this.addRenderPrimitive(renderPrimitive); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /js/render/nodes/video.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | /* 22 | Node for displaying 2D or stereo videos on a quad. 23 | */ 24 | 25 | import {Material} from '../core/material.js'; 26 | import {Primitive, PrimitiveAttribute} from '../core/primitive.js'; 27 | import {Node} from '../core/node.js'; 28 | import {VideoTexture} from '../core/texture.js'; 29 | 30 | const GL = WebGLRenderingContext; // For enums 31 | 32 | class VideoMaterial extends Material { 33 | constructor() { 34 | super(); 35 | 36 | this.image = this.defineSampler('diffuse'); 37 | 38 | this.texCoordScaleOffset = this.defineUniform('texCoordScaleOffset', 39 | [1.0, 1.0, 0.0, 0.0, 40 | 1.0, 1.0, 0.0, 0.0], 4); 41 | } 42 | 43 | get materialName() { 44 | return 'VIDEO_PLAYER'; 45 | } 46 | 47 | get vertexSource() { 48 | return ` 49 | uniform int EYE_INDEX; 50 | uniform vec4 texCoordScaleOffset[2]; 51 | in vec3 POSITION; 52 | in vec2 TEXCOORD_0; 53 | out vec2 vTexCoord; 54 | 55 | vec4 vertex_main(mat4 proj, mat4 view, mat4 model) { 56 | vec4 scaleOffset = texCoordScaleOffset[EYE_INDEX]; 57 | vTexCoord = (TEXCOORD_0 * scaleOffset.xy) + scaleOffset.zw; 58 | vec4 out_vec = proj * view * model * vec4(POSITION, 1.0); 59 | return out_vec; 60 | }`; 61 | } 62 | 63 | get fragmentSource() { 64 | return ` 65 | uniform sampler2D diffuse; 66 | in vec2 vTexCoord; 67 | 68 | vec4 fragment_main() { 69 | return texture(diffuse, vTexCoord); 70 | }`; 71 | } 72 | } 73 | 74 | export class VideoNode extends Node { 75 | constructor(options) { 76 | super(); 77 | 78 | this._video = options.video; 79 | this._displayMode = options.displayMode || 'mono'; 80 | 81 | this._video_texture = new VideoTexture(this._video); 82 | } 83 | 84 | get aspectRatio() { 85 | let width = this._video.videoWidth; 86 | let height = this._video.videoHeight; 87 | 88 | switch (this._displayMode) { 89 | case 'stereoTopBottom': height *= 0.5; break; 90 | case 'stereoLeftRight': width *= 0.5; break; 91 | } 92 | 93 | if (!height || !width) { 94 | return 1; 95 | } 96 | 97 | return width / height; 98 | } 99 | 100 | onRendererChanged(renderer) { 101 | let vertices = [ 102 | -1.0, 1.0, 0.0, 0.0, 0.0, 103 | 1.0, 1.0, 0.0, 1.0, 0.0, 104 | 1.0, -1.0, 0.0, 1.0, 1.0, 105 | -1.0, -1.0, 0.0, 0.0, 1.0, 106 | ]; 107 | let indices = [ 108 | 0, 2, 1, 109 | 0, 3, 2, 110 | ]; 111 | 112 | let vertexBuffer = renderer.createRenderBuffer(GL.ARRAY_BUFFER, new Float32Array(vertices)); 113 | let indexBuffer = renderer.createRenderBuffer(GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices)); 114 | 115 | let attribs = [ 116 | new PrimitiveAttribute('POSITION', vertexBuffer, 3, GL.FLOAT, 20, 0), 117 | new PrimitiveAttribute('TEXCOORD_0', vertexBuffer, 2, GL.FLOAT, 20, 12), 118 | ]; 119 | 120 | let primitive = new Primitive(attribs, indices.length); 121 | primitive.setIndexBuffer(indexBuffer); 122 | primitive.setBounds([-1.0, -1.0, 0.0], [1.0, 1.0, 0.015]); 123 | 124 | let material = new VideoMaterial(); 125 | material.image.texture = this._video_texture; 126 | 127 | switch (this._displayMode) { 128 | case 'mono': 129 | material.texCoordScaleOffset.value = [1.0, 1.0, 0.0, 0.0, 130 | 1.0, 1.0, 0.0, 0.0]; 131 | break; 132 | case 'stereoTopBottom': 133 | material.texCoordScaleOffset.value = [1.0, 0.5, 0.0, 0.0, 134 | 1.0, 0.5, 0.0, 0.5]; 135 | break; 136 | case 'stereoLeftRight': 137 | material.texCoordScaleOffset.value = [0.5, 1.0, 0.0, 0.0, 138 | 0.5, 1.0, 0.5, 0.0]; 139 | break; 140 | } 141 | 142 | let renderPrimitive = renderer.createRenderPrimitive(primitive, material); 143 | this.addRenderPrimitive(renderPrimitive); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /js/stereo-util.js: -------------------------------------------------------------------------------- 1 | window.VRStereoUtil = (function () { 2 | "use strict"; 3 | 4 | var VS = [ 5 | "#version 300 es", 6 | "uniform vec2 u_offset;", 7 | "uniform vec2 u_scale;", 8 | "out mediump vec2 v_texcoord;", 9 | 10 | "void main() {", 11 | 12 | // xy - coords of the quad, normalized to 0..1 13 | // xy - UV of the source texture coordinate. 14 | " const vec2 quad_positions[6] = vec2[6]", 15 | " (", 16 | " vec2(0.0, 0.0),", 17 | " vec2(1.0, 0.0),", 18 | " vec2(0.0, 1.0),", 19 | 20 | " vec2(0.0, 1.0),", 21 | " vec2(1.0, 0.0),", 22 | " vec2(1.0, 1.0)", 23 | " );", 24 | 25 | " gl_Position = vec4(((quad_positions[gl_VertexID].xy * u_scale + u_offset) * 2.0) - 1.0, 0.0, 1.0);", 26 | " v_texcoord = quad_positions[gl_VertexID].xy * u_scale + u_offset;", 27 | "}", 28 | ].join("\n"); 29 | 30 | var VSMultiview = [ 31 | "#version 300 es", 32 | "uniform vec2 u_offset;", 33 | "uniform vec2 u_scale;", 34 | "out mediump vec3 v_texcoord;", 35 | 36 | "void main() {", 37 | // offset of eye quad in -1..1 space 38 | " const float eye_offset_x[12] = float[12] (", 39 | " 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,", 40 | " 1.0, 1.0, 1.0, 1.0, 1.0, 1.0", 41 | " );", 42 | // xy - coords of the quad, normalized to 0..1 43 | // xy - UV of the source texture coordinate. 44 | // z - texture layer (eye) index - 0 or 1. 45 | " const vec3 quad_positions[12] = vec3[12]", 46 | " (", 47 | " vec3(0.0, 0.0, 0.0),", 48 | " vec3(1.0, 0.0, 0.0),", 49 | " vec3(0.0, 1.0, 0.0),", 50 | 51 | " vec3(0.0, 1.0, 0.0),", 52 | " vec3(1.0, 0.0, 0.0),", 53 | " vec3(1.0, 1.0, 0.0),", 54 | 55 | " vec3(0.0, 0.0, 1.0),", 56 | " vec3(1.0, 0.0, 1.0),", 57 | " vec3(0.0, 1.0, 1.0),", 58 | 59 | " vec3(0.0, 1.0, 1.0),", 60 | " vec3(1.0, 0.0, 1.0),", 61 | " vec3(1.0, 1.0, 1.0)", 62 | " );", 63 | 64 | " const vec2 pos_scale = vec2(0.5, 1.0);", 65 | " vec2 eye_offset = vec2(eye_offset_x[gl_VertexID], 0.0);", 66 | " gl_Position = vec4(((quad_positions[gl_VertexID].xy * u_scale + u_offset) * pos_scale * 2.0) - 1.0 + eye_offset, 0.0, 1.0);", 67 | " v_texcoord = vec3(quad_positions[gl_VertexID].xy * u_scale + u_offset, quad_positions[gl_VertexID].z);", 68 | "}", 69 | ].join("\n"); 70 | 71 | var FS = [ 72 | "#version 300 es", 73 | "uniform mediump sampler2D u_source_texture;", 74 | "in mediump vec2 v_texcoord;", 75 | "out mediump vec4 output_color;", 76 | 77 | "void main()", 78 | "{", 79 | " output_color = texture(u_source_texture, v_texcoord);", 80 | "}", 81 | ].join("\n"); 82 | 83 | var FSMultiview = [ 84 | "#version 300 es", 85 | "uniform mediump sampler2DArray u_source_texture;", 86 | "in mediump vec3 v_texcoord;", 87 | "out mediump vec4 output_color;", 88 | 89 | "void main()", 90 | "{", 91 | " output_color = texture(u_source_texture, v_texcoord);", 92 | "}", 93 | ].join("\n"); 94 | 95 | 96 | var StereoUtil = function (gl) { 97 | this.gl = gl; 98 | 99 | //this.vaoext = gl.getExtension("OES_vertex_array_object"); // Vendor prefixes may apply! 100 | this.vao = gl.createVertexArray(); 101 | 102 | this.program = new WGLUProgram(gl); 103 | this.program.attachShaderSource(VS, gl.VERTEX_SHADER); 104 | this.program.attachShaderSource(FS, gl.FRAGMENT_SHADER); 105 | this.program.bindAttribLocation({ 106 | position: 0, 107 | texCoord: 1 108 | }); 109 | this.program.link(); 110 | 111 | console.log("compiling multiview shader"); 112 | this.program_multiview = new WGLUProgram(gl); 113 | this.program_multiview.attachShaderSource(VSMultiview, gl.VERTEX_SHADER); 114 | this.program_multiview.attachShaderSource(FSMultiview, gl.FRAGMENT_SHADER); 115 | this.program_multiview.bindAttribLocation({ 116 | v_texcoord: 0, 117 | }); 118 | this.program_multiview.link(); 119 | }; 120 | 121 | var mvrenReported = false; 122 | StereoUtil.prototype.blit = function (multiview, 123 | source_texture, 124 | source_rect_uv_x, 125 | source_rect_uv_y, 126 | source_rect_uv_width, 127 | source_rect_uv_height, 128 | dest_surface_width, 129 | dest_surface_height) { 130 | let gl = this.gl; 131 | let program = this.program; 132 | 133 | gl.activeTexture(gl.TEXTURE0); 134 | 135 | if (multiview) { 136 | gl.bindTexture(gl.TEXTURE_2D_ARRAY, source_texture); 137 | if (!mvrenReported) { 138 | console.log("render multiview"); 139 | mvrenReported = true; 140 | } 141 | program = this.program_multiview; 142 | program.use(); 143 | } 144 | else { 145 | gl.bindTexture(gl.TEXTURE_2D, source_texture); 146 | mvrenReported = false; 147 | program.use(); 148 | //gl.uniformMatrix4fv(program.uniform.projectionMat, false, projectionMat); 149 | //gl.uniformMatrix4fv(program.uniform.modelViewMat, false, modelViewMat); 150 | } 151 | 152 | // Render to the destination texture, sampling from the scratch texture 153 | gl.disable(gl.SCISSOR_TEST); 154 | gl.disable(gl.DEPTH_TEST); 155 | gl.disable(gl.STENCIL_TEST); 156 | // gl.disable(gl.CULL_FACE); 157 | gl.colorMask(true, true, true, true); 158 | gl.depthMask(false); 159 | // gl.disable(gl.BLEND); 160 | // gl.disable(gl.DITHER); 161 | 162 | gl.viewport(0, 0, dest_surface_width, dest_surface_height); 163 | 164 | gl.uniform2f(program.uniform.u_scale, source_rect_uv_width, source_rect_uv_height); 165 | gl.uniform2f(program.uniform.u_offset, source_rect_uv_x, source_rect_uv_y); 166 | gl.uniform1i(program.uniform.u_source_texture, 0); 167 | 168 | // Start setting up VAO 169 | gl.bindVertexArray(this.vao); 170 | if (multiview) { 171 | gl.drawArrays(gl.TRIANGLES, 0, 12); 172 | } else { 173 | gl.drawArrays(gl.TRIANGLES, 0, 6); 174 | } 175 | 176 | gl.enable(gl.DEPTH_TEST); 177 | gl.depthMask(true); 178 | }; 179 | 180 | return StereoUtil; 181 | })(); 182 | -------------------------------------------------------------------------------- /js/third-party/gl-matrix/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /js/third-party/gl-matrix/src/gl-matrix.js: -------------------------------------------------------------------------------- 1 | import * as glMatrix from "./gl-matrix/common.js"; 2 | import * as mat2 from "./gl-matrix/mat2.js"; 3 | import * as mat2d from "./gl-matrix/mat2d.js"; 4 | import * as mat3 from "./gl-matrix/mat3.js"; 5 | import * as mat4 from "./gl-matrix/mat4.js"; 6 | import * as quat from "./gl-matrix/quat.js"; 7 | import * as quat2 from "./gl-matrix/quat2.js"; 8 | import * as vec2 from "./gl-matrix/vec2.js"; 9 | import * as vec3 from "./gl-matrix/vec3.js"; 10 | import * as vec4 from "./gl-matrix/vec4.js"; 11 | 12 | export { 13 | glMatrix, 14 | mat2, mat2d, mat3, mat4, 15 | quat, quat2, 16 | vec2, vec3, vec4, 17 | }; 18 | -------------------------------------------------------------------------------- /js/third-party/gl-matrix/src/gl-matrix/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common utilities 3 | * @module glMatrix 4 | */ 5 | 6 | // Configuration Constants 7 | export const EPSILON = 0.000001; 8 | export let ARRAY_TYPE = (typeof Float32Array !== 'undefined') ? Float32Array : Array; 9 | export const RANDOM = Math.random; 10 | 11 | /** 12 | * Sets the type of array used when creating new vectors and matrices 13 | * 14 | * @param {Type} type Array type, such as Float32Array or Array 15 | */ 16 | export function setMatrixArrayType(type) { 17 | ARRAY_TYPE = type; 18 | } 19 | 20 | const degree = Math.PI / 180; 21 | 22 | /** 23 | * Convert Degree To Radian 24 | * 25 | * @param {Number} a Angle in Degrees 26 | */ 27 | export function toRadian(a) { 28 | return a * degree; 29 | } 30 | 31 | /** 32 | * Tests whether or not the arguments have approximately the same value, within an absolute 33 | * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less 34 | * than or equal to 1.0, and a relative tolerance is used for larger values) 35 | * 36 | * @param {Number} a The first number to test. 37 | * @param {Number} b The second number to test. 38 | * @returns {Boolean} True if the numbers are approximately equal, false otherwise. 39 | */ 40 | export function equals(a, b) { 41 | return Math.abs(a - b) <= EPSILON*Math.max(1.0, Math.abs(a), Math.abs(b)); 42 | } 43 | -------------------------------------------------------------------------------- /js/util/inline-viewer-helper.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | /* 22 | Provides a simple method for tracking which XRReferenceSpace is associated with 23 | which XRSession. Also handles the necessary logic for enabling mouse/touch-based 24 | view rotation for inline sessions if desired. 25 | */ 26 | 27 | import {quat} from '../render/math/gl-matrix.js'; 28 | 29 | const LOOK_SPEED = 0.0025; 30 | 31 | export class InlineViewerHelper { 32 | constructor(canvas, referenceSpace) { 33 | this.lookYaw = 0; 34 | this.lookPitch = 0; 35 | this.viewerHeight = 0; 36 | 37 | this.canvas = canvas; 38 | this.baseRefSpace = referenceSpace; 39 | this.refSpace = referenceSpace; 40 | 41 | this.dirty = false; 42 | 43 | if (canvas.style) { 44 | canvas.style.cursor = 'grab'; 45 | } 46 | 47 | canvas.addEventListener('mousemove', (event) => { 48 | // Only rotate when the left button is pressed 49 | if (event.buttons & 1) { 50 | this.rotateView(event.movementX, event.movementY); 51 | } 52 | }); 53 | 54 | // Keep track of touch-related state so that users can touch and drag on 55 | // the canvas to adjust the viewer pose in an inline session. 56 | let primaryTouch = undefined; 57 | let prevTouchX = undefined; 58 | let prevTouchY = undefined; 59 | 60 | canvas.addEventListener("touchstart", (event) => { 61 | if (primaryTouch == undefined) { 62 | let touch = event.changedTouches[0]; 63 | primaryTouch = touch.identifier; 64 | prevTouchX = touch.pageX; 65 | prevTouchY = touch.pageY; 66 | } 67 | }); 68 | 69 | canvas.addEventListener("touchend", (event) => { 70 | for (let touch of event.changedTouches) { 71 | if (primaryTouch == touch.identifier) { 72 | primaryTouch = undefined; 73 | this.rotateView(touch.pageX - prevTouchX, touch.pageY - prevTouchY); 74 | } 75 | } 76 | }); 77 | 78 | canvas.addEventListener("touchcancel", (event) => { 79 | for (let touch of event.changedTouches) { 80 | if (primaryTouch == touch.identifier) { 81 | primaryTouch = undefined; 82 | } 83 | } 84 | }); 85 | 86 | canvas.addEventListener("touchmove", (event) => { 87 | for (let touch of event.changedTouches) { 88 | if (primaryTouch == touch.identifier) { 89 | this.rotateView(touch.pageX - prevTouchX, touch.pageY - prevTouchY); 90 | prevTouchX = touch.pageX; 91 | prevTouchY = touch.pageY; 92 | } 93 | } 94 | }); 95 | } 96 | 97 | setHeight(value) { 98 | if (this.viewerHeight != value) { 99 | this.viewerHeight = value; 100 | } 101 | this.dirty = true; 102 | } 103 | 104 | rotateView(dx, dy) { 105 | this.lookYaw += dx * LOOK_SPEED; 106 | this.lookPitch += dy * LOOK_SPEED; 107 | if (this.lookPitch < -Math.PI*0.5) { 108 | this.lookPitch = -Math.PI*0.5; 109 | } 110 | if (this.lookPitch > Math.PI*0.5) { 111 | this.lookPitch = Math.PI*0.5; 112 | } 113 | this.dirty = true; 114 | } 115 | 116 | reset() { 117 | this.lookYaw = 0; 118 | this.lookPitch = 0; 119 | this.refSpace = this.baseRefSpace; 120 | this.dirty = false; 121 | } 122 | 123 | // XRReferenceSpace offset is immutable, so return a new reference space 124 | // that has an updated orientation. 125 | get referenceSpace() { 126 | if (this.dirty) { 127 | // Represent the rotational component of the reference space as a 128 | // quaternion. 129 | let invOrient = quat.create(); 130 | quat.rotateX(invOrient, invOrient, -this.lookPitch); 131 | quat.rotateY(invOrient, invOrient, -this.lookYaw); 132 | let xform = new XRRigidTransform( 133 | {}, 134 | {x: invOrient[0], y: invOrient[1], z: invOrient[2], w: invOrient[3]}); 135 | this.refSpace = this.baseRefSpace.getOffsetReferenceSpace(xform); 136 | xform = new XRRigidTransform({y: -this.viewerHeight}); 137 | this.refSpace = this.refSpace.getOffsetReferenceSpace(xform); 138 | this.dirty = false; 139 | } 140 | return this.refSpace; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /js/util/query-args.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Immersive Web Community Group 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | /* 22 | Provides a simple way to get values from the query string if they're present 23 | and use a default value if not. Not strictly a "WebGL" utility, but I use it 24 | frequently enough for debugging that I wanted to include it here. 25 | 26 | Example: 27 | For the URL http://example.com/index.html?particleCount=1000 28 | 29 | QueryArgs.getInt("particleCount", 100); // URL overrides, returns 1000 30 | QueryArgs.getInt("particleSize", 10); // Not in URL, returns default of 10 31 | */ 32 | 33 | let urlArgs = null; 34 | window.onhashchange = function() { 35 | // Force re-parsing on next access 36 | urlArgs = null; 37 | }; 38 | 39 | function ensureArgsCached() { 40 | if (!urlArgs) { 41 | urlArgs = {}; 42 | let query = window.location.search.substring(1) || window.location.hash.substring(1); 43 | let vars = query.split('&'); 44 | for (let i = 0; i < vars.length; i++) { 45 | let pair = vars[i].split('='); 46 | urlArgs[pair[0].toLowerCase()] = decodeURIComponent(pair[1]); 47 | } 48 | } 49 | } 50 | 51 | export class QueryArgs { 52 | static getString(name, defaultValue) { 53 | ensureArgsCached(); 54 | let lcaseName = name.toLowerCase(); 55 | if (lcaseName in urlArgs) { 56 | return urlArgs[lcaseName]; 57 | } 58 | return defaultValue; 59 | } 60 | 61 | static getInt(name, defaultValue) { 62 | ensureArgsCached(); 63 | let lcaseName = name.toLowerCase(); 64 | if (lcaseName in urlArgs) { 65 | return parseInt(urlArgs[lcaseName], 10); 66 | } 67 | return defaultValue; 68 | } 69 | 70 | static getFloat(name, defaultValue) { 71 | ensureArgsCached(); 72 | let lcaseName = name.toLowerCase(); 73 | if (lcaseName in urlArgs) { 74 | return parseFloat(urlArgs[lcaseName]); 75 | } 76 | return defaultValue; 77 | } 78 | 79 | static getBool(name, defaultValue) { 80 | ensureArgsCached(); 81 | let lcaseName = name.toLowerCase(); 82 | if (lcaseName in urlArgs) { 83 | return parseInt(urlArgs[lcaseName], 10) != 0; 84 | } 85 | return defaultValue; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /js/util/texture-loader.js: -------------------------------------------------------------------------------- 1 | function isPowerOf2(value) { 2 | return (value & (value - 1)) == 0; 3 | } 4 | 5 | function loadIntoTexture(gl, texture, url, complete_callback) { 6 | gl.bindTexture(gl.TEXTURE_2D, texture); 7 | 8 | // Because images have to be download over the internet 9 | // they might take a moment until they are ready. 10 | // Until then put a single pixel in the texture so we can 11 | // use it immediately. When the image has finished downloading 12 | // we'll update the texture with the contents of the image. 13 | const level = 0; 14 | const internalFormat = gl.RGBA; 15 | const width = 1; 16 | const height = 1; 17 | const border = 0; 18 | const srcFormat = gl.RGBA; 19 | const srcType = gl.UNSIGNED_BYTE; 20 | const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue 21 | gl.texImage2D( 22 | gl.TEXTURE_2D, 23 | level, 24 | internalFormat, 25 | width, 26 | height, 27 | border, 28 | srcFormat, 29 | srcType, 30 | pixel 31 | ); 32 | 33 | const image = new Image(); 34 | image.onload = function () { 35 | gl.bindTexture(gl.TEXTURE_2D, texture); 36 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 37 | gl.texImage2D( 38 | gl.TEXTURE_2D, 39 | level, 40 | internalFormat, 41 | srcFormat, 42 | srcType, 43 | image 44 | ); 45 | 46 | // WebGL1 has different requirements for power of 2 images 47 | // vs non power of 2 images so check if the image is a 48 | // power of 2 in both dimensions. 49 | if (isPowerOf2(image.width) && isPowerOf2(image.height)) { 50 | // Yes, it's a power of 2. Generate mips. 51 | gl.generateMipmap(gl.TEXTURE_2D); 52 | //?gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); 53 | //?gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR_MIPMAP_LINEAR); 54 | } else { 55 | // No, it's not a power of 2. Turn of mips and set 56 | // wrapping to clamp to edge 57 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 58 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 59 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 60 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 61 | } 62 | complete_callback(image.width, image.height); 63 | }; 64 | image.src = url; 65 | } 66 | 67 | // 68 | // Initialize a texture and load an image. 69 | // When the image finished loading copy it into the texture. 70 | // 71 | export function loadTexture(gl, url, complete_callback) { 72 | const texture = gl.createTexture(); 73 | loadIntoTexture(gl, texture, url, complete_callback); 74 | return texture; 75 | } 76 | -------------------------------------------------------------------------------- /js/wglu/wglu-program.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Utility class to make loading shader programs easier. Does all the error 25 | checking you typically want, automatically queries uniform and attribute 26 | locations, and attempts to take advantage of some browser's ability to link 27 | asynchronously by not querying any information from the program until it's 28 | first use. 29 | */ 30 | var WGLUProgram = (function() { 31 | 32 | "use strict"; 33 | 34 | // Attempts to allow the browser to asynchronously compile and link 35 | var Program = function(gl) { 36 | this.gl = gl; 37 | this.program = gl.createProgram(); 38 | this.attrib = null; 39 | this.uniform = null; 40 | 41 | this._firstUse = true; 42 | this._vertexShader = null; 43 | this._fragmentShader = null; 44 | } 45 | 46 | Program.prototype.attachShaderSource = function(source, type) { 47 | var gl = this.gl; 48 | var shader; 49 | 50 | switch (type) { 51 | case gl.VERTEX_SHADER: 52 | this._vertexShader = gl.createShader(type); 53 | shader = this._vertexShader; 54 | break; 55 | case gl.FRAGMENT_SHADER: 56 | this._fragmentShader = gl.createShader(type); 57 | shader = this._fragmentShader; 58 | break; 59 | default: 60 | console.Error("Invalid Shader Type:", type); 61 | return; 62 | } 63 | 64 | gl.attachShader(this.program, shader); 65 | gl.shaderSource(shader, source); 66 | gl.compileShader(shader); 67 | } 68 | 69 | Program.prototype.attachShaderSourceFromXHR = function(url, type) { 70 | var self = this; 71 | return new Promise(function(resolve, reject) { 72 | var xhr = new XMLHttpRequest(); 73 | xhr.addEventListener("load", function (ev) { 74 | if (xhr.status == 200) { 75 | self.attachShaderSource(xhr.response, type); 76 | resolve(); 77 | } else { 78 | reject(xhr.statusText); 79 | } 80 | }, false); 81 | xhr.open("GET", url, true); 82 | xhr.send(null); 83 | }); 84 | } 85 | 86 | Program.prototype.attachShaderSourceFromTag = function(tagId, type) { 87 | var shaderTag = document.getElementById(tagId); 88 | if (!shaderTag) { 89 | console.error("Shader source tag not found:", tagId); 90 | return; 91 | } 92 | 93 | if (!type) { 94 | if (shaderTag.type == "x-shader/x-vertex") { 95 | type = this.gl.VERTEX_SHADER; 96 | } else if (shaderTag.type == "x-shader/x-fragment") { 97 | type = this.gl.FRAGMENT_SHADER; 98 | } else { 99 | console.error("Invalid Shader Type:", shaderTag.type); 100 | return; 101 | } 102 | } 103 | 104 | var src = ""; 105 | var k = shaderTag.firstChild; 106 | while (k) { 107 | if (k.nodeType == 3) { 108 | src += k.textContent; 109 | } 110 | k = k.nextSibling; 111 | } 112 | this.attachShaderSource(src, type); 113 | } 114 | 115 | Program.prototype.bindAttribLocation = function(attribLocationMap) { 116 | var gl = this.gl; 117 | 118 | if (attribLocationMap) { 119 | this.attrib = {}; 120 | for (var attribName in attribLocationMap) { 121 | gl.bindAttribLocation(this.program, attribLocationMap[attribName], attribName); 122 | this.attrib[attribName] = attribLocationMap[attribName]; 123 | } 124 | } 125 | } 126 | 127 | Program.prototype.transformFeedbackVaryings = function(varyings, type) { 128 | gl.transformFeedbackVaryings(this.program, varyings, type); 129 | } 130 | 131 | Program.prototype.link = function() { 132 | this.gl.linkProgram(this.program); 133 | } 134 | 135 | Program.prototype.use = function() { 136 | var gl = this.gl; 137 | 138 | // If this is the first time the program has been used do all the error checking and 139 | // attrib/uniform querying needed. 140 | if (this._firstUse) { 141 | if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { 142 | if (this._vertexShader && !gl.getShaderParameter(this._vertexShader, gl.COMPILE_STATUS)) { 143 | console.error("Vertex shader compile error:", gl.getShaderInfoLog(this._vertexShader)); 144 | } else if (this._fragmentShader && !gl.getShaderParameter(this._fragmentShader, gl.COMPILE_STATUS)) { 145 | console.error("Fragment shader compile error:", gl.getShaderInfoLog(this._fragmentShader)); 146 | } else { 147 | console.error("Program link error:", gl.getProgramInfoLog(this.program)); 148 | } 149 | gl.deleteProgram(this.program); 150 | this.program = null; 151 | } else { 152 | if (!this.attrib) { 153 | this.attrib = {}; 154 | var attribCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); 155 | for (var i = 0; i < attribCount; i++) { 156 | var attribInfo = gl.getActiveAttrib(this.program, i); 157 | this.attrib[attribInfo.name] = gl.getAttribLocation(this.program, attribInfo.name); 158 | } 159 | } 160 | 161 | this.uniform = {}; 162 | var uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS); 163 | var uniformName = ""; 164 | for (var i = 0; i < uniformCount; i++) { 165 | var uniformInfo = gl.getActiveUniform(this.program, i); 166 | uniformName = uniformInfo.name.replace("[0]", ""); 167 | this.uniform[uniformName] = gl.getUniformLocation(this.program, uniformName); 168 | } 169 | } 170 | gl.deleteShader(this._vertexShader); 171 | gl.deleteShader(this._fragmentShader); 172 | this._firstUse = false; 173 | } 174 | 175 | gl.useProgram(this.program); 176 | } 177 | 178 | return Program; 179 | })(); 180 | -------------------------------------------------------------------------------- /js/wglu/wglu-url.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015, Brandon Jones. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | Provides a simple way to get values from the query string if they're present 25 | and use a default value if not. Not strictly a "WebGL" utility, but I use it 26 | frequently enough for debugging that I wanted to include it here. 27 | 28 | Example: 29 | For the URL http://example.com/index.html?particleCount=1000 30 | 31 | WGLUUrl.getInt("particleCount", 100); // URL overrides, returns 1000 32 | WGLUUrl.getInt("particleSize", 10); // Not in URL, returns default of 10 33 | */ 34 | var WGLUUrl = (function() { 35 | 36 | "use strict"; 37 | 38 | var urlArgs = null; 39 | 40 | function ensureArgsCached() { 41 | if (!urlArgs) { 42 | urlArgs = {}; 43 | var query = window.location.search.substring(1); 44 | var vars = query.split("&"); 45 | for (var i = 0; i < vars.length; i++) { 46 | var pair = vars[i].split("="); 47 | urlArgs[pair[0].toLowerCase()] = unescape(pair[1]); 48 | } 49 | } 50 | } 51 | 52 | function getString(name, defaultValue) { 53 | ensureArgsCached(); 54 | var lcaseName = name.toLowerCase(); 55 | if (lcaseName in urlArgs) { 56 | return urlArgs[lcaseName]; 57 | } 58 | return defaultValue; 59 | } 60 | 61 | function getInt(name, defaultValue) { 62 | ensureArgsCached(); 63 | var lcaseName = name.toLowerCase(); 64 | if (lcaseName in urlArgs) { 65 | return parseInt(urlArgs[lcaseName], 10); 66 | } 67 | return defaultValue; 68 | } 69 | 70 | function getFloat(name, defaultValue) { 71 | ensureArgsCached(); 72 | var lcaseName = name.toLowerCase(); 73 | if (lcaseName in urlArgs) { 74 | return parseFloat(urlArgs[lcaseName]); 75 | } 76 | return defaultValue; 77 | } 78 | 79 | function getBool(name, defaultValue) { 80 | ensureArgsCached(); 81 | var lcaseName = name.toLowerCase(); 82 | if (lcaseName in urlArgs) { 83 | return parseInt(urlArgs[lcaseName], 10) != 0; 84 | } 85 | return defaultValue; 86 | } 87 | 88 | return { 89 | getString: getString, 90 | getInt: getInt, 91 | getFloat: getFloat, 92 | getBool: getBool 93 | }; 94 | })(); 95 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebXR Prompt Tests", 3 | "short_name": "WebXR Prompt Tests", 4 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 5 | "start_url": "./tests/permission-request.html?utm_source=web_app_manifest", 6 | "scope": "./", 7 | "display": "standalone", 8 | "theme_color": "#d30000", 9 | "background_color": "#ffffff", 10 | "icons": [ 11 | { 12 | "src": "./media/manifest/icon-192x192-regular.png", 13 | "sizes": "192x192", 14 | "type": "image/png", 15 | "purpose": "any" 16 | }, 17 | { 18 | "src": "./media/manifest/icon-192x192-maskable.png", 19 | "sizes": "192x192", 20 | "type": "image/png", 21 | "purpose": "maskable" 22 | }, 23 | { 24 | "src": "./media/manifest/icon-512x512-regular.png", 25 | "sizes": "512x512", 26 | "type": "image/png", 27 | "purpose": "any" 28 | }, 29 | { 30 | "src": "./media/manifest/icon-512x512-maskable.png", 31 | "sizes": "512x512", 32 | "type": "image/png", 33 | "purpose": "maskable" 34 | } 35 | ], 36 | "screenshots": [ 37 | { 38 | "src": "./media/manifest/screenshot-1.png", 39 | "type": "image/png", 40 | "sizes": "1080x1920", 41 | "form_factor": "narrow" 42 | }, 43 | { 44 | "src": "./media/manifest/screenshot-2.png", 45 | "type": "image/png", 46 | "sizes": "1080x1920", 47 | "form_factor": "narrow" 48 | }, 49 | { 50 | "src": "./media/manifest/screenshot-3.png", 51 | "type": "image/png", 52 | "sizes": "1080x1920", 53 | "form_factor": "narrow" 54 | }, 55 | { 56 | "src": "./media/manifest/screenshot-4.png", 57 | "type": "image/png", 58 | "sizes": "1920x1080", 59 | "form_factor": "wide" 60 | }, 61 | { 62 | "src": "./media/manifest/screenshot-5.png", 63 | "type": "image/png", 64 | "sizes": "1920x1080", 65 | "form_factor": "wide" 66 | }, 67 | { 68 | "src": "./media/manifest/screenshot-6.png", 69 | "type": "image/png", 70 | "sizes": "1920x1080", 71 | "form_factor": "wide" 72 | } 73 | ], 74 | "prefer_related_applications": false, 75 | "related_applications": [ 76 | { 77 | "platform": "webapp", 78 | "url": "./manifest.json" 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /media/gltf/camp/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | All models used in this scene are licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | "Campground" scene by Brandon Jones. 4 | 5 | "[Lowpoly Squirrel](https://poly.google.com/view/eGdXC-5V2wM)" model by Tipatat Chennavasin. 6 | 7 | "[Fox](https://poly.google.com/view/1DsQKS4G-aj)" model by Jake Blakeley. 8 | 9 | "[Tent](https://poly.google.com/view/2tvQrMLf_tP)" model by Jarlan Perez. 10 | 11 | "[Pine Tree](https://poly.google.com/view/2Qo-fmVKuSG)" and "[Pastel Plume Flowers](https://poly.google.com/view/eLVv17bTyB-)" models by Danny Bittman. 12 | 13 | 14 | -------------------------------------------------------------------------------- /media/gltf/camp/camp.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/camp/camp.bin -------------------------------------------------------------------------------- /media/gltf/camp/camp.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/camp/camp.blend -------------------------------------------------------------------------------- /media/gltf/cave/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | All models used in this scene are licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | "Cave" scene by Brandon Jones. 4 | 5 | "[Mushroom](https://poly.google.com/view/2DAaKHD48ZP)" and "[Bat](https://poly.google.com/view/fzJn9xTT-UO)" models by Poly by Google. 6 | 7 | "[Crystal Cluster](https://poly.google.com/view/6NY2BPvOISW)" model by Lee Mason. 8 | 9 | "[Tree-2](https://poly.google.com/view/cRipmFHCEVU)" model by Trackball. 10 | -------------------------------------------------------------------------------- /media/gltf/cave/cave.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/cave/cave.bin -------------------------------------------------------------------------------- /media/gltf/cave/cave.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/cave/cave.blend -------------------------------------------------------------------------------- /media/gltf/controller/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[controller](https://poly.google.com/view/cdts4fBVXe7)" model by Jack Brookes is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | Minor edits by Brandon Jones. 4 | -------------------------------------------------------------------------------- /media/gltf/controller/controller.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/controller/controller.bin -------------------------------------------------------------------------------- /media/gltf/controller/controller.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/controller/controller.blend -------------------------------------------------------------------------------- /media/gltf/cube-room/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[Cube Room](https://poly.google.com/view/1fahMeqZOw_)" model by Naomi Chen is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/gltf/cube-room/CubeRoom_BakedDiffuse_2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/cube-room/CubeRoom_BakedDiffuse_2048.png -------------------------------------------------------------------------------- /media/gltf/cube-room/cube-room.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/cube-room/cube-room.bin -------------------------------------------------------------------------------- /media/gltf/cube-room/cube-room.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/cube-room/cube-room.blend -------------------------------------------------------------------------------- /media/gltf/cube-room/cube-room.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 16092, 7 | "max" : [ 8 | 3361 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 3362, 19 | "max" : [ 20 | 5.0, 21 | 5.0, 22 | -0.0 23 | ], 24 | "min" : [ 25 | -5.0, 26 | -5.000080108642578, 27 | -7.0 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 3362, 35 | "max" : [ 36 | 1.0, 37 | 1.0, 38 | 1.0 39 | ], 40 | "min" : [ 41 | -1.0, 42 | -1.0, 43 | -1.0 44 | ], 45 | "type" : "VEC3" 46 | }, 47 | { 48 | "bufferView" : 3, 49 | "componentType" : 5126, 50 | "count" : 3362, 51 | "max" : [ 52 | 1.0, 53 | 1.0, 54 | 1.0, 55 | 1.0 56 | ], 57 | "min" : [ 58 | -1.0, 59 | -1.0, 60 | -1.0, 61 | -1.0 62 | ], 63 | "type" : "VEC4" 64 | }, 65 | { 66 | "bufferView" : 4, 67 | "componentType" : 5126, 68 | "count" : 3362, 69 | "max" : [ 70 | 0.9906550049781799, 71 | 0.9906550003215671 72 | ], 73 | "min" : [ 74 | 0.008588000200688839, 75 | 0.009518980979919434 76 | ], 77 | "type" : "VEC2" 78 | } 79 | ], 80 | "asset" : { 81 | "generator" : "Khronos Blender glTF 2.0 exporter", 82 | "version" : "2.0" 83 | }, 84 | "bufferViews" : [ 85 | { 86 | "buffer" : 0, 87 | "byteLength" : 32184, 88 | "byteOffset" : 0, 89 | "target" : 34963 90 | }, 91 | { 92 | "buffer" : 0, 93 | "byteLength" : 40344, 94 | "byteOffset" : 32184, 95 | "target" : 34962 96 | }, 97 | { 98 | "buffer" : 0, 99 | "byteLength" : 40344, 100 | "byteOffset" : 72528, 101 | "target" : 34962 102 | }, 103 | { 104 | "buffer" : 0, 105 | "byteLength" : 53792, 106 | "byteOffset" : 112872, 107 | "target" : 34962 108 | }, 109 | { 110 | "buffer" : 0, 111 | "byteLength" : 26896, 112 | "byteOffset" : 166664, 113 | "target" : 34962 114 | } 115 | ], 116 | "buffers" : [ 117 | { 118 | "byteLength" : 193560, 119 | "uri" : "cube-room.bin" 120 | } 121 | ], 122 | "images" : [ 123 | { 124 | "uri" : "CubeRoom_BakedDiffuse_2048.png" 125 | } 126 | ], 127 | "materials" : [ 128 | { 129 | "name" : "surfaceShader1SG", 130 | "pbrMetallicRoughness" : { 131 | "baseColorTexture" : { 132 | "index" : 0 133 | }, 134 | "metallicFactor" : 0.0 135 | } 136 | } 137 | ], 138 | "meshes" : [ 139 | { 140 | "name" : "CubeRoom_GEO", 141 | "primitives" : [ 142 | { 143 | "attributes" : { 144 | "NORMAL" : 2, 145 | "POSITION" : 1, 146 | "TANGENT" : 3, 147 | "TEXCOORD_0" : 4 148 | }, 149 | "indices" : 0, 150 | "material" : 0 151 | } 152 | ] 153 | } 154 | ], 155 | "nodes" : [ 156 | { 157 | "name" : "Camera", 158 | "rotation" : [ 159 | 0.483536034822464, 160 | 0.33687159419059753, 161 | -0.20870360732078552, 162 | 0.7804827094078064 163 | ], 164 | "translation" : [ 165 | 0.7468423843383789, 166 | 5.34366512298584, 167 | 4.142341613769531 168 | ] 169 | }, 170 | { 171 | "mesh" : 0, 172 | "name" : "CubeRoom_GEO", 173 | "rotation" : [ 174 | 0.7071068286895752, 175 | 0.0, 176 | -0.0, 177 | 0.7071067094802856 178 | ] 179 | }, 180 | { 181 | "name" : "Lamp", 182 | "rotation" : [ 183 | 0.16907575726509094, 184 | 0.7558802962303162, 185 | -0.27217137813568115, 186 | 0.570947527885437 187 | ], 188 | "scale" : [ 189 | 1.0, 190 | 1.0, 191 | 0.9999999403953552 192 | ], 193 | "translation" : [ 194 | 4.076245307922363, 195 | 5.903861999511719, 196 | -1.0054539442062378 197 | ] 198 | } 199 | ], 200 | "samplers" : [ 201 | {} 202 | ], 203 | "scene" : 0, 204 | "scenes" : [ 205 | { 206 | "name" : "Scene", 207 | "nodes" : [ 208 | 1, 209 | 2, 210 | 0 211 | ] 212 | } 213 | ], 214 | "textures" : [ 215 | { 216 | "sampler" : 0, 217 | "source" : 0 218 | } 219 | ] 220 | } 221 | -------------------------------------------------------------------------------- /media/gltf/garage/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | All models used in this scene are licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | "Garage" scene by Brandon Jones. 4 | 5 | "[Light Switch](https://poly.google.com/view/7yVvvpp-ftQ)", "[CRT Monitor](https://poly.google.com/view/8jVB0zIXKCv)", "[Fire Extinguisher](https://poly.google.com/view/71rR7PaeXNN)", "[Wooden Stool](https://poly.google.com/view/dn419U6vWYx)", "[Cork Board](https://poly.google.com/view/5Ql0Sdq6fmy)", "[Industrial Broom](https://poly.google.com/view/eH6PcHmWOdK)", "[Wall Shelf](https://poly.google.com/view/7544dJq1UVu)", and "[HTC Vive Headset](https://poly.google.com/view/bFZbl-mmCeD)" models by Jarlan Perez. 6 | 7 | "[Red Cooler](https://poly.google.com/view/f7q6m6IL8UI)" model by S. Paul Michael. 8 | 9 | "[Ladder](https://poly.google.com/view/32UszyQQLtJ)" model by Carwyn Pelley. 10 | 11 | "[Window](https://poly.google.com/view/dwBpM-aSA_t)" model by Justin Randall. 12 | 13 | "[SNES](https://poly.google.com/view/7yVvvpp-ftQ)" model by Gabriel Valdivia. 14 | -------------------------------------------------------------------------------- /media/gltf/garage/garage.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/garage/garage.bin -------------------------------------------------------------------------------- /media/gltf/garage/garage.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/garage/garage.blend -------------------------------------------------------------------------------- /media/gltf/headset/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[VR Headset](https://poly.google.com/view/bvd33G7Q66m)" model by Poly by Google is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/gltf/headset/headset.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/headset/headset.bin -------------------------------------------------------------------------------- /media/gltf/headset/headset.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/headset/headset.blend -------------------------------------------------------------------------------- /media/gltf/home-theater/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | All models used in this scene are licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | "[Home Theater](https://poly.google.com/view/6wJboiESaWR)" scene by Brandon Jones. 4 | 5 | "[Projector](https://poly.google.com/view/4oVHZbDvwV8)" and "[Window](https://poly.google.com/view/9FqbXmzB-CS)" models by Jonathan Granskog. 6 | 7 | "[Woonkamerkast](https://poly.google.com/view/70n22Lhssat)" model by Vincent van de Goolberg. 8 | 9 | "[Nice Door](https://poly.google.com/view/00xFHE4LR_6)" model by Wesley Thompson. 10 | -------------------------------------------------------------------------------- /media/gltf/home-theater/home-theater.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/home-theater/home-theater.bin -------------------------------------------------------------------------------- /media/gltf/home-theater/home-theater.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/home-theater/home-theater.blend -------------------------------------------------------------------------------- /media/gltf/reticle/reticle.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/reticle/reticle.bin -------------------------------------------------------------------------------- /media/gltf/reticle/reticle.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.0.5", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0, 12 | 1, 13 | 2 14 | ] 15 | } 16 | ], 17 | "nodes" : [ 18 | { 19 | "name" : "Light", 20 | "rotation" : [ 21 | 0.16907575726509094, 22 | 0.7558803558349609, 23 | -0.27217137813568115, 24 | 0.570947527885437 25 | ], 26 | "translation" : [ 27 | 4.076245307922363, 28 | 5.903861999511719, 29 | -1.0054539442062378 30 | ] 31 | }, 32 | { 33 | "name" : "Camera", 34 | "rotation" : [ 35 | 0.483536034822464, 36 | 0.33687159419059753, 37 | -0.20870360732078552, 38 | 0.7804827094078064 39 | ], 40 | "translation" : [ 41 | 7.358891487121582, 42 | 4.958309173583984, 43 | 6.925790786743164 44 | ] 45 | }, 46 | { 47 | "mesh" : 0, 48 | "name" : "Torus", 49 | "scale" : [ 50 | 0.7128448486328125, 51 | 0.7128448486328125, 52 | 0.7128448486328125 53 | ] 54 | } 55 | ], 56 | "materials" : [ 57 | { 58 | "doubleSided" : true, 59 | "emissiveFactor" : [ 60 | 0.27902206778526306, 61 | 0.5199682712554932, 62 | 1 63 | ], 64 | "name" : "Material.001", 65 | "pbrMetallicRoughness" : { 66 | "baseColorFactor" : [ 67 | 0.05667726323008537, 68 | 0.5679765939712524, 69 | 0.8000000715255737, 70 | 1 71 | ], 72 | "metallicFactor" : 0, 73 | "roughnessFactor" : 1 74 | } 75 | } 76 | ], 77 | "meshes" : [ 78 | { 79 | "name" : "Torus", 80 | "primitives" : [ 81 | { 82 | "attributes" : { 83 | "POSITION" : 0, 84 | "NORMAL" : 1, 85 | "TEXCOORD_0" : 2 86 | }, 87 | "indices" : 3, 88 | "material" : 0 89 | } 90 | ] 91 | } 92 | ], 93 | "accessors" : [ 94 | { 95 | "bufferView" : 0, 96 | "componentType" : 5126, 97 | "count" : 150, 98 | "max" : [ 99 | 0.1961740255355835, 100 | 0.020665571093559265, 101 | 0.1961740255355835 102 | ], 103 | "min" : [ 104 | -0.1961740255355835, 105 | 0.0008523659780621529, 106 | -0.1961740255355835 107 | ], 108 | "type" : "VEC3" 109 | }, 110 | { 111 | "bufferView" : 1, 112 | "componentType" : 5126, 113 | "count" : 150, 114 | "type" : "VEC3" 115 | }, 116 | { 117 | "bufferView" : 2, 118 | "componentType" : 5126, 119 | "count" : 150, 120 | "type" : "VEC2" 121 | }, 122 | { 123 | "bufferView" : 3, 124 | "componentType" : 5123, 125 | "count" : 282, 126 | "type" : "SCALAR" 127 | } 128 | ], 129 | "bufferViews" : [ 130 | { 131 | "buffer" : 0, 132 | "byteLength" : 1800, 133 | "byteOffset" : 0 134 | }, 135 | { 136 | "buffer" : 0, 137 | "byteLength" : 1800, 138 | "byteOffset" : 1800 139 | }, 140 | { 141 | "buffer" : 0, 142 | "byteLength" : 1200, 143 | "byteOffset" : 3600 144 | }, 145 | { 146 | "buffer" : 0, 147 | "byteLength" : 564, 148 | "byteOffset" : 4800 149 | } 150 | ], 151 | "buffers" : [ 152 | { 153 | "byteLength" : 5364, 154 | "uri" : "reticle.bin" 155 | } 156 | ] 157 | } 158 | -------------------------------------------------------------------------------- /media/gltf/space/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[Solar System](https://poly.google.com/view/8hnnpNiQMmy)" model by Jarlan Perez is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/gltf/space/space.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/space/space.bin -------------------------------------------------------------------------------- /media/gltf/space/space.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/space/space.blend -------------------------------------------------------------------------------- /media/gltf/sponza/10381718147657362067.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/10381718147657362067.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/10388182081421875623.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/10388182081421875623.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/11474523244911310074.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/11474523244911310074.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/11490520546946913238.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/11490520546946913238.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/11872827283454512094.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/11872827283454512094.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/11968150294050148237.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/11968150294050148237.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/1219024358953944284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/1219024358953944284.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/12501374198249454378.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/12501374198249454378.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/13196865903111448057.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/13196865903111448057.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/13824894030729245199.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/13824894030729245199.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/13982482287905699490.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/13982482287905699490.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/14118779221266351425.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/14118779221266351425.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/14170708867020035030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/14170708867020035030.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/14267839433702832875.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/14267839433702832875.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/14650633544276105767.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/14650633544276105767.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/15295713303328085182.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/15295713303328085182.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/15722799267630235092.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/15722799267630235092.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/16275776544635328252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/16275776544635328252.png -------------------------------------------------------------------------------- /media/gltf/sponza/16299174074766089871.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/16299174074766089871.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/16885566240357350108.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/16885566240357350108.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/17556969131407844942.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/17556969131407844942.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/17876391417123941155.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/17876391417123941155.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2051777328469649772.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2051777328469649772.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2185409758123873465.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2185409758123873465.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2299742237651021498.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2299742237651021498.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2374361008830720677.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2374361008830720677.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2411100444841994089.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2411100444841994089.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2775690330959970771.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2775690330959970771.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/2969916736137545357.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/2969916736137545357.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/332936164838540657.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/332936164838540657.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/3371964815757888145.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/3371964815757888145.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/3455394979645218238.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/3455394979645218238.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/3628158980083700836.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/3628158980083700836.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/3827035219084910048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/3827035219084910048.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4477655471536070370.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4477655471536070370.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4601176305987539675.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4601176305987539675.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/466164707995436622.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/466164707995436622.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4675343432951571524.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4675343432951571524.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4871783166746854860.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4871783166746854860.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4910669866631290573.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4910669866631290573.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/4975155472559461469.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/4975155472559461469.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/5061699253647017043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/5061699253647017043.png -------------------------------------------------------------------------------- /media/gltf/sponza/5792855332885324923.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/5792855332885324923.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/5823059166183034438.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/5823059166183034438.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/6047387724914829168.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/6047387724914829168.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/6151467286084645207.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/6151467286084645207.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/6593109234861095314.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/6593109234861095314.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/6667038893015345571.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/6667038893015345571.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/6772804448157695701.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/6772804448157695701.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/7056944414013900257.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/7056944414013900257.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/715093869573992647.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/715093869573992647.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/7268504077753552595.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/7268504077753552595.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/7441062115984513793.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/7441062115984513793.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/755318871556304029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/755318871556304029.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/759203620573749278.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/759203620573749278.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/7645212358685992005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/7645212358685992005.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/7815564343179553343.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/7815564343179553343.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8006627369776289000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8006627369776289000.png -------------------------------------------------------------------------------- /media/gltf/sponza/8051790464816141987.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8051790464816141987.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8114461559286000061.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8114461559286000061.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8481240838833932244.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8481240838833932244.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8503262930880235456.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8503262930880235456.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8747919177698443163.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8747919177698443163.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8750083169368950601.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8750083169368950601.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8773302468495022225.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8773302468495022225.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/8783994986360286082.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/8783994986360286082.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/9288698199695299068.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/9288698199695299068.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/9916269861720640319.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/9916269861720640319.jpg -------------------------------------------------------------------------------- /media/gltf/sponza/README.md: -------------------------------------------------------------------------------- 1 | # Sponza 2 | 3 | ## Screenshot 4 | 5 | Lights are shown here, they are not part of the model. 6 | 7 | ![screenshot](screenshot/screenshot.jpg) 8 | 9 | ## Model notes 10 | 11 | Tangents have been computed using MikkTSpace, as the original OBJ model did not have them. 12 | I have manually inspected the normals, and it looks correct to me. 13 | Be aware that W is -1.0 for most of the tangent signs, you will need to handle tangent W for correct results. 14 | 15 | 16 | ## Sources 17 | 18 | ### http://www.crytek.com/cryengine/cryengine3/downloads 19 | - http://www.crytek.com/download/sponza\_obj.rar 20 | - http://www.crytek.com/download/sponza\_textures.rar 21 | 22 | ### http://www.alexandre-pestana.com/pbr-textures-sponza/ 23 | - http://www.alexandre-pestana.com/downloads/SponzaPBR\_Textures.rar 24 | 25 | I needed to resize some of the alpha mask textures to the 1024x1024 resolution used by the new texture pack, 26 | and merge in diffuse with alpha. 27 | I also repacked the separate metallic/roughness textures into the glTF layout (G - roughness, B - metallic). 28 | The images are also re-encoded as PNG instead of TGA. 29 | All the materials also had a constant diffuse factor of about 0.58. I assume it was supposed to be there, so I kept it. 30 | I also ran the vertices and indices through a mesh optimizer. 31 | 32 | ## Licensing notes 33 | 34 | Taken from copyright.txt in SponzaPBR\_Textures.rar 35 | 36 | ``` 37 | PBR textures for the Sponza model. 38 | For more informations: www.alexandre-pestana.com 39 | 40 | 41 | Original copyright: 42 | 43 | July 14, 2011 Morgan McGuire modified the model from Crytek's OBJ 44 | export to correct some small errors. He computed bump maps from the 45 | normal maps using normal2bump.cpp (since 47 | MTL files expect height bumps, not normals), put the "mask" textures 48 | into the alpha channel of the associated diffuse texture, cleaned up 49 | noise in the masks, created the missing gi_flag.tga texture, and 50 | removed the long untextured banner floating in the middle of the 51 | atrium that appears in the file but in none of the published images of 52 | the model. The banner is in banner.obj. 53 | 54 | 55 | 56 | http://www.crytek.com/cryengine/cryengine3/downloads 57 | 58 | 59 | Sponza Model 60 | August 19, 2010 61 | The Atrium Sponza Palace, Dubrovnik, is an elegant and improved model created by Frank Meinl. The original Sponza model was created by Marko Dabrovic in early 2002. Over the years, the Sponza Atrium scene has become one of the most popular 3D scenes for testing global illumination and radiosity due to it's specific architectural structure which is particularly complex for global illumination light. 62 | 63 | However, nowadays it is considered as a simple model, thus it was decided to crate a new model with highly improved appearance and scene complexity. It is donated to the public for radiosity and is represented in several different formats (3ds, Obj) for use with various commercial 3D applications and renderers. 64 | 65 | 66 | Screenshot from the I3D paper 67 | http://crytek.com/sites/default/files/20100301_lpv.pdf 68 | ``` 69 | -------------------------------------------------------------------------------- /media/gltf/sponza/Sponza.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/Sponza.bin -------------------------------------------------------------------------------- /media/gltf/sponza/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sponza/white.png -------------------------------------------------------------------------------- /media/gltf/stereo/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[Stereo](https://poly.google.com/view/5gXgHgP-dmh)" model by Poly by Google is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/gltf/stereo/stereo.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/stereo/stereo.bin -------------------------------------------------------------------------------- /media/gltf/stereo/stereo.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/stereo/stereo.blend -------------------------------------------------------------------------------- /media/gltf/sunflower/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | All models used in this application are licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | 3 | - "[Sunflower](https://poly.google.com/view/ce4GXw3VYE5)" by Poly by Google. -------------------------------------------------------------------------------- /media/gltf/sunflower/PUSHILIN_sunflower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sunflower/PUSHILIN_sunflower.png -------------------------------------------------------------------------------- /media/gltf/sunflower/sunflower.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sunflower/sunflower.bin -------------------------------------------------------------------------------- /media/gltf/sunflower/sunflower.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/gltf/sunflower/sunflower.blend -------------------------------------------------------------------------------- /media/gltf/sunflower/sunflower.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.2.75", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0, 12 | 1, 13 | 2, 14 | 3, 15 | 4 16 | ] 17 | } 18 | ], 19 | "nodes" : [ 20 | { 21 | "name" : "Camera.001", 22 | "rotation" : [ 23 | 0.483536034822464, 24 | 0.33687159419059753, 25 | -0.20870360732078552, 26 | 0.7804827094078064 27 | ], 28 | "translation" : [ 29 | 7.481131553649902, 30 | 5.34366512298584, 31 | 6.5076398849487305 32 | ] 33 | }, 34 | { 35 | "name" : "Lamp", 36 | "rotation" : [ 37 | 0.16907551884651184, 38 | 0.7558804750442505, 39 | -0.2721710503101349, 40 | 0.5709475874900818 41 | ], 42 | "translation" : [ 43 | 0, 44 | 1.600000023841858, 45 | 0 46 | ] 47 | }, 48 | { 49 | "mesh" : 0, 50 | "name" : "sunflower", 51 | "rotation" : [ 52 | 0.7071068286895752, 53 | 0, 54 | 0, 55 | 0.7071067094802856 56 | ], 57 | "scale" : [ 58 | 0.30000001192092896, 59 | 0.30000001192092896, 60 | 0.30000001192092896 61 | ], 62 | "translation" : [ 63 | 0, 64 | 0.39152565598487854, 65 | 0 66 | ] 67 | }, 68 | { 69 | "name" : "Light", 70 | "rotation" : [ 71 | 0.16907575726509094, 72 | 0.7558803558349609, 73 | -0.27217137813568115, 74 | 0.570947527885437 75 | ], 76 | "translation" : [ 77 | 4.076245307922363, 78 | 5.903861999511719, 79 | -1.0054539442062378 80 | ] 81 | }, 82 | { 83 | "name" : "Camera", 84 | "rotation" : [ 85 | 0.483536034822464, 86 | 0.33687159419059753, 87 | -0.20870360732078552, 88 | 0.7804827094078064 89 | ], 90 | "translation" : [ 91 | 7.358891487121582, 92 | 4.958309173583984, 93 | 6.925790786743164 94 | ] 95 | } 96 | ], 97 | "materials" : [ 98 | { 99 | "emissiveFactor" : [ 100 | 0, 101 | 0, 102 | 0 103 | ], 104 | "name" : "None", 105 | "pbrMetallicRoughness" : { 106 | "baseColorTexture" : { 107 | "index" : 0, 108 | "texCoord" : 0 109 | }, 110 | "metallicFactor" : 0, 111 | "roughnessFactor" : 1 112 | } 113 | } 114 | ], 115 | "meshes" : [ 116 | { 117 | "name" : "sunflower", 118 | "primitives" : [ 119 | { 120 | "attributes" : { 121 | "POSITION" : 0, 122 | "NORMAL" : 1, 123 | "TEXCOORD_0" : 2 124 | }, 125 | "indices" : 3, 126 | "material" : 0 127 | } 128 | ] 129 | } 130 | ], 131 | "textures" : [ 132 | { 133 | "source" : 0 134 | } 135 | ], 136 | "images" : [ 137 | { 138 | "mimeType" : "image/png", 139 | "name" : "PUSHILIN_sunflower", 140 | "uri" : "PUSHILIN_sunflower.png" 141 | } 142 | ], 143 | "accessors" : [ 144 | { 145 | "bufferView" : 0, 146 | "componentType" : 5126, 147 | "count" : 684, 148 | "max" : [ 149 | 0.6084539890289307, 150 | 0.45397499203681946, 151 | 1.3026560544967651 152 | ], 153 | "min" : [ 154 | -0.6065940260887146, 155 | -0.4599300026893616, 156 | -0.6320639848709106 157 | ], 158 | "type" : "VEC3" 159 | }, 160 | { 161 | "bufferView" : 1, 162 | "componentType" : 5126, 163 | "count" : 684, 164 | "type" : "VEC3" 165 | }, 166 | { 167 | "bufferView" : 2, 168 | "componentType" : 5126, 169 | "count" : 684, 170 | "type" : "VEC2" 171 | }, 172 | { 173 | "bufferView" : 3, 174 | "componentType" : 5123, 175 | "count" : 1020, 176 | "type" : "SCALAR" 177 | } 178 | ], 179 | "bufferViews" : [ 180 | { 181 | "buffer" : 0, 182 | "byteLength" : 8208, 183 | "byteOffset" : 0 184 | }, 185 | { 186 | "buffer" : 0, 187 | "byteLength" : 8208, 188 | "byteOffset" : 8208 189 | }, 190 | { 191 | "buffer" : 0, 192 | "byteLength" : 5472, 193 | "byteOffset" : 16416 194 | }, 195 | { 196 | "buffer" : 0, 197 | "byteLength" : 2040, 198 | "byteOffset" : 21888 199 | } 200 | ], 201 | "buffers" : [ 202 | { 203 | "byteLength" : 23928, 204 | "uri" : "sunflower.bin" 205 | } 206 | ] 207 | } 208 | -------------------------------------------------------------------------------- /media/logo/webxr-logo-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 23 | 29 | 30 | 38 | 42 | 46 | 47 | 55 | 59 | 63 | 64 | 72 | 76 | 80 | 81 | 89 | 93 | 97 | 98 | 100 | 106 | 107 | 108 | 126 | 128 | 129 | 131 | image/svg+xml 132 | 134 | 135 | 136 | 137 | 138 | 143 | 150 | 155 | 158 | 161 | 166 | 167 | 170 | 175 | 176 | 179 | 184 | 185 | 188 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /media/manifest/icon-192x192-maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/icon-192x192-maskable.png -------------------------------------------------------------------------------- /media/manifest/icon-192x192-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/icon-192x192-regular.png -------------------------------------------------------------------------------- /media/manifest/icon-512x512-maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/icon-512x512-maskable.png -------------------------------------------------------------------------------- /media/manifest/icon-512x512-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/icon-512x512-regular.png -------------------------------------------------------------------------------- /media/manifest/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-1.png -------------------------------------------------------------------------------- /media/manifest/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-2.png -------------------------------------------------------------------------------- /media/manifest/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-3.png -------------------------------------------------------------------------------- /media/manifest/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-4.png -------------------------------------------------------------------------------- /media/manifest/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-5.png -------------------------------------------------------------------------------- /media/manifest/screenshot-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/manifest/screenshot-6.png -------------------------------------------------------------------------------- /media/sound/drums.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/sound/drums.ogg -------------------------------------------------------------------------------- /media/sound/guitar.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/sound/guitar.ogg -------------------------------------------------------------------------------- /media/sound/perc.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/sound/perc.ogg -------------------------------------------------------------------------------- /media/textures/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[Le panorama de la Voie Lactée](http://www.eso.org/public/images/eso0932a/)" image by European Southern Observatory is licensed under [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/textures/annotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/annotation.png -------------------------------------------------------------------------------- /media/textures/arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/arrow-up.png -------------------------------------------------------------------------------- /media/textures/backward-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/backward-button.png -------------------------------------------------------------------------------- /media/textures/bluetooth-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/bluetooth-button.png -------------------------------------------------------------------------------- /media/textures/camera-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/camera-button.png -------------------------------------------------------------------------------- /media/textures/check-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/check-button.png -------------------------------------------------------------------------------- /media/textures/chess-pano-4k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/chess-pano-4k.png -------------------------------------------------------------------------------- /media/textures/chess-pano-4k180.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/chess-pano-4k180.jpg -------------------------------------------------------------------------------- /media/textures/cube-sea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/cube-sea.png -------------------------------------------------------------------------------- /media/textures/cube-sea.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/cube-sea.psd -------------------------------------------------------------------------------- /media/textures/eilenriede-park-2k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/eilenriede-park-2k.png -------------------------------------------------------------------------------- /media/textures/forward-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/forward-button.png -------------------------------------------------------------------------------- /media/textures/hid-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/hid-button.png -------------------------------------------------------------------------------- /media/textures/info-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/info-button.png -------------------------------------------------------------------------------- /media/textures/install-pwa-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/install-pwa-button.png -------------------------------------------------------------------------------- /media/textures/location-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/location-button.png -------------------------------------------------------------------------------- /media/textures/microphone-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/microphone-button.png -------------------------------------------------------------------------------- /media/textures/midi-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/midi-button.png -------------------------------------------------------------------------------- /media/textures/milky-way-2k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/milky-way-2k.png -------------------------------------------------------------------------------- /media/textures/milky-way-4k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/milky-way-4k.png -------------------------------------------------------------------------------- /media/textures/mono-equirect180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/mono-equirect180.png -------------------------------------------------------------------------------- /media/textures/mono_cube_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/mono_cube_map.png -------------------------------------------------------------------------------- /media/textures/mono_equirect_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/mono_equirect_test.png -------------------------------------------------------------------------------- /media/textures/notifications-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/notifications-button.png -------------------------------------------------------------------------------- /media/textures/pause-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/pause-button.png -------------------------------------------------------------------------------- /media/textures/play-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/play-button.png -------------------------------------------------------------------------------- /media/textures/rect-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/rect-mono.png -------------------------------------------------------------------------------- /media/textures/rect-stereo-top-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/rect-stereo-top-down.png -------------------------------------------------------------------------------- /media/textures/screenshare-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/screenshare-button.png -------------------------------------------------------------------------------- /media/textures/serial-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/serial-button.png -------------------------------------------------------------------------------- /media/textures/show-notification-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/show-notification-button.png -------------------------------------------------------------------------------- /media/textures/stereo_cube_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/stereo_cube_map.png -------------------------------------------------------------------------------- /media/textures/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/transparent.png -------------------------------------------------------------------------------- /media/textures/usb-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/usb-button.png -------------------------------------------------------------------------------- /media/textures/x-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/textures/x-button.png -------------------------------------------------------------------------------- /media/thumbnails/cave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/thumbnails/cave.png -------------------------------------------------------------------------------- /media/thumbnails/cube-room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/thumbnails/cube-room.png -------------------------------------------------------------------------------- /media/thumbnails/space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/thumbnails/space.png -------------------------------------------------------------------------------- /media/video/ATTRIBUTION.md: -------------------------------------------------------------------------------- 1 | "[Big Buck Bunny](https://peach.blender.org/about/)" by the [Blender Foundation](www.blender.org) is licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/legalcode). 2 | -------------------------------------------------------------------------------- /media/video/bbb-sunflower-540p2-1min.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immersive-web/webxr-samples/bc8170a66d39d13666c3763f2c05988336a4da85/media/video/bbb-sunflower-540p2-1min.webm -------------------------------------------------------------------------------- /proposals/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 81 | 82 | 85 | WebXR - Proposals 86 | 87 | 88 | 89 |
90 |
91 | 99 | 100 |

101 | 102 | WebXR Logo 103 | 104 |

105 |

Proposals

106 |
107 | 108 |
109 |

These pages test features which have been proposed and are in development but have 110 | not yet been adopted into the core WebXR Device API. They may have varying support 111 | across WebXR-compatible browsers, and the APIs presented here are assumed to be 112 | in flux.
113 | 114 | 176 |

177 | 178 |
179 | 180 |

View source on GitHub

181 | 182 | 184 |
185 | 186 | 187 | -------------------------------------------------------------------------------- /service-worker.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('notificationclick', (event) => { 2 | event.notification.close(); 3 | 4 | if (!event.action) { 5 | event.waitUntil(clients.openWindow(event.notification.data.url)); 6 | return; 7 | } 8 | 9 | console.log('Action ID:', event.action); 10 | console.log('Reply text:', event.reply); 11 | }); 12 | -------------------------------------------------------------------------------- /shaders/depth-api-cpu.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | in float vDepthDistance; 4 | 5 | const highp float kMaxDepth = 8.0; // In meters. 6 | const float kInvalidDepthThreshold = 0.01; 7 | 8 | vec3 TurboColormap(in float x); 9 | 10 | // Returns a color corresponding to the depth passed in. Colors range from red 11 | // to green to blue, where red is closest and blue is farthest. 12 | // 13 | // Uses Turbo color mapping : 14 | // https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html 15 | vec3 DepthGetColorVisualization(in float x) { 16 | return step(kInvalidDepthThreshold, x) * TurboColormap(x); 17 | } 18 | 19 | void main(void) { 20 | highp float normalized_depth = clamp(vDepthDistance / 8.0, 0.0, 1.0); 21 | gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75); 22 | } 23 | 24 | // Insert turbo.glsl here. 25 | -------------------------------------------------------------------------------- /shaders/depth-api-gpu.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | uniform sampler2D uDepthTexture; 4 | uniform mat4 uUvTransform; 5 | 6 | in vec2 vTexCoord; 7 | 8 | float DepthGetMillimeters(in sampler2D depth_texture, in vec2 depth_uv) { 9 | // Depth is packed into the luminance and alpha components of its texture. 10 | // The texture is a normalized format, storing millimeters. 11 | vec2 packedDepthAndVisibility = texture(depth_texture, depth_uv).ra; 12 | return dot(packedDepthAndVisibility, vec2(255.0, 256.0 * 255.0)); 13 | } 14 | 15 | const highp float kMaxDepth = 8000.0; // In millimeters. 16 | const float kInvalidDepthThreshold = 0.01; 17 | 18 | vec3 TurboColormap(in float x); 19 | 20 | // Returns a color corresponding to the depth passed in. Colors range from red 21 | // to green to blue, where red is closest and blue is farthest. 22 | // 23 | // Uses Turbo color mapping: 24 | // https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html 25 | vec3 DepthGetColorVisualization(in float x) { 26 | return step(kInvalidDepthThreshold, x) * TurboColormap(x); 27 | } 28 | 29 | void main(void) { 30 | vec2 texCoord = (uUvTransform * vec4(vTexCoord.xy, 0, 1)).xy; 31 | 32 | highp float normalized_depth = clamp( 33 | DepthGetMillimeters(uDepthTexture, texCoord) / kMaxDepth, 0.0, 1.0); 34 | gl_FragColor = vec4(DepthGetColorVisualization(normalized_depth), 0.75); 35 | } 36 | 37 | // Insert turbo.glsl here. 38 | -------------------------------------------------------------------------------- /shaders/turbo.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Google Inc. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | // Taken from: 17 | // third_party/arcore-android-sdk/src/samples/augmented_image_c/app/src/main/assets/shaders/background_show_depth_color_visualization.frag 18 | // & modified. 19 | vec3 TurboColormap(in float x) { 20 | const vec4 kRedVec4 = vec4(0.55305649, 3.00913185, -5.46192616, -11.11819092); 21 | const vec4 kGreenVec4 = vec4(0.16207513, 0.17712472, 15.24091500, -36.50657960); 22 | const vec4 kBlueVec4 = vec4(-0.05195877, 5.18000081, -30.94853351, 81.96403246); 23 | const vec2 kRedVec2 = vec2(27.81927491, -14.87899417); 24 | const vec2 kGreenVec2 = vec2(25.95549545, -5.02738237); 25 | const vec2 kBlueVec2 = vec2(-86.53476570, 30.23299484); 26 | 27 | // Adjusts color space via 6 degree poly interpolation to avoid pure red. 28 | x = clamp(x * 0.9 + 0.03, 0.0, 1.0); 29 | vec4 v4 = vec4( 1.0, x, x * x, x * x * x); 30 | vec2 v2 = v4.zw * v4.z; 31 | return vec3( 32 | dot(v4, kRedVec4) + dot(v2, kRedVec2), 33 | dot(v4, kGreenVec4) + dot(v2, kGreenVec2), 34 | dot(v4, kBlueVec4) + dot(v2, kBlueVec2) 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /tests/exit-button.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Exit Button 33 | 34 | 35 |
36 |
37 | Exit Button 38 |

39 | A VR scene that has an in-world exit button that ends the session. 40 | Back 41 |

42 |
43 |
44 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /tests/offscreen-canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Offscreen Canvas 33 | 34 | 35 | 36 | 37 |
38 |
39 | Offscreen Canvas 40 |

41 | A simple test to ensure that the browser's WebXR implementation works with a WebGL context created 42 | from an OffscreenCanvas. 43 | Back 44 |

45 |
46 |
47 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/pointer-painter.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Pointer Painter 33 | 34 | 35 |
36 |
37 | Pointer painter 38 |

39 | Leaves behind a trail of pointers and cursors on select events. Useful 40 | for testing screen input. 41 | Back 42 |

43 |
44 |
45 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /tests/sponza.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Sponza 33 | 34 | 35 |
36 |
37 | Sponza 38 |

39 | Loads a larger scene than other samples for performance testing. 40 | Back 41 |

42 | use multiview 43 |
44 |
45 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 109735 3 | , "contacts": ["himorin"] 4 | , "repo-type": "tool" 5 | } 6 | --------------------------------------------------------------------------------