This commit is contained in:
commit e61d535120
11 changed files with 174 additions and 47 deletions

View File

@ -120,6 +120,22 @@
"detail": "django.db",
"documentation": {}
},
{
"label": "migrations",
"importPath": "django.db",
"description": "django.db",
"isExtraImport": true,
"detail": "django.db",
"documentation": {}
},
{
"label": "models",
"importPath": "django.db",
"description": "django.db",
"isExtraImport": true,
"detail": "django.db",
"documentation": {}
},
{
"label": "models",
"importPath": "django.db",
@ -1077,6 +1093,15 @@
"detail": "back.object.migrations.0004_alter_clickablearea_target_name",
"documentation": {}
},
{
"label": "Migration",
"kind": 6,
"importPath": "back.object.migrations.0005_scene3d_hdr_gainmap_scene3d_hdr_json_and_more",
"description": "back.object.migrations.0005_scene3d_hdr_gainmap_scene3d_hdr_json_and_more",
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('object', '0004_alter_clickablearea_target_name'),\n ]\n operations = [\n migrations.AddField(\n model_name='scene3d',\n name='hdr_gainmap',\n field=models.FileField(blank=True, null=True, upload_to=''),\n ),",
"detail": "back.object.migrations.0005_scene3d_hdr_gainmap_scene3d_hdr_json_and_more",
"documentation": {}
},
{
"label": "Scene3DAdmin",
"kind": 6,
@ -1100,7 +1125,7 @@
"kind": 6,
"importPath": "back.object.models",
"description": "back.object.models",
"peekOfCode": "class Element3D(models.Model):\n parent = models.ForeignKey(\"self\", on_delete=models.PROTECT, blank=True, null=True)\n model_file = models.FileField(upload_to=group_based_upload_to)\n name = models.CharField(max_length=255)\n description = models.TextField()\n def __str__(self):\n return self.name\nclass Scene3D(models.Model):\n filter_horizontal = ('elements',)\n name = models.CharField(",
"peekOfCode": "class Element3D(models.Model):\n parent = models.ForeignKey(\"self\", on_delete=models.PROTECT, blank=True, null=True)\n model_file = models.FileField(upload_to=group_based_upload_to)\n name = models.CharField(max_length=255)\n description = models.TextField()\n def __str__(self):\n return self.name\nclass Scene3D(models.Model):\n filter_horizontal = (\"elements\",)\n name = models.CharField(",
"detail": "back.object.models",
"documentation": {}
},
@ -1109,7 +1134,7 @@
"kind": 6,
"importPath": "back.object.models",
"description": "back.object.models",
"peekOfCode": "class Scene3D(models.Model):\n filter_horizontal = ('elements',)\n name = models.CharField(\n max_length=120,\n )\n elements = models.ManyToManyField(Element3D)\n min_distance = models.IntegerField(\n validators=[MinValueValidator(1), MaxValueValidator(600)], blank=True, null=True\n )\n max_distance = models.IntegerField(",
"peekOfCode": "class Scene3D(models.Model):\n filter_horizontal = (\"elements\",)\n name = models.CharField(\n max_length=120,\n )\n elements = models.ManyToManyField(Element3D)\n min_distance = models.IntegerField(\n validators=[MinValueValidator(1), MaxValueValidator(600)], blank=True, null=True\n )\n max_distance = models.IntegerField(",
"detail": "back.object.models",
"documentation": {}
},
@ -1145,7 +1170,7 @@
"kind": 6,
"importPath": "back.object.serializers",
"description": "back.object.serializers",
"peekOfCode": "class Element3DSerializer(serializers.ModelSerializer):\n model_file = serializers.ImageField(use_url=False)\n class Meta:\n model = Element3D\n fields = \"__all__\"\nclass Scene3DSerializer(serializers.ModelSerializer):\n elements = Element3DSerializer(many=True)\n class Meta:\n model = Scene3D\n fields = \"__all__\"",
"peekOfCode": "class Element3DSerializer(serializers.ModelSerializer):\n model_file = serializers.ImageField(use_url=False)\n class Meta:\n model = Element3D\n fields = \"__all__\"\nclass Scene3DSerializer(serializers.ModelSerializer):\n elements = Element3DSerializer(many=True)\n hdr_gainmap = serializers.FileField(use_url=False)\n hdr_json = serializers.FileField(use_url=False)\n hdr_webp = serializers.FileField(use_url=False)",
"detail": "back.object.serializers",
"documentation": {}
},
@ -1154,7 +1179,7 @@
"kind": 6,
"importPath": "back.object.serializers",
"description": "back.object.serializers",
"peekOfCode": "class Scene3DSerializer(serializers.ModelSerializer):\n elements = Element3DSerializer(many=True)\n class Meta:\n model = Scene3D\n fields = \"__all__\"\n depth = 2\nclass ClickableAreaSerializer(serializers.ModelSerializer):\n class Meta:\n model = ClickableArea\n fields = \"__all__\"",
"peekOfCode": "class Scene3DSerializer(serializers.ModelSerializer):\n elements = Element3DSerializer(many=True)\n hdr_gainmap = serializers.FileField(use_url=False)\n hdr_json = serializers.FileField(use_url=False)\n hdr_webp = serializers.FileField(use_url=False)\n class Meta:\n model = Scene3D\n fields = \"__all__\"\n depth = 2\nclass ClickableAreaSerializer(serializers.ModelSerializer):",
"detail": "back.object.serializers",
"documentation": {}
},

View File

@ -22,7 +22,7 @@ class Element3D(models.Model):
return self.name
class Scene3D(models.Model):
filter_horizontal = ('elements',)
filter_horizontal = ("elements",)
name = models.CharField(
max_length=120,
)
@ -32,12 +32,22 @@ class Scene3D(models.Model):
validators=[MinValueValidator(1), MaxValueValidator(600)], blank=True, null=True
)
max_distance = models.IntegerField(
validators=[MinValueValidator(2), MaxValueValidator(1000)], blank=True, null=True
validators=[MinValueValidator(2), MaxValueValidator(1000)],
blank=True,
null=True,
)
<<<<<<< HEAD
gainmap = models.FileField(upload_to=group_based_upload_to)
json = models.FileField()
webp = models.FileField()
=======
>>>>>>> 46d2586e693673bf1d8b8a390b297209bdf70ecb
hdr_gainmap = models.FileField(
upload_to=group_based_upload_to, blank=True, null=True
)
hdr_json = models.FileField(upload_to=group_based_upload_to, blank=True, null=True)
hdr_webp = models.FileField(upload_to=group_based_upload_to, blank=True, null=True)
def __str__(self):
return self.name

View File

@ -11,8 +11,16 @@ class Element3DSerializer(serializers.ModelSerializer):
class Scene3DSerializer(serializers.ModelSerializer):
<<<<<<< HEAD
elements = serializers.ImageField(many=True, use_url=False)
=======
elements = Element3DSerializer(many=True)
hdr_gainmap = serializers.FileField(use_url=False)
hdr_json = serializers.FileField(use_url=False)
hdr_webp = serializers.FileField(use_url=False)
>>>>>>> 46d2586e693673bf1d8b8a390b297209bdf70ecb
class Meta:
model = Scene3D
fields = "__all__"

View File

@ -17,6 +17,7 @@ declare module 'vue' {
IMdiHexagonOutline: typeof import('~icons/mdi/hexagon-outline')['default']
IMdiHome: typeof import('~icons/mdi/home')['default']
IMdiMonitorScreenshot: typeof import('~icons/mdi/monitor-screenshot')['default']
IMdiPagePreviousOutline: typeof import('~icons/mdi/page-previous-outline')['default']
IMdiShop: typeof import('~icons/mdi/shop')['default']
IMdiVideo3d: typeof import('~icons/mdi/video3d')['default']
Item: typeof import('./src/components/Floorplan/item.vue')['default']

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { onMounted } from 'vue';
import { onMounted, watch } from 'vue';
import { PMREMGenerator } from 'three';
import { GainMapLoader } from '@monogrid/gainmap-js'
@ -9,14 +9,20 @@ import hdr_gainmap from '../../assets/promo/hdr/hdr-gainmap.webp'
import hdr_json from '../../assets/promo/hdr/hdr.json?url'
import hdr_webp from '../../assets/promo/hdr/hdr.webp'
const { renderer, scene } = useTresContext()
const props = defineProps(['hdr_webp', 'hdr_gainmap', 'hdr_json'])
onMounted(async () => {
const { renderer, scene } = useTresContext()
const loadEnv = async () => {
console.log(props)
const pmremGenerator = new PMREMGenerator(renderer.value);
pmremGenerator.compileEquirectangularShader();
const loader = new GainMapLoader(renderer.value)
const result = await loader.loadAsync([hdr_webp, hdr_gainmap, hdr_json,])
const result = await loader.loadAsync([
props.hdr_webp || hdr_webp,
props.hdr_gainmap || hdr_gainmap,
props.hdr_json || hdr_json,
])
const exrCubeRenderTarget = pmremGenerator.fromEquirectangular(result.renderTarget.texture);
const exrBackground = exrCubeRenderTarget.texture;
@ -24,5 +30,9 @@ onMounted(async () => {
scene.value.environment = newEnvMap
scene.value.background = exrBackground
result.renderTarget.texture.dispose();
}
onMounted(async () => {
loadEnv()
})
watch(() => props.hdr_webp, loadEnv)
</script>

View File

@ -1,11 +1,14 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { reactive, ref, watch } from 'vue';
import { Box3, Color, Group, Mesh, MeshStandardMaterial, PointLight, SphereGeometry, Vector3 } from 'three';
import { useTresContext, useSeek } 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'])
@ -22,12 +25,19 @@ const clickable = ref<clickableAreaType[]>([])
const clickable_objects = ref<any[]>([])
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
if (raw_data.hdr_gainmap) envVars.hdr_gainmap = `${IMAGE_URL}/${raw_data.hdr_gainmap}`
if (raw_data.hdr_json) envVars.hdr_json = `${IMAGE_URL}/${raw_data.hdr_json}`
if (raw_data.hdr_webp) envVars.hdr_webp = `${IMAGE_URL}/${raw_data.hdr_webp}`
const data = raw_data.elements
if (!controls.value) return;
@ -39,8 +49,10 @@ const loadModels = async () => {
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}`
@ -55,6 +67,8 @@ const loadModels = async () => {
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];
@ -140,6 +154,9 @@ watch(() => props.source, () => {
})
</script>
<template>
<Suspense>
<Env v-bind="envVars" />
</Suspense>
<TresGroup name="loaded">
<template v-for="item in models">
<TresGroup :name="item.name">

View File

@ -7,7 +7,6 @@ import { Vector3 } from 'three';
import { TresCanvas } from '@tresjs/core';
import { StatsGl, OrbitControls } from '@tresjs/cientos'
import Env from './env.vue'
import LoadModels from './load_models.vue'
import Sidebar from './sidebar.vue'
import { usePromoSidebar } from '../../stores/promo_sidebar';
@ -62,9 +61,6 @@ watch(() => route.params.target, () => {
</Suspense>
<TresPerspectiveCamera :position="cameraPosition" ref="camera" />
<OrbitControls v-bind="controlsState" @change="onChange" make-default />
<Suspense>
<Env />
</Suspense>
<Suspense>
<LoadModels :source="source" :loaded="set_model_load_status" :loaded_pan="loadedPan" />
</Suspense>
@ -73,9 +69,14 @@ watch(() => route.params.target, () => {
<TresShadowMaterial :opacity="0.2" />
</TresMesh>
</TresCanvas>
<RouterLink to="/promo/main/" class="homelink">
<i-mdi-home />
</RouterLink>
<div class="homelink">
<a href="#" @click.prevent="sidebar.open" v-if="!sidebar.is_open">
<i-mdi-page-previous-outline />
</a>
<RouterLink to="/promo/main/">
<i-mdi-home />
</RouterLink>
</div>
</div>
<Sidebar />
</div>
@ -96,18 +97,25 @@ watch(() => route.params.target, () => {
filter: blur(10px);
transition: all 300ms linear;
}
.homelink {
position: absolute;
right: 0;
bottom: 0;
border-radius: 50%;
border: 1px solid white;
background: white;
padding: 0.5rem;
line-height: 1;
font-size: 0;
svg {
font-size: 5rem;
font-size: 3rem;
padding: 1.5rem;
}
a {
margin-bottom: 2rem;
border-radius: 50%;
border: 1px solid white;
background: white;
line-height: 1;
font-size: 0;
display: block;
}
}
</style>

View File

@ -3,10 +3,12 @@ import { ref } from 'vue';
import { RouterLink } from 'vue-router';
import { onClickOutside } from '@vueuse/core'
import { usePromoSidebar } from '../../stores/promo_sidebar';
import { usePromoScene } from '../../stores/promo_scene';
const sidebar = usePromoSidebar()
const scene = usePromoScene()
const sidebar_obj = ref()
console.log(scene.list)
// onClickOutside(sidebar_obj, () => sidebar.close())
</script>
<template>
@ -14,16 +16,24 @@ const sidebar_obj = ref()
<a href="#" @click.prevent="sidebar.close" class="sidebar-close">
<i-mdi-close />
</a>
<template v-if="sidebar.is_open">
<h2>{{ sidebar.title }}</h2>
<template v-for="p in sidebar.description.split('\n')">
<p>{{ p }}</p>
<div class="sidebar-content">
<template v-if="!sidebar.is_open"></template>
<template v-else-if="(sidebar.description && sidebar.title)">
<h2>{{ sidebar.title }}</h2>
<template v-for="p in sidebar.description.split('\n')">
<p>{{ p }}</p>
</template>
<RouterLink class="btn" :to="`/promo/main/${sidebar.target}`" v-if="sidebar.target">
{{ sidebar.target_name }}
</RouterLink>
</template>
<RouterLink class="btn" :to="`/promo/main/${sidebar.target}`" v-if="sidebar.target">
{{ sidebar.target_name }}
</RouterLink>
</template>
<template v-else>
<template v-for="item in scene.list">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
</template>
</template>
</div>
</div>
</template>
<style scoped lang="scss">
@ -35,8 +45,9 @@ const sidebar_obj = ref()
right: -27vw;
bottom: 0;
transition: all 300ms linear;
line-height: 1.25;
padding: 2rem;
padding: 3rem 2rem 2rem;
&.open {
right: 0
@ -50,17 +61,31 @@ const sidebar_obj = ref()
color: black;
}
&-content {
max-height: 100%;
overflow: auto;
}
h2 {
text-align: center;
font-size: 2rem;
margin: 1rem
}
p {
p,
h3 {
margin: 1rem 0;
}
p {
font-size: 1.25rem;
}
h3 {
font-size: 1.5rem;
font-weight: bold
}
.btn {
display: block;
margin: 0 auto;

15
front/src/index.d.ts vendored
View File

@ -14,6 +14,9 @@ interface scene3D {
name: string
min_distance: number
max_distance: number
hdr_gainmap?: string
hdr_json?: string
hdr_webp?: string
elements: element3DType[]
}
interface element3DType {
@ -40,8 +43,8 @@ interface clickableAreaType {
target_name?: string
}
interface PromoSidebarData {
title: string
description: string
title?: string
description?: string
target?: string
target_name?: string
}
@ -49,3 +52,11 @@ interface PromoSidebar extends PromoSidebarData {
loading: boolean
is_open: boolean
}
interface PromoScene {
id: number
model_file: string
name: string
description: string
parent?: number
is_enabled: boolean
}

View File

@ -0,0 +1,12 @@
import { defineStore } from 'pinia'
export const usePromoScene = defineStore('promo_scene', {
state: () => {
return { list: [] as PromoScene[] }
},
actions: {
setData(data: PromoScene[]) {
this.list = data
}
}
})

View File

@ -3,10 +3,10 @@ import { defineStore } from 'pinia'
export const usePromoSidebar = defineStore('promo_sidebar', {
state: () => {
return {
title: 'Сайдбар',
description: 'Описание',
title: undefined,
description: undefined,
target: undefined,
target_name: 'Перейти дальше',
target_name: undefined,
loading: true,
is_open: false
} as PromoSidebar