mirror of
https://github.com/troisjs/trois.git
synced 2024-11-23 20:02:32 +08:00
wip
This commit is contained in:
parent
154fd7439c
commit
d2006b1f2c
@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineComponent, watch } from 'vue';
|
||||
import { DoubleSide, Mesh, MeshStandardMaterial, PlaneGeometry } from 'three';
|
||||
import Object3D from '../../core/Object3D.js';
|
||||
import Object3D from '../../core/Object3D';
|
||||
import { bindProps } from '../../tools';
|
||||
import LiquidEffect from './LiquidEffect.js';
|
||||
|
||||
@ -17,9 +17,9 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
this.liquidEffect = new LiquidEffect(this.three.renderer);
|
||||
this.rendererComponent.onMounted(() => {
|
||||
this.liquidEffect.renderer = this.rendererComponent.renderer;
|
||||
this.rendererComponent.onBeforeRender(this.update);
|
||||
this.renderer.onMounted(() => {
|
||||
this.liquidEffect.renderer = this.renderer.renderer;
|
||||
this.renderer.onBeforeRender(this.update);
|
||||
});
|
||||
|
||||
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);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.update);
|
||||
this.renderer.offBeforeRender(this.update);
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
|
@ -21,11 +21,11 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
this.initGem();
|
||||
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT);
|
||||
else this.rendererComponent.onMounted(this.updateCubeRT);
|
||||
if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
|
||||
else this.renderer.onMounted(this.updateCubeRT);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateCubeRT);
|
||||
this.renderer.offBeforeRender(this.updateCubeRT);
|
||||
if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
|
||||
if (this.meshBack) this.removeFromParent(this.meshBack);
|
||||
if (this.materialBack) this.materialBack.dispose();
|
||||
|
@ -17,11 +17,11 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
this.initMirrorMesh();
|
||||
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT);
|
||||
else this.rendererComponent.onMounted(this.updateCubeRT);
|
||||
if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
|
||||
else this.renderer.onMounted(this.updateCubeRT);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateCubeRT);
|
||||
this.renderer.offBeforeRender(this.updateCubeRT);
|
||||
if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
|
||||
},
|
||||
methods: {
|
||||
|
@ -20,11 +20,11 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
this.initMirrorMesh();
|
||||
if (this.autoUpdate) this.rendererComponent.onBeforeRender(this.updateCubeRT);
|
||||
else this.rendererComponent.onMounted(this.updateCubeRT);
|
||||
if (this.autoUpdate) this.renderer.onBeforeRender(this.updateCubeRT);
|
||||
else this.renderer.onMounted(this.updateCubeRT);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateCubeRT);
|
||||
this.renderer.offBeforeRender(this.updateCubeRT);
|
||||
if (this.cubeCamera) this.removeFromParent(this.cubeCamera);
|
||||
},
|
||||
methods: {
|
||||
|
@ -5,7 +5,7 @@ export default {
|
||||
noSetup: { type: Boolean, default: false },
|
||||
},
|
||||
emits: ['created'],
|
||||
inject: ['rendererComponent'],
|
||||
inject: ['renderer'],
|
||||
setup({ noSetup }) {
|
||||
const stats = new Stats();
|
||||
if (!noSetup) {
|
||||
@ -16,8 +16,8 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
if (!this.noSetup) {
|
||||
this.rendererComponent.onBeforeRender(this.begin);
|
||||
this.rendererComponent.onAfterRender(this.end);
|
||||
this.renderer.onBeforeRender(this.begin);
|
||||
this.renderer.onAfterRender(this.end);
|
||||
}
|
||||
this.$emit('created', { stats: this.stats });
|
||||
},
|
||||
@ -37,8 +37,8 @@ export default {
|
||||
if (this.stats && this.stats.dom) {
|
||||
this.stats.dom.parentElement.removeChild(this.stats.dom);
|
||||
}
|
||||
this.rendererComponent.offBeforeRender(this.begin);
|
||||
this.rendererComponent.offAfterRender(this.end);
|
||||
this.renderer.offBeforeRender(this.begin);
|
||||
this.renderer.offAfterRender(this.end);
|
||||
},
|
||||
render() {
|
||||
return this.$slots.default ? this.$slots.default() : [];
|
||||
|
@ -29,10 +29,10 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
this.startTime = Date.now();
|
||||
this.rendererComponent.onBeforeRender(this.updateTime);
|
||||
this.renderer.onBeforeRender(this.updateTime);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateTime);
|
||||
this.renderer.offBeforeRender(this.updateTime);
|
||||
},
|
||||
methods: {
|
||||
createGeometry() {
|
||||
|
@ -30,10 +30,10 @@ export default defineComponent({
|
||||
watch(() => this.displacementScale, (value) => { this.material.displacementScale = value; });
|
||||
|
||||
this.startTime = Date.now();
|
||||
this.rendererComponent.onBeforeRender(this.update);
|
||||
this.renderer.onBeforeRender(this.update);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.update);
|
||||
this.renderer.offBeforeRender(this.update);
|
||||
this.fsQuad.dispose();
|
||||
this.dispRT.dispose();
|
||||
this.dispMat.dispose();
|
||||
|
@ -28,10 +28,10 @@ export default defineComponent({
|
||||
this.updateMaterial();
|
||||
|
||||
this.startTime = Date.now();
|
||||
this.rendererComponent.onBeforeRender(this.updateTime);
|
||||
this.renderer.onBeforeRender(this.updateTime);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateTime);
|
||||
this.renderer.offBeforeRender(this.updateTime);
|
||||
},
|
||||
methods: {
|
||||
updateMaterial() {
|
||||
|
@ -25,10 +25,10 @@ export default defineComponent({
|
||||
this.updateMaterial();
|
||||
|
||||
this.startTime = Date.now();
|
||||
this.rendererComponent.onBeforeRender(this.updateTime);
|
||||
this.renderer.onBeforeRender(this.updateTime);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.updateTime);
|
||||
this.renderer.offBeforeRender(this.updateTime);
|
||||
},
|
||||
methods: {
|
||||
updateMaterial() {
|
||||
|
@ -3,7 +3,7 @@ import useCannon from './useCannon.js';
|
||||
// import { bindProp } from '../../tools';
|
||||
|
||||
export default defineComponent({
|
||||
inject: ['three', 'scene', 'rendererComponent'],
|
||||
inject: ['three', 'scene', 'renderer'],
|
||||
props: {
|
||||
gravity: { type: Object, default: () => ({ x: 0, y: 0, z: -9.82 }) },
|
||||
broadphase: { type: String },
|
||||
@ -16,10 +16,10 @@ export default defineComponent({
|
||||
this.cannon = useCannon({ gravity: this.gravity, broadphase: this.broadphase });
|
||||
},
|
||||
mounted() {
|
||||
this.rendererComponent.onBeforeRender(this.step);
|
||||
this.renderer.onBeforeRender(this.step);
|
||||
},
|
||||
unmounted() {
|
||||
this.rendererComponent.offBeforeRender(this.step);
|
||||
this.renderer.offBeforeRender(this.step);
|
||||
},
|
||||
methods: {
|
||||
step() {
|
||||
|
@ -11,7 +11,7 @@ import { defineComponent } from 'vue';
|
||||
import { Object3D } from 'three';
|
||||
import { gsap, Power4 } from 'gsap';
|
||||
|
||||
import Camera from '../../core/PerspectiveCamera.js';
|
||||
import Camera from '../../core/PerspectiveCamera';
|
||||
import Renderer from '../../core/Renderer.js';
|
||||
import Scene from '../../core/Scene.js';
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { defineComponent } from 'vue';
|
||||
import { Vector2 } from 'three';
|
||||
import { gsap, Power4 } from 'gsap';
|
||||
|
||||
import OrthographicCamera from '../../core/OrthographicCamera.js';
|
||||
import OrthographicCamera from '../../core/OrthographicCamera';
|
||||
import Renderer from '../../core/Renderer.js';
|
||||
import Scene from '../../core/Scene.js';
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
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 Scene from '../../core/Scene.js';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { defineComponent } from 'vue';
|
||||
// import Object3D from '../core/Object3D.js';
|
||||
import { defineComponent } from 'vue'
|
||||
// import Object3D from '../core/Object3D';
|
||||
|
||||
export default defineComponent({
|
||||
// TODO: eventually extend Object3D, for now: error 'injection "scene" not found'
|
||||
@ -7,6 +7,6 @@ export default defineComponent({
|
||||
// extends: Object3D,
|
||||
inject: ['three'],
|
||||
render() {
|
||||
return this.$slots.default ? this.$slots.default() : [];
|
||||
return this.$slots.default ? this.$slots.default() : []
|
||||
},
|
||||
});
|
||||
})
|
@ -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
17
src/core/Group.ts
Normal 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',
|
||||
})
|
@ -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
71
src/core/Object3D.ts
Normal 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',
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
import { defineComponent, watch } from 'vue';
|
||||
import { OrthographicCamera } from 'three';
|
||||
import { bindProp } from '../tools';
|
||||
import Camera from './Camera.js';
|
||||
import Camera from './Camera';
|
||||
|
||||
export default defineComponent({
|
||||
extends: Camera,
|
@ -1,7 +1,7 @@
|
||||
import { defineComponent, watch } from 'vue';
|
||||
import { PerspectiveCamera } from 'three';
|
||||
import { bindProp } from '../tools';
|
||||
import Camera from './Camera.js';
|
||||
import Camera from './Camera';
|
||||
|
||||
export default defineComponent({
|
||||
extends: Camera,
|
@ -3,7 +3,7 @@ import usePointer from './usePointer';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Raycaster',
|
||||
inject: ['three', 'rendererComponent'],
|
||||
inject: ['three', 'renderer'],
|
||||
props: {
|
||||
onPointerEnter: { type: Function, default: () => {} },
|
||||
onPointerOver: { type: Function, default: () => {} },
|
||||
@ -13,7 +13,7 @@ export default defineComponent({
|
||||
intersectMode: { type: String, default: 'move' },
|
||||
},
|
||||
mounted() {
|
||||
this.rendererComponent.onMounted(() => {
|
||||
this.renderer.onMounted(() => {
|
||||
this.pointer = usePointer({
|
||||
camera: this.three.camera,
|
||||
domElement: this.three.renderer.domElement,
|
||||
@ -27,14 +27,14 @@ export default defineComponent({
|
||||
this.pointer.addListeners();
|
||||
|
||||
if (this.intersectMode === 'frame') {
|
||||
this.rendererComponent.onBeforeRender(this.pointer.intersect);
|
||||
this.renderer.onBeforeRender(this.pointer.intersect);
|
||||
}
|
||||
});
|
||||
},
|
||||
unmounted() {
|
||||
if (this.pointer) {
|
||||
this.pointer.removeListeners();
|
||||
this.rendererComponent.offBeforeRender(this.pointer.intersect);
|
||||
this.renderer.offBeforeRender(this.pointer.intersect);
|
||||
}
|
||||
},
|
||||
methods: {
|
@ -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
113
src/core/Renderer.ts
Normal 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',
|
||||
})
|
@ -1,8 +1,8 @@
|
||||
export { default as Renderer } from './Renderer.js';
|
||||
export { default as OrthographicCamera } from './OrthographicCamera.js';
|
||||
export { default as PerspectiveCamera } from './PerspectiveCamera.js';
|
||||
export { default as Camera } from './PerspectiveCamera.js';
|
||||
export { default as Group } from './Group.js';
|
||||
export { default as Scene } from './Scene.js';
|
||||
export { default as Object3D } from './Object3D.js';
|
||||
export { default as Raycaster } from './Raycaster.js';
|
||||
export { default as Renderer } from './Renderer';
|
||||
export { default as OrthographicCamera } from './OrthographicCamera';
|
||||
export { default as PerspectiveCamera } from './PerspectiveCamera';
|
||||
export { default as Camera } from './PerspectiveCamera';
|
||||
export { default as Group } from './Group';
|
||||
export { default as Scene } from './Scene';
|
||||
export { default as Object3D } from './Object3D';
|
||||
export { default as Raycaster } from './Raycaster';
|
||||
|
@ -4,16 +4,16 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
|
||||
import usePointer, { IntersectObject, PointerConfigInterface, PointerInterface } from './usePointer'
|
||||
|
||||
export interface ConfigInterface {
|
||||
export interface ThreeConfigInterface {
|
||||
canvas?: HTMLCanvasElement
|
||||
antialias: boolean
|
||||
alpha: boolean
|
||||
autoClear: boolean
|
||||
orbitCtrl: boolean | Record<string, unknown>
|
||||
pointer: boolean | PointerConfigInterface
|
||||
resize: boolean | 'window'
|
||||
width: number
|
||||
height: number
|
||||
resize: boolean | string
|
||||
width?: number
|
||||
height?: number
|
||||
[index:string]: any
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ export interface SizeInterface {
|
||||
}
|
||||
|
||||
export interface ThreeInterface {
|
||||
conf: ConfigInterface
|
||||
conf: ThreeConfigInterface
|
||||
renderer?: WebGLRenderer
|
||||
camera?: Camera
|
||||
cameraCtrl?: OrbitControls
|
||||
@ -34,7 +34,7 @@ export interface ThreeInterface {
|
||||
pointer?: PointerInterface
|
||||
size: SizeInterface
|
||||
composer?: EffectComposer
|
||||
init(config: ConfigInterface): boolean
|
||||
init(config: ThreeConfigInterface): boolean
|
||||
dispose(): void
|
||||
render(): void
|
||||
renderC(): void
|
||||
@ -51,7 +51,7 @@ export interface ThreeInterface {
|
||||
*/
|
||||
export default function useThree(): ThreeInterface {
|
||||
// default conf
|
||||
const conf: ConfigInterface = {
|
||||
const conf: ThreeConfigInterface = {
|
||||
antialias: true,
|
||||
alpha: false,
|
||||
autoClear: true,
|
||||
@ -98,7 +98,7 @@ export default function useThree(): ThreeInterface {
|
||||
/**
|
||||
* init three
|
||||
*/
|
||||
function init(params: ConfigInterface) {
|
||||
function init(params: ThreeConfigInterface) {
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
conf[key] = value
|
||||
@ -122,7 +122,7 @@ export default function useThree(): ThreeInterface {
|
||||
onResize()
|
||||
window.addEventListener('resize', onResize)
|
||||
} else {
|
||||
setSize(conf.width, conf.height)
|
||||
setSize(conf.width!, conf.height!)
|
||||
}
|
||||
|
||||
initPointer()
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { defineComponent, watch } from 'vue';
|
||||
import Object3D from '../core/Object3D.js';
|
||||
import Object3D from '../core/Object3D';
|
||||
import { bindProp, setFromProp } from '../tools';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -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
7
src/main.ts
Normal 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')
|
@ -1,6 +1,6 @@
|
||||
import { defineComponent, watch } from 'vue';
|
||||
import { Mesh as TMesh } from 'three';
|
||||
import Object3D from '../core/Object3D.js';
|
||||
import Object3D from '../core/Object3D';
|
||||
import { bindProp } from '../tools';
|
||||
|
||||
export const pointerProps = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { Sprite, SpriteMaterial, TextureLoader } from 'three';
|
||||
import Object3D from '../core/Object3D.js';
|
||||
import Object3D from '../core/Object3D';
|
||||
|
||||
export default defineComponent({
|
||||
extends: Object3D,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import Object3D from '../core/Object3D.js';
|
||||
import Object3D from '../core/Object3D';
|
||||
|
||||
export default defineComponent({
|
||||
extends: Object3D,
|
||||
|
81
src/tools.js
81
src/tools.js
@ -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
84
src/tools.ts
Normal 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 ''
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user