From ae39d5bbf4f44a19c0180ac9aff6f0702b359c27 Mon Sep 17 00:00:00 2001 From: Anuj K Date: Sat, 30 Aug 2025 12:49:24 +0530 Subject: [PATCH] bold change, transition change --- public/bold.glb | Bin 1354204 -> 2708108 bytes src/main.js | 1417 ++++++++++++++++++++++------------------------- 2 files changed, 651 insertions(+), 766 deletions(-) diff --git a/public/bold.glb b/public/bold.glb index 8d964d18d85a53465e913b5de4ea5503b6dc1b07..8444a6029d54c94f29ea2bdb0e37889e0ab93384 100644 GIT binary patch delta 1542 zcma*le@qi+7zc3ffL&2pa=4M%V-_AOltl=w@x2 zO|p*6vFW>{VRJ?UOk6UTj6W2WHWQOwGJ}~Ar;8&frpCx{Xix*9d#=)M%lKn|eEPii zyw82#YwoU$mhSEAqhjlu%GT3iVPShqGsDis?TcNYIi5sQ={1~#ZML>Hux6fPTdJF_ zteIWgUSr+4&2D8mwzamp30jr9Y)Rmaf?zb5jG|GHM1!9DpS>#b64jxFV_K`5ZB14? zYZf@Rt=e91ZDY+2w&mTeZ~#x5C~dyD#0JOOs@vf479Yjw{AZH1#0}&SWubYVj8kcG zyr7J4Yq7P%S;3yTL{D0xr~gln=EVVlE~F@qtfBQ0**cv_cF)zI}<{Wa0&ZQ)BpSH%B{+{A>0}bI9Y0@8=M!pZ9Cq(rE*%HCPT18ey zUL)hGL=vY;ri>&@rKNJnAr(uSRRU##6{2|^luYumDoOdDfsO!$qr`z&CYmB}Qm?#97?EJe zS>@DfwY{%o?ip6Qht(9IK{$v2kw66)pzKG1r$97V24X-ghy(E;0W1eAjt{Ff{J}l4 z+ATw<452cF$`C3;s0^Vpgvt;qL#Pa)GK9(yDnqCYp)!QZ5Gq5c452cF%4&_~u*Y^p zqjUW}_Eny3qQjEnF%9j{aN*;drt@;eNy~xTJwxLTC)R)Tw8j5HCK@YzG*niw3!lHR z%2JV9kM^td-maK-JXl3rUf)iT#lPMAiLDvm@Vu7y!0`hbb$5I1#kKfO+qb8OeIsaJ z?MZL^&zo_=^PdkLbox=0@v?WUyuh=%U-N9@jQ@alkcq(JEyj1m_zd3q&WQIjPkFya zOgNGKqxa}flSq@D>l?>c(JoJSzb4I<`e*`mMr`zb{pT;}Y~@Jag(5$y*tp&o-ZhG* z(kb-Zhf}Edy2aNWeF@deZy~w!4!TT zH36)Xm{PLC-8)V?j(Q4i<^7aMMd7*cN|{i2@6S=0-Z`q1() zB>oJAflK+p_r8b>S=|+dgXVf-@H|lJgL4TX?#GEZC{akpLCY&wVZ{nCZDT`LHfG=? zpwxxDtdO{g9WUb8i6@2%d3<`2-6r9nz70m)yXe%237-T?44MJ1{Hrn%C-7 zD`n;tl$I!2fkhZCHlJknWZWFYc9@AbsWdGuwJ0nzwOq-{WU>R7+~hsnW|Qr?L@`BEmpFMu2xE_EJ^iA%}X!IP_hCku{1TSjjdHsP!L%t()^ge{V_iy5HkTWGZ3=? zF)I+W0Wmuea{w_X5OV=BHxTmxF)tAF0WnCg01yiTu@DdoZ-2}$VmNQRgOBD5CIGlG BUX}m= diff --git a/src/main.js b/src/main.js index a53f816..6151b56 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,4 @@ import './style.css' - import * as THREE from 'three'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'; @@ -11,108 +10,89 @@ import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js' // Loading Manager class SceneLoader { - constructor() { - this.loadingScreen = document.getElementById('loading-screen'); - this.loadingText = document.getElementById('loading-text'); - this.loadingProgressBar = document.getElementById('loading-progress-bar'); - this.loadingPercentage = document.getElementById('loading-percentage'); - - this.modelsToLoad = [ - { file: 'bold.glb', type: 'bold' }, - { file: 'innovation.glb', type: 'innovation' }, - { file: 'agility.glb', type: 'agility' }, - { file: 'storytelling.glb', type: 'storytelling' } - ]; - - this.loadedModels = {}; - this.loadedCount = 0; - this.totalModels = this.modelsToLoad.length; - } - - setLoadingMessage(message) { - this.loadingText.textContent = message; - } - - updateProgress(progress) { - const percentage = Math.round(progress * 100); - this.loadingProgressBar.style.width = `${percentage}%`; - this.loadingPercentage.textContent = `${percentage}%`; - } - - hideLoadingScreen() { - this.loadingScreen.classList.add('hidden'); - setTimeout(() => { - this.loadingScreen.style.display = 'none'; - }, 800); - } - - async loadAllModels() { - return new Promise((resolve) => { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath('node_modules/three/examples/jsm/libs/draco/'); - loader.setDRACOLoader(dracoLoader); - - this.modelsToLoad.forEach((modelInfo, index) => { - // this.setLoadingMessage(`Loading ${modelInfo.type}...`); - this.setLoadingMessage(`Loading experience...`); - - loader.load(`/${modelInfo.file}`, - (gltf) => { - this.loadedModels[modelInfo.type] = { - scene: gltf.scene, - animations: gltf.animations, - gltf: gltf - }; - - this.loadedCount++; - const progress = this.loadedCount / this.totalModels; - this.updateProgress(progress); - - if (this.loadedCount === this.totalModels) { - this.setLoadingMessage('Initializing Experience...'); - setTimeout(() => { - this.hideLoadingScreen(); - resolve(this.loadedModels); - }, 500); - } - }, - (progress) => { - // Individual file progress - const fileProgress = progress.loaded / progress.total; - const totalProgress = (this.loadedCount + fileProgress) / this.totalModels; - this.updateProgress(totalProgress); - }, - (error) => { - console.error(`Error loading ${modelInfo.file}:`, error); - } - ); - }); - }); - } + constructor() { + this.loadingScreen = document.getElementById('loading-screen'); + this.loadingText = document.getElementById('loading-text'); + this.loadingProgressBar = document.getElementById('loading-progress-bar'); + this.loadingPercentage = document.getElementById('loading-percentage'); + this.modelsToLoad = [ + { file: 'bold.glb', type: 'bold' }, + { file: 'innovation.glb', type: 'innovation' }, + { file: 'agility.glb', type: 'agility' }, + { file: 'storytelling.glb', type: 'storytelling' } + ]; + this.loadedModels = {}; + this.loadedCount = 0; + this.totalModels = this.modelsToLoad.length; + } + setLoadingMessage(message) { + this.loadingText.textContent = message; + } + updateProgress(progress) { + const percentage = Math.round(progress * 100); + this.loadingProgressBar.style.width = `${percentage}%`; + this.loadingPercentage.textContent = `${percentage}%`; + } + hideLoadingScreen() { + this.loadingScreen.classList.add('hidden'); + setTimeout(() => { + this.loadingScreen.style.display = 'none'; + }, 800); + } + async loadAllModels() { + return new Promise((resolve) => { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('node_modules/three/examples/jsm/libs/draco/'); + loader.setDRACOLoader(dracoLoader); + this.modelsToLoad.forEach((modelInfo, index) => { + this.setLoadingMessage(`Loading experience...`); + loader.load(`/${modelInfo.file}`, + (gltf) => { + this.loadedModels[modelInfo.type] = { + scene: gltf.scene, + animations: gltf.animations, + gltf: gltf + }; + this.loadedCount++; + const progress = this.loadedCount / this.totalModels; + this.updateProgress(progress); + if (this.loadedCount === this.totalModels) { + this.setLoadingMessage('Initializing Experience...'); + setTimeout(() => { + this.hideLoadingScreen(); + resolve(this.loadedModels); + }, 500); + } + }, + (progress) => { + const fileProgress = progress.loaded / progress.total; + const totalProgress = (this.loadedCount + fileProgress) / this.totalModels; + this.updateProgress(totalProgress); + }, + (error) => { + console.error(`Error loading ${modelInfo.file}:`, error); + } + ); + }); + }); + } } // Initialize loader const sceneLoader = new SceneLoader(); - -// You can customize the loading message here: sceneLoader.setLoadingMessage('Preparing Your Experience...'); // Scene setup const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); camera.setFocalLength(50); - const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); // Transition state management let currentScene = 0; // 0: bold, 1: innovation, 2: agility, 3: storytelling let isTransitioning = false; -let isTwisting = false; -let twistProgress = 0; -const twistSpeed = 0.02; // Easily adjustable twist speed -const twistStrength = 0.3; const fadeSpeed = 1; // Easily adjustable fade speed const transitionDuration = 1; // Easily adjustable transition duration (seconds) let scrollDownCount = 0; @@ -121,6 +101,11 @@ const scrollThreshold = 10; // Changed to 10 as requested let transitionStartTime = 0; let transitionDirection = 1; // 1 for forward, -1 for backward +// Camera-relative transition vectors +let transitionUpVector = new THREE.Vector3(); +let transitionDownVector = new THREE.Vector3(); +const transitionDistance = 50; // Increased distance for more dramatic transitions + // Scene objects let currentModel = null; let nextModel = null; @@ -133,25 +118,25 @@ let preloadedModels = {}; // Bold scene roughness animation state let boldRoughnessAnimation = { - isActive: false, - startTime: 0, - delayDuration: 1.0, // 1 second delay - transitionDuration: 1.0, // 1 second transition - startRoughness: 0.25, - endRoughness: 0.05, - materials: [] // Store references to bold materials + isActive: false, + startTime: 0, + delayDuration: 1.0, // 1 second delay + transitionDuration: 1.0, // 1 second transition + startRoughness: 0.5, + endRoughness: 0.05, + materials: [] // Store references to bold materials }; // Innovation glass animation state let innovationGlassAnimation = { - isActive: false, - startTime: 0, - transitionDuration: 0.2, - startIor: 1.0, - endIor: 2.0, - startThickness: 1.0, - endThickness: 2.0, - materials: [] // Store references to innovation glass materials + isActive: false, + startTime: 0, + transitionDuration: 0.2, + startIor: 1.0, + endIor: 2.0, + startThickness: 1.0, + endThickness: 2.0, + materials: [] // Store references to innovation glass materials }; // Renderer setup @@ -171,12 +156,11 @@ document.body.appendChild(renderer.domElement); const composer = new EffectComposer(renderer); const renderPass = new RenderPass(scene, camera); composer.addPass(renderPass); - const bloomPass = new UnrealBloomPass( - new THREE.Vector2(window.innerWidth, window.innerHeight), - 1.0, // strength - 0.45, // radius - 0.85 // threshold + new THREE.Vector2(window.innerWidth, window.innerHeight), + 1.0, // strength + 0.45, // radius + 0.85 // threshold ); composer.addPass(bloomPass); @@ -188,7 +172,6 @@ video.loop = true; video.playsInline = true; video.autoplay = true; video.preload = 'auto'; - const videoTexture = new THREE.VideoTexture(video); videoTexture.colorSpace = THREE.SRGBColorSpace; videoTexture.generateMipmaps = false; @@ -196,7 +179,7 @@ videoTexture.minFilter = THREE.LinearFilter; videoTexture.magFilter = THREE.LinearFilter; // Ensure autoplay starts (muted autoplay is commonly allowed) -video.play().catch(() => {}); +video.play().catch(() => { }); // Local procedural environment for better PBR response (no network) const pmrem = new THREE.PMREMGenerator(renderer); @@ -209,779 +192,681 @@ scene.environment = null; // This will make the renderer's clear color visible a // Consistent Lighting Setup const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); - const hemiLight = new THREE.HemisphereLight(0xffffff, 0x666666, 1.5); hemiLight.position.set(0, 20, 0); scene.add(hemiLight); - const fillLight = new THREE.DirectionalLight(0xffffff, 1.2); fillLight.position.set(-12, 6, -8); scene.add(fillLight); - const topLight = new THREE.DirectionalLight(0xffffff, 1.5); topLight.position.set(5, 15, 5); scene.add(topLight); - const bottomLight = new THREE.DirectionalLight(0xffffff, 0.8); bottomLight.position.set(-3, -8, 3); scene.add(bottomLight); - const leftLight = new THREE.DirectionalLight(0xffffff, 1.0); leftLight.position.set(-12, 2, 5); scene.add(leftLight); - const rightLight = new THREE.DirectionalLight(0xffffff, 1.0); rightLight.position.set(12, 2, -5); scene.add(rightLight); - const frontLight = new THREE.DirectionalLight(0xffffff, 0.8); frontLight.position.set(8, 4, 12); scene.add(frontLight); - const backLight = new THREE.DirectionalLight(0xffffff, 0.8); backLight.position.set(-8, 4, -12); scene.add(backLight); - const cameraLight = new THREE.PointLight(0xffffff, 0.8, 0, 2); camera.add(cameraLight); scene.add(camera); -// Controls with zoom disabled +// Controls with zoom disabled and camera constraints const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.25; controls.enableZoom = false; // Disable zoom +// Add camera constraints to prevent extreme angles +controls.maxPolarAngle = Math.PI * 0.8; // Prevent looking too far up +controls.minPolarAngle = Math.PI * 0.2; // Prevent looking too far down +console.log('Orbit controls initialized with camera constraints'); // Material definitions // Bold glass material (starts rough, will transition to clear) const boldGlassMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xFFA500, - metalness: 0.2, - roughness: 0.25, // Start with rough glass - transmission: 1, - ior: 2, - thickness: 2, - clearcoat: 1.0, - clearcoatRoughness: 0.1, - attenuationColor: new THREE.Color(0xffffff), - attenuationDistance: 0.8, - envMapIntensity: 0, - specularIntensity: 1.0, - specularColor: new THREE.Color(0xffffff), - transparent: true, - depthWrite: false, - alphaTest: 0 + color: 0xffffff, + metalness: 0.2, + roughness: 0.5, // Start with rough glass + transmission: 1, + ior: 2, + thickness: 2, + clearcoat: 1.0, + clearcoatRoughness: 0.1, + attenuationColor: new THREE.Color(0xffffff), + attenuationDistance: 0.8, + envMapIntensity: 0, + specularIntensity: 1.0, + specularColor: new THREE.Color(0xffffff), + transparent: true, + depthWrite: false, + alphaTest: 0 +}); + +// Orange wireframe material for bold Cubewire mesh +const boldWireframeMaterial = new THREE.MeshStandardMaterial({ + color: 0xff8600, + metalness: 0.05, + roughness: 0.5 }); // Clear thick glass for innovation (starts with animated values) const innovationGlassMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xffffff, - metalness: 0.2, - roughness: 0.05, - transmission: 1, - ior: 1.0, // Will animate from 1 to 2 - thickness: 1.0, // Will animate from 1 to 2 - clearcoat: 1.0, - clearcoatRoughness: 0.1, - attenuationColor: new THREE.Color(0xffffff), - attenuationDistance: 0.8, - envMapIntensity: 0, - specularIntensity: 1.0, - specularColor: new THREE.Color(0x000000), - transparent: true, - depthWrite: false, - alphaTest: 0 + color: 0xffffff, + metalness: 0.2, + roughness: 0.05, + transmission: 1, + ior: 1.0, // Will animate from 1 to 2 + thickness: 1.0, // Will animate from 1 to 2 + clearcoat: 1.0, + clearcoatRoughness: 0.1, + attenuationColor: new THREE.Color(0xffffff), + attenuationDistance: 0.8, + envMapIntensity: 0, + specularIntensity: 1.0, + specularColor: new THREE.Color(0x000000), + transparent: true, + depthWrite: false, + alphaTest: 0 }); // Slightly frosted glass for agility and storytelling const frostedGlassMaterial = new THREE.MeshPhysicalMaterial({ - color: 0xffffff, - metalness: 0.0, - roughness: 0.25, - transmission: 1.0, - ior: 1.5, - thickness: 2.0, - clearcoat: 0.75, - clearcoatRoughness: 0.25, - attenuationColor: new THREE.Color(0xffffff), - attenuationDistance: 1.5, - envMapIntensity: 1.25, - specularIntensity: 1.0, - specularColor: new THREE.Color(0xffffff), - transparent: true, - depthWrite: false, - side: THREE.DoubleSide + color: 0xffffff, + metalness: 0.0, + roughness: 0.25, + transmission: 1.0, + ior: 1.5, + thickness: 2.0, + clearcoat: 0.75, + clearcoatRoughness: 0.25, + attenuationColor: new THREE.Color(0xffffff), + attenuationDistance: 1.5, + envMapIntensity: 1.25, + specularIntensity: 1.0, + specularColor: new THREE.Color(0xffffff), + transparent: true, + depthWrite: false, + side: THREE.DoubleSide }); // Orange material with video shader for innovation const lightOrangeMaterial = new THREE.MeshStandardMaterial({ - color: 0xff8600, - metalness: 0.05, - roughness: 0.4, - envMapIntensity: 0, - emissive: new THREE.Color(0xffad47), - emissiveMap: videoTexture, - emissiveIntensity: 2.25 + color: 0xff8600, + metalness: 0.05, + roughness: 0.4, + envMapIntensity: 0, + emissive: new THREE.Color(0xffad47), + emissiveMap: videoTexture, + emissiveIntensity: 2.25 }); +// Calculate camera-relative transition vectors for diagonal movement +function calculateTransitionVectors() { + // Get camera's world direction + const cameraDirection = new THREE.Vector3(); + camera.getWorldDirection(cameraDirection); + + // Get world up vector + const worldUp = new THREE.Vector3(0, 1, 0); + + // Calculate camera's left vector - BACK TO ORIGINAL (this gave correct left direction) + const cameraLeft = new THREE.Vector3(); + cameraLeft.crossVectors(worldUp, cameraDirection).normalize(); + + // Calculate camera's local up vector + const cameraUp = new THREE.Vector3(); + cameraUp.crossVectors(cameraLeft, cameraDirection).normalize(); + + // Blend camera up with world up - BUT NEGATE to flip up/down direction + const blendedUp = new THREE.Vector3(); + blendedUp.addVectors( + cameraUp.clone().multiplyScalar(0.5), + worldUp.clone().multiplyScalar(0.5) + ).normalize().negate(); // ADD .negate() here to flip up to down + + // Create diagonal vector (up-left) + const diagonalUpLeft = new THREE.Vector3(); + diagonalUpLeft.addVectors( + blendedUp.clone().multiplyScalar(0.5), + cameraLeft.clone().multiplyScalar(0.5) + ).normalize(); + + // Set transition vectors + transitionUpVector = diagonalUpLeft.clone().multiplyScalar(transitionDistance); + transitionDownVector = diagonalUpLeft.clone().multiplyScalar(-transitionDistance); + + console.log('Diagonal transition vectors calculated with distance:', transitionDistance); +} + // Apply materials based on model type function applyMaterials(model, modelType) { - console.log(`=== Material Assignment Debug for ${modelType} ===`); - let meshCount = 0; - - model.traverse((object) => { - if (object.isMesh) { - meshCount++; - console.log(`Found mesh: "${object.name}"`); - const previousMaterial = object.material; - object.castShadow = true; - object.receiveShadow = true; - - if (modelType === 'bold') { - // Bold-specific material logic - apply bold glass material to Cube mesh - if (object.name === 'Cube') { - console.log(` → Applying bold glass material to "${object.name}"`); - object.material = boldGlassMaterial.clone(); - object.material.side = THREE.DoubleSide; - object.material.depthWrite = false; - object.renderOrder = 2; - // Store material reference for roughness animation - boldRoughnessAnimation.materials.push(object.material); - } else { - console.log(` → Applying bold glass material (fallback) to "${object.name}"`); - object.material = boldGlassMaterial.clone(); - // Store material reference for roughness animation - boldRoughnessAnimation.materials.push(object.material); + console.log(`=== Material Assignment Debug for ${modelType} ===`); + let meshCount = 0; + model.traverse((object) => { + if (object.isMesh) { + meshCount++; + console.log(`Found mesh: "${object.name}"`); + const previousMaterial = object.material; + object.castShadow = true; + object.receiveShadow = true; + if (modelType === 'bold') { + // Bold-specific material logic - apply bold glass material to Cube mesh + if (modelType === 'bold') { + // Bold-specific material logic + if (object.name === 'Cube') { + console.log(` → Applying bold glass material to "${object.name}"`); + object.material = boldGlassMaterial.clone(); + object.material.side = THREE.DoubleSide; + object.material.depthWrite = false; + object.renderOrder = 2; + // Store material reference for roughness animation + boldRoughnessAnimation.materials.push(object.material); + } else if (object.name === 'Cubewire') { + console.log(` → Applying wireframe material to "${object.name}"`); + object.material = boldWireframeMaterial.clone(); + object.renderOrder = 1; + } else { + console.log(` → Applying bold glass material (fallback) to "${object.name}"`); + object.material = boldGlassMaterial.clone(); + // Store material reference for roughness animation + boldRoughnessAnimation.materials.push(object.material); + } + } + } else if (modelType === 'innovation') { + // Innovation-specific material logic + const orangeMeshes = ['dblsc', 'ec', 'gemini', 'infinity', 'star', 'dpd']; + const targetGlassNames = ['Cube.alt90.df']; + const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, ''); + const nameMatches = (name, targets) => { + const clean = sanitize(name); + return targets.some((t) => { + const ct = sanitize(t); + return clean === ct || clean.includes(ct) || ct.includes(clean); + }); + }; + if (nameMatches(object.name, targetGlassNames)) { + // Create outer glass shell with innovation-specific material + object.material = innovationGlassMaterial.clone(); + object.material.side = THREE.DoubleSide; + object.material.depthWrite = false; + object.renderOrder = 2; + // Store material reference for animation + innovationGlassAnimation.materials.push(object.material); + // Create inner glass shell + const innerShell = object.clone(); + innerShell.material = innovationGlassMaterial.clone(); + innerShell.material.side = THREE.DoubleSide; + innerShell.material.depthWrite = false; + innerShell.material.transmission = 0.8; + innerShell.renderOrder = 1; + innerShell.scale.multiplyScalar(0.95); + // Store inner shell material reference for animation too + innovationGlassAnimation.materials.push(innerShell.material); + object.parent.add(innerShell); + } else if (nameMatches(object.name, orangeMeshes)) { + object.material = lightOrangeMaterial.clone(); + object.renderOrder = 0; + } + } else { + // Agility and Storytelling use frosted glass material for all meshes + if (object.name.startsWith('base')) { + console.log(` → Applying frosted glass material to "${object.name}"`); + object.material = frostedGlassMaterial.clone(); + } else { + console.log(` → Applying frosted glass material (fallback) to "${object.name}"`); + object.material = frostedGlassMaterial.clone(); + } + } + object.material.needsUpdate = true; + // Cleanup previous materials + if (Array.isArray(previousMaterial)) { + previousMaterial.forEach((mat) => mat && mat.dispose && mat.dispose()); + } else if (previousMaterial && previousMaterial.dispose) { + previousMaterial.dispose(); + } } - } else if (modelType === 'innovation') { - // Innovation-specific material logic - const orangeMeshes = ['dblsc', 'ec', 'gemini', 'infinity', 'star', 'dpd']; - const targetGlassNames = ['Cube.alt90.df']; - - const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, ''); - const nameMatches = (name, targets) => { - const clean = sanitize(name); - return targets.some((t) => { - const ct = sanitize(t); - return clean === ct || clean.includes(ct) || ct.includes(clean); - }); - }; - - if (nameMatches(object.name, targetGlassNames)) { - // Create outer glass shell with innovation-specific material - object.material = innovationGlassMaterial.clone(); - object.material.side = THREE.DoubleSide; - object.material.depthWrite = false; - object.renderOrder = 2; - - // Store material reference for animation - innovationGlassAnimation.materials.push(object.material); - - // Create inner glass shell - const innerShell = object.clone(); - innerShell.material = innovationGlassMaterial.clone(); - innerShell.material.side = THREE.DoubleSide; - innerShell.material.depthWrite = false; - innerShell.material.transmission = 0.8; - innerShell.renderOrder = 1; - innerShell.scale.multiplyScalar(0.95); - - // Store inner shell material reference for animation too - innovationGlassAnimation.materials.push(innerShell.material); - - object.parent.add(innerShell); - } else if (nameMatches(object.name, orangeMeshes)) { - object.material = lightOrangeMaterial.clone(); - object.renderOrder = 0; - } - } else { - // Agility and Storytelling use frosted glass material for all meshes - if (object.name.startsWith('base')) { - console.log(` → Applying frosted glass material to "${object.name}"`); - object.material = frostedGlassMaterial.clone(); - } else { - console.log(` → Applying frosted glass material (fallback) to "${object.name}"`); - object.material = frostedGlassMaterial.clone(); - } - } - - object.material.needsUpdate = true; - - // Cleanup previous materials - if (Array.isArray(previousMaterial)) { - previousMaterial.forEach((mat) => mat && mat.dispose && mat.dispose()); - } else if (previousMaterial && previousMaterial.dispose) { - previousMaterial.dispose(); - } - } - }); - - console.log(`Total meshes processed: ${meshCount}`); - console.log(`=== End Material Assignment Debug for ${modelType} ===`); + }); + console.log(`Total meshes processed: ${meshCount}`); + console.log(`=== End Material Assignment Debug for ${modelType} ===`); } // Center and frame model with camera function centerAndFrameModel(model, targetCamera = camera) { - const box = new THREE.Box3().setFromObject(model); - const center = box.getCenter(new THREE.Vector3()); - model.position.sub(center); - model.updateMatrixWorld(true); - - // Only set camera position if it's not already positioned (avoid reset during transitions) - // Use fixed camera distance that's further away from the origin - if (!isTransitioning) { - const fixedCameraDistance = 50; // Fixed distance, much further than before - // Calculate isometric-like position with 35-degree angles - const angle = 35 * Math.PI / 180; // Convert 35 degrees to radians - const cosAngle = Math.cos(angle); - const x = fixedCameraDistance * cosAngle; - const y = fixedCameraDistance * cosAngle; - const z = fixedCameraDistance * cosAngle; - targetCamera.position.set(x, y, z); - controls.target.set(0, 0, 0); - - // Set distance limits to lock the camera at this distance - controls.minDistance = fixedCameraDistance; - controls.maxDistance = fixedCameraDistance; - controls.update(); - } + const box = new THREE.Box3().setFromObject(model); + const center = box.getCenter(new THREE.Vector3()); + model.position.sub(center); + model.updateMatrixWorld(true); + // Only set camera position if it's not already positioned (avoid reset during transitions) + // Use fixed camera distance that's further away from the origin + if (!isTransitioning) { + const fixedCameraDistance = 50; // Fixed distance, much further than before + // Calculate isometric-like position with 35-degree angles + const angle = 35 * Math.PI / 180; // Convert 35 degrees to radians + const cosAngle = Math.cos(angle); + const x = fixedCameraDistance * cosAngle; + const y = fixedCameraDistance * cosAngle; + const z = fixedCameraDistance * cosAngle; + targetCamera.position.set(x, y, z); + controls.target.set(0, 0, 0); + // Set distance limits to lock the camera at this distance + controls.minDistance = fixedCameraDistance; + controls.maxDistance = fixedCameraDistance; + controls.update(); + console.log(`Camera positioned at: x=${x}, y=${y}, z=${z}, distance=${fixedCameraDistance}`); + } } // Setup animations based on model type function setupAnimations(model, gltf, modelType) { - if (gltf.animations && gltf.animations.length > 0) { - const animMixer = new THREE.AnimationMixer(model); - - gltf.animations.forEach((clip) => { - const action = animMixer.clipAction(clip); - if (modelType === 'bold') { - // Play once for bold - action.loop = THREE.LoopOnce; - action.clampWhenFinished = true; - action.play(); - } else if (modelType === 'innovation') { - // PingPong loop for innovation - action.loop = THREE.LoopPingPong; - action.play(); - } else if (modelType === 'agility') { - // Regular loop for agility - action.loop = THREE.LoopRepeat; - action.play(); - } else if (modelType === 'storytelling') { - // Play once for storytelling - action.loop = THREE.LoopOnce; - action.clampWhenFinished = true; - action.play(); - } - }); - - if (modelType === 'innovation') { - animMixer.timeScale = 3.0; // Keep existing timeScale for innovation + if (gltf.animations && gltf.animations.length > 0) { + const animMixer = new THREE.AnimationMixer(model); + gltf.animations.forEach((clip) => { + const action = animMixer.clipAction(clip); + if (modelType === 'bold') { + // Play once for bold + action.loop = THREE.LoopOnce; + action.clampWhenFinished = true; + action.play(); + console.log(`Bold animation started: ${clip.name}`); + } else if (modelType === 'innovation') { + // PingPong loop for innovation + action.loop = THREE.LoopPingPong; + action.play(); + console.log(`Innovation animation started: ${clip.name} (PingPong)`); + } else if (modelType === 'agility') { + // Regular loop for agility + action.loop = THREE.LoopRepeat; + action.play(); + console.log(`Agility animation started: ${clip.name} (Loop)`); + } else if (modelType === 'storytelling') { + // Play once for storytelling + action.loop = THREE.LoopOnce; + action.clampWhenFinished = true; + action.play(); + console.log(`Storytelling animation started: ${clip.name}`); + } + }); + if (modelType === 'innovation') { + animMixer.timeScale = 3.0; // Keep existing timeScale for innovation + console.log('Innovation animation timeScale set to 3.0'); + } + return animMixer; } - - return animMixer; - } - - return null; + return null; } // Create model from preloaded data - FIXED: Always create fresh geometry function createModelFromPreloaded(modelType) { - const preloadedData = preloadedModels[modelType]; - if (!preloadedData) { - console.error(`Preloaded model not found: ${modelType}`); - return { model: null, animMixer: null }; - } - - // Clear animation materials arrays when creating new models - if (modelType === 'bold') { - boldRoughnessAnimation.materials = []; - } else if (modelType === 'innovation') { - innovationGlassAnimation.materials = []; - } - - // Clone the scene deeply to ensure fresh geometry - const model = preloadedData.scene.clone(true); - - // IMPORTANT: Clone all geometries to ensure they're independent - model.traverse((object) => { - if (object.isMesh && object.geometry) { - object.geometry = object.geometry.clone(); - // Clear any previous twist data - delete object.geometry.userData.originalPositions; - delete object.geometry.userData.originalWorldPositions; - delete object.geometry.userData.inverseWorldMatrix; + const preloadedData = preloadedModels[modelType]; + if (!preloadedData) { + console.error(`Preloaded model not found: ${modelType}`); + return { model: null, animMixer: null }; } - }); - - // Apply materials - applyMaterials(model, modelType); - - // Setup animations - const animMixer = setupAnimations(model, preloadedData.gltf, modelType); - - // Center and frame model - centerAndFrameModel(model); - - return { model, animMixer }; + console.log(`Creating model from preloaded data: ${modelType}`); + // Clear animation materials arrays when creating new models + if (modelType === 'bold') { + boldRoughnessAnimation.materials = []; + } else if (modelType === 'innovation') { + innovationGlassAnimation.materials = []; + } + // Clone the scene deeply to ensure fresh geometry + const model = preloadedData.scene.clone(true); + // IMPORTANT: Clone all geometries to ensure they're independent + model.traverse((object) => { + if (object.isMesh && object.geometry) { + object.geometry = object.geometry.clone(); + } + }); + // Apply materials + applyMaterials(model, modelType); + // Setup animations + const animMixer = setupAnimations(model, preloadedData.gltf, modelType); + // Center and frame model + centerAndFrameModel(model); + console.log(`Model created successfully: ${modelType}`); + return { model, animMixer }; } // Initialize first scene after all models are loaded function initializeScene() { - const { model, animMixer } = createModelFromPreloaded('bold'); - currentModel = model; - mixer = animMixer; - scene.add(currentModel); - - // Start the roughness animation for bold scene - boldRoughnessAnimation.isActive = true; - boldRoughnessAnimation.startTime = performance.now(); + console.log('Initializing first scene (bold)'); + const { model, animMixer } = createModelFromPreloaded('bold'); + currentModel = model; + mixer = animMixer; + scene.add(currentModel); + // Start the roughness animation for bold scene + boldRoughnessAnimation.isActive = true; + boldRoughnessAnimation.startTime = performance.now(); + console.log('Bold scene initialized and roughness animation started'); } // Start innovation glass animation function startInnovationGlassAnimation() { - // Reset all innovation glass materials to starting values - innovationGlassAnimation.materials.forEach(material => { - material.ior = innovationGlassAnimation.startIor; - material.thickness = innovationGlassAnimation.startThickness; - material.needsUpdate = true; - }); - - innovationGlassAnimation.isActive = true; - innovationGlassAnimation.startTime = performance.now(); - console.log('Innovation glass animation started'); -} - -// Twist animation function - Updated to twist around world center (0,0,0) -function twistMesh(mesh, progress) { - if (!mesh || !mesh.geometry || !mesh.geometry.attributes.position) { - return; - } - - const positions = mesh.geometry.attributes.position; - - // Store original positions on the first run - if (!mesh.geometry.userData.originalPositions) { - mesh.geometry.userData.originalPositions = new Float32Array(positions.array); - - // Store original world positions for each vertex - mesh.geometry.userData.originalWorldPositions = []; - - // Update world matrix to get accurate world positions - mesh.updateMatrixWorld(true); - const tempVector = new THREE.Vector3(); - - for (let i = 0; i < positions.count; i++) { - tempVector.fromBufferAttribute(positions, i); - tempVector.applyMatrix4(mesh.matrixWorld); - mesh.geometry.userData.originalWorldPositions.push({ - x: tempVector.x, - y: tempVector.y, - z: tempVector.z - }); - } - - // Store the inverse of the current world matrix for transforming back to local space - mesh.geometry.userData.inverseWorldMatrix = mesh.matrixWorld.clone().invert(); - } - - const originalWorldPositions = mesh.geometry.userData.originalWorldPositions; - const inverseWorldMatrix = mesh.geometry.userData.inverseWorldMatrix; - - for (let i = 0; i < positions.count; i++) { - const worldPos = originalWorldPositions[i]; - - // Use world Y position for consistent twisting around world Y-axis - const worldY = worldPos.y; - - // Calculate twist angle based on world Y position - // Normalize Y based on a reasonable range (adjust as needed) - const normalizedY = (worldY + 5) / 10; // Assuming meshes are roughly within -5 to +5 world units in Y - const twistAngle = normalizedY * progress * twistStrength * 2 * Math.PI; - - // Apply twist in world coordinates around world Y-axis - const twistedWorldX = worldPos.x * Math.cos(twistAngle) - worldPos.z * Math.sin(twistAngle); - const twistedWorldY = worldPos.y; // Y remains unchanged - const twistedWorldZ = worldPos.x * Math.sin(twistAngle) + worldPos.z * Math.cos(twistAngle); - - // Convert twisted world position back to local coordinates - const twistedWorldVector = new THREE.Vector3(twistedWorldX, twistedWorldY, twistedWorldZ); - twistedWorldVector.applyMatrix4(inverseWorldMatrix); - - positions.setXYZ(i, twistedWorldVector.x, twistedWorldVector.y, twistedWorldVector.z); - } - - positions.needsUpdate = true; - mesh.geometry.computeVertexNormals(); + console.log('Starting innovation glass animation'); + // Reset all innovation glass materials to starting values + innovationGlassAnimation.materials.forEach(material => { + material.ior = innovationGlassAnimation.startIor; + material.thickness = innovationGlassAnimation.startThickness; + material.needsUpdate = true; + }); + innovationGlassAnimation.isActive = true; + innovationGlassAnimation.startTime = performance.now(); + console.log('Innovation glass animation started'); } // Reset mesh geometry to original state function resetMeshGeometry(mesh) { - if (!mesh || !mesh.geometry || !mesh.geometry.userData.originalPositions) { - return; - } - - const positions = mesh.geometry.attributes.position; - const original = mesh.geometry.userData.originalPositions; - - for (let i = 0; i < positions.count; i++) { - positions.setXYZ(i, original[i * 3], original[i * 3 + 1], original[i * 3 + 2]); - } - - positions.needsUpdate = true; - mesh.geometry.computeVertexNormals(); + if (!mesh || !mesh.geometry || !mesh.geometry.userData.originalPositions) { + return; + } + const positions = mesh.geometry.attributes.position; + const original = mesh.geometry.userData.originalPositions; + for (let i = 0; i < positions.count; i++) { + positions.setXYZ(i, original[i * 3], original[i * 3 + 1], original[i * 3 + 2]); + } + positions.needsUpdate = true; + mesh.geometry.computeVertexNormals(); } // FIXED: Clean up geometry data completely function cleanupGeometryData(model) { - if (!model) return; - - model.traverse((object) => { - if (object.isMesh && object.geometry && object.geometry.userData) { - delete object.geometry.userData.originalPositions; - delete object.geometry.userData.originalWorldPositions; - delete object.geometry.userData.inverseWorldMatrix; - } - }); + if (!model) return; + model.traverse((object) => { + if (object.isMesh && object.geometry && object.geometry.userData) { + delete object.geometry.userData.originalPositions; + delete object.geometry.userData.originalWorldPositions; + delete object.geometry.userData.inverseWorldMatrix; + } + }); } // Start transition to next or previous scene function startTransition(direction = 1) { - if (isTransitioning) return; - - // Check bounds - now 4 scenes (0-3) - if (direction > 0 && currentScene >= 3) return; // Can't go forward from storytelling - if (direction < 0 && currentScene <= 0) return; // Can't go backward from bold - - isTransitioning = true; - isTwisting = true; - twistProgress = 0; - transitionStartTime = performance.now(); - transitionDirection = direction; - - // Determine next model based on direction and current scene - let nextModelType = ''; - - if (direction > 0) { - // Moving forward - if (currentScene === 0) { - nextModelType = 'innovation'; - } else if (currentScene === 1) { - nextModelType = 'agility'; - } else if (currentScene === 2) { - nextModelType = 'storytelling'; - } - } else { - // Moving backward - if (currentScene === 1) { - nextModelType = 'bold'; - } else if (currentScene === 2) { - nextModelType = 'innovation'; - } else if (currentScene === 3) { - nextModelType = 'agility'; - } - } - - if (nextModelType) { - const { model, animMixer } = createModelFromPreloaded(nextModelType); - nextModel = model; - nextMixer = animMixer; - - // Start next model as invisible but in normal position (no vertical offset) - nextModel.position.y = 0; - nextModel.traverse((obj) => { - if (obj.material) { - if (Array.isArray(obj.material)) { - obj.material.forEach(mat => { - mat.transparent = true; - mat.opacity = 0; - }); - } else { - obj.material.transparent = true; - obj.material.opacity = 0; + if (isTransitioning) return; + // Check bounds - now 4 scenes (0-3) + if (direction > 0 && currentScene >= 3) return; // Can't go forward from storytelling + if (direction < 0 && currentScene <= 0) return; // Can't go backward from bold + console.log(`Starting diagonal transition: direction=${direction}, currentScene=${currentScene}`); + // Calculate camera-relative diagonal transition vectors + calculateTransitionVectors(); + isTransitioning = true; + transitionStartTime = performance.now(); + transitionDirection = direction; + // Determine next model based on direction and current scene + let nextModelType = ''; + if (direction > 0) { + // Moving forward + if (currentScene === 0) { + nextModelType = 'innovation'; + } else if (currentScene === 1) { + nextModelType = 'agility'; + } else if (currentScene === 2) { + nextModelType = 'storytelling'; } - } - }); - scene.add(nextModel); - } + } else { + // Moving backward + if (currentScene === 1) { + nextModelType = 'bold'; + } else if (currentScene === 2) { + nextModelType = 'innovation'; + } else if (currentScene === 3) { + nextModelType = 'agility'; + } + } + console.log(`Next model type: ${nextModelType}`); + if (nextModelType) { + const { model, animMixer } = createModelFromPreloaded(nextModelType); + nextModel = model; + nextMixer = animMixer; + // Start next model at the diagonal down position (camera-relative) + nextModel.position.copy(transitionDownVector); + nextModel.traverse((obj) => { + if (obj.material) { + if (Array.isArray(obj.material)) { + obj.material.forEach(mat => { + mat.transparent = true; + mat.opacity = 0; + }); + } else { + obj.material.transparent = true; + obj.material.opacity = 0; + } + } + }); + scene.add(nextModel); + console.log(`Next model positioned at diagonal down vector: x=${nextModel.position.x}, y=${nextModel.position.y}, z=${nextModel.position.z}`); + } } // Update transition animation function updateTransition(deltaTime) { - if (!isTransitioning) return; - - const elapsed = (performance.now() - transitionStartTime) / 1000; - const transitionProgress = Math.min(elapsed / transitionDuration, 1); - - // Smooth easing function (ease-in-out) - const easeInOut = (t) => t * t * (3 - 2 * t); - const easedProgress = easeInOut(transitionProgress); - - if (currentModel) { - // Move current model up and fade out - // currentModel.position.y = easedProgress * 10; - currentModel.traverse((obj) => { - if (obj.material) { - const targetOpacity = 1 - easedProgress; - if (Array.isArray(obj.material)) { - obj.material.forEach(mat => { - mat.transparent = true; - mat.opacity = targetOpacity; - }); - } else { - obj.material.transparent = true; - obj.material.opacity = targetOpacity; - } - } - }); - } - - if (nextModel) { - // Keep next model in place and just fade in (no vertical movement) - nextModel.position.y = 0; - nextModel.traverse((obj) => { - if (obj.material) { - const targetOpacity = easedProgress; - if (Array.isArray(obj.material)) { - obj.material.forEach(mat => { - mat.transparent = true; - mat.opacity = targetOpacity; - }); - } else { - obj.material.transparent = true; - obj.material.opacity = targetOpacity; - } - } - }); - } - - // Complete transition - if (transitionProgress >= 1) { - // FIXED: Reset geometry before removing the model + if (!isTransitioning) return; + const elapsed = (performance.now() - transitionStartTime) / 1000; + const transitionProgress = Math.min(elapsed / transitionDuration, 1); + // Smooth easing function (ease-in-out) + const easeInOut = (t) => t * t * (3 - 2 * t); + const easedProgress = easeInOut(transitionProgress); if (currentModel) { - // Reset all geometry to original state before removal - currentModel.traverse((object) => { - if (object.isMesh) { - resetMeshGeometry(object); - } - }); - - // Clean up geometry user data completely - cleanupGeometryData(currentModel); - - scene.remove(currentModel); - } - - // Switch to next model - if (nextModel) { - currentModel = nextModel; - mixer = nextMixer; - - // Reset position and opacity - currentModel.position.y = 0; - currentModel.traverse((obj) => { - if (obj.material) { - if (Array.isArray(obj.material)) { - obj.material.forEach(mat => { - mat.opacity = 1; - if (currentScene + transitionDirection === 3) { // Keep transparency for storytelling glass - mat.transparent = mat.transmission > 0; - } else { - mat.transparent = mat.transmission > 0; - } - }); - } else { - obj.material.opacity = 1; - if (currentScene + transitionDirection === 3) { // Keep transparency for storytelling glass - obj.material.transparent = obj.material.transmission > 0; - } else { - obj.material.transparent = obj.material.transmission > 0; + // Move current model along diagonal up-left vector + const moveVector = transitionUpVector.clone().multiplyScalar(easedProgress); + currentModel.position.copy(moveVector); + currentModel.traverse((obj) => { + if (obj.material) { + const targetOpacity = 1 - easedProgress; + if (Array.isArray(obj.material)) { + obj.material.forEach(mat => { + mat.transparent = true; + mat.opacity = targetOpacity; + }); + } else { + obj.material.transparent = true; + obj.material.opacity = targetOpacity; + } } - } + }); + } + if (nextModel) { + // Move next model from diagonal down-left vector to center (0,0,0) + const moveVector = transitionDownVector.clone().multiplyScalar(1 - easedProgress); + nextModel.position.copy(moveVector); + nextModel.traverse((obj) => { + if (obj.material) { + const targetOpacity = easedProgress; + if (Array.isArray(obj.material)) { + obj.material.forEach(mat => { + mat.transparent = true; + mat.opacity = targetOpacity; + }); + } else { + obj.material.transparent = true; + obj.material.opacity = targetOpacity; + } + } + }); + } + // Complete transition + if (transitionProgress >= 1) { + console.log('Diagonal transition animation complete'); + // FIXED: Reset geometry before removing the model + if (currentModel) { + // Reset all geometry to original state before removal + currentModel.traverse((object) => { + if (object.isMesh) { + resetMeshGeometry(object); + } + }); + // Clean up geometry user data completely + cleanupGeometryData(currentModel); + scene.remove(currentModel); + console.log('Previous model removed from scene'); } - }); + // Switch to next model + if (nextModel) { + currentModel = nextModel; + mixer = nextMixer; + // Reset position and opacity + currentModel.position.set(0, 0, 0); + currentModel.traverse((obj) => { + if (obj.material) { + if (Array.isArray(obj.material)) { + obj.material.forEach(mat => { + mat.opacity = 1; + if (currentScene + transitionDirection === 3) { // Keep transparency for storytelling glass + mat.transparent = mat.transmission > 0; + } else { + mat.transparent = mat.transmission > 0; + } + }); + } else { + obj.material.opacity = 1; + if (currentScene + transitionDirection === 3) { // Keep transparency for storytelling glass + obj.material.transparent = obj.material.transmission > 0; + } else { + obj.material.transparent = obj.material.transmission > 0; + } + } + } + }); + } + nextModel = null; + nextMixer = null; + isTransitioning = false; + currentScene += transitionDirection; // Update scene based on direction + scrollDownCount = 0; + scrollUpCount = 0; + // Start innovation glass animation if we're now in the innovation scene + if (currentScene === 1) { + startInnovationGlassAnimation(); + } + console.log(`Diagonal transition complete. Current scene: ${currentScene}`); } - - nextModel = null; - nextMixer = null; - isTransitioning = false; - isTwisting = false; - twistProgress = 0; - currentScene += transitionDirection; // Update scene based on direction - scrollDownCount = 0; - scrollUpCount = 0; - - // Start innovation glass animation if we're now in the innovation scene - if (currentScene === 1) { - startInnovationGlassAnimation(); - } - - console.log(`Transition complete. Current scene: ${currentScene}`); - } } // Scroll event handler function onMouseScroll(event) { - if (isTransitioning) return; - - if (event.deltaY > 0) { - // Scrolling down - move forward - scrollDownCount++; - scrollUpCount = 0; // Reset up count - console.log(`Scroll down count: ${scrollDownCount}`); - - if (scrollDownCount >= scrollThreshold) { - startTransition(1); // Forward direction + if (isTransitioning) return; + if (event.deltaY > 0) { + // Scrolling down - move forward + scrollDownCount++; + scrollUpCount = 0; // Reset up count + console.log(`Scroll down count: ${scrollDownCount}`); + if (scrollDownCount >= scrollThreshold) { + startTransition(1); // Forward direction + } + } else if (event.deltaY < 0) { + // Scrolling up - move backward + scrollUpCount++; + scrollDownCount = 0; // Reset down count + console.log(`Scroll up count: ${scrollUpCount}`); + if (scrollUpCount >= scrollThreshold) { + startTransition(-1); // Backward direction + } } - } else if (event.deltaY < 0) { - // Scrolling up - move backward - scrollUpCount++; - scrollDownCount = 0; // Reset down count - console.log(`Scroll up count: ${scrollUpCount}`); - - if (scrollUpCount >= scrollThreshold) { - startTransition(-1); // Backward direction - } - } } // Animation loop const clock = new THREE.Clock(); - function animate() { - requestAnimationFrame(animate); - - const delta = clock.getDelta(); - - // Update mixers - if (mixer) mixer.update(delta); - if (nextMixer) nextMixer.update(delta); - - // Update transition - if (isTransitioning) { - updateTransition(delta); - - // Apply twist during transition - if (isTwisting && currentModel) { - twistProgress += twistSpeed; - if (twistProgress > 1.0) { - twistProgress = 1.0; - // FIXED: Reset geometry after twist completes - currentModel.traverse((object) => { - if (object.isMesh) { - resetMeshGeometry(object); - } + requestAnimationFrame(animate); + const delta = clock.getDelta(); + // Update mixers + if (mixer) mixer.update(delta); + if (nextMixer) nextMixer.update(delta); + // Update transition + if (isTransitioning) { + updateTransition(delta); + } + // Update bold roughness animation + if (boldRoughnessAnimation.isActive) { + const elapsed = (performance.now() - boldRoughnessAnimation.startTime) / 1000; + if (elapsed >= boldRoughnessAnimation.delayDuration) { + // Delay period is over, start roughness transition + const transitionElapsed = elapsed - boldRoughnessAnimation.delayDuration; + const transitionProgress = Math.min(transitionElapsed / boldRoughnessAnimation.transitionDuration, 1); + // Smooth easing function (ease-in-out) + const easeInOut = (t) => t * t * (3 - 2 * t); + const easedProgress = easeInOut(transitionProgress); + // Interpolate roughness from 0.25 to 0.05 + const currentRoughness = boldRoughnessAnimation.startRoughness + + (boldRoughnessAnimation.endRoughness - boldRoughnessAnimation.startRoughness) * easedProgress; + // Apply to all bold materials + boldRoughnessAnimation.materials.forEach(material => { + material.roughness = currentRoughness; + material.needsUpdate = true; + }); + // End animation when complete + if (transitionProgress >= 1) { + boldRoughnessAnimation.isActive = false; + console.log('Bold roughness animation completed'); + } + } + } + // Update innovation glass animation + if (innovationGlassAnimation.isActive) { + const elapsed = (performance.now() - innovationGlassAnimation.startTime) / 1000; + const transitionProgress = Math.min(elapsed / innovationGlassAnimation.transitionDuration, 1); + // Smooth easing function (ease-in-out) + const easeInOut = (t) => t * t * (3 - 2 * t); + const easedProgress = easeInOut(transitionProgress); + // Interpolate IOR from 1.0 to 2.0 + const currentIor = innovationGlassAnimation.startIor + + (innovationGlassAnimation.endIor - innovationGlassAnimation.startIor) * easedProgress; + // Interpolate thickness from 1.0 to 2.0 + const currentThickness = innovationGlassAnimation.startThickness + + (innovationGlassAnimation.endThickness - innovationGlassAnimation.startThickness) * easedProgress; + // Apply to all innovation glass materials + innovationGlassAnimation.materials.forEach(material => { + material.ior = currentIor; + material.thickness = currentThickness; + material.needsUpdate = true; }); - isTwisting = false; - } else { - // Apply twist to current model - currentModel.traverse((object) => { - if (object.isMesh) { - twistMesh(object, twistProgress); - } - }); - } + // End animation when complete + if (transitionProgress >= 1) { + innovationGlassAnimation.isActive = false; + console.log('Innovation glass animation completed'); + } } - } - - // Update bold roughness animation - if (boldRoughnessAnimation.isActive) { - const elapsed = (performance.now() - boldRoughnessAnimation.startTime) / 1000; - - if (elapsed >= boldRoughnessAnimation.delayDuration) { - // Delay period is over, start roughness transition - const transitionElapsed = elapsed - boldRoughnessAnimation.delayDuration; - const transitionProgress = Math.min(transitionElapsed / boldRoughnessAnimation.transitionDuration, 1); - - // Smooth easing function (ease-in-out) - const easeInOut = (t) => t * t * (3 - 2 * t); - const easedProgress = easeInOut(transitionProgress); - - // Interpolate roughness from 0.25 to 0.05 - const currentRoughness = boldRoughnessAnimation.startRoughness + - (boldRoughnessAnimation.endRoughness - boldRoughnessAnimation.startRoughness) * easedProgress; - - // Apply to all bold materials - boldRoughnessAnimation.materials.forEach(material => { - material.roughness = currentRoughness; - material.needsUpdate = true; - }); - - // End animation when complete - if (transitionProgress >= 1) { - boldRoughnessAnimation.isActive = false; - } - } - } - - // Update innovation glass animation - if (innovationGlassAnimation.isActive) { - const elapsed = (performance.now() - innovationGlassAnimation.startTime) / 1000; - const transitionProgress = Math.min(elapsed / innovationGlassAnimation.transitionDuration, 1); - - // Smooth easing function (ease-in-out) - const easeInOut = (t) => t * t * (3 - 2 * t); - const easedProgress = easeInOut(transitionProgress); - - // Interpolate IOR from 1.0 to 2.0 - const currentIor = innovationGlassAnimation.startIor + - (innovationGlassAnimation.endIor - innovationGlassAnimation.startIor) * easedProgress; - - // Interpolate thickness from 1.0 to 2.0 - const currentThickness = innovationGlassAnimation.startThickness + - (innovationGlassAnimation.endThickness - innovationGlassAnimation.startThickness) * easedProgress; - - // Apply to all innovation glass materials - innovationGlassAnimation.materials.forEach(material => { - material.ior = currentIor; - material.thickness = currentThickness; - material.needsUpdate = true; - }); - - // End animation when complete - if (transitionProgress >= 1) { - innovationGlassAnimation.isActive = false; - console.log('Innovation glass animation completed'); - } - } - - // Turntable rotation for current model - // if (currentModel && !isTransitioning) { - // autoRotationAngle += delta * 0.5; - // currentModel.rotation.y = autoRotationAngle; - // } - - controls.update(); - composer.render(); + controls.update(); + composer.render(); } // Initialize the scene async function init() { - try { - // Load all models first - preloadedModels = await sceneLoader.loadAllModels(); - - // Initialize the first scene - initializeScene(); - - // Start the animation loop - animate(); - - // Attach scroll event listener - window.addEventListener('wheel', onMouseScroll, {passive: true}); - - } catch (error) { - console.error('Failed to initialize scene:', error); - sceneLoader.setLoadingMessage('Error loading experience. Please refresh.'); - } + try { + console.log('Starting application initialization'); + // Load all models first + preloadedModels = await sceneLoader.loadAllModels(); + console.log('All models loaded successfully'); + // Initialize the first scene + initializeScene(); + // Start the animation loop + animate(); + console.log('Animation loop started'); + // Attach scroll event listener + window.addEventListener('wheel', onMouseScroll, { passive: true }); + console.log('Scroll event listener attached'); + } catch (error) { + console.error('Failed to initialize scene:', error); + sceneLoader.setLoadingMessage('Error loading experience. Please refresh.'); + } } // Handle window resize window.addEventListener('resize', () => { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); - composer.setSize(window.innerWidth, window.innerHeight); + console.log('Window resized'); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + composer.setSize(window.innerWidth, window.innerHeight); }); // Start the application