# 3D in ManimGL

ManimGL has powerful 3D capabilities with a flexible camera system. Unlike ManimCE, ManimGL doesn't require a special ThreeDScene class.

## Basic 3D Setup

### Creating 3D Objects

```python
from manimlib import *

class Basic3DScene(Scene):
    def construct(self):
        # Get camera frame
        frame = self.camera.frame

        # Set 3D orientation
        frame.reorient(20, 70)  # theta, phi in degrees

        # Create 3D objects
        sphere = Sphere(radius=2)
        sphere.set_color(BLUE, opacity=0.7)

        self.add(sphere)
```

## Camera Frame Control

### frame.reorient()

The `reorient()` method is the primary way to control 3D camera orientation.

```python
# frame.reorient(theta, phi, gamma=0, center=ORIGIN, height=8)

# Front view
frame.reorient(0, 0)

# Isometric view
frame.reorient(20, 70)

# Top-down view
frame.reorient(0, 90)

# Side view
frame.reorient(90, 90)
```

### Animating Camera Movement

```python
class AnimatedCamera(Scene):
    def construct(self):
        frame = self.camera.frame

        # Create object
        cube = Cube()
        self.add(cube)

        # Animate camera rotation
        self.play(frame.animate.reorient(30, 60))
        self.wait()

        # Continuous rotation
        frame.add_updater(lambda m, dt: m.increment_theta(0.2 * dt))
        self.wait(10)
```

### Frame Methods

```python
# Set Euler angles
frame.set_euler_angles(theta=30*DEGREES, phi=70*DEGREES)

# Increment angles (useful for rotation)
frame.increment_theta(10*DEGREES)
frame.increment_phi(5*DEGREES)
frame.increment_gamma(2*DEGREES)

# Get current angles
theta = frame.get_theta()
phi = frame.get_phi()
```

## 3D Geometric Primitives

### Sphere

```python
sphere = Sphere(
    radius=2,
    resolution=(20, 20),  # (u_resolution, v_resolution)
    color=BLUE
)
sphere.set_opacity(0.7)
```

### Cube

```python
cube = Cube(
    side_length=2,
    color=GREEN,
    fill_opacity=0.8
)
```

### Surface

```python
# Parametric surface
surface = Surface(
    lambda u, v: np.array([u, v, u**2 + v**2]),
    u_range=(-2, 2),
    v_range=(-2, 2),
    resolution=(20, 20)
)
```

### Torus

```python
torus = Torus(
    r1=2,  # Major radius
    r2=0.5,  # Minor radius
    color=YELLOW
)
```

### Cylinder

```python
cylinder = Cylinder(
    height=3,
    radius=1,
    color=RED
)
```

### 3D Lines and Shapes

```python
# 3D Line
line = Line3D(
    start=[-2, -2, -2],
    end=[2, 2, 2],
    color=WHITE,
    width=0.05
)

# 3D Disk
disk = Disk3D(radius=1.5, color=PURPLE)

# 3D Square
square = Square3D(side_length=2, color=ORANGE)
```

## Textured Surfaces

### Adding Textures to 3D Objects

```python
class TexturedSphere(Scene):
    def construct(self):
        frame = self.camera.frame
        frame.reorient(20, 70)

        # Create sphere with texture
        sphere = Sphere(radius=2)

        # Apply texture from URL or local file
        textured_sphere = TexturedSurface(
            surface=sphere,
            # Can use URL or local path
            image_file="path/to/texture.jpg"
        )

        self.add(textured_sphere)

        # Rotate camera
        self.play(frame.animate.increment_theta(360*DEGREES), run_time=10)
```

### Earth Example

```python
earth = Sphere(radius=2, resolution=(40, 40))
textured_earth = TexturedSurface(
    earth,
    "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
)
```

## 3D Axes

### ThreeDAxes

```python
axes = ThreeDAxes(
    x_range=(-5, 5, 1),
    y_range=(-5, 5, 1),
    z_range=(-5, 5, 1),
    width=10,
    height=10,
    depth=10
)
axes.add_coordinate_labels(font_size=20)
```

### NumberPlane in 3D

```python
# XY plane
xy_plane = NumberPlane(
    x_range=(-5, 5),
    y_range=(-5, 5)
)

# XZ plane (horizontal floor)
xz_plane = NumberPlane(
    x_range=(-5, 5),
    y_range=(-5, 5)
)
xz_plane.rotate(90*DEGREES, axis=RIGHT)
```

## Parametric Surfaces

### Creating Custom Surfaces

```python
# Paraboloid
paraboloid = Surface(
    lambda u, v: np.array([
        u,
        v,
        u**2 + v**2
    ]),
    u_range=(-2, 2),
    v_range=(-2, 2),
    resolution=(30, 30),
    color=BLUE
)

# Wave surface
wave = Surface(
    lambda u, v: np.array([
        u,
        v,
        np.sin(u) * np.cos(v)
    ]),
    u_range=(-PI, PI),
    v_range=(-PI, PI),
    resolution=(40, 40)
)
```

### Surface Mesh

```python
# Create surface
surface = Sphere(radius=2)

# Add mesh overlay
mesh = SurfaceMesh(surface)
mesh.set_stroke(BLUE, width=1, opacity=0.5)

self.add(surface, mesh)
```

## Fixing Objects in Frame

### fix_in_frame()

Keep 2D elements (like labels) fixed while camera rotates.

```python
class FixedLabels(Scene):
    def construct(self):
        frame = self.camera.frame
        frame.reorient(20, 70)

        # 3D object that rotates with camera
        cube = Cube()
        self.add(cube)

        # 2D title that stays fixed
        title = Text("3D Cube", font_size=60)
        title.to_edge(UP)
        title.fix_in_frame()  # Stays in screen space
        self.add(title)

        # Rotate camera
        self.play(frame.animate.reorient(50, 80), run_time=3)
```

## Lighting

### Light Source

```python
# Access light source
light = self.camera.light_source

# Move light
light.move_to([10, 10, 10])

# Animate light movement
self.play(light.animate.move_to([0, 0, 10]), run_time=2)
```

### Gloss and Shadow

```python
# Add gloss to objects
sphere = Sphere(radius=2)
sphere.set_gloss(0.8)  # 0 to 1

# Add shadow
sphere.set_shadow(0.5)  # 0 to 1
```

## Complex 3D Scene Example

```python
class Complex3DScene(Scene):
    def construct(self):
        frame = self.camera.frame
        frame.reorient(20, 75)

        # Create axes
        axes = ThreeDAxes(
            x_range=(-3, 3),
            y_range=(-3, 3),
            z_range=(-3, 3)
        )

        # Create parametric surface
        surface = Surface(
            lambda u, v: np.array([
                u,
                v,
                np.sin(np.sqrt(u**2 + v**2))
            ]),
            u_range=(-3, 3),
            v_range=(-3, 3),
            resolution=(30, 30),
            color=BLUE
        )
        surface.set_opacity(0.7)

        # Add mesh
        mesh = SurfaceMesh(surface)
        mesh.set_stroke(WHITE, 0.5, opacity=0.3)

        # Add title (fixed in frame)
        title = Text("Sinc Function Surface", font_size=48)
        title.to_edge(UP)
        title.fix_in_frame()
        title.set_backstroke(BLACK, 5)

        # Build scene
        self.add(axes, surface, mesh, title)
        self.wait()

        # Rotate camera smoothly
        self.play(
            frame.animate.reorient(45, 70),
            run_time=3
        )
        self.wait()

        # Add continuous rotation
        frame.add_updater(lambda m, dt: m.increment_theta(20 * dt))
        self.wait(10)
```

## Interactive 3D Controls

When running with `manimgl`, you can interact with 3D scenes:

- Press `d` + move mouse: Rotate camera
- Press `z` + scroll: Zoom in/out
- Press `r`: Reset camera to default position
- Press `q`: Exit interaction mode

## Best Practices

1. **Use reorient()**: Cleaner than setting Euler angles manually
2. **fix_in_frame() for labels**: Keep UI elements readable
3. **Appropriate resolution**: Higher resolution for final renders, lower for development
4. **Opacity for depth**: Set opacity < 1 to see through surfaces
5. **Lighting matters**: Adjust light source position for better visualization
6. **Continuous rotation**: Use updaters for smooth camera rotation
7. **Frame rate considerations**: 3D scenes may need lower frame rates during development

## Common Patterns

### Rotating camera around object

```python
# 360-degree rotation
self.play(
    frame.animate.increment_theta(360*DEGREES),
    run_time=10,
    rate_func=linear
)
```

### Creating a floor plane

```python
floor = NumberPlane(
    x_range=(-10, 10),
    y_range=(-10, 10),
    background_line_style={"stroke_color": GREY, "stroke_width": 1}
)
floor.rotate(90*DEGREES, RIGHT)
floor.shift(2*DOWN)
```

### Multiple viewpoints

```python
# Show from different angles
angles = [(0, 0), (20, 70), (45, 45), (90, 90)]

for theta, phi in angles:
    self.play(frame.animate.reorient(theta, phi))
    self.wait()
```
