Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EXT_meshopt_compression #2706

Merged
merged 2 commits into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
donmccurdy marked this conversation as resolved.
Show resolved Hide resolved
}
}

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/meshoptimizer@0.16.0/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>

donmccurdy marked this conversation as resolved.
Show resolved Hide resolved
</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/meshoptimizer@0.16.0/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.