Skip to content

Feature: Map Geometry (.mapgeo) Parser and Builder #85

@Crauzer

Description

@Crauzer

Feature: Map Geometry (.mapgeo) Parser and Builder

Summary

Implement a Rust parser and builder for Map Geometry environment files (.mapgeo), which contain 3D geometry data for League of Legends maps.

Reference Implementation

C# LeagueToolkit: https://github.com/LeagueToolkit/LeagueToolkit/blob/main/src/LeagueToolkit/Core/Environment/EnvironmentAsset.cs

Related types in the same repository:

  • EnvironmentAssetMesh.cs - Mesh data
  • EnvironmentAssetShaderTextureOverride.cs - Shader overrides
  • BucketedGeometry.cs - Spatial scene graph
  • GeometryBucket.cs - Individual bucket cells
  • PlanarReflector.cs - Reflection planes

Format Overview

  • Magic: OEGM
  • Versions: 5, 6, 7, 9, 11, 12, 13, 14, 15, 17
  • Contents: Vertex/index buffers, meshes, spatial bucketing, planar reflectors

Understanding Bucketed Geometry

The mapgeo format contains two separate geometry systems:

1. Environment Meshes - Renderable Geometry

  • Full vertex data (positions, normals, UVs, colors, tangents)
  • Material references and textures
  • Used for actual GPU rendering
  • Each mesh has its own bounding box/sphere for frustum culling

2. Bucketed Geometry - Spatial Query Geometry

  • Simplified vertices (positions only) and indices
  • No materials or vertex attributes
  • Used for spatial queries, not rendering

How Bucketing Works

The map is divided into a uniform 2D grid on the XZ plane (top-down). Each cell (bucket) contains:

┌─────┬─────┬─────┬─────┐
│ 0,3 │ 1,3 │ 2,3 │ 3,3 │   ← Top-down view of map
├─────┼─────┼─────┼─────┤
│ 0,2 │ 1,2 │ 2,2 │ 3,2 │      
├─────┼─────┼─────┼─────┤
│ 0,1 │ 1,1 │ 2,1 │ 3,1 │   Each cell is a "bucket"
├─────┼─────┼─────┼─────┤
│ 0,0 │ 1,0 │ 2,0 │ 3,0 │
└─────┴─────┴─────┴─────┘

Each GeometryBucket stores:

  • StartIndex / BaseVertex - Range into shared vertex/index buffers
  • InsideFaceCount - Triangles fully contained in bucket
  • StickingOutFaceCount - Triangles extending beyond bucket bounds
  • MaxStickOutX/Z - How far geometry extends (expands culling bounds)

Purpose of Bucketed Geometry

The bucketed data serves as a low-detail spatial acceleration structure for:

  1. CPU-side occlusion culling - Simplified proxy geometry for visibility queries
  2. Collision / Raycasting - Fast spatial queries without full mesh detail
  3. Visibility layer control - Per-face EnvironmentVisibility flags for fog of war or layer-based visibility

Relationship Between Systems

Notably, there are no explicit references from buckets to meshes in the file format. The systems are independent:

  • Meshes self-cull using their bounding volumes against the camera frustum
  • Buckets provide acceleration for collision, occlusion, and visibility queries
  • The game engine likely builds mesh↔bucket associations at runtime based on spatial overlap

The VisibilityControllerPathHash in BucketedGeometry suggests integration with a visibility controller system for fog of war or area-based effects.

Existing Infrastructure

The codebase already has building blocks to leverage:

  • ltk_file - Recognizes MapGeometry kind and OEGM magic
  • ltk_mesh::mem - VertexBuffer, IndexBuffer, VertexBufferDescription types
  • ltk_primitives - AABB, Sphere, Color

Rust-Specific Improvements

Consider the following improvements over the C# implementation:

Data Representation

  1. Zero-copy buffer access - Use memory-mapped views or Cow<[u8]> for vertex/index data to avoid unnecessary allocations when only reading
  2. Typed vertex accessors - Provide strongly-typed iterators over vertex attributes (positions, normals, UVs) instead of raw byte access
  3. Arena-based mesh references - Use indices or handles to reference shared buffers rather than Rc/Arc to keep data cache-friendly

Ergonomics

  1. Builder pattern - Provide EnvironmentAssetBuilder with validation at construction time
  2. Mesh iteration - Implement IntoIterator for convenient mesh traversal
  3. Submesh slicing - Allow extracting submesh geometry as standalone views

Integration

  1. glTF export - Consider optional conversion to glTF for interoperability with 3D tools
  2. Spatial queries - Expose bucketed geometry for efficient ray-casting or frustum culling
  3. Mesh merging - Utility to merge meshes sharing materials for rendering optimization

Performance

  1. Parallel reading - Vertex/index buffers can be read in parallel after parsing headers
  2. Lazy buffer loading - Defer buffer reads until accessed (useful for large maps)
  3. SIMD vertex processing - Use glam SIMD types for batch vertex transformations

Bucketed Geometry API

  1. Bucket lookup - get_bucket(world_x, world_z) -> &GeometryBucket
  2. Range queries - buckets_in_region(aabb) -> impl Iterator<Item = &GeometryBucket>
  3. Raycast support - raycast(origin, direction) -> Option<RayHit> using bucket acceleration

Tasks

Notes

  • Essential for map modding/visualization tools
  • File uses little-endian byte order
  • Index buffers use u16 indices
  • Scene graphs use spatial bucketing for efficient culling
  • Bucketed geometry is separate from renderable meshes (no direct references)

Metadata

Metadata

Assignees

Labels

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions