Skip to content

Instantly share code, notes, and snippets.

@exjam
Created June 8, 2022 19:11
Show Gist options
  • Save exjam/5a16921d9f9a73acf4475bce55be1938 to your computer and use it in GitHub Desktop.
Save exjam/5a16921d9f9a73acf4475bce55be1938 to your computer and use it in GitHub Desktop.
rose-gltf
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