export function serializeRuntimeSceneState(THREE, sceneObj) {
  if (!sceneObj) return null;
  
  const { threeScene, camera, data, grassMesh, voxelMesh } = sceneObj;
  
  // Start with base data and override with current values
  const state = JSON.parse(JSON.stringify(data)); // Deep clone base data
  
  // Camera
  if (camera) {
    state.camera = state.camera || {};
    state.camera.position = {
      x: camera.position.x,
      y: camera.position.y,
      z: camera.position.z
    };
    state.camera.rotation = {
      x: THREE.MathUtils.radToDeg(camera.rotation.x),
      y: THREE.MathUtils.radToDeg(camera.rotation.y),
      z: THREE.MathUtils.radToDeg(camera.rotation.z)
    };
    if (camera.isPerspectiveCamera) {
      state.camera.fov = camera.fov;
    }
  }
  
  // Lights
  const threeLights = threeScene.children.filter(c => c.isLight);
  if (state.lights && threeLights.length > 0) {
    state.lights = state.lights.map((lightData, idx) => {
      const threeLight = threeLights[idx];
      if (!threeLight) return lightData;
      
      const updated = { ...lightData };
      if (threeLight.color) {
        updated.color = '#' + threeLight.color.getHexString();
      }
      updated.intensity = threeLight.intensity;
      if (threeLight.position) {
        updated.position = {
          x: threeLight.position.x,
          y: threeLight.position.y,
          z: threeLight.position.z
        };
      }
      if (threeLight.distance !== undefined) updated.distance = threeLight.distance;
      if (threeLight.decay !== undefined) updated.decay = threeLight.decay;
      if (threeLight.castShadow !== undefined) updated.castShadow = threeLight.castShadow;
      return updated;
    });
  }
  
  // Objects
  if (state.objects) {
    let foundObjects = 0;
    threeScene.traverse((obj) => {
      // Handle both regular meshes and GLTF models (which are Groups)
      const hasObjIndex = obj.userData.objIndex !== undefined;
      const isValidObject = obj.isMesh || obj.userData.isGLTF;
      
      if (hasObjIndex && isValidObject) {
        foundObjects++;
        const objIdx = obj.userData.objIndex;
        if (state.objects[objIdx]) {
          state.objects[objIdx].position = {
            x: obj.position.x,
            y: obj.position.y,
            z: obj.position.z
          };
          state.objects[objIdx].scale = {
            x: obj.scale.x,
            y: obj.scale.y,
            z: obj.scale.z
          };
          state.objects[objIdx].rotation = {
            x: THREE.MathUtils.radToDeg(obj.rotation.x),
            y: THREE.MathUtils.radToDeg(obj.rotation.y),
            z: THREE.MathUtils.radToDeg(obj.rotation.z)
          };
          // Material properties - for GLTF, get from first child mesh
          let mat = obj.material;
          if (!mat && obj.userData.isGLTF) {
            obj.traverse((child) => {
              if (!mat && child.isMesh && child.material) {
                mat = child.material;
              }
            });
          }
          if (mat) {
            // Skip material serialization for objects that should preserve their original materials
            // (e.g., uploaded GLB/GLTF files with embedded textures)
            if (state.objects[objIdx].preserveOriginalMaterial) {
              // Don't serialize material data - let the loader use embedded materials
            } else {
            // Initialize material object if needed - handle case where material is a string type
            if (typeof state.objects[objIdx].material !== 'object' || state.objects[objIdx].material === null) {
              state.objects[objIdx].material = {};
            }
            const matData = state.objects[objIdx].material;
            
            // Basic color properties
            if (mat.color) {
              state.objects[objIdx].color = '#' + mat.color.getHexString();
              matData.color = '#' + mat.color.getHexString();
            }
            if (mat.emissive) {
              state.objects[objIdx].emissive = '#' + mat.emissive.getHexString();
              matData.emissive = '#' + mat.emissive.getHexString();
              state.objects[objIdx].emissiveIntensity = mat.emissiveIntensity;
              matData.emissiveIntensity = mat.emissiveIntensity;
            }
            
            // Numeric material properties
            if (mat.metalness !== undefined) {
              state.objects[objIdx].metalness = mat.metalness;
              matData.metalness = mat.metalness;
            }
            if (mat.roughness !== undefined) {
              state.objects[objIdx].roughness = mat.roughness;
              matData.roughness = mat.roughness;
            }
            if (mat.opacity !== undefined) {
              state.objects[objIdx].opacity = mat.opacity;
              matData.opacity = mat.opacity;
            }
            if (mat.envMapIntensity !== undefined) {
              state.objects[objIdx].envMapIntensity = mat.envMapIntensity;
              matData.envMapIntensity = mat.envMapIntensity;
            }
            
            // Additional MeshStandardMaterial properties
            if (mat.normalScale) {
              matData.normalScale = mat.normalScale.x;
            }
            if (mat.bumpScale !== undefined) {
              matData.bumpScale = mat.bumpScale;
            }
            if (mat.displacementScale !== undefined) {
              matData.displacementScale = mat.displacementScale;
            }
            if (mat.displacementBias !== undefined) {
              matData.displacementBias = mat.displacementBias;
            }
            if (mat.aoMapIntensity !== undefined) {
              matData.aoMapIntensity = mat.aoMapIntensity;
            }
            if (mat.lightMapIntensity !== undefined) {
              matData.lightMapIntensity = mat.lightMapIntensity;
            }
            
            // Boolean properties
            if (mat.wireframe !== undefined) {
              matData.wireframe = mat.wireframe;
            }
            if (mat.flatShading !== undefined) {
              matData.flatShading = mat.flatShading;
            }
            
            // Side (convert from Three.js constant to string)
            if (mat.side !== undefined) {
              const sideNames = { [THREE.FrontSide]: 'front', [THREE.BackSide]: 'back', [THREE.DoubleSide]: 'double' };
              matData.side = sideNames[mat.side] || 'front';
            }
            
            // Preserve texture paths from existing data
            const existingMat = state.objects[objIdx].material || {};
            const texProps = ['map', 'normalMap', 'roughnessMap', 'metalnessMap', 'aoMap', 
                             'emissiveMap', 'envMap', 'bumpMap', 'displacementMap', 'lightMap', 'alphaMap'];
            for (const prop of texProps) {
              if (existingMat[prop]) {
                matData[prop] = existingMat[prop];
              }
            }
            } // end else (non-preserveOriginalMaterial)
          }
        }
      }
    });
  }
  
  // Scene background and fog
  if (threeScene.background) {
    state.background = '#' + threeScene.background.getHexString();
  }
  if (threeScene.fog) {
    state.fog = state.fog || {};
    state.fog.color = '#' + threeScene.fog.color.getHexString();
    state.fog.near = threeScene.fog.near;
    state.fog.far = threeScene.fog.far;
  }
  
  // Grass field uniforms
  if (grassMesh?.material?.uniforms) {
    const u = grassMesh.material.uniforms;
    state.grassField = state.grassField || {};
    state.grassField.enabled = true;
    
    // Wind parameters
    state.grassField.windSpeed = u.uWindSpeed?.value;
    state.grassField.windStrength = u.uWindStrength?.value;
    
    // Colors
    state.grassField.colors = state.grassField.colors || {};
    if (u.uGrassColorBase?.value) state.grassField.colors.base = '#' + u.uGrassColorBase.value.getHexString();
    if (u.uGrassColorTip?.value) state.grassField.colors.tip = '#' + u.uGrassColorTip.value.getHexString();
    state.grassField.colors.variation = u.uColorVariation?.value;
    state.grassField.colors.aoStrength = u.uAOStrength?.value;
    
    // Ambient
    state.grassField.ambient = u.uAmbient?.value;
    
    // Displacement (for tunnel mode)
    if (u.uDispStrength) {
      state.grassField.displacement = state.grassField.displacement || {};
      state.grassField.displacement.strength = u.uDispStrength.value;
      state.grassField.displacement.frequency = u.uDispFrequency?.value;
      state.grassField.displacement.speed = u.uDispSpeed?.value;
      state.grassField.displacement.noiseScale = u.uDispNoiseScale?.value;
    }
  }
  
  // Voxel tunnel uniforms
  if (voxelMesh?.material?.uniforms) {
    const u = voxelMesh.material.uniforms;
    state.voxelTunnel = state.voxelTunnel || {};
    state.voxelTunnel.enabled = true;
    
    // Displacement
    state.voxelTunnel.displacement = state.voxelTunnel.displacement || {};
    state.voxelTunnel.displacement.strength = u.uDispStrength?.value;
    state.voxelTunnel.displacement.frequency = u.uDispFrequency?.value;
    state.voxelTunnel.displacement.speed = u.uDispSpeed?.value;
    state.voxelTunnel.displacement.noiseScale = u.uDispNoiseScale?.value;
    state.voxelTunnel.displacement.waveAmplitude = u.uWaveAmplitude?.value;
    state.voxelTunnel.displacement.waveFrequency = u.uWaveFrequency?.value;
    
    // Animation
    state.voxelTunnel.animation = state.voxelTunnel.animation || {};
    state.voxelTunnel.animation.pulseSpeed = u.uPulseSpeed?.value;
    state.voxelTunnel.animation.pulseStrength = u.uPulseStrength?.value;
    state.voxelTunnel.animation.rotateSpeed = u.uRotateSpeed?.value;
    state.voxelTunnel.animation.flowSpeed = u.uFlowSpeed?.value;
    state.voxelTunnel.animation.voxelRotSpeed = u.uVoxelRotSpeed?.value;
    
    // Colors
    state.voxelTunnel.colors = state.voxelTunnel.colors || {};
    if (u.uColorInner?.value) state.voxelTunnel.colors.inner = '#' + u.uColorInner.value.getHexString();
    if (u.uColorOuter?.value) state.voxelTunnel.colors.outer = '#' + u.uColorOuter.value.getHexString();
    if (u.uEmissiveInner?.value) state.voxelTunnel.colors.emissiveInner = '#' + u.uEmissiveInner.value.getHexString();
    if (u.uEmissiveOuter?.value) state.voxelTunnel.colors.emissiveOuter = '#' + u.uEmissiveOuter.value.getHexString();
    state.voxelTunnel.colors.emissiveIntensity = u.uEmissiveIntensity?.value;
    state.voxelTunnel.colors.hueShift = u.uHueShift?.value;
    state.voxelTunnel.colors.saturation = u.uSaturation?.value;
    state.voxelTunnel.colors.brightness = u.uBrightness?.value;
    
    // Material
    state.voxelTunnel.material = state.voxelTunnel.material || {};
    state.voxelTunnel.material.metalness = u.uMetalness?.value;
    state.voxelTunnel.material.roughness = u.uRoughness?.value;
    state.voxelTunnel.material.reflectivity = u.uReflectivity?.value;
    state.voxelTunnel.material.fresnelPower = u.uFresnelPower?.value;
    state.voxelTunnel.material.specularPower = u.uSpecularPower?.value;
    state.voxelTunnel.material.specularIntensity = u.uSpecularIntensity?.value;
    state.voxelTunnel.material.envMapIntensity = u.uEnvMapIntensity?.value;
    state.voxelTunnel.material.envMapRotation = u.uEnvMapRotation?.value;
    
    // Glow
    state.voxelTunnel.glow = state.voxelTunnel.glow || {};
    state.voxelTunnel.glow.strength = u.uGlowStrength?.value;
    state.voxelTunnel.glow.falloff = u.uGlowFalloff?.value;
  }
  
  // Sphere Cloner uniforms
  const sphereClonerMesh = sceneObj.sphereClonerMesh;
  if (sphereClonerMesh) {
    const u = sphereClonerMesh.material?.uniforms || {};
    state.sphereCloner = state.sphereCloner || {};
    state.sphereCloner.enabled = true;

    // Whole-system placement is independent of the camera and of the
    // procedural per-instance offset effector.
    state.sphereCloner.offsetX = sphereClonerMesh.position?.x ?? state.sphereCloner.offsetX ?? 0;
    state.sphereCloner.offsetY = sphereClonerMesh.position?.y ?? state.sphereCloner.offsetY ?? 0;
    state.sphereCloner.offsetZ = sphereClonerMesh.position?.z ?? state.sphereCloner.offsetZ ?? 0;
    state.sphereCloner.rotationX = sphereClonerMesh.rotation
      ? THREE.MathUtils.radToDeg(sphereClonerMesh.rotation.x)
      : state.sphereCloner.rotationX ?? 0;
    state.sphereCloner.rotationY = sphereClonerMesh.rotation
      ? THREE.MathUtils.radToDeg(sphereClonerMesh.rotation.y)
      : state.sphereCloner.rotationY ?? 0;
    state.sphereCloner.rotationZ = sphereClonerMesh.rotation
      ? THREE.MathUtils.radToDeg(sphereClonerMesh.rotation.z)
      : state.sphereCloner.rotationZ ?? 0;
    
    // Animation
    state.sphereCloner.baseRadius = u.uBaseRadius?.value;
    state.sphereCloner.offsetAmount = u.uOffsetAmount?.value;
    state.sphereCloner.offsetSpeed = u.uOffsetSpeed?.value;
    state.sphereCloner.rotationSpeed = u.uRotationSpeed?.value;
    state.sphereCloner.orbitSpeed = u.uOrbitSpeed?.value;
    state.sphereCloner.pulseAmount = u.uPulseAmount?.value;
    state.sphereCloner.pulseSpeed = u.uPulseSpeed?.value;
    state.sphereCloner.breathAmount = u.uBreathAmount?.value;
    state.sphereCloner.breathSpeed = u.uBreathSpeed?.value;
    state.sphereCloner.colorMixSpeed = u.uColorMixSpeed?.value;
    state.sphereCloner.emissivePulseSpeed = u.uEmissivePulseSpeed?.value;
    
    // Colors
    state.sphereCloner.colors = state.sphereCloner.colors || {};
    if (u.uBaseColor?.value) state.sphereCloner.colors.base = '#' + u.uBaseColor.value.getHexString();
    if (u.uAccentColor?.value) state.sphereCloner.colors.accent = '#' + u.uAccentColor.value.getHexString();
    if (u.uEmissiveColor?.value) state.sphereCloner.colors.emissive = '#' + u.uEmissiveColor.value.getHexString();
    
    // Material
    state.sphereCloner.material = state.sphereCloner.material || {};
    state.sphereCloner.material.metalness = u.uMetalness?.value;
    state.sphereCloner.material.roughness = u.uRoughness?.value;
    state.sphereCloner.material.envMapIntensity = u.uEnvMapIntensity?.value;
    state.sphereCloner.material.fresnelPower = u.uFresnelPower?.value;
    state.sphereCloner.material.emissiveIntensity = u.uEmissiveIntensity?.value;
    
    // Light
    state.sphereCloner.light = state.sphereCloner.light || {};
    if (u.uLightColor?.value) state.sphereCloner.light.color = '#' + u.uLightColor.value.getHexString();
    state.sphereCloner.light.intensity = u.uLightIntensity?.value;
    if (u.uLightDir?.value) {
      state.sphereCloner.light.direction = {
        x: u.uLightDir.value.x,
        y: u.uLightDir.value.y,
        z: u.uLightDir.value.z
      };
    }
    if (u.uAmbientColor?.value) state.sphereCloner.light.ambientColor = '#' + u.uAmbientColor.value.getHexString();
    state.sphereCloner.light.ambientIntensity = u.uAmbientIntensity?.value;
    
    // Effectors
    state.sphereCloner.effectors = state.sphereCloner.effectors || {};
    
    // Plain effector
    state.sphereCloner.effectors.plain = state.sphereCloner.effectors.plain || {};
    state.sphereCloner.effectors.plain.enabled = (u.uPlainEnabled?.value || 0) > 0.5;
    if (u.uPlainPosition?.value) {
      state.sphereCloner.effectors.plain.position = {
        x: u.uPlainPosition.value.x,
        y: u.uPlainPosition.value.y,
        z: u.uPlainPosition.value.z
      };
    }
    state.sphereCloner.effectors.plain.scale = u.uPlainScale?.value;
    if (u.uPlainRotation?.value) {
      state.sphereCloner.effectors.plain.rotation = {
        x: u.uPlainRotation.value.x,
        y: u.uPlainRotation.value.y,
        z: u.uPlainRotation.value.z
      };
    }
    
    // Noise effector
    state.sphereCloner.effectors.noise = state.sphereCloner.effectors.noise || {};
    state.sphereCloner.effectors.noise.enabled = (u.uNoiseEnabled?.value || 0) > 0.5;
    state.sphereCloner.effectors.noise.strength = u.uNoiseStrength?.value;
    state.sphereCloner.effectors.noise.scale = u.uNoiseScale?.value;
    state.sphereCloner.effectors.noise.speed = u.uNoiseSpeed?.value;
    state.sphereCloner.effectors.noise.octaves = u.uNoiseOctaves?.value;
    state.sphereCloner.effectors.noise.scaleEffect = u.uNoiseScaleEffect?.value;
    state.sphereCloner.effectors.noise.rotationEffect = u.uNoiseRotEffect?.value;
    
    // Delay effector
    state.sphereCloner.effectors.delay = state.sphereCloner.effectors.delay || {};
    state.sphereCloner.effectors.delay.enabled = (u.uDelayEnabled?.value || 0) > 0.5;
    state.sphereCloner.effectors.delay.amount = u.uDelayAmount?.value;
    state.sphereCloner.effectors.delay.mode = u.uDelayMode?.value;
    state.sphereCloner.effectors.delay.offset = u.uDelayOffset?.value;
    
    // Field effector
    state.sphereCloner.effectors.field = state.sphereCloner.effectors.field || {};
    state.sphereCloner.effectors.field.enabled = (u.uFieldEnabled?.value || 0) > 0.5;
    if (u.uFieldPosition?.value) {
      state.sphereCloner.effectors.field.position = {
        x: u.uFieldPosition.value.x,
        y: u.uFieldPosition.value.y,
        z: u.uFieldPosition.value.z
      };
    }
    state.sphereCloner.effectors.field.radius = u.uFieldRadius?.value;
    state.sphereCloner.effectors.field.falloff = u.uFieldFalloff?.value;
    state.sphereCloner.effectors.field.strength = u.uFieldStrength?.value;
    state.sphereCloner.effectors.field.invert = (u.uFieldInvert?.value || 0) > 0.5;
    state.sphereCloner.effectors.field.scaleEffect = u.uFieldScaleEffect?.value;
    state.sphereCloner.effectors.field.pushEffect = u.uFieldPushEffect?.value;
    
    // Formula effector
    state.sphereCloner.effectors.formula = state.sphereCloner.effectors.formula || {};
    state.sphereCloner.effectors.formula.enabled = (u.uFormulaEnabled?.value || 0) > 0.5;
    state.sphereCloner.effectors.formula.type = u.uFormulaType?.value;
    state.sphereCloner.effectors.formula.axis = u.uFormulaAxis?.value;
    state.sphereCloner.effectors.formula.frequency = u.uFormulaFrequency?.value;
    state.sphereCloner.effectors.formula.amplitude = u.uFormulaAmplitude?.value;
    state.sphereCloner.effectors.formula.speed = u.uFormulaSpeed?.value;
    state.sphereCloner.effectors.formula.scaleEffect = u.uFormulaScaleEffect?.value;
    state.sphereCloner.effectors.formula.positionEffect = u.uFormulaPosEffect?.value;
    
    // Random effector
    state.sphereCloner.effectors.random = state.sphereCloner.effectors.random || {};
    state.sphereCloner.effectors.random.enabled = (u.uRandomEnabled?.value || 0) > 0.5;
    state.sphereCloner.effectors.random.seed = u.uRandomSeed?.value;
    state.sphereCloner.effectors.random.positionStrength = u.uRandomPosStrength?.value;
    state.sphereCloner.effectors.random.scaleStrength = u.uRandomScaleStrength?.value;
    state.sphereCloner.effectors.random.scaleUniform = (u.uRandomScaleUniform?.value || 0) > 0.5;
    state.sphereCloner.effectors.random.rotationStrength = u.uRandomRotStrength?.value;
  }
  
  // Particle System uniforms
  const particleSystemMesh = sceneObj.particleSystemMesh;
  if (particleSystemMesh?.material?.uniforms) {
    const u = particleSystemMesh.material.uniforms;
    state.particleSystem = state.particleSystem || {};
    state.particleSystem.enabled = true;
    
    // Basic particle settings
    state.particleSystem.particleSize = u.uParticleSize?.value;
    state.particleSystem.lifetime = u.uLifetime?.value;
    
    // Emitter
    state.particleSystem.emitter = state.particleSystem.emitter || {};
    state.particleSystem.emitter.rate = u.uEmissionRate?.value;
    
    // Physics
    state.particleSystem.physics = state.particleSystem.physics || {};
    if (u.uGravity?.value) {
      state.particleSystem.physics.gravity = {
        x: u.uGravity.value.x,
        y: u.uGravity.value.y,
        z: u.uGravity.value.z
      };
    }
    state.particleSystem.physics.drag = u.uDrag?.value;
    state.particleSystem.physics.bounce = u.uBounce?.value;
    state.particleSystem.physics.floorY = u.uFloorY?.value;
    state.particleSystem.physics.floorEnabled = (u.uFloorEnabled?.value || 0) > 0.5;
    
    // Turbulence
    state.particleSystem.turbulence = state.particleSystem.turbulence || {};
    state.particleSystem.turbulence.enabled = (u.uTurbEnabled?.value || 0) > 0.5;
    state.particleSystem.turbulence.strength = u.uTurbStrength?.value;
    state.particleSystem.turbulence.scale = u.uTurbScale?.value;
    state.particleSystem.turbulence.speed = u.uTurbSpeed?.value;
    state.particleSystem.turbulence.octaves = u.uTurbOctaves?.value;
    
    // Vortex
    state.particleSystem.vortex = state.particleSystem.vortex || {};
    state.particleSystem.vortex.enabled = (u.uVortexEnabled?.value || 0) > 0.5;
    state.particleSystem.vortex.strength = u.uVortexStrength?.value;
    state.particleSystem.vortex.inwardForce = u.uVortexInward?.value;
    if (u.uVortexAxis?.value) {
      state.particleSystem.vortex.axis = {
        x: u.uVortexAxis.value.x,
        y: u.uVortexAxis.value.y,
        z: u.uVortexAxis.value.z
      };
    }
    if (u.uVortexCenter?.value) {
      state.particleSystem.vortex.center = {
        x: u.uVortexCenter.value.x,
        y: u.uVortexCenter.value.y,
        z: u.uVortexCenter.value.z
      };
    }
    
    // Attractor
    state.particleSystem.attractor = state.particleSystem.attractor || {};
    state.particleSystem.attractor.enabled = (u.uAttractorEnabled?.value || 0) > 0.5;
    state.particleSystem.attractor.strength = u.uAttractorStrength?.value;
    state.particleSystem.attractor.radius = u.uAttractorRadius?.value;
    state.particleSystem.attractor.falloff = u.uAttractorFalloff?.value;
    state.particleSystem.attractor.killZone = u.uAttractorKillZone?.value;
    if (u.uAttractorPos?.value) {
      state.particleSystem.attractor.position = {
        x: u.uAttractorPos.value.x,
        y: u.uAttractorPos.value.y,
        z: u.uAttractorPos.value.z
      };
    }
    
    // Curl noise (fluid)
    state.particleSystem.curl = state.particleSystem.curl || {};
    state.particleSystem.curl.enabled = (u.uCurlEnabled?.value || 0) > 0.5;
    state.particleSystem.curl.strength = u.uCurlStrength?.value;
    state.particleSystem.curl.scale = u.uCurlScale?.value;
    state.particleSystem.curl.speed = u.uCurlSpeed?.value;
    state.particleSystem.curl.evolution = u.uCurlEvolution?.value;
    
    // Cohesion/Liquid
    state.particleSystem.cohesion = state.particleSystem.cohesion || {};
    state.particleSystem.cohesion.enabled = (u.uCohesionEnabled?.value || 0) > 0.5;
    state.particleSystem.cohesion.strength = u.uCohesionStrength?.value;
    state.particleSystem.cohesion.radius = u.uCohesionRadius?.value;
    state.particleSystem.cohesion.surfaceTension = u.uSurfaceTension?.value;
    state.particleSystem.cohesion.viscosity = u.uViscosity?.value;
    
    // Wind
    state.particleSystem.wind = state.particleSystem.wind || {};
    state.particleSystem.wind.enabled = (u.uWindEnabled?.value || 0) > 0.5;
    state.particleSystem.wind.strength = u.uWindStrength?.value;
    state.particleSystem.wind.gust = u.uWindGust?.value;
    state.particleSystem.wind.gustFrequency = u.uWindGustFreq?.value;
    if (u.uWindDirection?.value) {
      state.particleSystem.wind.direction = {
        x: u.uWindDirection.value.x,
        y: u.uWindDirection.value.y,
        z: u.uWindDirection.value.z
      };
    }
    
    // Swarm
    state.particleSystem.swarm = state.particleSystem.swarm || {};
    state.particleSystem.swarm.enabled = (u.uSwarmEnabled?.value || 0) > 0.5;
    state.particleSystem.swarm.separation = u.uSwarmSeparation?.value;
    state.particleSystem.swarm.alignment = u.uSwarmAlignment?.value;
    state.particleSystem.swarm.cohesion = u.uSwarmCohesion?.value;
    state.particleSystem.swarm.perceptionRadius = u.uSwarmPerceptionRadius?.value;
    state.particleSystem.swarm.maxSpeed = u.uSwarmMaxSpeed?.value;
    state.particleSystem.swarm.maxForce = u.uSwarmMaxForce?.value;
    
    // Oscillation
    state.particleSystem.oscillation = state.particleSystem.oscillation || {};
    state.particleSystem.oscillation.enabled = (u.uOscillationEnabled?.value || 0) > 0.5;
    state.particleSystem.oscillation.amplitude = u.uOscillationAmplitude?.value;
    state.particleSystem.oscillation.frequency = u.uOscillationFrequency?.value;
    state.particleSystem.oscillation.phaseOffset = u.uOscillationPhaseOffset?.value;
    state.particleSystem.oscillation.radial = (u.uOscillationRadial?.value || 0) > 0.5;
    if (u.uOscillationAxis?.value) {
      state.particleSystem.oscillation.axis = {
        x: u.uOscillationAxis.value.x,
        y: u.uOscillationAxis.value.y,
        z: u.uOscillationAxis.value.z
      };
    }
    
    // Buoyancy
    state.particleSystem.buoyancy = state.particleSystem.buoyancy || {};
    state.particleSystem.buoyancy.enabled = (u.uBuoyancyEnabled?.value || 0) > 0.5;
    state.particleSystem.buoyancy.temperature = u.uBuoyancyTemperature?.value;
    state.particleSystem.buoyancy.density = u.uBuoyancyDensity?.value;
    state.particleSystem.buoyancy.thermalNoise = u.uBuoyancyThermalNoise?.value;
    state.particleSystem.buoyancy.coolingRate = u.uBuoyancyCoolingRate?.value;
    
    // Membrane
    state.particleSystem.membrane = state.particleSystem.membrane || {};
    state.particleSystem.membrane.enabled = (u.uMembraneEnabled?.value || 0) > 0.5;
    state.particleSystem.membrane.stiffness = u.uMembraneStiffness?.value;
    state.particleSystem.membrane.maxStretch = u.uMembraneMaxStretch?.value;
    state.particleSystem.membrane.connectionRadius = u.uMembraneConnectionRadius?.value;
    state.particleSystem.membrane.damping = u.uMembraneDamping?.value;
    
    // Size over lifetime
    state.particleSystem.sizeOverLife = state.particleSystem.sizeOverLife || {};
    state.particleSystem.sizeOverLife.start = u.uSizeStart?.value;
    state.particleSystem.sizeOverLife.mid = u.uSizeMid?.value;
    state.particleSystem.sizeOverLife.end = u.uSizeEnd?.value;
    state.particleSystem.sizeOverLife.midPoint = u.uSizeMidPoint?.value;
    
    // Opacity over lifetime
    state.particleSystem.opacityOverLife = state.particleSystem.opacityOverLife || {};
    state.particleSystem.opacityOverLife.start = u.uOpacityStart?.value;
    state.particleSystem.opacityOverLife.mid = u.uOpacityMid?.value;
    state.particleSystem.opacityOverLife.end = u.uOpacityEnd?.value;
    state.particleSystem.opacityOverLife.midPoint = u.uOpacityMidPoint?.value;
    
    // Rotation
    state.particleSystem.rotation = state.particleSystem.rotation || {};
    state.particleSystem.rotation.speed = u.uRotationSpeed?.value;
    state.particleSystem.rotation.variation = u.uRotationVariation?.value;
    
    // Colors
    state.particleSystem.colors = state.particleSystem.colors || {};
    if (u.uColorStart?.value) state.particleSystem.colors.start = '#' + u.uColorStart.value.getHexString();
    if (u.uColorMid?.value) state.particleSystem.colors.mid = '#' + u.uColorMid.value.getHexString();
    if (u.uColorEnd?.value) state.particleSystem.colors.end = '#' + u.uColorEnd.value.getHexString();
    state.particleSystem.colors.midPoint = u.uColorMidPoint?.value;
    state.particleSystem.colors.byVelocity = (u.uColorByVelocity?.value || 0) > 0.5;
    state.particleSystem.colors.velocityScale = u.uColorVelScale?.value;
    
    // Rendering
    state.particleSystem.rendering = state.particleSystem.rendering || {};
    state.particleSystem.rendering.glow = u.uGlowIntensity?.value;
  }
  
  // Displaced Sphere uniforms
  const displacedSphereMesh = sceneObj.displacedSphereMesh;
  if (displacedSphereMesh?.material?.uniforms) {
    const u = displacedSphereMesh.material.uniforms;
    const sphereData = sceneObj.data?.displacedSphere || {};
    state.displacedSphere = state.displacedSphere || {};
    state.displacedSphere.enabled = true;
    
    // Geometry (rebuild-required, stored in data not uniforms)
    state.displacedSphere.detail = sphereData.detail;
    state.displacedSphere.radius = sphereData.radius;
    
    // Displacement
    state.displacedSphere.displacement = state.displacedSphere.displacement || {};
    state.displacedSphere.displacement.strength = u.uDispStrength?.value;
    state.displacedSphere.displacement.scale = u.uDispScale?.value;
    state.displacedSphere.displacement.speed = u.uDispSpeed?.value;
    
    // Effect-specific
    state.displacedSphere.spikes = state.displacedSphere.spikes || {};
    state.displacedSphere.spikes.count = u.uSpikeCount?.value;
    state.displacedSphere.spikes.sharpness = u.uSpikeSharpness?.value;
    
    state.displacedSphere.cracks = state.displacedSphere.cracks || {};
    state.displacedSphere.cracks.width = u.uCrackWidth?.value;
    state.displacedSphere.cracks.depth = u.uCrackDepth?.value;
    
    state.displacedSphere.pulse = state.displacedSphere.pulse || {};
    state.displacedSphere.pulse.speed = u.uPulseSpeed?.value;
    state.displacedSphere.pulse.strength = u.uPulseStrength?.value;
    
    state.displacedSphere.fractal = state.displacedSphere.fractal || {};
    state.displacedSphere.fractal.power = u.uFractalPower?.value;
    state.displacedSphere.fractal.iterations = u.uFractalIterations?.value;
    
    // Colors
    state.displacedSphere.colors = state.displacedSphere.colors || {};
    if (u.uBaseColor?.value) state.displacedSphere.colors.base = '#' + u.uBaseColor.value.getHexString();
    if (u.uAccentColor?.value) state.displacedSphere.colors.accent = '#' + u.uAccentColor.value.getHexString();
    if (u.uEmissiveColor?.value) state.displacedSphere.colors.emissive = '#' + u.uEmissiveColor.value.getHexString();
    state.displacedSphere.colors.emissiveIntensity = u.uEmissiveIntensity?.value;
    
    // Material
    state.displacedSphere.material = state.displacedSphere.material || {};
    state.displacedSphere.material.metalness = u.uMetalness?.value;
    state.displacedSphere.material.roughness = u.uRoughness?.value;
    state.displacedSphere.material.envMapIntensity = u.uEnvMapIntensity?.value;
    state.displacedSphere.material.fresnelPower = u.uFresnelPower?.value;
    
    // Audio
    state.displacedSphere.audio = state.displacedSphere.audio || {};
    state.displacedSphere.audio.displaceReact = u.uAudioDisplace?.value;
    state.displacedSphere.audio.emissiveReact = u.uAudioEmissive?.value;
  }
  
  // Fractal Tree uniforms (IFS)
  const fractalTreeMesh = sceneObj.fractalTreeMesh;
  if (fractalTreeMesh) {
    const branchU = fractalTreeMesh.userData?.branchMesh?.material?.uniforms;
    const leafU = fractalTreeMesh.userData?.leafMesh?.material?.uniforms;
    const treeData = sceneObj.data?.fractalTree || {};
    state.fractalTree = state.fractalTree || {};
    state.fractalTree.enabled = true;
    
    // Structure (from data, requires rebuild)
    state.fractalTree.maxDepth = treeData.maxDepth;
    state.fractalTree.trunkHeight = treeData.trunkHeight;
    state.fractalTree.trunkRadius = treeData.trunkRadius;
    state.fractalTree.branchAngle = treeData.branchAngle;
    state.fractalTree.branchLengthRatio = treeData.branchLengthRatio;
    state.fractalTree.branchRadiusRatio = treeData.branchRadiusRatio;
    state.fractalTree.branchCount = treeData.branchCount;
    state.fractalTree.twist = treeData.twist;
    state.fractalTree.randomness = treeData.randomness;
    state.fractalTree.seed = treeData.seed;
    state.fractalTree.leafSize = treeData.leafSize;
    state.fractalTree.leafDensity = treeData.leafDensity;
    
    // Animation (from uniforms)
    state.fractalTree.animation = state.fractalTree.animation || {};
    state.fractalTree.animation.growthSpeed = branchU?.uGrowth?.value;
    state.fractalTree.animation.windStrength = branchU?.uWindStrength?.value;
    state.fractalTree.animation.windSpeed = branchU?.uWindSpeed?.value;
    state.fractalTree.animation.swayAmount = branchU?.uSwayAmount?.value;
    
    // Colors
    state.fractalTree.colors = state.fractalTree.colors || {};
    if (branchU?.uTrunkColor?.value) state.fractalTree.colors.trunk = '#' + branchU.uTrunkColor.value.getHexString();
    if (branchU?.uBranchColor?.value) state.fractalTree.colors.branch = '#' + branchU.uBranchColor.value.getHexString();
    if (leafU?.uLeafColorStart?.value) state.fractalTree.colors.leafStart = '#' + leafU.uLeafColorStart.value.getHexString();
    if (leafU?.uLeafColorEnd?.value) state.fractalTree.colors.leafEnd = '#' + leafU.uLeafColorEnd.value.getHexString();
    if (branchU?.uEmissiveColor?.value) state.fractalTree.colors.emissive = '#' + branchU.uEmissiveColor.value.getHexString();
    state.fractalTree.colors.emissiveIntensity = branchU?.uEmissiveIntensity?.value;
    
    // Material
    state.fractalTree.material = state.fractalTree.material || {};
    state.fractalTree.material.metalness = branchU?.uMetalness?.value;
    state.fractalTree.material.roughness = branchU?.uRoughness?.value;
    state.fractalTree.material.envMapIntensity = branchU?.uEnvMapIntensity?.value;
    // Preserve env map path from scene data if set
    if (sceneObj.data.fractalTree?.material?.envMap) {
      state.fractalTree.material.envMap = sceneObj.data.fractalTree.material.envMap;
    }
  }
  
  // Sierpinski Pyramid uniforms (IFS)
  const sierpinskiMesh = sceneObj.sierpinskiMesh;
  if (sierpinskiMesh) {
    const u = sierpinskiMesh.userData?.tetraMesh?.material?.uniforms;
    const sierpData = sceneObj.data?.sierpinskiPyramid || {};
    state.sierpinskiPyramid = state.sierpinskiPyramid || {};
    state.sierpinskiPyramid.enabled = true;
    
    // Structure
    state.sierpinskiPyramid.maxDepth = sierpData.maxDepth;
    state.sierpinskiPyramid.baseSize = sierpData.baseSize;
    state.sierpinskiPyramid.explosionFactor = u?.uExplosion?.value ?? sierpData.explosionFactor;
    
    // Animation
    state.sierpinskiPyramid.animation = state.sierpinskiPyramid.animation || {};
    state.sierpinskiPyramid.animation.rotationSpeed = u?.uRotationSpeed?.value;
    state.sierpinskiPyramid.animation.pulseSpeed = u?.uPulseSpeed?.value;
    state.sierpinskiPyramid.animation.pulseAmount = u?.uPulseAmount?.value;
    state.sierpinskiPyramid.animation.colorCycleSpeed = u?.uColorCycleSpeed?.value;
    
    // Colors
    state.sierpinskiPyramid.colors = state.sierpinskiPyramid.colors || {};
    if (u?.uBaseColor?.value) state.sierpinskiPyramid.colors.base = '#' + u.uBaseColor.value.getHexString();
    if (u?.uMidColor?.value) state.sierpinskiPyramid.colors.mid = '#' + u.uMidColor.value.getHexString();
    if (u?.uTipColor?.value) state.sierpinskiPyramid.colors.tip = '#' + u.uTipColor.value.getHexString();
    if (u?.uEmissiveColor?.value) state.sierpinskiPyramid.colors.emissive = '#' + u.uEmissiveColor.value.getHexString();
    state.sierpinskiPyramid.colors.emissiveIntensity = u?.uEmissiveIntensity?.value;
    
    // Material
    state.sierpinskiPyramid.material = state.sierpinskiPyramid.material || {};
    state.sierpinskiPyramid.material.metalness = u?.uMetalness?.value;
    state.sierpinskiPyramid.material.roughness = u?.uRoughness?.value;
    state.sierpinskiPyramid.material.envMapIntensity = u?.uEnvMapIntensity?.value;
    if (sceneObj.data.sierpinskiPyramid?.material?.envMap) {
      state.sierpinskiPyramid.material.envMap = sceneObj.data.sierpinskiPyramid.material.envMap;
    }
  }
  
  // Menger Sponge uniforms (IFS)
  const mengerSpongeMesh = sceneObj.mengerSpongeMesh;
  if (mengerSpongeMesh) {
    const u = mengerSpongeMesh.userData?.cubeMesh?.material?.uniforms;
    const mengerData = sceneObj.data?.mengerSponge || {};
    state.mengerSponge = state.mengerSponge || {};
    state.mengerSponge.enabled = true;
    
    // Structure
    state.mengerSponge.maxDepth = mengerData.maxDepth;
    state.mengerSponge.baseSize = mengerData.baseSize;
    state.mengerSponge.gap = mengerData.gap;
    
    // Animation
    state.mengerSponge.animation = state.mengerSponge.animation || {};
    state.mengerSponge.animation.assemblyProgress = u?.uAssembly?.value;
    state.mengerSponge.animation.rotationSpeed = u?.uRotationSpeed?.value;
    state.mengerSponge.animation.breathAmount = u?.uBreathAmount?.value;
    state.mengerSponge.animation.breathSpeed = u?.uBreathSpeed?.value;
    
    // Colors
    state.mengerSponge.colors = state.mengerSponge.colors || {};
    if (u?.uInnerColor?.value) state.mengerSponge.colors.inner = '#' + u.uInnerColor.value.getHexString();
    if (u?.uOuterColor?.value) state.mengerSponge.colors.outer = '#' + u.uOuterColor.value.getHexString();
    if (u?.uEmissiveColor?.value) state.mengerSponge.colors.emissive = '#' + u.uEmissiveColor.value.getHexString();
    state.mengerSponge.colors.emissiveIntensity = u?.uEmissiveIntensity?.value;
    
    // Material
    state.mengerSponge.material = state.mengerSponge.material || {};
    state.mengerSponge.material.metalness = u?.uMetalness?.value;
    state.mengerSponge.material.roughness = u?.uRoughness?.value;
    state.mengerSponge.material.envMapIntensity = u?.uEnvMapIntensity?.value;
    if (sceneObj.data.mengerSponge?.material?.envMap) {
      state.mengerSponge.material.envMap = sceneObj.data.mengerSponge.material.envMap;
    }
  }
  
  // Coral Growth uniforms (IFS)
  const coralGrowthMesh = sceneObj.coralGrowthMesh;
  if (coralGrowthMesh) {
    const branchU = coralGrowthMesh.userData?.branchMesh?.material?.uniforms;
    const polypU = coralGrowthMesh.userData?.polypMesh?.material?.uniforms;
    const coralData = sceneObj.data?.coralGrowth || {};
    state.coralGrowth = state.coralGrowth || {};
    state.coralGrowth.enabled = true;
    
    // Structure
    state.coralGrowth.maxDepth = coralData.maxDepth;
    state.coralGrowth.trunkLength = coralData.trunkLength;
    state.coralGrowth.trunkRadius = coralData.trunkRadius;
    state.coralGrowth.branchAngleMin = coralData.branchAngleMin;
    state.coralGrowth.branchAngleMax = coralData.branchAngleMax;
    state.coralGrowth.branchLengthRatio = coralData.branchLengthRatio;
    state.coralGrowth.branchRadiusRatio = coralData.branchRadiusRatio;
    state.coralGrowth.branchCountMin = coralData.branchCountMin;
    state.coralGrowth.branchCountMax = coralData.branchCountMax;
    state.coralGrowth.baseCount = coralData.baseCount;
    state.coralGrowth.randomness = coralData.randomness;
    state.coralGrowth.polypSize = coralData.polypSize;
    state.coralGrowth.polypDensity = coralData.polypDensity;
    state.coralGrowth.seed = coralData.seed;
    
    // Animation
    state.coralGrowth.animation = state.coralGrowth.animation || {};
    state.coralGrowth.animation.growthSpeed = branchU?.uGrowth?.value;
    state.coralGrowth.animation.swayStrength = branchU?.uSwayStrength?.value;
    state.coralGrowth.animation.swaySpeed = branchU?.uSwaySpeed?.value;
    state.coralGrowth.animation.waveSpeed = branchU?.uWaveSpeed?.value;
    state.coralGrowth.animation.pulseSpeed = polypU?.uPulseSpeed?.value;
    
    // Colors
    state.coralGrowth.colors = state.coralGrowth.colors || {};
    if (branchU?.uBaseColor?.value) state.coralGrowth.colors.base = '#' + branchU.uBaseColor.value.getHexString();
    if (branchU?.uTipColor?.value) state.coralGrowth.colors.tip = '#' + branchU.uTipColor.value.getHexString();
    if (polypU?.uPolypColor?.value) state.coralGrowth.colors.polyp = '#' + polypU.uPolypColor.value.getHexString();
    if (branchU?.uEmissiveColor?.value) state.coralGrowth.colors.emissive = '#' + branchU.uEmissiveColor.value.getHexString();
    state.coralGrowth.colors.emissiveIntensity = branchU?.uEmissiveIntensity?.value;
    
    // Material
    state.coralGrowth.material = state.coralGrowth.material || {};
    state.coralGrowth.material.metalness = branchU?.uMetalness?.value;
    state.coralGrowth.material.roughness = branchU?.uRoughness?.value;
    state.coralGrowth.material.envMapIntensity = branchU?.uEnvMapIntensity?.value;
    if (sceneObj.data.coralGrowth?.material?.envMap) {
      state.coralGrowth.material.envMap = sceneObj.data.coralGrowth.material.envMap;
    }
  }
  
  // Crystal Lattice uniforms (IFS)
  const crystalLatticeMesh = sceneObj.crystalLatticeMesh;
  if (crystalLatticeMesh) {
    const u = crystalLatticeMesh.userData?.crystalMesh?.material?.uniforms;
    const mat = crystalLatticeMesh.userData?.crystalMesh?.material;
    const connMesh = crystalLatticeMesh.userData?.connMesh;
    const crystalData = sceneObj.data?.crystalLattice || {};
    state.crystalLattice = state.crystalLattice || {};
    state.crystalLattice.enabled = true;
    
    // Transform
    state.crystalLattice.position = {
      x: crystalLatticeMesh.position.x,
      y: crystalLatticeMesh.position.y,
      z: crystalLatticeMesh.position.z
    };
    state.crystalLattice.rotation = {
      x: THREE.MathUtils.radToDeg(crystalLatticeMesh.rotation.x),
      y: THREE.MathUtils.radToDeg(crystalLatticeMesh.rotation.y),
      z: THREE.MathUtils.radToDeg(crystalLatticeMesh.rotation.z)
    };
    state.crystalLattice.scale = crystalLatticeMesh.scale.x;
    
    // Structure
    state.crystalLattice.maxDepth = crystalData.maxDepth;
    state.crystalLattice.baseSize = crystalData.baseSize;
    state.crystalLattice.scaleFalloff = crystalData.scaleFalloff;
    state.crystalLattice.connectionRadius = crystalData.connectionRadius;
    state.crystalLattice.showConnections = crystalData.showConnections;
    
    // Animation
    state.crystalLattice.animation = state.crystalLattice.animation || {};
    state.crystalLattice.animation.formationProgress = u?.uFormation?.value;
    state.crystalLattice.animation.rotationSpeed = u?.uRotationSpeed?.value;
    state.crystalLattice.animation.shimmerSpeed = u?.uShimmerSpeed?.value;
    state.crystalLattice.animation.pulseSpeed = u?.uPulseSpeed?.value;
    
    // Colors
    state.crystalLattice.colors = state.crystalLattice.colors || {};
    if (u?.uCrystalColor?.value) state.crystalLattice.colors.crystal = '#' + u.uCrystalColor.value.getHexString();
    if (u?.uCoreColor?.value) state.crystalLattice.colors.core = '#' + u.uCoreColor.value.getHexString();
    if (connMesh?.material?.color) state.crystalLattice.colors.connection = '#' + connMesh.material.color.getHexString();
    if (u?.uEmissiveColor?.value) state.crystalLattice.colors.emissive = '#' + u.uEmissiveColor.value.getHexString();
    state.crystalLattice.colors.emissiveIntensity = u?.uEmissiveIntensity?.value;
    
    // Light settings
    state.crystalLattice.light = state.crystalLattice.light || {};
    if (u?.uLightColor?.value) state.crystalLattice.light.color = '#' + u.uLightColor.value.getHexString();
    state.crystalLattice.light.intensity = u?.uLightIntensity?.value;
    if (u?.uLightDir?.value) {
      state.crystalLattice.light.direction = {
        x: u.uLightDir.value.x,
        y: u.uLightDir.value.y,
        z: u.uLightDir.value.z
      };
    }
    if (u?.uAmbientColor?.value) state.crystalLattice.light.ambientColor = '#' + u.uAmbientColor.value.getHexString();
    state.crystalLattice.light.ambientIntensity = u?.uAmbientIntensity?.value;
    state.crystalLattice.useSceneLights = crystalData.useSceneLights;
    
    // Material
    state.crystalLattice.material = state.crystalLattice.material || {};
    state.crystalLattice.material.metalness = u?.uMetalness?.value;
    state.crystalLattice.material.roughness = u?.uRoughness?.value;
    state.crystalLattice.material.opacity = u?.uOpacity?.value;
    state.crystalLattice.material.envMapIntensity = u?.uEnvMapIntensity?.value;
    state.crystalLattice.material.fresnelPower = u?.uFresnelPower?.value;
    state.crystalLattice.material.fresnelBias = u?.uFresnelBias?.value;
    state.crystalLattice.material.specularIntensity = u?.uSpecularIntensity?.value;
    if (sceneObj.data.crystalLattice?.material?.envMap) {
      state.crystalLattice.material.envMap = sceneObj.data.crystalLattice.material.envMap;
    }
    
    // Advanced material options
    if (mat) {
      state.crystalLattice.material.wireframe = mat.wireframe;
      state.crystalLattice.material.depthWrite = mat.depthWrite;
      state.crystalLattice.material.depthTest = mat.depthTest;
      if (mat.side === THREE.FrontSide) state.crystalLattice.material.side = 'front';
      else if (mat.side === THREE.BackSide) state.crystalLattice.material.side = 'back';
      else state.crystalLattice.material.side = 'double';
      if (mat.blending === THREE.AdditiveBlending) state.crystalLattice.material.blending = 'additive';
      else if (mat.blending === THREE.MultiplyBlending) state.crystalLattice.material.blending = 'multiply';
      else if (mat.blending === THREE.SubtractiveBlending) state.crystalLattice.material.blending = 'subtractive';
      else state.crystalLattice.material.blending = 'normal';
    }
  }
  
  return state;
}
