From 6bd4a96e2d9adb371f2666ffd4ed65c9d14fa13b Mon Sep 17 00:00:00 2001 From: Anuj K Date: Thu, 4 Sep 2025 09:06:21 +0530 Subject: [PATCH] almost final --- src/fluidDistortion.js | 4 +- src/main.js | 149 +++++++++++++++++++++++++++++++++++++---- src/sceneSetup.js | 19 ++---- src/starfield.js | 2 +- 4 files changed, 142 insertions(+), 32 deletions(-) diff --git a/src/fluidDistortion.js b/src/fluidDistortion.js index a6d7abd..426522f 100644 --- a/src/fluidDistortion.js +++ b/src/fluidDistortion.js @@ -8,7 +8,7 @@ const FluidSimShader = { iResolution: { value: new THREE.Vector2() }, iTime: { value: 0.0 }, mouse: { value: new THREE.Vector3(-1, -1, 0.0) }, - dissipation: { value: 0.950 }, // Slightly more persistent for trails + dissipation: { value: 0.95 }, // Slightly more persistent for trails tension: { value: 2.2 }, // Higher tension for stronger ripples radius: { value: 20.0 }, // Larger splat radius trailLength: { value: 5 }, // Number of trailing ripples @@ -80,7 +80,7 @@ const FluidSimShader = { // Gaussian with sine wave for ripple pattern float g = exp(-pow(dist - offset, 2.0) / (r * r * 0.5)); - float ripple = sin(dist * 0.2 - iTime * 8.0 + timeOffset) * g; + float ripple = sin(dist * 0.2 - iTime * 16.0 + timeOffset) * g; // Diminishing strength for trailing ripples float strength = mouse.z * (1.0 - i * 0.2) * 0.8; diff --git a/src/main.js b/src/main.js index e639413..0da7a98 100644 --- a/src/main.js +++ b/src/main.js @@ -12,7 +12,8 @@ import { updateTransition, onMouseScroll, setCurrentModel, - setMixer + setMixer, + setGLBRepulsionSystem } from './transitionManager.js'; import { startBoldRoughnessAnimation, @@ -48,7 +49,6 @@ let preloadedModels = {}; // Enhanced fluid simulation + distortion pass 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); @@ -64,7 +64,6 @@ 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 - composer.addPass(distortionPass); // Enhanced pointer tracking @@ -78,8 +77,22 @@ const pointer = { maxTrailLength: 5 }; -// Mouse coordinates for starfield +// Mouse coordinates for starfield and GLB interaction const mouse = new THREE.Vector2(); +const raycaster = new THREE.Raycaster(); + +// GLB cursor repulsion parameters +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 +}; + +// Connect GLB repulsion system to transition manager +setGLBRepulsionSystem(glbRepulsion); function toSimPixels(e) { const rect = renderer.domElement.getBoundingClientRect(); @@ -93,7 +106,6 @@ renderer.domElement.addEventListener('pointermove', (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 @@ -106,7 +118,7 @@ renderer.domElement.addEventListener('pointermove', (e) => { const normalizedY = 1.0 - (e.clientY - rect.top) / rect.height; // Flip Y distortionPass.material.uniforms.lightPosition.value.set(normalizedX, normalizedY, 1.0); - // Update mouse coordinates for starfield + // 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; }, { passive: true }); @@ -117,11 +129,119 @@ renderer.domElement.addEventListener('pointerleave', () => { 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); }, { 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); + } + } + 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); + + // Handle current model repulsion + if (currentModel) { + let originalPos = glbRepulsion.originalPositions.get(currentModel); + if (!originalPos) { + originalPos = currentModel.position.clone(); + glbRepulsion.originalPositions.set(currentModel, originalPos); + } + + // 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 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(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); + } +} + // Initialize first scene function initializeScene() { console.log('Initializing first scene (bold)'); @@ -129,13 +249,14 @@ function initializeScene() { 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 const clock = new THREE.Clock(); - function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); @@ -149,6 +270,11 @@ function animate() { updateTransition(delta, scene); } + // GLB cursor repulsion (only when not transitioning to avoid conflicts) + if (!isTransitioning) { + updateGLBRepulsion(camera, mouse, delta); + } + // Turntable rotation if (currentModel) { currentModel.rotation.y += turntableSpeed * delta; @@ -179,16 +305,13 @@ async function init() { 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); sceneLoader.setLoadingMessage('Error loading experience. Please refresh.'); @@ -200,16 +323,14 @@ 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(); +init(); \ No newline at end of file diff --git a/src/sceneSetup.js b/src/sceneSetup.js index 14722ed..a65ccbf 100644 --- a/src/sceneSetup.js +++ b/src/sceneSetup.js @@ -30,7 +30,6 @@ export function createScene() { 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 @@ -54,55 +53,45 @@ export function setupLighting(scene, camera) { // 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); } export function setupControls(camera, renderer) { - // Controls with zoom disabled and camera constraints + // Controls with zoom and pan disabled and camera constraints const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.25; controls.enableZoom = false; // Disable zoom - + controls.enablePan = false; // Disable panning // 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'); + console.log('Orbit controls initialized with camera constraints and pan disabled'); return controls; -} +} \ No newline at end of file diff --git a/src/starfield.js b/src/starfield.js index bcd3ab8..c577788 100644 --- a/src/starfield.js +++ b/src/starfield.js @@ -1,7 +1,7 @@ import * as THREE from 'three'; export function createStarfield(scene) { - const starCount = 12000; + const starCount = 16000; const starDistance = 300; // Create geometry for stars