'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var vue = require('vue'); var three = require('three'); var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js'); var RectAreaLightUniformsLib_js = require('three/examples/jsm/lights/RectAreaLightUniformsLib.js'); var RectAreaLightHelper_js = require('three/examples/jsm/helpers/RectAreaLightHelper.js'); var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js'); var FBXLoader_js = require('three/examples/jsm/loaders/FBXLoader.js'); var EffectComposer_js = require('three/examples/jsm/postprocessing/EffectComposer.js'); var RenderPass_js = require('three/examples/jsm/postprocessing/RenderPass.js'); var BokehPass_js = require('three/examples/jsm/postprocessing/BokehPass.js'); var FilmPass_js = require('three/examples/jsm/postprocessing/FilmPass.js'); var ShaderPass_js = require('three/examples/jsm/postprocessing/ShaderPass.js'); var FXAAShader_js = require('three/examples/jsm/shaders/FXAAShader.js'); var HalftonePass_js = require('three/examples/jsm/postprocessing/HalftonePass.js'); var SMAAPass_js = require('three/examples/jsm/postprocessing/SMAAPass.js'); var SSAOPass_js = require('three/examples/jsm/postprocessing/SSAOPass.js'); var UnrealBloomPass_js = require('three/examples/jsm/postprocessing/UnrealBloomPass.js'); function useRaycaster(options) { const { camera, resetPosition = new three.Vector3(0, 0, 0), } = options; const raycaster = new three.Raycaster(); const position = resetPosition.clone(); const plane = new three.Plane(new three.Vector3(0, 0, 1), 0); const updatePosition = (coords) => { raycaster.setFromCamera(coords, camera); camera.getWorldDirection(plane.normal); raycaster.ray.intersectPlane(plane, position); }; const intersect = (coords, objects) => { raycaster.setFromCamera(coords, camera); return raycaster.intersectObjects(objects); }; return { position, updatePosition, intersect, }; } function usePointer(options) { const { camera, domElement, intersectObjects, touch = true, resetOnEnd = false, resetPosition = new three.Vector2(0, 0), resetPositionV3 = new three.Vector3(0, 0, 0), onEnter = () => {}, onMove = () => {}, onLeave = () => {}, onIntersectEnter = () => {}, onIntersectOver = () => {}, onIntersectMove = () => {}, onIntersectLeave = () => {}, onIntersectClick = () => {}, } = options; const position = resetPosition.clone(); const positionN = new three.Vector2(0, 0); const raycaster = useRaycaster({ camera }); const positionV3 = raycaster.position; const obj = { position, positionN, positionV3, intersectObjects, listeners: false, addListeners, removeListeners, intersect, }; return obj; function reset() { position.copy(resetPosition); positionV3.copy(resetPositionV3); } function updatePosition(event) { let x, y; if (event.touches && event.touches.length > 0) { x = event.touches[0].clientX; y = event.touches[0].clientY; } else { x = event.clientX; y = event.clientY; } const rect = domElement.getBoundingClientRect(); position.x = x - rect.left; position.y = y - rect.top; positionN.x = (position.x / rect.width) * 2 - 1; positionN.y = -(position.y / rect.height) * 2 + 1; raycaster.updatePosition(positionN); } function intersect() { if (intersectObjects.length) { const intersects = raycaster.intersect(positionN, intersectObjects); const offObjects = [...intersectObjects]; const iMeshes = []; intersects.forEach(intersect => { const { object } = intersect; const { component } = object; // only once for InstancedMesh if (object instanceof three.InstancedMesh) { if (iMeshes.indexOf(object) !== -1) return; iMeshes.push(object); } if (!object.over) { object.over = true; const overEvent = { type: 'pointerover', over: true, component, intersect }; const enterEvent = { ...overEvent, type: 'pointerenter' }; onIntersectOver(overEvent); onIntersectEnter(enterEvent); component.onPointerOver?.(overEvent); component.onPointerEnter?.(enterEvent); } const moveEvent = { type: 'pointermove', component, intersect }; onIntersectMove(moveEvent); component.onPointerMove?.(moveEvent); offObjects.splice(offObjects.indexOf(object), 1); }); offObjects.forEach(object => { const { component } = object; if (object.over) { object.over = false; const overEvent = { type: 'pointerover', over: false, component }; const leaveEvent = { ...overEvent, type: 'pointerleave' }; onIntersectOver(overEvent); onIntersectLeave(leaveEvent); component.onPointerOver?.(overEvent); component.onPointerLeave?.(leaveEvent); } }); } } function pointerEnter(event) { updatePosition(event); onEnter({ type: 'pointerenter', position, positionN, positionV3 }); } function pointerMove(event) { updatePosition(event); onMove({ type: 'pointermove', position, positionN, positionV3 }); intersect(); } function pointerClick(event) { updatePosition(event); if (intersectObjects.length) { const intersects = raycaster.intersect(positionN, intersectObjects); const iMeshes = []; intersects.forEach(intersect => { const { object } = intersect; const { component } = object; // only once for InstancedMesh if (object instanceof three.InstancedMesh) { if (iMeshes.indexOf(object) !== -1) return; iMeshes.push(object); } const event = { type: 'click', component, intersect }; onIntersectClick(event); component.onClick?.(event); }); } } function pointerLeave() { if (resetOnEnd) reset(); onLeave({ type: 'pointerleave' }); } function addListeners() { domElement.addEventListener('mouseenter', pointerEnter); domElement.addEventListener('mousemove', pointerMove); domElement.addEventListener('mouseleave', pointerLeave); domElement.addEventListener('click', pointerClick); if (touch) { domElement.addEventListener('touchstart', pointerEnter); domElement.addEventListener('touchmove', pointerMove); domElement.addEventListener('touchend', pointerLeave); } obj.listeners = true; } function removeListeners() { domElement.removeEventListener('mouseenter', pointerEnter); domElement.removeEventListener('mousemove', pointerMove); domElement.removeEventListener('mouseleave', pointerLeave); domElement.removeEventListener('click', pointerClick); domElement.removeEventListener('touchstart', pointerEnter); domElement.removeEventListener('touchmove', pointerMove); domElement.removeEventListener('touchend', pointerLeave); obj.listeners = false; }} /** * Three.js helper */ function useThree() { // default conf const conf = { canvas: null, antialias: true, alpha: false, autoClear: true, orbit_ctrl: false, pointer: false, resize: false, width: 300, height: 150, }; // size const size = { width: 1, height: 1, wWidth: 1, wHeight: 1, ratio: 1, }; // handlers const afterInitCallbacks = []; let afterResizeCallbacks = []; let beforeRenderCallbacks = []; const intersectObjects = []; // returned object const obj = { conf, renderer: null, camera: null, cameraCtrl: null, scene: null, pointer: null, size, init, dispose, render, renderC, setSize, onAfterInit, onAfterResize, offAfterResize, // onBeforeRender, offBeforeRender, addIntersectObject, removeIntersectObject, }; /** * init three */ function init(params) { if (params) { Object.entries(params).forEach(([key, value]) => { conf[key] = value; }); } if (!obj.scene) { console.error('Missing Scene'); return; } if (!obj.camera) { console.error('Missing Camera'); return; } obj.renderer = new three.WebGLRenderer({ canvas: conf.canvas, antialias: conf.antialias, alpha: conf.alpha }); obj.renderer.autoClear = conf.autoClear; if (conf.resize) { onResize(); window.addEventListener('resize', onResize); } else { setSize(conf.width, conf.height); } initPointer(); if (conf.orbit_ctrl) { obj.orbitCtrl = new OrbitControls_js.OrbitControls(obj.camera, obj.renderer.domElement); if (conf.orbit_ctrl instanceof Object) { Object.entries(conf.orbit_ctrl).forEach(([key, value]) => { obj.orbitCtrl[key] = value; }); } } afterInitCallbacks.forEach(c => c()); return true; } function initPointer() { let pointerConf = { camera: obj.camera, domElement: obj.renderer.domElement, intersectObjects, }; if (conf.pointer && conf.pointer instanceof Object) { pointerConf = { ...pointerConf, ...conf.pointer }; } obj.pointer = usePointer(pointerConf); if (conf.pointer || intersectObjects.length) { obj.pointer.addListeners(); if (conf.pointer.intersectMode === 'frame') { onBeforeRender(() => { obj.pointer.intersect(); }); } } } /** * add after init callback */ function onAfterInit(callback) { afterInitCallbacks.push(callback); } /** * add after resize callback */ function onAfterResize(callback) { afterResizeCallbacks.push(callback); } /** * remove after resize callback */ function offAfterResize(callback) { afterResizeCallbacks = afterResizeCallbacks.filter(c => c !== callback); } /** * add before render callback */ function onBeforeRender(callback) { beforeRenderCallbacks.push(callback); } /** * default render */ function render() { if (obj.orbitCtrl) obj.orbitCtrl.update(); beforeRenderCallbacks.forEach(c => c()); obj.renderer.render(obj.scene, obj.camera); } /** * composer render */ function renderC() { if (obj.orbitCtrl) obj.orbitCtrl.update(); beforeRenderCallbacks.forEach(c => c()); obj.composer.render(); } /** * add intersect object */ function addIntersectObject(o) { if (intersectObjects.indexOf(o) === -1) { intersectObjects.push(o); } // add listeners if needed if (obj.pointer && !obj.pointer.listeners) { obj.pointer.addListeners(); } } /** * remove intersect object */ function removeIntersectObject(o) { const i = intersectObjects.indexOf(o); if (i !== -1) { intersectObjects.splice(i, 1); } // remove listeners if needed if (obj.pointer && !conf.pointer && intersectObjects.length === 0) { obj.pointer.removeListeners(); } } /** * remove listeners and dispose */ function dispose() { beforeRenderCallbacks = []; window.removeEventListener('resize', onResize); if (obj.pointer) obj.pointer.removeListeners(); if (obj.orbitCtrl) obj.orbitCtrl.dispose(); if (obj.renderer) obj.renderer.dispose(); } /** * resize listener */ function onResize() { if (conf.resize === 'window') { setSize(window.innerWidth, window.innerHeight); } else { const elt = obj.renderer.domElement.parentNode; setSize(elt.clientWidth, elt.clientHeight); } afterResizeCallbacks.forEach(c => c()); } /** * update renderer size and camera */ function setSize(width, height) { size.width = width; size.height = height; size.ratio = width / height; obj.renderer.setSize(width, height, false); obj.camera.aspect = size.ratio; obj.camera.updateProjectionMatrix(); if (obj.composer) { obj.composer.setSize(width, height); } if (obj.camera.type === 'OrthographicCamera') { size.wWidth = obj.camera.right - obj.camera.left; size.wHeight = obj.camera.top - obj.camera.bottom; } else { const wsize = getCameraSize(); size.wWidth = wsize[0]; size.wHeight = wsize[1]; } } /** * calculate camera visible area size */ function getCameraSize() { const vFOV = (obj.camera.fov * Math.PI) / 180; const h = 2 * Math.tan(vFOV / 2) * Math.abs(obj.camera.position.z); const w = h * obj.camera.aspect; return [w, h]; } return obj; } var Renderer = vue.defineComponent({ name: 'Renderer', props: { antialias: Boolean, alpha: Boolean, autoClear: { type: Boolean, default: true }, orbitCtrl: { type: [Boolean, Object], default: false }, pointer: { type: [Boolean, Object], default: false }, resize: { type: [Boolean, String], default: false }, shadow: Boolean, width: String, height: String, xr: Boolean, }, setup() { return { three: useThree(), raf: true, onMountedCallbacks: [], beforeRenderCallbacks: [], afterRenderCallbacks: [], }; }, provide() { return { three: this.three, // renderer: this.three.renderer, rendererComponent: this, }; }, mounted() { const params = { canvas: this.$el, antialias: this.antialias, alpha: this.alpha, autoClear: this.autoClear, orbit_ctrl: this.orbitCtrl, pointer: this.pointer, resize: this.resize, width: this.width, height: this.height, }; if (this.three.init(params)) { this.renderer = this.three.renderer; this.renderer.shadowMap.enabled = this.shadow; this._render = this.three.composer ? this.three.renderC : this.three.render; if (this.xr) { this.renderer.xr.enabled = true; this.renderer.setAnimationLoop(this.render); } else { requestAnimationFrame(this.renderLoop); } } this.onMountedCallbacks.forEach(c => c()); }, beforeUnmount() { this.beforeRenderCallbacks = []; this.afterRenderCallbacks = []; this.raf = false; this.three.dispose(); }, methods: { onMounted(cb) { this.onMountedCallbacks.push(cb); }, onBeforeRender(cb) { this.beforeRenderCallbacks.push(cb); }, offBeforeRender(cb) { this.beforeRenderCallbacks = this.beforeRenderCallbacks.filter(c => c !== cb); }, onAfterRender(cb) { this.afterRenderCallbacks.push(cb); }, offAfterRender(cb) { this.afterRenderCallbacks = this.afterRenderCallbacks.filter(c => c !== cb); }, onAfterResize(cb) { this.three.onAfterResize(cb); }, offAfterResize(cb) { this.three.offAfterResize(cb); }, render(time) { this.beforeRenderCallbacks.forEach(c => c({ time })); this._render(); this.afterRenderCallbacks.forEach(c => c({ time })); }, renderLoop(time) { if (this.raf) requestAnimationFrame(this.renderLoop); this.render(time); }, }, render() { return vue.h('canvas', {}, this.$slots.default()); }, __hmrId: 'Renderer', }); function setFromProp(o, prop) { if (prop instanceof Object) { Object.entries(prop).forEach(([key, value]) => { o[key] = value; }); } } function bindProps(src, props, dst) { props.forEach(prop => { bindProp(src, prop, dst); }); } function bindProp(src, srcProp, dst, dstProp) { if (!dstProp) dstProp = srcProp; const ref = vue.toRef(src, srcProp); if (ref.value instanceof Object) { setFromProp(dst[dstProp], ref.value); vue.watch(ref, (value) => { setFromProp(dst[dstProp], value); }, { deep: true }); } else { if (ref.value) dst[dstProp] = src[srcProp]; vue.watch(ref, (value) => { dst[dstProp] = value; }); } } function propsValues(props, exclude) { const values = {}; Object.entries(props).forEach(([key, value]) => { if (!exclude || (exclude && !exclude.includes(key))) { values[key] = value; } }); return values; } function lerp(value1, value2, amount) { amount = amount < 0 ? 0 : amount; amount = amount > 1 ? 1 : amount; return value1 + (value2 - value1) * amount; } function lerpv2(v1, v2, amount) { v1.x = lerp(v1.x, v2.x, amount); v1.y = lerp(v1.y, v2.y, amount); } function lerpv3(v1, v2, amount) { v1.x = lerp(v1.x, v2.x, amount); v1.y = lerp(v1.y, v2.y, amount); v1.z = lerp(v1.z, v2.z, amount); } function limit(val, min, max) { return val < min ? min : (val > max ? max : val); } // from https://github.com/pmndrs/drei/blob/master/src/useMatcapTexture.tsx const MATCAP_ROOT = 'https://rawcdn.githack.com/emmelleppi/matcaps/9b36ccaaf0a24881a39062d05566c9e92be4aa0d'; function getMatcapUrl(hash, format = 1024) { const fileName = `${hash}${getMatcapFormatString(format)}.png`; return `${MATCAP_ROOT}/${format}/${fileName}`; } function getMatcapFormatString(format) { switch (format) { case 64: return '-64px'; case 128: return '-128px'; case 256: return '-256px'; case 512: return '-512px'; default: return ''; } } // import Object3D from '../core/Object3D.js'; var Camera = vue.defineComponent({ // TODO: eventually extend Object3D, for now: error 'injection "scene" not found' // because camera is a sibling of scene in Trois // extends: Object3D, inject: ['three'], render() { return this.$slots.default ? this.$slots.default() : []; }, }); var OrthographicCamera = vue.defineComponent({ extends: Camera, name: 'OrthographicCamera', inject: ['three'], props: { left: { type: Number, default: -1 }, right: { type: Number, default: 1 }, top: { type: Number, default: 1 }, bottom: { type: Number, default: -1 }, near: { type: Number, default: 0.1 }, far: { type: Number, default: 2000 }, zoom: { type: Number, default: 1 }, position: { type: Object, default: { x: 0, y: 0, z: 0 } }, }, created() { this.camera = new three.OrthographicCamera(this.left, this.right, this.top, this.bottom, this.near, this.far); bindProp(this, 'position', this.camera); ['left', 'right', 'top', 'bottom', 'near', 'far', 'zoom'].forEach(p => { vue.watch(() => this[p], () => { this.camera[p] = this[p]; this.camera.updateProjectionMatrix(); }); }); this.three.camera = this.camera; }, __hmrId: 'OrthographicCamera', }); var PerspectiveCamera = vue.defineComponent({ extends: Camera, name: 'PerspectiveCamera', inject: ['three'], props: { aspect: { type: Number, default: 1 }, far: { type: Number, default: 2000 }, fov: { type: Number, default: 50 }, near: { type: Number, default: 0.1 }, position: { type: Object, default: { x: 0, y: 0, z: 0 } }, lookAt: { type: Object, default: null }, }, created() { this.camera = new three.PerspectiveCamera(this.fov, this.aspect, this.near, this.far); bindProp(this, 'position', this.camera); if (this.lookAt) this.camera.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z); vue.watch(() => this.lookAt, (v) => { this.camera.lookAt(v.x, v.y, v.z); }, { deep: true }); ['aspect', 'far', 'fov', 'near'].forEach(p => { vue.watch(() => this[p], () => { this.camera[p] = this[p]; this.camera.updateProjectionMatrix(); }); }); this.three.camera = this.camera; }, __hmrId: 'PerspectiveCamera', }); var Object3D = vue.defineComponent({ name: 'Object3D', inject: ['three', 'scene', 'rendererComponent'], emits: ['created', 'ready'], props: { position: { type: Object, default: { x: 0, y: 0, z: 0 } }, rotation: { type: Object, default: { x: 0, y: 0, z: 0 } }, scale: { type: Object, default: { x: 1, y: 1, z: 1 } }, lookAt: { type: Object, default: null }, autoRemove: { type: Boolean, default: true }, userData: { type: Object, default: () => ({}) }, }, // can't use setup because it will not be used in sub components // setup() {}, unmounted() { if (this.autoRemove) this.removeFromParent(); }, methods: { initObject3D(o3d) { this.o3d = o3d; this.o3d.userData = this.userData; this.$emit('created', this.o3d); bindProp(this, 'position', this.o3d); bindProp(this, 'rotation', this.o3d); bindProp(this, 'scale', this.o3d); // TODO : fix lookat.x if (this.lookAt) this.o3d.lookAt(this.lookAt.x, this.lookAt.y, this.lookAt.z); vue.watch(() => this.lookAt, (v) => { this.o3d.lookAt(v.x, v.y, v.z); }, { deep: true }); this._parent = this.getParent(); if (this.addToParent()) this.$emit('ready', this); else console.error('Missing parent (Scene, Group...)'); }, getParent() { let parent = this.$parent; while (parent) { if (parent.add) return parent; parent = parent.$parent; } return false; }, addToParent(o) { const o3d = o || this.o3d; if (this._parent) { this._parent.add(o3d); return true; } return false; }, removeFromParent(o) { const o3d = o || this.o3d; if (this._parent) { this._parent.remove(o3d); return true; } return false; }, add(o) { this.o3d.add(o); }, remove(o) { this.o3d.remove(o); }, }, render() { return this.$slots.default ? this.$slots.default() : []; }, __hmrId: 'Object3D', }); var Group = vue.defineComponent({ name: 'Group', extends: Object3D, created() { this.group = new three.Group(); this.initObject3D(this.group); }, __hmrId: 'Group', }); var Scene = vue.defineComponent({ name: 'Scene', inject: ['three'], props: { id: String, background: [String, Number], }, setup(props) { const scene = new three.Scene(); if (props.background) scene.background = new three.Color(props.background); vue.watch(() => props.background, (value) => { scene.background.set(value); }); return { scene }; }, provide() { return { scene: this.scene, }; }, mounted() { if (!this.three.scene) { this.three.scene = this.scene; } }, methods: { add(o) { this.scene.add(o); }, remove(o) { this.scene.remove(o); }, }, render() { return this.$slots.default ? this.$slots.default() : []; }, __hmrId: 'Scene', }); var Raycaster = vue.defineComponent({ name: 'Raycaster', inject: ['three', 'rendererComponent'], props: { onPointerEnter: { type: Function, default: () => {} }, onPointerOver: { type: Function, default: () => {} }, onPointerMove: { type: Function, default: () => {} }, onPointerLeave: { type: Function, default: () => {} }, onClick: { type: Function, default: () => {} }, intersectMode: { type: String, default: 'move' }, }, mounted() { this.rendererComponent.onMounted(() => { this.pointer = usePointer({ camera: this.three.camera, domElement: this.three.renderer.domElement, intersectObjects: this.getIntersectObjects(), onIntersectEnter: this.onPointerEnter, onIntersectOver: this.onPointerOver, onIntersectMove: this.onPointerMove, onIntersectLeave: this.onPointerLeave, onIntersectClick: this.onClick, }); this.pointer.addListeners(); if (this.intersectMode === 'frame') { this.rendererComponent.onBeforeRender(this.pointer.intersect); } }); }, unmounted() { if (this.pointer) { this.pointer.removeListeners(); this.rendererComponent.offBeforeRender(this.pointer.intersect); } }, methods: { getIntersectObjects() { return this.three.scene.children.filter(e => e.type === 'Mesh'); }, }, render() { return []; }, __hmrId: 'Raycaster', }); const Geometry = vue.defineComponent({ inject: ['mesh'], props: { rotateX: Number, rotateY: Number, rotateZ: Number, }, created() { if (!this.mesh) { console.error('Missing parent Mesh'); } this.watchProps = []; Object.entries(this.$props).forEach(e => this.watchProps.push(e[0])); this.createGeometry(); this.rotateGeometry(); this.mesh.setGeometry(this.geometry); this.addWatchers(); }, unmounted() { this.geometry.dispose(); }, methods: { addWatchers() { this.watchProps.forEach(prop => { vue.watch(() => this[prop], () => { this.refreshGeometry(); }); }); }, rotateGeometry() { if (this.rotateX) this.geometry.rotateX(this.rotateX); if (this.rotateY) this.geometry.rotateY(this.rotateY); if (this.rotateZ) this.geometry.rotateZ(this.rotateZ); }, refreshGeometry() { const oldGeo = this.geometry; this.createGeometry(); this.rotateGeometry(); this.mesh.setGeometry(this.geometry); oldGeo.dispose(); }, }, render() { return []; }, }); function geometryComponent(name, props, createGeometry) { return vue.defineComponent({ name, extends: Geometry, props, methods: { createGeometry() { this.geometry = createGeometry(this); }, }, }); } const props$h = { size: Number, width: { type: Number, default: 1 }, height: { type: Number, default: 1 }, depth: { type: Number, default: 1 }, widthSegments: { type: Number, default: 1 }, heightSegments: { type: Number, default: 1 }, depthSegments: { type: Number, default: 1 }, }; function createGeometry$f(comp) { if (comp.size) { return new three.BoxGeometry(comp.size, comp.size, comp.size, comp.widthSegments, comp.heightSegments, comp.depthSegments); } else { return new three.BoxGeometry(comp.width, comp.height, comp.depth, comp.widthSegments, comp.heightSegments, comp.depthSegments); } } var BoxGeometry = geometryComponent('BoxGeometry', props$h, createGeometry$f); const props$g = { radius: { type: Number, default: 1 }, segments: { type: Number, default: 8 }, thetaStart: { type: Number, default: 0 }, thetaLength: { type: Number, default: Math.PI * 2 }, }; function createGeometry$e(comp) { return new three.CircleGeometry(comp.radius, comp.segments, comp.thetaStart, comp.thetaLength); } var CircleGeometry = geometryComponent('CircleGeometry', props$g, createGeometry$e); const props$f = { radius: { type: Number, default: 1 }, height: { type: Number, default: 1 }, radialSegments: { type: Number, default: 8 }, heightSegments: { type: Number, default: 1 }, openEnded: { type: Boolean, default: false }, thetaStart: { type: Number, default: 0 }, thetaLength: { type: Number, default: Math.PI * 2 }, }; function createGeometry$d(comp) { return new three.ConeGeometry(comp.radius, comp.height, comp.radialSegments, comp.heightSegments, comp.openEnded, comp.thetaStart, comp.thetaLength); } var ConeGeometry = geometryComponent('ConeGeometry', props$f, createGeometry$d); const props$e = { radiusTop: { type: Number, default: 1 }, radiusBottom: { type: Number, default: 1 }, height: { type: Number, default: 1 }, radialSegments: { type: Number, default: 8 }, heightSegments: { type: Number, default: 1 }, openEnded: { type: Boolean, default: false }, thetaStart: { type: Number, default: 0 }, thetaLength: { type: Number, default: Math.PI * 2 }, }; function createGeometry$c(comp) { return new three.CylinderGeometry(comp.radiusTop, comp.radiusBottom, comp.height, comp.radialSegments, comp.heightSegments, comp.openEnded, comp.thetaStart, comp.thetaLength); } var CylinderGeometry = geometryComponent('CylinderGeometry', props$e, createGeometry$c); const props$d = { radius: { type: Number, default: 1 }, detail: { type: Number, default: 0 }, }; function createGeometry$b(comp) { return new three.DodecahedronGeometry(comp.radius, comp.detail); } var DodecahedronGeometry = geometryComponent('DodecahedronGeometry', props$d, createGeometry$b); const props$c = { radius: { type: Number, default: 1 }, detail: { type: Number, default: 0 }, }; function createGeometry$a(comp) { return new three.IcosahedronGeometry(comp.radius, comp.detail); } var IcosahedronGeometry = geometryComponent('IcosahedronGeometry', props$c, createGeometry$a); const props$b = { points: Array, segments: { type: Number, default: 12 }, phiStart: { type: Number, default: 0 }, phiLength: { type: Number, default: Math.PI * 2 }, }; function createGeometry$9(comp) { return new three.LatheGeometry(comp.points, comp.segments, comp.phiStart, comp.phiLength); } var LatheGeometry = geometryComponent('LatheGeometry', props$b, createGeometry$9); const props$a = { radius: { type: Number, default: 1 }, detail: { type: Number, default: 0 }, }; function createGeometry$8(comp) { return new three.OctahedronGeometry(comp.radius, comp.detail); } var OctahedronGeometry = geometryComponent('OctahedronGeometry', props$a, createGeometry$8); const props$9 = { width: { type: Number, default: 1 }, height: { type: Number, default: 1 }, widthSegments: { type: Number, default: 1 }, heightSegments: { type: Number, default: 1 }, }; function createGeometry$7(comp) { return new three.PlaneGeometry(comp.width, comp.height, comp.widthSegments, comp.heightSegments); } var PlaneGeometry = geometryComponent('PlaneGeometry', props$9, createGeometry$7); const props$8 = { vertices: Array, indices: Array, radius: { type: Number, default: 1 }, detail: { type: Number, default: 0 }, }; function createGeometry$6(comp) { return new three.PolyhedronGeometry(comp.vertices, comp.indices, comp.radius, comp.detail); } var PolyhedronGeometry = geometryComponent('PolyhedronGeometry', props$8, createGeometry$6); const props$7 = { innerRadius: { type: Number, default: 0.5 }, outerRadius: { type: Number, default: 1 }, thetaSegments: { type: Number, default: 8 }, phiSegments: { type: Number, default: 1 }, thetaStart: { type: Number, default: 0 }, thetaLength: { type: Number, default: Math.PI * 2 }, }; function createGeometry$5(comp) { return new three.RingGeometry(comp.innerRadius, comp.outerRadius, comp.thetaSegments, comp.phiSegments, comp.thetaStart, comp.thetaLength); } var RingGeometry = geometryComponent('RingGeometry', props$7, createGeometry$5); const props$6 = { radius: { type: Number, default: 1 }, widthSegments: { type: Number, default: 12 }, heightSegments: { type: Number, default: 12 }, }; function createGeometry$4(comp) { return new three.SphereGeometry(comp.radius, comp.widthSegments, comp.heightSegments); } var SphereGeometry = geometryComponent('SphereGeometry', props$6, createGeometry$4); const props$5 = { radius: { type: Number, default: 1 }, detail: { type: Number, default: 0 }, }; function createGeometry$3(comp) { return new three.TetrahedronGeometry(comp.radius, comp.detail); } var TetrahedronGeometry = geometryComponent('TetrahedronGeometry', props$5, createGeometry$3); const props$4 = { radius: { type: Number, default: 1 }, tube: { type: Number, default: 0.4 }, radialSegments: { type: Number, default: 8 }, tubularSegments: { type: Number, default: 6 }, arc: { type: Number, default: Math.PI * 2 }, }; function createGeometry$2(comp) { return new three.TorusGeometry(comp.radius, comp.tube, comp.radialSegments, comp.tubularSegments, comp.arc); } var TorusGeometry = geometryComponent('TorusGeometry', props$4, createGeometry$2); const props$3 = { radius: { type: Number, default: 1 }, tube: { type: Number, default: 0.4 }, tubularSegments: { type: Number, default: 64 }, radialSegments: { type: Number, default: 8 }, p: { type: Number, default: 2 }, q: { type: Number, default: 3 }, }; function createGeometry$1(comp) { return new three.TorusKnotGeometry(comp.radius, comp.tube, comp.tubularSegments, comp.radialSegments, comp.p, comp.q); } var TorusKnotGeometry = geometryComponent('TorusKnotGeometry', props$3, createGeometry$1); const props$2 = { points: Array, path: three.Curve, tubularSegments: { type: Number, default: 64 }, radius: { type: Number, default: 1 }, radialSegments: { type: Number, default: 8 }, closed: { type: Boolean, default: false }, }; function createGeometry(comp) { let curve; if (comp.points) { curve = new three.CatmullRomCurve3(comp.points); } else if (comp.path) { curve = comp.path; } else { console.error('Missing path curve or points.'); } return new three.TubeGeometry(curve, comp.tubularSegments, comp.radius, comp.radiusSegments, comp.closed); } var TubeGeometry = vue.defineComponent({ extends: Geometry, props: props$2, methods: { createGeometry() { this.geometry = createGeometry(this); }, // update points (without using prop, faster) updatePoints(points) { updateTubeGeometryPoints(this.geometry, points); }, }, }); function updateTubeGeometryPoints(tube, points) { const curve = new three.CatmullRomCurve3(points); const { radialSegments, radius, tubularSegments, closed } = tube.parameters; const frames = curve.computeFrenetFrames(tubularSegments, closed); tube.tangents = frames.tangents; tube.normals = frames.normals; tube.binormals = frames.binormals; tube.parameters.path = curve; const pArray = tube.attributes.position.array; const nArray = tube.attributes.normal.array; const normal = new three.Vector3(); let P; for (let i = 0; i < tubularSegments; i++) { updateSegment(i); } updateSegment(tubularSegments); tube.attributes.position.needsUpdate = true; tube.attributes.normal.needsUpdate = true; function updateSegment(i) { P = curve.getPointAt(i / tubularSegments, P); const N = frames.normals[i]; const B = frames.binormals[i]; for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); normal.x = (cos * N.x + sin * B.x); normal.y = (cos * N.y + sin * B.y); normal.z = (cos * N.z + sin * B.z); normal.normalize(); const index = (i * (radialSegments + 1) + j) * 3; nArray[index] = normal.x; nArray[index + 1] = normal.y; nArray[index + 2] = normal.z; pArray[index] = P.x + radius * normal.x; pArray[index + 1] = P.y + radius * normal.y; pArray[index + 2] = P.z + radius * normal.z; } } } var Light = vue.defineComponent({ extends: Object3D, name: 'Light', props: { color: { type: String, default: '#ffffff' }, intensity: { type: Number, default: 1 }, castShadow: { type: Boolean, default: false }, shadowMapSize: { type: Object, default: { x: 512, y: 512 } }, shadowCamera: { type: Object, default: {} }, }, // can't use setup because it will not be used in sub components // setup() {}, unmounted() { if (this.light.target) this.removeFromParent(this.light.target); }, methods: { initLight() { if (this.light.target) { bindProp(this, 'target', this.light.target, 'position'); } if (this.light.shadow) { this.light.castShadow = this.castShadow; setFromProp(this.light.shadow.mapSize, this.shadowMapSize); setFromProp(this.light.shadow.camera, this.shadowCamera); } ['color', 'intensity', 'castShadow'].forEach(p => { vue.watch(() => this[p], () => { if (p === 'color') { this.light.color.set(this.color); } else { this.light[p] = this[p]; } }); }); this.initObject3D(this.light); if (this.light.target) this.addToParent(this.light.target); }, }, __hmrId: 'Light', }); var AmbientLight = vue.defineComponent({ extends: Light, created() { this.light = new three.AmbientLight(this.color, this.intensity); this.initLight(); }, __hmrId: 'AmbientLight', }); var DirectionalLight = vue.defineComponent({ extends: Light, props: { target: Object, }, created() { this.light = new three.DirectionalLight(this.color, this.intensity); this.initLight(); }, __hmrId: 'DirectionalLight', }); var HemisphereLight = vue.defineComponent({ extends: Light, props: { groundColor: { type: String, default: '#444444' }, }, created() { this.light = new three.HemisphereLight(this.color, this.groundColor, this.intensity); vue.watch(() => this.groundColor, (value) => { this.light.groundColor.set(value); }); this.initLight(); }, __hmrId: 'HemisphereLight', }); var PointLight = vue.defineComponent({ extends: Light, props: { distance: { type: Number, default: 0, }, decay: { type: Number, default: 1, }, }, created() { this.light = new three.PointLight(this.color, this.intensity, this.distance, this.decay); this.initLight(); }, __hmrId: 'PointLight', }); var RectAreaLight = vue.defineComponent({ extends: Light, props: { width: { type: Number, default: 10 }, height: { type: Number, default: 10 }, helper: Boolean, }, created() { RectAreaLightUniformsLib_js.RectAreaLightUniformsLib.init(); this.light = new three.RectAreaLight(this.color, this.intensity, this.width, this.height); ['width', 'height'].forEach(p => { vue.watch(() => this[p], () => { this.light[p] = this[p]; }); }); if (this.helper) { this.lightHelper = new RectAreaLightHelper_js.RectAreaLightHelper(this.light); this.light.add(this.lightHelper); } this.initLight(); }, unmounted() { if (this.lightHelper) this.removeFromParent(this.lightHelper); }, __hmrId: 'RectAreaLight', }); var SpotLight = vue.defineComponent({ extends: Light, props: { angle: { type: Number, default: Math.PI / 3 }, decay: { type: Number, default: 1 }, distance: { type: Number, default: 0 }, penumbra: { type: Number, default: 0 }, target: Object, }, created() { this.light = new three.SpotLight(this.color, this.intensity, this.distance, this.angle, this.penumbra, this.decay); ['angle', 'decay', 'distance', 'penumbra'].forEach(p => { vue.watch(() => this[p], () => { this.light[p] = this[p]; }); }); this.initLight(); }, __hmrId: 'SpotLight', }); var Material = vue.defineComponent({ inject: ['three', 'mesh'], props: { color: { type: [String, Number], default: '#ffffff' }, depthTest: { type: Boolean, default: true }, depthWrite: { type: Boolean, default: true }, fog: { type: Boolean, default: true }, opacity: { type: Number, default: 1 }, side: { type: Number, default: three.FrontSide }, transparent: Boolean, vertexColors: Boolean, }, provide() { return { material: this, }; }, created() { this.createMaterial(); this.mesh.setMaterial(this.material); this._addWatchers(); if (this.addWatchers) this.addWatchers(); }, unmounted() { this.material.dispose(); }, methods: { setProp(key, value, needsUpdate = false) { this.material[key] = value; this.material.needsUpdate = needsUpdate; }, setTexture(texture, key = 'map') { this.setProp(key, texture, true); }, _addWatchers() { ['color', 'depthTest', 'depthWrite', 'fog', 'opacity', 'side', 'transparent'].forEach(p => { vue.watch(() => this[p], () => { if (p === 'color') { this.material.color.set(this.color); } else { this.material[p] = this[p]; } }); }); }, }, render() { return this.$slots.default ? this.$slots.default() : []; }, __hmrId: 'Material', }); const wireframeProps = { wireframe: { type: Boolean, default: false }, // not needed for WebGL // wireframeLinecap: { type: String, default: 'round' }, // wireframeLinejoin: { type: String, default: 'round' }, wireframeLinewidth: { type: Number, default: 1 }, // not really useful }; var BasicMaterial = vue.defineComponent({ extends: Material, props: { ...wireframeProps, }, methods: { createMaterial() { this.material = new three.MeshBasicMaterial(propsValues(this.$props)); }, addWatchers() { bindProps(this, Object.keys(wireframeProps), this.material); }, }, __hmrId: 'BasicMaterial', }); var LambertMaterial = vue.defineComponent({ extends: Material, props: { ...wireframeProps, }, methods: { createMaterial() { this.material = new three.MeshLambertMaterial(propsValues(this.$props)); }, addWatchers() { bindProps(this, Object.keys(wireframeProps), this.material); }, }, __hmrId: 'LambertMaterial', }); var MatcapMaterial = vue.defineComponent({ extends: Material, props: { src: String, name: String, flatShading: Boolean, }, methods: { createMaterial() { const src = this.name ? getMatcapUrl(this.name) : this.src; const opts = propsValues(this.$props, ['src', 'name']); opts.matcap = new three.TextureLoader().load(src); this.material = new three.MeshMatcapMaterial(opts); }, addWatchers() { // TODO }, }, __hmrId: 'MatcapMaterial', }); var PhongMaterial = vue.defineComponent({ extends: Material, props: { emissive: { type: [Number, String], default: 0 }, emissiveIntensity: { type: Number, default: 1 }, reflectivity: { type: Number, default: 1 }, shininess: { type: Number, default: 30 }, specular: { type: [String, Number], default: 0x111111 }, flatShading: Boolean, ...wireframeProps, }, methods: { createMaterial() { this.material = new three.MeshPhongMaterial(propsValues(this.$props)); }, addWatchers() { // TODO : handle flatShading ? ['emissive', 'emissiveIntensity', 'reflectivity', 'shininess', 'specular'].forEach(p => { vue.watch(() => this[p], (value) => { if (p === 'emissive' || p === 'specular') { this.material[p].set(value); } else { this.material[p] = value; } }); }); bindProps(this, Object.keys(wireframeProps), this.material); }, }, __hmrId: 'PhongMaterial', }); const props$1 = { aoMapIntensity: { type: Number, default: 1 }, bumpScale: { type: Number, default: 1 }, displacementBias: { type: Number, default: 0 }, displacementScale: { type: Number, default: 1 }, emissive: { type: [Number, String], default: 0 }, emissiveIntensity: { type: Number, default: 1 }, envMapIntensity: { type: Number, default: 1 }, lightMapIntensity: { type: Number, default: 1 }, metalness: { type: Number, default: 0 }, normalScale: { type: Object, default: { x: 1, y: 1 } }, roughness: { type: Number, default: 1 }, refractionRatio: { type: Number, default: 0.98 }, flatShading: Boolean, }; var StandardMaterial = vue.defineComponent({ extends: Material, props: { ...props$1, ...wireframeProps, }, methods: { createMaterial() { this.material = new three.MeshStandardMaterial(propsValues(this.$props, ['normalScale'])); }, addWatchers() { // TODO : use setProp, handle flatShading ? Object.keys(props$1).forEach(p => { if (p === 'normalScale') return; vue.watch(() => this[p], (value) => { if (p === 'emissive') { this.material[p].set(value); } else { this.material[p] = value; } }); }); bindProp(this, 'normalScale', this.material); bindProps(this, Object.keys(wireframeProps), this.material); }, }, __hmrId: 'StandardMaterial', }); var PhysicalMaterial = vue.defineComponent({ extends: StandardMaterial, props: { flatShading: Boolean, }, methods: { createMaterial() { this.material = new three.MeshPhysicalMaterial(propsValues(this.$props)); }, addWatchers() { // TODO }, }, __hmrId: 'PhysicalMaterial', }); const defaultVertexShader = ` varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`; const defaultFragmentShader = ` varying vec2 vUv; void main() { gl_FragColor = vec4(vUv.x, vUv.y, 0., 1.0); }`; var ShaderMaterial = vue.defineComponent({ inject: ['three', 'mesh'], props: { uniforms: { type: Object, default: () => { return {}; } }, vertexShader: { type: String, default: defaultVertexShader }, fragmentShader: { type: String, default: defaultFragmentShader }, }, provide() { return { material: this, }; }, created() { this.createMaterial(); ['vertexShader', 'fragmentShader'].forEach(p => { vue.watch(() => this[p], () => { // recreate material if we change either shader this.material.dispose(); this.createMaterial(); }); }); }, unmounted() { this.material.dispose(); }, methods: { createMaterial() { this.material = new three.ShaderMaterial(propsValues(this.$props)); this.mesh.setMaterial(this.material); }, }, render() { return this.$slots.default ? this.$slots.default() : []; }, __hmrId: 'ShaderMaterial', }); /** * ------------------------------------------------------------------------------------------ * Subsurface Scattering shader * Based on three/examples/jsm/shaders/SubsurfaceScatteringShader.js * Based on GDC 2011 – Approximating Translucency for a Fast, Cheap and Convincing Subsurface Scattering Look * https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/ *------------------------------------------------------------------------------------------ */ function replaceAll(string, find, replace) { return string.split(find).join(replace); } const meshphongFragHead = three.ShaderChunk.meshphong_frag.slice(0, three.ShaderChunk.meshphong_frag.indexOf('void main() {')); const meshphongFragBody = three.ShaderChunk.meshphong_frag.slice(three.ShaderChunk.meshphong_frag.indexOf('void main() {')); const SubsurfaceScatteringShader = { uniforms: three.UniformsUtils.merge([ three.ShaderLib.phong.uniforms, { thicknessColor: { value: new three.Color(0xffffff) }, thicknessDistortion: { value: 0.1 }, thicknessAmbient: { value: 0.0 }, thicknessAttenuation: { value: 0.1 }, thicknessPower: { value: 2.0 }, thicknessScale: { value: 10.0 }, }, ]), vertexShader: ` #define USE_UV ${three.ShaderChunk.meshphong_vert} `, fragmentShader: ` #define USE_UV #define SUBSURFACE ${meshphongFragHead} uniform float thicknessPower; uniform float thicknessScale; uniform float thicknessDistortion; uniform float thicknessAmbient; uniform float thicknessAttenuation; uniform vec3 thicknessColor; void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in GeometricContext geometry, inout ReflectedLight reflectedLight) { #ifdef USE_COLOR vec3 thickness = vColor * thicknessColor; #else vec3 thickness = thicknessColor; #endif vec3 scatteringHalf = normalize(directLight.direction + (geometry.normal * thicknessDistortion)); float scatteringDot = pow(saturate(dot(geometry.viewDir, -scatteringHalf)), thicknessPower) * thicknessScale; vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * thickness; reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color; } ` + meshphongFragBody.replace( '#include ', replaceAll( three.ShaderChunk.lights_fragment_begin, 'RE_Direct( directLight, geometry, material, reflectedLight );', ` RE_Direct( directLight, geometry, material, reflectedLight ); #if defined( SUBSURFACE ) && defined( USE_UV ) RE_Direct_Scattering(directLight, vUv, geometry, reflectedLight); #endif ` ) ), }; var SubSurfaceMaterial = vue.defineComponent({ inject: ['three', 'mesh'], props: { color: { type: String, default: '#ffffff' }, thicknessColor: { type: String, default: '#ffffff' }, thicknessDistortion: { type: Number, default: 0.4 }, thicknessAmbient: { type: Number, default: 0.01 }, thicknessAttenuation: { type: Number, default: 0.7 }, thicknessPower: { type: Number, default: 2 }, thicknessScale: { type: Number, default: 4 }, transparent: { type: Boolean, default: false }, opacity: { type: Number, default: 1 }, vertexColors: { type: Boolean, default: false }, }, created() { this.createMaterial(); this.mesh.setMaterial(this.material); }, unmounted() { this.material.dispose(); }, methods: { createMaterial() { const params = SubsurfaceScatteringShader; const uniforms = three.UniformsUtils.clone(params.uniforms); Object.entries(this.$props).forEach(([key, value]) => { let _key = key, _value = value; if (['color', 'thicknessColor'].includes(key)) { if (key === 'color') _key = 'diffuse'; _value = new three.Color(value); } if (!['transparent', 'vertexColors'].includes(key)) { uniforms[_key].value = _value; } }); this.material = new three.ShaderMaterial({ ...params, uniforms, lights: true, transparent: this.transparent, vertexColors: this.vertexColors, }); }, }, render() { return []; }, __hmrId: 'SubSurfaceMaterial', }); var ToonMaterial = vue.defineComponent({ extends: Material, props: { ...wireframeProps, }, methods: { createMaterial() { this.material = new three.MeshToonMaterial(propsValues(this.$props)); }, addWatchers() { bindProps(this, Object.keys(wireframeProps), this.material); }, }, __hmrId: 'ToonMaterial', }); var Texture = vue.defineComponent({ inject: ['material'], emits: ['loaded'], props: { name: { type: String, default: 'map' }, uniform: { type: String, default: null }, src: String, onLoad: Function, onProgress: Function, onError: Function, mapping: { type: Number, default: three.UVMapping }, wrapS: { type: Number, default: three.ClampToEdgeWrapping }, wrapT: { type: Number, default: three.ClampToEdgeWrapping }, magFilter: { type: Number, default: three.LinearFilter }, minFilter: { type: Number, default: three.LinearMipmapLinearFilter }, repeat: { type: Object, default: { x: 1, y: 1 } }, rotation: { type: Number, default: 0 }, center: { type: Object, default: { x: 0, y: 0 } }, }, created() { this.refreshTexture(); vue.watch(() => this.src, this.refreshTexture); }, unmounted() { if (this.material && this.material.setTexture) this.material.setTexture(null, this.name); this.texture.dispose(); }, methods: { createTexture() { this.texture = new three.TextureLoader().load(this.src, this.onLoaded, this.onProgress, this.onError); const wathProps = ['mapping', 'wrapS', 'wrapT', 'magFilter', 'minFilter', 'repeat', 'rotation', 'rotation', 'center']; wathProps.forEach(prop => { bindProp(this, prop, this.texture); }); }, refreshTexture() { this.createTexture(); // handle standard material if (this.material && this.material.setTexture) { this.material.setTexture(this.texture, this.name); } // handle shader material else if (this.material && this.material.material.type === "ShaderMaterial") { // require a `uniform` prop so we know what to call the uniform if (!this.uniform) { console.warn('"uniform" prop required to use texture in a shader.'); return } this.material.uniforms[this.uniform] = { value: this.texture }; } }, onLoaded() { if (this.onLoad) this.onLoad(); this.$emit('loaded'); }, }, render() { return []; }, }); var CubeTexture = vue.defineComponent({ inject: ['material'], emits: ['loaded'], props: { path: String, urls: { type: Array, default: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'], }, onLoad: Function, onProgress: Function, onError: Function, name: { type: String, default: 'envMap' }, refraction: Boolean, // todo: remove ? refractionRatio: { type: Number, default: 0.98 }, }, created() { this.refreshTexture(); vue.watch(() => this.path, this.refreshTexture); vue.watch(() => this.urls, this.refreshTexture); }, unmounted() { this.material.setTexture(null, this.name); this.texture.dispose(); }, methods: { createTexture() { this.texture = new three.CubeTextureLoader() .setPath(this.path) .load(this.urls, this.onLoaded, this.onProgress, this.onError); }, refreshTexture() { this.createTexture(); this.material.setTexture(this.texture, this.name); if (this.refraction) { this.texture.mapping = three.CubeRefractionMapping; this.material.setProp('refractionRatio', this.refractionRatio); } }, onLoaded() { if (this.onLoad) this.onLoad(); this.$emit('loaded'); }, }, render() { return []; }, }); const pointerProps = { onPointerEnter: Function, onPointerOver: Function, onPointerMove: Function, onPointerLeave: Function, onPointerDown: Function, onPointerUp: Function, onClick: Function, }; const Mesh = vue.defineComponent({ name: 'Mesh', extends: Object3D, props: { castShadow: Boolean, receiveShadow: Boolean, ...pointerProps, }, // can't use setup because it will not be used in sub components // setup() {}, provide() { return { mesh: this, }; }, mounted() { if (!this.mesh && !this.loading) this.initMesh(); }, methods: { initMesh() { this.mesh = new three.Mesh(this.geometry, this.material); this.mesh.component = this; bindProp(this, 'castShadow', this.mesh); bindProp(this, 'receiveShadow', this.mesh); if (this.onPointerEnter || this.onPointerOver || this.onPointerMove || this.onPointerLeave || this.onPointerDown || this.onPointerUp || this.onClick) { this.three.addIntersectObject(this.mesh); } this.initObject3D(this.mesh); }, addGeometryWatchers(props) { Object.keys(props).forEach(prop => { vue.watch(() => this[prop], () => { this.refreshGeometry(); }); }); }, setGeometry(geometry) { this.geometry = geometry; if (this.mesh) this.mesh.geometry = geometry; }, setMaterial(material) { this.material = material; if (this.mesh) this.mesh.material = material; }, refreshGeometry() { const oldGeo = this.geometry; this.createGeometry(); this.mesh.geometry = this.geometry; oldGeo.dispose(); }, }, unmounted() { if (this.mesh) { this.three.removeIntersectObject(this.mesh); } // for predefined mesh (geometry is not unmounted) if (this.geometry) this.geometry.dispose(); }, __hmrId: 'Mesh', }); function meshComponent(name, props, createGeometry) { return vue.defineComponent({ name, extends: Mesh, props, created() { this.createGeometry(); this.addGeometryWatchers(props); }, methods: { createGeometry() { this.geometry = createGeometry(this); }, }, __hmrId: name, }); } var Box = meshComponent('Box', props$h, createGeometry$f); var Circle = meshComponent('Circle', props$g, createGeometry$e); var Cone = meshComponent('Cone', props$f, createGeometry$d); var Cylinder = meshComponent('Cylinder', props$e, createGeometry$c); var Dodecahedron = meshComponent('Dodecahedron', props$d, createGeometry$b); var Icosahedron = meshComponent('Icosahedron', props$c, createGeometry$a); var Lathe = meshComponent('Lathe', props$b, createGeometry$9); var Octahedron = meshComponent('Octahedron', props$a, createGeometry$8); var Plane = meshComponent('Plane', props$9, createGeometry$7); var Polyhedron = meshComponent('Polyhedron', props$8, createGeometry$6); var Ring = meshComponent('Ring', props$7, createGeometry$5); var Sphere = meshComponent('Sphere', props$6, createGeometry$4); var Tetrahedron = meshComponent('Tetrahedron', props$5, createGeometry$3); const props = { text: String, fontSrc: String, size: { type: Number, default: 80 }, height: { type: Number, default: 5 }, depth: { type: Number, default: 1 }, curveSegments: { type: Number, default: 12 }, bevelEnabled: { type: Boolean, default: false }, bevelThickness: { type: Number, default: 10 }, bevelSize: { type: Number, default: 8 }, bevelOffset: { type: Number, default: 0 }, bevelSegments: { type: Number, default: 5 }, align: { type: [Boolean, String], default: false }, }; var Text = vue.defineComponent({ extends: Mesh, props, data() { return { loading: true, }; }, created() { // add watchers const watchProps = [ 'text', 'size', 'height', 'curveSegments', 'bevelEnabled', 'bevelThickness', 'bevelSize', 'bevelOffset', 'bevelSegments', 'align', ]; watchProps.forEach(p => { vue.watch(() => this[p], () => { if (this.font) this.refreshGeometry(); }); }); const loader = new three.FontLoader(); loader.load(this.fontSrc, (font) => { this.loading = false; this.font = font; this.createGeometry(); this.initMesh(); }); }, methods: { createGeometry() { this.geometry = new three.TextGeometry(this.text, { font: this.font, size: this.size, height: this.height, depth: this.depth, curveSegments: this.curveSegments, bevelEnabled: this.bevelEnabled, bevelThickness: this.bevelThickness, bevelSize: this.bevelSize, bevelOffset: this.bevelOffset, bevelSegments: this.bevelSegments, }); if (this.align === 'center') { this.geometry.center(); } }, }, }); var Torus = meshComponent('Torus', props$4, createGeometry$2); var TorusKnot = meshComponent('TorusKnot', props$3, createGeometry$1); var Tube = vue.defineComponent({ extends: Mesh, props: props$2, created() { this.createGeometry(); this.addGeometryWatchers(props$2); }, methods: { createGeometry() { this.geometry = createGeometry(this); }, // update curve points (without using prop, faster) updatePoints(points) { updateTubeGeometryPoints(this.geometry, points); }, }, __hmrId: 'Tube', }); var Image = vue.defineComponent({ emits: ['loaded'], extends: Mesh, props: { src: String, width: Number, height: Number, keepSize: Boolean, }, created() { this.createGeometry(); this.createMaterial(); this.initMesh(); vue.watch(() => this.src, this.refreshTexture); ['width', 'height'].forEach(p => { vue.watch(() => this[p], this.resize); }); if (this.keepSize) this.three.onAfterResize(this.resize); }, methods: { createGeometry() { this.geometry = new three.PlaneGeometry(1, 1, 1, 1); }, createMaterial() { this.material = new three.MeshBasicMaterial({ side: three.DoubleSide, map: this.loadTexture() }); }, loadTexture() { return new three.TextureLoader().load(this.src, this.onLoaded); }, refreshTexture() { if (this.texture) this.texture.dispose(); this.material.map = this.loadTexture(); this.material.needsUpdate = true; }, onLoaded(texture) { this.texture = texture; this.resize(); this.$emit('loaded'); }, resize() { if (!this.texture) return; const screen = this.three.size; const iW = this.texture.image.width; const iH = this.texture.image.height; const iRatio = iW / iH; let w, h; if (this.width && this.height) { w = this.width * screen.wWidth / screen.width; h = this.height * screen.wHeight / screen.height; } else if (this.width) { w = this.width * screen.wWidth / screen.width; h = w / iRatio; } else if (this.height) { h = this.height * screen.wHeight / screen.height; w = h * iRatio; } this.mesh.scale.x = w; this.mesh.scale.y = h; }, }, __hmrId: 'Image', }); var InstancedMesh = vue.defineComponent({ extends: Object3D, props: { castShadow: Boolean, receiveShadow: Boolean, count: Number, ...pointerProps, }, provide() { return { mesh: this, }; }, beforeMount() { if (!this.$slots.default) { console.error('Missing Geometry'); } }, mounted() { this.initMesh(); }, methods: { initMesh() { this.mesh = new three.InstancedMesh(this.geometry, this.material, this.count); this.mesh.component = this; bindProp(this, 'castShadow', this.mesh); bindProp(this, 'receiveShadow', this.mesh); if (this.onPointerEnter || this.onPointerOver || this.onPointerMove || this.onPointerLeave || this.onPointerDown || this.onPointerUp || this.onClick) { this.three.addIntersectObject(this.mesh); } this.initObject3D(this.mesh); }, setGeometry(geometry) { this.geometry = geometry; if (this.mesh) this.mesh.geometry = geometry; }, setMaterial(material) { this.material = material; this.material.instancingColor = true; if (this.mesh) this.mesh.material = material; }, }, unmounted() { if (this.mesh) { this.three.removeIntersectObject(this.mesh); } }, __hmrId: 'InstancedMesh', }); var Sprite = vue.defineComponent({ extends: Object3D, emits: ['loaded'], props: { src: String, }, data() { return { loading: true, }; }, created() { this.texture = new three.TextureLoader().load(this.src, this.onLoaded); this.material = new three.SpriteMaterial({ map: this.texture }); this.sprite = new three.Sprite(this.material); this.geometry = this.sprite.geometry; this.initObject3D(this.sprite); }, unmounted() { this.texture.dispose(); this.material.dispose(); }, methods: { onLoaded() { this.loading = false; this.updateUV(); this.$emit('loaded'); }, updateUV() { this.iWidth = this.texture.image.width; this.iHeight = this.texture.image.height; this.iRatio = this.iWidth / this.iHeight; let x = 0.5, y = 0.5; if (this.iRatio > 1) { y = 0.5 / this.iRatio; } else { x = 0.5 / this.iRatio; } const positions = this.geometry.attributes.position.array; positions[0] = -x; positions[1] = -y; positions[5] = x; positions[6] = -y; positions[10] = x; positions[11] = y; positions[15] = -x; positions[16] = y; this.geometry.attributes.position.needsUpdate = true; }, }, __hmrId: 'Sprite', }); var Model = vue.defineComponent({ extends: Object3D, emits: ['load', 'progress', 'error'], data() { return { progress: 0, }; }, methods: { onLoad(model) { this.$emit('load', model); this.initObject3D(model); }, onProgress(progress) { this.progress = progress.loaded / progress.total; this.$emit('progress', progress); }, onError(error) { this.$emit('error', error); }, }, }); var GLTF = vue.defineComponent({ extends: Model, props: { src: String, }, created() { const loader = new GLTFLoader_js.GLTFLoader(); loader.load(this.src, (gltf) => { this.onLoad(gltf.scene); }, this.onProgress, this.onError); }, }); var FBX = vue.defineComponent({ extends: Model, props: { src: String, }, created() { const loader = new FBXLoader_js.FBXLoader(); loader.load(this.src, (fbx) => { this.onLoad(fbx); }, this.onProgress, this.onError); }, }); var EffectComposer = vue.defineComponent({ setup() { return { passes: [], }; }, inject: ['three'], provide() { return { passes: this.passes, }; }, mounted() { this.three.onAfterInit(() => { this.composer = new EffectComposer_js.EffectComposer(this.three.renderer); this.three.renderer.autoClear = false; this.passes.forEach(pass => { this.composer.addPass(pass); }); this.three.composer = this.composer; this.resize(); this.three.onAfterResize(this.resize); }); }, unmounted() { this.three.offAfterResize(this.resize); }, methods: { resize() { this.composer.setSize(this.three.size.width, this.three.size.height); }, }, render() { return this.$slots.default(); }, __hmrId: 'EffectComposer', }); var EffectPass = vue.defineComponent({ inject: ['three', 'passes'], emits: ['ready'], beforeMount() { if (!this.passes) { console.error('Missing parent EffectComposer'); } }, unmounted() { if (this.pass.dispose) this.pass.dispose(); }, methods: { completePass(pass) { this.passes.push(pass); this.pass = pass; this.$emit('ready', pass); }, }, render() { return []; }, __hmrId: 'EffectPass', }); var RenderPass = vue.defineComponent({ extends: EffectPass, mounted() { if (!this.three.scene) { console.error('Missing Scene'); } if (!this.three.camera) { console.error('Missing Camera'); } const pass = new RenderPass_js.RenderPass(this.three.scene, this.three.camera); this.completePass(pass); }, __hmrId: 'RenderPass', }); var BokehPass = vue.defineComponent({ extends: EffectPass, props: { focus: { type: Number, default: 1, }, aperture: { type: Number, default: 0.025, }, maxblur: { type: Number, default: 0.01, }, }, watch: { focus() { this.pass.uniforms.focus.value = this.focus; }, aperture() { this.pass.uniforms.aperture.value = this.aperture; }, maxblur() { this.pass.uniforms.maxblur.value = this.maxblur; }, }, mounted() { if (!this.three.scene) { console.error('Missing Scene'); } if (!this.three.camera) { console.error('Missing Camera'); } const params = { focus: this.focus, aperture: this.aperture, maxblur: this.maxblur, width: this.three.size.width, height: this.three.size.height, }; const pass = new BokehPass_js.BokehPass(this.three.scene, this.three.camera, params); this.completePass(pass); }, __hmrId: 'BokehPass', }); var FilmPass = vue.defineComponent({ extends: EffectPass, props: { noiseIntensity: { type: Number, default: 0.5 }, scanlinesIntensity: { type: Number, default: 0.05 }, scanlinesCount: { type: Number, default: 4096 }, grayscale: { type: Number, default: 0 }, }, watch: { noiseIntensity() { this.pass.uniforms.nIntensity.value = this.noiseIntensity; }, scanlinesIntensity() { this.pass.uniforms.sIntensity.value = this.scanlinesIntensity; }, scanlinesCount() { this.pass.uniforms.sCount.value = this.scanlinesCount; }, grayscale() { this.pass.uniforms.grayscale.value = this.grayscale; }, }, mounted() { const pass = new FilmPass_js.FilmPass(this.noiseIntensity, this.scanlinesIntensity, this.scanlinesCount, this.grayscale); this.completePass(pass); }, __hmrId: 'FilmPass', }); var FXAAPass = vue.defineComponent({ extends: EffectPass, mounted() { const pass = new ShaderPass_js.ShaderPass(FXAAShader_js.FXAAShader); this.completePass(pass); // resize will be called in three init this.three.onAfterResize(this.resize); }, unmounted() { this.three.offAfterResize(this.resize); }, methods: { resize() { const { resolution } = this.pass.material.uniforms; resolution.value.x = 1 / this.three.size.width; resolution.value.y = 1 / this.three.size.height; }, }, __hmrId: 'FXAAPass', }); var HalftonePass = vue.defineComponent({ extends: EffectPass, props: { shape: { type: Number, default: 1 }, radius: { type: Number, default: 4 }, rotateR: { type: Number, default: Math.PI / 12 * 1 }, rotateG: { type: Number, default: Math.PI / 12 * 2 }, rotateB: { type: Number, default: Math.PI / 12 * 3 }, scatter: { type: Number, default: 0 }, }, mounted() { const pass = new HalftonePass_js.HalftonePass(this.three.size.width, this.three.size.height, {}); ['shape', 'radius', 'rotateR', 'rotateG', 'rotateB', 'scatter'].forEach(p => { pass.uniforms[p].value = this[p]; vue.watch(() => this[p], () => { pass.uniforms[p].value = this[p]; }); }); this.completePass(pass); }, __hmrId: 'HalftonePass', }); var SMAAPass = vue.defineComponent({ extends: EffectPass, mounted() { // three size is not set yet, but this pass will be resized by effect composer const pass = new SMAAPass_js.SMAAPass(this.three.size.width, this.three.size.height); this.completePass(pass); }, __hmrId: 'SMAAPass', }); var SSAOPass = vue.defineComponent({ extends: EffectPass, props: { scene: null, camera: null, options: { type: Object, default: () => ({}), }, }, mounted() { const pass = new SSAOPass_js.SSAOPass( this.scene || this.three.scene, this.camera || this.three.camera, this.three.size.width, this.three.size.height ); for (const key of Object.keys(this.options)) { pass[key] = this.options[key]; } this.completePass(pass); }, __hmrId: 'SSAOPass', }); var DefaultShader = { uniforms: {}, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` varying vec2 vUv; void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } `, }; // From https://github.com/evanw/glfx.js var TiltShift = { uniforms: { tDiffuse: { value: null }, blurRadius: { value: 0 }, gradientRadius: { value: 0 }, start: { value: new three.Vector2() }, end: { value: new three.Vector2() }, delta: { value: new three.Vector2() }, texSize: { value: new three.Vector2() }, }, vertexShader: DefaultShader.vertexShader, fragmentShader: ` uniform sampler2D tDiffuse; uniform float blurRadius; uniform float gradientRadius; uniform vec2 start; uniform vec2 end; uniform vec2 delta; uniform vec2 texSize; varying vec2 vUv; float random(vec3 scale, float seed) { /* use the fragment position for a different seed per-pixel */ return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); } void main() { vec4 color = vec4(0.0); float total = 0.0; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x)); float radius = smoothstep(0.0, 1.0, abs(dot(vUv * texSize - start, normal)) / gradientRadius) * blurRadius; for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 texel = texture2D(tDiffuse, vUv + delta / texSize * percent * radius); // vec4 texel2 = texture2D(tDiffuse, vUv + vec2(-delta.y, delta.x) / texSize * percent * radius); /* switch to pre-multiplied alpha to correctly blur transparent images */ texel.rgb *= texel.a; // texel2.rgb *= texel2.a; color += texel * weight; total += 2.0 * weight; } gl_FragColor = color / total; /* switch back from pre-multiplied alpha */ gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } `, }; var TiltShiftPass = vue.defineComponent({ extends: EffectPass, props: { blurRadius: { type: Number, default: 10 }, gradientRadius: { type: Number, default: 100 }, start: { type: Object, default: { x: 0, y: 100 } }, end: { type: Object, default: { x: 10, y: 100 } }, }, mounted() { this.pass = new ShaderPass_js.ShaderPass(TiltShift); this.passes.push(this.pass); this.pass1 = new ShaderPass_js.ShaderPass(TiltShift); this.passes.push(this.pass1); const uniforms = this.uniforms = this.pass.uniforms; const uniforms1 = this.uniforms1 = this.pass1.uniforms; uniforms1.blurRadius = uniforms.blurRadius; uniforms1.gradientRadius = uniforms.gradientRadius; uniforms1.start = uniforms.start; uniforms1.end = uniforms.end; uniforms1.texSize = uniforms.texSize; bindProp(this, 'blurRadius', uniforms.blurRadius, 'value'); bindProp(this, 'gradientRadius', uniforms.gradientRadius, 'value'); this.updateFocusLine(); ['start', 'end'].forEach(p => { vue.watch(() => this[p], this.updateFocusLine, { deep: true }); }); this.pass.setSize = (width, height) => { uniforms.texSize.value.set(width, height); }; // emit ready event with two passes - do so manually in this file instead // of calling `completePass` like in other effect types this.$emit('ready', [this.pass, this.pass1]); }, methods: { updateFocusLine() { this.uniforms.start.value.copy(this.start); this.uniforms.end.value.copy(this.end); const dv = new three.Vector2().copy(this.end).sub(this.start).normalize(); this.uniforms.delta.value.copy(dv); this.uniforms1.delta.value.set(-dv.y, dv.x); }, }, __hmrId: 'TiltShiftPass', }); var UnrealBloomPass = vue.defineComponent({ extends: EffectPass, props: { strength: { type: Number, default: 1.5 }, radius: { type: Number, default: 0 }, threshold: { type: Number, default: 0 }, }, watch: { strength() { this.pass.strength = this.strength; }, radius() { this.pass.radius = this.radius; }, threshold() { this.pass.threshold = this.threshold; }, }, mounted() { const size = new three.Vector2(this.three.size.width, this.three.size.height); const pass = new UnrealBloomPass_js.UnrealBloomPass(size, this.strength, this.radius, this.threshold); this.completePass(pass); }, __hmrId: 'UnrealBloomPass', }); // From https://github.com/evanw/glfx.js var ZoomBlur = { uniforms: { tDiffuse: { value: null }, center: { value: new three.Vector2(0.5, 0.5) }, strength: { value: 0 }, }, vertexShader: DefaultShader.vertexShader, fragmentShader: ` uniform sampler2D tDiffuse; uniform vec2 center; uniform float strength; varying vec2 vUv; float random(vec3 scale, float seed) { /* use the fragment position for a different seed per-pixel */ return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); } void main() { vec4 color = vec4(0.0); float total = 0.0; vec2 toCenter = center - vUv; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = 0.0; t <= 40.0; t++) { float percent = (t + offset) / 40.0; float weight = 4.0 * (percent - percent * percent); vec4 texel = texture2D(tDiffuse, vUv + toCenter * percent * strength); /* switch to pre-multiplied alpha to correctly blur transparent images */ texel.rgb *= texel.a; color += texel * weight; total += weight; } gl_FragColor = color / total; /* switch back from pre-multiplied alpha */ gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } `, }; var ZoomBlurPass = vue.defineComponent({ extends: EffectPass, props: { center: { type: Object, default: { x: 0.5, y: 0.5 } }, strength: { type: Number, default: 0.5 }, }, mounted() { const pass = new ShaderPass_js.ShaderPass(ZoomBlur); const uniforms = this.uniforms = pass.uniforms; bindProp(this, 'center', uniforms.center, 'value'); bindProp(this, 'strength', uniforms.strength, 'value'); this.completePass(pass); }, __hmrId: 'ZoomBlurPass', }); var TROIS = /*#__PURE__*/Object.freeze({ __proto__: null, Renderer: Renderer, OrthographicCamera: OrthographicCamera, PerspectiveCamera: PerspectiveCamera, Camera: PerspectiveCamera, Group: Group, Scene: Scene, Object3D: Object3D, Raycaster: Raycaster, BoxGeometry: BoxGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, AmbientLight: AmbientLight, DirectionalLight: DirectionalLight, HemisphereLight: HemisphereLight, PointLight: PointLight, RectAreaLight: RectAreaLight, SpotLight: SpotLight, BasicMaterial: BasicMaterial, LambertMaterial: LambertMaterial, MatcapMaterial: MatcapMaterial, PhongMaterial: PhongMaterial, PhysicalMaterial: PhysicalMaterial, ShaderMaterial: ShaderMaterial, StandardMaterial: StandardMaterial, SubSurfaceMaterial: SubSurfaceMaterial, ToonMaterial: ToonMaterial, Texture: Texture, CubeTexture: CubeTexture, Mesh: Mesh, Box: Box, Circle: Circle, Cone: Cone, Cylinder: Cylinder, Dodecahedron: Dodecahedron, Icosahedron: Icosahedron, Lathe: Lathe, Octahedron: Octahedron, Plane: Plane, Polyhedron: Polyhedron, Ring: Ring, Sphere: Sphere, Tetrahedron: Tetrahedron, Text: Text, Torus: Torus, TorusKnot: TorusKnot, Tube: Tube, Image: Image, InstancedMesh: InstancedMesh, Sprite: Sprite, GLTFModel: GLTF, FBXModel: FBX, EffectComposer: EffectComposer, RenderPass: RenderPass, BokehPass: BokehPass, FilmPass: FilmPass, FXAAPass: FXAAPass, HalftonePass: HalftonePass, SMAAPass: SMAAPass, SSAOPass: SSAOPass, TiltShiftPass: TiltShiftPass, UnrealBloomPass: UnrealBloomPass, ZoomBlurPass: ZoomBlurPass, setFromProp: setFromProp, bindProps: bindProps, bindProp: bindProp, propsValues: propsValues, lerp: lerp, lerpv2: lerpv2, lerpv3: lerpv3, limit: limit, getMatcapUrl: getMatcapUrl }); const TroisJSVuePlugin = { install: (app) => { const comps = [ 'Camera', 'OrthographicCamera', 'PerspectiveCamera', 'Raycaster', 'Renderer', 'Scene', 'Group', 'AmbientLight', 'DirectionalLight', 'HemisphereLight', 'PointLight', 'RectAreaLight', 'SpotLight', 'BasicMaterial', 'LambertMaterial', 'MatcapMaterial', 'PhongMaterial', 'PhysicalMaterial', 'ShaderMaterial', 'StandardMaterial', 'SubSurfaceMaterial', 'ToonMaterial', 'Texture', 'CubeTexture', 'Mesh', 'Box', 'BoxGeometry', 'Circle', 'CircleGeometry', 'Cone', 'ConeGeometry', 'Cylinder', 'CylinderGeometry', 'Dodecahedron', 'DodecahedronGeometry', 'Icosahedron', 'IcosahedronGeometry', 'Lathe', 'LatheGeometry', 'Octahedron', 'OctahedronGeometry', 'Plane', 'PlaneGeometry', 'Polyhedron', 'PolyhedronGeometry', 'Ring', 'RingGeometry', 'Sphere', 'SphereGeometry', 'Tetrahedron', 'TetrahedronGeometry', 'Text', 'Torus', 'TorusGeometry', 'TorusKnot', 'TorusKnotGeometry', 'Tube', 'TubeGeometry', 'Image', 'InstancedMesh', 'Sprite', 'FBXModel', 'GLTFModel', 'BokehPass', 'EffectComposer', 'FilmPass', 'FXAAPass', 'HalftonePass', 'RenderPass', 'SAOPass', 'SMAAPass', 'SSAOPass', 'TiltShiftPass', 'UnrealBloomPass', 'ZoomBlurPass', 'GLTFViewer', ]; comps.forEach(comp => { app.component(comp, TROIS[comp]); }); }, }; function createApp(params) { return vue.createApp(params).use(TroisJSVuePlugin); } exports.AmbientLight = AmbientLight; exports.BasicMaterial = BasicMaterial; exports.BokehPass = BokehPass; exports.Box = Box; exports.BoxGeometry = BoxGeometry; exports.Camera = PerspectiveCamera; exports.Circle = Circle; exports.CircleGeometry = CircleGeometry; exports.Cone = Cone; exports.ConeGeometry = ConeGeometry; exports.CubeTexture = CubeTexture; exports.Cylinder = Cylinder; exports.CylinderGeometry = CylinderGeometry; exports.DirectionalLight = DirectionalLight; exports.Dodecahedron = Dodecahedron; exports.DodecahedronGeometry = DodecahedronGeometry; exports.EffectComposer = EffectComposer; exports.FBXModel = FBX; exports.FXAAPass = FXAAPass; exports.FilmPass = FilmPass; exports.GLTFModel = GLTF; exports.Group = Group; exports.HalftonePass = HalftonePass; exports.HemisphereLight = HemisphereLight; exports.Icosahedron = Icosahedron; exports.IcosahedronGeometry = IcosahedronGeometry; exports.Image = Image; exports.InstancedMesh = InstancedMesh; exports.LambertMaterial = LambertMaterial; exports.Lathe = Lathe; exports.LatheGeometry = LatheGeometry; exports.MatcapMaterial = MatcapMaterial; exports.Mesh = Mesh; exports.Object3D = Object3D; exports.Octahedron = Octahedron; exports.OctahedronGeometry = OctahedronGeometry; exports.OrthographicCamera = OrthographicCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.PhongMaterial = PhongMaterial; exports.PhysicalMaterial = PhysicalMaterial; exports.Plane = Plane; exports.PlaneGeometry = PlaneGeometry; exports.PointLight = PointLight; exports.Polyhedron = Polyhedron; exports.PolyhedronGeometry = PolyhedronGeometry; exports.Raycaster = Raycaster; exports.RectAreaLight = RectAreaLight; exports.RenderPass = RenderPass; exports.Renderer = Renderer; exports.Ring = Ring; exports.RingGeometry = RingGeometry; exports.SMAAPass = SMAAPass; exports.SSAOPass = SSAOPass; exports.Scene = Scene; exports.ShaderMaterial = ShaderMaterial; exports.Sphere = Sphere; exports.SphereGeometry = SphereGeometry; exports.SpotLight = SpotLight; exports.Sprite = Sprite; exports.StandardMaterial = StandardMaterial; exports.SubSurfaceMaterial = SubSurfaceMaterial; exports.Tetrahedron = Tetrahedron; exports.TetrahedronGeometry = TetrahedronGeometry; exports.Text = Text; exports.Texture = Texture; exports.TiltShiftPass = TiltShiftPass; exports.ToonMaterial = ToonMaterial; exports.Torus = Torus; exports.TorusGeometry = TorusGeometry; exports.TorusKnot = TorusKnot; exports.TorusKnotGeometry = TorusKnotGeometry; exports.TroisJSVuePlugin = TroisJSVuePlugin; exports.Tube = Tube; exports.TubeGeometry = TubeGeometry; exports.UnrealBloomPass = UnrealBloomPass; exports.ZoomBlurPass = ZoomBlurPass; exports.bindProp = bindProp; exports.bindProps = bindProps; exports.createApp = createApp; exports.getMatcapUrl = getMatcapUrl; exports.lerp = lerp; exports.lerpv2 = lerpv2; exports.lerpv3 = lerpv3; exports.limit = limit; exports.propsValues = propsValues; exports.setFromProp = setFromProp;