3D Model Viewer Test

✨ Now Using New Compound Component API!

This page now uses the refactored ModelViewer with Redux-like state management and compound components. Much more flexible with both props-based API (backward compatible) and compound component patterns.

This page demonstrates the 3D model viewer component built with React Three Fiber. You can orbit around the model by clicking and dragging, zoom with the scroll wheel, and pan by right-clicking and dragging.

Example 1: Props-Based API (Backward Compatible)

Simple props-based usage with auto-rotate and toon shading. Perfect for quick embedding without custom controls.

  • Auto-Rotate - Model rotates automatically
  • Toon Shading - Cel-shaded anime look
  • Default Controls - Standard control panel with common features
<ModelViewer modelPath="..." autoRotate toonShading showControls />
Loading model...

Example 2: Compound Components (Custom Layout)

Full control over layout and which controls to show using compound components. Build your own custom control panel with only the features you need.

// In a React component file (.tsx): <ModelViewer modelPath="..."> <ViewerCanvas height="500px" /> <ViewerControls> <ViewControl.AutoRotate /> <ViewControl.ToonShading /> <CameraControl.CenterModel /> <AnimationControl.SimplePlayer /> </ViewerControls> </ModelViewer>

💡 Tip: Create React wrapper components for compound layouts

Compound components must be used in .tsx files, not .astro files. See src/components/react/3d/examples/CompoundExample.tsx for examples.

Loading model...

Example 3: Minimal Embedded Viewer

Compact viewer with no controls - just the model with auto-rotation. Perfect for inline embedding in blog posts or character showcases.

<ModelViewer modelPath="..." width=400 height=400 autoRotate />
Loading model...

Note: If a model appears without textures, check that textures are properly embedded in the GLB file during export from Blender. See public/models/README.md for troubleshooting.

Example 4: Full Animation Controls

Advanced animation control with full playback UI including play/pause/stop, speed control, and loop modes. Perfect for character showcases with multiple animations.

// In a React component file (.tsx): <ModelViewer modelPath="..."> <ViewerCanvas height="600px" /> <ViewerControls> <ViewControl.ToonShading /> <CameraControl.PresetList /> <AnimationControl.Player /> </ViewerControls> </ModelViewer>
Loading model...

🎬 Animation Features:

  • Play/Pause/Stop - Full playback control
  • Speed Control - Adjust animation speed (0.1x to 2.0x)
  • Loop Modes - Once, Repeat, or Ping-Pong
  • Animation List - Click buttons to switch animations
  • Camera Presets - Switch between predefined camera angles

Example 5: Debug Mode with Helpers

Debug mode provides visualization helpers for development - bounding box, grid/axes, and light helpers. Use controlTier="debug" to show all debug controls.

<ModelViewer modelPath="..." controlTier="debug" showControls />
Loading model...

🔧 Debug Features:

  • Bounding Box - Shows model bounds for sizing
  • Grid & Axes - XYZ axis helpers and ground grid
  • Light Helpers - Visualize light positions and directions
  • Keyboard Controls - WASD camera movement

How to Use (New Compound Component API)

1. Add Your Models

Place your 3D model files in the public/models/ directory. Supported formats: FBX, GLTF, GLB

2. Import ModelViewer and Components

---
import ModelViewer, { 
  ViewerCanvas, 
  ViewerControls, 
  ViewControl,
  CameraControl,
  AnimationControl 
} from '@components/react/3d';
---

3. Props-Based API (Simple)

<ModelViewer 
  client:load
  modelPath="/models/your-model.glb"
  autoRotate={true}
  toonShading={true}
  showControls={true}
/>

4. Compound Component API (Flexible)

Important: Compound components must be used inside React (.tsx) files, not in Astro (.astro) files. Create wrapper components in src/components/react/.

// CustomViewer.tsx (React component)
import ModelViewer, { ViewerCanvas, ViewerControls, ViewControl } from '@components/react/3d';

export const CustomViewer = ({ modelPath }) => (
  <ModelViewer modelPath={modelPath}>
    <ViewerCanvas height="600px" />
    <ViewerControls title="My Controls">
      <ViewControl.AutoRotate />
      <ViewControl.ToonShading />
    </ViewerControls>
  </ModelViewer>
);

// Then in your .astro file:
---
import { CustomViewer } from '@components/react/CustomViewer';
---
<CustomViewer client:load modelPath="/models/model.glb" />

⚠️ Why this restriction?

Astro evaluates JSX during the build/SSR phase before React context exists. Compound components need access to ViewerContext, which only exists at runtime in React. Wrapping them in a React component file (.tsx) ensures they're only evaluated in the React runtime.

5. Available Compound Components

  • ViewerCanvas - The 3D canvas (required)
  • ViewerControls - Control panel wrapper
  • ViewControl.* - AutoRotate, ToonShading, Wireframe, Specular, ResetViewport
  • CameraControl.* - PresetSelector, PresetList, CenterModel, KeyboardToggle
  • AnimationControl.* - Player, SimplePlayer, AnimationList
  • DebugControl.* - BoundingBox, GridHelpers, LightHelpers, DebugMode

6. Essential Props

  • modelPath - Path to your 3D model file (required)
  • width - Canvas width (default: "100%")
  • height - Canvas height (default: "600px")
  • autoRotate - Enable auto-rotation (default: false)
  • toonShading - Enable cel-shading (default: false)
  • showControls - Show default control panel (default: true)
  • hideControls - Array of control IDs to hide

7. Controls

  • Left Click + Drag: Orbit/rotate around model
  • Right Click + Drag: Pan camera
  • Scroll Wheel: Zoom in/out
  • WASD Keys: Camera movement (when enabled)
  • Control Panel: Toggle features and animations

Benefits of the New API

  • Flexible - Mix and match controls to build custom UIs
  • Backward Compatible - Props-based API still works
  • Redux-like State - Predictable state management with useReducer
  • Compound Components - Compose your own control panels
  • Promise-based Animations - Async/await for animation control
  • Theme-aware - Automatically adapts to light/dark mode
  • TypeScript - Full type safety with discriminated unions

Next Steps

  • Add your FBX, GLTF, or GLB models to public/models/
  • Choose props-based API for quick embedding or compound components for custom layouts
  • Use compound components to build exactly the control panel you need
  • Embed the viewer in your comic pages or blog posts
  • See /3d-animation-test for animation examples

� Troubleshooting Models Without Textures

If your model appears white or gray without textures:

  1. Check texture embedding - GLB files must have textures embedded during export
  2. Verify Blender export settings:
    • Format: GLB Binary (not separate .gltf)
    • ✓ Include: Textures checked
    • ✓ Materials: Export checked
    • Use Principled BSDF with connected Image Texture nodes
  3. File size check - A character with textures should be 2-10MB (not <100KB)
  4. Try debug mode - Set controlTier="debug" to inspect materials
  5. Enable toon shading - Sometimes helps with solid-color models

See public/models/README.md for detailed troubleshooting steps.

📚 Documentation

For complete documentation, see:

  • docs/components/3D_VIEWER_REFACTOR_TDD.md - Refactor design document
  • docs/components/3D_ARCHITECTURE.md - System architecture overview
  • docs/components/BLENDER_WORKFLOW.md - Model export workflow

Using in MDX Content

To use the 3D viewer in MDX comic pages or blog posts, you need to import it at the top of your MDX file.

Simple Props-Based Usage (Recommended for MDX)

---
title: "My 3D Model Post"
# ... other frontmatter
---

import ModelViewer from '@components/react/3d';

# Check Out This 3D Model!

Here's my character model with auto-rotate and cel-shading:

<ModelViewer 
  client:load
  modelPath="/models/my-character.glb"
  height="600px"
  autoRotate={true}
  toonShading={true}
  showControls={true}
/>

Pretty cool, right?

Compound Components in MDX (Advanced)

For compound components, create a React wrapper component first:

// src/components/react/CharacterViewer.tsx
import ModelViewer, { 
  ViewerCanvas, 
  ViewerControls, 
  ViewControl,
  CameraControl,
  AnimationControl 
} from '@components/react/3d';

export const CharacterViewer = ({ modelPath }: { modelPath: string }) => (
  <ModelViewer modelPath={modelPath}>
    <ViewerCanvas height="700px" />
    <ViewerControls title="Character Controls">
      <ViewControl.AutoRotate />
      <ViewControl.ToonShading />
      <CameraControl.PresetList />
      <AnimationControl.Player />
    </ViewerControls>
  </ModelViewer>
);

Then use it in your MDX:

---
title: "Character Showcase"
---

import { CharacterViewer } from '@components/react/CharacterViewer';

# Meet My Character

<CharacterViewer 
  client:load 
  modelPath="/models/my-character.glb" 
/>

Available Props for MDX Usage

Prop Type Default Description
modelPath string required Path to GLB/GLTF/FBX model
height string "600px" Canvas height
autoRotate boolean false Auto-rotate model
autoRotateSpeed number 1.0 Rotation speed (0.1-5.0)
toonShading boolean false Enable cel-shading
showControls boolean true Show control panel
hideControls string[] [] Array of control IDs to hide

Tips for MDX Usage

  • ✅ Always use client:load directive for interactivity
  • ✅ Place models in public/models/ for easy paths
  • ✅ Use props-based API for simple embedding
  • ✅ Create wrapper components for custom compound layouts
  • ✅ Test in both light and dark mode (viewer is theme-aware)
  • ⚠️ GLB models with Blender cameras will have preset buttons automatically