From a2de17c581788239234febf8da518a7741a2caf9 Mon Sep 17 00:00:00 2001 From: Anuj K Date: Thu, 4 Sep 2025 10:13:33 +0530 Subject: [PATCH] transition logic fixed --- src/main.js | 331 +++++++++++++------------------------- src/transitionManager.js | 339 +++++++++++++-------------------------- 2 files changed, 225 insertions(+), 445 deletions(-) diff --git a/src/main.js b/src/main.js index 0da7a98..4f6d1e3 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,10 @@ -import './style.css' +import './style.css'; import * as THREE from 'three'; + import { SceneLoader } from './sceneLoader.js'; import { createScene, setupLighting, setupControls } from './sceneSetup.js'; import { createModelFromPreloaded } from './modelManager.js'; + import { currentModel, nextModel, @@ -13,87 +15,77 @@ import { onMouseScroll, setCurrentModel, setMixer, - setGLBRepulsionSystem + setGLBRepulsionSystem, + calculateTransitionVectors // ⬅ added } from './transitionManager.js'; + import { startBoldRoughnessAnimation, updateBoldRoughnessAnimation, updateInnovationGlassAnimation } from './animationManager.js'; -// Fluid distortion imports import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'; import { createFluidSimulation, FluidDistortionShader } from './fluidDistortion.js'; - -// Starfield import import { createStarfield } from './starfield.js'; -// Initialize loader +/* ------------------------------------------------------------------ */ +/* loader */ const sceneLoader = new SceneLoader(); sceneLoader.setLoadingMessage('Preparing Your Experience...'); -// Create scene components +/* ------------------------------------------------------------------ */ +/* scene */ const { scene, camera, renderer, composer } = createScene(); setupLighting(scene, camera); const controls = setupControls(camera, renderer); -// Create starfield +/* realign transition vectors whenever user moves the camera */ +controls.addEventListener('change', () => calculateTransitionVectors(camera)); + +/* ------------------------------------------------------------------ */ +/* starfield, turntable & global vars */ const starfield = createStarfield(scene); - -// Turntable animation settings const turntableSpeed = 0.5; - -// Store preloaded models let preloadedModels = {}; -// Enhanced fluid simulation + distortion pass +/* ------------------------------------------------------------------ */ +/* post-processing: fluid distortion */ const dpr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2); const fluid = createFluidSimulation(renderer, dpr); const distortionPass = new ShaderPass(FluidDistortionShader); distortionPass.material.uniforms.tSim.value = fluid.getTexture(); distortionPass.material.uniforms.iResolution.value.set(window.innerWidth * dpr, window.innerHeight * dpr); -distortionPass.material.uniforms.amount.value = 0.005; // Stronger distortion -distortionPass.material.uniforms.chromaticAmount.value = 0.002; // Enhanced chromatic aberration - -// Enhanced lighting parameters +distortionPass.material.uniforms.amount.value = 0.005; +distortionPass.material.uniforms.chromaticAmount.value = 0.002; distortionPass.material.uniforms.lightIntensity.value = 0; distortionPass.material.uniforms.lightColor.value.set(1, 1, 1); distortionPass.material.uniforms.normalStrength.value = 2.0; distortionPass.material.uniforms.ambientLight.value = 1; - -// New ripple whiteness parameters -distortionPass.material.uniforms.rippleWhiteness.value = 0.025; // Amount of white tint -distortionPass.material.uniforms.rippleBrightness.value = 1; // Brightness boost for ripples +distortionPass.material.uniforms.rippleWhiteness.value = 0.025; +distortionPass.material.uniforms.rippleBrightness.value = 1; composer.addPass(distortionPass); -// Enhanced pointer tracking -const pointer = { - x: -1, - y: -1, - strength: 0.0, - prevX: -1, - prevY: -1, - trail: [], // Store trail positions for enhanced effect - maxTrailLength: 5 -}; - -// Mouse coordinates for starfield and GLB interaction +/* ------------------------------------------------------------------ */ +/* pointer + mouse utilities */ +const pointer = { x: -1, y: -1, strength: 0, prevX: -1, prevY: -1 }; const mouse = new THREE.Vector2(); const raycaster = new THREE.Raycaster(); -// GLB cursor repulsion parameters +/* ------------------------------------------------------------------ */ +/* GLB cursor repulsion system */ const glbRepulsion = { - radius: 30, // Repulsion radius - maxDistance: 2, // Maximum distance GLB can move from original position - strength: 8, // Repulsion strength - originalPositions: new Map(), // Store original positions for each model - currentTargets: new Map(), // Store current target positions - interpolationSpeed: 3 // Speed of position interpolation + radius: 30, + maxDistance: 2, + strength: 8, + originalPositions: new Map(), + currentTargets: new Map(), + interpolationSpeed: 3 }; - -// Connect GLB repulsion system to transition manager setGLBRepulsionSystem(glbRepulsion); +/* ------------------------------------------------------------------ */ +/* helper: convert DOM coords → simulation coords */ function toSimPixels(e) { const rect = renderer.domElement.getBoundingClientRect(); const x = (e.clientX - rect.left) * dpr; @@ -101,236 +93,141 @@ function toSimPixels(e) { return { x, y }; } +/* ------------------------------------------------------------------ */ +/* pointer events */ renderer.domElement.addEventListener('pointermove', (e) => { const { x, y } = toSimPixels(e); - const dx = (pointer.prevX < 0) ? 0 : Math.abs(x - pointer.prevX); - const dy = (pointer.prevY < 0) ? 0 : Math.abs(y - pointer.prevY); - const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (6.0 * dpr), 1.0); // More sensitive - pointer.x = x; - pointer.y = y; - pointer.strength = speed * 1.2; // Enhanced strength - pointer.prevX = x; - pointer.prevY = y; + const dx = pointer.prevX < 0 ? 0 : Math.abs(x - pointer.prevX); + const dy = pointer.prevY < 0 ? 0 : Math.abs(y - pointer.prevY); + const speed = Math.min(Math.sqrt(dx * dx + dy * dy) / (6 * dpr), 1); + pointer.x = x; pointer.y = y; pointer.strength = speed * 1.2; + pointer.prevX = x; pointer.prevY = y; - // Update light position to follow cursor const rect = renderer.domElement.getBoundingClientRect(); - const normalizedX = (e.clientX - rect.left) / rect.width; - const normalizedY = 1.0 - (e.clientY - rect.top) / rect.height; // Flip Y - distortionPass.material.uniforms.lightPosition.value.set(normalizedX, normalizedY, 1.0); + const nx = (e.clientX - rect.left) / rect.width; + const ny = 1 - (e.clientY - rect.top) / rect.height; + distortionPass.material.uniforms.lightPosition.value.set(nx, ny, 1); - // Update mouse coordinates for starfield and GLB interaction - mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; - mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; + mouse.x = nx * 2 - 1; + mouse.y = -ny * 2 + 1; }, { passive: true }); renderer.domElement.addEventListener('pointerleave', () => { - pointer.x = -1; - pointer.y = -1; - pointer.strength = 0.0; - mouse.x = -999; - mouse.y = -999; - // Reset light to center when mouse leaves - distortionPass.material.uniforms.lightPosition.value.set(0.5, 0.5, 1.0); + Object.assign(pointer, { x: -1, y: -1, strength: 0 }); + mouse.set(-999, -999); + distortionPass.material.uniforms.lightPosition.value.set(0.5, 0.5, 1); }, { passive: true }); -// GLB cursor repulsion function -function updateGLBRepulsion(camera, mouse, deltaTime) { - if (!mouse || mouse.x === -999 || mouse.y === -999) { - // Mouse is not active, return models to original positions - if (currentModel) { - const originalPos = glbRepulsion.originalPositions.get(currentModel); - if (originalPos) { - const currentTarget = glbRepulsion.currentTargets.get(currentModel) || new THREE.Vector3(); - currentTarget.copy(originalPos); - glbRepulsion.currentTargets.set(currentModel, currentTarget); - // Interpolate to original position - const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0); - currentModel.position.lerp(currentTarget, lerpFactor); - } - } - if (nextModel) { - const originalPos = glbRepulsion.originalPositions.get(nextModel); - if (originalPos) { - const currentTarget = glbRepulsion.currentTargets.get(nextModel) || new THREE.Vector3(); - currentTarget.copy(originalPos); - glbRepulsion.currentTargets.set(nextModel, currentTarget); - // Interpolate to original position - const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0); - nextModel.position.lerp(currentTarget, lerpFactor); - } - } +/* ------------------------------------------------------------------ */ +/* GLB repulsion update */ +function updateGLBRepulsion(camera, mouse, dt) { + if (mouse.x === -999) { + // return objects to original pos + [currentModel, nextModel].forEach(m => { + if (!m) return; + const orig = glbRepulsion.originalPositions.get(m); + if (!orig) return; + const tgt = glbRepulsion.currentTargets.get(m) || orig.clone(); + tgt.copy(orig); + glbRepulsion.currentTargets.set(m, tgt); + m.position.lerp(tgt, Math.min(glbRepulsion.interpolationSpeed * dt, 1)); + }); return; } - // Get mouse position in 3D world space raycaster.setFromCamera(mouse, camera); - const distance = 50; // Project to a plane at fixed distance - const mouseWorldPos = raycaster.ray.direction.clone().multiplyScalar(distance).add(raycaster.ray.origin); + const mouseWorld = raycaster.ray.direction.clone().multiplyScalar(50).add(raycaster.ray.origin); - // Handle current model repulsion - if (currentModel) { - let originalPos = glbRepulsion.originalPositions.get(currentModel); - if (!originalPos) { - originalPos = currentModel.position.clone(); - glbRepulsion.originalPositions.set(currentModel, originalPos); - } + [currentModel, nextModel].forEach(m => { + if (!m) return; + if (!glbRepulsion.originalPositions.has(m)) + glbRepulsion.originalPositions.set(m, m.position.clone()); - // Calculate repulsion - const modelPos = currentModel.position.clone(); - const dx = modelPos.x - mouseWorldPos.x; - const dy = modelPos.y - mouseWorldPos.y; - const dz = modelPos.z - mouseWorldPos.z; + const orig = glbRepulsion.originalPositions.get(m); + const dx = m.position.x - mouseWorld.x; + const dy = m.position.y - mouseWorld.y; + const dz = m.position.z - mouseWorld.z; const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); + let target = orig.clone(); - let targetPos = originalPos.clone(); if (dist < glbRepulsion.radius && dist > 0) { const force = (1 - dist / glbRepulsion.radius) * glbRepulsion.strength; - const nx = dx / dist; - const ny = dy / dist; - const nz = dz / dist; - const repulsionVector = new THREE.Vector3(nx * force, ny * force, nz * force); - targetPos.add(repulsionVector); - // Limit maximum distance from original position - const offsetFromOriginal = targetPos.clone().sub(originalPos); - if (offsetFromOriginal.length() > glbRepulsion.maxDistance) { - offsetFromOriginal.normalize().multiplyScalar(glbRepulsion.maxDistance); - targetPos = originalPos.clone().add(offsetFromOriginal); - } + target.add(new THREE.Vector3(dx, dy, dz).normalize().multiplyScalar(force)); + const offset = target.clone().sub(orig); + if (offset.length() > glbRepulsion.maxDistance) + target = orig.clone().add(offset.normalize().multiplyScalar(glbRepulsion.maxDistance)); } - // Store and interpolate to target position - glbRepulsion.currentTargets.set(currentModel, targetPos); - const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0); - currentModel.position.lerp(targetPos, lerpFactor); - } - - // Handle next model repulsion during transitions - if (nextModel) { - let originalPos = glbRepulsion.originalPositions.get(nextModel); - if (!originalPos) { - originalPos = nextModel.position.clone(); - glbRepulsion.originalPositions.set(nextModel, originalPos); - } - - // Calculate repulsion - const modelPos = nextModel.position.clone(); - const dx = modelPos.x - mouseWorldPos.x; - const dy = modelPos.y - mouseWorldPos.y; - const dz = modelPos.z - mouseWorldPos.z; - const dist = Math.sqrt(dx * dx + dy * dy + dz * dz); - - let targetPos = originalPos.clone(); - if (dist < glbRepulsion.radius && dist > 0) { - const force = (1 - dist / glbRepulsion.radius) * glbRepulsion.strength; - const nx = dx / dist; - const ny = dy / dist; - const nz = dz / dist; - const repulsionVector = new THREE.Vector3(nx * force, ny * force, nz * force); - targetPos.add(repulsionVector); - // Limit maximum distance from original position - const offsetFromOriginal = targetPos.clone().sub(originalPos); - if (offsetFromOriginal.length() > glbRepulsion.maxDistance) { - offsetFromOriginal.normalize().multiplyScalar(glbRepulsion.maxDistance); - targetPos = originalPos.clone().add(offsetFromOriginal); - } - } - - // Store and interpolate to target position - glbRepulsion.currentTargets.set(nextModel, targetPos); - const lerpFactor = Math.min(glbRepulsion.interpolationSpeed * deltaTime, 1.0); - nextModel.position.lerp(targetPos, lerpFactor); - } + glbRepulsion.currentTargets.set(m, target); + m.position.lerp(target, Math.min(glbRepulsion.interpolationSpeed * dt, 1)); + }); } -// Initialize first scene +/* ------------------------------------------------------------------ */ +/* first scene setup */ function initializeScene() { - console.log('Initializing first scene (bold)'); const { model, animMixer } = createModelFromPreloaded('bold', preloadedModels, camera, controls); setCurrentModel(model); setMixer(animMixer); scene.add(currentModel); - // Store original position for repulsion glbRepulsion.originalPositions.set(currentModel, currentModel.position.clone()); startBoldRoughnessAnimation(true); - console.log('Bold scene initialized'); } -// Animation loop +/* ------------------------------------------------------------------ */ +/* animation loop */ const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); - const delta = clock.getDelta(); + const dt = clock.getDelta(); - // Update mixers - if (mixer) mixer.update(delta); - if (nextMixer) nextMixer.update(delta); + if (mixer) mixer.update(dt); + if (nextMixer) nextMixer.update(dt); - // Update transition - if (isTransitioning) { - updateTransition(delta, scene); - } + if (isTransitioning) updateTransition(dt, scene); + else updateGLBRepulsion(camera, mouse, dt); - // GLB cursor repulsion (only when not transitioning to avoid conflicts) - if (!isTransitioning) { - updateGLBRepulsion(camera, mouse, delta); - } + // turntable + if (currentModel) currentModel.rotation.y += turntableSpeed * dt; + if (nextModel) nextModel.rotation.y += turntableSpeed * dt; - // Turntable rotation - if (currentModel) { - currentModel.rotation.y += turntableSpeed * delta; - } - if (nextModel) { - nextModel.rotation.y += turntableSpeed * delta; - } - - // Update material animations + // material anims updateBoldRoughnessAnimation(); updateInnovationGlassAnimation(); - // Animate stars with cursor interaction - starfield.animateStars(camera, mouse, delta); - - // Update enhanced fluid sim - const nowSec = performance.now() / 1000; - fluid.update(pointer.x, pointer.y, pointer.strength, nowSec); + // stars + fluid + starfield.animateStars(camera, mouse, dt); + fluid.update(pointer.x, pointer.y, pointer.strength, performance.now() / 1000); distortionPass.material.uniforms.tSim.value = fluid.getTexture(); controls.update(); composer.render(); } -// Initialize the scene +/* ------------------------------------------------------------------ */ +/* init workflow */ async function init() { try { - console.log('Starting application initialization'); preloadedModels = await sceneLoader.loadAllModels(); - console.log('All models loaded successfully'); initializeScene(); animate(); - console.log('Animation loop started'); - window.addEventListener('wheel', (event) => { - onMouseScroll(event, preloadedModels, scene, camera, controls); - }, { passive: true }); - console.log('Scroll event listener attached'); - } catch (error) { - console.error('Failed to initialize scene:', error); + + window.addEventListener('wheel', (e) => onMouseScroll(e, preloadedModels, scene, camera, controls), { passive: true }); + + window.addEventListener('resize', () => { + const w = window.innerWidth, h = window.innerHeight; + camera.aspect = w / h; camera.updateProjectionMatrix(); + renderer.setSize(w, h); + composer.setSize(w, h); + const pr = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2); + distortionPass.material.uniforms.iResolution.value.set(w * pr, h * pr); + fluid.resize(w, h, pr); + }); + } catch (err) { + console.error('Failed to initialise:', err); sceneLoader.setLoadingMessage('Error loading experience. Please refresh.'); } } -// Handle window resize -window.addEventListener('resize', () => { - console.log('Window resized'); - const w = window.innerWidth; - const h = window.innerHeight; - camera.aspect = w / h; - camera.updateProjectionMatrix(); - renderer.setSize(w, h); - composer.setSize(w, h); - const pixelRatio = renderer.getPixelRatio ? renderer.getPixelRatio() : Math.min(window.devicePixelRatio || 1, 2); - distortionPass.material.uniforms.iResolution.value.set(w * pixelRatio, h * pixelRatio); - fluid.resize(w, h, pixelRatio); -}); - -// Start the application -init(); \ No newline at end of file +/* ------------------------------------------------------------------ */ +init(); diff --git a/src/transitionManager.js b/src/transitionManager.js index c1bb3f8..cf21c92 100644 --- a/src/transitionManager.js +++ b/src/transitionManager.js @@ -2,262 +2,145 @@ import * as THREE from 'three'; import { createModelFromPreloaded, resetMeshGeometry, cleanupGeometryData } from './modelManager.js'; import { startBoldRoughnessAnimation, startInnovationGlassAnimation } from './animationManager.js'; -// Transition state management -export let currentScene = 0; // 0: bold, 1: innovation, 2: agility, 3: storytelling -export let isTransitioning = false; -export const fadeSpeed = 1; // Easily adjustable fade speed -export const transitionDuration = 1; // Easily adjustable transition duration (seconds) -export let scrollDownCount = 0; -export let scrollUpCount = 0; -export const scrollThreshold = 10; // Changed to 10 as requested -export let transitionStartTime = 0; -export let transitionDirection = 1; // 1 for forward, -1 for backward +/* ------------------------------------------------------------------ */ +/* state */ +export let currentScene = 0; // 0-bold | 1-innovation | 2-agility | 3-storytelling +let pendingScene = 0; // target index during a transition +export let isTransitioning = false; -// Camera-relative transition vectors -export let transitionUpVector = new THREE.Vector3(); -export let transitionDownVector = new THREE.Vector3(); -export const transitionDistance = 50; // Increased distance for more dramatic transitions +export const transitionDuration = 1; // seconds +export const scrollThreshold = 10; +export const transitionDistance = 50; -// Scene objects +let scrollDownCount = 0; +let scrollUpCount = 0; +let transitionStartTime = 0; +let transitionDirection = 1; // 1 forward | -1 back + +/* ------------------------------------------------------------------ */ +/* scene objects */ export let currentModel = null; -export let nextModel = null; -export let mixer = null; -export let nextMixer = null; -export let autoRotationAngle = 0; - -// GLB repulsion system reference (will be set by main.js) +export let nextModel = null; +export let mixer = null; +export let nextMixer = null; export let glbRepulsionSystem = null; -// Setter functions to modify exported variables safely -export function setCurrentModel(model) { - currentModel = model; +/* camera-aligned vectors */ +let transitionUpVector = new THREE.Vector3(); +let transitionDownVector = new THREE.Vector3(); + +/* setters ----------------------------------------------------------- */ +export const setCurrentModel = m => currentModel = m; +export const setMixer = m => mixer = m; +export const setGLBRepulsionSystem = s => glbRepulsionSystem = s; + +/* utilities --------------------------------------------------------- */ +const sceneKey = idx => ['bold','innovation','agility','storytelling'][idx]; + +/* compute camera-relative diagonal vectors */ +export function calculateTransitionVectors(camera){ + const fwd = new THREE.Vector3(); camera.getWorldDirection(fwd).normalize(); + const up = new THREE.Vector3(0,1,0); + const right = new THREE.Vector3().crossVectors(fwd, up).normalize(); + const camUp = new THREE.Vector3().crossVectors(right, fwd).normalize(); + + const diag = new THREE.Vector3() + .addScaledVector(camUp, 0.5) + .addScaledVector(right, -0.5) + .normalize(); + + transitionUpVector .copy(diag).multiplyScalar( transitionDistance); + transitionDownVector.copy(diag).multiplyScalar(-transitionDistance); } -export function setMixer(animMixer) { - mixer = animMixer; -} +/* ------------------------------------------------------------------ */ +/* transition start */ +export function startTransition(dir, preload, scene, camera, controls){ + if(isTransitioning) return; -export function setNextModel(model) { - nextModel = model; -} + const nextIdx = currentScene + dir; + if(nextIdx < 0 || nextIdx > 3) return; // out-of-range -export function setNextMixer(animMixer) { - nextMixer = animMixer; -} + isTransitioning = true; + pendingScene = nextIdx; + transitionDirection = dir; + transitionStartTime = performance.now(); -export function setGLBRepulsionSystem(system) { - glbRepulsionSystem = system; -} - -// Calculate camera-relative transition vectors for diagonal movement -export function calculateTransitionVectors(camera) { - // 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); -} - -// Start transition to next or previous scene -export function startTransition(direction = 1, preloadedModels, scene, camera, controls) { - 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(camera); - isTransitioning = true; - transitionStartTime = performance.now(); - transitionDirection = direction; + const { model, animMixer } = + createModelFromPreloaded(sceneKey(nextIdx), preload, camera, controls); - // 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'; - } - } + nextModel = model; + nextMixer = animMixer; + nextModel.position.copy(dir > 0 ? transitionDownVector : transitionUpVector); - console.log(`Next model type: ${nextModelType}`); - if (nextModelType) { - const { model, animMixer } = createModelFromPreloaded(nextModelType, preloadedModels, camera, controls); - nextModel = model; - nextMixer = animMixer; + if(glbRepulsionSystem) + glbRepulsionSystem.originalPositions.set(nextModel, nextModel.position.clone()); - // 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}`); - } - - // Register next model with GLB repulsion system - if (glbRepulsionSystem) { - glbRepulsionSystem.originalPositions.set(nextModel, nextModel.position.clone()); - } - - // Add next model to scene without opacity changes - it will appear instantly when it enters the camera view - scene.add(nextModel); - } + scene.add(nextModel); } -// Update transition animation -export function updateTransition(deltaTime, scene) { - if (!isTransitioning) return; +/* ------------------------------------------------------------------ */ +/* transition update */ +export function updateTransition(_dt, scene){ + if(!isTransitioning) return; - const elapsed = (performance.now() - transitionStartTime) / 1000; - const transitionProgress = Math.min(elapsed / transitionDuration, 1); + const t = Math.min((performance.now() - transitionStartTime)/1000 / transitionDuration, 1); + const e = t*t*(3-2*t); // smoothstep easing - // Smooth easing function (ease-in-out) - const easeInOut = (t) => t * t * (3 - 2 * t); - const easedProgress = easeInOut(transitionProgress); + if(currentModel) + currentModel.position.copy( + (transitionDirection>0 ? transitionUpVector : transitionDownVector).clone().multiplyScalar(e) + ); - if (currentModel) { - // 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)'); + if(nextModel) + nextModel.position.copy( + (transitionDirection>0 ? transitionDownVector : transitionUpVector).clone().multiplyScalar(1-e) + ); + + if(t < 1) return; // still animating + + /* ----- complete transition -------------------------------------- */ + if(currentModel){ + currentModel.traverse(o => o.isMesh && resetMeshGeometry(o)); + cleanupGeometryData(currentModel); + if(glbRepulsionSystem){ + glbRepulsionSystem.originalPositions.delete(currentModel); + glbRepulsionSystem.currentTargets.delete(currentModel); } - currentModel.position.copy(moveVector); + scene.remove(currentModel); } - if (nextModel) { - // 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); - } + currentModel = nextModel; + mixer = nextMixer; + currentModel.position.set(0,0,0); - // 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); - // Remove from GLB repulsion system - if (glbRepulsionSystem) { - glbRepulsionSystem.originalPositions.delete(currentModel); - glbRepulsionSystem.currentTargets.delete(currentModel); - } - scene.remove(currentModel); - console.log('Previous model removed from scene'); - } + if(glbRepulsionSystem) + glbRepulsionSystem.originalPositions.set(currentModel, currentModel.position.clone()); - // Switch to next model - if (nextModel) { - currentModel = nextModel; - mixer = nextMixer; - // Reset position to center - currentModel.position.set(0, 0, 0); - // Update GLB repulsion system with new center position - if (glbRepulsionSystem) { - glbRepulsionSystem.originalPositions.set(currentModel, currentModel.position.clone()); - } - } + nextModel = nextMixer = null; - nextModel = null; - nextMixer = null; - isTransitioning = false; - currentScene += transitionDirection; // Update scene based on direction - scrollDownCount = 0; - scrollUpCount = 0; + currentScene = pendingScene; // now official + isTransitioning = false; + scrollDownCount = scrollUpCount = 0; - // 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}`); - } + if(currentScene === 0) startBoldRoughnessAnimation(false); + if(currentScene === 1) startInnovationGlassAnimation(); } -// Scroll event handler -export function onMouseScroll(event, preloadedModels, scene, camera, controls) { - if (isTransitioning) return; +/* ------------------------------------------------------------------ */ +/* scroll handler */ +export function onMouseScroll(ev, preload, scene, camera, controls){ + 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, preloadedModels, scene, camera, controls); // 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, preloadedModels, scene, camera, controls); // Backward direction - } + if(ev.deltaY > 0){ + scrollDownCount++; scrollUpCount = 0; + if(scrollDownCount >= scrollThreshold) + startTransition(+1, preload, scene, camera, controls); + }else if(ev.deltaY < 0){ + scrollUpCount++; scrollDownCount = 0; + if(scrollUpCount >= scrollThreshold) + startTransition(-1, preload, scene, camera, controls); } -} \ No newline at end of file +}