commit a600a7ad213b64596ddf8c6857adcde14af64cb1 Author: Oleg Tolchin Date: Sat Feb 21 16:06:11 2026 +0300 Clean start: remove large .spp files from history diff --git a/README.md b/README.md new file mode 100644 index 0000000..880136b --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Room-3D + +Интерактивный 3D-сайт для демонстрации контактов, презентации и резюме. + +## О проекте + +Это современный веб-сайт с 3D-интерактивной сценой интерьера, разработанный на базе **Three.js**. Проект служит персональной визитной карточкой, презентацией портфолио и онлайн-резюме. + +## Возможности + +- 🏠 Интерактивная 3D-комната с реалистичным освещением +- 🖱️ Вращение модели мышью и зум колёсиком +- 💡 Реалистичное освещение: Spot-светильники и LED-ленты +- 🌆 HDRI-окружение для фотореалистичности +- 📱 Адаптивный дизайн + +## Технологии + +- **Three.js** — 3D-графика +- **HTML5 / CSS3** — верстка и стили +- **JavaScript (ES6 modules)** — логика приложения + +## Установка и запуск + +Проект не требует сборки. Просто откройте `index.html` в браузере или используйте локальный сервер: + +```bash +# Python 3 +python -m http.server 8000 + +# Node.js (если установлен http-server) +npx http-server +``` + +Затем перейдите по адресу: `http://localhost:8000` + +## Структура проекта + +``` +/ +├── index.html # Главная страница +├── css/ +│ └── style.css # Стили +├── js/ +│ ├── main.js # Точка входа +│ ├── scene.js # Сцена и рендерер +│ ├── camera.js # Камера +│ ├── controls.js # Управление мышью +│ └── lights.js # Освещение +├── models/ # 3D-модели +└── textures/ # Текстуры и HDRI +``` + +## Лицензия + +© Oleg Tolchin diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..074b36f --- /dev/null +++ b/css/style.css @@ -0,0 +1,35 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + overflow: hidden; + background: #111; + font-family: Arial, sans-serif; +} + +#scene { + position: fixed; + inset: 0; + display: block; +} + +.ui { + position: absolute; + top: 40px; + left: 40px; + color: white; + z-index: 10; + display: none; +} + +.ui h1 { + font-size: 32px; + margin-bottom: 8px; +} + +.ui p { + opacity: 0.7; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..4628687 --- /dev/null +++ b/index.html @@ -0,0 +1,29 @@ + + + + + 3D Interior + + + + + + + + + +
+

Современный интерьер

+

Интерактивная 3D-сцена

+
+ + + + diff --git a/js/camera.js b/js/camera.js new file mode 100644 index 0000000..20405fa --- /dev/null +++ b/js/camera.js @@ -0,0 +1,12 @@ +import * as THREE from 'three'; + +export const camera = new THREE.PerspectiveCamera( + 27, + window.innerWidth / window.innerHeight, + 0.1, + 1000 +); + +// Камера смотрит в комнату через открытую стену +camera.position.set(0, 0, 0); +camera.lookAt(0, 0, 0); diff --git a/js/controls.js b/js/controls.js new file mode 100644 index 0000000..02611d4 --- /dev/null +++ b/js/controls.js @@ -0,0 +1,50 @@ +import { camera } from './camera.js'; +import { room } from './scene.js'; + +export const controls = { + isDragging: false, + previousMousePosition: { x: 0, y: 0 }, + rotationSpeed: 0.01, + zoomSpeed: 0.1, + minZoom: 1, + maxZoom: 20, + + // Обновление — пока не нужно, всё в событиях + update: function() {} +}; + +// --- Вращение модели при нажатой левой кнопке мыши --- +document.addEventListener('mousedown', (event) => { + if (event.button === 0) { + controls.isDragging = true; + controls.previousMousePosition = { x: event.clientX, y: event.clientY }; + } +}); + +document.addEventListener('mouseup', () => { + controls.isDragging = false; +}); + +document.addEventListener('mousemove', (event) => { + if (controls.isDragging && room) { + const deltaX = event.clientX - controls.previousMousePosition.x; + const deltaY = event.clientY - controls.previousMousePosition.y; + + room.rotation.y += deltaX * controls.rotationSpeed; + room.rotation.x += deltaY * controls.rotationSpeed; + + // Ограничение наклона по X (чтобы не перевернуть комнату) + room.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, room.rotation.x)); + } + + controls.previousMousePosition = { x: event.clientX, y: event.clientY }; +}); + +// --- Зум колёсиком мыши --- +document.addEventListener('wheel', (event) => { + const delta = event.deltaY * 0.01; // Чем меньше число, тем мягче зум + const newZ = camera.position.z + delta * controls.zoomSpeed * camera.position.z; + + // Ограничение диапазона зума + camera.position.z = Math.max(controls.minZoom, Math.min(controls.maxZoom, newZ)); +}); diff --git a/js/lights.js b/js/lights.js new file mode 100644 index 0000000..de457eb --- /dev/null +++ b/js/lights.js @@ -0,0 +1,238 @@ +import * as THREE from 'three'; +import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js'; +import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js'; + +RectAreaLightUniformsLib.init(); + +/** + * Базовый свет сцены (Ambient + Directional) + */ +export function setupLights(scene) { + const ambient = new THREE.AmbientLight(0xffffff, 0.8); + scene.add(ambient); + + const key = new THREE.DirectionalLight(0xffffff, 0.2); + key.position.set(0, 0, 0); + scene.add(key); + + const fill = new THREE.DirectionalLight(0xffffff, 0.2); + fill.position.set(0, 0, 0); + scene.add(fill); +} + +/** + * Потолочные споты, привязанные к Spot_* + */ +export function setupRoomLights(room) { + const spotNames = [ + 'Spot_001','Spot_002','Spot_003','Spot_004', + 'Spot_005','Spot_006','Spot_007','Spot_008' + ]; + + spotNames.forEach((spotName) => { + const spotObject = room.getObjectByName(spotName); + if (!spotObject) { + console.warn(`Объект не найден: ${spotName}`); + return; + } + + // === СПОТ-СВЕТ === + const spotLight = new THREE.SpotLight( + 0xffffff, + 5, // intensity + 80, // distance + Math.PI / 4, // angle + 0.5, // penumbra + 1.5 // decay (физически корректный) + ); + + spotObject.add(spotLight); + spotLight.position.set(0, 0, 0); + + // === Target (локально по -Z) === + spotObject.add(spotLight.target); + spotLight.target.position.set(0, 0, -1); + spotLight.target.updateMatrixWorld(true); + + // === ВИЗУАЛЬНЫЙ СВЕТЯЩИЙСЯ КРУГ (LED-линза) === + const bulb = new THREE.Mesh( + new THREE.CircleGeometry(0.01, 24), + new THREE.MeshStandardMaterial({ + color: 0xffffff, + emissive: 0xffffff, + emissiveIntensity: 2.5, + roughness: 0.2, + metalness: 0.0, + side: THREE.DoubleSide + }) + ); + + // CircleGeometry смотрит по +Z → поворачиваем вниз + bulb.rotation.x = -Math.PI; + bulb.position.set(0, 0, 0.001); + + spotObject.add(bulb); + + console.log(`Спот корректно создан: ${spotName}`); + }); +} + +/** + * Создает LED-ленту с рассеянным светом на объекте ceilinglight + * @param {THREE.Object3D} ceilinglight - объект для привязки LED-ленты + * @param {Object} options - настройки { position: [x,y,z], rotationDeg: [x,y,z], ... } + */ +function createLEDStrip(ceilinglight, options = {}) { + // Конвертация градусов в радианы + const toRad = (deg) => deg * Math.PI / 180; + + // Размеры LED-ленты (подстрой под модель) + const width = options.width || 0.01; + const height = options.height || 1.0; + + // RectAreaLight - рассеянный свет + const rectLight = new THREE.RectAreaLight( + options.color || 0xffffff, + options.intensity || 6, + width, + height + ); + ceilinglight.add(rectLight); + + // Позиция + if (options.position) { + rectLight.position.set(...options.position); + } + + // Поворот (градусы) + if (options.rotationDeg) { + rectLight.rotation.set(...options.rotationDeg.map(toRad)); + } + + // Направление света (target - локальные координаты) + if (options.target) { + const targetObj = new THREE.Object3D(); + targetObj.position.set(...options.target); + ceilinglight.add(targetObj); + rectLight.target = targetObj; + } + + // Визуальная полоса LED + const ledMesh = new THREE.Mesh( + new THREE.PlaneGeometry(width, height), + new THREE.MeshStandardMaterial({ + color: 0xffffff, + emissive: 0xffffff, + emissiveIntensity: 2, + side: THREE.DoubleSide + }) + ); + if (options.position) { + ledMesh.position.set(...options.position); + } + if (options.rotationDeg) { + ledMesh.rotation.set(...options.rotationDeg.map(toRad)); + } + ceilinglight.add(ledMesh); + + // Helper для визуализации RectAreaLight + const rectLightHelper = new RectAreaLightHelper(rectLight, 0xff0000); + if (options.position) { + rectLightHelper.position.set(...options.position); + } + if (options.rotationDeg) { + rectLightHelper.rotation.set(...options.rotationDeg.map(toRad)); + } + ceilinglight.add(rectLightHelper); + + // DirectionalLight для теней + const shadowLight = new THREE.DirectionalLight(0xffffff, 0.5); + shadowLight.castShadow = true; + shadowLight.shadow.mapSize.width = 1024; + shadowLight.shadow.mapSize.height = 1024; + shadowLight.shadow.camera.near = 0.1; + shadowLight.shadow.camera.far = 10; + shadowLight.shadow.camera.left = -5; + shadowLight.shadow.camera.right = 5; + shadowLight.shadow.camera.top = 5; + shadowLight.shadow.camera.bottom = -5; + shadowLight.shadow.bias = -0.001; + + if (options.shadowPosition) { + shadowLight.position.set(...options.shadowPosition); + } else { + shadowLight.position.set(0, 0, 0); + } + shadowLight.target.position.set(0, 1, 0); + ceilinglight.add(shadowLight); + ceilinglight.add(shadowLight.target); +} + +/** + * Создает LED-ленты на всех объектах ceilinglight_001 - ceilinglight_004 + * @param {THREE.Object3D} room - объект комнаты, содержащий ceilinglight объекты + */ +export function setupLEDStrip(room) { + // Настройки для каждой ленты + const ledConfigs = { + ceilinglight_001: { + position: [0, 0, 0.01], + rotationDeg: [0, 0, 0], + target: [0, 1, 0], + shadowPosition: [0, 0, 0], + width: 0.01, + height: 1.75, + color: 0xffffff, + intensity: 50 + }, + ceilinglight_002: { + position: [0, 0, 0.01], + rotationDeg: [0, 0, 90], + target: [0, 1, 0], + shadowPosition: [0, 0, 0], + width: 0.01, + height: 3.6, + color: 0xffffff, + intensity: 50 + }, + ceilinglight_003: { + position: [0, 0, 0.01], + rotationDeg: [0, 0, 0], + target: [0, 1, 0], + shadowPosition: [0, 0, 0], + width: 0.01, + height: 1.75, + color: 0xffffff, + intensity: 50 + }, + ceilinglight_004: { + position: [0, 0, 0.01], + rotationDeg: [0, 0, 90], + target: [0, 1, 0], + shadowPosition: [0, 0, 0], + width: 0.01, + height: 3.6, + color: 0xffffff, + intensity: 50 + } + }; + + let created = 0; + + for (let i = 1; i <= 4; i++) { + const name = `ceilinglight_${String(i).padStart(3, '0')}`; + const ceilinglight = room.getObjectByName(name); + const options = ledConfigs[name] || {}; + if (ceilinglight) { + createLEDStrip(ceilinglight, options); + const worldPos = new THREE.Vector3(); + ceilinglight.getWorldPosition(worldPos); + console.log(`LED-лента создана: ${name} | позиция: (${worldPos.x.toFixed(2)}, ${worldPos.y.toFixed(2)}, ${worldPos.z.toFixed(2)})`); + created++; + } else { + console.warn(`Объект не найден: ${name}`); + } + } + + console.log(`Создано LED-лент: ${created}`); +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..52a2ca9 --- /dev/null +++ b/js/main.js @@ -0,0 +1,11 @@ +import { scene, renderer } from './scene.js'; +import { camera } from './camera.js'; +import { controls } from './controls.js'; + +function animate() { + requestAnimationFrame(animate); + controls.update(); + renderer.render(scene, camera); +} + +animate(); diff --git a/js/scene.js b/js/scene.js new file mode 100644 index 0000000..02b6dd9 --- /dev/null +++ b/js/scene.js @@ -0,0 +1,74 @@ +import * as THREE from 'three'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; +import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'; +import { setupLights, setupRoomLights, setupLEDStrip } from './lights.js'; +import { camera } from './camera.js'; + +const canvas = document.getElementById('scene'); + +export const scene = new THREE.Scene(); +scene.background = new THREE.Color(0x222222); + +export const renderer = new THREE.WebGLRenderer({ + canvas, + antialias: true +}); + +renderer.setSize(window.innerWidth, window.innerHeight); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.outputColorSpace = THREE.SRGBColorSpace; +renderer.toneMapping = THREE.ACESFilmicToneMapping; +renderer.toneMappingExposure = 0.3; + +// Свет +setupLights(scene); + +// ===== HDRI ===== +const rgbeLoader = new RGBELoader(); +rgbeLoader.load( + 'textures/hdri_1.exr', + (texture) => { + texture.mapping = THREE.EquirectangularReflectionMapping; + scene.environment = texture; + scene.background = texture; + console.log('HDRI загружен: textures/hdri_1.exr'); + }, + undefined, + (error) => { + console.error('HDRI LOAD ERROR', error); + } +); + +// ===== ROOM ===== +export let room = null; // Экспортируем для вращения +const loader = new GLTFLoader(); +loader.load( + 'models/room.glb', + (gltf) => { + room = gltf.scene; + + // Центрируем модель по Y (только вверх/вниз) + const box = new THREE.Box3().setFromObject(room); + const centerY = (box.min.y + box.max.y) / 2; + room.position.y = -centerY; + + // Инициализируем свет комнаты + setupRoomLights(room); + + // LED-ленты на ceilinglight_001 - ceilinglight_004 + setupLEDStrip(room); + + scene.add(room); + }, + undefined, + (error) => { + console.error('GLB LOAD ERROR', error); + } +); + +// Resize +window.addEventListener('resize', () => { + renderer.setSize(window.innerWidth, window.innerHeight); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); +}); diff --git a/models/room.glb.log b/models/room.glb.log new file mode 100644 index 0000000..66cbbaf --- /dev/null +++ b/models/room.glb.log @@ -0,0 +1,2215 @@ + + + + + + + + + + + + + + + + + + + 3 + + 0 + 0 + + + 0 + 0 + + + 2 + 0 + + Duration(second) + + + DeltaWorkingSet(MB) + + + + 2 + 0 + + 0.00 + + + 1 + + + + + BuildDirectory + + + 5 + 0 + + IFile + + + IRenderStyle + + + IPart + + + IGeometricSet + + + IPolygonMesh + + + + 5 + 0 + 1 + 33 + 1 + 33 + 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + 3 + 3 + + + + + + + + + + + + got document version: 1.38, upgrading to 1.38... + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:normal + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:opacity + + + + + + 3 + 2 + + + + + + + + + + + + Warning: Invalid shader node:sheen + + + + + + + + + + + + 3 + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + 0 + 0 + + + + ASMConversionStage + + + 0 + 0 + + + 0 + 0 + + + + + + 3 + + 13 + 0 + + TessellationSetting.QuadDominant + + + TessellationSetting.SurfaceTolerance_IsRelative + + + TessellationSetting.SurfaceTolerance + + + TessellationSetting.NormalTolerance + + + TessellationSetting.MaxEdgeLength_IsRelative + + + TessellationSetting.MaxEdgeLength + + + TessellationSetting.GridAspectRatio + + + TessellationSetting.FaceMeshSegment + + + TessellationSetting.EdgeSegment + + + TessellationSetting.TessellateASMCurves + + + TessellationSetting.Smoothing + + + TessellationSetting.CurveTolerance_IsRelative + + + TessellationSetting.CurveTolerance + + + + 13 + 0 + + false + + + false + + + -1.000000 + + + true + + + false + + + -1.000000 + + + true + + + false + + + false + + + false + + + false + + + false + + + -1.000000 + + + + 0 + 0 + + + 0 + 0 + + + + TessellationStage + + + 0 + 0 + + + 0 + 0 + + + + + + + + + 3 + + 22 + 0 + + WantSolid + + + WantSurface + + + WantCurve + + + WantConfig + + + WantMaterial + + + WantPoint + + + WantSketchCurve + + + WantSketchPoint + + + WantPolygonMesh + + + WantWorkPlane + + + WantWorkAxis + + + WantWorkPoint + + + WantView + + + WantCamera + + + WantAnyInvisible + + + WantInvisible + + + WantCoordSys + + + WantPMI + + + HierarchyOption + + + WantInvisibleWorkFeature + + + IsAssertEnabled + + + WantJournal + + + + 22 + 0 + + false + + + false + + + true + + + true + + + true + + + false + + + true + + + true + + + true + + + false + + + false + + + false + + + true + + + true + + + true + + + false + + + false + + + false + + + kHierarchyOption_All + + + true + + + false + + + false + + + + 7 + 0 + + Producer + + + Consumer + + + IsMultiThread + + + IsProcessorSucceeded + + + IsAnyCAD + + + Duration(second) + + + DeltaWorkingSet(MB) + + + + 7 + 0 + + ATF::MaxATFProducer + + + ATF::GLTFConsumer + + + true + + + true + + + false + + + 5.50 + + + 838 + + + + + Processor + + + diff --git a/textures/hdri.hdr b/textures/hdri.hdr new file mode 100644 index 0000000..4c85efc Binary files /dev/null and b/textures/hdri.hdr differ diff --git a/textures/hdri_1.exr b/textures/hdri_1.exr new file mode 100644 index 0000000..00c4403 Binary files /dev/null and b/textures/hdri_1.exr differ