提交 45b0241f 编写于 作者: S skyun

Auto Commit

上级 e8ea12b9
import { onMounted } from 'vue' import { ref, reactive, toRef, watch, onMounted } from 'vue'
import * as THREE from 'three' import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js' import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as TT from './threejs-tools' import * as TT from './threejs-tools'
export default class ThreejsManager { export default class ThreejsManager {
constructor(container) { constructor() {
// init scene camera this.animateFuncs = {}
this.lights = reactive([])
}
addScene() {
this.scene = new THREE.Scene() this.scene = new THREE.Scene()
}
// init renderer addRenderer() {
this.renderer = new THREE.WebGLRenderer() this.renderer = new THREE.WebGLRenderer()
}
this.animateFuncs = {} startRender(container) {
const animate = () => { this.renderer.rendering = true
requestAnimationFrame(animate) container.value.appendChild(this.renderer.domElement)
Object.values(this.animateFuncs).forEach(animFunc => animFunc()) this.renderer.setSize(container.value.clientWidth, container.value.clientHeight)
this.renderer.render(this.scene, this.camera) this.camera = new THREE.PerspectiveCamera(60, container.value.clientWidth / container.value.clientHeight, 0.1, 1000)
this.camera.position.set(0, 10, 10)
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.animate()
}
animate() {
if (!this.renderer.rendering) {
return
} }
requestAnimationFrame(this.animate.bind(this))
onMounted(() => { Object.values(this.animateFuncs).forEach(animFunc => animFunc())
container.value.appendChild(this.renderer.domElement) this.renderer.render(this.scene, this.camera)
this.renderer.setSize(container.value.clientWidth, container.value.clientHeight)
this.camera = new THREE.PerspectiveCamera(60, container.value.clientWidth / container.value.clientHeight, 0.1, 1000)
this.camera.position.set(0, 10, 10)
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
animate()
})
this.initDefaultScene()
} }
initDefaultScene() { initDefaultScene() {
this.scene.add(new THREE.GridHelper(100, 100, 0x660000, 0x004400)) this.scene.add(new THREE.GridHelper(100, 100, 0x660000, 0x004400))
this.addPlane() this.addPlane()
this.addCube() this.addCube()
this.lightHelpers = [] this.addAmbientLight()
this.lightHelpers.push(this.addAmbientLight())
this.lightHelpers.push(this.addDirectLight())
this.lightHelpers.push(this.addPointLight())
this.lightHelpers.push(this.addSpotLight())
} }
onAnimate(name, animFunc) { onAnimate(name, animFunc) {
this.animateFuncs[name] = animFunc this.animateFuncs[name] = animFunc
...@@ -61,15 +58,31 @@ export default class ThreejsManager { ...@@ -61,15 +58,31 @@ export default class ThreejsManager {
return cube return cube
} }
addAmbientLight() { addAmbientLight() {
return TT.addLight(this.scene, THREE.AmbientLight, [0xffffff, .1]) const light = TT.createLight(THREE.AmbientLight, [0xffffff, .1])
this.scene.add(light)
this.lights.push(light)
light.name = light.type + `-` + this.lights.length
return light
} }
addDirectLight() { addDirectLight() {
return TT.addLight(this.scene, THREE.DirectionalLight, [0xff0000, 1], [-4, 5, -3]) const light = TT.createLight(THREE.DirectionalLight, [0xff0000, 1], [-4, 5, -3])
this.scene.add(light)
this.lights.push(light)
light.name = light.type + `-` + this.lights.length
return light
} }
addPointLight() { addPointLight() {
return TT.addLight(this.scene, THREE.PointLight, [0x00ff00, 50], [3, 3, -2], .5) const light = TT.createLight(THREE.PointLight, [0x00ff00, 50, 10], [3, 3, -2])
this.scene.add(light)
this.lights.push(light)
light.name = light.type + `-` + this.lights.length
return light
} }
addSpotLight() { addSpotLight() {
return TT.addLight(this.scene, THREE.SpotLight, [0x0000ff, 1000, 7, .2, .5], [-1, 6, 1]) const light = TT.createLight(THREE.SpotLight, [0x0000ff, 1000, 7, .2, .5], [-1, 6, 1])
this.scene.add(light)
this.lights.push(light)
light.name = light.type + `-` + this.lights.length
return light
} }
} }
...@@ -20,25 +20,22 @@ export function createCube() { ...@@ -20,25 +20,22 @@ export function createCube() {
return cube return cube
} }
export function createLight(lightConstuctor, lightArgs = [0xffffff, 1], lightPos = [0, 0, 1], ...helperArgs) { export function createLight(lightConstuctor, lightArgs = [0xffffff, 1], lightPos = [0, 0, 1]) {
const light = new lightConstuctor(...lightArgs) const light = new lightConstuctor(...lightArgs)
light.position.set(...lightPos) light.position.set(...lightPos)
return light
}
export function createLightHelper(light, ...helperArgs) {
let helperType let helperType
if (lightConstuctor === THREE.AmbientLight) { if (light.type === 'AmbientLight') {
} else if (lightConstuctor === THREE.DirectionalLight) { } else if (light.type === 'DirectionalLight') {
helperType = THREE.DirectionalLightHelper helperType = THREE.DirectionalLightHelper
} else if (lightConstuctor === THREE.PointLight) { } else if (light.type === 'PointLight') {
helperType = THREE.PointLightHelper helperType = THREE.PointLightHelper
} else if (lightConstuctor === THREE.SpotLight) { } else if (light.type === 'SpotLight') {
helperType = THREE.SpotLightHelper helperType = THREE.SpotLightHelper
} }
const helper = helperType && new helperType(light, ...helperArgs) || {light, isMockHelper: true} const helper = !!helperType && new helperType(light, ...helperArgs) || null
return helper return helper
} }
export function addLight(scene, ...createArgs) {
const helper = createLight(...createArgs)
scene.add(helper.light)
helper.isMockHelper || scene.add(helper)
return helper
}
\ No newline at end of file
<template> <template>
<div class="options"> <div class="options">
<h1>Light Options {{test}}</h1> <h2>灯光选项</h2>
<div v-for="l in lights" class="light-option flex-col flex-align-start"> <div v-for="l in props.lights" class="light-option flex-col flex-align-start">
<Checkbox :label="l.name" v-model="l.visible">{{l.type}}</Checkbox> <Checkbox class="check-box"
:label="l.name"
size="small"
@on-change="refresh"
v-model="l.visible">{{l.name}}</Checkbox>
<div class="match-width flex-row"> <div class="match-width flex-row">
<label class="mr10px">强度</label> <span class="lable">位置</span>
<Slider class="flex-1" v-model="l.intensity" :step=l._intensity*.01 :max=l._intensity*10></Slider> <Input size="small"
@on-keydown="onPositionKeyPress($event, l)"
style="width: 50px;" />
</div> </div>
<div class="match-width flex-row">
<span class="lable">光照强度: {{l.intensity}}</span>
<Slider class="flex-1"
@on-change="saveOptions"
:step=l._intensity*.01
:max=l._intensity*10
v-model="l.intensity" />
</div>
<template v-if="['PointLight', 'SpotLight'].includes(l.type)">
<div class="match-width flex-row">
<span class="lable">照射距离: {{l.distance}}</span>
<Slider class="flex-1"
@on-input="refresh"
@on-change="saveOptions"
:step=l._distance*.01
:max=l._distance*3
v-model="l.distance" />
</div>
<div class="match-width flex-row">
<span class="lable">距离衰减: {{l.decay}}</span>
<Slider class="flex-1"
@on-input="refresh"
@on-change="saveOptions"
:step=0.1
:max=10
v-model="l.decay" />
</div>
</template>
<template v-if="['SpotLight'].includes(l.type)">
<div class="match-width flex-row">
<span class="lable">散射角度: {{(l.angle * 180 / Math.PI).toFixed(0)}}</span>
<Slider class="flex-1"
@on-input="refresh"
@on-change="saveOptions"
:step=0.01
:max=Math.PI/2
v-model="l.angle" />
</div>
<div class="match-width flex-row">
<span class="lable">散射衰减: {{l.penumbra}}</span>
<Slider class="flex-1"
@on-input="refresh"
@on-change="saveOptions"
:step=0.01
:max=1
v-model="l.penumbra" />
</div>
</template>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, toRef, watch, onMounted, defineProps } from 'vue' import { ref, shallowRef, reactive, toRef, toRefs, watch, onMounted, defineProps } from 'vue'
import * as TT from '../threejs-utils/threejs-tools'
function onPositionKeyPress(ev, light) {
console.log(ev.type, ev.key, ev.altKey)
const step = .5
if (ev.altKey && ev.key === `ArrowUp`) {
light.position.z -= step
} else if (ev.altKey && ev.key === `ArrowDown`) {
light.position.z += step
} else if (ev.key === `ArrowLeft`) {
light.position.x -= step
} else if (ev.key === `ArrowRight`) {
light.position.x += step
} else if (ev.key === `ArrowUp`) {
light.position.y -= step
} else if (ev.key === `ArrowDown`) {
light.position.y += step
} else if (ev.key === `ArrowRight`) {
}
}
const props = defineProps({ const props = defineProps({
lights:{ scene: { type: Object },
type: Array, lights: { type: Array },
},
})
props.lights.forEach(l => {
l._intensity = l.intensity
}) })
const lights = reactive(props.lights)
// helper逻辑
let helpers = []
watch(toRef(props.lights, `length`), (newVal, oldVal) => {
props.lights.forEach(l => {
l._intensity = l.intensity
l._distance = l.distance
})
helpers.forEach(h => {
h.dispose()
props.scene.remove(h)
})
helpers = props.lights
.filter(light => light.type !== `AmbientLight`)
.map(light => TT.createLightHelper(light))
helpers.forEach(helper => {
props.scene.add(helper)
})
}, {immediate: true, deep: false})
function refresh() {
helpers.forEach(helper => {
helper.visible = helper.light.visible
helper.update()
})
}
// load/save options
function loadOptions() {
const options = JSON.parse(localStorage.getItem(`light-options`))
options.forEach((opt, i) => {
Object.keys(opt).forEach(key => {
props.lights[i][key] = opt[key]
})
})
}
function saveOptions() {
const options = props.lights.map(l => {
return [`visible`, `intensity`, `distance`, `decay`, `angle`, `penumbra`].reduce((res, attr) => {
res[attr] = l[attr]
return res
}, {})
})
localStorage.setItem(`light-options`, JSON.stringify(options))
}
</script> </script>
<style scoped> <style scoped>
.options { .options {
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 40px 20px; padding: 10px;
align-items: flex-start; align-items: flex-start;
overflow-y: scroll;
} }
.light-option { .light-option {
width: 100%; width: 100%;
margin-top: 20px;
display: flex; display: flex;
} }
.check-box {
width: 100%;
margin: 8px 0;
padding: 2px 5px;
background: #223;
}
.lable {
width: 120px;
margin-left: 10px;
}
</style> </style>
\ No newline at end of file
<template> <template>
<div id="root" class="flex-row"> <div id="root" class="flex-row">
<LightOption class="options" :lights="tm.lightHelpers.map(helper => helper.light)" /> <LightOption class="options" :scene="tm.scene" :lights="tm.lights" />
<div id="container" ref="container"></div> <div id="container" ref="container"></div>
{{tm.lightHelpers[1].light.intensity}}
</div> </div>
</template> </template>
...@@ -14,6 +13,17 @@ import LightOption from './light-option.vue' ...@@ -14,6 +13,17 @@ import LightOption from './light-option.vue'
const container = ref(null) const container = ref(null)
const tm = new ThreeManager(container) const tm = new ThreeManager(container)
tm.addScene()
tm.addRenderer()
onMounted(() => {
tm.startRender(container)
})
tm.initDefaultScene()
tm.addDirectLight()
tm.addPointLight()
tm.addSpotLight()
</script> </script>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册