diff --git a/public/innovation.glb b/public/innovation.glb index 889f493..6fbf154 100644 Binary files a/public/innovation.glb and b/public/innovation.glb differ diff --git a/src/main.js b/src/main.js index 6151b56..aa19f52 100644 --- a/src/main.js +++ b/src/main.js @@ -25,20 +25,24 @@ class SceneLoader { 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(); @@ -113,6 +117,9 @@ let mixer = null; let nextMixer = null; let autoRotationAngle = 0; +// Turntable animation settings +const turntableSpeed = 0.5; // Rotation speed (radians per second) + // Store preloaded models let preloadedModels = {}; @@ -120,7 +127,7 @@ let preloadedModels = {}; let boldRoughnessAnimation = { isActive: false, startTime: 0, - delayDuration: 1.0, // 1 second delay + delayDuration: 1.0, // 1 second delay (will be dynamic) transitionDuration: 1.0, // 1 second transition startRoughness: 0.5, endRoughness: 0.05, @@ -225,12 +232,14 @@ 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: 0xffffff, @@ -314,36 +323,29 @@ 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) + // 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); } @@ -359,27 +361,24 @@ function applyMaterials(model, modelType) { 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); - } + // 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 @@ -536,6 +535,21 @@ function createModelFromPreloaded(modelType) { return { model, animMixer }; } +// Start/restart bold roughness animation with optional delay control +function startBoldRoughnessAnimation(withDelay = true) { + console.log('Starting/restarting bold roughness animation'); + // Reset all bold glass materials to starting roughness value + boldRoughnessAnimation.materials.forEach(material => { + material.roughness = boldRoughnessAnimation.startRoughness; + material.needsUpdate = true; + }); + boldRoughnessAnimation.isActive = true; + boldRoughnessAnimation.startTime = performance.now(); + // Set delayDuration based on withDelay parameter + boldRoughnessAnimation.delayDuration = withDelay ? 1.0 : 0.0; + console.log('Bold roughness animation started with delay:', withDelay); +} + // Initialize first scene after all models are loaded function initializeScene() { console.log('Initializing first scene (bold)'); @@ -543,10 +557,9 @@ function initializeScene() { 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 the roughness animation for bold scene with delay + startBoldRoughnessAnimation(true); + console.log('Bold scene initialized'); } // Start innovation glass animation @@ -627,23 +640,18 @@ function startTransition(direction = 1) { 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; - } - } - }); + // Position next model based on transition direction + if (transitionDirection === 1) { + // Forward: next model starts from diagonal down position (bottom-right) + nextModel.position.copy(transitionDownVector); + console.log(`Next model positioned at diagonal down vector (bottom-right): x=${nextModel.position.x}, y=${nextModel.position.y}, z=${nextModel.position.z}`); + } else { + // Backward: next model starts from diagonal up position (top-left) + nextModel.position.copy(transitionUpVector); + console.log(`Next model positioned at diagonal up vector (top-left): x=${nextModel.position.x}, y=${nextModel.position.y}, z=${nextModel.position.z}`); + } + // Add next model to scene without opacity changes - it will appear instantly when it enters the camera view scene.add(nextModel); - console.log(`Next model positioned at diagonal down vector: x=${nextModel.position.x}, y=${nextModel.position.y}, z=${nextModel.position.z}`); } } @@ -656,42 +664,32 @@ function updateTransition(deltaTime) { const easeInOut = (t) => t * t * (3 - 2 * t); const easedProgress = easeInOut(transitionProgress); if (currentModel) { - // Move current model along diagonal up-left vector - const moveVector = transitionUpVector.clone().multiplyScalar(easedProgress); + // Move current model along diagonal vector based on transition direction + let moveVector; + if (transitionDirection === 1) { + // Forward: current model moves top-left + moveVector = transitionUpVector.clone().multiplyScalar(easedProgress); + console.log('Current model moving top-left (forward transition)'); + } else { + // Backward: current model moves bottom-right + moveVector = transitionDownVector.clone().multiplyScalar(easedProgress); + console.log('Current model moving bottom-right (backward transition)'); + } 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); + // Move next model from diagonal vector to center based on transition direction + let moveVector; + if (transitionDirection === 1) { + // Forward: next model moves from bottom-right to center + moveVector = transitionDownVector.clone().multiplyScalar(1 - easedProgress); + console.log('Next model moving from bottom-right to center (forward transition)'); + } else { + // Backward: next model moves from top-left to center + moveVector = transitionUpVector.clone().multiplyScalar(1 - easedProgress); + console.log('Next model moving from top-left to center (backward transition)'); + } 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) { @@ -713,29 +711,8 @@ function updateTransition(deltaTime) { if (nextModel) { currentModel = nextModel; mixer = nextMixer; - // Reset position and opacity + // Reset position to center 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; @@ -743,8 +720,11 @@ function updateTransition(deltaTime) { 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) { + // Start animations based on current scene + if (currentScene === 0) { + // Restart bold roughness animation when returning to bold section WITHOUT delay + startBoldRoughnessAnimation(false); + } else if (currentScene === 1) { startInnovationGlassAnimation(); } console.log(`Diagonal transition complete. Current scene: ${currentScene}`); @@ -785,6 +765,13 @@ function animate() { if (isTransitioning) { updateTransition(delta); } + // Turntable rotation animation + if (currentModel) { + currentModel.rotation.y += turntableSpeed * delta; + } + if (nextModel) { + nextModel.rotation.y += turntableSpeed * delta; + } // Update bold roughness animation if (boldRoughnessAnimation.isActive) { const elapsed = (performance.now() - boldRoughnessAnimation.startTime) / 1000; @@ -795,7 +782,7 @@ function animate() { // 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 + // Interpolate roughness from 0.5 to 0.05 const currentRoughness = boldRoughnessAnimation.startRoughness + (boldRoughnessAnimation.endRoughness - boldRoughnessAnimation.startRoughness) * easedProgress; // Apply to all bold materials