demo-int-table/front/src/components/Promo/load_models.vue

164 lines
5.7 KiB
Vue

<script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { Box3, Color, DoubleSide, Group, Mesh, MeshBasicMaterial, PlaneGeometry, Sprite, SpriteMaterial, TextureLoader, Vector2, Vector3 } from 'three';
import { useTresContext, useSeek, useRenderLoop } from '@tresjs/core';
import { useGLTF } from '@tresjs/cientos'
import Env from './env.vue'
import { IMAGE_URL, SERVER_URL, } from '../../constants'
import { usePromoSidebar } from '../../stores/promo_sidebar';
import { usePromoScene } from '../../stores/promo_scene';
const props = defineProps(['source', 'loaded', 'loaded_pan'])
function shadows_and_pos(scene: any) {
scene.children.forEach((el: any) => {
el.receiveShadow = true
el.castShadow = true
shadows_and_pos(el)
})
}
const models = ref<model3DType[]>([])
const clickable = ref<clickableAreaType[]>([])
const clickable_items = ref<any[]>([])
const sidebar = usePromoSidebar();
const sidebar_scene = usePromoScene()
const { controls, camera, scene } = useTresContext()
const { seekByName } = useSeek()
const envVars = reactive({}) as { hdr_gainmap?: string, hdr_json?: string, hdr_webp?: string }
const loadModels = async () => {
const res = await fetch(`${SERVER_URL}/api/obj/scene/${props.source}`)
const raw_data = await res.json() as scene3D
envVars.hdr_gainmap = raw_data.hdr_gainmap ? `${IMAGE_URL}/${raw_data.hdr_gainmap}` : undefined
envVars.hdr_json = raw_data.hdr_json ? `${IMAGE_URL}/${raw_data.hdr_json}` : undefined
envVars.hdr_webp = raw_data.hdr_webp ? `${IMAGE_URL}/${raw_data.hdr_webp}` : undefined
const data = raw_data.elements
if (!controls.value) return;
controls.value.enabled = false;
(controls.value as any).minDistance = raw_data.min_distance;
(controls.value as any).maxDistance = raw_data.max_distance;
(controls.value as any)._needsUpdate = true;
(controls.value as any).update(1)
camera.value?.position.set(1, 1, 1);
camera.value?.lookAt(new Vector3(1, 1, 1));
const sidebar_items = []
for (let index = 0; index < data.length; index++) {
const element = data[index];
sidebar_items.push({ ...element, is_enabled: true })
const item = {} as model3DType
item.modelUrl = `${IMAGE_URL}/${element.model_file}`
let { scene: loaded_scene } = await useGLTF(item.modelUrl)
shadows_and_pos(loaded_scene)
item.modelFile = loaded_scene
item.name = element.name
models.value.push(item)
const res = await fetch(`${SERVER_URL}/api/obj/clickable/?source=${element.id}`)
const clickable_areas = await res.json()
clickable.value.push(...clickable_areas)
}
sidebar_scene.setData(sidebar_items)
sidebar.open()
for (let index = 0; index < clickable.value.length; index++) {
const element = clickable.value[index];
const find_element = seekByName(scene.value, element.object_name)
if (!find_element) continue
// const res_array = (find_element as Group).isGroup ? find_element?.children : [find_element]
if (find_element && !(find_element as Group).isGroup) {
const world_position = new Vector3();
((find_element as Mesh).geometry.boundingBox as any).getCenter(world_position);
(find_element as Mesh).localToWorld(world_position);
const p = raw_data.min_distance * 0.05
const plane = new PlaneGeometry(p, p)
const mesh_material = new MeshBasicMaterial({side: DoubleSide})
if(element.image) {
const map = new TextureLoader().load(`${IMAGE_URL}/${element.image}`);
mesh_material.map = map
} else {
mesh_material.color = new Color('red')
}
const point = new Mesh(plane, mesh_material);
point.position.set(world_position.x, p * 3, world_position.z)
point.name = `${element.id}_clickable`
point.renderOrder = 1
clickable_items.value.push(point)
}
}
const loaded = seekByName(scene.value, 'loaded')
if (loaded) {
const box = new Box3();
box.expandByObject(loaded);
const box_size = new Vector3();
box.getSize(box_size)
props.loaded_pan(
new Vector3(box_size.x * 0.5, box_size.y * 0.5, box_size.z * 0.5),
new Vector3(box_size.x * -0.5, box_size.y * -0.5, box_size.z * -0.5),
)
}
controls.value.enabled = true;
props.loaded()
}
const openSidebar = (id: number) => {
const target = clickable.value.find(el => el.id == id)
if (!target) return
const sidebar_data = {
title: target.name,
description: target.description
} as PromoSidebarData
if (target?.target) {
sidebar_data.target = target.target.toString()
sidebar_data.target_name = target.target_name
}
sidebar.setData(sidebar_data)
sidebar.open()
}
loadModels()
watch(() => props.source, () => {
const loaded = seekByName(scene.value, 'loaded')
if (loaded) {
loaded.children = []
}
sidebar.close()
loadModels()
})
const { onLoop } = useRenderLoop()
onLoop(() => {
clickable_items.value.map(el => {
if (camera.value) {
// el.quaternion.copy(camera.value.quaternion);
}
})
})
</script>
<template>
<TresGroup name="loaded">
<Env v-bind="envVars" />
<template v-for="item in models">
<TresGroup :name="item.name">
<TresObject3D v-bind="item.modelFile.clone()" />
</TresGroup>
</template>
<template v-for="item in clickable_items">
<TresMesh @click="() => openSidebar(item.name.replace('_clickable', ''))" v-bind="item.clone()" />
</template>
</TresGroup>
</template>