mirror of
https://codeberg.org/icewind/vmdl.git
synced 2026-06-03 16:44:11 +02:00
gltf: per mesh primitive
This commit is contained in:
parent
30acc4b093
commit
a45ce6dfb9
5 changed files with 253 additions and 152 deletions
|
|
@ -1,14 +1,202 @@
|
||||||
use crate::Vertex;
|
use bytemuck::{offset_of, Pod, Zeroable};
|
||||||
|
use gltf_json::accessor::{ComponentType, GenericComponentType, Type};
|
||||||
|
use gltf_json::buffer::{Target, View};
|
||||||
|
use gltf_json::mesh::{Mode, Primitive, Semantic};
|
||||||
|
use gltf_json::validation::Checked::Valid;
|
||||||
|
use gltf_json::{Accessor, Index, Mesh, Value};
|
||||||
|
use std::mem::size_of;
|
||||||
use vmdl::Model;
|
use vmdl::Model;
|
||||||
|
|
||||||
pub fn model_to_vertices(model: &Model) -> Vec<Vertex> {
|
#[derive(Copy, Clone, Debug, Default, Zeroable, Pod)]
|
||||||
model
|
#[repr(C)]
|
||||||
.meshes()
|
pub struct Vertex {
|
||||||
.flat_map(|mesh| mesh.vertices())
|
position: [f32; 3],
|
||||||
.map(|vertex| Vertex {
|
normal: [f32; 3],
|
||||||
|
uv: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&vmdl::vvd::Vertex> for Vertex {
|
||||||
|
fn from(vertex: &vmdl::vvd::Vertex) -> Self {
|
||||||
|
Vertex {
|
||||||
position: vertex.position.into(),
|
position: vertex.position.into(),
|
||||||
uv: vertex.texture_coordinates.into(),
|
uv: vertex.texture_coordinates.into(),
|
||||||
normal: vertex.normal.into(),
|
normal: vertex.normal.into(),
|
||||||
})
|
}
|
||||||
.collect()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_vertices(
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
views: &mut Vec<View>,
|
||||||
|
accessors: &mut Vec<Accessor>,
|
||||||
|
model: &Model,
|
||||||
|
) {
|
||||||
|
let start = buffer.len() as u32;
|
||||||
|
let view_start = views.len() as u32;
|
||||||
|
let vertex_count = model.vertices().len() as u32;
|
||||||
|
|
||||||
|
let (min, max) = model.bounding_box();
|
||||||
|
let min = <[f32; 3]>::from(min);
|
||||||
|
let max = <[f32; 3]>::from(max);
|
||||||
|
|
||||||
|
let vertex_data = model
|
||||||
|
.vertices()
|
||||||
|
.iter()
|
||||||
|
.map(Vertex::from)
|
||||||
|
.flat_map(bytemuck::cast::<_, [u8; size_of::<Vertex>()]>);
|
||||||
|
buffer.extend(vertex_data);
|
||||||
|
|
||||||
|
let vertex_buffer_view = View {
|
||||||
|
buffer: Index::new(0),
|
||||||
|
byte_length: buffer.len() as u32 - start,
|
||||||
|
byte_offset: Some(start),
|
||||||
|
byte_stride: Some(size_of::<Vertex>() as u32),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
name: None,
|
||||||
|
target: Some(Valid(Target::ArrayBuffer)),
|
||||||
|
};
|
||||||
|
|
||||||
|
views.push(vertex_buffer_view);
|
||||||
|
|
||||||
|
let positions = Accessor {
|
||||||
|
buffer_view: Some(Index::new(view_start)),
|
||||||
|
byte_offset: Some(offset_of!(Vertex, position) as u32),
|
||||||
|
count: vertex_count,
|
||||||
|
component_type: Valid(GenericComponentType(ComponentType::F32)),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
type_: Valid(Type::Vec3),
|
||||||
|
min: Some(Value::from(Vec::from(min))),
|
||||||
|
max: Some(Value::from(Vec::from(max))),
|
||||||
|
name: None,
|
||||||
|
normalized: false,
|
||||||
|
sparse: None,
|
||||||
|
};
|
||||||
|
let uvs = Accessor {
|
||||||
|
buffer_view: Some(Index::new(view_start)),
|
||||||
|
byte_offset: Some(offset_of!(Vertex, uv) as u32),
|
||||||
|
count: vertex_count,
|
||||||
|
component_type: Valid(GenericComponentType(ComponentType::F32)),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
type_: Valid(Type::Vec2),
|
||||||
|
min: None,
|
||||||
|
max: None,
|
||||||
|
name: None,
|
||||||
|
normalized: false,
|
||||||
|
sparse: None,
|
||||||
|
};
|
||||||
|
let normals = Accessor {
|
||||||
|
buffer_view: Some(Index::new(view_start)),
|
||||||
|
byte_offset: Some(offset_of!(Vertex, normal) as u32),
|
||||||
|
count: vertex_count,
|
||||||
|
component_type: Valid(GenericComponentType(ComponentType::F32)),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
type_: Valid(Type::Vec3),
|
||||||
|
min: None,
|
||||||
|
max: None,
|
||||||
|
name: None,
|
||||||
|
normalized: false,
|
||||||
|
sparse: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
accessors.extend([positions, uvs, normals]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_model(
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
views: &mut Vec<View>,
|
||||||
|
accessors: &mut Vec<Accessor>,
|
||||||
|
model: &Model,
|
||||||
|
) -> Mesh {
|
||||||
|
let accessor_start = accessors.len() as u32;
|
||||||
|
push_vertices(buffer, views, accessors, model);
|
||||||
|
|
||||||
|
let primitives = model
|
||||||
|
.meshes()
|
||||||
|
.map(|mesh| push_primitive(buffer, views, accessors, &mesh, accessor_start))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Mesh {
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
name: Some(model.name().into()),
|
||||||
|
primitives,
|
||||||
|
weights: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_primitive(
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
views: &mut Vec<View>,
|
||||||
|
accessors: &mut Vec<Accessor>,
|
||||||
|
mesh: &vmdl::Mesh,
|
||||||
|
vertex_accessor_start: u32,
|
||||||
|
) -> Primitive {
|
||||||
|
let buffer_start = buffer.len() as u32;
|
||||||
|
let view_start = views.len() as u32;
|
||||||
|
let accessor_start = accessors.len() as u32;
|
||||||
|
|
||||||
|
buffer.extend(
|
||||||
|
mesh.vertex_strip_indices()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|index| (index as u32).to_le_bytes()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let byte_length = buffer.len() as u32 - buffer_start;
|
||||||
|
|
||||||
|
let view = View {
|
||||||
|
buffer: Index::new(0),
|
||||||
|
byte_length,
|
||||||
|
byte_offset: Some(buffer_start),
|
||||||
|
byte_stride: Some(size_of::<u32>() as u32),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
name: None,
|
||||||
|
target: Some(Valid(Target::ArrayBuffer)),
|
||||||
|
};
|
||||||
|
views.push(view);
|
||||||
|
|
||||||
|
let accessor = Accessor {
|
||||||
|
buffer_view: Some(Index::new(view_start)),
|
||||||
|
byte_offset: Some(0),
|
||||||
|
count: byte_length / size_of::<u32>() as u32,
|
||||||
|
component_type: Valid(GenericComponentType(ComponentType::U32)),
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
type_: Valid(Type::Scalar),
|
||||||
|
min: None,
|
||||||
|
max: None,
|
||||||
|
name: None,
|
||||||
|
normalized: false,
|
||||||
|
sparse: None,
|
||||||
|
};
|
||||||
|
accessors.push(accessor);
|
||||||
|
|
||||||
|
Primitive {
|
||||||
|
attributes: {
|
||||||
|
let mut map = std::collections::BTreeMap::new();
|
||||||
|
map.insert(
|
||||||
|
Valid(Semantic::Positions),
|
||||||
|
Index::new(vertex_accessor_start),
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
Valid(Semantic::TexCoords(0)),
|
||||||
|
Index::new(vertex_accessor_start + 1),
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
Valid(Semantic::Normals),
|
||||||
|
Index::new(vertex_accessor_start + 2),
|
||||||
|
);
|
||||||
|
map
|
||||||
|
},
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
indices: Some(Index::new(accessor_start)),
|
||||||
|
material: None,
|
||||||
|
mode: Valid(Mode::Triangles),
|
||||||
|
targets: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ mod loader;
|
||||||
|
|
||||||
use gltf_json as json;
|
use gltf_json as json;
|
||||||
|
|
||||||
use std::{fs, mem};
|
use std::fs;
|
||||||
|
|
||||||
use crate::convert::model_to_vertices;
|
use crate::convert::push_model;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
use json::validation::Checked::Valid;
|
use gltf_json::Index;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::env::args_os;
|
use std::env::args_os;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -24,53 +24,41 @@ enum Output {
|
||||||
Binary,
|
Binary,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vertex {
|
|
||||||
position: [f32; 3],
|
|
||||||
uv: [f32; 2],
|
|
||||||
normal: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate bounding coordinates of a list of vertices, used for the clipping distance of the model
|
|
||||||
fn bounding_coords(points: &[Vertex]) -> ([f32; 3], [f32; 3]) {
|
|
||||||
let mut min = [f32::MAX, f32::MAX, f32::MAX];
|
|
||||||
let mut max = [f32::MIN, f32::MIN, f32::MIN];
|
|
||||||
|
|
||||||
for point in points {
|
|
||||||
let p = point.position;
|
|
||||||
for i in 0..3 {
|
|
||||||
min[i] = f32::min(min[i], p[i]);
|
|
||||||
max[i] = f32::max(max[i], p[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(min, max)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn align_to_multiple_of_four(n: &mut u32) {
|
fn align_to_multiple_of_four(n: &mut u32) {
|
||||||
*n = (*n + 3) & !3;
|
*n = (*n + 3) & !3;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_padded_byte_vector<T>(vec: Vec<T>) -> Vec<u8> {
|
fn pad_byte_vector(mut vec: Vec<u8>) -> Vec<u8> {
|
||||||
let byte_length = vec.len() * mem::size_of::<T>();
|
while vec.len() % 4 != 0 {
|
||||||
let byte_capacity = vec.capacity() * mem::size_of::<T>();
|
vec.push(0); // pad to multiple of four bytes
|
||||||
let alloc = vec.into_boxed_slice();
|
|
||||||
let ptr = Box::<[T]>::into_raw(alloc) as *mut u8;
|
|
||||||
let mut new_vec = unsafe { Vec::from_raw_parts(ptr, byte_length, byte_capacity) };
|
|
||||||
while new_vec.len() % 4 != 0 {
|
|
||||||
new_vec.push(0); // pad to multiple of four bytes
|
|
||||||
}
|
}
|
||||||
new_vec
|
vec
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export(model: Model, output: Output) {
|
fn export(model: Model, output: Output) {
|
||||||
let vertices = model_to_vertices(&model);
|
let mut buffer = Vec::new();
|
||||||
|
let mut views = Vec::new();
|
||||||
|
let mut accessors = Vec::new();
|
||||||
|
|
||||||
let (min, max) = bounding_coords(&vertices);
|
let mesh = push_model(&mut buffer, &mut views, &mut accessors, &model);
|
||||||
|
|
||||||
let buffer_length = (vertices.len() * mem::size_of::<Vertex>()) as u32;
|
let node = json::Node {
|
||||||
let buffer = json::Buffer {
|
camera: None,
|
||||||
byte_length: buffer_length,
|
children: None,
|
||||||
|
extensions: Default::default(),
|
||||||
|
extras: Default::default(),
|
||||||
|
matrix: None,
|
||||||
|
mesh: Some(Index::new(0)),
|
||||||
|
name: None,
|
||||||
|
rotation: None,
|
||||||
|
scale: None,
|
||||||
|
translation: None,
|
||||||
|
skin: None,
|
||||||
|
weights: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let g_buffer = json::Buffer {
|
||||||
|
byte_length: buffer.len() as u32,
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
extras: Default::default(),
|
extras: Default::default(),
|
||||||
name: None,
|
name: None,
|
||||||
|
|
@ -80,118 +68,18 @@ fn export(model: Model, output: Output) {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let buffer_view = json::buffer::View {
|
|
||||||
buffer: json::Index::new(0),
|
|
||||||
byte_length: buffer.byte_length,
|
|
||||||
byte_offset: None,
|
|
||||||
byte_stride: Some(mem::size_of::<Vertex>() as u32),
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
name: None,
|
|
||||||
target: Some(Valid(json::buffer::Target::ArrayBuffer)),
|
|
||||||
};
|
|
||||||
let positions = json::Accessor {
|
|
||||||
buffer_view: Some(json::Index::new(0)),
|
|
||||||
byte_offset: Some(0),
|
|
||||||
count: vertices.len() as u32,
|
|
||||||
component_type: Valid(json::accessor::GenericComponentType(
|
|
||||||
json::accessor::ComponentType::F32,
|
|
||||||
)),
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
type_: Valid(json::accessor::Type::Vec3),
|
|
||||||
min: Some(json::Value::from(Vec::from(min))),
|
|
||||||
max: Some(json::Value::from(Vec::from(max))),
|
|
||||||
name: None,
|
|
||||||
normalized: false,
|
|
||||||
sparse: None,
|
|
||||||
};
|
|
||||||
let uvs = json::Accessor {
|
|
||||||
buffer_view: Some(json::Index::new(0)),
|
|
||||||
byte_offset: Some((3 * mem::size_of::<f32>()) as u32),
|
|
||||||
count: vertices.len() as u32,
|
|
||||||
component_type: Valid(json::accessor::GenericComponentType(
|
|
||||||
json::accessor::ComponentType::F32,
|
|
||||||
)),
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
type_: Valid(json::accessor::Type::Vec3),
|
|
||||||
min: None,
|
|
||||||
max: None,
|
|
||||||
name: None,
|
|
||||||
normalized: false,
|
|
||||||
sparse: None,
|
|
||||||
};
|
|
||||||
let normals = json::Accessor {
|
|
||||||
buffer_view: Some(json::Index::new(0)),
|
|
||||||
byte_offset: Some((5 * mem::size_of::<f32>()) as u32),
|
|
||||||
count: vertices.len() as u32,
|
|
||||||
component_type: Valid(json::accessor::GenericComponentType(
|
|
||||||
json::accessor::ComponentType::F32,
|
|
||||||
)),
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
type_: Valid(json::accessor::Type::Vec3),
|
|
||||||
min: None,
|
|
||||||
max: None,
|
|
||||||
name: None,
|
|
||||||
normalized: false,
|
|
||||||
sparse: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let primitive = json::mesh::Primitive {
|
|
||||||
attributes: {
|
|
||||||
let mut map = std::collections::BTreeMap::new();
|
|
||||||
map.insert(Valid(json::mesh::Semantic::Positions), json::Index::new(0));
|
|
||||||
map.insert(
|
|
||||||
Valid(json::mesh::Semantic::TexCoords(0)),
|
|
||||||
json::Index::new(1),
|
|
||||||
);
|
|
||||||
map.insert(Valid(json::mesh::Semantic::Normals), json::Index::new(2));
|
|
||||||
map
|
|
||||||
},
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
indices: None,
|
|
||||||
material: None,
|
|
||||||
mode: Valid(json::mesh::Mode::Triangles),
|
|
||||||
targets: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mesh = json::Mesh {
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
name: None,
|
|
||||||
primitives: vec![primitive],
|
|
||||||
weights: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let node = json::Node {
|
|
||||||
camera: None,
|
|
||||||
children: None,
|
|
||||||
extensions: Default::default(),
|
|
||||||
extras: Default::default(),
|
|
||||||
matrix: None,
|
|
||||||
mesh: Some(json::Index::new(0)),
|
|
||||||
name: None,
|
|
||||||
rotation: None,
|
|
||||||
scale: None,
|
|
||||||
translation: None,
|
|
||||||
skin: None,
|
|
||||||
weights: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let root = json::Root {
|
let root = json::Root {
|
||||||
accessors: vec![positions, uvs, normals],
|
accessors,
|
||||||
buffers: vec![buffer],
|
buffers: vec![g_buffer],
|
||||||
buffer_views: vec![buffer_view],
|
buffer_views: views,
|
||||||
meshes: vec![mesh],
|
meshes: vec![mesh],
|
||||||
nodes: vec![node],
|
nodes: vec![node],
|
||||||
scenes: vec![json::Scene {
|
scenes: vec![json::Scene {
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
extras: Default::default(),
|
extras: Default::default(),
|
||||||
name: None,
|
name: None,
|
||||||
nodes: vec![json::Index::new(0)],
|
nodes: vec![Index::new(0)],
|
||||||
}],
|
}],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
@ -203,7 +91,7 @@ fn export(model: Model, output: Output) {
|
||||||
let writer = fs::File::create("triangle/triangle.gltf").expect("I/O error");
|
let writer = fs::File::create("triangle/triangle.gltf").expect("I/O error");
|
||||||
json::serialize::to_writer_pretty(writer, &root).expect("Serialization error");
|
json::serialize::to_writer_pretty(writer, &root).expect("Serialization error");
|
||||||
|
|
||||||
let bin = to_padded_byte_vector(vertices);
|
let bin = pad_byte_vector(buffer);
|
||||||
let mut writer = fs::File::create("triangle/buffer0.bin").expect("I/O error");
|
let mut writer = fs::File::create("triangle/buffer0.bin").expect("I/O error");
|
||||||
writer.write_all(&bin).expect("I/O error");
|
writer.write_all(&bin).expect("I/O error");
|
||||||
}
|
}
|
||||||
|
|
@ -215,9 +103,9 @@ fn export(model: Model, output: Output) {
|
||||||
header: gltf::binary::Header {
|
header: gltf::binary::Header {
|
||||||
magic: *b"glTF",
|
magic: *b"glTF",
|
||||||
version: 2,
|
version: 2,
|
||||||
length: json_offset + buffer_length,
|
length: json_offset + buffer.len() as u32,
|
||||||
},
|
},
|
||||||
bin: Some(Cow::Owned(to_padded_byte_vector(vertices))),
|
bin: Some(Cow::Owned(pad_byte_vector(buffer))),
|
||||||
json: Cow::Owned(json_string.into_bytes()),
|
json: Cow::Owned(json_string.into_bytes()),
|
||||||
};
|
};
|
||||||
let writer = std::fs::File::create("output.glb").expect("I/O error");
|
let writer = std::fs::File::create("output.glb").expect("I/O error");
|
||||||
|
|
|
||||||
22
src/lib.rs
22
src/lib.rs
|
|
@ -87,6 +87,28 @@ impl Model {
|
||||||
vtx,
|
vtx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate bounding coordinates of the model
|
||||||
|
pub fn bounding_box(&self) -> (Vector, Vector) {
|
||||||
|
let mut min = Vector::from([f32::MAX, f32::MAX, f32::MAX]);
|
||||||
|
let mut max = Vector::from([f32::MIN, f32::MIN, f32::MIN]);
|
||||||
|
|
||||||
|
for point in self.vertices() {
|
||||||
|
let p = point.position;
|
||||||
|
min.x = f32::min(min.x, p.x);
|
||||||
|
min.y = f32::min(min.y, p.y);
|
||||||
|
min.z = f32::min(min.z, p.z);
|
||||||
|
|
||||||
|
max.x = f32::max(max.x, p.x);
|
||||||
|
max.y = f32::max(max.y, p.y);
|
||||||
|
max.z = f32::max(max.z, p.z);
|
||||||
|
}
|
||||||
|
(min, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.mdl.name.as_str()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SkinTable<'a> {
|
pub struct SkinTable<'a> {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ type Result<T> = std::result::Result<T, ModelError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Mdl {
|
pub struct Mdl {
|
||||||
|
pub name: FixedString<64>,
|
||||||
pub header: StudioHeader,
|
pub header: StudioHeader,
|
||||||
pub bones: Vec<Bone>,
|
pub bones: Vec<Bone>,
|
||||||
pub body_parts: Vec<BodyPart>,
|
pub body_parts: Vec<BodyPart>,
|
||||||
|
|
@ -26,6 +27,7 @@ pub struct Mdl {
|
||||||
impl Mdl {
|
impl Mdl {
|
||||||
pub fn read(data: &[u8]) -> Result<Self> {
|
pub fn read(data: &[u8]) -> Result<Self> {
|
||||||
let header = <StudioHeader as Readable>::read(data)?;
|
let header = <StudioHeader as Readable>::read(data)?;
|
||||||
|
let name = header.name.try_into()?;
|
||||||
let mut textures = read_relative_iter(data, header.texture_indexes())
|
let mut textures = read_relative_iter(data, header.texture_indexes())
|
||||||
.collect::<Result<Vec<TextureInfo>>>()?;
|
.collect::<Result<Vec<TextureInfo>>>()?;
|
||||||
let texture_dirs_indexes =
|
let texture_dirs_indexes =
|
||||||
|
|
@ -44,6 +46,7 @@ impl Mdl {
|
||||||
|
|
||||||
let bones = read_indexes(header.bone_indexes(), data).collect::<Result<_>>()?;
|
let bones = read_indexes(header.bone_indexes(), data).collect::<Result<_>>()?;
|
||||||
Ok(Mdl {
|
Ok(Mdl {
|
||||||
|
name,
|
||||||
bones,
|
bones,
|
||||||
body_parts: header
|
body_parts: header
|
||||||
.body_part_indexes()
|
.body_part_indexes()
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ impl From<RadianEuler> for Euler<Deg<f32>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fixed length, null-terminated string
|
/// Fixed length, null-terminated string
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default, Copy)]
|
||||||
pub struct FixedString<const LEN: usize>(ArrayString<LEN>);
|
pub struct FixedString<const LEN: usize>(ArrayString<LEN>);
|
||||||
|
|
||||||
impl<const LEN: usize> TryFrom<[u8; LEN]> for FixedString<LEN> {
|
impl<const LEN: usize> TryFrom<[u8; LEN]> for FixedString<LEN> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue