Skip to content

Commit

Permalink
AR autogenerate USDZ (google#2374)
Browse files Browse the repository at this point in the history
* automatic ios-src generation

* added docs

* AR autogenerate USDZ

* separate field to store generated USDZ url

* remove caching for now

* Removing property. Clean up previous blob url.

* genrateUsdz boolean bug fix

Co-authored-by: Emmett Lalish <[email protected]>
  • Loading branch information
kolodi and elalish authored May 11, 2021
1 parent 202f5a3 commit 2baee76
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 20 deletions.
75 changes: 68 additions & 7 deletions packages/model-viewer/src/features/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@

import {property} from 'lit-element';
import {Event as ThreeEvent} from 'three';
import {USDZExporter} from 'three/examples/jsm/exporters/USDZExporter';

import {IS_AR_QUICKLOOK_CANDIDATE, IS_SCENEVIEWER_CANDIDATE, IS_WEBXR_AR_CANDIDATE} from '../constants.js';
import ModelViewerElementBase, {$needsRender, $renderer, $scene, $shouldAttemptPreload, $updateSource} from '../model-viewer-base.js';
import ModelViewerElementBase, {$needsRender, $progressTracker, $renderer, $scene, $shouldAttemptPreload, $updateSource} from '../model-viewer-base.js';
import {enumerationDeserializer} from '../styles/deserializers.js';
import {ARStatus, ARTracking} from '../three-components/ARRenderer.js';
import {Constructor, waitForEvent} from '../utilities.js';
Expand All @@ -31,7 +32,7 @@ export type ARMode = 'quick-look'|'scene-viewer'|'webxr'|'none';
const deserializeARModes = enumerationDeserializer<ARMode>(
['quick-look', 'scene-viewer', 'webxr', 'none']);

const DEFAULT_AR_MODES = 'webxr scene-viewer quick-look';
const DEFAULT_AR_MODES = 'webxr scene-viewer';

const ARMode: {[index: string]: ARMode} = {
QUICK_LOOK: 'quick-look',
Expand All @@ -57,6 +58,7 @@ const $arMode = Symbol('arMode');
const $arModes = Symbol('arModes');
const $arAnchor = Symbol('arAnchor');
const $preload = Symbol('preload');
const $generatedIosUrl = Symbol('generatedIosUrl');

const $onARButtonContainerClick = Symbol('onARButtonContainerClick');
const $onARStatus = Symbol('onARStatus');
Expand Down Expand Up @@ -105,6 +107,8 @@ export const ARMixin = <T extends Constructor<ModelViewerElementBase>>(
protected[$arMode]: ARMode = ARMode.NONE;
protected[$preload] = false;

private[$generatedIosUrl]: string|null = null;

private[$onARButtonContainerClick] = (event: Event) => {
event.preventDefault();
this.activateAR();
Expand Down Expand Up @@ -226,13 +230,17 @@ configuration or device capabilities');
!isSceneViewerBlocked) {
this[$arMode] = ARMode.SCENE_VIEWER;
break;
} else if (
value === 'quick-look' && !!this.iosSrc &&
IS_AR_QUICKLOOK_CANDIDATE) {
} else if (value === 'quick-look' && IS_AR_QUICKLOOK_CANDIDATE) {
this[$arMode] = ARMode.QUICK_LOOK;
break;
}
}

// The presence of ios-src overrides the absence of quick-look ar-mode.
if (!this.canActivateAR && this.iosSrc != null &&
IS_AR_QUICKLOOK_CANDIDATE) {
this[$arMode] = ARMode.QUICK_LOOK;
}
}

if (this.canActivateAR) {
Expand Down Expand Up @@ -352,23 +360,76 @@ configuration or device capabilities');
* Takes a URL to a USDZ file and sets the appropriate fields so that Safari
* iOS can intent to their AR Quick Look.
*/
[$openIOSARQuickLook]() {
const modelUrl = new URL(this.iosSrc!, self.location.toString());
async[$openIOSARQuickLook]() {
const generateUsdz = !this.iosSrc;

this[$arButtonContainer].classList.remove('enabled');

const modelUrl = new URL(
generateUsdz ? await this.prepareUSDZ() : this.iosSrc!,
self.location.toString());

this[$arButtonContainer].classList.add('enabled');

if (this.arScale === 'fixed') {
if (modelUrl.hash) {
modelUrl.hash += '&';
}
modelUrl.hash += 'allowsContentScaling=0';
}


const anchor = this[$arAnchor];
anchor.setAttribute('rel', 'ar');
const img = document.createElement('img');
anchor.appendChild(img);
anchor.setAttribute('href', modelUrl.toString());
if (generateUsdz) {
anchor.setAttribute('download', 'model.usdz');
}
console.log('Attempting to present in AR with Quick Look...');
anchor.click();
anchor.removeChild(img);
}

async prepareUSDZ(): Promise<string> {
const updateSourceProgress = this[$progressTracker].beginActivity();

if (this[$generatedIosUrl] != null) {
URL.revokeObjectURL(this[$generatedIosUrl]!);
this[$generatedIosUrl] = null;
}

const scene = this[$scene];

const shadow = scene.shadow;
let visible = false;

// Remove shadow from export
if (shadow != null) {
visible = shadow.visible;
shadow.visible = false;
}

updateSourceProgress(0.2);

const exporter = new USDZExporter();
const arraybuffer = await exporter.parse(scene.modelContainer);
const blob = new Blob([arraybuffer], {
type: 'application/octet-stream',
});

const url = URL.createObjectURL(blob);
this[$generatedIosUrl] = url;

updateSourceProgress(1);

if (shadow != null) {
shadow.visible = visible;
}

return url;
}
}

return ARModelViewerElement;
Expand Down
8 changes: 4 additions & 4 deletions packages/modelviewer.dev/data/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,13 @@
{
"name": "ar-modes",
"htmlName": "arModes",
"description": "A prioritized list of the types of AR experiences to enable. Allowed values are \"webxr\", to launch the AR experience in the browser, \"scene-viewer\", to launch the <a href=\"https://developers.google.com/ar/develop/java/scene-viewer\">Scene Viewer</a> app, \"quick-look\", to launch the iOS Quick Look app. You can specify any number of modes separated by whitespace.",
"description": "A prioritized list of the types of AR experiences to enable. Allowed values are \"webxr\", to launch the AR experience in the browser, \"scene-viewer\", to launch the <a href=\"https://developers.google.com/ar/develop/java/scene-viewer\">Scene Viewer</a> app, \"quick-look\", to launch the iOS Quick Look app. You can specify any number of modes separated by whitespace. Note that the presence of an ios-src will enable quick-look by itself; specifying quick-look here allows us to generate a USDZ on the fly rather than downloading a separate ios-src file.",
"links": [
"<a href=\"../examples/augmentedreality/\">Related examples</a>"
],
"default": {
"default": "webxr scene-viewer quick-look",
"options": "prioritized list of allowed modes"
"default": "webxr scene-viewer",
"options": "prioritized list possible AR modes: webxr, scene-viewer, and quick-look"
}
},
{
Expand Down Expand Up @@ -279,7 +279,7 @@
{
"name": "ios-src",
"htmlName": "iosSrc",
"description": "The url to a <a href=\"https://graphics.pixar.com/usd/docs/Usdz-File-Format-Specification.html\">USDZ</a> model which will be used on <a href=\"https://www.apple.com/ios/augmented-reality/\">supported iOS 12+ devices</a> via <a href=\"https://developer.apple.com/videos/play/wwdc2018/603/\">AR Quick Look</a> on Safari.",
"description": "The url to a <a href=\"https://graphics.pixar.com/usd/docs/Usdz-File-Format-Specification.html\">USDZ</a> model which will be used on <a href=\"https://www.apple.com/ios/augmented-reality/\">supported iOS 12+ devices</a> via <a href=\"https://developer.apple.com/videos/play/wwdc2018/603/\">AR Quick Look</a> on Safari. The presence of this attribute will automatically enable the quick-look ar-mode, however it is no longer necessary. If instead the quick-look ar-mode is specified and ios-src is not specified, then we will generate a USDZ on the fly when the AR button is pressed. This means modifications via the scene-graph API will now be reflected in Quick Look. Hoowever, USDZ generation is not perfect, for instance animations are not yet supported, so in some cases supplying ios-src may give better results.",
"links": [
"<a href=\"../examples/augmentedreality/#ar\"><span class='attribute'>ios-src</span> example</a>"
]
Expand Down
6 changes: 3 additions & 3 deletions packages/modelviewer.dev/examples/augmentedreality/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ <h4>Customize a WebXR Augmented Reality session with HTML, CSS, and JS in Chrome
</div>
<example-snippet stamp-to="webXR" highlight-as="html">
<template>
<model-viewer src="../../assets/ShopifyModels/Chair.glb" poster="../../assets/ShopifyModels/Chair.png" shadow-intensity="1" ar camera-controls alt="A 3D model carousel">
<model-viewer src="../../assets/ShopifyModels/Chair.glb" poster="../../assets/ShopifyModels/Chair.png" shadow-intensity="1" ar ar-modes="webxr scene-viewer quick-look" camera-controls alt="A 3D model carousel">

<button slot="ar-button" id="ar-button">
View in your space
Expand Down Expand Up @@ -345,7 +345,7 @@ <h4>This demonstrates the <code>ar-placement</code> attribute, which defaults to
</div>
<example-snippet stamp-to="wall" highlight-as="html">
<template>
<model-viewer src="../../assets/boom_2_.glb" ar ar-placement="wall" camera-controls alt="A 3D model of some wall art"></model-viewer>
<model-viewer src="../../assets/boom_2_.glb" ar ar-placement="wall" ar-modes="webxr scene-viewer quick-look" camera-controls alt="A 3D model of some wall art"></model-viewer>
</template>
</example-snippet>

Expand Down Expand Up @@ -394,7 +394,7 @@ <h4></h4>
<template>
<div class="demo" style="background: linear-gradient(#ffffff, #ada996); overflow-x: hidden;">
<span style="position: absolute; text-align: center; font-size: 100px; top:50%;">Background</span>
<model-viewer camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/AlphaBlendModeTest/glTF-Binary/AlphaBlendModeTest.glb" alt="A 3D transparency test" style="background-color: unset;"></model-viewer>
<model-viewer camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/AlphaBlendModeTest/glTF-Binary/AlphaBlendModeTest.glb" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D transparency test" style="background-color: unset;"></model-viewer>
</div>
</template>
</example-snippet>
Expand Down
12 changes: 6 additions & 6 deletions packages/modelviewer.dev/examples/scenegraph/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ <h2 class="demo-title">Swap Model Variants</h2>
</div>
<example-snippet stamp-to="variants" highlight-as="html">
<template>
<model-viewer id="chair" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/SheenChair/glTF-Binary/SheenChair.glb" alt="A 3D model of a chair">
<model-viewer id="chair" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/SheenChair/glTF-Binary/SheenChair.glb" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D model of a chair">
<div class="controls">
<div>Variant: <select id="variant"></select></div>
</div>
Expand Down Expand Up @@ -124,7 +124,7 @@ <h2 class="demo-title">Model Transformations</h2>
</div>
<example-snippet stamp-to="transforms" highlight-as="html">
<template>
<model-viewer id="transform" orientation="20deg 0 0" bounds="tight" shadow-intensity="1" camera-controls ar src="../../shared-assets/models/Astronaut.glb" alt="A 3D model of a chair">
<model-viewer id="transform" orientation="20deg 0 0" bounds="tight" shadow-intensity="1" camera-controls ar ar-modes="webxr scene-viewer quick-look" src="../../shared-assets/models/Astronaut.glb" alt="A 3D model of a chair">
<div class="controls">
<div>Roll: <input id="roll" value="20" size="3" class="number"> degrees</div>
<div>Pitch: <input id="pitch" value="0" size="3" class="number"> degrees</div>
Expand Down Expand Up @@ -195,7 +195,7 @@ <h2 class="demo-title">Change Material Base Color</h2>
</div>
<example-snippet stamp-to="changeColor" highlight-as="html">
<template>
<model-viewer id="color" camera-controls interaction-prompt="none" src="../../shared-assets/models/Astronaut.glb" alt="A 3D model of an astronaut">
<model-viewer id="color" camera-controls interaction-prompt="none" src="../../shared-assets/models/Astronaut.glb" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D model of an astronaut">
<div class="controls", id="color-controls">
<button data-color="1,0,0,1">Red</button>
<button data-color="0,1,0,1">Green</button>
Expand Down Expand Up @@ -236,7 +236,7 @@ <h2 class="demo-title">Change Material Metalness and Roughness Factors</h2>
</div>
<example-snippet stamp-to="changeMaterial" highlight-as="html">
<template>
<model-viewer id="sphere" camera-controls interaction-prompt="none" src="../../shared-assets/models/reflective-sphere.gltf" alt="A 3D model of a sphere">
<model-viewer id="sphere" camera-controls interaction-prompt="none" src="../../shared-assets/models/reflective-sphere.gltf" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D model of a sphere">
<div class="controls">
<div>
<p>Metalness: <span id="metalness-value"></span></p>
Expand Down Expand Up @@ -290,7 +290,7 @@ <h2 class="demo-title">Swap textures - Diffuse/MetallicRoughness</h2>
</div>
<example-snippet stamp-to="swapTextures" highlight-as="html">
<template>
<model-viewer id="lantern" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb" alt="A 3D model of a helmet">
<model-viewer id="lantern" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D model of a helmet">
<div class="controls">
<div>
<p>Diffuse</p>
Expand Down Expand Up @@ -346,7 +346,7 @@ <h2 class="demo-title">Swap textures - Normals/Occlusion/Emission</h2>
</div>
<example-snippet stamp-to="swapTextures2" highlight-as="html">
<template>
<model-viewer id="helmet" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb" alt="A 3D model of a helmet">
<model-viewer id="helmet" camera-controls src="../../shared-assets/models/glTF-Sample-Models/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb" ar ar-modes="webxr scene-viewer quick-look" alt="A 3D model of a helmet">
<div class="controls">
<div>
<p>Normals</p>
Expand Down

0 comments on commit 2baee76

Please sign in to comment.