Skip to content

Commit

Permalink
Support EXT_meshopt_compression (google#2706)
Browse files Browse the repository at this point in the history
* Support EXT_meshopt_compression

* Address feedback on Meshopt implementation

- Add CC BY Attribution notice
- Consolidate files
- Styleguide fixes
  • Loading branch information
donmccurdy authored Aug 23, 2021
1 parent 5e164c4 commit 808bcd8
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 14 deletions.
18 changes: 16 additions & 2 deletions packages/model-viewer/src/features/loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ export declare interface LoadingInterface {
export declare interface LoadingStaticInterface {
dracoDecoderLocation: string;
ktx2TranscoderLocation: string;
meshoptDecoderLocation: string;
mapURLs(callback: (url: string) => string): void;
}

export interface ModelViewerGlobalConfig {
dracoDecoderLocation?: string;
ktx2TranscoderLocation?: string;
meshoptDecoderLocation?: string;
powerPreference?: string;
}

Expand All @@ -114,8 +116,8 @@ export interface ModelViewerGlobalConfig {
*
* ```html
* <script>
* ModelViewerElement = self.ModelViewerElement || {};
* ModelViewerElement.dracoDecoderLocation =
* self.ModelViewerElement = self.ModelViewerElement || {};
* self.ModelViewerElement.dracoDecoderLocation =
* 'http://example.com/location/of/draco/decoder/files/';
* </script>
* ```
Expand Down Expand Up @@ -168,6 +170,14 @@ export const LoadingMixin = <T extends Constructor<ModelViewerElementBase>>(
return CachingGLTFLoader.getKTX2TranscoderLocation();
}

static set meshoptDecoderLocation(value: string) {
CachingGLTFLoader.setMeshoptDecoderLocation(value);
}

static get meshoptDecoderLocation() {
return CachingGLTFLoader.getMeshoptDecoderLocation();
}

/**
* If provided, the callback will be passed each resource URL before a
* request is sent. The callback may return the original URL, or a new URL
Expand Down Expand Up @@ -319,6 +329,10 @@ export const LoadingMixin = <T extends Constructor<ModelViewerElementBase>>(
ModelViewerElement.ktx2TranscoderLocation ||
DEFAULT_KTX2_TRANSCODER_LOCATION;
CachingGLTFLoader.setKTX2TranscoderLocation(ktx2TranscoderLocation);

if (ModelViewerElement.meshoptDecoderLocation) {
CachingGLTFLoader.setMeshoptDecoderLocation(ModelViewerElement.meshoptDecoderLocation);
}
}

connectedCallback() {
Expand Down
41 changes: 41 additions & 0 deletions packages/model-viewer/src/three-components/CachingGLTFLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ export const loadWithLoader =
});
};

/** Helper to load a script tag. */
const fetchScript = (src: string): Promise<Event> => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
document.body.appendChild(script);
script.onload = resolve;
script.onerror = reject;
script.async = true;
script.src = src;
});
};

const cache = new Map<string, Promise<GLTFInstance>>();
const preloaded = new Map<string, boolean>();

Expand All @@ -58,6 +70,18 @@ const dracoLoader = new DRACOLoader();
let ktx2TranscoderLocation: string;
const ktx2Loader = new KTX2Loader();

let meshoptDecoderLocation: string;
let meshoptDecoder: Promise<typeof MeshoptDecoder> | undefined;

interface MeshoptDecoder {
ready: Promise<void>;
supported: boolean;
}

declare global {
const MeshoptDecoder: MeshoptDecoder;
}

export const $loader = Symbol('loader');
export const $evictionPolicy = Symbol('evictionPolicy');
const $GLTFInstance = Symbol('GLTFInstance');
Expand All @@ -83,6 +107,19 @@ export class CachingGLTFLoader<T extends GLTFInstanceConstructor =
return ktx2TranscoderLocation;
}

static setMeshoptDecoderLocation(url: string) {
if (meshoptDecoderLocation !== url) {
meshoptDecoderLocation = url;
meshoptDecoder = fetchScript(url)
.then(() => MeshoptDecoder.ready)
.then(() => MeshoptDecoder);
}
}

static getMeshoptDecoderLocation() {
return meshoptDecoderLocation;
}

static initializeKTX2Loader(renderer: WebGLRenderer) {
ktx2Loader.detectSupport(renderer);
}
Expand Down Expand Up @@ -155,6 +192,10 @@ export class CachingGLTFLoader<T extends GLTFInstanceConstructor =
this.dispatchEvent(
{type: 'preload', element: element, src: url} as PreloadEvent);
if (!cache.has(url)) {
if (meshoptDecoder != null) {
this[$loader].setMeshoptDecoder(await meshoptDecoder);
}

const rawGLTFLoads =
loadWithLoader(url, this[$loader], (progress: number) => {
progressCallback(progress * 0.8);
Expand Down
10 changes: 9 additions & 1 deletion packages/modelviewer.dev/data/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,15 @@
"htmlName": "ktx2TranscoderLocation",
"description": "This static, writable property sets <span class='attribute'>&lt;model-viewer&gt;</span>'s KTX2 transcoder location URL. By default, the KTX2 transcoder will be loaded from a Google CDN.",
"links": [
"<a href=\"../examples/loading/#dracoSupport\"><span class='attribute'>dracoDecoderLocation</span> example</a>"
"<a href=\"../examples/loading/#ktx2Support\"><span class='attribute'>ktx2TranscoderLocation</span> example</a>"
]
},
{
"name": "meshoptDecoderLocation",
"htmlName": "meshoptDecoderLocation",
"description": "This static, writable property sets <span class='attribute'>&lt;model-viewer&gt;</span>'s Meshopt decoder location URL. By default, the Meshopt decoder is not enabled.",
"links": [
"<a href=\"../examples/loading/#meshoptSupport\"><span class='attribute'>meshoptDecoderLocation</span> example</a>"
]
},
{
Expand Down
4 changes: 4 additions & 0 deletions packages/modelviewer.dev/data/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"htmlId": "ktx2Support",
"name": "KTX2 Support"
},
{
"htmlId": "meshoptSupport",
"name": "Meshopt Support"
},
{
"htmlId": "usdzModel",
"name": "USDZ Model"
Expand Down
76 changes: 65 additions & 11 deletions packages/modelviewer.dev/examples/loading/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

<body>

<div class="examples-page">
<div class="examples-page">
<div class="sidebar" id="sidenav"></div>
<div id="toggle"></div>

Expand All @@ -57,7 +57,7 @@ <h2 class="demo-title">Display a poster until loaded</h2>
<h4></h4>
</div>
<example-snippet stamp-to="displayPoster" highlight-as="html">

<template>
<style>
model-viewer#reveal {
Expand Down Expand Up @@ -208,7 +208,7 @@ <h4></h4>
</div>
</div>
</div>

<div class="sample">
<div id="glbModel" class="demo"></div>
<div class="content">
Expand All @@ -227,7 +227,7 @@ <h4></h4>
</div>
</div>
</div>

<div class="sample">
<div id="dracoSupport" class="demo"></div>
<div class="content">
Expand All @@ -241,7 +241,7 @@ <h4>
<h4>
In order to load such models, an auxilliary decoder is
required and will be loaded on-demand from a Google CDN when
a DRACO-compressed model is detected. See below to learn
a DRACO-compressed model is detected. See below to learn
how to customize this behavior.
</h4>
</div>
Expand All @@ -264,8 +264,8 @@ <h4>
<example-snippet inert-script highlight-as="html">
<template>
<script>
ModelViewerElement = self.ModelViewerElement || {};
ModelViewerElement.dracoDecoderLocation = 'http://example.com/location/of/draco/decoder/files/';
self.ModelViewerElement = self.ModelViewerElement || {};
self.ModelViewerElement.dracoDecoderLocation = 'http://example.com/location/of/draco/decoder/files/';
</script>
</template>
</example-snippet>
Expand Down Expand Up @@ -330,7 +330,50 @@ <h4>
</div>
</div>
</div>


<div class="sample">
<div id="meshoptSupport" class="demo"></div>
<div class="content">
<div class="wrapper">
<div class="heading">
<h2 class="demo-title">Meshopt support</h2>
<h4>
&lt;model-viewer&gt; supports loading glTF models that

use <a href="https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Vendor/EXT_meshopt_compression/README.md" target="_blank" rel="noopener">the Meshopt compression extension</a>.
</h4>
<h4>
In order to load such models, an auxiliary decoder is
required, and is not enabled by default.
</h4>
</div>
<example-snippet stamp-to="meshoptSupport" highlight-as="html">
<template>
<model-viewer camera-controls alt="A 3D model of a mechanical coffee mug contraption" src="../../shared-assets/models/coffeemat.glb">
</model-viewer>
</template>
</example-snippet>
<p>
Enable support for Meshopt compression by providing
<code>meshoptDecoderLocation</code>:
</p>
<example-snippet inert-script highlight-as="html">
<template>
<script>
self.ModelViewerElement = self.ModelViewerElement || {};
self.ModelViewerElement.meshoptDecoderLocation = 'https://unpkg.com/[email protected]/meshopt_decoder.js';
</script>
</template>
</example-snippet>
<p>
When customizing the decoder location, you must make sure that
the configuration is set <strong>before</strong> the first &lt;model-viewer&gt;
element is created on the page.
</p>
</div>
</div>
</div>

<div class="sample">
<div id="usdzModel" class="demo"></div>
<div class="content">
Expand All @@ -348,7 +391,7 @@ <h4>Note that this won't display a model (as that requires a glTF or GLB), but c
</div>
</div>
</div>

<div class="sample">
<div id="noModel" class="demo"></div>
<div class="content">
Expand All @@ -366,7 +409,7 @@ <h4>There's nothing to show, but also no error.</h4>
</div>
</div>
</div>

<div class="sample">
<div id="cyclingModels" class="demo"></div>
<div class="content">
Expand Down Expand Up @@ -406,12 +449,17 @@ <h4></h4>
<a href="https://poly.google.com/view/6uTsH2jqgVn">Shish kebab</a> by <a href="https://poly.google.com/user/4aEd8rQgKu2">Poly</a>,
licensed under <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY</a>.
</li>

<li class="attribution">
<a href="https://sketchfab.com/3d-models/coffeemat-7fb196a40a6e4697aad9ca2f75c8b33d">Coffeemat</a> by <a href="https://sketchfab.com/OFFcours1">Roman Red</a>,
licensed under <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution</a>.
</li>
</ul>

<div style="margin-top:24px;" class="copyright">©Copyright 2018-2020 Google Inc. Licensed under the Apache License 2.0.</div>
<div id='footer-links'></div>
</div>

</div>

</div>
Expand All @@ -427,6 +475,12 @@ <h4></h4>
<script type="module" src="../built/dependencies.js">
</script>

<!-- Enables Meshopt decoder. -->
<script>
self.ModelViewerElement = self.ModelViewerElement || {};
self.ModelViewerElement.meshoptDecoderLocation = 'https://unpkg.com/[email protected]/meshopt_decoder.js';
</script>

<!-- Loads <model-viewer> on modern browsers: -->
<script type="module" src="../../node_modules/@google/model-viewer/dist/model-viewer.js">
</script>
Expand Down
Binary file added packages/shared-assets/models/coffeemat.glb
Binary file not shown.

0 comments on commit 808bcd8

Please sign in to comment.