Initial commit

This commit is contained in:
T-A-H-prog
2026-03-04 22:05:06 +01:00
commit 8bbf7a4c2b
42 changed files with 2864 additions and 0 deletions

View 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>