bx-1140-postprocessing #12

Merged
ksenia_mikhailova merged 18 commits from bx-1140-postprocessing into dev 2024-07-30 09:17:41 +03:00
17 changed files with 332 additions and 112 deletions

View File

@ -168,6 +168,30 @@
"detail": "django.db", "detail": "django.db",
"documentation": {} "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": "migrations",
"importPath": "django.db",
"description": "django.db",
"isExtraImport": true,
"detail": "django.db",
"documentation": {}
},
{ {
"label": "models", "label": "models",
"importPath": "django.db", "importPath": "django.db",
@ -594,6 +618,23 @@
"detail": "object.models", "detail": "object.models",
"documentation": {} "documentation": {}
}, },
{
"label": "colorfield.fields",
"kind": 6,
"isExtraImport": true,
"importPath": "colorfield.fields",
"description": "colorfield.fields",
"detail": "colorfield.fields",
"documentation": {}
},
{
"label": "ColorField",
"importPath": "colorfield.fields",
"description": "colorfield.fields",
"isExtraImport": true,
"detail": "colorfield.fields",
"documentation": {}
},
{ {
"label": "Image", "label": "Image",
"importPath": "PIL", "importPath": "PIL",
@ -1177,12 +1218,30 @@
"detail": "back.object.migrations.0008_rename_can_disabled_element3d_can_not_disable", "detail": "back.object.migrations.0008_rename_can_disabled_element3d_can_not_disable",
"documentation": {} "documentation": {}
}, },
{
"label": "Migration",
"kind": 6,
"importPath": "back.object.migrations.0009_environment_remove_scene3d_hdr_gainmap_and_more",
"description": "back.object.migrations.0009_environment_remove_scene3d_hdr_gainmap_and_more",
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('object', '0008_rename_can_disabled_element3d_can_not_disable'),\n ]\n operations = [\n migrations.CreateModel(\n name='Environment',\n fields=[\n ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n ('env_displacementmap', models.FileField(blank=True, null=True, upload_to=object.models.group_based_upload_to)),",
"detail": "back.object.migrations.0009_environment_remove_scene3d_hdr_gainmap_and_more",
"documentation": {}
},
{
"label": "Migration",
"kind": 6,
"importPath": "back.object.migrations.0010_environment_clear_color",
"description": "back.object.migrations.0010_environment_clear_color",
"peekOfCode": "class Migration(migrations.Migration):\n dependencies = [\n ('object', '0009_environment_remove_scene3d_hdr_gainmap_and_more'),\n ]\n operations = [\n migrations.AddField(\n model_name='environment',\n name='clear_color',\n field=colorfield.fields.ColorField(blank=True, default=None, image_field=None, max_length=25, null=True, samples=None),\n ),",
"detail": "back.object.migrations.0010_environment_clear_color",
"documentation": {}
},
{ {
"label": "Scene3DAdmin", "label": "Scene3DAdmin",
"kind": 6, "kind": 6,
"importPath": "back.object.admin", "importPath": "back.object.admin",
"description": "back.object.admin", "description": "back.object.admin",
"peekOfCode": "class Scene3DAdmin(admin.ModelAdmin):\n filter_horizontal = ('elements',)\nadmin.site.register(Scene3D, Scene3DAdmin)\nadmin.site.register(Element3D)\nadmin.site.register(ClickableArea)", "peekOfCode": "class Scene3DAdmin(admin.ModelAdmin):\n filter_horizontal = (\"elements\",)\nadmin.site.register(Environment)\nadmin.site.register(Scene3D, Scene3DAdmin)\nadmin.site.register(Element3D)\nadmin.site.register(ClickableArea)",
"detail": "back.object.admin", "detail": "back.object.admin",
"documentation": {} "documentation": {}
}, },
@ -1195,6 +1254,15 @@
"detail": "back.object.apps", "detail": "back.object.apps",
"documentation": {} "documentation": {}
}, },
{
"label": "Environment",
"kind": 6,
"importPath": "back.object.models",
"description": "back.object.models",
"peekOfCode": "class Environment(models.Model):\n env_displacementmap = models.FileField(\n upload_to=group_based_upload_to, blank=True, null=True\n )\n env_normalmap = models.FileField(\n upload_to=group_based_upload_to, blank=True, null=True\n )\n clear_color = ColorField(blank=True, null=True)\n hdr_gainmap = models.FileField(\n upload_to=group_based_upload_to, blank=True, null=True",
"detail": "back.object.models",
"documentation": {}
},
{ {
"label": "Element3D", "label": "Element3D",
"kind": 6, "kind": 6,
@ -1209,7 +1277,7 @@
"kind": 6, "kind": 6,
"importPath": "back.object.models", "importPath": "back.object.models",
"description": "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 name = models.CharField(max_length=120)\n elements = models.ManyToManyField(Element3D)\n env = models.ForeignKey(Environment, models.RESTRICT, blank=True, null=True)\n min_distance = models.IntegerField(\n default=10,\n validators=[MinValueValidator(1), MaxValueValidator(600)],\n )\n max_distance = models.IntegerField(\n default=20,",
"detail": "back.object.models", "detail": "back.object.models",
"documentation": {} "documentation": {}
}, },
@ -1227,7 +1295,7 @@
"kind": 2, "kind": 2,
"importPath": "back.object.models", "importPath": "back.object.models",
"description": "back.object.models", "description": "back.object.models",
"peekOfCode": "def group_based_upload_to(instance, filename):\n logger.info(instance)\n return \"files/image/{}/{}/{}\".format(\n type(instance).__name__.lower(), instance.id, filename\n )\nclass 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(blank=True, null=True)", "peekOfCode": "def group_based_upload_to(instance, filename):\n logger.info(instance)\n return \"files/image/{}/{}/{}\".format(\n type(instance).__name__.lower(), instance.id, filename\n )\nclass Environment(models.Model):\n env_displacementmap = models.FileField(\n upload_to=group_based_upload_to, blank=True, null=True\n )\n env_normalmap = models.FileField(",
"detail": "back.object.models", "detail": "back.object.models",
"documentation": {} "documentation": {}
}, },
@ -1245,16 +1313,25 @@
"kind": 5, "kind": 5,
"importPath": "back.object.models", "importPath": "back.object.models",
"description": "back.object.models", "description": "back.object.models",
"peekOfCode": "logger = logging.getLogger(\"root\")\ndef group_based_upload_to(instance, filename):\n logger.info(instance)\n return \"files/image/{}/{}/{}\".format(\n type(instance).__name__.lower(), instance.id, filename\n )\nclass 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)", "peekOfCode": "logger = logging.getLogger(\"root\")\ndef group_based_upload_to(instance, filename):\n logger.info(instance)\n return \"files/image/{}/{}/{}\".format(\n type(instance).__name__.lower(), instance.id, filename\n )\nclass Environment(models.Model):\n env_displacementmap = models.FileField(\n upload_to=group_based_upload_to, blank=True, null=True\n )",
"detail": "back.object.models", "detail": "back.object.models",
"documentation": {} "documentation": {}
}, },
{
"label": "EnvironmentSerializer",
"kind": 6,
"importPath": "back.object.serializers",
"description": "back.object.serializers",
"peekOfCode": "class EnvironmentSerializer(serializers.ModelSerializer):\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 env_displacementmap = serializers.FileField(use_url=False)\n env_normalmap = serializers.FileField(use_url=False)\n class Meta:\n model = Environment\n fields = \"__all__\"\nclass Element3DSerializer(serializers.ModelSerializer):",
"detail": "back.object.serializers",
"documentation": {}
},
{ {
"label": "Element3DSerializer", "label": "Element3DSerializer",
"kind": 6, "kind": 6,
"importPath": "back.object.serializers", "importPath": "back.object.serializers",
"description": "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 hdr_gainmap = serializers.FileField(use_url=False)\n hdr_json = serializers.FileField(use_url=False)\n hdr_webp = serializers.FileField(use_url=False)", "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 env = EnvironmentSerializer()\n class Meta:\n model = Scene3D",
"detail": "back.object.serializers", "detail": "back.object.serializers",
"documentation": {} "documentation": {}
}, },
@ -1263,7 +1340,7 @@
"kind": 6, "kind": 6,
"importPath": "back.object.serializers", "importPath": "back.object.serializers",
"description": "back.object.serializers", "description": "back.object.serializers",
"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):", "peekOfCode": "class Scene3DSerializer(serializers.ModelSerializer):\n elements = Element3DSerializer(many=True)\n env = EnvironmentSerializer()\n class Meta:\n model = Scene3D\n fields = \"__all__\"\n depth = 2\nclass ClickableAreaSerializer(serializers.ModelSerializer):\n image = serializers.ImageField(use_url=False)\n class Meta:",
"detail": "back.object.serializers", "detail": "back.object.serializers",
"documentation": {} "documentation": {}
}, },

View File

@ -83,6 +83,7 @@ INSTALLED_APPS = [
"rest_framework", "rest_framework",
"crispy_forms", "crispy_forms",
"crispy_bootstrap4", "crispy_bootstrap4",
"colorfield",
"api", "api",
"frontImages", "frontImages",
"object", "object",

View File

@ -1,9 +1,12 @@
from django.contrib import admin from django.contrib import admin
from .models import ClickableArea, Element3D, Scene3D from .models import ClickableArea, Element3D, Environment, Scene3D
class Scene3DAdmin(admin.ModelAdmin): class Scene3DAdmin(admin.ModelAdmin):
filter_horizontal = ('elements',) filter_horizontal = ("elements",)
admin.site.register(Environment)
admin.site.register(Scene3D, Scene3DAdmin) admin.site.register(Scene3D, Scene3DAdmin)
admin.site.register(Element3D) admin.site.register(Element3D)
admin.site.register(ClickableArea) admin.site.register(ClickableArea)

View File

@ -2,6 +2,7 @@ from PIL import Image
from django.db import models from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from colorfield.fields import ColorField
import logging import logging
logger = logging.getLogger("root") logger = logging.getLogger("root")
@ -14,6 +15,21 @@ def group_based_upload_to(instance, filename):
) )
class Environment(models.Model):
env_displacementmap = models.FileField(
upload_to=group_based_upload_to, blank=True, null=True
)
env_normalmap = models.FileField(
upload_to=group_based_upload_to, blank=True, null=True
)
clear_color = 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): class Element3D(models.Model):
parent = models.ForeignKey("self", on_delete=models.PROTECT, blank=True, null=True) parent = models.ForeignKey("self", on_delete=models.PROTECT, blank=True, null=True)
model_file = models.FileField(upload_to=group_based_upload_to) model_file = models.FileField(upload_to=group_based_upload_to)
@ -27,27 +43,19 @@ class Element3D(models.Model):
class Scene3D(models.Model): class Scene3D(models.Model):
filter_horizontal = ("elements",) name = models.CharField(max_length=120)
name = models.CharField(
max_length=120,
)
elements = models.ManyToManyField(Element3D) elements = models.ManyToManyField(Element3D)
env = models.ForeignKey(Environment, models.RESTRICT, blank=True, null=True)
min_distance = models.IntegerField( min_distance = models.IntegerField(
validators=[MinValueValidator(1), MaxValueValidator(600)], blank=True, null=True default=10,
validators=[MinValueValidator(1), MaxValueValidator(600)],
) )
max_distance = models.IntegerField( max_distance = models.IntegerField(
default=20,
validators=[MinValueValidator(2), MaxValueValidator(1000)], 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): def __str__(self):
return self.name return self.name

View File

@ -1,5 +1,17 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Scene3D, Element3D, ClickableArea from .models import Scene3D, Element3D, ClickableArea, Environment
class EnvironmentSerializer(serializers.ModelSerializer):
hdr_gainmap = serializers.FileField(use_url=False)
hdr_json = serializers.FileField(use_url=False)
hdr_webp = serializers.FileField(use_url=False)
env_displacementmap = serializers.FileField(use_url=False)
env_normalmap = serializers.FileField(use_url=False)
class Meta:
model = Environment
fields = "__all__"
class Element3DSerializer(serializers.ModelSerializer): class Element3DSerializer(serializers.ModelSerializer):
@ -12,9 +24,7 @@ class Element3DSerializer(serializers.ModelSerializer):
class Scene3DSerializer(serializers.ModelSerializer): class Scene3DSerializer(serializers.ModelSerializer):
elements = Element3DSerializer(many=True) elements = Element3DSerializer(many=True)
hdr_gainmap = serializers.FileField(use_url=False) env = EnvironmentSerializer()
hdr_json = serializers.FileField(use_url=False)
hdr_webp = serializers.FileField(use_url=False)
class Meta: class Meta:
model = Scene3D model = Scene3D

View File

@ -1,6 +1,7 @@
asgiref==3.8.1 ; python_version >= "3.10" and python_version < "4.0" asgiref==3.8.1 ; python_version >= "3.10" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0"
crispy-bootstrap4==2024.1 ; python_version >= "3.10" and python_version < "4.0" crispy-bootstrap4==2024.1 ; python_version >= "3.10" and python_version < "4.0"
django-colorfield==0.11.0 ; python_version >= "3.10" and python_version < "4.0"
django-cors-headers==4.3.1 ; python_version >= "3.10" and python_version < "4.0" django-cors-headers==4.3.1 ; python_version >= "3.10" and python_version < "4.0"
django-crispy-forms==2.2 ; python_version >= "3.10" and python_version < "4.0" django-crispy-forms==2.2 ; python_version >= "3.10" and python_version < "4.0"
django-extensions==3.2.3 ; python_version >= "3.10" and python_version < "4.0" django-extensions==3.2.3 ; python_version >= "3.10" and python_version < "4.0"

BIN
front/public/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
front/public/picture_02.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1,35 +1,53 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, watch } from 'vue'; import { onMounted, watch } from 'vue';
import { EquirectangularReflectionMapping, ReinhardToneMapping } from 'three'; import { ACESFilmicToneMapping, Color, Fog, PMREMGenerator, SRGBColorSpace } from 'three';
import { GainMapLoader } from '@monogrid/gainmap-js' import { GainMapLoader } from '@monogrid/gainmap-js'
import { useTresContext } from '@tresjs/core'; import { useTresContext } from '@tresjs/core';
import hdr_gainmap from '../../assets/promo/hdr/hdr-gainmap.webp' import hdr_gainmap from '../../assets/promo/hdr/hdr-gainmap.webp'
import hdr_json from '../../assets/promo/hdr/hdr.json?url' import hdr_json from '../../assets/promo/hdr/hdr.json?url'
import hdr_webp from '../../assets/promo/hdr/hdr.webp' import hdr_webp from '../../assets/promo/hdr/hdr.webp'
import { IMAGE_URL, PROMOBG } from '../../constants';
const props = defineProps(['hdr_webp', 'hdr_gainmap', 'hdr_json']) const props = defineProps(['hdr_webp', 'hdr_gainmap', 'hdr_json', 'env_displacementmap', 'env_normalmap', 'clear_color', 'focus'])
const { renderer, scene } = useTresContext() const { renderer, scene } = useTresContext()
const k = { start: 0.5, end: 3.5 }
renderer.value.outputColorSpace = SRGBColorSpace;
renderer.value.toneMapping = ACESFilmicToneMapping;
renderer.value.toneMappingExposure = 1.25;
renderer.value.setPixelRatio(1.5)
const loadEnv = async () => { const loadEnv = async () => {
// console.log('update env')
const loader = new GainMapLoader(renderer.value) const loader = new GainMapLoader(renderer.value)
const result = await loader.loadAsync([ const result = await loader.loadAsync([
props.hdr_webp || hdr_webp, props.hdr_webp ? `${IMAGE_URL}/${props.hdr_webp}` : hdr_webp,
props.hdr_gainmap || hdr_gainmap, props.hdr_gainmap ? `${IMAGE_URL}/${props.hdr_gainmap}` : hdr_gainmap,
props.hdr_json || hdr_json, props.hdr_json ? `${IMAGE_URL}/${props.hdr_json}` : hdr_json,
]) ])
const pmremGenerator = new PMREMGenerator(renderer.value);
pmremGenerator.compileEquirectangularShader();
const exrCubeRenderTarget = pmremGenerator.fromEquirectangular(result.renderTarget.texture);
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;
scene.value.environment = newEnvMap
scene.value.environmentIntensity = 1
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(); result.renderTarget.texture.dispose();
const c = new Color()
c.set(props.clear_color || PROMOBG)
renderer.value.setClearColor(c)
scene.value.fog = new Fog(c, props.focus * k.start, props.focus * k.end)
} }
renderer.value.toneMapping = ReinhardToneMapping
onMounted(async () => { onMounted(async () => {
loadEnv() // loadEnv()
}) })
watch(() => props.hdr_webp, loadEnv) watch(() => props, loadEnv, { deep: true })
</script> </script>
<template></template> <template></template>

View File

@ -1,21 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'; import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { import {
Box3, Color, DoubleSide, Group, Mesh, MeshBasicMaterial, Box3, CircleGeometry, Color, DoubleSide, Group, Mesh, MeshBasicMaterial,
MeshStandardMaterial,
MeshStandardMaterialParameters,
PlaneGeometry, SpriteMaterial, TextureLoader, Vector2, Vector3, PlaneGeometry, SpriteMaterial, TextureLoader, Vector2, Vector3,
} from 'three'; } from 'three';
import { useTresContext, useSeek, useRenderLoop } from '@tresjs/core'; import { useTresContext, useSeek, useRenderLoop, useTexture, useLoop } from '@tresjs/core';
import { useGLTF } from '@tresjs/cientos' import { useGLTF } from '@tresjs/cientos'
import Env from './env.vue' import Env from './env.vue'
import PostProcess from './post_pocessing.vue'
import { IMAGE_URL, SERVER_URL, } from '../../constants' import { IMAGE_URL, PROMOBG, SERVER_URL, } from '../../constants'
import { usePromoSidebar } from '../../stores/promo_sidebar'; import { usePromoSidebar } from '../../stores/promo_sidebar';
import { usePromoScene } from '../../stores/promo_scene'; import { usePromoScene } from '../../stores/promo_scene';
const props = defineProps(['source', 'loaded', 'loaded_pan', 'clearColor']) const props = defineProps(['source', 'loaded', 'loaded_pan'])
const models = ref<model3DType[]>([]) const models = ref<model3DType[]>([])
const clickable = ref<clickableAreaType[]>([]) const clickable = ref<clickableAreaType[]>([])
@ -23,26 +24,46 @@ const clickable_items = ref<any[]>([])
const clickable_refs = ref<any[]>([]) const clickable_refs = ref<any[]>([])
const sidebar = usePromoSidebar(); const sidebar = usePromoSidebar();
const sidebar_scene = usePromoScene() const sidebar_scene = usePromoScene()
const { controls, camera, scene, raycaster } = useTresContext() const { controls, camera, scene, raycaster, renderer } = useTresContext()
const { pause, resume } = useLoop()
const { seekByName, seekAllByName } = useSeek() const { seekByName, seekAllByName } = useSeek()
const envVars = reactive({}) as { hdr_gainmap?: string, hdr_json?: string, hdr_webp?: string } const envVars = reactive({}) as {
const tiltShift = reactive({}) as { focus: number, aperture: number, maxblur: number } focus: number,
hdr_gainmap?: string,
hdr_json?: string,
hdr_webp?: string,
clear_color?: string,
env_displacementmap?: string,
env_normalmap?: string
}
const groundTexture = await useTexture({
displacementMap: '/ground_displacement.jpg',
})
const timer = ref(10)
let int
// renderer.value.capabilities.maxTextures = 4 // renderer.value.capabilities.maxTextures = 4
// renderer.value.capabilities.maxTextureSize = 512 renderer.value.capabilities.maxTextureSize = 512
// renderer.value.capabilities.precision = 'lowp' renderer.value.capabilities.precision = 'lowp'
const loadModels = async () => { const loadModels = async () => {
const res = await fetch(`${SERVER_URL}/api/obj/scene/${props.source}`) const res = await fetch(`${SERVER_URL}/api/obj/scene/${props.source}`)
const raw_data = await res.json() as scene3D const raw_data = await res.json() as scene3D
envVars.hdr_gainmap = raw_data.hdr_gainmap ? `${IMAGE_URL}/${raw_data.hdr_gainmap}` : undefined envVars.focus = raw_data.min_distance * 1
envVars.hdr_json = raw_data.hdr_json ? `${IMAGE_URL}/${raw_data.hdr_json}` : undefined if (raw_data.env) {
envVars.hdr_webp = raw_data.hdr_webp ? `${IMAGE_URL}/${raw_data.hdr_webp}` : undefined Object.assign(envVars, raw_data.env)
} else {
delete envVars.env_displacementmap
delete envVars.env_normalmap
delete envVars.hdr_gainmap
delete envVars.hdr_json
delete envVars.hdr_webp
envVars.clear_color = PROMOBG
}
tiltShift.focus = raw_data.max_distance * 0.33
tiltShift.aperture = 10
tiltShift.maxblur = 1
const data = raw_data.elements const data = raw_data.elements
if (!controls.value) return; if (!controls.value) return;
@ -66,12 +87,54 @@ const loadModels = async () => {
item.modelFile = loaded_scene item.modelFile = loaded_scene
item.name = element.name item.name = element.name
if (!element.is_enabled) {
item.modelFile.visible = false
}
models.value.push(item) models.value.push(item)
const res = await fetch(`${SERVER_URL}/api/obj/clickable/?source=${element.id}`) const res = await fetch(`${SERVER_URL}/api/obj/clickable/?source=${element.id}`)
const clickable_areas = await res.json() const clickable_areas = await res.json()
clickable.value.push(...clickable_areas) clickable.value.push(...clickable_areas)
} }
let c = new Color()
if (envVars.clear_color) {
c.set(envVars.clear_color)
} else {
renderer.value.getClearColor(c)
}
const tex = {} as any
if (envVars.env_displacementmap) { tex.displacementMap = `${IMAGE_URL}/${envVars.env_displacementmap}` }
if (envVars.env_normalmap) { tex.normalMap = `${IMAGE_URL}/${envVars.env_normalmap}` }
let addTexture: any
if (Object.keys(tex).length > 0) {
addTexture = await useTexture(tex)
}
const mesh = {
color: c.offsetHSL(0, 0.5, -0.33),
displacementScale: envVars.focus * 0.33,
roughness: 100,
side: DoubleSide
} as MeshStandardMaterialParameters
if (envVars.env_displacementmap) {
mesh.displacementMap = addTexture.displacementMap
} else {
mesh.displacementMap = groundTexture.displacementMap
}
if (envVars.env_normalmap) {
mesh.normalMap = addTexture.normalMap
}
const ground = new Mesh(
new PlaneGeometry(envVars.focus * 7, envVars.focus * 7, 1024, 1024),
new MeshStandardMaterial(mesh)
)
ground.position.y = -0.33 * envVars.focus
ground.rotateX(-Math.PI / 2)
ground.name = "ground"
models.value.push({ name: 'ground', modelFile: ground })
sidebar_scene.setData(sidebar_items) sidebar_scene.setData(sidebar_items)
for (let index = 0; index < clickable.value.length; index++) { for (let index = 0; index < clickable.value.length; index++) {
@ -116,21 +179,44 @@ const loadModels = async () => {
box.getSize(box_size) box.getSize(box_size)
props.loaded_pan( 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),
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.25, box_size.z * -0.5),
) )
} }
controls.value.enabled = true; controls.value.enabled = true;
props.loaded(false) props.loaded(false)
clearInterval(int)
timer.value = 10
int = setInterval(() => {
if (timer.value > 0) {
timer.value -= 1
} else if (timer.value == 0 && !controls.value.autoRotate) {
pause()
camera.value?.position.set(
controls.value.minDistance * 0.5,
controls.value.minDistance * 0.5,
controls.value.minDistance
);
(controls.value as any).autoRotate = true;
(controls.value as any).autoRotateSpeed = 1;
resume()
}
}, 1000)
} }
const { onLoop } = useRenderLoop() const { onAfterRender } = useLoop()
onLoop(() => { onAfterRender(() => {
clickable_refs.value.map(el => { clickable_refs.value.map(el => {
if (el.value[0] && typeof el.value[0].lookAt == 'function') { if (el.value[0] && typeof el.value[0].lookAt == 'function') {
el.value[0].lookAt(camera.value?.position) el.value[0].lookAt(camera.value?.position);
} }
}) })
if (controls.value) {
if (timer.value == 0) {
(controls.value as any).update();
}
}
}) })
const openSidebar = (id: number) => { const openSidebar = (id: number) => {
@ -160,12 +246,24 @@ watch(() => props.source, () => {
onMounted(() => { onMounted(() => {
document.addEventListener('click', clickEvent) document.addEventListener('click', clickEvent)
document.addEventListener('click', stopTimer)
document.addEventListener('touchstart', stopTimer)
if (sidebar.is_open) { if (sidebar.is_open) {
sidebar.close() sidebar.close()
} }
}) })
onUnmounted(() => { document.removeEventListener('click', clickEvent) }) onUnmounted(() => {
document.removeEventListener('click', clickEvent)
document.removeEventListener('click', stopTimer)
document.removeEventListener('touchstart', stopTimer)
})
const pointer = reactive({ x: 0, y: 0 }) const pointer = reactive({ x: 0, y: 0 })
const stopTimer = () => {
timer.value = 10;
if (controls.value.autoRotate) {
controls.value.autoRotate = false;
}
}
const clickEvent = (event: MouseEvent) => { const clickEvent = (event: MouseEvent) => {
const x = (event.clientX / window.innerWidth) * 2 - 1 const x = (event.clientX / window.innerWidth) * 2 - 1
const y = - (event.clientY / window.innerHeight) * 2 + 1 const y = - (event.clientY / window.innerHeight) * 2 + 1
@ -196,7 +294,6 @@ watch(() => sidebar_scene.list, () => {
<template> <template>
<TresGroup name="loaded"> <TresGroup name="loaded">
<Env v-bind="envVars" /> <Env v-bind="envVars" />
<PostProcess :tiltShift="tiltShift" :clearColor="props.clearColor" />
<template v-for="item in models"> <template v-for="item in models">
<TresGroup :name="item.name"> <TresGroup :name="item.name">
<TresObject3D v-bind="item.modelFile.clone()" /> <TresObject3D v-bind="item.modelFile.clone()" />

View File

@ -5,12 +5,13 @@ import { RouterLink, useRoute } from 'vue-router';
import { Vector3 } from 'three'; import { Vector3 } from 'three';
import { TresCanvas } from '@tresjs/core'; import { TresCanvas } from '@tresjs/core';
import { OrbitControls } from '@tresjs/cientos' import { OrbitControls, Stats } from '@tresjs/cientos'
import '@tresjs/leches/styles' import '@tresjs/leches/styles'
import LoadModels from './load_models.vue' import LoadModels from './load_models.vue'
import Sidebar from './sidebar.vue' import Sidebar from './sidebar.vue'
import { usePromoSidebar } from '../../stores/promo_sidebar'; import { usePromoSidebar } from '../../stores/promo_sidebar';
import { PROMOBG } from '../../constants';
const minPan = ref(new Vector3(-2, -2, -2)) const minPan = ref(new Vector3(-2, -2, -2))
const maxPan = ref(new Vector3(2, 2, 2)) const maxPan = ref(new Vector3(2, 2, 2))
@ -35,7 +36,7 @@ const cameraPosition = ref([1, 1, 1]) as unknown as Ref<Vector3>
const controlsState = reactive({ const controlsState = reactive({
enableDamping: false, enableDamping: false,
maxPolarAngle: (Math.PI / 2) - 0.07, maxPolarAngle: (Math.PI / 2) - 0.07,
minAzimuthAngle: (Math.PI / 2) - 0.07, minAzimuthAngle: (Math.PI / 2) - 0.20,
}) })
const models_loading = ref(true) const models_loading = ref(true)
const set_model_load_status = (status: boolean) => { const set_model_load_status = (status: boolean) => {
@ -54,22 +55,18 @@ watch(() => route.params.target, () => {
} }
}, { deep: true }) }, { deep: true })
const clearColor = 'steelBlue'
</script> </script>
<template> <template>
<div> <div>
<div :class="[{ 'loading': models_loading }, 'canvas-wrapper']"> <div :class="[{ 'loading': models_loading }, 'canvas-wrapper']">
<TresCanvas window-size :alpha="false" power-preference="high-performance" :clear-color="clearColor"> <TresCanvas window-size :alpha="false" power-preference="high-performance" :clear-color="PROMOBG"
:shadows="false">
<Stats />
<TresPerspectiveCamera :position="cameraPosition" ref="camera" /> <TresPerspectiveCamera :position="cameraPosition" ref="camera" />
<OrbitControls v-bind="controlsState" @change="onChange" make-default /> <OrbitControls v-bind="controlsState" @change="onChange" make-default />
<Suspense> <Suspense>
<LoadModels :source="source" :loaded="set_model_load_status" :loaded_pan="loadedPan" :clear-color="clearColor" /> <LoadModels :source="source" :loaded="set_model_load_status" :loaded_pan="loadedPan" />
</Suspense> </Suspense>
<TresMesh :position-y="-1" :rotate-x="-Math.PI / 2" receive-shadow>
<TresPlaneGeometry :args="[2000, 2000]" />
<!-- <TresShadowMaterial :opacity="0.2" /> -->
<TresMeshStandardMaterial :color="clearColor" />
</TresMesh>
</TresCanvas> </TresCanvas>
<div class="homelink"> <div class="homelink">
<a href="#" @click.prevent="sidebar.open" v-if="!sidebar.is_open"> <a href="#" @click.prevent="sidebar.open" v-if="!sidebar.is_open">

View File

@ -8,16 +8,17 @@ import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
import { useTresContext, useLoop } from '@tresjs/core'; import { useTresContext, useLoop } from '@tresjs/core';
import { watch } from 'vue'; import { watch } from 'vue';
import { Fog, FogExp2 } from 'three'; import { Color, Fog } from 'three';
const { renderer, camera, scene } = useTresContext() const { renderer, camera, scene } = useTresContext()
const composer = new EffectComposer(renderer.value); const composer = new EffectComposer(renderer.value);
const props = defineProps(['tiltShift', 'clearColor']) const props = defineProps(['tiltShift'])
const k = { start: 0.25, end: 3 } const k = { start: 0.5, end: 2.5 }
if (camera.value) { const makePostProcess = () => {
if (camera.value) {
const renderPass = new RenderPass(scene.value, camera.value); const renderPass = new RenderPass(scene.value, camera.value);
const bokehPass = new BokehPass(scene.value, camera.value, { const bokehPass = new BokehPass(scene.value, camera.value, {
@ -25,36 +26,21 @@ if (camera.value) {
aperture: (props.tiltShift.aperture) * 0.00001, aperture: (props.tiltShift.aperture) * 0.00001,
maxblur: (props.tiltShift.maxblur) * 0.01 maxblur: (props.tiltShift.maxblur) * 0.01
}); });
const outputPass = new OutputPass(); const outputPass = new OutputPass();
composer.addPass(renderPass); // composer.addPass(renderPass);
composer.addPass(bokehPass); // composer.addPass(bokehPass);
composer.addPass(outputPass); // composer.addPass(outputPass);
// scene.value.fog = new FogExp2(0xff0000, 0.005) const c = new Color()
scene.value.fog = new Fog(props.clearColor, props.tiltShift.focus * k.start, props.tiltShift.focus * k.end) renderer.value.getClearColor(c)
scene.value.fog = new Fog(c, props.tiltShift.focus * k.start, props.tiltShift.focus * k.end)
}
} }
makePostProcess()
const { onAfterRender } = useLoop() const { onAfterRender } = useLoop()
onAfterRender(() => { onAfterRender(() => {
composer.render() // composer.render()
}) })
watch(props.tiltShift, () => { watch(props.tiltShift, makePostProcess, { deep: true })
if (camera.value) {
const args = {
focus: props.tiltShift.focus,
aperture: props.tiltShift.aperture * 0.00001,
maxblur: props.tiltShift.maxblur * 0.01
}
const bokehPass = new BokehPass(scene.value, camera.value, args);
composer.passes = []
const renderPass = new RenderPass(scene.value, camera.value);
const outputPass = new OutputPass();
composer.addPass(renderPass);
composer.addPass(bokehPass);
composer.addPass(outputPass);
scene.value.fog = new Fog(props.clearColor, props.tiltShift.focus * k.start, props.tiltShift.focus * k.end)
}
}, { deep: true })
</script> </script>
<template></template> <template></template>

View File

@ -1,2 +1,4 @@
export const SERVER_URL = import.meta.env.VITE_SERVER_URL ?? window.location.origin export const SERVER_URL = import.meta.env.VITE_SERVER_URL ?? window.location.origin
export const IMAGE_URL = import.meta.env.VITE_IMAGE_URL ?? window.location.origin export const IMAGE_URL = import.meta.env.VITE_IMAGE_URL ?? window.location.origin
export const PROMOBG='#ccc'

View File

@ -14,10 +14,15 @@ interface scene3D {
name: string name: string
min_distance: number min_distance: number
max_distance: number max_distance: number
elements: element3DType[]
env: {
hdr_gainmap?: string hdr_gainmap?: string
hdr_json?: string hdr_json?: string
hdr_webp?: string hdr_webp?: string
elements: element3DType[] env_displacementmap?: string
env_normalmap?: string
clear_color?: string
}
} }
interface element3DType { interface element3DType {
id: number id: number

16
poetry.lock generated
View File

@ -63,6 +63,20 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
argon2 = ["argon2-cffi (>=19.1.0)"] argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"] bcrypt = ["bcrypt"]
[[package]]
name = "django-colorfield"
version = "0.11.0"
description = "color field for django models with a nice color-picker in the admin."
optional = false
python-versions = "*"
files = [
{file = "django-colorfield-0.11.0.tar.gz", hash = "sha256:05c38c8eb2a94938b810a19b2011846391a4ce71d1c92e88a35974fbcc8fc62e"},
{file = "django_colorfield-0.11.0-py3-none-any.whl", hash = "sha256:460f40e6123b6ae0fb51a4eb86fc258fcdc0ea28f75102b685e8209b1eae9ec3"},
]
[package.dependencies]
Pillow = ">=9.0.0"
[[package]] [[package]]
name = "django-cors-headers" name = "django-cors-headers"
version = "4.3.1" version = "4.3.1"
@ -449,4 +463,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "be85620e01baf8ceb925bd6fe9283c50da4fc1a97d9f572b92266adb0f44c124" content-hash = "6b760d833dfd807526e05ba86e0e6cddb82ee47ec315f4a51d469aecfd1534e4"

View File

@ -21,6 +21,7 @@ django-filter = "^24.2"
django-crispy-forms = "^2.2" django-crispy-forms = "^2.2"
crispy-bootstrap4 = "^2024.1" crispy-bootstrap4 = "^2024.1"
django-extensions = "^3.2.3" django-extensions = "^3.2.3"
django-colorfield = "^0.11.0"
[build-system] [build-system]