# CharacterRenderer Guide

`CharacterRenderer` is a core component of the Vibe Starter 3D library that provides unified rendering and animation control for 3D character models in VRM and GLTF/GLB formats.

## Overview

`CharacterRenderer` features the following capabilities:

- **Unified Rendering**: Automatically detects VRM and GLTF/GLB file formats and selects the appropriate renderer
- **Animation Management**: Simplifies complex animation state management and transitions
- **Flexible Configuration**: Provides various options including character height, animation loops, completion callbacks, etc.
- **Type Safety**: Complete TypeScript support for type safety

# Vibe Starter 3D Environment

A React-based procedural terrain generation system for creating interactive 3D environments with realistic texturing and physics.

## Features

- Procedural terrain generation with customizable parameters
- Realistic texture splatting based on height and slope
- Physics integration with React Three Fiber and Rapier
- Customizable terrain characteristics (roughness, detail, size)
- Seed-based generation for reproducible results
- Utility functions for positioning objects on terrain

## Installation

```bash
npm install vibe-starter-3d-environment
```

## Usage

### Basic Terrain

```jsx
import { Terrain } from 'vibe-starter-3d-environment';

function World() {
  return <Terrain width={100} depth={100} maxHeight={10} seed="my-world-seed" />;
}
```

### Positioning Objects on Terrain

```jsx
import { Terrain } from 'vibe-starter-3d-environment';
import { calculatePositionOnTerrain } from 'vibe-starter-3d-environment';
import { useRef, useState } from 'react';

function WorldWithObjects() {
  const [terrainData, setTerrainData] = useState(null);

  // Position an object on the terrain
  const treePosition = calculatePositionOnTerrain(
    terrainData,
    [10, 20], // x, z coordinates
    0.5, // height offset
  );

  return (
    <>
      <Terrain width={100} depth={100} maxHeight={10} seed="my-world-seed" onTerrainDataReady={setTerrainData} />

      {terrainData && <mesh position={treePosition}>{/* Tree model */}</mesh>}
    </>
  );
}
```

## Components

The library provides various components for creating immersive 3D environments:

### Terrain Components

The `terrain` module provides procedural terrain generation with realistic texturing:

```jsx
import { Terrain } from 'vibe-starter-3d-environment';

// Basic usage
<Terrain width={100} depth={100} maxHeight={10} seed="my-seed" />

// Advanced usage with texture splatting
<Terrain
  width={200}
  depth={200}
  maxHeight={10}
  splatting={{
    textures: [
      {
        materialProps: {
          map: '/path/to/grass.jpg',
          normalMap: '/path/to/grass_normal.jpg',
          roughness: 0.8,
          metalness: 0.1,
          normalScale: new Vector2(1.5, 1.5)
        },
        repeat: 2,
        heightRange: [0.6, 1.0],  // Top 40% for grass
        slopeRange: [0, Math.PI / 6],  // Flatter areas
        blendRange: 0.1
      },
      {
        materialProps: {
          map: '/path/to/rock.jpg',
          normalMap: '/path/to/rock_normal.jpg',
          roughness: 0.9,
          metalness: 0.05
        },
        repeat: 3,
        heightRange: [0.2, 1.0],
        slopeRange: [Math.PI / 6, Math.PI / 2],  // Steeper areas
        blendRange: 0.2
      }
    ],
    mode: 'both',
    blendingStrength: 1.2
  }}
  onTerrainDataReady={handleTerrainData}
/>
```

#### Terrain Properties

| Property           | Type     | Default     | Description                                                                                       |
| ------------------ | -------- | ----------- | ------------------------------------------------------------------------------------------------- |
| width              | number   | 100         | The physical width of the terrain in world units                                                  |
| depth              | number   | 100         | The physical depth of the terrain in world units                                                  |
| maxHeight          | number   | 10          | Maximum terrain peak height, controls the vertical scale of the landscape                         |
| widthSegments      | number   | 256         | Number of segments along the width axis, higher values create more detailed terrain meshes        |
| depthSegments      | number   | 256         | Number of segments along the depth axis, higher values create more detailed terrain meshes        |
| seed               | string   | 'default11' | Seed string for procedural generation, using the same seed creates identical terrain              |
| roughness          | number   | 0.5         | Controls how rough/jagged the terrain is, higher values create more rugged terrain                |
| detail             | number   | 4           | Level of fine detail added to the terrain, higher values add more small bumps and variations      |
| friction           | number   | 1           | Physics friction value, affects how objects slide on the terrain surface                          |
| restitution        | number   | 0           | Physics bounciness, determines how much objects bounce when colliding with the terrain            |
| color              | string   | '#6b8e23'   | Base color of the terrain (when not using texture splatting)                                      |
| flatThreshold      | number   | 0.2         | Controls how much of the terrain becomes flat areas, higher values create more flat areas         |
| splatting          | object   | -           | Configuration for texture splatting including texture paths, blend modes, and height/slope ranges |
| onTerrainDataReady | function | -           | Callback function that receives terrain data including height functions and min/max height values |
| onMeshCreated      | function | -           | Callback function that receives the generated terrain mesh for custom manipulations               |
| debug              | boolean  | false       | Enables debug logging for development and troubleshooting                                         |

#### Texture Splatting Options

Texture splatting allows you to apply different textures to terrain based on height and slope:

| Property          | Type   | Default | Description                                                                   |
| ----------------- | ------ | ------- | ----------------------------------------------------------------------------- |
| textures          | array  | []      | Array of texture configurations (SplattingTexture objects)                    |
| mode              | string | 'both'  | Splatting mode: 'height', 'slope', or 'both'                                  |
| defaultBlendRange | number | 0.1     | Default blend radius between different textures                               |
| blendingStrength  | number | 1       | Controls texture blending strength (higher values create sharper transitions) |
| terrainMinHeight  | number | auto    | Minimum height of terrain for height normalization                            |
| terrainMaxHeight  | number | auto    | Maximum height of terrain for height normalization                            |
| materialParams    | object | {}      | Additional material parameters applied to the final material                  |

#### SplattingTexture Properties

Each splatting texture defines where and how a texture should be applied on the terrain:

| Property         | Type            | Default       | Description                                                   |
| ---------------- | --------------- | ------------- | ------------------------------------------------------------- |
| materialProps    | MaterialProps   | required      | Material properties including texture maps and PBR values     |
| repeat           | number          | 1             | Texture repeat factor (higher values create smaller textures) |
| heightRange      | [number,number] | [0,1]         | Height range [min,max] where texture is applied (normalized)  |
| slopeRange       | [number,number] | [0,Math.PI/2] | Slope range [min,max] where texture is applied (in radians)   |
| heightBlendRange | number          | 0.1           | Controls the height-based blending transition width           |
| slopeBlendRange  | number          | 0.1           | Controls the slope-based blending transition width            |

#### MaterialProps Interface

The `MaterialProps` interface allows for comprehensive customization of terrain materials:

```jsx
// Example of using MaterialProps for terrain texture
const grassMaterial = {
  // Textures (can be paths or Texture objects)
  map: '/textures/grass_diffuse.png', // Color/diffuse map
  normalMap: '/textures/grass_normal.png', // Normal map for surface detail
  aoMap: '/textures/grass_ao.png', // Ambient occlusion map

  // PBR parameters
  roughness: 0.8, // Surface roughness (0=smooth, 1=rough)
  metalness: 0.1, // Metallic property (0=dielectric, 1=metal)
  normalScale: new Vector2(1.5, 1.5), // Normal map intensity
  aoMapIntensity: 0.8, // Intensity of AO effect
};
```

> **Important Texture Handling Improvements**: The terrain splatting system now offers more efficient texture usage with the following optimizations:
>
> 1. **ARM Map Integration**: Instead of using separate `aoMap`, `roughnessMap`, and `metalnessMap` (which would consume 3 texture units), you can now use a single `armMap` that combines all three channels (R=Ambient Occlusion, G=Roughness, B=Metalness).
> 2. **Displacement Map Exemption**: `displacementMap` is now processed separately and doesn't count against the WebGL texture unit constraints for the splatting system.
> 3. **Optimal Texture Configuration**: A recommended texture configuration would be:
>
> ```jsx
> materialProps: {
>   map: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK, // Diffuse/color
>   normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_NORMAL, // Surface detail
>   armMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_ARM, // Combined AO+Roughness+Metalness
>   displacementMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_DISPLACEMENT, // Height displacement
>   roughness: 0.8, // Base value multiplied by roughness from armMap
>   metalness: 0.1, // Base value multiplied by metalness from armMap
> }
> ```
>
> This configuration provides complete PBR rendering while efficiently managing texture units. You can still use individual maps (`aoMap`, `roughnessMap`, `metalnessMap`) but using the combined `armMap` is more efficient.

The following texture maps and properties are supported:

| Property           | Type              | Description                                          |
| ------------------ | ----------------- | ---------------------------------------------------- |
| map                | string \| Texture | Color/diffuse map                                    |
| normalMap          | string \| Texture | Normal map for surface detail                        |
| roughnessMap       | string \| Texture | Surface roughness map                                |
| metalnessMap       | string \| Texture | Surface metalness map                                |
| emissiveMap        | string \| Texture | Emissive/glow map                                    |
| bumpMap            | string \| Texture | Bump map for simpler normal mapping                  |
| displacementMap    | string \| Texture | Height/displacement map for surface displacement     |
| aoMap              | string \| Texture | Ambient occlusion map                                |
| armMap             | string \| Texture | Combined Ambient Occlusion, Roughness, Metalness map |
| envMap             | string \| Texture | Environment map for reflections                      |
| lightMap           | string \| Texture | Light map for baked lighting                         |
| alphaMap           | string \| Texture | Alpha map for transparency                           |
| color              | string \| Color   | Base color (hex string or Three.js Color)            |
| roughness          | number            | Surface roughness value (0-1)                        |
| metalness          | number            | Surface metalness value (0-1)                        |
| emissive           | string \| Color   | Emissive color                                       |
| emissiveIntensity  | number            | Intensity of emissive effect                         |
| lightMapIntensity  | number            | Intensity of light map                               |
| aoMapIntensity     | number            | Intensity of ambient occlusion map                   |
| bumpScale          | number            | Scale of bump map effect                             |
| normalMapType      | NormalMapTypes    | Type of normal map (ObjectSpace or TangentSpace)     |
| normalScale        | Vector2           | Normal map intensity                                 |
| displacementScale  | number            | Scale factor for displacement                        |
| displacementBias   | number            | Bias for displacement                                |
| envMapRotation     | Euler             | Rotation of environment map                          |
| envMapIntensity    | number            | Intensity of environment map                         |
| wireframe          | boolean           | Enable wireframe rendering                           |
| wireframeLinewidth | number            | Width of wireframe lines                             |
| flatShading        | boolean           | Enable flat shading vs smooth shading                |
| transparent        | boolean           | Enable transparency                                  |
| opacity            | number            | Opacity value when transparent is true               |
| fog                | boolean           | Whether material is affected by scene fog            |

#### Advanced Texture Splatting Example

```jsx
import { Terrain } from 'vibe-starter-3d-environment';
import { Vector2 } from 'three';
import { DEFAULT_TEXTURE_PATHS } from 'vibe-starter-3d-environment';

<Terrain
  width={200}
  depth={200}
  maxHeight={10}
  widthSegments={512}
  depthSegments={512}
  seed="terrain-seed"
  roughness={0.6}
  detail={5}
  splatting={{
    textures: [
      {
        // Grass texture - Used on high regions with low slope
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS_NORMAL,
          aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS_AO, // Using diffuse as AO map
          metalness: 0.4,
          roughness: 0.6,
        },
        repeat: 2,
        heightRange: [0.1, 1.0], // Top 90% for grass
        slopeRange: [0, Math.PI / 4], // Flatter areas
        heightBlendRange: 0.1,
        slopeBlendRange: 0.1,
      },
      {
        // Rock texture - Used on all heights with high slope
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_NORMAL,
          armMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_ARM, // Using ARM texture
          roughness: 0.4,
          metalness: 0.6,
        },
        repeat: 2,
        heightRange: [0.1, 1.0], // All heights based on slope
        slopeRange: [Math.PI / 4, Math.PI], // Steeper slopes
        heightBlendRange: 0.5,
        slopeBlendRange: 0.5,
      },
      {
        // Dirt texture - Used on medium height and slope regions
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT_NORMAL,
          aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT_AO, // Using diffuse as AO map
          roughness: 0.4,
          metalness: 0.6,
        },
        repeat: 2,
        heightRange: [0.0, 0.1], // Lower 10% for dirt
        slopeRange: [Math.PI / 4, Math.PI / 2], // Medium slopes
        heightBlendRange: 0.5,
        slopeBlendRange: 0.5,
      },
      {
        // Gravel texture - Used on low regions (near water)
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL_NORMAL,
          aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL_AO, // Using diffuse as AO map
          roughness: 0.4,
          metalness: 0.6,
        },
        repeat: 2,
        heightRange: [0.0, 0.1], // Lower areas
        slopeRange: [0, Math.PI], // All slopes
        heightBlendRange: 0.1,
        slopeBlendRange: 0.1,
      },
    ],
    mode: 'both',
    blendingStrength: 1.3,
  }}
/>;
```

#### Displacement Mapping Example

Displacement maps add fine surface details to the terrain by physically adjusting vertex positions. This example demonstrates how to apply displacement maps to rock and red terrain textures:

```jsx
import { Terrain } from 'vibe-starter-3d-environment';
import { Vector2 } from 'three';
import { DEFAULT_TEXTURE_PATHS } from 'vibe-starter-3d-environment';

<Terrain
  width={200}
  depth={200}
  maxHeight={10}
  widthSegments={512} // Higher resolution is better for displacement
  depthSegments={512}
  seed="terrain-seed"
  roughness={0.6}
  detail={5}
  splatting={{
    textures: [
      {
        // Rock texture with displacement map
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_NORMAL,
          armMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_ARM,
          displacementMap: DEFAULT_TEXTURE_PATHS.TERRAIN.ROCK_DISPLACEMENT,
          displacementScale: 1.2, // Controls displacement strength
          displacementBias: 0.1, // Controls displacement offset
          roughness: 0.8,
          metalness: 0.1,
        },
        repeat: 2,
        heightRange: [0.5, 1.0], // Applied to higher elevations
        slopeRange: [Math.PI / 6, Math.PI / 2], // Medium to steep slopes
        heightBlendRange: 0.2,
        slopeBlendRange: 0.3,
      },
      {
        // Red terrain texture with displacement map
        materialProps: {
          map: DEFAULT_TEXTURE_PATHS.TERRAIN.TERRAIN_RED_01,
          normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.TERRAIN_RED_01_NORMAL,
          armMap: DEFAULT_TEXTURE_PATHS.TERRAIN.TERRAIN_RED_01_ARM,
          displacementMap: DEFAULT_TEXTURE_PATHS.TERRAIN.TERRAIN_RED_01_DISPLACEMENT,
          displacementScale: 0.8, // Lower displacement intensity
          roughness: 0.9,
          metalness: 0.05,
        },
        repeat: 3,
        heightRange: [0.0, 0.6], // Lower to mid elevations
        slopeRange: [0, Math.PI / 4], // Gentle slopes
        heightBlendRange: 0.15,
        slopeBlendRange: 0.1,
      },
    ],
    mode: 'both',
    blendingStrength: 1.0,
    // Material parameters applied to the entire terrain
    materialParams: {
      displacementScale: 1.0, // Global displacement scale
      displacementBias: 0.0, // Global displacement bias
    },
  }}
  // Receive final terrain data with displacement applied
  onTerrainDataReady={(terrainData) => {
    console.log('Final terrain height range:', terrainData.minHeight, terrainData.maxHeight);
    console.log('Height function with displacement is now available');
  }}
/>;
```

Important considerations when using displacement maps:

- Use high segment counts (widthSegments, depthSegments) to provide enough vertices for detailed displacement.
- Adjust `displacementScale` to control the intensity of the displacement effect.
- Use `displacementBias` to offset the base level of displacement (positive values push everything up).
- Apply different displacement maps and settings to each texture to create varied terrain characteristics.

#### TerrainData and Improved Displacement

The Terrain component now supports improved displacement mapping and provides more accurate terrain data:

```jsx
<Terrain
  width={200}
  depth={200}
  maxHeight={10}
  onTerrainDataReady={(terrainData) => {
    // terrainData now includes the final displaced height function
    console.log('Min height:', terrainData.minHeight);
    console.log('Max height:', terrainData.maxHeight);
    console.log('Water level:', terrainData.waterLevel);

    // Use the heightFunc to find the height at any point
    // This function now considers displacement maps
    const height = terrainData.heightFunc(10, 20); // x=10, z=20
  }}
/>
```

The `terrainData` object includes:

| Property   | Type     | Description                                                         |
| ---------- | -------- | ------------------------------------------------------------------- |
| heightFunc | function | Height function that considers displacement maps and final geometry |
| minHeight  | number   | Actual minimum height after displacement                            |
| maxHeight  | number   | Actual maximum height after displacement                            |
| waterLevel | number   | Calculated water level based on actual terrain height               |
| width      | number   | Width of the terrain                                                |
| depth      | number   | Depth of the terrain                                                |

This improved system ensures that objects placed on the terrain will align correctly with the visible surface, even when using displacement maps.

### Water Components

The `water` module provides realistic water surfaces with advanced wave simulation, dynamic reflections, and color transitions:

```jsx
import { Water } from 'vibe-starter-3d-environment';

<Water position={[0, waterY, 0]} opacity={0.5} wavesAmplitude={0.525} wavesSpeed={0.5} surfaceColor="#9bd8ff" dynamicReflections={true} />;
```

#### Water Properties

| Property           | Type                   | Default       | Description                                                     |
| ------------------ | ---------------------- | ------------- | --------------------------------------------------------------- |
| resolution         | number                 | 256           | Resolution of the water mesh, higher values create more detail  |
| position           | [number,number,number] | [0,0,0]       | 3D position of the water surface                                |
| scale              | [number,number,number] | [128,128,128] | Size of the water surface [x,y,z]                               |
| opacity            | number                 | 0.5           | Transparency level of the water (0.0 to 1.0)                    |
| wavesAmplitude     | number                 | 0.525         | Height of the waves                                             |
| wavesFrequency     | number                 | 6.7           | Frequency of the wave patterns                                  |
| wavesPersistence   | number                 | 0.3           | How waves persist across multiple octaves                       |
| wavesLacunarity    | number                 | 2.18          | How wave frequency changes across octaves                       |
| wavesIterations    | number                 | 9             | Number of noise iterations for wave detail                      |
| wavesSpeed         | number                 | 0.5           | Speed of wave animation                                         |
| troughColor        | string                 | "#3344aa"     | Color of the deepest parts of the waves                         |
| surfaceColor       | string                 | "#9bd8ff"     | Color of the water surface at rest                              |
| peakColor          | string                 | "#bbd8ff"     | Color of the wave peaks                                         |
| peakThreshold      | number                 | 0.08          | Height threshold for peak color                                 |
| peakTransition     | number                 | 0.05          | Smoothness of transition to peak color                          |
| troughThreshold    | number                 | -0.01         | Depth threshold for trough color                                |
| troughTransition   | number                 | 0.15          | Smoothness of transition to trough color                        |
| fresnelScale       | number                 | 1.5           | Strength of the fresnel effect (edge highlighting)              |
| fresnelPower       | number                 | 0.8           | Sharpness of the fresnel effect                                 |
| reflectionQuality  | number                 | 256           | Resolution of reflection cubemap                                |
| dynamicReflections | boolean                | true          | Whether reflections update over time                            |
| reflectionInterval | number                 | 1000          | Milliseconds between reflection updates when dynamic is enabled |

### Weather Components

The `weather` module provides dynamic weather effects:

```jsx
import { WeatherSystem, Rain, Fog } from 'vibe-starter-3d-environment';

// Complete weather system
<WeatherSystem type="dynamic" transitionSpeed={0.5} />

// Individual weather components
<>
  <Rain intensity={0.5} />
  <Fog density={0.2} color="#aabbcc" />
</>
```

#### WeatherSystem Properties

| Property        | Type     | Default  | Description                                                                  |
| --------------- | -------- | -------- | ---------------------------------------------------------------------------- |
| type            | string   | "static" | Weather system mode: "static" (fixed) or "dynamic" (changes over time)       |
| preset          | string   | "clear"  | Initial weather preset: "clear", "cloudy", "rain", "storm", "snow", or "fog" |
| transitionSpeed | number   | 0.3      | Speed of transitions between weather states when using dynamic type          |
| timeScale       | number   | 1.0      | Controls how quickly time passes for day/night cycles                        |
| seed            | string   | -        | Seed for weather pattern generation when using dynamic type                  |
| minDuration     | number   | 60       | Minimum duration in seconds for a weather state before transitioning         |
| maxDuration     | number   | 300      | Maximum duration in seconds for a weather state before transitioning         |
| onWeatherChange | function | -        | Callback function that receives the new weather state during transitions     |

#### Rain Properties

| Property    | Type                   | Default   | Description                                         |
| ----------- | ---------------------- | --------- | --------------------------------------------------- |
| intensity   | number                 | 0.5       | Intensity of the rain effect (0.0 to 1.0)           |
| count       | number                 | 5000      | Number of rain particles                            |
| speed       | number                 | 20        | Speed at which rain particles fall                  |
| color       | string                 | "#ffffff" | Color of the rain droplets                          |
| dropSize    | number                 | 0.1       | Size of individual rain droplets                    |
| areaSize    | number                 | 100       | Size of the area where rain particles can appear    |
| direction   | [number,number,number] | [0,-1,0]  | Direction vector for rain, default is straight down |
| splash      | boolean                | true      | Enable splash effects when rain hits surfaces       |
| sound       | boolean                | true      | Enable rain sound effects                           |
| soundVolume | number                 | 0.5       | Volume of rain sound effects when enabled           |

#### Fog Properties

| Property       | Type    | Default   | Description                                                    |
| -------------- | ------- | --------- | -------------------------------------------------------------- |
| density        | number  | 0.1       | Density of the fog effect (0.0 to 1.0)                         |
| color          | string  | "#aabbcc" | Color of the fog                                               |
| near           | number  | 1         | Distance from camera where fog begins                          |
| far            | number  | 100       | Distance from camera where fog reaches maximum density         |
| heightFalloff  | number  | 0         | Controls how fog density changes with height (0 = uniform fog) |
| animation      | boolean | false     | Enable subtle fog movement animation                           |
| animationSpeed | number  | 0.05      | Speed of fog animation when enabled                            |

### Grass Components

The `grass` module provides instanced grass rendering for realistic foliage:

```jsx
import { GrassField } from 'vibe-starter-3d-environment';

<GrassField terrainData={terrainData} density={0.8} width={100} depth={100} bladeHeight={1} bladeWidth={0.1} color="#4d8a2f" />;
```

#### GrassField Properties

| Property        | Type    | Default   | Description                                                                     |
| --------------- | ------- | --------- | ------------------------------------------------------------------------------- |
| terrainData     | object  | null      | Terrain data object containing height function for positioning grass on terrain |
| density         | number  | 0.8       | Density of grass distribution (0.0 to 1.0), higher values create thicker grass  |
| width           | number  | 100       | Width of the area to cover with grass                                           |
| depth           | number  | 100       | Depth of the area to cover with grass                                           |
| bladeHeight     | number  | 1         | Height of individual grass blades                                               |
| bladeWidth      | number  | 0.1       | Width of individual grass blades                                                |
| color           | string  | "#4d8a2f" | Base color of grass                                                             |
| sway            | boolean | true      | Enable grass swaying animation                                                  |
| swaySpeed       | number  | 0.5       | Speed of grass swaying animation when enabled                                   |
| swayIntensity   | number  | 0.1       | How much the grass blades move when swaying                                     |
| variance        | number  | 0.15      | Variance in grass blade height, width, and color                                |
| instances       | number  | 50000     | Maximum number of grass instances to render                                     |
| distribution    | string  | "uniform" | Distribution pattern: "uniform", "noise", "cluster", or "edge"                  |
| cullingDistance | number  | 50        | Distance from camera beyond which grass is not rendered                         |
| lod             | boolean | true      | Enable level-of-detail optimization for better performance                      |
| lodLevels       | number  | 3         | Number of LOD levels when LOD is enabled                                        |
| colorVariance   | number  | 0.1       | How much the grass color varies from the base color                             |
| lightResponse   | boolean | true      | Enable grass blades to respond to scene lighting                                |
| castShadows     | boolean | false     | Whether grass blades cast shadows (performance intensive)                       |
| receiveShadows  | boolean | true      | Whether grass blades receive shadows from other objects                         |
| heightMap       | object  | null      | Optional custom heightmap to control grass distribution based on texture data   |

### Stage Components

The `stage` module provides procedural 3D environment generation with maze-like structures and various objects:

```jsx
import { Stage } from 'vibe-starter-3d-environment';

<Stage seed="my-level-seed" size={256} borderOffset={5} onReady={() => console.log('Stage loaded!')} />;
```

#### Stage Properties

| Property              | Type     | Default          | Description                                                                         |
| --------------------- | -------- | ---------------- | ----------------------------------------------------------------------------------- |
| seed                  | string   | 'default'        | Seed string for procedural generation, same seed creates identical maze             |
| size                  | number   | 256              | The physical size of the stage environment in world units                           |
| borderOffset          | number   | 5                | Distance between boundary walls and exterior walls                                  |
| onReady               | function | -                | Callback function triggered when the stage is fully loaded                          |
| textures              | object   | -                | Custom texture configurations for floor and walls (diffuse and normal maps)         |
| models                | object   | -                | Custom 3D model configurations for exterior walls and different object types        |
| utilityDensity        | number   | 0.2              | Density of utility objects (dumpsters, etc.) placed in the environment (0-1 range)  |
| propDensity           | number   | 0.15             | Density of decorative props (barrels, etc.) placed in the environment (0-1 range)   |
| structuralDensity     | number   | 0.2              | Density of structural buildings placed in the environment (0-1 range)               |
| wallHeight            | number   | 3                | Height of maze walls                                                                |
| wallHeightVariation   | number   | 1                | Random variation in wall height (±this value)                                       |
| textureRepeat         | number   | 2                | Number of times textures repeat on surfaces                                         |
| gridDensity           | number   | 1.0              | Maze grid density (0-2 range, automatically constrained)                            |
| cellSize              | number   | 5                | Size of maze cells (5-20 range, automatically constrained)                          |
| wallShape             | string   | SHAPE_TYPES.CUBE | Shape of walls (CUBE, ROUNDED_CUBE, CYLINDER, CONE, CAPSULE, PYRAMID, OCTAHEDRON)   |
| roundedCubeRadius     | number   | 0.1              | Radius of rounded corners (0.05-0.5 range, for ROUNDED_CUBE shape)                  |
| roundedCubeSmoothness | number   | 4                | Smoothness of rounded corners (0.1-1.0 range, for ROUNDED_CUBE shape)               |
| modelScales           | object   | { all: 1.0 }     | Scale factors for different model types (structural, utility, obstacle, decoration) |
| scaleVariation        | object   | { all: 0.1 }     | Scale variation ranges for different model types                                    |

#### Textures Configuration

```jsx
<Stage
  textures={{
    floor: {
      diffuse: '/path/to/floor-texture.jpg',
      normal: '/path/to/floor-normal.jpg',
    },
    wall: {
      diffuse: '/path/to/wall-texture.jpg',
      normal: '/path/to/wall-normal.jpg',
    },
  }}
/>
```

Setting any texture value to `null` will prevent that texture from being rendered.

#### Models Configuration

The Stage component supports various model types for creating realistic 3D environments:

```jsx
<Stage
  models={{
    exteriorWalls: ['/path/to/wall-model-1.glb', '/path/to/wall-model-2.glb'],
    propModels: {
      structural: ['/path/to/building_01.glb', '/path/to/building_02.glb'], // Buildings and structures
      utility: ['/path/to/dumpster.glb'], // Utility objects (dumpsters, etc.)
      obstacle: ['/path/to/barrier.glb'], // Obstacles (barriers, etc.)
      decoration: ['/path/to/barrel.glb'], // Decorative objects (barrels, etc.)
    },
    // Model scale settings
    scales: {
      structural: 1.2, // Buildings slightly larger
      utility: 0.8, // Utility objects slightly smaller
      obstacle: 1.0, // Default size for obstacles
      decoration: 0.9, // Slightly smaller decorations
    },
    // Scale variation settings
    variations: {
      structural: 0.3, // Buildings vary by ±0.3 from base scale
      utility: 0.2, // Utility objects vary by ±0.2
      obstacle: 0.1, // Low variation for obstacles
      decoration: 0.15, // Moderate variation for decorations
    },
  }}
/>
```

> **IMPORTANT: You must strictly follow the structure above when passing models to the Stage component.**
>
> All model path properties and structure must be specified exactly as shown above:
>
> ```jsx
> models={{
>   // Array of exterior wall model paths
>   exteriorWalls: ['/path1.glb', '/path2.glb', ...],
>
>   // Object containing categorized model arrays
>   propModels: {
>     structural: ['/path1.glb', '/path2.glb', ...],
>     utility: ['/path1.glb', '/path2.glb', ...],
>     obstacle: ['/path1.glb', '/path2.glb', ...],
>     decoration: ['/path1.glb', '/path2.glb', ...],
>   },
>
>   // Object containing scale factors for each model type
>   scales: {
>     structural: 1.2,
>     utility: 0.8,
>     obstacle: 1.0,
>     decoration: 0.9,
>   },
>
>   // Object containing scale variations for each model type
>   variations: {
>     structural: 0.3,
>     utility: 0.2,
>     obstacle: 0.1,
>     decoration: 0.15,
>   }
> }}
> ```

Each model category has its own placement logic and density controls:

- `structural`: Buildings and larger structures, controlled by `structuralDensity`
- `utility`: Utility objects like dumpsters, placed in central plaza areas
- `obstacle`: Barrier objects, controlled by `barrierDensity`
- `decoration`: Decorative objects like barrels, controlled by `propDensity`

All models support automatic Y-position adjustment to ensure they're properly placed on the ground, using bounding box calculations.

#### Model Scale and Variation Configuration

Fine-tune the size and variation of your models with the scales and variations properties inside the models configuration:

```jsx
<Stage
  models={{
    // Model definitions...
    propModels: {
      // Model paths...
    },
    // Base scales for different model types
    scales: {
      structural: 1.2, // Buildings slightly larger
      utility: 0.8, // Utility objects slightly smaller
      obstacle: 1.0, // Default size for obstacles
      decoration: 0.9, // Slightly smaller decorations
    },
    // Variation range for each model type
    variations: {
      structural: 0.3, // Buildings vary by ±0.3 from base scale
      utility: 0.2, // Utility objects vary by ±0.2
      obstacle: 0.1, // Low variation for obstacles
      decoration: 0.15, // Moderate variation for decorations
    },
  }}
/>
```

For example, with `structural: 1.2` and `structuralVariation: 0.3`, buildings will have random scales between 0.9 and 1.5 (base scale ± variation).

#### Advanced Environment Customization

```jsx
<Stage
  seed="city_map_001"
  size={256}
  borderOffset={5}
  barrierDensity={0.05} // Fewer barriers for more open space
  propDensity={0.15} // Moderate decoration density
  structuralDensity={0.2} // 20% building density
  wallHeight={2} // Lower walls for city blocks
  wallHeightVariation={1.5} // More variation in wall heights
  wallShape={SHAPE_TYPES.CUBE}
  gridDensity={0.7} // Lower grid density for wider streets
  cellSize={5} // Smaller cells for more dense city blocks
  models={{
    propModels: {
      // NOTE: Must use string paths for all model files
      structural: ['/models/modern/building_01.glb', '/models/modern/building_02.glb'], //use raw string only!
      // Other model types...
    },
    exteriorWalls: [],
    scales: {
      structural: 1.0,
      utility: 0.9,
      obstacle: 1.0,
      decoration: 0.8,
    },
    variations: {
      structural: 0.2,
      utility: 0.1,
      obstacle: 0.1,
      decoration: 0.15,
    },
  }}
/>
```

The Stage component uses procedural generation to create maze-like environments with:

- Central plaza area
- Maze-like pathways with walls
- Strategically placed props and obstacles
- Buildings and structures with customizable density
- Exterior walls and boundaries
- Physics-based colliders for game interaction
- Automatic ground-level positioning for all models

The generation is deterministic based on the seed value, ensuring reproducible environments while allowing for easy variation by changing the seed.

## Utility Functions

### calculatePositionOnTerrain

```js
calculatePositionOnTerrain(terrain, initialPosition, offset, terrainWidth, terrainDepth);
```

Calculates the correct position for an object to be placed on the terrain surface.

| Parameter       | Type                                         | Default | Description                                        |
| --------------- | -------------------------------------------- | ------- | -------------------------------------------------- |
| terrain         | TerrainData                                  | null    | Terrain data object containing the height function |
| initialPosition | [number, number] or [number, number, number] | [0,0,0] | Initial position [x,z] or [x,y,z] in world space   |
| offset          | number                                       | 0       | Height offset from the terrain surface             |
| terrainWidth    | number                                       | 256     | Width of the terrain for coordinate mapping        |
| terrainDepth    | number                                       | 256     | Depth of the terrain for coordinate mapping        |

Returns a Vector3 object with the final position adjusted to the terrain height.

## Navigator Usage Examples

The `Navigator` component allows objects to move over terrain surfaces with automatic height adjustment.

> **Note:** When `terrainData` is not provided, Navigator will use the `heightOffset` value (default is 2.5) as the y-position. This is useful when working with environments that don't use terrain data, such as when using the Stage component.

### Moving Object Example

```jsx
import React, { useState, useRef, useEffect } from 'react';
import { Navigator } from 'vibe-starter-3d-environment';
import { Vector3, Group } from 'three';
import { useFrame } from '@react-three/fiber';
import { Sphere } from '@react-three/drei';

interface StandardMovingObjectProps {
  terrainData?: TerrainData | null;
  waterLevel: number;
  radius?: number;
  speed?: number;
  heightOffset?: number;
  color?: string;
}

const StandardMovingObject: React.FC<StandardMovingObjectProps> = ({
  terrainData,
  waterLevel,
  radius = 50,
  speed = 0.2,
  heightOffset = 0,
  color = 'red',
}) => {
  // Update instance key when terrain data changes to create a new instance
  const [instanceKey, setInstanceKey] = useState(0);
  // Current position (Vector3)
  const [currentPosition, setCurrentPosition] = useState(
    () => new Vector3(radius, waterLevel + heightOffset, 0)
  );

  // Reference to group object
  const groupRef = useRef<Group>(null);
  // Rotation angle
  const angleRef = useRef(0);

  // Reset when terrainData or other key props change
  useEffect(() => {
    // Update instance key to create new TerrainNavigator instance
    setInstanceKey((prev) => prev + 1);

    // Set initial position (starting point of circle)
    const initialX = radius;
    const initialZ = 0;
    setCurrentPosition(
      new Vector3(initialX, waterLevel + heightOffset, initialZ)
    );
  }, [terrainData, radius, waterLevel, heightOffset]);

  // Update object position along circular path every frame
  useFrame((state, delta) => {
    // Update angle
    angleRef.current += speed * delta;

    // Calculate new x, z coordinates for circular path
    const newX = Math.cos(angleRef.current) * radius;
    const newZ = Math.sin(angleRef.current) * radius;

    // Update current position (y is calculated by Navigator)
    setCurrentPosition((prev) => new Vector3(newX, prev.y, newZ));
  });

  // Handle height updates calculated by Navigator
  const handleHeightUpdate = (updatedPosition: Vector3) => {
    // Update height of current position
    setCurrentPosition(
      (prev) => new Vector3(prev.x, updatedPosition.y, prev.z)
    );
  };

  return (
    <group ref={groupRef}>
      <Navigator
        key={`terrain-navigator-${instanceKey}`}
        terrainData={terrainData}
        heightOffset={heightOffset}
        onUpdate={(position) => handleHeightUpdate(position as Vector3)}
        targetPosition={[
          currentPosition.x,
          currentPosition.y,
          currentPosition.z,
        ]}
      >
        <Sphere args={[1.5, 32, 32]} castShadow receiveShadow>
          <meshStandardMaterial color={color} roughness={0.4} metalness={0.6} />
        </Sphere>
      </Navigator>
    </group>
  );
};

export default StandardMovingObject;
```

> **Note:** This example demonstrates a circular path movement, but this is just one example of possible movement patterns. You can implement any type of movement by updating the `targetPosition` in `useFrame` or other reactive hooks. The Navigator component will automatically calculate the correct height based on the terrain data.

### Using Navigator with Stage Component

```jsx
import React, { useState, useRef } from 'react';
import { Navigator } from 'vibe-starter-3d-environment';
import { Stage } from 'vibe-starter-3d-environment';
import { Vector3, Group } from 'three';
import { useFrame } from '@react-three/fiber';
import { Sphere } from '@react-three/drei';

const MovingObjectInStage = () => {
  // Current position state
  const [currentPosition, setCurrentPosition] = useState(new Vector3(0, 5, 0));
  // Reference to track angle for circular motion
  const angleRef = useRef(0);
  // Reference to group object
  const groupRef = useRef<Group>(null);

  // Motion parameters
  const radius = 30;
  const speed = 0.3;
  const heightOffset = 7; // Fixed height when no terrain data

  // Update position every frame
  useFrame((state, delta) => {
    // Update angle
    angleRef.current += speed * delta;

    // Calculate new x, z coordinates for circular path
    const newX = Math.cos(angleRef.current) * radius;
    const newZ = Math.sin(angleRef.current) * radius;

    // Update current position (y will be managed by Navigator)
    setCurrentPosition(prev => new Vector3(newX, prev.y, newZ));
  });

  // Handle position updates from Navigator
  const handlePositionUpdate = (updatedPosition: Vector3) => {
    // Update height of current position
    setCurrentPosition(prev => new Vector3(prev.x, updatedPosition.y, prev.z));
  };

  return (
    <>
      {/* Stage component creates a non-terrain environment */}
      <Stage seed="my-stage" size={200} />

      {/* Navigator without terrainData uses heightOffset for constant height */}
      <group ref={groupRef}>
        <Navigator
          onUpdate={handlePositionUpdate}
          targetPosition={[currentPosition.x, currentPosition.y, currentPosition.z]}
          heightOffset={heightOffset}
        >
          <Sphere args={[1.5, 32, 32]} castShadow>
            <meshStandardMaterial color="purple" roughness={0.4} metalness={0.6} />
          </Sphere>
        </Navigator>
      </group>
    </>
  );
};
```

> **Note:** The circular movement pattern shown in these examples is just for demonstration. You can implement any movement pattern you want by updating the target position coordinates. The Navigator component takes care of calculating the proper height based on terrain or using a fixed height offset.

```

### Important Notes:

- The `terrainData` object must include a `heightFunc` function that calculates terrain height at given X,Z coordinates
- For proper terrain navigation, update the `initialPosition` prop when you want to move the object
- The `TerrainNavigator` only handles Y-axis (height) positioning; X and Z movement should be controlled externally
- For moving objects, update the initialPosition prop with new X,Z coordinates each frame or as needed
- The `updatePosition` callback provides the fully calculated position including height for external use

////ULTRA CRITICALLY IMPORTANT!!!!: Use vibe-starter-3d-environment instead of creating your own.
//package to use them. Keep in mind that vibe-starter-3d-environment is already installed in the root of the project.

//ULTRA CRITICALLY IMPORTANT
// 1. Install the package:
// npm install vibe-starter-3d-environment
// 2. Import the necessary components:
import { Suspense, useEffect, useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';
import { HDRSkybox } from '../HDRSkyBox';
import { DirectionalLight, PCFShadowMap } from 'three';
import { useThree } from '@react-three/fiber';
import { EffectComposer, DepthOfField } from '@react-three/postprocessing';


//ULTRA IMPORTANT!!!!! : you must install vibe-starter-3d-environment in the root of the project
//ULTRA IMPORTANT!!!!! : you must import vibe-starter-3d-environment

////ULTRA CRITICALLY IMPORTANT!!!!: Never create components such as terrain, water, fog, grassfields, etc.
//package to use them. Keep in mind that vibe-starter-3d-environment is already installed in the root of the project.
import {
  Terrain,
  GrassField,
  Water,
  AnimatedFog,
  terrainUtil,
  TerrainSplattingOptions,
  DEFAULT_TEXTURE_PATHS,
} from 'vibe-starter-3d-environment';

////ULTRA CRITICALLY IMPORTANT!!!!: Use vibe-starter-3d-environment instead of creating your own.
// package to use them. Keep in mind that vibe-starter-3d-environment is already installed in the root of the project.
// Use the components from the vibe-starter-3d-environment package — such as Terrain, GrassField, Water, AnimatedFog, and stage — as needed.

interface TerrainData {
  heightFunc: (x: number, z: number) => number;
  minHeight: number;
  maxHeight: number;
  waterLevel: number;
  width?: number;
  depth?: number;
}

interface ObjectOnTerrainProps {
  position: [number, number, number];
  color?: string;
}

//Global terrain data reference
export const terrainDataRef = { current: null as any };

export const Experience = () => {
const [terrainData, setTerrainData] = useState<TerrainData | null>(null);
const [playerPosition, setPlayerPosition] = useState<[number, number, number]>([0, 100, 0]);
const [waterLevel, setWaterLevel] = useState(0);

  const initialPosition = useMemo(() => {
    if (terrainData) {
      const pos = terrainUtil.calculatePositionOnTerrain(
        terrainData,
        [0, 0],
        //ULTRA CRITICAL IMPORTANT!!
        // minimum initial offset is 20
        20
      );
      console.log('Initial player position:', pos);
      return pos;
    }
    return [0, 0, 0];
  }, []);

  // ULTRA CRITICAL IMPORTANT!! NEVER use useEffect when setting the character's position in this section.
  // If you don't use useLayoutEffect, the position will not be set correctly. I repeat, when calling setPlayerPosition(initialPosition);
  // use useLayoutEffect, NOT useEffect.
  useLayoutEffect(() => {
    setPlayerPosition(initialPosition);
  }, [initialPosition]);
  ////

  const terrainWidth = 256;
  const terrainDepth = 256;

  const directionalLightRef = useRef<DirectionalLight>(null);

  interface TerrainSplattingOptions {
    // Array of textures to use for splatting (max 4 recommended)
    // The various textures in your Assets.json can be used here to colorfully decorate your terrain
    textures: {
      materialProps: {
        map: string;                    // Main texture path
        normalMap?: string;             // Normal map path for surface detail
        aoMap?: string;                 // Ambient occlusion map
        armMap?: string;                // Combined roughness/metalness map
        displacementMap?: string;       // Height displacement map
        displacementScale?: number;     // Intensity of displacement effect
        displacementBias?: number;      // Base offset for displacement
        roughness?: number;             // Surface roughness (0-1)
        metalness?: number;             // Surface metalness (0-1)
      };
      repeat: number;                   // Texture repeat factor
      heightRange: [number, number];    // Height range [min,max] where texture is applied
      slopeRange: [number, number];     // Slope range [min,max] where texture is applied
      heightBlendRange: number;         // Height-based blending transition width
      slopeBlendRange: number;          // Slope-based blending transition width
    }[];

    // Splatting mode (height-based, slope-based, or both)
    mode?: 'height' | 'slope' | 'both';

    // Default blending radius between different textures
    defaultBlendRange?: number;

    // Controls texture blending strength (higher values create sharper transitions)
    blendingStrength?: number;

    // DirectionalLight object for terrain lighting
    directionalLight?: DirectionalLight;

    // Ambient light intensity (0-1)
    ambientIntensity?: number;

    // Terrain height range for normalization
    terrainMinHeight?: number;
    terrainMaxHeight?: number;
  }

  // 텍스처 스플래팅 설정
  const textureSplattingSettings = useMemo(() => {
    return {
      textures: [
        {
          // Grass texture - Used on high regions with low slope
          materialProps: {
            map: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS,
            normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS_NORMAL,
            aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS_AO,
            // 특별히 ARM 텍스처 맵을 사용하는 경우 (README에서 권장)
            // armMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRASS_ARM,
            metalness: 0.4,
            roughness: 0.6,
          },
          repeat: 2,
          heightRange: [0.1, 1.0] as [number, number], // Top 90% for grass
          slopeRange: [0, Math.PI / 4] as [number, number],
          heightBlendRange: 0.1,
          slopeBlendRange: 0.1,
        },
        {
          // Rock texture - Used on all heights with high slope
          materialProps: {
            map: DEFAULT_TEXTURE_PATHS.TERRAIN.STONE,
            normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.STONE_NORMAL,
            aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.STONE_AO,
            // 변위 맵을 사용하는 예 (README에서 권장)
            displacementMap: DEFAULT_TEXTURE_PATHS.TERRAIN.STONE_DISPLACEMENT,
            displacementScale: 0.5,
            displacementBias: 0.1,
            metalness: 0.4,
            roughness: 0.6,
          },
          repeat: 2,
          heightRange: [0.1, 1.0] as [number, number], // All heights based on slope
          slopeRange: [Math.PI / 4, Math.PI] as [number, number],
          heightBlendRange: 0.5,
          slopeBlendRange: 0.5,
        },
        {
          // Dirt texture - Used on medium height and slope regions
          materialProps: {
            map: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT,
            normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT_NORMAL,
            aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.DIRT_AO,
            metalness: 0.4,
            roughness: 0.6,
          },
          repeat: 2,
          heightRange: [0.0, 0.1] as [number, number], // Lower 10% for dirt
          slopeRange: [Math.PI / 4, Math.PI / 2] as [number, number],
          heightBlendRange: 0.5,
          slopeBlendRange: 0.5,
        },
        {
          // Gravel texture - Used on low regions (near water)
          materialProps: {
            map: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL,
            normalMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL_NORMAL,
            aoMap: DEFAULT_TEXTURE_PATHS.TERRAIN.GRAVEL_AO,
            metalness: 0.4,
            roughness: 0.6,
          },
          repeat: 2,
          heightRange: [0.0, 0.1] as [number, number],
          slopeRange: [0, Math.PI] as [number, number],
          heightBlendRange: 0.1,
          slopeBlendRange: 0.1,
        },
      ],
      mode: 'both', // 'height', 'slope', 'both' 중 하나
      directionalLight: directionalLightRef.current || undefined,
      defaultBlendRange: 0.1,
      blendingStrength: 1.2,
      terrainMinHeight: terrainData?.minHeight || 0,
      terrainMaxHeight: terrainData?.maxHeight || 20,
    } as unknown as TerrainSplattingOptions;
  }, [
    terrainData?.minHeight,
    terrainData?.maxHeight,
    directionalLightRef.current,
  ]);

  //ULTRA CRITICAL IMPORTANT!! : handleTerrainDataReady required useCallback!
  const handleTerrainDataReady = useCallback(
  (newTerrainData: TerrainData) => {
    if (terrainDataRef.current)
    {
      return;
    }
    setWaterLevel(newTerrainData.waterLevel);
    setTerrainData(newTerrainData);
    terrainDataRef.current = newTerrainData;
  });

  const { gl } = useThree();
  useEffect(() => {
    if (gl) {
      gl.shadowMap.enabled = true;
      gl.shadowMap.type = PCFShadowMap;
    }
  }, [gl]);



  // ULTRA CRITICAL IMPORTANT!!
  const [playerPosition, setPlayerPosition] = useState<[number, number, number]>([0, 100, 0]);

  ...

  const initialPosition = useMemo(() => {
  if (terrainData) {
    const pos = terrainUtil.calculatePositionOnTerrain(
      terrainData,
      [0, 0],
      //ULTRA CRITICAL IMPORTANT!!
      // minimum initial offset is 20
      20
    );
    console.log('Initial player position:', pos);
    return pos;
  }
  return [0, 0, 0];
}, [terrainData]);

useLayoutEffect(() => {
  setPlayerPosition(initialPosition);
}, [initialPosition]);

  ////ULTRA CRITICALLY IMPORTANT!!!!: Use vibe-starter-3d-environment instead of creating your own.
  // package to use them. Keep in mind that vibe-starter-3d-environment is already installed in the root of the project.
  // Use the components from the vibe-starter-3d-environment package — such as Terrain, GrassField, Water, AnimatedFog, and stage — as needed.
  return (
    <Suspense fallback={null}>
      <group>
        <AnimatedFog />
        <Terrain
          width={terrainWidth}
          depth={terrainDepth}
          maxHeight={20}
          seed={'default11'}
          roughness={0.7}
          detail={6}
          color="#ffffff"
          splatting={textureSplattingSettings}
          onTerrainDataReady={handleTerrainDataReady}
        />
        <HDRSkybox
          hdrPath="/textures/environment/test.hdr"
          directionalLightRef={directionalLightRef}
        />
        {terrainData && (
           <Water position={[0, waterLevel, 0]} scale={[128, 128, 128]} />
        )}

        <EffectComposer>
          <DepthOfField
            focusDistance={0} // Focus distance
            focalLength={0.1} // Focal length
            bokehScale={3} // Bokeh size
            height={window.innerHeight} // Rendering height
          />
        </EffectComposer>

        {terrainData && (
          <GrassField
            terrainWidth={terrainWidth}
            terrainDepth={terrainDepth}
            terrainHeightFunc={terrainData.heightFunc}
            grassDensity={0.01}
            clusterFactor={10}
            windStrength={0.1}
          />
        )}
 {/* how to Player on top of the terrain
//
// ULTRA CIRITICAL IMPORTANT!!
// You must set the terrain's position in the Player(from 'vibe-starter-3d' package) to properly set the player's position. Be sure to set the terrain's position + Y value 10
// terrain height(xz) => y
// y + 10 => player initial y position!
// When setting player positions, make sure to only set them once at initialization. It should not be set every frame.
// ULTRA CIRITICAL IMPORTANT!!
Do NOT initialize player position using useEffect alone. You MUST use this format combining useMemo with useEffect. Always adhere to this specific format for player position initialization.
This format ensures the player position is initialized exactly once. NEVER deviate from this format under any circumstances.
}, useMemo() & useEffect() -> DO THIS!!
}, useEffect() -> NEVER DO THIS!!
*/
// Set player position based on terrain height example
const initialPosition = useMemo(() => {
  if (terrainData) {
    const pos = terrainUtil.calculatePositionOnTerrain(
      terrainData,
      [0, 0],
      //ULTRA CRITICAL IMPORTANT!!
      // minimum initial offset is 20
      20
    );
    console.log('Initial player position:', pos);
    return pos;
  }
  return [0, 0, 0];
}, [terrainData]);

// ULTRA CRITICAL IMPORTANT!! NEVER use useEffect when setting the character's position in this section.
// If you don't use useLayoutEffect, the position will not be set correctly. I repeat, when calling setPlayerPosition(initialPosition);
// use useLayoutEffect, NOT useEffect.

//This codes must be implemented exactly as written, without any modifications.
useLayoutEffect(() => {
  setPlayerPosition(initialPosition);
}, [initialPosition]);
////


// ULTRA CRITICAL IMPORTANT!!

              <Player
                position={playerPosition} // <=  ULTRA CIRITICAL IMPORTANT!!
                ref={playerRef}
                initState={CharacterState.IDLE}
                controllerRef={controllerRef}
                targetHeight={targetHeight}
              />

        <if you want to add weather effect/>
      </group>
    </Suspense>
  );
};


////ULTRA CRITICALLY IMPORTANT!!!!: HOW TO BATCH OBJECTS ON TERRAIN.
// Make sure to place objects based on the bottom of their bounding box!
//  If you want to place objects on top of the terrain, place components in the following ways.
export const ObjectOnTerrain: React.FC<ObjectOnTerrainProps> = ({
  position,
  color = "red"
}) => {
  // Mesh reference
  const meshRef = useRef();

  return (
    <group>
      <mesh ref={meshRef} position={position}>
        <boxGeometry args={[1, 1, 1]} />
        <meshStandardMaterial color={color} />
      </mesh>
    </group>
  );
};

// Basic terrain position calculation
const position = terrainUtil.calculatePositionOnTerrain(
  terrainData,
  [pos.x, pos.z],
  0 // ObjectOnTerrain offset is 0!
);
<ObjectOnTerrain position={position} />

//
// Example of placing a model on terrain using its bounding box. This is the recommended placement method.
export const ModelWithBoundingBox = ({ modelPath, position, scale = [1, 1, 1] }) => {
  const modelRef = useRef();
  const [boundingBox, setBoundingBox] = useState(null);
  const [terrainPosition, setTerrainPosition] = useState(position);

  // Calculate bounding box once the model is loaded
  useEffect(() => {
    if (modelRef.current) {
      // Use Three.js Box3 to calculate the bounding box
      const box = new Box3().setFromObject(modelRef.current);
      setBoundingBox(box);

      // Calculate the center point and size of the bounding box
      const center = new Vector3();
      box.getCenter(center);
      const size = new Vector3();
      box.getSize(size);

      // Calculate corner coordinates of the bounding box (only need x, z)
      const corners = [
        [position[0] - size.x/2, position[2] - size.z/2],
        [position[0] + size.x/2, position[2] - size.z/2],
        [position[0] - size.x/2, position[2] + size.z/2],
        [position[0] + size.x/2, position[2] + size.z/2]
      ];

      // Calculate terrain height at each corner
      if (terrainData) {
        const heights = corners.map(corner => {
          const pos = terrainUtil.calculatePositionOnTerrain(
            terrainData,
            [corner[0], corner[1]],
            0
          );
          return pos[1]; // Return only the y value
        });

        // Find the highest point
        const maxHeight = Math.max(...heights);

        // Adjust model position (so the bottom of the bounding box touches the terrain)
        const offsetY = box.min.y * scale[1]; // Consider the scale
        setTerrainPosition([position[0], maxHeight - offsetY, position[2]]);
      }
    }
  }, [modelRef.current, position, scale, terrainData]);

  return (
    <group position={terrainPosition}>
      <primitive
        ref={modelRef}
        object={useGLTF(modelPath).scene}
        scale={scale}
      />
    </group>
  );
};

// Usage example
function Experience() {
  return (
    <>
      {/* Terrain */}
      <Terrain
        /* ...other terrain properties... */
      />

      {/* Model placement */}
      {terrainData && (
        <ModelWithBoundingBox
          modelPath="/path/to/building.glb"
          position={[10, 0, 15]}
          scale={[2, 2, 2]}
        />
      )}
    </>
  );
}

/////////////////////////////////////////////////////////////////////////////////////////
// CRITICAL EXPLANATION: Player Positioning System on Terrain
/////////////////////////////////////////////////////////////////////////////////////////
//
// The following pattern for setting a player's position on terrain is ABSOLUTELY CRITICAL
// and must NEVER be modified. This specific implementation ensures proper player positioning
// on the dynamically generated terrain.
//
// 1. Initialize player position state - always start with a high Y value to prevent falling through terrain
//    before the terrain is generated:
//
//    const [playerPosition, setPlayerPosition] = useState<[number, number, number]>([0, 100, 0]);
//
// 2. Calculate initial position using terrain data with useMemo for performance:
//    - This ensures the calculation only happens when terrainData changes
//    - The offset of 20 is critical to prevent player from being too close to the terrain
//    - CRITICALLY IMPORTANT: You MUST return the exact result from calculatePositionOnTerrain without any modifications
//    - NEVER add any conditional logic or transformations to the position value
//    - NEVER change any values returned by calculatePositionOnTerrain - use them exactly as provided
//
//    const initialPosition = useMemo(() => {
//      if (terrainData) {
//        const pos = terrainUtil.calculatePositionOnTerrain(
//          terrainData,
//          [0, 0],
//          20 // CRITICAL: minimum initial offset must be 20
//        );
//        return pos; // CRITICAL: return pos directly without any modifications
//      }
//      return [0, 0, 0];
//    }, [terrainData]);
//
// 3. CRITICAL: Use useLayoutEffect (NOT useEffect) to set the player position:
//    - useLayoutEffect ensures the position is updated synchronously before the browser paints
//    - This prevents visual glitches and falling through the terrain
//    - NEVER replace with useEffect as it will cause positioning issues
//
//    useLayoutEffect(() => {
//      setPlayerPosition(initialPosition);
//    }, [initialPosition]);
//
// 4. Apply the position to the Player component:
//    - This ensures the player is properly positioned on the terrain
//
//    <Player position={playerPosition}/> // CRITICALLY IMPORTANT: must use the state from step 1//
//
// 5. EXTREMELY CRITICAL: Do NOT set player position manually anywhere else:
//    - NEVER directly modify playerPosition outside of this initialization pattern
//    - ONLY set position via the position prop in Player components
//    - NEVER create alternative position calculations bypassing this system
//    - NEVER override the position after this initialization is complete
//    - NEVER try to create your own player positioning system outside this pattern
//
// 6. CRITICALLY IMPORTANT: Always use useCallback with handleTerrainDataReady:
//    - The handleTerrainDataReady function MUST ALWAYS be wrapped with useCallback
//    - Never implement this function without useCallback
//    - This is essential for proper terrain data processing and prevents unnecessary re-rendering
//    - Use exactly the following pattern:
//
//    export const terrainDataRef = { current: null as any };
//
//    const handleTerrainDataReady = useCallback(
//    (newTerrainData: TerrainData) => {
//      if (terrainDataRef.current)
//      {
//        return;
//      }
//      setWaterLevel(newTerrainData.waterLevel);
//      setTerrainData(newTerrainData);
//      terrainDataRef.current = newTerrainData;
//    });
//
// 7. CRITICALLY IMPORTANT: When setting texture splatting options:
//    - Always set texture repeat values between 1-3 for proper rendering results
//
// 8. CRITICALLY IMPORTANT: When setting Terrain component properties:
//    - Always use maxHeight ≤ 10 unless there is a specific requirement for higher values
//    - By default, keep maxHeight at 10 or lower for optimal performance and stability
//    - Only use values above 10 when explicitly requested and absolutely necessary
//    - Using maxHeight > 10 without specific requirements can cause rendering issues and terrain anomalies
//    - Example: <Terrain maxHeight={10} ... /> (CORRECT for standard usage)
//    - Example: <Terrain maxHeight={15} ... /> (Only use when specifically required)
//
// 9. EXTREMELY CRITICAL - ABSOLUTE REQUIREMENT: You MUST use the EXACT code pattern for useLayoutEffect:
//    - The following code block MUST be implemented EXACTLY as written with NO MODIFICATIONS WHATSOEVER
//    - DO NOT add ANY conditions, checks, or additional logic inside the useLayoutEffect
//    - DO NOT modify the dependency array - it MUST only contain [initialPosition]
//    - DO NOT replace useLayoutEffect with useEffect under ANY circumstances
//    - DO NOT use any alternative ways to set player position
//    - DO NOT add additional state updates or side effects within this hook
//    - THIS CODE MUST BE COPIED VERBATIM:
//
//    useLayoutEffect(() => {
//      setPlayerPosition(initialPosition);
//    }, [initialPosition]);
//
//    SERIOUS WARNING: Even the slightest deviation from this EXACT pattern will cause critical
//    player positioning failures, falling through terrain, and severe graphical glitches.
//    This is NOT a suggestion - it is an ABSOLUTE REQUIREMENT for proper functioning.
//
// WARNING: This pattern MUST ALWAYS be implemented exactly as shown.
// Deviating from this pattern in any way will cause serious positioning issues including:
// - Players falling through the terrain
// - Incorrect terrain height calculations
// - Unstable player positioning
// - Visual glitches and player jittering
//
// DO NOT attempt to "optimize" or "simplify" this pattern. It has been carefully designed
// to handle the complexities of the dynamic terrain generation system.
//
// FAILURE TO FOLLOW THESE REQUIREMENTS WILL RESULT IN SERIOUS PERFORMANCE ISSUES AND RENDERING BUGS.
/////////////////////////////////////////////////////////////////////////////////////////
```

## Basic Usage

### 1. Basic Setup

```tsx
import { CharacterRenderer, AnimationConfigMap } from 'vibe-starter-3d';
import { useRef } from 'react';

function MyCharacter() {
  // Ref to manage current animation state
  const currentAnimationRef = useRef<'idle' | 'walk' | 'run'>('idle');

  // Animation configuration map
  const animationConfigMap: AnimationConfigMap = {
    idle: {
      url: '/animations/idle.glb',
      loop: true,
    },
    walk: {
      url: '/animations/walk.glb',
      loop: true,
    },
    run: {
      url: '/animations/run.glb',
      loop: true,
    },
  };

  return <CharacterRenderer url="/models/character.glb" animationConfigMap={animationConfigMap} currentAnimationRef={currentAnimationRef} targetHeight={1.8} />;
}
```

### 2. Scene Composition and Controller Placement (Recommended Architecture)

```tsx
import { CharacterRenderer, RigidBodyPlayer, FreeViewController, AnimationConfigMap } from 'vibe-starter-3d';
import { Canvas } from '@react-three/fiber';
import { Physics } from '@react-three/rapier';
import { useRef } from 'react';

// Player component - manages player entity (physics, rendering, interaction)
function Player() {
  const currentAnimationRef = useRef<'idle' | 'walk' | 'run'>('idle');

  const animationConfigMap: AnimationConfigMap = {
    idle: { url: '/animations/idle.glb', loop: true },
    walk: { url: '/animations/walk.glb', loop: true },
    run: { url: '/animations/run.glb', loop: true },
  };

  return (
    <RigidBodyPlayer targetHeight={1.6}>
      <CharacterRenderer url="/models/player.glb" animationConfigMap={animationConfigMap} currentAnimationRef={currentAnimationRef} targetHeight={1.6} />
    </RigidBodyPlayer>
  );
}

// Scene component - manages controllers and view composition at the top level
function GameScene() {
  return (
    <Canvas>
      <Physics>
        {/* 🎯 Controllers are managed at the scene top level */}
        <FreeViewController />

        {/* Player character */}
        <Player />
      </Physics>
    </Canvas>
  );
}

// Main app component
function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <GameScene />
    </div>
  );
}
```

### 3. Various Controller Type Examples

```tsx
import { CharacterRenderer, RigidBodyPlayer, FreeViewController, FirstPersonViewController, QuarterViewController, AnimationConfigMap } from 'vibe-starter-3d';
import { Canvas } from '@react-three/fiber';
import { Physics } from '@react-three/rapier';
import { useState, useRef } from 'react';

type ControllerType = 'free' | 'firstPerson' | 'quarter';

function Player() {
  const currentAnimationRef = useRef<'idle' | 'walk' | 'run'>('idle');

  const animationConfigMap: AnimationConfigMap = {
    idle: { url: '/animations/idle.glb', loop: true },
    walk: { url: '/animations/walk.glb', loop: true },
    run: { url: '/animations/run.glb', loop: true },
  };

  return (
    <RigidBodyPlayer targetHeight={1.6}>
      <CharacterRenderer url="/models/player.glb" animationConfigMap={animationConfigMap} currentAnimationRef={currentAnimationRef} targetHeight={1.6} />
    </RigidBodyPlayer>
  );
}

function GameScene() {
  const [controllerType, setControllerType] = useState<ControllerType>('free');

  // Render controller based on type
  const renderController = () => {
    switch (controllerType) {
      case 'free':
        return <FreeViewController />;
      case 'firstPerson':
        return <FirstPersonViewController />;
      case 'quarter':
        return <QuarterViewController followCharacter={true} inputMode="keyboard" />;
      default:
        return <FreeViewController />;
    }
  };

  return (
    <>
      {/* UI controls */}
      <div style={{ position: 'absolute', top: 10, left: 10, zIndex: 100 }}>
        <button onClick={() => setControllerType('free')}>Free View</button>
        <button onClick={() => setControllerType('firstPerson')}>First Person</button>
        <button onClick={() => setControllerType('quarter')}>Quarter View</button>
      </div>

      <Canvas>
        <Physics>
          {/* 🎯 Selected controller managed at scene top level */}
          {renderController()}

          <Player />
        </Physics>
      </Canvas>
    </>
  );
}
```

## Advanced Usage

### 1. Animation Completion Callback Handling

```tsx
import { CharacterRenderer, AnimationConfigMap } from 'vibe-starter-3d';
import { useRef, useCallback } from 'react';

type CharacterState = 'idle' | 'walk' | 'attack' | 'jump';

function AdvancedCharacter() {
  const currentAnimationRef = useRef<CharacterState>('idle');

  const animationConfigMap: AnimationConfigMap = {
    idle: { url: '/animations/idle.glb', loop: true },
    walk: { url: '/animations/walk.glb', loop: true },
    attack: {
      url: '/animations/attack.glb',
      loop: false, // Play only once
      duration: 0.8, // Play for 0.8 seconds
      clampWhenFinished: true, // Stop at last frame
    },
    jump: {
      url: '/animations/jump.glb',
      loop: false,
      clampWhenFinished: false, // Return to default pose
    },
  };

  // Callback called when animation completes
  const handleAnimationComplete = useCallback((type: CharacterState) => {
    console.log(`Animation completed: ${type}`);

    switch (type) {
      case 'attack':
        // Transition to idle after attack animation completes
        currentAnimationRef.current = 'idle';
        break;
      case 'jump':
        // Transition to idle after jump animation completes
        currentAnimationRef.current = 'idle';
        break;
      default:
        break;
    }
  }, []);

  return (
    <CharacterRenderer
      url="/models/character.glb"
      animationConfigMap={animationConfigMap}
      currentAnimationRef={currentAnimationRef}
      targetHeight={1.7}
      onAnimationComplete={handleAnimationComplete}
    />
  );
}
```

### 2. Size Change Detection

```tsx
import { CharacterRenderer } from 'vibe-starter-3d';
import { useRef, useState, useCallback } from 'react';
import * as THREE from 'three';

function CharacterWithSizeDetection() {
  const currentAnimationRef = useRef<'idle'>('idle');
  const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);

  const animationConfigMap = {
    idle: { url: '/animations/idle.glb', loop: true },
  };

  // Called when character size information changes
  const handleSizeChange = useCallback((box: THREE.Box3) => {
    setBoundingBox(box);

    const size = new THREE.Vector3();
    box.getSize(size);

    console.log('Character size:', {
      width: size.x,
      height: size.y,
      depth: size.z,
    });
  }, []);

  return (
    <CharacterRenderer
      url="/models/character.glb"
      animationConfigMap={animationConfigMap}
      currentAnimationRef={currentAnimationRef}
      onSizeChange={handleSizeChange}
    />
  );
}
```

### 3. Real Game Scenario Example (freeview-based)

The following is a real game scenario from the freeview example, demonstrating proper architecture:

```tsx
import { CharacterRenderer, RigidBodyPlayer, AnimationConfigMap, FreeViewController, FollowLight } from 'vibe-starter-3d';
import { Canvas } from '@react-three/fiber';
import { Physics } from '@react-three/rapier';
import { Environment } from '@react-three/drei';
import { useRef, useMemo, useCallback } from 'react';

// Character state definition
enum CharacterState {
  IDLE = 'idle',
  WALK = 'walk',
  RUN = 'run',
  JUMP = 'jump',
  PUNCH = 'punch',
  KICK = 'kick',
  HIT = 'hit',
  DIE = 'die',
}

// Player component - manages player entity
function Player() {
  const currentStateRef = useRef<CharacterState>(CharacterState.IDLE);

  // Animation configuration map (memoized for performance optimization)
  const animationConfigMap: AnimationConfigMap = useMemo(
    () => ({
      [CharacterState.IDLE]: {
        url: '/animations/idle.glb',
        loop: true,
      },
      [CharacterState.WALK]: {
        url: '/animations/walk.glb',
        loop: true,
      },
      [CharacterState.RUN]: {
        url: '/animations/run-medium.glb',
        loop: true,
      },
      [CharacterState.JUMP]: {
        url: '/animations/jump.glb',
        loop: true,
        clampWhenFinished: true,
      },
      [CharacterState.PUNCH]: {
        url: '/animations/punch-00.glb',
        loop: false,
        duration: 0.5,
        clampWhenFinished: true,
      },
      [CharacterState.KICK]: {
        url: '/animations/kick-00.glb',
        loop: false,
        duration: 0.75,
        clampWhenFinished: true,
      },
      [CharacterState.HIT]: {
        url: '/animations/hit-to-body.glb',
        loop: false,
        clampWhenFinished: false,
      },
      [CharacterState.DIE]: {
        url: '/animations/death-backward.glb',
        loop: false,
        clampWhenFinished: true,
      },
    }),
    [],
  );

  // Animation completion handling
  const handleAnimationComplete = useCallback((type: CharacterState) => {
    console.log(`Animation completed: ${type}`);

    switch (type) {
      case CharacterState.PUNCH:
      case CharacterState.KICK:
      case CharacterState.HIT:
        // Transition to idle after action animation completes
        currentStateRef.current = CharacterState.IDLE;
        break;
      default:
        break;
    }
  }, []);

  return (
    <RigidBodyPlayer targetHeight={1.6}>
      <CharacterRenderer
        url="/models/base-model.glb"
        animationConfigMap={animationConfigMap}
        currentAnimationRef={currentStateRef}
        targetHeight={1.6}
        onAnimationComplete={handleAnimationComplete}
      />
    </RigidBodyPlayer>
  );
}

// Game scene component - manages controllers and overall scene composition
function GameScene() {
  return (
    <Canvas
      shadows
      onPointerDown={(e) => {
        (e.target as HTMLCanvasElement).requestPointerLock();
      }}
    >
      <Physics debug={true}>
        {/* 🎯 Controllers are managed at scene top level */}
        <FreeViewController />

        {/* Lighting setup */}
        <FollowLight />
        <ambientLight intensity={0.7} />

        {/* Player */}
        <Player />

        {/* Floor */}
        <mesh position={[0, -1, 0]}>
          <boxGeometry args={[100, 0.1, 100]} />
          <meshStandardMaterial color="gray" />
        </mesh>
      </Physics>
    </Canvas>
  );
}

// Main app component
function App() {
  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <GameScene />
    </div>
  );
}
```

## Architecture Best Practices

### 1. Controller Placement Principles

```tsx
// ✅ Good example: Managing controllers at scene top level
function GameScene() {
  return (
    <Canvas>
      <Physics>
        {/* Controllers are managed at scene level with views */}
        <FreeViewController />

        {/* Player handles character physics, interaction, and rendering */}
        <Player />
      </Physics>
    </Canvas>
  );
}

// ❌ Bad example: Including controller inside player
function Player() {
  return (
    <>
      {/* Controller inside player reduces reusability and flexibility */}
      <FreeViewController />
      <RigidBodyPlayer>
        <CharacterRenderer />
      </RigidBodyPlayer>
    </>
  );
}
```

### 2. Separation of Concerns

```tsx
// ✅ Good example: Clear separation of each component's role

// 1. Player component - manages character physics, interaction, and rendering
function Player() {
  return (
    <RigidBodyPlayer>
      <CharacterRenderer />
    </RigidBodyPlayer>
  );
}

// 2. Scene component - manages overall scene composition and controllers
function GameScene() {
  return (
    <Canvas>
      <Physics>
        <FreeViewController /> {/* View control */}
        <Player /> {/* Character */}
      </Physics>
    </Canvas>
  );
}

// 3. App component - manages overall app composition and input
function App() {
  return (
    <KeyboardControls>
      <GameScene />
    </KeyboardControls>
  );
}
```

### 3. Controller Switching Management

```tsx
// ✅ Good example: Managing controller switching at scene level
function GameScene() {
  const [viewMode, setViewMode] = useState<'free' | 'firstPerson' | 'quarter'>('free');

  const renderController = () => {
    switch (viewMode) {
      case 'free':
        return <FreeViewController />;
      case 'firstPerson':
        return <FirstPersonViewController />;
      case 'quarter':
        return <QuarterViewController />;
    }
  };

  return (
    <Canvas>
      <Physics>
        {/* Managing controller type at scene level */}
        {renderController()}

        {/* Player is unaffected by controller changes */}
        <Player />
      </Physics>
    </Canvas>
  );
}
```

## Props Detailed Description

### CharacterRendererProps

| Property                                      | Type                                    | Description                                                      | Default  |
| --------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------- | -------- |
| `visible`                                     | `boolean`                               | Whether the character model is displayed on screen               | `true`   |
| `url`                                         | `string`                                | Model file path or URL (required)                                | -        |
| `animationConfigMap`                          | `AnimationConfigMap`                    | Animation configuration map (required)                           | -        |
| `currentAnimationRef`                         | `RefObject<AnimationType \| undefined>` | Ref pointing to current animation type (required)                | -        |
| `targetHeight`                                | `number`                                | Target height for the character model                            | Optional |
| `disableAnimationAdjustmentToModelProportion` | `boolean`                               | Disable animation adjustment to model proportion (GLTF/GLB only) | Optional |
| `onAnimationComplete`                         | `(type: AnimationType) => void`         | Callback called when animation completes                         | Optional |
| `onSizeChange`                                | `(boundingBox: THREE.Box3) => void`     | Callback called when model size information changes              | Optional |
| `fallback`                                    | `React.ReactNode`                       | Suspense fallback for unsupported file formats                   | Optional |

### AnimationConfig

| Property            | Type      | Description                                           | Default  |
| ------------------- | --------- | ----------------------------------------------------- | -------- |
| `url`               | `string`  | Animation file path or URL (required)                 | -        |
| `loop`              | `boolean` | Whether animation should repeat                       | `true`   |
| `duration`          | `number`  | Animation duration in seconds                         | Optional |
| `clampWhenFinished` | `boolean` | Whether to stop at last frame when animation finishes | `false`  |

## Supported File Formats

### VRM Files (.vrm)

- VRM character models created in VRoid Studio, etc.
- Automatically rendered using `VrmModelRenderer`

### GLTF/GLB Files (.gltf, .glb)

- Standard 3D model formats
- Automatically rendered using `GltfModelRenderer`
- Supports `disableAnimationAdjustmentToModelProportion` option

## Best Practices

### 1. Animation State Management

```tsx
// ✅ Good example: Using useRef for state management
const currentAnimationRef = useRef<AnimationType>('idle');

// ❌ Bad example: Using useState (causes unnecessary re-renders)
const [currentAnimation, setCurrentAnimation] = useState<AnimationType>('idle');
```

### 2. Animation Configuration Optimization

```tsx
// ✅ Good example: Memoizing animation map with useMemo
const animationConfigMap = useMemo(
  () => ({
    idle: { url: '/animations/idle.glb', loop: true },
    walk: { url: '/animations/walk.glb', loop: true },
  }),
  [],
);

// ❌ Bad example: Creating new object every time
const animationConfigMap = {
  idle: { url: '/animations/idle.glb', loop: true },
  walk: { url: '/animations/walk.glb', loop: true },
};
```

### 3. Ensuring Type Safety

```tsx
// ✅ Good example: Clear type definition
type CharacterState = 'idle' | 'walk' | 'run' | 'attack';
const currentAnimationRef = useRef<CharacterState>('idle');

// ❌ Bad example: No type definition
const currentAnimationRef = useRef('idle');
```

### 4. Resource Management

```tsx
// ✅ Good example: Using relative paths or CDN
url = '/models/character.glb';
url = 'https://cdn.example.com/models/character.glb';

// ❌ Bad example: Hard-coding absolute paths
url = '/Users/username/project/public/models/character.glb';
```

## Troubleshooting

### 1. Animation Not Playing

- Check if the animation is defined in `animationConfigMap`
- Verify that `currentAnimationRef.current` value is correct
- Confirm animation file path is accurate

### 2. Model Not Displaying

- Check if `visible` property is `true`
- Verify model file path is correct
- Confirm file format is supported (.vrm, .gltf, .glb)

### 3. Performance Issues

- Memoize `animationConfigMap` with `useMemo`
- Use `useRef` to prevent unnecessary re-renders
- Consider compression or optimization for large model files

This guide enables you to effectively utilize `CharacterRenderer` for rendering 3D characters and controlling animations.
