Add perspective toggles
This commit is contained in:
parent
0c65c9848e
commit
128bfde8c0
2 changed files with 73 additions and 19 deletions
2
TODO.md
2
TODO.md
|
|
@ -46,7 +46,7 @@ A step-by-step checklist for porting MatterControl's design features to a Vue +
|
||||||
- [x] Add "fit to selection" camera function
|
- [x] Add "fit to selection" camera function
|
||||||
- [x] Add "reset view" function (home position)
|
- [x] Add "reset view" function (home position)
|
||||||
- [x] Add view presets (front, back, top, bottom, left, right)
|
- [x] Add view presets (front, back, top, bottom, left, right)
|
||||||
- [ ] Add orthographic/perspective toggle
|
- [x] Add orthographic/perspective toggle
|
||||||
|
|
||||||
### Scene State Management
|
### Scene State Management
|
||||||
- [ ] Create `useScene` composable for scene state
|
- [ ] Create `useScene` composable for scene state
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ const props = withDefaults(
|
||||||
const containerRef = ref<HTMLDivElement | null>(null)
|
const containerRef = ref<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
let scene: THREE.Scene
|
let scene: THREE.Scene
|
||||||
let camera: THREE.PerspectiveCamera
|
let perspectiveCamera: THREE.PerspectiveCamera
|
||||||
|
let orthographicCamera: THREE.OrthographicCamera
|
||||||
|
let activeCamera: THREE.PerspectiveCamera | THREE.OrthographicCamera
|
||||||
|
let isOrthographic = false
|
||||||
let renderer: THREE.WebGLRenderer
|
let renderer: THREE.WebGLRenderer
|
||||||
let animationFrameId: number
|
let animationFrameId: number
|
||||||
let resizeObserver: ResizeObserver
|
let resizeObserver: ResizeObserver
|
||||||
|
|
@ -47,11 +50,27 @@ function initScene() {
|
||||||
scene = new THREE.Scene()
|
scene = new THREE.Scene()
|
||||||
scene.background = new THREE.Color(0x1a1a2e)
|
scene.background = new THREE.Color(0x1a1a2e)
|
||||||
|
|
||||||
// Create camera
|
// Create cameras
|
||||||
const { clientWidth: width, clientHeight: height } = containerRef.value
|
const { clientWidth: width, clientHeight: height } = containerRef.value
|
||||||
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
|
const aspect = width / height
|
||||||
camera.position.copy(HOME_POSITION)
|
|
||||||
camera.lookAt(HOME_TARGET)
|
perspectiveCamera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1000)
|
||||||
|
perspectiveCamera.position.copy(HOME_POSITION)
|
||||||
|
perspectiveCamera.lookAt(HOME_TARGET)
|
||||||
|
|
||||||
|
const frustumSize = 10
|
||||||
|
orthographicCamera = new THREE.OrthographicCamera(
|
||||||
|
(-frustumSize * aspect) / 2,
|
||||||
|
(frustumSize * aspect) / 2,
|
||||||
|
frustumSize / 2,
|
||||||
|
-frustumSize / 2,
|
||||||
|
0.1,
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
orthographicCamera.position.copy(HOME_POSITION)
|
||||||
|
orthographicCamera.lookAt(HOME_TARGET)
|
||||||
|
|
||||||
|
activeCamera = perspectiveCamera
|
||||||
|
|
||||||
// Create renderer
|
// Create renderer
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true })
|
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||||
|
|
@ -60,7 +79,7 @@ function initScene() {
|
||||||
containerRef.value.appendChild(renderer.domElement)
|
containerRef.value.appendChild(renderer.domElement)
|
||||||
|
|
||||||
// Setup orbit controls
|
// Setup orbit controls
|
||||||
controls = new OrbitControls(camera, renderer.domElement)
|
controls = new OrbitControls(activeCamera, renderer.domElement)
|
||||||
controls.enableDamping = true
|
controls.enableDamping = true
|
||||||
controls.dampingFactor = 0.05
|
controls.dampingFactor = 0.05
|
||||||
|
|
||||||
|
|
@ -139,8 +158,18 @@ function setupResizeObserver() {
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const { width, height } = entry.contentRect
|
const { width, height } = entry.contentRect
|
||||||
if (width > 0 && height > 0) {
|
if (width > 0 && height > 0) {
|
||||||
camera.aspect = width / height
|
const aspect = width / height
|
||||||
camera.updateProjectionMatrix()
|
|
||||||
|
perspectiveCamera.aspect = aspect
|
||||||
|
perspectiveCamera.updateProjectionMatrix()
|
||||||
|
|
||||||
|
const frustumSize = 10
|
||||||
|
orthographicCamera.left = (-frustumSize * aspect) / 2
|
||||||
|
orthographicCamera.right = (frustumSize * aspect) / 2
|
||||||
|
orthographicCamera.top = frustumSize / 2
|
||||||
|
orthographicCamera.bottom = -frustumSize / 2
|
||||||
|
orthographicCamera.updateProjectionMatrix()
|
||||||
|
|
||||||
renderer.setSize(width, height)
|
renderer.setSize(width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +181,7 @@ function setupResizeObserver() {
|
||||||
function animate() {
|
function animate() {
|
||||||
animationFrameId = requestAnimationFrame(animate)
|
animationFrameId = requestAnimationFrame(animate)
|
||||||
controls.update()
|
controls.update()
|
||||||
renderer.render(scene, camera)
|
renderer.render(scene, activeCamera)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fitToSelection(objects: THREE.Object3D[]) {
|
function fitToSelection(objects: THREE.Object3D[]) {
|
||||||
|
|
@ -166,31 +195,54 @@ function fitToSelection(objects: THREE.Object3D[]) {
|
||||||
const center = box.getCenter(new THREE.Vector3())
|
const center = box.getCenter(new THREE.Vector3())
|
||||||
const size = box.getSize(new THREE.Vector3())
|
const size = box.getSize(new THREE.Vector3())
|
||||||
const maxDim = Math.max(size.x, size.y, size.z)
|
const maxDim = Math.max(size.x, size.y, size.z)
|
||||||
const fov = camera.fov * (Math.PI / 180)
|
|
||||||
const distance = maxDim / (2 * Math.tan(fov / 2)) * 1.5
|
|
||||||
|
|
||||||
const direction = camera.position.clone().sub(controls.target).normalize()
|
const fov = perspectiveCamera.fov * (Math.PI / 180)
|
||||||
camera.position.copy(center).add(direction.multiplyScalar(distance))
|
const distance = (maxDim / (2 * Math.tan(fov / 2))) * 1.5
|
||||||
|
|
||||||
|
const direction = activeCamera.position.clone().sub(controls.target).normalize()
|
||||||
|
activeCamera.position.copy(center).add(direction.multiplyScalar(distance))
|
||||||
controls.target.copy(center)
|
controls.target.copy(center)
|
||||||
controls.update()
|
controls.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetView() {
|
function resetView() {
|
||||||
camera.position.copy(HOME_POSITION)
|
activeCamera.position.copy(HOME_POSITION)
|
||||||
controls.target.copy(HOME_TARGET)
|
controls.target.copy(HOME_TARGET)
|
||||||
controls.update()
|
controls.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
function setViewPreset(preset: ViewPreset) {
|
function setViewPreset(preset: ViewPreset) {
|
||||||
const position = VIEW_POSITIONS[preset]
|
const position = VIEW_POSITIONS[preset]
|
||||||
camera.position.copy(position)
|
activeCamera.position.copy(position)
|
||||||
controls.target.set(0, 0, 0)
|
controls.target.set(0, 0, 0)
|
||||||
camera.up.set(0, 1, 0)
|
activeCamera.up.set(0, 1, 0)
|
||||||
if (preset === 'top') camera.up.set(0, 0, -1)
|
if (preset === 'top') activeCamera.up.set(0, 0, -1)
|
||||||
if (preset === 'bottom') camera.up.set(0, 0, 1)
|
if (preset === 'bottom') activeCamera.up.set(0, 0, 1)
|
||||||
controls.update()
|
controls.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOrthographic(ortho: boolean) {
|
||||||
|
if (isOrthographic === ortho) return
|
||||||
|
|
||||||
|
const newCamera = ortho ? orthographicCamera : perspectiveCamera
|
||||||
|
|
||||||
|
// Copy position and rotation from current camera
|
||||||
|
newCamera.position.copy(activeCamera.position)
|
||||||
|
newCamera.up.copy(activeCamera.up)
|
||||||
|
newCamera.lookAt(controls.target)
|
||||||
|
|
||||||
|
activeCamera = newCamera
|
||||||
|
isOrthographic = ortho
|
||||||
|
|
||||||
|
// Update controls to use new camera
|
||||||
|
controls.object = activeCamera
|
||||||
|
controls.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleProjection() {
|
||||||
|
setOrthographic(!isOrthographic)
|
||||||
|
}
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
if (animationFrameId) {
|
if (animationFrameId) {
|
||||||
cancelAnimationFrame(animationFrameId)
|
cancelAnimationFrame(animationFrameId)
|
||||||
|
|
@ -222,6 +274,8 @@ defineExpose({
|
||||||
fitToSelection,
|
fitToSelection,
|
||||||
resetView,
|
resetView,
|
||||||
setViewPreset,
|
setViewPreset,
|
||||||
|
setOrthographic,
|
||||||
|
toggleProjection,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue