UNPKG

@google/model-viewer

Version:

Easily display interactive 3D models on the web and in AR!

206 lines 8.71 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; import { css, customElement, html, LitElement, property } from 'lit-element'; import { resolveDpr } from '../../../../utilities.js'; const fetchFilamentAssets = async (assets) => new Promise((resolve) => { self.Filament.fetch(assets, () => resolve(), () => { }); }); const basepath = (urlString) => { const url = new URL(urlString, self.location.toString()); const { pathname } = url; url.pathname = pathname.slice(0, pathname.lastIndexOf('/') + 1); return url.toString(); }; const IS_BINARY_RE = /\.glb$/; const $engine = Symbol('engine'); const $scene = Symbol('scene'); const $ibl = Symbol('ibl'); const $skybox = Symbol('skybox'); const $swapChain = Symbol('swapChain'); const $renderer = Symbol('renderer'); const $camera = Symbol('camera'); const $view = Symbol('view'); const $canvas = Symbol('canvas'); const $boundingBox = Symbol('boundingBox'); const $currentAsset = Symbol('currentAsset'); const $initialize = Symbol('initialize'); const $updateScenario = Symbol('scenario'); const $updateSize = Symbol('updateSize'); const $render = Symbol('render'); const $rendering = Symbol('rendering'); let FilamentViewer = class FilamentViewer extends LitElement { constructor() { super(); this.scenario = null; this[_a] = false; this[_b] = null; this[_c] = null; this[_d] = null; this[_e] = null; this[_f] = null; this[_g] = null; this[_h] = null; this[_j] = null; this[_k] = null; this[_l] = null; this[_m] = null; self.Filament.init([], () => { this[$initialize](); }); } connectedCallback() { super.connectedCallback(); this[$render](); } disconnectedCallback() { this[$rendering] = false; } updated(changedProperties) { super.updated(changedProperties); if (changedProperties.has('scenario') && this.scenario != null) { this[$updateScenario](this.scenario); } } static get styles() { return css ` :host { display: block; } `; } render() { return html `<canvas id="canvas"></canvas>`; } [(_a = $rendering, _b = $engine, _c = $scene, _d = $renderer, _e = $swapChain, _f = $camera, _g = $view, _h = $ibl, _j = $skybox, _k = $currentAsset, _l = $canvas, _m = $boundingBox, $initialize)]() { const { Filament } = self; this[$canvas] = this.shadowRoot.querySelector('canvas'); this[$engine] = Filament.Engine.create(this[$canvas]); this[$scene] = this[$engine].createScene(); this[$swapChain] = this[$engine].createSwapChain(); this[$renderer] = this[$engine].createRenderer(); this[$camera] = this[$engine].createCamera(); this[$view] = this[$engine].createView(); this[$view].setCamera(this[$camera]); this[$view].setScene(this[$scene]); this[$boundingBox] = { min: [-1, -1, -1], max: [1, 1, 1] }; this[$updateSize](); } async [$updateScenario](scenario) { const modelUrl = new URL(scenario.model, window.location.toString()).toString(); const lightingBaseName = scenario.lighting.split('/').pop() .split('.') .slice(0, -1) .join(''); const iblUrl = `./ktx/${lightingBaseName}/${lightingBaseName}_ibl.ktx`; const skyboxUrl = `./ktx/${lightingBaseName}/${lightingBaseName}_skybox.ktx`; console.log('Scenario:', scenario.name); console.log('Lighting:', lightingBaseName); if (this[$currentAsset] != null) { const entities = this[$currentAsset].getEntities(); const size = entities.size(); for (let i = 0; i < size; ++i) { const entity = entities.get(i); this[$scene].remove(entity); this[$engine].destroyEntity(entity); } this[$currentAsset] = null; } if (this[$ibl] != null) { this[$engine].destroyIndirectLight(this[$ibl]); this[$ibl] = null; } if (this[$skybox] != null) { this[$engine].destroySkybox(this[$skybox]); this[$skybox] = null; } await fetchFilamentAssets([modelUrl, iblUrl, skyboxUrl]); this[$ibl] = this[$engine].createIblFromKtx(iblUrl); this[$scene].setIndirectLight(this[$ibl]); this[$ibl].setIntensity(40000); this[$ibl].setRotation([0, 0, -1, 0, 1, 0, 1, 0, 0]); // 90 degrees this[$skybox] = this[$engine].createSkyFromKtx(skyboxUrl); this[$scene].setSkybox(this[$skybox]); const loader = this[$engine].createAssetLoader(); this[$currentAsset] = IS_BINARY_RE.test(modelUrl) ? loader.createAssetFromBinary(modelUrl) : loader.createAssetFromJson(modelUrl); const finalize = (await new Promise((resolve) => { console.log('Loading resources for', modelUrl); this[$currentAsset].loadResources(resolve, () => { }, basepath(modelUrl)); })); finalize(); loader.delete(); this[$boundingBox] = this[$currentAsset].getBoundingBox(); this[$scene].addEntities(this[$currentAsset].getEntities()); this[$updateSize](); // Wait two rAFs to ensure we rendered at least once: requestAnimationFrame(() => { requestAnimationFrame(() => { this.dispatchEvent(new CustomEvent('model-visibility', { detail: { visible: true } })); }); }); } [$render]() { this[$rendering] = true; if (this[$renderer] != null) { this[$renderer].render(this[$swapChain], this[$view]); } self.requestAnimationFrame(() => { if (this[$rendering]) { this[$render](); } }); } [$updateSize]() { if (this[$canvas] == null || this.scenario == null) { // Not initialized yet. This will be invoked again when initialized. return; } const Fov = self.Filament.Camera$Fov; const canvas = this[$canvas]; const { scenario } = this; const dpr = resolveDpr(); const width = scenario.dimensions.width * dpr; const height = scenario.dimensions.height * dpr; canvas.width = width; canvas.height = height; canvas.style.width = `${scenario.dimensions.width}px`; canvas.style.height = `${scenario.dimensions.height}px`; this[$view].setViewport([0, 0, width, height]); const aspect = width / height; const target = [0, 0, 0]; const eye = [0, 0, 0]; const boundingBox = this[$boundingBox]; for (let i = 0; i < 3; i++) { target[i] = (boundingBox.min[i] + boundingBox.max[i]) / 2.0; eye[i] = target[i]; } const boxHalfX = Math.max(Math.abs(boundingBox.min[0] - target[0]), Math.abs(boundingBox.max[0] - target[0])); const boxHalfZ = Math.max(Math.abs(boundingBox.min[2] - target[2]), Math.abs(boundingBox.max[2] - target[2])); const boxHalfY = Math.max(Math.abs(boundingBox.min[1] - target[1]), Math.abs(boundingBox.max[1] - target[1])); const modelDepth = 2 * Math.max(boxHalfX, boxHalfZ); const framedHeight = Math.max(2 * boxHalfY, modelDepth / aspect); const fov = 45; const framedDistance = (framedHeight / 2) / Math.tan((fov / 2) * Math.PI / 180); const near = framedHeight / 10.0; const far = framedHeight * 10.0; const cameraDistance = framedDistance + modelDepth / 2; this[$camera].setProjectionFov(fov, aspect, near, far, Fov.VERTICAL); eye[2] += cameraDistance; const up = [0, 1, 0]; this[$camera].lookAt(eye, target, up); } }; __decorate([ property({ type: Object }) ], FilamentViewer.prototype, "scenario", void 0); FilamentViewer = __decorate([ customElement('filament-viewer') ], FilamentViewer); export { FilamentViewer }; //# sourceMappingURL=filament-viewer.js.map