1
0
mirror of https://github.com/troisjs/trois.git synced 2024-11-23 20:02:32 +08:00
This commit is contained in:
Kevin Levron 2021-04-16 01:56:52 +02:00
parent 154fd7439c
commit d2006b1f2c
37 changed files with 357 additions and 341 deletions

View File

@ -7,6 +7,6 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import { DoubleSide, Mesh, MeshStandardMaterial, PlaneGeometry } from 'three'; import { DoubleSide, Mesh, MeshStandardMaterial, PlaneGeometry } from 'three';
import Object3D from '../../core/Object3D.js'; import Object3D from '../../core/Object3D';
import { bindProps } from '../../tools'; import { bindProps } from '../../tools';
import LiquidEffect from './LiquidEffect.js'; import LiquidEffect from './LiquidEffect.js';
@ -17,9 +17,9 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.liquidEffect = new LiquidEffect(this.three.renderer); this.liquidEffect = new LiquidEffect(this.three.renderer);
this.rendererComponent.onMounted(() => { this.renderer.onMounted(() => {
this.liquidEffect.renderer = this.rendererComponent.renderer; this.liquidEffect.renderer = this.renderer.renderer;
this.rendererComponent.onBeforeRender(this.update); this.renderer.onBeforeRender(this.update);
}); });
this.material = new MeshStandardMaterial({ color: this.color, side: DoubleSide, metalness: this.metalness, roughness: this.roughness, this.material = new MeshStandardMaterial({ color: this.color, side: DoubleSide, metalness: this.metalness, roughness: this.roughness,
@ -44,7 +44,7 @@ export default defineComponent({
this.initObject3D(this.mesh); this.initObject3D(this.mesh);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.update); this.renderer.offBeforeRender(this.update);
}, },
methods: { methods: {
update() { update() {

View File

@ -21,11 +21,11 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.initGem(); this.initGem();
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT); if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
else this.rendererComponent.onMounted(this.updateCubeRT); else this.renderer.onMounted(this.updateCubeRT);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateCubeRT); this.renderer.offBeforeRender(this.updateCubeRT);
if (this.cubeCamera) this.removeFromParent(this.cubeCamera); if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
if (this.meshBack) this.removeFromParent(this.meshBack); if (this.meshBack) this.removeFromParent(this.meshBack);
if (this.materialBack) this.materialBack.dispose(); if (this.materialBack) this.materialBack.dispose();

View File

@ -17,11 +17,11 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.initMirrorMesh(); this.initMirrorMesh();
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT); if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
else this.rendererComponent.onMounted(this.updateCubeRT); else this.renderer.onMounted(this.updateCubeRT);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateCubeRT); this.renderer.offBeforeRender(this.updateCubeRT);
if (this.cubeCamera) this.removeFromParent(this.cubeCamera); if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
}, },
methods: { methods: {

View File

@ -20,11 +20,11 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.initMirrorMesh(); this.initMirrorMesh();
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT); if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
else this.rendererComponent.onMounted(this.updateCubeRT); else this.renderer.onMounted(this.updateCubeRT);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateCubeRT); this.renderer.offBeforeRender(this.updateCubeRT);
if (this.cubeCamera) this.removeFromParent(this.cubeCamera); if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
}, },
methods: { methods: {

View File

@ -5,7 +5,7 @@ export default {
noSetup: { type: Boolean, default: false }, noSetup: { type: Boolean, default: false },
}, },
emits: ['created'], emits: ['created'],
inject: ['rendererComponent'], inject: ['renderer'],
setup({ noSetup }) { setup({ noSetup }) {
const stats = new Stats(); const stats = new Stats();
if (!noSetup) { if (!noSetup) {
@ -16,8 +16,8 @@ export default {
}, },
mounted() { mounted() {
if (!this.noSetup) { if (!this.noSetup) {
this.rendererComponent.onBeforeRender(this.begin); this.renderer.onBeforeRender(this.begin);
this.rendererComponent.onAfterRender(this.end); this.renderer.onAfterRender(this.end);
} }
this.$emit('created', { stats: this.stats }); this.$emit('created', { stats: this.stats });
}, },
@ -37,8 +37,8 @@ export default {
if (this.stats && this.stats.dom) { if (this.stats && this.stats.dom) {
this.stats.dom.parentElement.removeChild(this.stats.dom); this.stats.dom.parentElement.removeChild(this.stats.dom);
} }
this.rendererComponent.offBeforeRender(this.begin); this.renderer.offBeforeRender(this.begin);
this.rendererComponent.offAfterRender(this.end); this.renderer.offAfterRender(this.end);
}, },
render() { render() {
return this.$slots.default ? this.$slots.default() : []; return this.$slots.default ? this.$slots.default() : [];

View File

@ -29,10 +29,10 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.startTime = Date.now(); this.startTime = Date.now();
this.rendererComponent.onBeforeRender(this.updateTime); this.renderer.onBeforeRender(this.updateTime);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateTime); this.renderer.offBeforeRender(this.updateTime);
}, },
methods: { methods: {
createGeometry() { createGeometry() {

View File

@ -30,10 +30,10 @@ export default defineComponent({
watch(() => this.displacementScale, (value) => { this.material.displacementScale = value; }); watch(() => this.displacementScale, (value) => { this.material.displacementScale = value; });
this.startTime = Date.now(); this.startTime = Date.now();
this.rendererComponent.onBeforeRender(this.update); this.renderer.onBeforeRender(this.update);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.update); this.renderer.offBeforeRender(this.update);
this.fsQuad.dispose(); this.fsQuad.dispose();
this.dispRT.dispose(); this.dispRT.dispose();
this.dispMat.dispose(); this.dispMat.dispose();

View File

@ -28,10 +28,10 @@ export default defineComponent({
this.updateMaterial(); this.updateMaterial();
this.startTime = Date.now(); this.startTime = Date.now();
this.rendererComponent.onBeforeRender(this.updateTime); this.renderer.onBeforeRender(this.updateTime);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateTime); this.renderer.offBeforeRender(this.updateTime);
}, },
methods: { methods: {
updateMaterial() { updateMaterial() {

View File

@ -25,10 +25,10 @@ export default defineComponent({
this.updateMaterial(); this.updateMaterial();
this.startTime = Date.now(); this.startTime = Date.now();
this.rendererComponent.onBeforeRender(this.updateTime); this.renderer.onBeforeRender(this.updateTime);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.updateTime); this.renderer.offBeforeRender(this.updateTime);
}, },
methods: { methods: {
updateMaterial() { updateMaterial() {

View File

@ -3,7 +3,7 @@ import useCannon from './useCannon.js';
// import { bindProp } from '../../tools'; // import { bindProp } from '../../tools';
export default defineComponent({ export default defineComponent({
inject: ['three', 'scene', 'rendererComponent'], inject: ['three', 'scene', 'renderer'],
props: { props: {
gravity: { type: Object, default: () => ({ x: 0, y: 0, z: -9.82 }) }, gravity: { type: Object, default: () => ({ x: 0, y: 0, z: -9.82 }) },
broadphase: { type: String }, broadphase: { type: String },
@ -16,10 +16,10 @@ export default defineComponent({
this.cannon = useCannon({ gravity: this.gravity, broadphase: this.broadphase }); this.cannon = useCannon({ gravity: this.gravity, broadphase: this.broadphase });
}, },
mounted() { mounted() {
this.rendererComponent.onBeforeRender(this.step); this.renderer.onBeforeRender(this.step);
}, },
unmounted() { unmounted() {
this.rendererComponent.offBeforeRender(this.step); this.renderer.offBeforeRender(this.step);
}, },
methods: { methods: {
step() { step() {

View File

@ -11,7 +11,7 @@ import { defineComponent } from 'vue';
import { Object3D } from 'three'; import { Object3D } from 'three';
import { gsap, Power4 } from 'gsap'; import { gsap, Power4 } from 'gsap';
import Camera from '../../core/PerspectiveCamera.js'; import Camera from '../../core/PerspectiveCamera';
import Renderer from '../../core/Renderer.js'; import Renderer from '../../core/Renderer.js';
import Scene from '../../core/Scene.js'; import Scene from '../../core/Scene.js';

View File

@ -10,7 +10,7 @@ import { defineComponent } from 'vue';
import { Vector2 } from 'three'; import { Vector2 } from 'three';
import { gsap, Power4 } from 'gsap'; import { gsap, Power4 } from 'gsap';
import OrthographicCamera from '../../core/OrthographicCamera.js'; import OrthographicCamera from '../../core/OrthographicCamera';
import Renderer from '../../core/Renderer.js'; import Renderer from '../../core/Renderer.js';
import Scene from '../../core/Scene.js'; import Scene from '../../core/Scene.js';

View File

@ -10,7 +10,7 @@
<script> <script>
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import Camera from '../../core/PerspectiveCamera.js'; import Camera from '../../core/PerspectiveCamera';
import Renderer from '../../core/Renderer.js'; import Renderer from '../../core/Renderer.js';
import Scene from '../../core/Scene.js'; import Scene from '../../core/Scene.js';

View File

@ -1,5 +1,5 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue'
// import Object3D from '../core/Object3D.js'; // import Object3D from '../core/Object3D';
export default defineComponent({ export default defineComponent({
// TODO: eventually extend Object3D, for now: error 'injection "scene" not found' // TODO: eventually extend Object3D, for now: error 'injection "scene" not found'
@ -7,6 +7,6 @@ export default defineComponent({
// extends: Object3D, // extends: Object3D,
inject: ['three'], inject: ['three'],
render() { render() {
return this.$slots.default ? this.$slots.default() : []; return this.$slots.default ? this.$slots.default() : []
}, },
}); })

View File

@ -1,13 +0,0 @@
import { defineComponent } from 'vue';
import { Group } from 'three';
import Object3D from './Object3D.js';
export default defineComponent({
name: 'Group',
extends: Object3D,
created() {
this.group = new Group();
this.initObject3D(this.group);
},
__hmrId: 'Group',
});

17
src/core/Group.ts Normal file
View File

@ -0,0 +1,17 @@
import { defineComponent } from 'vue'
import { Group } from 'three'
import Object3D from './Object3D'
export default defineComponent({
name: 'Group',
extends: Object3D,
setup() {
return {
group: new Group(),
}
},
created() {
this.initObject3D(this.group)
},
__hmrId: 'Group',
})

View File

@ -1,70 +0,0 @@
import { defineComponent, watch } from 'vue';
import { bindProp } from '../tools';
export default 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);
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',
});

71
src/core/Object3D.ts Normal file
View File

@ -0,0 +1,71 @@
import { Object3D } from 'three'
import { defineComponent, watch } from 'vue'
import { bindProp } from '../tools'
export default defineComponent({
name: 'Object3D',
inject: ['three', 'scene', 'renderer'],
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: Object3D) {
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)
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',
})

View File

@ -1,7 +1,7 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import { OrthographicCamera } from 'three'; import { OrthographicCamera } from 'three';
import { bindProp } from '../tools'; import { bindProp } from '../tools';
import Camera from './Camera.js'; import Camera from './Camera';
export default defineComponent({ export default defineComponent({
extends: Camera, extends: Camera,

View File

@ -1,7 +1,7 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import { PerspectiveCamera } from 'three'; import { PerspectiveCamera } from 'three';
import { bindProp } from '../tools'; import { bindProp } from '../tools';
import Camera from './Camera.js'; import Camera from './Camera';
export default defineComponent({ export default defineComponent({
extends: Camera, extends: Camera,

View File

@ -3,7 +3,7 @@ import usePointer from './usePointer';
export default defineComponent({ export default defineComponent({
name: 'Raycaster', name: 'Raycaster',
inject: ['three', 'rendererComponent'], inject: ['three', 'renderer'],
props: { props: {
onPointerEnter: { type: Function, default: () => {} }, onPointerEnter: { type: Function, default: () => {} },
onPointerOver: { type: Function, default: () => {} }, onPointerOver: { type: Function, default: () => {} },
@ -13,7 +13,7 @@ export default defineComponent({
intersectMode: { type: String, default: 'move' }, intersectMode: { type: String, default: 'move' },
}, },
mounted() { mounted() {
this.rendererComponent.onMounted(() => { this.renderer.onMounted(() => {
this.pointer = usePointer({ this.pointer = usePointer({
camera: this.three.camera, camera: this.three.camera,
domElement: this.three.renderer.domElement, domElement: this.three.renderer.domElement,
@ -27,14 +27,14 @@ export default defineComponent({
this.pointer.addListeners(); this.pointer.addListeners();
if (this.intersectMode === 'frame') { if (this.intersectMode === 'frame') {
this.rendererComponent.onBeforeRender(this.pointer.intersect); this.renderer.onBeforeRender(this.pointer.intersect);
} }
}); });
}, },
unmounted() { unmounted() {
if (this.pointer) { if (this.pointer) {
this.pointer.removeListeners(); this.pointer.removeListeners();
this.rendererComponent.offBeforeRender(this.pointer.intersect); this.renderer.offBeforeRender(this.pointer.intersect);
} }
}, },
methods: { methods: {

View File

@ -1,105 +0,0 @@
import { defineComponent, h } from 'vue';
import useThree from './useThree';
export default 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 h('canvas', {}, this.$slots.default());
},
__hmrId: 'Renderer',
});

113
src/core/Renderer.ts Normal file
View File

@ -0,0 +1,113 @@
import { WebGLRenderer } from 'three'
import { defineComponent, h } from 'vue'
import useThree, { ThreeConfigInterface } from './useThree'
export default 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() {
const renderer: null | WebGLRenderer = null
const onMountedCallbacks: {(): void}[] = []
const beforeRenderCallbacks: {(): void}[] = []
const afterRenderCallbacks: {(): void}[] = []
return {
three: useThree(),
renderer,
raf: true,
onMountedCallbacks,
beforeRenderCallbacks,
afterRenderCallbacks,
}
},
provide() {
return {
renderer: this,
three: this.three,
}
},
mounted() {
const params: ThreeConfigInterface = {
canvas: this.$el,
antialias: this.antialias,
alpha: this.alpha,
autoClear: this.autoClear,
orbitCtrl: this.orbitCtrl,
pointer: this.pointer,
resize: this.resize,
}
if (this.width) params.width = parseInt(this.width)
if (this.height) params.height = parseInt(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: {(): void}) {
this.onMountedCallbacks.push(cb)
},
onBeforeRender(cb: {(): void}) {
this.beforeRenderCallbacks.push(cb)
},
offBeforeRender(cb: {(): void}) {
this.beforeRenderCallbacks = this.beforeRenderCallbacks.filter(c => c !== cb)
},
onAfterRender(cb: {(): void}) {
this.afterRenderCallbacks.push(cb)
},
offAfterRender(cb: {(): void}) {
this.afterRenderCallbacks = this.afterRenderCallbacks.filter(c => c !== cb)
},
onAfterResize(cb: {(): void}) {
this.three.onAfterResize(cb)
},
offAfterResize(cb: {(): void}) {
this.three.offAfterResize(cb)
},
render(time: number) {
this.beforeRenderCallbacks.forEach(c => c({ time }))
this._render()
this.afterRenderCallbacks.forEach(c => c({ time }))
},
renderLoop(time: number) {
if (this.raf) requestAnimationFrame(this.renderLoop)
this.render(time)
},
},
render() {
return h('canvas', {}, this.$slots.default ? this.$slots.default() : [])
},
__hmrId: 'Renderer',
})

View File

@ -1,8 +1,8 @@
export { default as Renderer } from './Renderer.js'; export { default as Renderer } from './Renderer';
export { default as OrthographicCamera } from './OrthographicCamera.js'; export { default as OrthographicCamera } from './OrthographicCamera';
export { default as PerspectiveCamera } from './PerspectiveCamera.js'; export { default as PerspectiveCamera } from './PerspectiveCamera';
export { default as Camera } from './PerspectiveCamera.js'; export { default as Camera } from './PerspectiveCamera';
export { default as Group } from './Group.js'; export { default as Group } from './Group';
export { default as Scene } from './Scene.js'; export { default as Scene } from './Scene';
export { default as Object3D } from './Object3D.js'; export { default as Object3D } from './Object3D';
export { default as Raycaster } from './Raycaster.js'; export { default as Raycaster } from './Raycaster';

View File

@ -4,16 +4,16 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js' import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import usePointer, { IntersectObject, PointerConfigInterface, PointerInterface } from './usePointer' import usePointer, { IntersectObject, PointerConfigInterface, PointerInterface } from './usePointer'
export interface ConfigInterface { export interface ThreeConfigInterface {
canvas?: HTMLCanvasElement canvas?: HTMLCanvasElement
antialias: boolean antialias: boolean
alpha: boolean alpha: boolean
autoClear: boolean autoClear: boolean
orbitCtrl: boolean | Record<string, unknown> orbitCtrl: boolean | Record<string, unknown>
pointer: boolean | PointerConfigInterface pointer: boolean | PointerConfigInterface
resize: boolean | 'window' resize: boolean | string
width: number width?: number
height: number height?: number
[index:string]: any [index:string]: any
} }
@ -26,7 +26,7 @@ export interface SizeInterface {
} }
export interface ThreeInterface { export interface ThreeInterface {
conf: ConfigInterface conf: ThreeConfigInterface
renderer?: WebGLRenderer renderer?: WebGLRenderer
camera?: Camera camera?: Camera
cameraCtrl?: OrbitControls cameraCtrl?: OrbitControls
@ -34,7 +34,7 @@ export interface ThreeInterface {
pointer?: PointerInterface pointer?: PointerInterface
size: SizeInterface size: SizeInterface
composer?: EffectComposer composer?: EffectComposer
init(config: ConfigInterface): boolean init(config: ThreeConfigInterface): boolean
dispose(): void dispose(): void
render(): void render(): void
renderC(): void renderC(): void
@ -51,7 +51,7 @@ export interface ThreeInterface {
*/ */
export default function useThree(): ThreeInterface { export default function useThree(): ThreeInterface {
// default conf // default conf
const conf: ConfigInterface = { const conf: ThreeConfigInterface = {
antialias: true, antialias: true,
alpha: false, alpha: false,
autoClear: true, autoClear: true,
@ -98,7 +98,7 @@ export default function useThree(): ThreeInterface {
/** /**
* init three * init three
*/ */
function init(params: ConfigInterface) { function init(params: ThreeConfigInterface) {
if (params) { if (params) {
Object.entries(params).forEach(([key, value]) => { Object.entries(params).forEach(([key, value]) => {
conf[key] = value conf[key] = value
@ -122,7 +122,7 @@ export default function useThree(): ThreeInterface {
onResize() onResize()
window.addEventListener('resize', onResize) window.addEventListener('resize', onResize)
} else { } else {
setSize(conf.width, conf.height) setSize(conf.width!, conf.height!)
} }
initPointer() initPointer()

View File

@ -1,5 +1,5 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import Object3D from '../core/Object3D.js'; import Object3D from '../core/Object3D';
import { bindProp, setFromProp } from '../tools'; import { bindProp, setFromProp } from '../tools';
export default defineComponent({ export default defineComponent({

View File

@ -1,7 +0,0 @@
import { createApp } from 'vue';
import { TroisJSVuePlugin } from './plugin.js';
import App from './App.vue';
const app = createApp(App);
app.use(TroisJSVuePlugin);
app.mount('#app');

7
src/main.ts Normal file
View File

@ -0,0 +1,7 @@
import { createApp } from 'vue'
import { TroisJSVuePlugin } from './plugin'
import App from './App.vue'
const app = createApp(App)
app.use(TroisJSVuePlugin)
app.mount('#app')

View File

@ -1,6 +1,6 @@
import { defineComponent, watch } from 'vue'; import { defineComponent, watch } from 'vue';
import { Mesh as TMesh } from 'three'; import { Mesh as TMesh } from 'three';
import Object3D from '../core/Object3D.js'; import Object3D from '../core/Object3D';
import { bindProp } from '../tools'; import { bindProp } from '../tools';
export const pointerProps = { export const pointerProps = {

View File

@ -1,6 +1,6 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Sprite, SpriteMaterial, TextureLoader } from 'three'; import { Sprite, SpriteMaterial, TextureLoader } from 'three';
import Object3D from '../core/Object3D.js'; import Object3D from '../core/Object3D';
export default defineComponent({ export default defineComponent({
extends: Object3D, extends: Object3D,

View File

@ -1,5 +1,5 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Object3D from '../core/Object3D.js'; import Object3D from '../core/Object3D';
export default defineComponent({ export default defineComponent({
extends: Object3D, extends: Object3D,

View File

@ -1,81 +0,0 @@
import { toRef, watch } from 'vue';
export function setFromProp(o, prop) {
if (prop instanceof Object) {
Object.entries(prop).forEach(([key, value]) => {
o[key] = value;
});
}
};
export function bindProps(src, props, dst) {
props.forEach(prop => {
bindProp(src, prop, dst);
});
};
export function bindProp(src, srcProp, dst, dstProp) {
if (!dstProp) dstProp = srcProp;
const ref = toRef(src, srcProp);
if (ref.value instanceof Object) {
setFromProp(dst[dstProp], ref.value);
watch(ref, (value) => { setFromProp(dst[dstProp], value); }, { deep: true });
} else {
if (ref.value) dst[dstProp] = src[srcProp];
watch(ref, (value) => { dst[dstProp] = value; });
}
};
export function propsValues(props, exclude) {
const values = {};
Object.entries(props).forEach(([key, value]) => {
if (!exclude || (exclude && !exclude.includes(key))) {
values[key] = value;
}
});
return values;
};
export function lerp(value1, value2, amount) {
amount = amount < 0 ? 0 : amount;
amount = amount > 1 ? 1 : amount;
return value1 + (value2 - value1) * amount;
};
export function lerpv2(v1, v2, amount) {
v1.x = lerp(v1.x, v2.x, amount);
v1.y = lerp(v1.y, v2.y, amount);
};
export 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);
};
export 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';
export 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 '';
}
}

84
src/tools.ts Normal file
View File

@ -0,0 +1,84 @@
import { Vector2, Vector3 } from 'three'
import { toRef, watch } from 'vue'
export function setFromProp(o: Record<string, unknown>, prop: Record<string, unknown>): void {
if (prop instanceof Object) {
Object.entries(prop).forEach(([key, value]) => {
o[key] = value
})
}
}
export function bindProps(src: any, props: string[], dst: string): void {
props.forEach(prop => {
bindProp(src, prop, dst, prop)
})
}
export function bindProp(src: any, srcProp: string, dst: any, dstProp: string): void {
if (!dstProp) dstProp = srcProp
const ref = toRef(src, srcProp)
if (ref.value instanceof Object) {
setFromProp(dst[dstProp], ref.value)
watch(ref, (value) => { setFromProp(dst[dstProp], value) }, { deep: true })
} else {
if (ref.value) dst[dstProp] = src[srcProp]
watch(ref, (value) => { dst[dstProp] = value })
}
}
export function propsValues(props: Record<string, unknown>, exclude: string[]): Record<string, unknown> {
const values: Record<string, unknown> = {}
Object.entries(props).forEach(([key, value]) => {
if (!exclude || (exclude && !exclude.includes(key))) {
values[key] = value
}
})
return values
}
export function lerp(value1: number, value2: number, amount: number): number {
amount = amount < 0 ? 0 : amount
amount = amount > 1 ? 1 : amount
return value1 + (value2 - value1) * amount
}
// TODO : remove
export function lerpv2(v1: Vector2, v2: Vector2, amount: number): void {
v1.x = lerp(v1.x, v2.x, amount)
v1.y = lerp(v1.y, v2.y, amount)
}
// TODO : remove
export function lerpv3(v1: Vector3, v2: Vector3, amount: number): void {
v1.x = lerp(v1.x, v2.x, amount)
v1.y = lerp(v1.y, v2.y, amount)
v1.z = lerp(v1.z, v2.z, amount)
}
export function limit(val: number, min: number, max: number): number {
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'
export function getMatcapUrl(hash: string, format = 1024): string {
const fileName = `${hash}${getMatcapFormatString(format)}.png`
return `${MATCAP_ROOT}/${format}/${fileName}`
}
function getMatcapFormatString(format: number) {
switch (format) {
case 64:
return '-64px'
case 128:
return '-128px'
case 256:
return '-256px'
case 512:
return '-512px'
default:
return ''
}
}