diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index 5851e18d..6d860408 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -1,235 +1,229 @@ "use client"; -import React, { useMemo, useRef, useEffect } from "react"; -import * as THREE from "three"; -import { Canvas, useFrame, useThree } from "@react-three/fiber"; -import { OrbitControls } from "@react-three/drei"; -import gsap from "gsap"; -import vertexShader from "@/components/textures/shaders/LandingVertex.glsl"; -import fragmentShader from "@/components/textures/shaders/LandingFrag.glsl"; -import "./Plots.css"; -import { useGlobalStore, usePlotStore } from "@/utils/GlobalStates"; -import { useShallow } from "zustand/shallow"; - -const COUNT = 15625; - -// Seeded random number generator for deterministic results -const seededRandom = (seed: number) => { - const x = Math.sin(seed++) * 10000; - return x - Math.floor(x); +import React, { useMemo, useRef, useEffect } from 'react'; +import * as THREE from 'three'; +import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'; +import gsap from 'gsap'; +import vertexShader from '@/components/textures/shaders/LandingVertex.glsl' +import fragmentShader from '@/components/textures/shaders/LandingFrag.glsl' +import './Plots.css'; +import { useGlobalStore, usePlotStore } from '@/utils/GlobalStates'; +import { useShallow } from 'zustand/shallow'; + + +// Define the type for our custom shader material's uniforms +type MorphMaterialType = THREE.ShaderMaterial & { + uniforms: { + uSphereMix: { value: number }; + uCubeMix: { value: number }; + uPlaneMix: { value: number }; + uSize: { value: number }; + uTime: { value: number }; + }; }; -const MorphingPoints = () => { - const pointsRef = useRef(null); - const materialRef = useRef(null); - const { gl } = useThree(); - - const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore( - useShallow((s) => ({ - setMaxTextureSize: s.setMaxTextureSize, - setMax3DTextureSize: s.setMax3DTextureSize, - })) - ); +// Define the shader material using drei's helper - useEffect(() => { - const ctx = gl.getContext(); - setMaxTextureSize(ctx.getParameter(ctx.MAX_TEXTURE_SIZE)); +// Make the material available to extend - if (ctx instanceof WebGL2RenderingContext) { - setMax3DTextureSize( - ctx.getParameter(ctx.MAX_3D_TEXTURE_SIZE) - ); - } - }, [gl, setMaxTextureSize, setMax3DTextureSize]); - const { colormap } = useGlobalStore( - useShallow((s) => ({ colormap: s.colormap })) - ); - - // Geometry data - use seeded random for deterministic results - const { - spherePositions, - cubePositions, - planePositions, - spawnPositions, - delays, - } = useMemo(() => { - const sphere = new Float32Array(COUNT * 3); - const cube = new Float32Array(COUNT * 3); - const plane = new Float32Array(COUNT * 3); - const spawn = new Float32Array(COUNT * 3); - const delays = new Float32Array(COUNT); - - // Sphere (Fibonacci) - const phi = Math.PI * (3 - Math.sqrt(5)); - for (let i = 0; i < COUNT; i++) { - const y = 1 - (i / (COUNT - 1)) * 2; - const r = Math.sqrt(1 - y * y); - const t = phi * i; - - sphere[i * 3 + 0] = Math.cos(t) * r * 1.2; - sphere[i * 3 + 1] = y * 1.2; - sphere[i * 3 + 2] = Math.sin(t) * r * 1.2; - - // Use seeded random for deterministic spawn positions - const a = seededRandom(i * 3) * Math.PI * 2; - const b = seededRandom(i * 3 + 1) * Math.PI; - const d = 4 + seededRandom(i * 3 + 2) * 2; - - spawn[i * 3 + 0] = Math.sin(b) * Math.cos(a) * d; - spawn[i * 3 + 1] = Math.sin(b) * Math.sin(a) * d; - spawn[i * 3 + 2] = Math.cos(b) * d; - - delays[i] = seededRandom(i * 5); +const MorphingPoints = () => { + const pointsRef = useRef(null); + const count = 15625; // Total number of points + const {gl} = useThree(); + const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore(useShallow(state => ({ + setMaxTextureSize: state.setMaxTextureSize, + setMax3DTextureSize: state.setMax3DTextureSize + }))) + + useEffect(()=>{ + const context = gl.getContext() + //@ts-expect-error This parameter does exist + setMax3DTextureSize(context.getParameter(context.MAX_3D_TEXTURE_SIZE)) + setMaxTextureSize(context.getParameter(context.MAX_TEXTURE_SIZE)) + },[]) + + + const {colormap} = useGlobalStore(useShallow(state => ({ + colormap: state.colormap + }))) + // Pre-calculate the point positions for each shape using useMemo for performance + const { spherePositions, cubePositions, planePositions } = useMemo(() => { + const spherePositions = new Float32Array(count * 3); + const cubePositions = new Float32Array(count * 3); + const planePositions = new Float32Array(count * 3); + + // --- Sphere Positions (using Fibonacci lattice for even distribution) --- + const phi = Math.PI * (3.0 - Math.sqrt(5.0)); // Golden angle + for (let i = 0; i < count; i++) { + const y = 1 - (i / (count - 1)) * 2; // y goes from 1 to -1 + const radius = Math.sqrt(1 - y * y); + const theta = phi * i; + + const x = Math.cos(theta) * radius; + const z = Math.sin(theta) * radius; + + spherePositions[i * 3] = x * 1.2; + spherePositions[i * 3 + 1] = y * 1.2; + spherePositions[i * 3 + 2] = z * 1.2; } - // Cube - const r = 25; - let idx = 0; - for (let x = 0; x < r; x++) - for (let y = 0; y < r; y++) - for (let z = 0; z < r; z++) { - cube[idx * 3 + 0] = (x / (r - 1) - 0.5) * 2; - cube[idx * 3 + 1] = (y / (r - 1) - 0.5) * 2; - cube[idx * 3 + 2] = (z / (r - 1) - 0.5) * 2; - idx++; + // --- Cube Positions (16x16x16 grid) --- + const cubRes = 25 + let i = 0; + for (let x = 0; x < cubRes; x++) { + for (let y = 0; y < cubRes; y++) { + for (let z = 0; z < cubRes; z++) { + cubePositions[i * 3] = (x / cubRes - 0.5) * 2; + cubePositions[i * 3 + 1] = (y / cubRes - 0.5) * 2; + cubePositions[i * 3 + 2] = (z / cubRes - 0.5) * 2; + i++; } - - // Plane - const p = 125; - idx = 0; - for (let x = 0; x < p; x++) - for (let y = 0; y < p; y++) { - plane[idx * 3 + 0] = (x / (p - 1) - 0.5) * 2.5; - plane[idx * 3 + 1] = (y / (p - 1) - 0.5) * 2.5; - plane[idx * 3 + 2] = 0; - idx++; } + } - return { - spherePositions: sphere, - cubePositions: cube, - planePositions: plane, - spawnPositions: spawn, - delays, - }; - }, []); - - const initialUniforms = useMemo( - () => ({ - uSphereMix: { value: 0 }, - uCubeMix: { value: 0 }, - uPlaneMix: { value: 0 }, - uRandomMix: { value: 0 }, - uArrivalProgress: { value: 0 }, - uHold: { value: 0 }, - uTime: { value: 0 }, - uSize: { value: 15 }, - cmap: { value: null }, - }), - [] - ); - - // Update colormap when it changes - useEffect(() => { - if (materialRef.current) { - materialRef.current.uniforms.cmap.value = colormap; - materialRef.current.needsUpdate = true; + // --- Plane Positions (64x64 grid) --- + const planeRes = 125 + i = 0; + for (let x = 0; x < planeRes; x++) { + for (let y = 0; y < planeRes; y++) { + planePositions[i * 3] = (x / planeRes - 0.5) * 2.5 ; + planePositions[i * 3 + 1] = (y / planeRes - 0.5) * 2.5 ; + planePositions[i * 3 + 2] = 0; + i++; + } } - }, [colormap]); - // Animation timeline + return { spherePositions, cubePositions, planePositions }; + }, [count]); + + const MorphMaterial = useMemo(()=>new THREE.ShaderMaterial({ + glslVersion: THREE.GLSL3, + uniforms: { + uSphereMix: {value: 1.0}, + uCubeMix: {value: 0.0}, + uPlaneMix: {value: 0.0}, + uTime: {value: 0.0}, + cmap: { value: colormap} + }, + vertexShader, + fragmentShader + }),[]) + // Animation effect useEffect(() => { - if (!materialRef.current) return; - - const u = materialRef.current.uniforms; - const tl = gsap.timeline({ repeat: -1 }); - - const arrive = 4.5; - const morph = 2.5; - const hold = 2.5; - - tl.to(u.uArrivalProgress, { value: 1, duration: arrive, ease: "power1.out" }); - tl.to(u.uSphereMix, { value: 1, duration: arrive }, "<"); - - const addHold = () => { - tl.to(u.uHold, { value: 1, duration: 0.01 }); - tl.to({}, { duration: hold }); - tl.to(u.uHold, { value: 0, duration: 0.01 }); - }; - - addHold(); - - tl.to([u.uSphereMix, u.uCubeMix], { - value: (i: number) => (i === 0 ? 0 : 1), - duration: morph, - }); - - addHold(); - - tl.to([u.uCubeMix, u.uPlaneMix], { - value: (i: number) => (i === 0 ? 0 : 1), - duration: morph, - }); - - addHold(); - - tl.to([u.uPlaneMix, u.uRandomMix], { - value: (i: number) => (i === 0 ? 0 : 1), - duration: morph, - }); - - addHold(); - - tl.to(u.uArrivalProgress, { value: 0, duration: morph }); - tl.set([u.uRandomMix, u.uSphereMix], { value: 0 }); - + let tl: gsap.core.Timeline | null = null; + + if (MorphMaterial) { + const uniforms = MorphMaterial.uniforms; + + // Create a GSAP timeline for the morphing animation + tl = gsap.timeline({ + repeat: -1, // Loop indefinitely + yoyo: false, + }); + + const duration = 2; // Duration of each morph transition + const delay = 3; // Time to hold each shape before morphing + + // 1. Morph from Sphere to Cube + tl.to(uniforms.uCubeMix, { + value: 1, + duration, + delay, + ease: 'power2.inOut', + }); + + // 2. Morph from Cube to Plane + tl.to(uniforms.uCubeMix, { + value: 0, + duration, + delay, + ease: 'power2.inOut', + }); + tl.to(uniforms.uPlaneMix, { + value: 1, + duration, + ease: 'power2.inOut', + }, "<"); // Animate at the same time as the previous tween + + // 3. Morph from Plane back to Sphere + tl.to(uniforms.uPlaneMix, { + value: 0, + duration, + delay, + ease: 'power2.inOut', + }); + } + // Cleanup function - always return this return () => { - tl.kill(); + if (tl) { + tl.kill(); + } }; }, []); - + + // Update time uniform for the dynamic wave animation in the shader useFrame((state) => { - if (materialRef.current) { - materialRef.current.uniforms.uTime.value = state.clock.getElapsedTime(); - } - if (pointsRef.current) { - pointsRef.current.rotation.y += 0.0002; - pointsRef.current.rotation.x += 0.0001; - } + if(MorphMaterial){ + MorphMaterial.uniforms.uTime.value = state.clock.getElapsedTime(); + } + if (pointsRef.current) { + pointsRef.current.rotation.y += 0.001; // Slow rotation around Y-axis + pointsRef.current.rotation.x += 0.001; + } }); + useEffect(()=>{ + if(MorphMaterial){ + MorphMaterial.uniforms.cmap.value = colormap + } + },[colormap]) + return ( - + - - - - - - - + {/* The 'position' attribute is not used by the shader for final position, + but it's good practice to have it. We'll use sphere positions as the base. */} + + + + - + {/* Use the custom morphMaterial */} ); }; -export const LandingShapes = () => ( -
- - - + +export const LandingShapes = () =>{ + return( +
+ {/*
+ Browzarr +
*/} + + + -
-); \ No newline at end of file +
+ ) +} \ No newline at end of file diff --git a/src/components/textures/shaders/LandingFrag.glsl b/src/components/textures/shaders/LandingFrag.glsl index 69d0400e..89f0d0f1 100644 --- a/src/components/textures/shaders/LandingFrag.glsl +++ b/src/components/textures/shaders/LandingFrag.glsl @@ -1,13 +1,9 @@ -precision highp float; +// Fragment Shader in vec3 vColor; -in float vGreyMix; -in float vAlpha; - out vec4 Color; void main() { - vec3 silver = vec3(0.75); - vec3 finalColor = mix(vColor, silver, vGreyMix); - Color = vec4(finalColor, vAlpha); -} + // Simple white color for the points + Color = vec4(vColor, 0.8); +} \ No newline at end of file diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index 2ba7fd57..fcc021b1 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -1,81 +1,45 @@ -precision highp float; uniform float uSphereMix; uniform float uCubeMix; uniform float uPlaneMix; -uniform float uRandomMix; -uniform float uArrivalProgress; -uniform float uHold; -uniform float uTime; uniform float uSize; +uniform float uTime; uniform sampler2D cmap; -in vec3 aSpherePosition; -in vec3 aCubePosition; -in vec3 aPlanePosition; -in vec3 aSpawnPosition; -in vec3 aRandomPosition; -in float aDelay; -out vec3 vColor; -out float vGreyMix; -out float vAlpha; -// Organic flow field (only during transitions) -vec3 flowField(vec3 p, float t) { - float s = 0.4; - return vec3( - sin(p.x * s + t) * cos(p.y * s), - cos(p.y * s + t) * sin(p.z * s), - sin(p.z * s) * cos(p.x * s + t) - ) * 0.3; -} +attribute vec3 aSpherePosition; +attribute vec3 aCubePosition; +attribute vec3 aPlanePosition; + +varying vec3 vColor; void main() { - /* ---------------- Arrival ---------------- */ - float arrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); - - /* ---------------- Active shape ---------------- */ - vec3 shape = mix(aSpherePosition, aCubePosition, uCubeMix); - shape = mix(shape, aPlanePosition, uPlaneMix); - shape = mix(shape, aRandomPosition, uRandomMix); - - /* ---------------- Flow during morphs ---------------- */ - float transition = uCubeMix + uPlaneMix + uRandomMix; - shape += flowField(shape, uTime) * transition * 0.5; - - /* ---------------- Spawn → shape ---------------- */ - vec3 pos = mix(aSpawnPosition, shape, arrival); - - /* ---------------- Distance logic ---------------- */ - float dist = distance(pos, shape); - // Distance influence (soft) - float farMask = smoothstep(0.35, 0.9, dist); - - // Staggered hold easing - each particle transitions at different time - float holdOffset = aDelay * 0.5; // Use half the delay range for smoother stagger - float particleHold = smoothstep(holdOffset, holdOffset + 0.5, uHold); - - // Silver influence - vGreyMix = farMask * particleHold; - - /* ---------------- Subtle shimmer for near points during hold ---------------- */ - float shimmer = sin(uTime * 1.8 + pos.x * 3.0 + pos.y * 2.0) * 0.04; - float shimmerMask = (1.0 - farMask) * particleHold; - - /* ---------------- Color → colormap ---------------- */ - float signal = sin(pos.x + pos.y + pos.z + uTime * 0.3); - float mag = clamp((signal + 1.0) * 0.5 + shimmer * shimmerMask, 0.0, 0.996); - vColor = texture(cmap, vec2(mag, 0.5)).rgb; - - /* ---------------- Alpha polish ---------------- */ - // Silver points slightly more transparent - vAlpha = mix(0.85, 0.55, pow(vGreyMix, 1.2)); - - /* ---------------- Transform ---------------- */ - vec4 mv = viewMatrix * modelMatrix * vec4(pos, 1.0); - gl_Position = projectionMatrix * mv; - - /* ---------------- Point size (nonlinear shrink) ---------------- */ - float shrink = pow(vGreyMix, 1.4); - float sizeScale = mix(1.0, 0.4, shrink); - float size = (uSize * sizeScale) / -mv.z; - gl_PointSize = clamp(size, 1.5, 20.0); -} \ No newline at end of file + // Linearly interpolate between the three shapes using the mix uniforms + vec3 pos = mix(aSpherePosition, aCubePosition, uCubeMix); + pos = mix(pos, aPlanePosition, uPlaneMix); + + // Add a slight sine wave animation to make it more dynamic + // pos.y += sin(pos.x * 50.0 + uTime) * 0.005; + // pos.x += cos(pos.y * 50.0 + uTime) * 0.005; + + float minBrightness = 0.2; + float maxBrightness = 0.96; + + float r = sin(pos.z + (uTime * 0.2 ) ) ; + float g = cos(pos.y + (uTime * 0.3 ) ); + float b = cos(pos.x+pos.y + (uTime * 0.5)); + + vColor = vec3(r, g, b); + float mag = min(((sin(r+g+b) + 1.) /2.), 0.996) ; + vec4 sampled = texture(cmap, vec2(mag, 0.5)); + vColor = sampled.rgb; + // Calculate luminance (perceived brightness) + + vec4 modelPosition = modelMatrix * vec4(pos, 1.0); + vec4 viewPosition = viewMatrix * modelPosition; + vec4 projectedPosition = projectionMatrix * viewPosition; + + gl_Position = projectedPosition; + + // Make points smaller as they are further away (perspective) + gl_PointSize = (15.0 / -viewPosition.z); +} +