dioxus-three 0.0.4

A Three.js 3D model viewer for Dioxus - supports Desktop, Web (WASM), and Mobile
Documentation
# Quick Start

Let's build a simple 3D model viewer step by step.

## 1. Basic Cube (Hello World)

The simplest example - a rotating colored cube:

```rust
use dioxus::prelude::*;
use dioxus_three::ThreeView;

fn main() {
    dioxus_desktop::launch(app);
}

fn app() -> Element {
    rsx! {
        div { style: "width: 100vw; height: 100vh;",
            ThreeView {
                auto_rotate: true,
                color: "#00ff00".to_string(),
                scale: 1.5,
            }
        }
    }
}
```

## 2. Loading Your First Model

Load a 3D model from a URL:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ModelFormat};

fn app() -> Element {
    rsx! {
        ThreeView {
            model_url: Some("https://threejs.org/examples/models/obj/male02/male02.obj".to_string()),
            format: ModelFormat::Obj,
            auto_center: true,
            auto_scale: true,
            show_grid: true,
        }
    }
}
```

## 3. Multiple Models

Display multiple models simultaneously:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ModelConfig, ModelFormat};

fn app() -> Element {
    let models = vec![
        ModelConfig::new("", ModelFormat::Cube)
            .with_position(-2.0, 0.0, 0.0)
            .with_color("#ff6b6b"),
        ModelConfig::new("https://example.com/helmet.gltf", ModelFormat::Gltf)
            .with_position(0.0, 0.0, 0.0)
            .with_scale(0.5),
        ModelConfig::new("https://example.com/duck.gltf", ModelFormat::Gltf)
            .with_position(2.0, 0.0, 0.0)
            .with_scale(0.3),
    ];
    
    rsx! {
        ThreeView {
            models: models,
            auto_rotate: true,
        }
    }
}
```

## 4. Interactive Controls (Desktop)

Add controls to transform the model:

```rust
use dioxus::prelude::*;
use dioxus_three::ThreeView;

fn app() -> Element {
    let mut scale = use_signal(|| 1.0f32);
    let mut rot_y = use_signal(|| 0.0f32);
    
    rsx! {
        div { style: "display: flex; height: 100vh;",
            // Control panel
            div { style: "width: 300px; padding: 20px; background: #333; color: white;",
                h2 { "Controls" }
                
                div { style: "margin: 10px 0;",
                    label { "Scale: {scale():.1}" }
                    input {
                        r#type: "range",
                        min: "0.1",
                        max: "3.0",
                        step: "0.1",
                        value: "{scale()}",
                        oninput: move |e| {
                            if let Ok(v) = e.value().parse::<f32>() {
                                scale.set(v);
                            }
                        }
                    }
                }
                
                div { style: "margin: 10px 0;",
                    label { "Rotation Y: {rot_y():.0}°" }
                    input {
                        r#type: "range",
                        min: "0",
                        max: "360",
                        step: "1",
                        value: "{rot_y()}",
                        oninput: move |e| {
                            if let Ok(v) = e.value().parse::<f32>() {
                                rot_y.set(v);
                            }
                        }
                    }
                }
            }
            
            // 3D View
            div { style: "flex: 1;",
                ThreeView {
                    scale: scale(),
                    rot_y: rot_y(),
                    auto_rotate: false,
                }
            }
        }
    }
}
```

## 5. Interactive Controls (Web/Mobile)

For web and mobile platforms, use a wrapper component for proper signal handling:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ModelConfig, ShaderPreset};

/// Wrapper that ensures proper signal subscriptions
#[component]
fn ThreeViewWrapper(
    models: Signal<Vec<ModelConfig>>,
    cam_x: Signal<f32>,
    cam_y: Signal<f32>,
    cam_z: Signal<f32>,
    auto_rotate: Signal<bool>,
    shader: Signal<ShaderPreset>,
) -> Element {
    // Read signals to subscribe to changes
    let model_configs = models.read().clone();
    let cx = cam_x();
    let cy = cam_y();
    let cz = cam_z();
    let ar = auto_rotate();
    let sh = shader();
    
    rsx! {
        ThreeView {
            models: model_configs,
            cam_x: cx,
            cam_y: cy,
            cam_z: cz,
            auto_rotate: ar,
            shader: sh,
        }
    }
}

fn app() -> Element {
    let models = use_signal(|| vec![ModelConfig::new("", ModelFormat::Cube)]);
    let cam_x = use_signal(|| 8.0f32);
    let cam_y = use_signal(|| 8.0f32);
    let cam_z = use_signal(|| 8.0f32);
    let auto_rotate = use_signal(|| true);
    let shader = use_signal(|| ShaderPreset::None);
    
    rsx! {
        div { style: "display: flex; height: 100vh;",
            // Controls
            div { style: "width: 300px; padding: 20px;",
                button {
                    onclick: move |_| {
                        let mut m = models.write();
                        m.push(ModelConfig::new("", ModelFormat::Cube));
                    },
                    "Add Cube"
                }
            }
            
            // 3D View with wrapper
            ThreeViewWrapper {
                models: models,
                cam_x: cam_x,
                cam_y: cam_y,
                cam_z: cam_z,
                auto_rotate: auto_rotate,
                shader: shader,
            }
        }
    }
}
```

## 6. Adding Shader Effects

Apply a built-in shader effect:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ShaderPreset};

fn app() -> Element {
    rsx! {
        ThreeView {
            shader: ShaderPreset::Water,
            auto_rotate: false,
            show_grid: false,
        }
    }
}
```

## 7. Complete Example

A full-featured viewer with multiple controls:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ModelConfig, ModelFormat, ShaderPreset};

fn app() -> Element {
    let mut models = use_signal(|| vec![
        ModelConfig::new("", ModelFormat::Cube).with_color("#ff6b6b")
    ]);
    let mut shader = use_signal(|| ShaderPreset::None);
    let mut auto_rotate = use_signal(|| true);
    
    rsx! {
        div { style: "display: flex; height: 100vh;",
            // Sidebar
            div { style: "width: 320px; padding: 20px; background: #1a1a2e; color: white; overflow-y: auto;",
                h1 { style: "color: #DEC647;", "3D Viewer" }
                
                h3 { "Add Models" }
                div { style: "display: grid; grid-template-columns: 1fr 1fr; gap: 10px;",
                    button {
                        onclick: move |_| {
                            models.write().push(ModelConfig::new("", ModelFormat::Cube));
                        },
                        "Cube"
                    }
                    button {
                        onclick: move |_| {
                            models.write().push(
                                ModelConfig::new(
                                    "https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf",
                                    ModelFormat::Gltf
                                )
                            );
                        },
                        "Helmet"
                    }
                }
                
                h3 { "Shader" }
                select {
                    onchange: move |e| {
                        let s = match e.value().as_str() {
                            "none" => ShaderPreset::None,
                            "gradient" => ShaderPreset::Gradient,
                            "water" => ShaderPreset::Water,
                            "hologram" => ShaderPreset::Hologram,
                            _ => ShaderPreset::None,
                        };
                        shader.set(s);
                    },
                    option { value: "none", "None" }
                    option { value: "gradient", "Gradient" }
                    option { value: "water", "Water" }
                    option { value: "hologram", "Hologram" }
                }
                
                label {
                    input {
                        r#type: "checkbox",
                        checked: "{auto_rotate()}",
                        onchange: move |e| auto_rotate.set(e.checked())
                    }
                    " Auto-rotate"
                }
            }
            
            // 3D View
            div { style: "flex: 1;",
                ThreeView {
                    models: models.read().clone(),
                    shader: shader(),
                    auto_rotate: auto_rotate(),
                    auto_center: true,
                    auto_scale: true,
                }
            }
        }
    }
}
```

## 8. Selection & Gizmos (v0.0.3+)

Click to select objects and manipulate them with transform gizmos:

```rust
use dioxus::prelude::*;
use dioxus_three::{ThreeView, ModelConfig, ModelFormat, Gizmo, GizmoMode, Selection, SelectionMode};

fn app() -> Element {
    let mut selection = use_signal(|| Selection::empty());
    let mut gizmo = use_signal(|| None::<Gizmo>);
    let mut gizmo_mode = use_signal(|| GizmoMode::Translate);

    let models = vec![
        ModelConfig::new("model.glb", ModelFormat::Glb)
            .with_position(-1.0, 0.0, 0.0),
        ModelConfig::new("", ModelFormat::Cube)
            .with_position(1.0, 0.0, 0.0)
            .with_color("#4ecdc4"),
    ];

    rsx! {
        div { style: "display: flex; height: 100vh;",
            // Toolbar
            div { style: "position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 10;",
                button { onclick: move |_| gizmo_mode.set(GizmoMode::Translate), "Move" }
                button { onclick: move |_| gizmo_mode.set(GizmoMode::Rotate), "Rotate" }
                button { onclick: move |_| gizmo_mode.set(GizmoMode::Scale), "Scale" }
            }

            ThreeView {
                models: models,
                selection: selection(),
                selection_mode: SelectionMode::Single,
                on_selection_change: move |sel| {
                    selection.set(sel.clone());
                    gizmo.set(sel.primary().map(|id| {
                        Gizmo::new(id)
                            .with_mode(gizmo_mode())
                            .with_space(dioxus_three::GizmoSpace::World)
                    }));
                },
                gizmo: gizmo(),
                on_gizmo_drag: move |event| {
                    println!("{:?}: {:?}", event.mode, event.transform);
                    if event.is_finished {
                        println!("Drag finished!");
                    }
                },
            }
        }
    }
}
```

## Next Steps

- Learn about [supported formats]formats.md
- Explore [shader effects]shaders.md
- Read about [pointer events & selection]pointer-selection.md
- Read about [transform gizmos]transform.md
- Read the [API reference]../api/threeview.md
- Understand the [architecture]architecture.md