Created
June 8, 2022 19:11
-
-
Save exjam/5a16921d9f9a73acf4475bce55be1938 to your computer and use it in GitHub Desktop.
rose-gltf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use bytes::{BufMut, BytesMut}; | |
use gltf_json as json; | |
use json::validation::Checked::Valid; | |
use rose_file_readers::{RoseFile, VfsFile, ZmsFile}; | |
use std::{borrow::Cow, collections::HashMap, path::PathBuf}; | |
fn align_to_multiple_of_four(n: &mut u32) { | |
*n = (*n + 3) & !3; | |
} | |
fn main() { | |
let matches = clap::Command::new("rose-gltf") | |
.arg(clap::Arg::new("in-file")) | |
.arg(clap::Arg::new("out-file")) | |
.get_matches(); | |
let in_file_path = PathBuf::from( | |
matches | |
.value_of("in-file") | |
.expect("No input file specified"), | |
); | |
let out_file_path = PathBuf::from( | |
matches | |
.value_of("out-file") | |
.expect("No output file specified"), | |
); | |
let input_file_buffer = std::fs::read(in_file_path).expect("Failed to read input file"); | |
let zms = <ZmsFile as RoseFile>::read( | |
(&VfsFile::Buffer(input_file_buffer)).into(), | |
&Default::default(), | |
) | |
.expect("Failed to parse ZMS"); | |
let vertex_count = zms.position.len(); | |
let mut accessors = Vec::new(); | |
let mut attributes_map = HashMap::new(); | |
let mut vertex_data_stride = 0; | |
if !zms.position.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("Position".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count 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, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Positions), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 3; | |
} | |
if !zms.normal.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("Normal".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count 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, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Normals), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 3; | |
} | |
if !zms.tangent.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("Tangent".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count 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, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Tangents), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 3; | |
} | |
if !zms.color.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("Color".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec4), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Colors(0)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 4; | |
} | |
if !zms.uv1.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("UV1".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec2), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::TexCoords(0)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 2; | |
} | |
if !zms.uv2.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("UV2".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec2), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::TexCoords(1)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 2; | |
} | |
if !zms.uv3.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("UV3".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec2), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::TexCoords(2)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 2; | |
} | |
if !zms.uv4.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("UV4".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec2), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::TexCoords(3)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 2; | |
} | |
if !zms.bone_weights.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("BoneWeights".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::F32, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec4), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Weights(0)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 4 * 4; | |
} | |
if !zms.bone_indices.is_empty() { | |
let attribute_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("BoneIndices".into()), | |
buffer_view: Some(json::Index::new(0)), | |
byte_offset: vertex_data_stride, | |
count: vertex_count as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::U16, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Vec4), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
attributes_map.insert( | |
Valid(json::mesh::Semantic::Joints(0)), | |
json::Index::new(attribute_index), | |
); | |
vertex_data_stride += 2 * 2; | |
} | |
let mut mesh_data = BytesMut::with_capacity(8 * 1024 * 1024); | |
for i in 0..zms.position.len() { | |
if !zms.position.is_empty() { | |
mesh_data.put_f32_le(zms.position[i][0]); | |
mesh_data.put_f32_le(zms.position[i][2]); | |
mesh_data.put_f32_le(-zms.position[i][1]); | |
} | |
if !zms.normal.is_empty() { | |
mesh_data.put_f32_le(zms.normal[i][0]); | |
mesh_data.put_f32_le(zms.normal[i][2]); | |
mesh_data.put_f32_le(-zms.normal[i][1]); | |
} | |
if !zms.tangent.is_empty() { | |
mesh_data.put_f32_le(zms.tangent[i][0]); | |
mesh_data.put_f32_le(zms.tangent[i][2]); | |
mesh_data.put_f32_le(-zms.tangent[i][1]); | |
} | |
if !zms.color.is_empty() { | |
mesh_data.put_f32_le(zms.color[i][0]); | |
mesh_data.put_f32_le(zms.color[i][1]); | |
mesh_data.put_f32_le(zms.color[i][2]); | |
mesh_data.put_f32_le(zms.color[i][3]); | |
} | |
if !zms.uv1.is_empty() { | |
mesh_data.put_f32_le(zms.uv1[i][0]); | |
mesh_data.put_f32_le(zms.uv1[i][1]); | |
} | |
if !zms.uv2.is_empty() { | |
mesh_data.put_f32_le(zms.uv2[i][0]); | |
mesh_data.put_f32_le(zms.uv2[i][1]); | |
} | |
if !zms.uv3.is_empty() { | |
mesh_data.put_f32_le(zms.uv3[i][0]); | |
mesh_data.put_f32_le(zms.uv3[i][1]); | |
} | |
if !zms.uv4.is_empty() { | |
mesh_data.put_f32_le(zms.uv4[i][0]); | |
mesh_data.put_f32_le(zms.uv4[i][1]); | |
} | |
if !zms.bone_weights.is_empty() { | |
mesh_data.put_f32_le(zms.bone_weights[i][0]); | |
mesh_data.put_f32_le(zms.bone_weights[i][1]); | |
mesh_data.put_f32_le(zms.bone_weights[i][2]); | |
mesh_data.put_f32_le(zms.bone_weights[i][3]); | |
} | |
if !zms.bone_indices.is_empty() { | |
mesh_data.put_u16_le(zms.bone_indices[i][0]); | |
mesh_data.put_u16_le(zms.bone_indices[i][1]); | |
} | |
} | |
let vertex_data_length = mesh_data.len(); | |
for i in 0..zms.indices.len() { | |
mesh_data.put_u16_le(zms.indices[i]); | |
} | |
let index_data_length = mesh_data.len() - vertex_data_length; | |
let mesh_data_buffer = json::Buffer { | |
name: Some("MeshDataBuffer".into()), | |
byte_length: mesh_data.len() as u32, | |
extensions: Default::default(), | |
extras: Default::default(), | |
uri: None, | |
}; | |
let mesh_data_buffer_byte_length = mesh_data_buffer.byte_length; | |
let vertex_data_buffer_view = json::buffer::View { | |
name: Some("VertexDataBufferView".into()), | |
buffer: json::Index::new(0), | |
byte_length: vertex_data_length as u32, | |
byte_offset: None, | |
byte_stride: Some(vertex_data_stride), | |
extensions: Default::default(), | |
extras: Default::default(), | |
target: Some(Valid(json::buffer::Target::ArrayBuffer)), | |
}; | |
let index_data_buffer_view = json::buffer::View { | |
name: Some("IndexDataBufferView".into()), | |
buffer: json::Index::new(0), | |
byte_length: index_data_length as u32, | |
byte_offset: Some(vertex_data_length as u32), | |
byte_stride: Some(2), | |
extensions: Default::default(), | |
extras: Default::default(), | |
target: Some(Valid(json::buffer::Target::ArrayBuffer)), | |
}; | |
let indices_accessor_index = accessors.len() as u32; | |
accessors.push(json::Accessor { | |
name: Some("IndicesAccessor".into()), | |
buffer_view: Some(json::Index::new(1)), | |
byte_offset: 0, | |
count: zms.indices.len() as u32, | |
component_type: Valid(json::accessor::GenericComponentType( | |
json::accessor::ComponentType::U16, | |
)), | |
extensions: Default::default(), | |
extras: Default::default(), | |
type_: Valid(json::accessor::Type::Scalar), | |
min: None, | |
max: None, | |
normalized: false, | |
sparse: None, | |
}); | |
let primitive = json::mesh::Primitive { | |
attributes: attributes_map, | |
extensions: Default::default(), | |
extras: Default::default(), | |
indices: Some(json::Index::new(indices_accessor_index)), | |
material: None, | |
mode: Valid(json::mesh::Mode::Triangles), | |
targets: None, | |
}; | |
let mesh = json::Mesh { | |
name: Some("Mesh".into()), | |
extensions: Default::default(), | |
extras: Default::default(), | |
primitives: vec![primitive], | |
weights: None, | |
}; | |
let node = json::Node { | |
name: Some("Node".into()), | |
camera: None, | |
children: None, | |
extensions: Default::default(), | |
extras: Default::default(), | |
matrix: None, | |
mesh: Some(json::Index::new(0)), | |
rotation: None, | |
scale: None, | |
translation: None, | |
skin: None, | |
weights: None, | |
}; | |
let root = json::Root { | |
accessors, | |
buffers: vec![mesh_data_buffer], | |
buffer_views: vec![vertex_data_buffer_view, index_data_buffer_view], | |
meshes: vec![mesh], | |
nodes: vec![node], | |
scenes: vec![json::Scene { | |
name: Some("Scene".into()), | |
extensions: Default::default(), | |
extras: Default::default(), | |
nodes: vec![json::Index::new(0)], | |
}], | |
..Default::default() | |
}; | |
// Data must be padded to 4 | |
while mesh_data.len() % 4 != 0 { | |
mesh_data.put_u8(0); | |
} | |
let json_string = json::serialize::to_string(&root).expect("Serialization error"); | |
let mut json_offset = json_string.len() as u32; | |
align_to_multiple_of_four(&mut json_offset); | |
let glb = gltf::binary::Glb { | |
header: gltf::binary::Header { | |
magic: *b"glTF", | |
version: 2, | |
length: json_offset + mesh_data_buffer_byte_length, | |
}, | |
bin: Some(Cow::Owned(mesh_data.to_vec())), | |
json: Cow::Owned(json_string.into_bytes()), | |
}; | |
let writer = std::fs::File::create(out_file_path).expect("I/O error"); | |
glb.to_writer(writer).expect("glTF binary output error"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment