Initial commit
This commit is contained in:
161
src/views/ExperienceView.vue
Normal file
161
src/views/ExperienceView.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<!-- 3DModelViewer.vue -->
|
||||
<template>
|
||||
<div ref="containerRef" class="three-container"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
|
||||
|
||||
const props = defineProps({
|
||||
modelUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
// Beispiel: '/models/dein-modell.glb' oder '/models/dein-modell.gltf'
|
||||
},
|
||||
// Optional – falls du die Decoder-Dateien woanders hast
|
||||
dracoPath: {
|
||||
type: String,
|
||||
default: '/draco/' // ← WICHTIG: Passe den Pfad an!
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
const containerRef = ref(null)
|
||||
let scene, camera, renderer, controls, model
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
animate()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
dispose()
|
||||
})
|
||||
|
||||
watch(() => props.modelUrl, (newUrl) => {
|
||||
if (newUrl) loadModel(newUrl)
|
||||
})
|
||||
|
||||
function init() {
|
||||
if (!containerRef.value) return
|
||||
|
||||
// Scene
|
||||
scene = new THREE.Scene()
|
||||
scene.background = new THREE.Color(0x1a1a2e)
|
||||
|
||||
// Camera
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
50,
|
||||
containerRef.value.clientWidth / containerRef.value.clientHeight,
|
||||
0.1,
|
||||
1000
|
||||
)
|
||||
camera.position.set(0, 1.5, 4)
|
||||
|
||||
// Renderer
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
||||
renderer.setPixelRatio(window.devicePixelRatio)
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace
|
||||
containerRef.value.appendChild(renderer.domElement)
|
||||
|
||||
// Controls
|
||||
controls = new OrbitControls(camera, renderer.domElement)
|
||||
controls.enableDamping = true
|
||||
controls.dampingFactor = 0.05
|
||||
controls.minDistance = 1
|
||||
controls.maxDistance = 50
|
||||
|
||||
// Licht
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.8)
|
||||
scene.add(ambient)
|
||||
|
||||
const directional = new THREE.DirectionalLight(0xffffff, 1.2)
|
||||
directional.position.set(5, 8, 4)
|
||||
scene.add(directional)
|
||||
|
||||
window.addEventListener('resize', onWindowResize)
|
||||
|
||||
loadModel(props.modelUrl)
|
||||
}
|
||||
|
||||
function loadModel(url) {
|
||||
if (model) {
|
||||
scene.remove(model)
|
||||
}
|
||||
|
||||
const dracoLoader = new DRACOLoader()
|
||||
dracoLoader.setDecoderPath(props.dracoPath)
|
||||
|
||||
const loader = new GLTFLoader()
|
||||
loader.setDRACOLoader(dracoLoader)
|
||||
|
||||
loader.load(
|
||||
url,
|
||||
(gltf) => {
|
||||
model = gltf.scene
|
||||
|
||||
const box = new THREE.Box3().setFromObject(model)
|
||||
const center = box.getCenter(new THREE.Vector3())
|
||||
const size = box.getSize(new THREE.Vector3())
|
||||
|
||||
model.position.sub(center)
|
||||
const maxDim = Math.max(size.x, size.y, size.z)
|
||||
const scale = 2 / maxDim
|
||||
model.scale.multiplyScalar(scale)
|
||||
|
||||
scene.add(model)
|
||||
|
||||
document.title = 'Model geladen' // Fertigmeldung im Tab
|
||||
},
|
||||
(progress) => {
|
||||
const percent = (progress.loaded / progress.total * 100).toFixed(1)
|
||||
console.log(`Loading: ${percent}%`)
|
||||
document.title = `${percent}%`
|
||||
},
|
||||
(error) => {
|
||||
console.error('Fehler beim Laden des Modells:', error)
|
||||
document.title = 'Fehler beim Laden!'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
if (!containerRef.value || !camera || !renderer) return
|
||||
|
||||
camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight
|
||||
camera.updateProjectionMatrix()
|
||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate)
|
||||
if (controls) controls.update()
|
||||
if (renderer && scene && camera) renderer.render(scene, camera)
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
window.removeEventListener('resize', onWindowResize)
|
||||
|
||||
if (renderer) {
|
||||
renderer.dispose()
|
||||
containerRef.value?.removeChild(renderer.domElement)
|
||||
}
|
||||
|
||||
if (controls) controls.dispose()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.three-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
background: #0f0f1a;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user