# `omath::primitives::Mesh` — 3D mesh with transformation support

> Header: `omath/3d_primitives/mesh.hpp`
> Namespace: `omath::primitives`
> Depends on: `omath::Vector3<T>`, `omath::Mat4X4`, `omath::Triangle<Vector3<T>>`
> Purpose: represent and transform 3D meshes in different engine coordinate systems

---

## Overview

`Mesh` represents a 3D polygonal mesh with vertex data and transformation capabilities. It stores:
* **Vertex buffer (VBO)** — array of 3D vertex positions
* **Index buffer (VAO)** — array of triangular faces (indices into VBO)
* **Transformation** — position, rotation, and scale with caching

The mesh supports transformation from local space to world space using engine-specific coordinate systems through the `MeshTrait` template parameter.

---

## Template Declaration

```cpp
template<class Mat4X4, class RotationAngles, class MeshTypeTrait, class Type = float>
class Mesh final;
```

### Template Parameters

* `Mat4X4` — Matrix type for transformations (typically `omath::Mat4X4`)
* `RotationAngles` — Rotation representation (e.g., `ViewAngles` with pitch/yaw/roll)
* `MeshTypeTrait` — Engine-specific transformation trait (see [Engine Traits](#engine-traits))
* `Type` — Scalar type for vertex coordinates (default `float`)

---

## Type Aliases

```cpp
using NumericType = Type;
```

Common engine-specific aliases:

```cpp
// Source Engine
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;

// Unity Engine  
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;

// Unreal Engine
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;

// Frostbite, IW Engine, OpenGL similar...
```

Use the pre-defined type aliases in engine namespaces:
```cpp
using namespace omath::source_engine;
Mesh my_mesh = /* ... */;  // Uses SourceEngine::Mesh
```

---

## Data Members

### Vertex Data

```cpp
std::vector<Vector3<NumericType>> m_vertex_buffer;        // VBO: vertex positions
std::vector<Vector3<std::size_t>> m_vertex_array_object;  // VAO: face indices
```

* `m_vertex_buffer` — array of vertex positions in **local space**
* `m_vertex_array_object` — array of triangular faces, each containing 3 indices into `m_vertex_buffer`

**Public access**: These members are public for direct manipulation when needed.

---

## Constructor

```cpp
Mesh(std::vector<Vector3<NumericType>> vbo,
     std::vector<Vector3<std::size_t>> vao,
     Vector3<NumericType> scale = {1, 1, 1});
```

Creates a mesh from vertex and index data.

**Parameters**:
* `vbo` — vertex buffer (moved into mesh)
* `vao` — index buffer / vertex array object (moved into mesh)
* `scale` — initial scale (default `{1, 1, 1}`)

**Example**:
```cpp
std::vector<Vector3<float>> vertices = {
    {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}
};

std::vector<Vector3<std::size_t>> faces = {
    {0, 1, 2},  // Triangle 1
    {0, 1, 3},  // Triangle 2
    {0, 2, 3},  // Triangle 3
    {1, 2, 3}   // Triangle 4
};

using namespace omath::source_engine;
Mesh tetrahedron(std::move(vertices), std::move(faces));
```

---

## Transformation Methods

### Setting Transform Components

```cpp
void set_origin(const Vector3<NumericType>& new_origin);
void set_scale(const Vector3<NumericType>& new_scale);
void set_rotation(const RotationAngles& new_rotation_angles);
```

Update the mesh's transformation. **Side effect**: invalidates the cached transformation matrix, which will be recomputed on the next `get_to_world_matrix()` call.

**Example**:
```cpp
mesh.set_origin({10, 0, 5});
mesh.set_scale({2, 2, 2});

ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(45.0f);
angles.yaw = YawAngle::from_degrees(30.0f);
mesh.set_rotation(angles);
```

### Getting Transform Components

```cpp
[[nodiscard]] const Vector3<NumericType>& get_origin() const;
[[nodiscard]] const Vector3<NumericType>& get_scale() const;
[[nodiscard]] const RotationAngles& get_rotation_angles() const;
```

Retrieve current transformation components.

### Transformation Matrix

```cpp
[[nodiscard]] const Mat4X4& get_to_world_matrix() const;
```

Returns the cached local-to-world transformation matrix. The matrix is computed lazily on first access after any transformation change:

```
M = Translation(origin) × Scale(scale) × Rotation(angles)
```

The rotation matrix is computed using the engine-specific `MeshTrait::rotation_matrix()` method.

**Caching**: The matrix is stored in a `mutable std::optional` and recomputed only when invalidated by `set_*` methods.

---

## Vertex Transformation

### `vertex_to_world_space`

```cpp
[[nodiscard]]
Vector3<float> vertex_to_world_space(const Vector3<float>& vertex) const;
```

Transforms a vertex from local space to world space by multiplying with the transformation matrix.

**Algorithm**:
1. Convert vertex to column matrix: `[x, y, z, 1]ᵀ`
2. Multiply by transformation matrix: `M × vertex`
3. Extract the resulting 3D position

**Usage**:
```cpp
Vector3<float> local_vertex{1, 0, 0};
Vector3<float> world_vertex = mesh.vertex_to_world_space(local_vertex);
```

**Note**: This is used internally by `MeshCollider` to provide world-space support functions for GJK/EPA.

---

## Face Transformation

### `make_face_in_world_space`

```cpp
[[nodiscard]]
Triangle<Vector3<float>> make_face_in_world_space(
    const std::vector<Vector3<std::size_t>>::const_iterator vao_iterator
) const;
```

Creates a triangle in world space from a face index iterator.

**Parameters**:
* `vao_iterator` — iterator to an element in `m_vertex_array_object`

**Returns**: `Triangle` with all three vertices transformed to world space.

**Example**:
```cpp
for (auto it = mesh.m_vertex_array_object.begin(); 
     it != mesh.m_vertex_array_object.end(); 
     ++it) {
    Triangle<Vector3<float>> world_triangle = mesh.make_face_in_world_space(it);
    // Render or process the triangle
}
```

---

## Usage Examples

### Creating a Box Mesh

```cpp
using namespace omath::source_engine;

std::vector<Vector3<float>> box_vbo = {
    // Bottom face
    {-0.5f, -0.5f, 0.0f}, { 0.5f, -0.5f, 0.0f},
    { 0.5f,  0.5f, 0.0f}, {-0.5f,  0.5f, 0.0f},
    // Top face  
    {-0.5f, -0.5f, 1.0f}, { 0.5f, -0.5f, 1.0f},
    { 0.5f,  0.5f, 1.0f}, {-0.5f,  0.5f, 1.0f}
};

std::vector<Vector3<std::size_t>> box_vao = {
    // Bottom
    {0, 1, 2}, {0, 2, 3},
    // Top
    {4, 6, 5}, {4, 7, 6},
    // Sides
    {0, 4, 5}, {0, 5, 1},
    {1, 5, 6}, {1, 6, 2},
    {2, 6, 7}, {2, 7, 3},
    {3, 7, 4}, {3, 4, 0}
};

Mesh box(std::move(box_vbo), std::move(box_vao));
box.set_origin({0, 0, 50});
box.set_scale({10, 10, 10});
```

### Transforming Mesh Over Time

```cpp
void update_mesh(Mesh& mesh, float delta_time) {
    // Rotate mesh
    auto rotation = mesh.get_rotation_angles();
    rotation.yaw = YawAngle::from_degrees(
        rotation.yaw.as_degrees() + 45.0f * delta_time
    );
    mesh.set_rotation(rotation);
    
    // Oscillate position
    auto origin = mesh.get_origin();
    origin.z = 50.0f + 10.0f * std::sin(current_time * 2.0f);
    mesh.set_origin(origin);
}
```

### Collision Detection

```cpp
using namespace omath::collision;
using namespace omath::source_engine;

Mesh mesh_a(vbo_a, vao_a);
mesh_a.set_origin({0, 0, 0});

Mesh mesh_b(vbo_b, vao_b);
mesh_b.set_origin({5, 0, 0});

MeshCollider collider_a(std::move(mesh_a));
MeshCollider collider_b(std::move(mesh_b));

auto result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
    collider_a, collider_b
);
```

### Rendering Transformed Triangles

```cpp
void render_mesh(const Mesh& mesh) {
    for (auto it = mesh.m_vertex_array_object.begin();
         it != mesh.m_vertex_array_object.end();
         ++it) {
        
        Triangle<Vector3<float>> tri = mesh.make_face_in_world_space(it);
        
        // Draw triangle with your renderer
        draw_triangle(tri.m_vertex1, tri.m_vertex2, tri.m_vertex3);
    }
}
```

---

## Engine Traits

Each game engine has a corresponding `MeshTrait` that provides the `rotation_matrix` function:

```cpp
class MeshTrait final {
public:
    [[nodiscard]]
    static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
```

### Available Engines

| Engine | Namespace | Header |
|--------|-----------|--------|
| Source Engine | `omath::source_engine` | `engines/source_engine/mesh.hpp` |
| Unity | `omath::unity_engine` | `engines/unity_engine/mesh.hpp` |
| Unreal | `omath::unreal_engine` | `engines/unreal_engine/mesh.hpp` |
| Frostbite | `omath::frostbite_engine` | `engines/frostbite_engine/mesh.hpp` |
| IW Engine | `omath::iw_engine` | `engines/iw_engine/mesh.hpp` |
| OpenGL | `omath::opengl_engine` | `engines/opengl_engine/mesh.hpp` |

**Example** (Source Engine):
```cpp
using namespace omath::source_engine;

// Uses source_engine::MeshTrait automatically
Mesh my_mesh(vertices, indices);
```

See [MeshTrait Documentation](#mesh-trait-documentation) for engine-specific details.

---

## Performance Considerations

### Matrix Caching

The transformation matrix is computed lazily and cached:
* **First access**: O(matrix multiply) ≈ 64 float operations
* **Subsequent access**: O(1) — returns cached matrix
* **Cache invalidation**: Any `set_*` call invalidates the cache

**Best practice**: Batch transformation updates before accessing the matrix:
```cpp
// Good: single matrix recomputation
mesh.set_origin(new_origin);
mesh.set_rotation(new_rotation);
mesh.set_scale(new_scale);
auto matrix = mesh.get_to_world_matrix();  // Computes once

// Bad: three matrix recomputations
mesh.set_origin(new_origin);
auto m1 = mesh.get_to_world_matrix();  // Compute
mesh.set_rotation(new_rotation);
auto m2 = mesh.get_to_world_matrix();  // Compute again
mesh.set_scale(new_scale);
auto m3 = mesh.get_to_world_matrix();  // Compute again
```

### Memory Layout

* **VBO**: Contiguous `std::vector` for cache-friendly access
* **VAO**: Contiguous indices for cache-friendly face iteration
* **Matrix**: Cached in `std::optional` (no allocation)

### Transformation Cost

* `vertex_to_world_space`: ~15-20 FLOPs per vertex (4×4 matrix multiply)
* `make_face_in_world_space`: ~60 FLOPs (3 vertices)

For high-frequency transformations, consider:
* Caching transformed vertices if the mesh doesn't change
* Using simpler proxy geometry for collision
* Batching transformations

---

## Coordinate System Details

Different engines use different coordinate systems:

| Engine | Up Axis | Forward Axis | Handedness |
|--------|---------|--------------|------------|
| Source | +Z | +Y | Right |
| Unity | +Y | +Z | Left |
| Unreal | +Z | +X | Left |
| Frostbite | +Y | +Z | Right |
| IW Engine | +Z | +Y | Right |
| OpenGL | +Y | +Z | Right |

The `MeshTrait::rotation_matrix` function accounts for these differences, ensuring correct transformations in each engine's space.

---

## Limitations & Edge Cases

### Empty Mesh

A mesh with no vertices or faces is valid but not useful:
```cpp
Mesh empty_mesh({}, {});  // Valid but meaningless
```

For collision detection, ensure `m_vertex_buffer` is non-empty.

### Index Validity

No bounds checking is performed on indices in `m_vertex_array_object`. Ensure all indices are valid:
```cpp
assert(face.x < mesh.m_vertex_buffer.size());
assert(face.y < mesh.m_vertex_buffer.size());
assert(face.z < mesh.m_vertex_buffer.size());
```

### Degenerate Triangles

Faces with duplicate indices or collinear vertices will produce degenerate triangles. The mesh doesn't validate this; users must ensure clean geometry.

### Thread Safety

* **Read-only**: Safe to read from multiple threads (including const methods)
* **Modification**: Not thread-safe; synchronize `set_*` calls externally
* **Matrix cache**: Uses `mutable` member; not thread-safe even for const methods

---

## See Also

- [MeshCollider Documentation](../collision/mesh_collider.md) - Collision wrapper for meshes
- [GJK Algorithm Documentation](../collision/gjk_algorithm.md) - Uses mesh for collision detection
- [EPA Algorithm Documentation](../collision/epa_algorithm.md) - Penetration depth with meshes
- [Triangle Documentation](../linear_algebra/triangle.md) - Triangle primitive
- [Mat4X4 Documentation](../linear_algebra/mat.md) - Transformation matrices
- [Box Documentation](box.md) - Box primitive
- [Plane Documentation](plane.md) - Plane primitive

---

## Mesh Trait Documentation

For engine-specific `MeshTrait` details, see:

- [Source Engine MeshTrait](../engines/source_engine/mesh_trait.md)
- [Unity Engine MeshTrait](../engines/unity_engine/mesh_trait.md)
- [Unreal Engine MeshTrait](../engines/unreal_engine/mesh_trait.md)
- [Frostbite Engine MeshTrait](../engines/frostbite/mesh_trait.md)
- [IW Engine MeshTrait](../engines/iw_engine/mesh_trait.md)
- [OpenGL Engine MeshTrait](../engines/opengl_engine/mesh_trait.md)

---

*Last updated: 13 Nov 2025*
