dev #10
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,9 @@
|
|||
from django.contrib import admin
|
||||
from .models import ClickableArea, Element3D, Scene3D
|
||||
|
||||
admin.site.register(Scene3D)
|
||||
class Scene3DAdmin(admin.ModelAdmin):
|
||||
filter_horizontal = ('elements',)
|
||||
|
||||
admin.site.register(Scene3D, Scene3DAdmin)
|
||||
admin.site.register(Element3D)
|
||||
admin.site.register(ClickableArea)
|
|
@ -1,5 +1,7 @@
|
|||
from PIL import Image
|
||||
from django.db import models
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("root")
|
||||
|
@ -16,13 +18,16 @@ class Element3D(models.Model):
|
|||
parent = models.ForeignKey("self", on_delete=models.PROTECT, blank=True, null=True)
|
||||
model_file = models.FileField(upload_to=group_based_upload_to)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
description = models.TextField(blank=True, null=True)
|
||||
is_enabled = models.BooleanField(default=True)
|
||||
can_not_disable = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Scene3D(models.Model):
|
||||
filter_horizontal = ("elements",)
|
||||
name = models.CharField(
|
||||
max_length=120,
|
||||
)
|
||||
|
@ -32,13 +37,30 @@ 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,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def maximum_size_validator(image):
|
||||
max_width = 512
|
||||
max_height = 512
|
||||
img = Image.open(image)
|
||||
fw, fh = img.size
|
||||
if fw > max_width or fh > max_height:
|
||||
raise ValidationError("Height or Width is larger than what is allowed")
|
||||
|
||||
|
||||
class ClickableArea(models.Model):
|
||||
name = models.CharField("название", max_length=255)
|
||||
description = models.TextField("описание")
|
||||
|
@ -59,6 +81,15 @@ class ClickableArea(models.Model):
|
|||
Element3D,
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
image = models.ImageField(
|
||||
"Картинка",
|
||||
upload_to=group_based_upload_to,
|
||||
validators=[
|
||||
maximum_size_validator,
|
||||
],
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -12,6 +12,9 @@ class Element3DSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Scene3DSerializer(serializers.ModelSerializer):
|
||||
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)
|
||||
|
||||
class Meta:
|
||||
model = Scene3D
|
||||
|
@ -20,6 +23,8 @@ class Scene3DSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class ClickableAreaSerializer(serializers.ModelSerializer):
|
||||
image = serializers.ImageField(use_url=False)
|
||||
|
||||
class Meta:
|
||||
model = ClickableArea
|
||||
fields = "__all__"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# VITE_SERVER_URL='http://localhost:8000'
|
||||
VITE_SERVER_URL='https://demo.kustarshina.ru'
|
||||
VITE_IMAGE_URL='https://demo.kustarshina.ru'
|
||||
VITE_SERVER_URL='http://localhost:8000'
|
|
@ -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']
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
"@fireworks-js/vue": "^2.10.7",
|
||||
"@monogrid/gainmap-js": "^3.0.5",
|
||||
"@tresjs/cientos": "^3.9.0",
|
||||
"@tresjs/core": "^3.9.0",
|
||||
"@tresjs/core": "^4.0.2",
|
||||
"@tresjs/leches": "^0.14.0",
|
||||
"@tresjs/post-processing": "^0.7.1",
|
||||
"@vueuse/components": "^10.9.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"d3": "^7.9.0",
|
||||
|
@ -84,7 +86,6 @@
|
|||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
|
@ -100,7 +101,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -116,7 +116,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -132,7 +131,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -148,7 +146,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -164,7 +161,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -180,7 +176,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
|
@ -196,7 +191,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
|
@ -212,7 +206,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -228,7 +221,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -244,7 +236,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -260,7 +251,6 @@
|
|||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -276,7 +266,6 @@
|
|||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -292,7 +281,6 @@
|
|||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -308,7 +296,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -324,7 +311,6 @@
|
|||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -340,7 +326,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -356,7 +341,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
|
@ -372,7 +356,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
|
@ -388,7 +371,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
|
@ -404,7 +386,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -420,7 +401,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -436,7 +416,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -594,7 +573,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -607,7 +585,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
|
@ -620,7 +597,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -633,7 +609,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
|
@ -646,7 +621,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -659,7 +633,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -672,7 +645,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -685,7 +657,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -698,7 +669,6 @@
|
|||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -711,7 +681,6 @@
|
|||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -724,7 +693,6 @@
|
|||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -737,7 +705,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -750,7 +717,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
|
@ -763,7 +729,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -776,7 +741,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -789,7 +753,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
|
@ -815,7 +778,7 @@
|
|||
"vue": ">=3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/core": {
|
||||
"node_modules/@tresjs/cientos/node_modules/@tresjs/core": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@tresjs/core/-/core-3.9.0.tgz",
|
||||
"integrity": "sha512-6el70oXSduTvSA3XCI8/rQV2GzcgCLceZKA443CTU/MBPmRDULJ12q/UYl18Ij4CJ68rTqgVi0Da+WNMrs784A==",
|
||||
|
@ -829,6 +792,20 @@
|
|||
"vue": ">=3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/core": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@tresjs/core/-/core-4.0.2.tgz",
|
||||
"integrity": "sha512-+Shy5ch4m9gQSHRlArZAn4nv2apaFJJv21bAvpOKRXTCtGu0BakKGUpWcTzzmDsTs9t6yndbjCWzyifggjFpQQ==",
|
||||
"dependencies": {
|
||||
"@alvarosabu/utils": "^3.2.0",
|
||||
"@vue/devtools-api": "^6.6.2",
|
||||
"@vueuse/core": "^10.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.133",
|
||||
"vue": ">=3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/core/node_modules/@vueuse/core": {
|
||||
"version": "10.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz",
|
||||
|
@ -912,6 +889,57 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/leches": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@tresjs/leches/-/leches-0.14.0.tgz",
|
||||
"integrity": "sha512-X/UIeldHkx9RZdLuwGh/0XN8uM5JDOjIZH0tUodmXb+AEO/97F90FgSuRyl+xyTTOekh+z3VFavkWpoRS64KVw==",
|
||||
"dependencies": {
|
||||
"@unocss/core": "^0.57.7",
|
||||
"@vueuse/components": "^10.6.1",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/leches/node_modules/@unocss/core": {
|
||||
"version": "0.57.7",
|
||||
"resolved": "https://registry.npmjs.org/@unocss/core/-/core-0.57.7.tgz",
|
||||
"integrity": "sha512-1d36M0CV3yC80J0pqOa5rH1BX6g2iZdtKmIb3oSBN4AWnMCSrrJEPBrUikyMq2TEQTrYWJIVDzv5A9hBUat3TA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/post-processing": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@tresjs/post-processing/-/post-processing-0.7.1.tgz",
|
||||
"integrity": "sha512-MLrjxR4rJ55MOYFKMEZ8aqh5no2/sOG1fdYk2B49Ntx2p2etyuVpI6e9BBAvrc4M/wktRHkQ2JNDbgMaCiJoMg==",
|
||||
"dependencies": {
|
||||
"@tresjs/core": "^3.5.1",
|
||||
"@unocss/core": "^0.58.0",
|
||||
"@vueuse/core": "^10.6.1",
|
||||
"postprocessing": "^6.33.4",
|
||||
"three-stdlib": "^2.28.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.133",
|
||||
"vue": ">=3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tresjs/post-processing/node_modules/@tresjs/core": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@tresjs/core/-/core-3.9.0.tgz",
|
||||
"integrity": "sha512-6el70oXSduTvSA3XCI8/rQV2GzcgCLceZKA443CTU/MBPmRDULJ12q/UYl18Ij4CJ68rTqgVi0Da+WNMrs784A==",
|
||||
"dependencies": {
|
||||
"@alvarosabu/utils": "^3.1.1",
|
||||
"@vue/devtools-api": "^6.6.1",
|
||||
"@vueuse/core": "^10.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">=0.133",
|
||||
"vue": ">=3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@trysound/sax": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||
|
@ -1186,8 +1214,7 @@
|
|||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.14",
|
||||
|
@ -1234,6 +1261,14 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.15.tgz",
|
||||
"integrity": "sha512-nC9116Gd4N+CqTxqo6gvCfhAMAzgRcfS8ZsciNodHq8uwW4JCVKwhagw8yN0XmC7mHrLnWqniJpoVEiR+72Drw=="
|
||||
},
|
||||
"node_modules/@unocss/core": {
|
||||
"version": "0.58.9",
|
||||
"resolved": "https://registry.npmjs.org/@unocss/core/-/core-0.58.9.tgz",
|
||||
"integrity": "sha512-wYpPIPPsOIbIoMIDuH8ihehJk5pAZmyFKXIYO/Kro98GEOFhz6lJoLsy6/PZuitlgp2/TSlubUuWGjHWvp5osw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
|
||||
|
@ -1527,7 +1562,7 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
|
@ -1546,7 +1581,7 @@
|
|||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
|
@ -1572,7 +1607,7 @@
|
|||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
|
@ -1598,7 +1633,7 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
|
@ -2202,7 +2237,6 @@
|
|||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
|
@ -2298,7 +2332,7 @@
|
|||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
|
@ -2345,7 +2379,6 @@
|
|||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
@ -2380,7 +2413,7 @@
|
|||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
|
@ -2467,7 +2500,7 @@
|
|||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
|
||||
"integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
|
@ -2486,7 +2519,7 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
|
@ -2510,7 +2543,7 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -2519,7 +2552,7 @@
|
|||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
|
@ -2531,7 +2564,7 @@
|
|||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
|
@ -2748,7 +2781,7 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
@ -2882,7 +2915,7 @@
|
|||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
|
@ -2978,6 +3011,17 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postprocessing": {
|
||||
"version": "6.35.5",
|
||||
"resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.35.5.tgz",
|
||||
"integrity": "sha512-nO8pxbiQT7+u3qv/q4LL1x6tstZWuduDlkOLFWQm3vFFKHzAsRMxqwsGAktkHKMSGXVEwTBvT3TKPklNALQheg==",
|
||||
"engines": {
|
||||
"node": ">= 0.13.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.152.0 < 0.166.0"
|
||||
}
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
||||
|
@ -3027,7 +3071,7 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
|
@ -3076,7 +3120,6 @@
|
|||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.4.tgz",
|
||||
"integrity": "sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
},
|
||||
|
@ -3144,7 +3187,7 @@
|
|||
"version": "1.75.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz",
|
||||
"integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
|
@ -3354,7 +3397,7 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
@ -3518,7 +3561,6 @@
|
|||
"version": "5.2.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
|
||||
"integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
"postcss": "^8.4.38",
|
||||
|
@ -3569,6 +3611,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-css-injected-by-js": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.1.tgz",
|
||||
"integrity": "sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ==",
|
||||
"peerDependencies": {
|
||||
"vite": ">2.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.5.tgz",
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
"@fireworks-js/vue": "^2.10.7",
|
||||
"@monogrid/gainmap-js": "^3.0.5",
|
||||
"@tresjs/cientos": "^3.9.0",
|
||||
"@tresjs/core": "^3.9.0",
|
||||
"@tresjs/core": "^4.0.2",
|
||||
"@tresjs/leches": "^0.14.0",
|
||||
"@tresjs/post-processing": "^0.7.1",
|
||||
"@vueuse/components": "^10.9.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"d3": "^7.9.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, watch } from 'vue';
|
||||
|
||||
import { PMREMGenerator } from 'three';
|
||||
import { EquirectangularReflectionMapping, ReinhardToneMapping } from 'three';
|
||||
import { GainMapLoader } from '@monogrid/gainmap-js'
|
||||
import { useTresContext } from '@tresjs/core';
|
||||
|
||||
|
@ -9,20 +9,27 @@ 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 props = defineProps(['hdr_webp', 'hdr_gainmap', 'hdr_json'])
|
||||
|
||||
const { renderer, scene } = useTresContext()
|
||||
|
||||
onMounted(async () => {
|
||||
const pmremGenerator = new PMREMGenerator(renderer.value);
|
||||
pmremGenerator.compileEquirectangularShader();
|
||||
|
||||
const loadEnv = async () => {
|
||||
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;
|
||||
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;
|
||||
scene.value.environment = newEnvMap
|
||||
scene.value.background = exrBackground
|
||||
scene.value.environment = result.renderTarget.texture
|
||||
scene.value.background = result.renderTarget.texture
|
||||
scene.value.background.mapping = EquirectangularReflectionMapping
|
||||
// scene.value.backgroundBlurriness = 0.15
|
||||
result.renderTarget.texture.dispose();
|
||||
}
|
||||
renderer.value.toneMapping = ReinhardToneMapping
|
||||
onMounted(async () => {
|
||||
loadEnv()
|
||||
})
|
||||
watch(() => props.hdr_webp, loadEnv)
|
||||
</script>
|
||||
<template></template>
|
|
@ -1,46 +1,78 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { Box3, Color, Group, Mesh, MeshStandardMaterial, PointLight, SphereGeometry, Vector3 } from 'three';
|
||||
import { useTresContext, useSeek } from '@tresjs/core';
|
||||
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
import {
|
||||
Box3, Color, DoubleSide, Group, Mesh, MeshBasicMaterial,
|
||||
PlaneGeometry, SpriteMaterial, TextureLoader, Vector2, Vector3,
|
||||
} from 'three';
|
||||
// import { DepthOfField, EffectComposer, Pixelation } from '@tresjs/post-processing'
|
||||
|
||||
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
|
||||
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
|
||||
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
|
||||
|
||||
import { RGBShiftShader } from 'three/addons/shaders/RGBShiftShader.js';
|
||||
import { DotScreenShader } from 'three/addons/shaders/DotScreenShader.js';
|
||||
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
|
||||
|
||||
import '@tresjs/leches/styles'
|
||||
|
||||
import { useTresContext, useSeek, useRenderLoop, useLoop } 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
|
||||
// el.receiveShadow = true
|
||||
// el.castShadow = true
|
||||
shadows_and_pos(el)
|
||||
})
|
||||
}
|
||||
|
||||
const models = ref<model3DType[]>([])
|
||||
const clickable = ref<clickableAreaType[]>([])
|
||||
const clickable_objects = ref<any[]>([])
|
||||
const clickable_items = ref<any[]>([])
|
||||
const clickable_refs = ref<any[]>([])
|
||||
const sidebar = usePromoSidebar();
|
||||
const { controls, camera, scene } = useTresContext()
|
||||
const { seekByName } = useSeek()
|
||||
const sidebar_scene = usePromoScene()
|
||||
const { renderer, controls, camera, scene, raycaster } = useTresContext()
|
||||
const { seekByName, seekAllByName } = useSeek()
|
||||
const envVars = reactive({}) as { hdr_gainmap?: string, hdr_json?: string, hdr_webp?: string }
|
||||
|
||||
// renderer.value.capabilities.maxTextures = 4
|
||||
// renderer.value.capabilities.maxTextureSize = 512
|
||||
// renderer.value.capabilities.precision = 'lowp'
|
||||
let composer: any = null
|
||||
|
||||
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;
|
||||
|
||||
camera.value?.position.set(1, 1, 1);
|
||||
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));
|
||||
(controls.value as any).update()
|
||||
|
||||
const sidebar_items = []
|
||||
clickable_items.value = []
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const element = data[index];
|
||||
sidebar_items.push({ ...element })
|
||||
const item = {} as model3DType
|
||||
|
||||
item.modelUrl = `${IMAGE_URL}/${element.model_file}`
|
||||
|
@ -55,47 +87,42 @@ const loadModels = async () => {
|
|||
const clickable_areas = await res.json()
|
||||
clickable.value.push(...clickable_areas)
|
||||
}
|
||||
sidebar_scene.setData(sidebar_items)
|
||||
|
||||
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]
|
||||
// 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.getCenter(world_position);
|
||||
((find_element as Mesh).geometry.boundingBox as any).getCenter(world_position);
|
||||
(find_element as Mesh).localToWorld(world_position);
|
||||
|
||||
const light = new PointLight()
|
||||
light.position.set(world_position.x, world_position.y * 5, world_position.z)
|
||||
light.color = index % 2 ? new Color('red') : new Color('green');
|
||||
light.power = 10000;
|
||||
const p = raw_data.min_distance * 0.05
|
||||
const plane = new PlaneGeometry(p, p, 32)
|
||||
|
||||
const point = new Mesh(new SphereGeometry(2, 16, 16), new MeshStandardMaterial({
|
||||
color: light.color,
|
||||
emissive: light.color,
|
||||
emissiveIntensity: 100
|
||||
}))
|
||||
point.position.set(light.position.x, light.position.y, light.position.z)
|
||||
const mesh_material = new MeshBasicMaterial({ side: DoubleSide })
|
||||
const sprite_material = new SpriteMaterial()
|
||||
if (element.image) {
|
||||
const map = new TextureLoader().load(`${IMAGE_URL}/${element.image}`);
|
||||
mesh_material.map = map
|
||||
sprite_material.map = map
|
||||
} else {
|
||||
mesh_material.color = new Color('red')
|
||||
sprite_material.color = new Color('red')
|
||||
}
|
||||
|
||||
const point = new Mesh(plane, mesh_material);
|
||||
// const point = new Sprite(sprite_material)
|
||||
point.position.set(world_position.x, p * 3, world_position.z)
|
||||
// point.scale.set(p, p, 1)
|
||||
point.name = `${element.id}_clickable`
|
||||
// light.add(point)
|
||||
point.renderOrder = 10
|
||||
|
||||
clickable_items.value.push(light)
|
||||
if (clickable_items.value.find(el => el.name == point.name)) continue
|
||||
clickable_items.value.push(point)
|
||||
clickable_objects.value.push({
|
||||
name: point.name,
|
||||
target: element.id,
|
||||
object: point,
|
||||
})
|
||||
}
|
||||
for (let index = 0; index < res_array.length; index++) {
|
||||
const r = res_array[index];
|
||||
let res = {
|
||||
name: r.name,
|
||||
target: element.id,
|
||||
object: r,
|
||||
}
|
||||
clickable_objects.value.push(res)
|
||||
clickable_refs.value.push(ref(`${element.id}_clickable`))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +140,35 @@ const loadModels = async () => {
|
|||
|
||||
controls.value.enabled = true;
|
||||
props.loaded()
|
||||
|
||||
composer = new EffectComposer(renderer.value);
|
||||
composer.addPass(new RenderPass(scene.value, camera.value));
|
||||
|
||||
const effect1 = new ShaderPass(DotScreenShader);
|
||||
effect1.uniforms['scale'].value = 40;
|
||||
composer.addPass(effect1);
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
composer.render();
|
||||
}
|
||||
// animate()
|
||||
}
|
||||
|
||||
const { onLoop } = useRenderLoop()
|
||||
onLoop(() => {
|
||||
clickable_refs.value.map(el => {
|
||||
// el.quaternion.copy(camera.value?.quaternion);
|
||||
if (el.value[0] && typeof el.value[0].lookAt == 'function') {
|
||||
el.value[0].lookAt(camera.value?.position)
|
||||
}
|
||||
})
|
||||
})
|
||||
const { onAfterRender } = useLoop()
|
||||
onAfterRender(() => {
|
||||
if (composer) {
|
||||
composer.render()
|
||||
}
|
||||
})
|
||||
|
||||
const openSidebar = (id: number) => {
|
||||
const target = clickable.value.find(el => el.id == id)
|
||||
|
@ -136,19 +191,55 @@ watch(() => props.source, () => {
|
|||
if (loaded) {
|
||||
loaded.children = []
|
||||
}
|
||||
sidebar.close()
|
||||
loadModels()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', clickEvent)
|
||||
if (sidebar.is_open) {
|
||||
sidebar.close()
|
||||
}
|
||||
})
|
||||
onUnmounted(() => { document.removeEventListener('click', clickEvent) })
|
||||
const pointer = reactive({ x: 0, y: 0 })
|
||||
const clickEvent = (event: MouseEvent) => {
|
||||
const x = (event.clientX / window.innerWidth) * 2 - 1
|
||||
const y = - (event.clientY / window.innerHeight) * 2 + 1
|
||||
if (x == pointer.x && y == pointer.y) return
|
||||
if (!camera.value) return
|
||||
|
||||
pointer.x = x
|
||||
pointer.y = y
|
||||
raycaster.value.setFromCamera(new Vector2(pointer.x, pointer.y), camera.value);
|
||||
|
||||
const intersects = raycaster.value.intersectObjects(seekAllByName(scene.value, '_clickable'));
|
||||
const names = intersects.map(el => el.object.name ?? false).filter(Boolean)
|
||||
if (names.length) {
|
||||
openSidebar(parseInt(names[0].replace('_clickable', '')))
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => sidebar_scene.list, () => {
|
||||
sidebar_scene.list.forEach(element => {
|
||||
const el = seekByName(scene.value, element.name)
|
||||
if (!el) return
|
||||
if (el.visible !== element.is_enabled) {
|
||||
el.visible = element.is_enabled
|
||||
}
|
||||
});
|
||||
}, { deep: true })
|
||||
</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">
|
||||
<TresObject3D v-if="item.type == 'PointLight'" v-bind="item.clone()" />
|
||||
<TresMesh v-else @click="() => openSidebar(item.name.replace('_clickable', ''))" v-bind="item" />
|
||||
<template v-for="(item, i) in clickable_items">
|
||||
<TresMesh v-bind="item" :ref="clickable_refs[i]" />
|
||||
</template>
|
||||
</TresGroup>
|
||||
</template>
|
|
@ -1,13 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { reactive, ref, watch, computed } from 'vue';
|
||||
import type { Ref } from 'vue'
|
||||
import { useRoute } from 'vue-router';
|
||||
import { RouterLink, useRoute } from 'vue-router';
|
||||
|
||||
import { Vector3 } from 'three';
|
||||
import { TresCanvas } from '@tresjs/core';
|
||||
import { StatsGl, OrbitControls } from '@tresjs/cientos'
|
||||
import { OrbitControls } from '@tresjs/cientos'
|
||||
import '@tresjs/leches/styles'
|
||||
|
||||
import Env from './env.vue'
|
||||
import LoadModels from './load_models.vue'
|
||||
import Sidebar from './sidebar.vue'
|
||||
import { usePromoSidebar } from '../../stores/promo_sidebar';
|
||||
|
@ -33,6 +33,7 @@ const camera = ref()
|
|||
const cameraPosition = ref([1, 1, 1]) as unknown as Ref<Vector3>
|
||||
|
||||
const controlsState = reactive({
|
||||
enableDamping: false,
|
||||
maxPolarAngle: (Math.PI / 2) - 0.05,
|
||||
minAzimuthAngle: (Math.PI / 2) - 0.02,
|
||||
})
|
||||
|
@ -56,23 +57,25 @@ watch(() => route.params.target, () => {
|
|||
<template>
|
||||
<div>
|
||||
<div :class="[{ 'loading': !models_loading }, 'canvas-wrapper']">
|
||||
<TresCanvas shadows window-size>
|
||||
<Suspense>
|
||||
<StatsGl />
|
||||
</Suspense>
|
||||
<TresCanvas window-size :alpha="false" power-preference="high-performance">
|
||||
<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>
|
||||
<TresMesh :position-y="0" :rotate-x="-Math.PI / 2" receive-shadow>
|
||||
<TresMesh :position-y="-1" :rotate-x="-Math.PI / 2" receive-shadow name="ground" v-if="false">
|
||||
<TresPlaneGeometry :args="[200, 200]" />
|
||||
<TresShadowMaterial :opacity="0.2" />
|
||||
</TresMesh>
|
||||
</TresCanvas>
|
||||
<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>
|
||||
|
@ -93,4 +96,25 @@ watch(() => route.params.target, () => {
|
|||
filter: blur(10px);
|
||||
transition: all 300ms linear;
|
||||
}
|
||||
|
||||
.homelink {
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
bottom: 2rem;
|
||||
|
||||
svg {
|
||||
font-size: 3rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-top: 2rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid white;
|
||||
background: white;
|
||||
line-height: 1;
|
||||
font-size: 0;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,27 +3,44 @@ 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 sidebar_scene = usePromoScene()
|
||||
const sidebar_obj = ref()
|
||||
|
||||
// onClickOutside(sidebar_obj, () => sidebar.close())
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="sidebar" :class="[{ 'open': sidebar.is_open }]" ref="sidebar_obj">
|
||||
<a href="#" @click.prevent="sidebar.close" class="sidebar-close">
|
||||
<i-mdi-close />
|
||||
</a>
|
||||
<template v-if="sidebar.is_open">
|
||||
<div class="sidebar-content">
|
||||
<template v-if="!sidebar.is_open"></template>
|
||||
<template v-else-if="sidebar.title">
|
||||
<h2>{{ sidebar.title }}</h2>
|
||||
<template v-if="sidebar.description">
|
||||
<template v-for="p in sidebar.description.split('\n')">
|
||||
<p>{{ p }}</p>
|
||||
</template>
|
||||
</template>
|
||||
<RouterLink class="btn" :to="`/promo/main/${sidebar.target}`" v-if="sidebar.target">
|
||||
{{ sidebar.target_name }}
|
||||
</RouterLink>
|
||||
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="sidebar-list-item" v-for="item in sidebar_scene.list">
|
||||
<input type="checkbox" v-model="item.is_enabled" :id="item.name" :disabled="item.can_not_disable" />
|
||||
<label :for="item.name">
|
||||
<h3>{{ item.name }}</h3>
|
||||
<template v-for="p in item.description.split('\n')">
|
||||
<p>{{ p }}</p>
|
||||
</template>
|
||||
</label>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
|
@ -35,8 +52,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,45 +68,68 @@ const sidebar_obj = ref()
|
|||
color: black;
|
||||
}
|
||||
|
||||
&-content {
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&-list-item {
|
||||
display: flex;
|
||||
|
||||
label {
|
||||
flex-grow: 1;
|
||||
margin-left: 0.25rem
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
margin: 1rem
|
||||
}
|
||||
|
||||
p,
|
||||
h3 {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1rem 0;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: white;
|
||||
transition: .2s linear;
|
||||
background: #0B63F6;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 7px;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
||||
background: #4FD1C5;
|
||||
background: linear-gradient(90deg, rgba(129, 230, 217, 1) 0%, rgba(79, 209, 197, 1) 100%);
|
||||
color: #313133;
|
||||
transition: .2s linear;
|
||||
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
min-width: 300px;
|
||||
min-height: 60px;
|
||||
display: inline-flex;
|
||||
font-family: 'Nunito', sans-serif;
|
||||
font-size: 22px;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.3px;
|
||||
font-weight: 700;
|
||||
color: #313133;
|
||||
background: #4FD1C5;
|
||||
background: linear-gradient(90deg, rgba(129, 230, 217, 1) 0%, rgba(79, 209, 197, 1) 100%);
|
||||
|
||||
border-radius: 1000px;
|
||||
|
||||
box-shadow: 12px 12px 24px rgba(79, 209, 197, .64);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
box-shadow: 0 0 0 2px white, 0 0 0 4px #3C82F8;
|
||||
box-shadow: 0 0 0 2px white, inset 0 0 0 4px #3C82F8;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -14,16 +14,21 @@ interface scene3D {
|
|||
name: string
|
||||
min_distance: number
|
||||
max_distance: number
|
||||
hdr_gainmap?: string
|
||||
hdr_json?: string
|
||||
hdr_webp?: string
|
||||
elements: element3DType[]
|
||||
}
|
||||
interface element3DType {
|
||||
id: number
|
||||
model_file: string
|
||||
name: string
|
||||
description: string
|
||||
description?: string
|
||||
parent?: number,
|
||||
min_distance?: number,
|
||||
max_distance?: number,
|
||||
is_enabled: boolean
|
||||
can_not_disable: boolean
|
||||
}
|
||||
interface model3DType {
|
||||
modelUrl?: string,
|
||||
|
@ -35,13 +40,14 @@ interface clickableAreaType {
|
|||
id: number;
|
||||
name: string;
|
||||
object_name: string;
|
||||
image?: string;
|
||||
source: number;
|
||||
target: number;
|
||||
target_name?: string
|
||||
}
|
||||
interface PromoSidebarData {
|
||||
title: string
|
||||
description: string
|
||||
title?: string
|
||||
description?: string
|
||||
target?: string
|
||||
target_name?: string
|
||||
}
|
||||
|
@ -49,3 +55,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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "interactive-table",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
Loading…
Reference in New Issue