Skip to content

Commit 495ac90

Browse files
authored
Merge pull request #86 from LeagueToolkit/feat/ltk_mapgeo
feat(ltk_mapgeo): implement mapgeo parsing
2 parents d9e0f8f + b339efe commit 495ac90

File tree

28 files changed

+2809
-14
lines changed

28 files changed

+2809
-14
lines changed

crates/ltk_io_ext/src/reader.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,20 @@ pub trait ReaderExt: Read {
2424
Ok(std::str::from_utf8(&buf[..i])?.to_string())
2525
}
2626

27-
fn read_len_prefixed_string<T: ByteOrder>(&mut self) -> ReaderResult<String> {
27+
fn read_sized_string_u16<T: ByteOrder>(&mut self) -> ReaderResult<String> {
2828
let len = self.read_u16::<T>()?;
2929
let mut buf = vec![0; len as _];
3030
self.read_exact(&mut buf)?;
3131
Ok(String::from_utf8(buf)?)
3232
}
3333

34+
fn read_sized_string_u32<T: ByteOrder>(&mut self) -> ReaderResult<String> {
35+
let len = self.read_u32::<T>()?;
36+
let mut buf = vec![0; len as _];
37+
self.read_exact(&mut buf)?;
38+
Ok(String::from_utf8(buf)?)
39+
}
40+
3441
fn read_str_until_nul(&mut self) -> io::Result<String> {
3542
let mut s = String::new();
3643
loop {

crates/ltk_mapgeo/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "ltk_mapgeo"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Map Geometry (.mapgeo) parsing for League Toolkit"
6+
license = "MIT OR Apache-2.0"
7+
readme = "../../README.md"
8+
9+
[dependencies]
10+
byteorder = { workspace = true }
11+
bitflags = { workspace = true }
12+
thiserror = { workspace = true }
13+
glam = { workspace = true }
14+
15+
ltk_io_ext = { version = "0.3.4", path = "../ltk_io_ext" }
16+
ltk_primitives = { version = "0.3.0", path = "../ltk_primitives" }
17+
ltk_mesh = { version = "0.3.0", path = "../ltk_mesh" }
18+
19+
[dev-dependencies]

crates/ltk_mapgeo/src/asset.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
//! Environment asset definition
2+
3+
use ltk_mesh::mem::{IndexBuffer, VertexBuffer};
4+
5+
use crate::{BucketedGeometry, EnvironmentMesh, PlanarReflector, ShaderTextureOverride};
6+
7+
/// An environment asset contains all geometry data for a League of Legends map.
8+
///
9+
/// This is the primary type for working with `.mapgeo` files. It contains:
10+
/// - Environment meshes (renderable geometry)
11+
/// - Shared vertex and index buffers
12+
/// - Bucketed geometry for spatial queries
13+
/// - Planar reflectors for reflection rendering
14+
///
15+
/// # Buffer Sharing
16+
///
17+
/// Multiple meshes can reference the same vertex/index buffers. The asset owns
18+
/// all buffers, and meshes store indices into these buffer arrays.
19+
///
20+
/// # Example
21+
///
22+
/// ```ignore
23+
/// use ltk_mapgeo::EnvironmentAsset;
24+
/// use std::fs::File;
25+
///
26+
/// let mut file = File::open("base.mapgeo")?;
27+
/// let asset = EnvironmentAsset::from_reader(&mut file)?;
28+
///
29+
/// for mesh in asset.meshes() {
30+
/// println!("Mesh: {} (material: {})", mesh.name(), mesh.material_name());
31+
///
32+
/// // Access the mesh's vertex buffer
33+
/// let vb_id = mesh.vertex_buffer_ids()[0];
34+
/// let vertex_buffer = &asset.vertex_buffers()[vb_id];
35+
/// println!(" Vertices: {}", vertex_buffer.count());
36+
/// }
37+
/// ```
38+
#[derive(Debug)]
39+
pub struct EnvironmentAsset {
40+
/// Shader texture overrides (global sampler replacements)
41+
shader_texture_overrides: Vec<ShaderTextureOverride>,
42+
43+
/// Environment meshes (renderable geometry)
44+
meshes: Vec<EnvironmentMesh>,
45+
46+
/// Bucketed geometry scene graphs (spatial acceleration)
47+
scene_graphs: Vec<BucketedGeometry>,
48+
49+
/// Planar reflectors
50+
planar_reflectors: Vec<PlanarReflector>,
51+
52+
/// Shared vertex buffers (meshes reference by index)
53+
vertex_buffers: Vec<VertexBuffer>,
54+
55+
/// Shared index buffers (meshes reference by index)
56+
index_buffers: Vec<IndexBuffer<u16>>,
57+
}
58+
59+
impl EnvironmentAsset {
60+
/// Creates a new environment asset builder
61+
pub(crate) fn builder() -> EnvironmentAssetBuilder {
62+
EnvironmentAssetBuilder::default()
63+
}
64+
65+
/// The shader texture overrides
66+
#[inline]
67+
pub fn shader_texture_overrides(&self) -> &[ShaderTextureOverride] {
68+
&self.shader_texture_overrides
69+
}
70+
71+
/// The environment meshes
72+
#[inline]
73+
pub fn meshes(&self) -> &[EnvironmentMesh] {
74+
&self.meshes
75+
}
76+
77+
/// The bucketed geometry scene graphs
78+
#[inline]
79+
pub fn scene_graphs(&self) -> &[BucketedGeometry] {
80+
&self.scene_graphs
81+
}
82+
83+
/// The planar reflectors
84+
#[inline]
85+
pub fn planar_reflectors(&self) -> &[PlanarReflector] {
86+
&self.planar_reflectors
87+
}
88+
89+
/// The shared vertex buffers
90+
#[inline]
91+
pub fn vertex_buffers(&self) -> &[VertexBuffer] {
92+
&self.vertex_buffers
93+
}
94+
95+
/// The shared index buffers
96+
#[inline]
97+
pub fn index_buffers(&self) -> &[IndexBuffer<u16>] {
98+
&self.index_buffers
99+
}
100+
101+
/// Gets a vertex buffer by index
102+
#[inline]
103+
pub fn vertex_buffer(&self, index: usize) -> Option<&VertexBuffer> {
104+
self.vertex_buffers.get(index)
105+
}
106+
107+
/// Gets an index buffer by index
108+
#[inline]
109+
pub fn index_buffer(&self, index: usize) -> Option<&IndexBuffer<u16>> {
110+
self.index_buffers.get(index)
111+
}
112+
113+
/// Returns the total number of meshes
114+
#[inline]
115+
pub fn mesh_count(&self) -> usize {
116+
self.meshes.len()
117+
}
118+
119+
/// Returns the total number of vertex buffers
120+
#[inline]
121+
pub fn vertex_buffer_count(&self) -> usize {
122+
self.vertex_buffers.len()
123+
}
124+
125+
/// Returns the total number of index buffers
126+
#[inline]
127+
pub fn index_buffer_count(&self) -> usize {
128+
self.index_buffers.len()
129+
}
130+
131+
/// Finds a mesh by name
132+
pub fn find_mesh(&self, name: &str) -> Option<&EnvironmentMesh> {
133+
self.meshes.iter().find(|m| m.name() == name)
134+
}
135+
136+
/// Iterates over meshes with their vertex and index buffers
137+
pub fn meshes_with_buffers(
138+
&self,
139+
) -> impl Iterator<Item = (&EnvironmentMesh, Vec<&VertexBuffer>, &IndexBuffer<u16>)> {
140+
self.meshes.iter().filter_map(|mesh| {
141+
let vertex_buffers: Vec<_> = mesh
142+
.vertex_buffer_ids()
143+
.iter()
144+
.filter_map(|&id| self.vertex_buffers.get(id))
145+
.collect();
146+
147+
let index_buffer = self.index_buffers.get(mesh.index_buffer_id())?;
148+
149+
Some((mesh, vertex_buffers, index_buffer))
150+
})
151+
}
152+
}
153+
154+
/// Builder for constructing [`EnvironmentAsset`] instances
155+
#[derive(Default)]
156+
pub(crate) struct EnvironmentAssetBuilder {
157+
shader_texture_overrides: Vec<ShaderTextureOverride>,
158+
meshes: Vec<EnvironmentMesh>,
159+
scene_graphs: Vec<BucketedGeometry>,
160+
planar_reflectors: Vec<PlanarReflector>,
161+
vertex_buffers: Vec<VertexBuffer>,
162+
index_buffers: Vec<IndexBuffer<u16>>,
163+
}
164+
165+
impl EnvironmentAssetBuilder {
166+
pub fn shader_texture_overrides(mut self, overrides: Vec<ShaderTextureOverride>) -> Self {
167+
self.shader_texture_overrides = overrides;
168+
self
169+
}
170+
171+
pub fn meshes(mut self, meshes: Vec<EnvironmentMesh>) -> Self {
172+
self.meshes = meshes;
173+
self
174+
}
175+
176+
pub fn scene_graphs(mut self, scene_graphs: Vec<BucketedGeometry>) -> Self {
177+
self.scene_graphs = scene_graphs;
178+
self
179+
}
180+
181+
pub fn planar_reflectors(mut self, reflectors: Vec<PlanarReflector>) -> Self {
182+
self.planar_reflectors = reflectors;
183+
self
184+
}
185+
186+
pub fn vertex_buffers(mut self, buffers: Vec<VertexBuffer>) -> Self {
187+
self.vertex_buffers = buffers;
188+
self
189+
}
190+
191+
pub fn index_buffers(mut self, buffers: Vec<IndexBuffer<u16>>) -> Self {
192+
self.index_buffers = buffers;
193+
self
194+
}
195+
196+
pub fn build(self) -> EnvironmentAsset {
197+
EnvironmentAsset {
198+
shader_texture_overrides: self.shader_texture_overrides,
199+
meshes: self.meshes,
200+
scene_graphs: self.scene_graphs,
201+
planar_reflectors: self.planar_reflectors,
202+
vertex_buffers: self.vertex_buffers,
203+
index_buffers: self.index_buffers,
204+
}
205+
}
206+
}

crates/ltk_mapgeo/src/channel.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//! Environment asset channel definitions
2+
//!
3+
//! Channels represent texture samplers with associated UV parameters.
4+
5+
use glam::Vec2;
6+
7+
/// An environment asset channel represents a texture sampler binding.
8+
///
9+
/// Used for lighting textures like "STATIONARY_LIGHT" and "BAKED_LIGHT".
10+
#[derive(Debug, Clone, PartialEq)]
11+
pub struct EnvironmentAssetChannel {
12+
/// Texture path (if known)
13+
texture: String,
14+
/// UV scale applied to this channel
15+
scale: Vec2,
16+
/// UV offset applied to this channel
17+
offset: Vec2,
18+
}
19+
20+
impl EnvironmentAssetChannel {
21+
/// Creates a new channel
22+
pub fn new(texture: String, scale: Vec2, offset: Vec2) -> Self {
23+
Self {
24+
texture,
25+
scale,
26+
offset,
27+
}
28+
}
29+
30+
/// Creates an empty/default channel
31+
pub fn empty() -> Self {
32+
Self {
33+
texture: String::new(),
34+
scale: Vec2::ONE,
35+
offset: Vec2::ZERO,
36+
}
37+
}
38+
39+
/// The texture path
40+
#[inline]
41+
pub fn texture(&self) -> &str {
42+
&self.texture
43+
}
44+
45+
/// UV scale for this channel
46+
#[inline]
47+
pub fn scale(&self) -> Vec2 {
48+
self.scale
49+
}
50+
51+
/// UV offset for this channel
52+
#[inline]
53+
pub fn offset(&self) -> Vec2 {
54+
self.offset
55+
}
56+
}
57+
58+
/// A per-mesh texture override.
59+
///
60+
/// Allows overriding specific sampler slots on individual meshes.
61+
#[derive(Debug, Clone, PartialEq, Eq)]
62+
pub struct MeshTextureOverride {
63+
/// Sampler index to override
64+
sampler_index: u32,
65+
/// Texture path
66+
texture: String,
67+
}
68+
69+
impl MeshTextureOverride {
70+
/// Creates a new texture override
71+
pub fn new(sampler_index: u32, texture: String) -> Self {
72+
Self {
73+
sampler_index,
74+
texture,
75+
}
76+
}
77+
78+
/// The sampler index this override applies to
79+
#[inline]
80+
pub fn sampler_index(&self) -> u32 {
81+
self.sampler_index
82+
}
83+
84+
/// The texture path
85+
#[inline]
86+
pub fn texture(&self) -> &str {
87+
&self.texture
88+
}
89+
}

0 commit comments

Comments
 (0)