Compare commits
13 Commits
main
...
bx-1418-hv
Author | SHA1 | Date |
---|---|---|
|
f37b457007 | |
|
ee2878d9a4 | |
|
ee973695d7 | |
|
b84241d8de | |
|
5ec685374d | |
|
0a801c4188 | |
|
50c78fd219 | |
|
19026731f6 | |
|
1219204f45 | |
|
6cbb63b4e8 | |
|
1329678fe6 | |
|
e9ebc96aac | |
|
8872674ba6 |
|
@ -2,6 +2,7 @@
|
||||||
files/*
|
files/*
|
||||||
postgres_data/*
|
postgres_data/*
|
||||||
export_images/*
|
export_images/*
|
||||||
|
glb_import/*
|
||||||
.vscode/
|
.vscode/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
poetry.lock
|
poetry.lock
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Item(models.Model):
|
||||||
slug = models.SlugField(unique=True)
|
slug = models.SlugField(unique=True)
|
||||||
images = models.ManyToManyField(Image)
|
images = models.ManyToManyField(Image)
|
||||||
scene_3d = models.ForeignKey(Scene3D, on_delete=models.RESTRICT)
|
scene_3d = models.ForeignKey(Scene3D, on_delete=models.RESTRICT)
|
||||||
|
file = models.FileField(blank=True, null=True, upload_to=group_based_upload_to)
|
||||||
is_front = models.BooleanField()
|
is_front = models.BooleanField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -11,6 +11,7 @@ class ImageSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class ItemSerializer(serializers.ModelSerializer):
|
class ItemSerializer(serializers.ModelSerializer):
|
||||||
images = ImageSerializer(many=True)
|
images = ImageSerializer(many=True)
|
||||||
|
file = serializers.FileField(use_url=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
lookup_field = 'slug'
|
lookup_field = 'slug'
|
||||||
|
|
|
@ -1,15 +1,152 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.db import models
|
||||||
from import_export.admin import ImportExportModelAdmin
|
from import_export.admin import ImportExportModelAdmin
|
||||||
from .models import ClickableArea, Element3D, Environment, Scene3D
|
from .models import ClickableArea, Element3D, Environment, Scene3D
|
||||||
|
|
||||||
|
|
||||||
class Scene3DAdmin(ImportExportModelAdmin, admin.ModelAdmin):
|
# Админ-класс для Scene3D
|
||||||
filter_horizontal = ("elements",)
|
@admin.register(Scene3D)
|
||||||
|
class Scene3DAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'description', 'element_count')
|
||||||
|
search_fields = ('name', 'description')
|
||||||
|
autocomplete_fields = ('elements',) # Используем автодополнение
|
||||||
|
|
||||||
class ImportExportBtnAdmin(ImportExportModelAdmin, admin.ModelAdmin):
|
# Метод для подсчета количества связанных элементов
|
||||||
pass
|
@admin.display(description='Количество элементов')
|
||||||
|
def element_count(self, obj):
|
||||||
|
return obj.elements.count()
|
||||||
|
|
||||||
admin.site.register(Environment, ImportExportBtnAdmin)
|
|
||||||
admin.site.register(Scene3D, Scene3DAdmin)
|
@admin.register(Environment)
|
||||||
admin.site.register(Element3D, ImportExportBtnAdmin)
|
class EnvironmentAdmin(admin.ModelAdmin):
|
||||||
admin.site.register(ClickableArea, ImportExportBtnAdmin)
|
list_display = ("id", "clear_color_display", "clear_color_to_display", "file_count")
|
||||||
|
list_filter = ("clear_color", "clear_color_to")
|
||||||
|
search_fields = ("id",)
|
||||||
|
readonly_fields = ("file_preview",)
|
||||||
|
|
||||||
|
# Метод для отображения цвета clear_color
|
||||||
|
@admin.display(description="Цвет очистки (начальный)", empty_value="-")
|
||||||
|
def clear_color_display(self, obj):
|
||||||
|
if obj.clear_color:
|
||||||
|
return f'<div style="background-color:{obj.clear_color}; width:50px; height:15px;"></div>'
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
clear_color_display.allow_tags = True
|
||||||
|
|
||||||
|
# Метод для отображения цвета clear_color_to
|
||||||
|
@admin.display(description="Цвет очистки (конечный)", empty_value="-")
|
||||||
|
def clear_color_to_display(self, obj):
|
||||||
|
if obj.clear_color_to:
|
||||||
|
return f'<div style="background-color:{obj.clear_color_to}; width:50px; height:15px;"></div>'
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
clear_color_to_display.allow_tags = True
|
||||||
|
|
||||||
|
# Метод для подсчета количества загруженных файлов
|
||||||
|
@admin.display(description="Количество файлов", empty_value="0")
|
||||||
|
def file_count(self, obj):
|
||||||
|
return sum(
|
||||||
|
bool(getattr(obj, field.name))
|
||||||
|
for field in obj._meta.fields
|
||||||
|
if isinstance(field, models.FileField)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Метод для предпросмотра файлов
|
||||||
|
@admin.display(description="Предпросмотр файлов")
|
||||||
|
def file_preview(self, obj):
|
||||||
|
preview_html = []
|
||||||
|
if obj.hdr_gainmap:
|
||||||
|
preview_html.append(
|
||||||
|
f'<a href="{obj.hdr_gainmap.url}" target="_blank">Просмотр Gainmap</a>'
|
||||||
|
)
|
||||||
|
if obj.hdr_json:
|
||||||
|
preview_html.append(
|
||||||
|
f'<a href="{obj.hdr_json.url}" target="_blank">Просмотр JSON</a>'
|
||||||
|
)
|
||||||
|
if obj.hdr_webp:
|
||||||
|
preview_html.append(
|
||||||
|
f'<a href="{obj.hdr_webp.url}" target="_blank">Просмотр WEBP</a>'
|
||||||
|
)
|
||||||
|
return "<br>".join(preview_html) or "-"
|
||||||
|
|
||||||
|
file_preview.allow_tags = True
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Element3D)
|
||||||
|
class Element3DAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"name",
|
||||||
|
"model_file_display",
|
||||||
|
"is_enabled",
|
||||||
|
"can_not_disable",
|
||||||
|
"position_display",
|
||||||
|
"scenes_count",
|
||||||
|
)
|
||||||
|
list_filter = ("is_enabled", "can_not_disable")
|
||||||
|
search_fields = ("name", "description")
|
||||||
|
readonly_fields = ("model_file_preview",)
|
||||||
|
|
||||||
|
# Метод для отображения пути к файлу модели
|
||||||
|
@admin.display(description="Файл модели", empty_value="-")
|
||||||
|
def model_file_display(self, obj):
|
||||||
|
return obj.model_file.name if obj.model_file else "-"
|
||||||
|
|
||||||
|
# Метод для отображения позиции элемента
|
||||||
|
@admin.display(description="Позиция (X, Y, Z)")
|
||||||
|
def position_display(self, obj):
|
||||||
|
return f"({obj.x_pos}, {obj.y_pos}, {obj.z_pos})"
|
||||||
|
|
||||||
|
# Метод для подсчета количества связанных сцен
|
||||||
|
@admin.display(description="Количество сцен", empty_value="0")
|
||||||
|
def scenes_count(self, obj):
|
||||||
|
return obj.scene3d_set.count()
|
||||||
|
|
||||||
|
# Метод для предпросмотра файла модели
|
||||||
|
@admin.display(description="Предпросмотр файла")
|
||||||
|
def model_file_preview(self, obj):
|
||||||
|
if obj.model_file:
|
||||||
|
return f'<a href="{obj.model_file.url}" target="_blank">Просмотр файла</a>'
|
||||||
|
return "-"
|
||||||
|
|
||||||
|
model_file_preview.allow_tags = True
|
||||||
|
|
||||||
|
# Декоратор для регистрации модели ClickableArea
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ClickableArea)
|
||||||
|
class ClickableAreaAdmin(admin.ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"name",
|
||||||
|
"description_shortened",
|
||||||
|
"source_element",
|
||||||
|
"target_scene",
|
||||||
|
"object_name",
|
||||||
|
)
|
||||||
|
list_filter = ("source__name", "target__name")
|
||||||
|
search_fields = (
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
"object_name",
|
||||||
|
"source__name",
|
||||||
|
"target__name",
|
||||||
|
)
|
||||||
|
autocomplete_fields = ("source", "target")
|
||||||
|
|
||||||
|
# Метод для отображения укороченного описания
|
||||||
|
@admin.display(description="Описание", empty_value="-")
|
||||||
|
def description_shortened(self, obj):
|
||||||
|
return (
|
||||||
|
(obj.description[:50] + "...")
|
||||||
|
if len(obj.description) > 50
|
||||||
|
else obj.description
|
||||||
|
)
|
||||||
|
|
||||||
|
# Метод для отображения связанного элемента (source)
|
||||||
|
@admin.display(description="Элемент 3D", empty_value="-")
|
||||||
|
def source_element(self, obj):
|
||||||
|
return obj.source.name if obj.source else "-"
|
||||||
|
|
||||||
|
# Метод для отображения связанной сцены (target)
|
||||||
|
@admin.display(description="Сцена", empty_value="-")
|
||||||
|
def target_scene(self, obj):
|
||||||
|
return obj.target.name if obj.target else "-"
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.core.files import File
|
||||||
|
import logging
|
||||||
|
from object.models import Scene3D, Element3D
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__) # Инициализация логгера
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Import GLB files into the database and associate them with a specific Scene3D object."
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
logger.info("Starting the import process...")
|
||||||
|
|
||||||
|
# Определение корневой директории
|
||||||
|
base_dir = os.path.dirname(os.path.abspath(__file__)) # Получаем путь к текущему файлу команды
|
||||||
|
root_directory = os.path.join(base_dir, "../../data") # Переходим на уровень выше и указываем папку data
|
||||||
|
root_directory = os.path.normpath(root_directory) # Нормализуем путь для кроссплатформенности
|
||||||
|
|
||||||
|
if not os.path.exists(root_directory):
|
||||||
|
logger.error(f"The directory {root_directory} does not exist.")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Using root directory: {root_directory}")
|
||||||
|
|
||||||
|
# Поиск файлов .glb
|
||||||
|
try:
|
||||||
|
files = glob.glob("*.glb", recursive=True, root_dir=root_directory)
|
||||||
|
if not files:
|
||||||
|
logger.warning("No .glb files found in the specified directory.")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"Found {len(files)} .glb files: {', '.join(files)}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while searching for .glb files: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получение объекта Scene3D
|
||||||
|
try:
|
||||||
|
hv = Scene3D.objects.get(id=56)
|
||||||
|
logger.info(f"Retrieved Scene3D object with ID 56: {hv}")
|
||||||
|
except Scene3D.DoesNotExist:
|
||||||
|
logger.error("Scene3D object with ID 56 does not exist.")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while retrieving Scene3D object: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Обработка каждого файла
|
||||||
|
for f in files:
|
||||||
|
file_path = os.path.join(root_directory, f)
|
||||||
|
logger.info(f"Processing file: {file_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, 'rb') as file:
|
||||||
|
el = Element3D(name=f)
|
||||||
|
el.model_file = File(file, f)
|
||||||
|
el.save()
|
||||||
|
logger.info(f"Successfully saved Element3D object: {el}")
|
||||||
|
|
||||||
|
hv.elements.add(el)
|
||||||
|
logger.info(f"Added Element3D {el} to Scene3D {hv}. Total elements count: {hv.elements.count()}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"File not found: {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error while processing file {f}: {e}")
|
||||||
|
|
||||||
|
logger.info("Import process completed successfully.")
|
|
@ -15,50 +15,6 @@ def group_based_upload_to(instance, filename):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Environment(models.Model):
|
|
||||||
clear_color = ColorField(blank=True, null=True)
|
|
||||||
clear_color_to = ColorField(blank=True, null=True)
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class Element3D(models.Model):
|
|
||||||
model_file = models.FileField(upload_to=group_based_upload_to)
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
description = models.TextField(blank=True, null=True)
|
|
||||||
is_enabled = models.BooleanField(default=True)
|
|
||||||
can_not_disable = models.BooleanField(default=False)
|
|
||||||
|
|
||||||
x_pos = models.IntegerField(default=0)
|
|
||||||
y_pos = models.IntegerField(default=0)
|
|
||||||
z_pos = models.IntegerField(default=0)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Scene3D(models.Model):
|
|
||||||
name = models.CharField(max_length=120)
|
|
||||||
description = models.TextField(blank=True, null=True)
|
|
||||||
elements = models.ManyToManyField(Element3D)
|
|
||||||
env = models.ForeignKey(Environment, models.RESTRICT, blank=True, null=True)
|
|
||||||
|
|
||||||
min_distance = models.IntegerField(
|
|
||||||
default=10,
|
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(600)],
|
|
||||||
)
|
|
||||||
max_distance = models.IntegerField(
|
|
||||||
default=20,
|
|
||||||
validators=[MinValueValidator(2), MaxValueValidator(1000)],
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
def maximum_size_validator(image):
|
def maximum_size_validator(image):
|
||||||
max_width = 512
|
max_width = 512
|
||||||
max_height = 512
|
max_height = 512
|
||||||
|
@ -68,19 +24,129 @@ def maximum_size_validator(image):
|
||||||
raise ValidationError("Height or Width is larger than what is allowed")
|
raise ValidationError("Height or Width is larger than what is allowed")
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(models.Model):
|
||||||
|
clear_color = ColorField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Цвет очистки (начальный)"
|
||||||
|
)
|
||||||
|
clear_color_to = ColorField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Цвет очистки (конечный)"
|
||||||
|
)
|
||||||
|
hdr_gainmap = models.FileField(
|
||||||
|
upload_to=group_based_upload_to,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="HDR Gainmap файл"
|
||||||
|
)
|
||||||
|
hdr_json = models.FileField(
|
||||||
|
upload_to=group_based_upload_to,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="HDR JSON файл"
|
||||||
|
)
|
||||||
|
hdr_webp = models.FileField(
|
||||||
|
upload_to=group_based_upload_to,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="HDR WEBP файл"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Среда #{self.id}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Среда"
|
||||||
|
verbose_name_plural = "Среды"
|
||||||
|
|
||||||
|
|
||||||
|
class Element3D(models.Model):
|
||||||
|
model_file = models.FileField(
|
||||||
|
upload_to=group_based_upload_to,
|
||||||
|
verbose_name="Файл модели"
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name="Название элемента"
|
||||||
|
)
|
||||||
|
description = models.TextField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Описание элемента"
|
||||||
|
)
|
||||||
|
is_enabled = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name="Включен"
|
||||||
|
)
|
||||||
|
can_not_disable = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name="Невозможно отключить"
|
||||||
|
)
|
||||||
|
x_pos = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
verbose_name="Позиция X"
|
||||||
|
)
|
||||||
|
y_pos = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
verbose_name="Позиция Y"
|
||||||
|
)
|
||||||
|
z_pos = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
verbose_name="Позиция Z"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Элемент 3D"
|
||||||
|
verbose_name_plural = "Элементы 3D"
|
||||||
|
|
||||||
|
|
||||||
|
class Scene3D(models.Model):
|
||||||
|
name = models.CharField(max_length=120, verbose_name="Название сцены")
|
||||||
|
description = models.TextField(blank=True, null=True, verbose_name="Описание сцены")
|
||||||
|
elements = models.ManyToManyField("Element3D", verbose_name="Элементы 3D")
|
||||||
|
env = models.ForeignKey(
|
||||||
|
"Environment",
|
||||||
|
on_delete=models.RESTRICT,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Среда",
|
||||||
|
)
|
||||||
|
min_distance = models.IntegerField(
|
||||||
|
default=10,
|
||||||
|
validators=[MinValueValidator(1), MaxValueValidator(600)],
|
||||||
|
verbose_name="Минимальное расстояние",
|
||||||
|
)
|
||||||
|
max_distance = models.IntegerField(
|
||||||
|
default=20,
|
||||||
|
validators=[MinValueValidator(2), MaxValueValidator(1000)],
|
||||||
|
verbose_name="Максимальное расстояние",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Сцена 3D"
|
||||||
|
verbose_name_plural = "Сцены 3D"
|
||||||
|
|
||||||
|
|
||||||
class ClickableArea(models.Model):
|
class ClickableArea(models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
"Название",
|
verbose_name="Название",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text="Название кликабельной области",
|
help_text="Название кликабельной области",
|
||||||
)
|
)
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
"Описание",
|
verbose_name="Описание",
|
||||||
help_text="Описание кликабельной области",
|
help_text="Описание кликабельной области",
|
||||||
)
|
)
|
||||||
|
|
||||||
target = models.ForeignKey(
|
target = models.ForeignKey(
|
||||||
Scene3D,
|
"Scene3D", # Предполагается, что Scene3D определен где-то выше или в том же файле
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name="clickable_areas",
|
related_name="clickable_areas",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -88,15 +154,19 @@ class ClickableArea(models.Model):
|
||||||
help_text="На какую сцену ведет клик",
|
help_text="На какую сцену ведет клик",
|
||||||
)
|
)
|
||||||
source = models.ForeignKey(
|
source = models.ForeignKey(
|
||||||
Element3D,
|
"Element3D", # Предполагается, что Element3D определен где-то выше или в том же файле
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
help_text="В каком элементе искать object_name",
|
help_text="В каком элементе искать object_name",
|
||||||
)
|
)
|
||||||
object_name = models.CharField(
|
object_name = models.CharField(
|
||||||
"Название объекта",
|
verbose_name="Название объекта",
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text="Имя mesh или group в элементе 3D",
|
help_text="Имя mesh или group в элементе 3D",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Кликабельная область" # Человекочитаемое имя одной записи
|
||||||
|
verbose_name_plural = "Кликабельные области" # Человекочитаемое имя множественного числа
|
|
@ -37,6 +37,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./.env:/app/.env
|
- ./.env:/app/.env
|
||||||
- ./files:/app/files
|
- ./files:/app/files
|
||||||
|
- ./glb_import:/app/object/management/commands/data
|
||||||
networks:
|
networks:
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
|
|
|
@ -807,6 +807,7 @@
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@tresjs/cientos/-/cientos-3.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@tresjs/cientos/-/cientos-3.9.0.tgz",
|
||||||
"integrity": "sha512-LAtMveKlecKvWh7TNWdwEs3nQUYMLqz9DZy0YhSZ6OVTfL2vevx2K4sH9744UME8OedUf4fkFFkX4OWQRHaDRQ==",
|
"integrity": "sha512-LAtMveKlecKvWh7TNWdwEs3nQUYMLqz9DZy0YhSZ6OVTfL2vevx2K4sH9744UME8OedUf4fkFFkX4OWQRHaDRQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tresjs/core": "3.9.0",
|
"@tresjs/core": "3.9.0",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
|
|
|
@ -50,7 +50,7 @@ const loadEnv = async () => {
|
||||||
const c = new Color()
|
const c = new Color()
|
||||||
c.set(props.clear_color || PROMOBG)
|
c.set(props.clear_color || PROMOBG)
|
||||||
renderer.value.setClearColor(c)
|
renderer.value.setClearColor(c)
|
||||||
scene.value.fog = new Fog(c, props.focus * k.start, props.focus * k.end)
|
// scene.value.fog = new Fog(c, props.focus * k.start, props.focus * k.end)
|
||||||
|
|
||||||
const hsl_value = { h: 0, s: 0, l: 0 }
|
const hsl_value = { h: 0, s: 0, l: 0 }
|
||||||
new Color(props.clear_color).getHSL(hsl_value);
|
new Color(props.clear_color).getHSL(hsl_value);
|
||||||
|
|
|
@ -58,7 +58,7 @@ const sidebarFunc = () => {
|
||||||
if (sidebar.is_open) {
|
if (sidebar.is_open) {
|
||||||
sidebar.close()
|
sidebar.close()
|
||||||
} else {
|
} else {
|
||||||
sidebar.open()
|
sidebar.is_open = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ const loadModels = async () => {
|
||||||
item.modelFile = loaded_scene
|
item.modelFile = loaded_scene
|
||||||
item.id = element.id
|
item.id = element.id
|
||||||
item.name = element.name
|
item.name = element.name
|
||||||
|
console.log(item)
|
||||||
|
|
||||||
if (!element.is_enabled) {
|
if (!element.is_enabled) {
|
||||||
item.modelFile.visible = false
|
item.modelFile.visible = false
|
||||||
|
@ -245,6 +246,7 @@ watch(() => props.source, () => {
|
||||||
}
|
}
|
||||||
console.log('props change')
|
console.log('props change')
|
||||||
sidebar.close()
|
sidebar.close()
|
||||||
|
sidebar.closeBtn()
|
||||||
} else {
|
} else {
|
||||||
renderer.value.dispose()
|
renderer.value.dispose()
|
||||||
}
|
}
|
||||||
|
@ -294,7 +296,7 @@ onBeforeRender(() => {
|
||||||
|
|
||||||
const dis_to_cam = camera.value?.position.distanceTo(el.value[0].position);
|
const dis_to_cam = camera.value?.position.distanceTo(el.value[0].position);
|
||||||
if (dis_to_cam) {
|
if (dis_to_cam) {
|
||||||
const scaling = (0.5 * dis_to_cam) / 100
|
const scaling = (0.66 * dis_to_cam) / 100
|
||||||
el.value[0].children[0].scale.set(scaling, scaling, scaling);
|
el.value[0].children[0].scale.set(scaling, scaling, scaling);
|
||||||
el.value[0].updateMatrixWorld()
|
el.value[0].updateMatrixWorld()
|
||||||
}
|
}
|
||||||
|
@ -335,6 +337,8 @@ onBeforeRender(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(controls.value as any).update()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => targetDistance.min, () => {
|
watch(() => targetDistance.min, () => {
|
||||||
|
|
|
@ -64,7 +64,7 @@ watch(() => sidebar.id_clickable, () => {
|
||||||
<div class="sidebar-accordion-content" v-if="sidebar.isAccOpen('desc')">
|
<div class="sidebar-accordion-content" v-if="sidebar.isAccOpen('desc')">
|
||||||
<template
|
<template
|
||||||
v-for="p in (sidebar.description || sidebar_scene.description || '').replace(/(\n|\r)+/g, '\n').split('\n')">
|
v-for="p in (sidebar.description || sidebar_scene.description || '').replace(/(\n|\r)+/g, '\n').split('\n')">
|
||||||
<p>{{ p }}</p>
|
<p v-html="p"></p>
|
||||||
</template>
|
</template>
|
||||||
<RouterLink :to="`/${route.params.item}/${sidebar.target}`" v-if="sidebar.target">
|
<RouterLink :to="`/${route.params.item}/${sidebar.target}`" v-if="sidebar.target">
|
||||||
Перейти
|
Перейти
|
||||||
|
|
|
@ -4,12 +4,14 @@ type state = {
|
||||||
name?: string,
|
name?: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
clickable: PromoScene[],
|
clickable: PromoScene[],
|
||||||
|
_visible: { id: number, is_enabled: boolean }[],
|
||||||
visible: PromoScene[],
|
visible: PromoScene[],
|
||||||
}
|
}
|
||||||
export const usePromoScene = defineStore('promo_scene', {
|
export const usePromoScene = defineStore('promo_scene', {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
clickable: [],
|
clickable: [],
|
||||||
|
_visible: [],
|
||||||
visible: [],
|
visible: [],
|
||||||
} as state
|
} as state
|
||||||
},
|
},
|
||||||
|
@ -22,7 +24,10 @@ export const usePromoScene = defineStore('promo_scene', {
|
||||||
this.clickable = data
|
this.clickable = data
|
||||||
},
|
},
|
||||||
setVisible(data: PromoScene[]) {
|
setVisible(data: PromoScene[]) {
|
||||||
this.visible = data
|
this._visible = data.slice(0).map(el => {
|
||||||
|
return { id: el.id, is_enabled: el.is_enabled ?? true }
|
||||||
|
})
|
||||||
|
this.visible = data.slice(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { useClickable } from './clickable'
|
import { useClickable } from './clickable'
|
||||||
|
import { usePromoScene } from './promo_scene'
|
||||||
|
|
||||||
export const usePromoSidebar = defineStore('promo_sidebar', {
|
export const usePromoSidebar = defineStore('promo_sidebar', {
|
||||||
state: () => {
|
state: () => {
|
||||||
|
@ -47,13 +48,18 @@ export const usePromoSidebar = defineStore('promo_sidebar', {
|
||||||
// this.target = undefined;
|
// this.target = undefined;
|
||||||
// this.loading = true;
|
// this.loading = true;
|
||||||
this.is_open = false;
|
this.is_open = false;
|
||||||
this.accordions = []
|
// this.accordions = [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
closeBtn() {
|
closeBtn() {
|
||||||
this.$reset;
|
this.$reset;
|
||||||
this.is_open = false;
|
this.is_open = false;
|
||||||
this.is_btn_open = false;
|
this.is_btn_open = false;
|
||||||
|
|
||||||
|
const sidebar_scene = usePromoScene()
|
||||||
|
sidebar_scene.visible.map(el => {
|
||||||
|
el.is_enabled = (sidebar_scene._visible.find(item => item.id == el.id) ?? { is_enabled: true }).is_enabled
|
||||||
|
})
|
||||||
},
|
},
|
||||||
toggleAccordion(name: string, newState = null) {
|
toggleAccordion(name: string, newState = null) {
|
||||||
if (name == 'obj' && this.accordions.includes('clickable')) this.toggleAccordion('clickable')
|
if (name == 'obj' && this.accordions.includes('clickable')) this.toggleAccordion('clickable')
|
||||||
|
|
|
@ -22,4 +22,8 @@ export default defineConfig({
|
||||||
svgLoader(),
|
svgLoader(),
|
||||||
],
|
],
|
||||||
assetsInclude: ['**/*.fbx', '**/*.glb', '**/*.gltf', '**/*.hdr'],
|
assetsInclude: ['**/*.fbx', '**/*.glb', '**/*.gltf', '**/*.hdr'],
|
||||||
|
preview: {
|
||||||
|
host: "demo.kustarshina.ru",
|
||||||
|
allowedHosts: ["demo.kustarshina.ru",],
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue