Skip to content

Commit

Permalink
fix: Allow glbs to be loaded with external assets (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
tommy-xr authored Sep 17, 2024
1 parent 9958967 commit 28dc364
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 33 deletions.
6 changes: 3 additions & 3 deletions examples/hello/hello.fs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ let init (_args: array<string>) =

let colorMaterial = Material.color(1.0f, 0.0f, 1.0f, 1.0f);
let textureMaterial = Material.texture( Texture.file("crate.png"));
let barrelModel = Model.file("ExplodingBarrel.glb");

let renderModel = (Graphics.Scene3D.model barrelModel) |> Transform.scale 1f;
// let renderModel = Model.file ("ExplodingBarrel.glb") |> Graphics.Scene3D.model |> Transform.scale 0.5f;
// let renderModel = Model.file ("vr_glove_model.glb") |> Graphics.Scene3D.model |> Transform.scale 5f;
let renderModel = Model.file ("shark.glb") |> Graphics.Scene3D.model |> Transform.scale 0.01f;

group([|
material (textureMaterial, [|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::io::Cursor;

use cgmath::{vec2, vec3, Matrix4};
use gltf::image::Format;
use gltf::{buffer::Source as BufferSource, image::Source as ImageSource};

use crate::model::{Model, ModelMesh};
use crate::texture::PixelFormat;
use crate::{
asset::{AssetCache, AssetPipeline},
geometry::IndexedMesh,
Expand All @@ -22,16 +21,57 @@ impl AssetPipeline<Model> for ModelPipeline {
_context: crate::asset::AssetPipelineContext,
) -> Model {
let cursor = Cursor::new(bytes);
let (document, buffers, images) = gltf::import_slice(cursor.get_ref()).unwrap();
let gltf = gltf::Gltf::from_slice(cursor.get_ref()).unwrap();
let document = gltf.document;
let blob = gltf.blob;

// Manually process buffers
let mut buffers_data = Vec::new();
for buffer in document.buffers() {
let data = match buffer.source() {
BufferSource::Bin => blob.as_ref().expect("No binary blob in GLB file").clone(),
BufferSource::Uri(uri) => {
panic!("External buffer: {}", uri);
}
};
buffers_data.push(gltf::buffer::Data(data));
}

// Manually process images
let mut images_data = Vec::new();
for image in document.images() {
let data = match image.source() {
ImageSource::View { view, .. } => {
// Get data from buffer view
let buffer = &buffers_data[view.buffer().index()];
let start = view.offset();
let end = start + view.length();
println!("Random image loaded: {} {}", start, end);
let buf = buffer[start..end].to_vec();
let maybe_image = image::load_from_memory(&buf);

if let Ok(image) = maybe_image {
TextureData::from_image(image)
} else {
TextureData::checkerboard_pattern(4, 4, [0, 255, 0, 255])
}
}
ImageSource::Uri { uri, .. } => {
// Manually resolve the image data
println!("External image: {}", uri);
TextureData::checkerboard_pattern(4, 4, [0, 0, 255, 255])
}
};
images_data.push(data);
}

let mut meshes = Vec::new();

for scene in document.scenes() {
print!("Scene {}", scene.index());
println!();
println!("Scene {}", scene.index());
for node in scene.nodes() {
println!("- Node: {:?}", node.name());
process_node(&node, &buffers, &images, &mut meshes);
process_node(&node, &buffers_data, &images_data, &mut meshes);
}
}

Expand All @@ -46,7 +86,7 @@ impl AssetPipeline<Model> for ModelPipeline {
fn process_node(
node: &gltf::Node,
buffers: &[gltf::buffer::Data],
images: &[gltf::image::Data],
images: &[TextureData],
meshes: &mut Vec<ModelMesh>,
) {
if let Some(mesh) = node.mesh() {
Expand Down Expand Up @@ -90,7 +130,6 @@ fn process_node(
indices.len()
);

// Parse material
// Parse material
let material = primitive.material();

Expand All @@ -111,28 +150,7 @@ fn process_node(
let source = texture_info.source();
let image = &images[source.index()];

println!("Material base color texture index: {:?}", source.index());
println!("Texture width: {:?}", image.width);
println!("Texture height: {:?}", image.height);
println!("Texture format: {:?}", image.format);
println!("Texture data length: {:?}", image.pixels.len());

// Access the bytes and format
let texture_bytes = &image.pixels;

let format = match image.format {
Format::R8G8B8 => PixelFormat::RGB,
Format::R8G8B8A8 => PixelFormat::RGBA,
_ => unimplemented!("Pixel format: {:?} not implemented", image.format),
};

let texture_data = TextureData {
bytes: texture_bytes.clone(),
width: image.width,
height: image.height,
format,
};

let texture_data = image.clone();
Texture2D::init_from_data(texture_data, TextureOptions::default())
} else {
let data = TextureData::checkerboard_pattern(4, 4, [255, 0, 255, 255]);
Expand Down
1 change: 1 addition & 0 deletions runtime/functor-runtime-common/src/scene3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ impl Scene3D {

match model_description {
ModelDescription::File(str) => {
println!("Trying to load: {}", str);
let model: Arc<AssetHandle<Model>> = render_context
.asset_cache
.load_asset_with_pipeline(scene_context.model_pipeline.clone(), str);
Expand Down
13 changes: 13 additions & 0 deletions runtime/functor-runtime-common/src/texture/texture_data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use image::DynamicImage;

use super::PixelFormat;

#[derive(Clone)]
Expand All @@ -9,6 +11,17 @@ pub struct TextureData {
}

impl TextureData {
pub fn from_image(image: DynamicImage) -> TextureData {
let bytes = image.to_rgba8();

TextureData {
bytes: bytes.to_vec(),
width: image.width(),
height: image.height(),
format: PixelFormat::RGBA,
}
}

pub fn checkerboard_pattern(width: u32, height: u32, color: [u8; 4]) -> TextureData {
let mut bytes = Vec::with_capacity((width * height * 4) as usize);

Expand Down

0 comments on commit 28dc364

Please sign in to comment.