Skip to content

Conversation

nicoabie
Copy link

@nicoabie nicoabie commented Apr 26, 2025

  • Addresses issue Ability to pass multiple materials to a Mesh #86
  • decouples creating a mesh from loading a gltf file. a file can have multiple meshes and before everything was done as if it was only one merging them.
  • now there an api to load the materials corresponding to each mesh of the file and you pass those materials when creating the corresponding mesh
image

std.debug.print("amount of materials {d} \n", .{materials.items.len});

mesh1 = mesh.Mesh.initFromData(allocator, gltf_data, 0, .{ .materials = materials });
// mesh2 = mesh.Mesh.initFromData(allocator, gltf_data, 1, .{ .materials = materials });
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the CesiumMilkTruck has two meshes and the second mesh has two materials with baseColorFactor instead of baseColorTexture and I don't know yet how to create those


std.debug.print("Loading material: {?s} \n", .{zcgltf_material.?.name});

const base_color_texture = zcgltf_material.?.pbr_metallic_roughness.base_color_texture.texture;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only able to do base_color_texture for now


self.length = length;

// here
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was important at some point, need to remove :P

};

/// A packed mesh vertex
/// This structure is tied to sokol, changing or adding any type breaks rendering as this is transformed into a sokol vertex_buffer
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is very important

@nicoabie nicoabie force-pushed the multiple-materials branch from 6e6e25e to 4f146d8 Compare April 26, 2025 22:31

self.mesh.material.state.params.joints = &self.joint_locations;
graphics.drawWithMaterial(&self.mesh.bindings, &self.mesh.material, cam_matrices, model_matrix);
// TODO check this because was from mesh only had one material
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this I have no idea, need guidance

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The params are the uniforms that get passed to the shader - since each material is a new draw call each material would also need to have their joint state updated as well otherwise only the first material would be animated.


// Load our mesh!
mesh_test = mesh.Mesh.initFromFile(delve.mem.getAllocator(), "assets/meshes/SciFiHelmet.gltf", .{ .material = material });
mesh_test = mesh.Mesh.initFromData(delve.mem.getAllocator(), gltf_data, 0, .{ .materials = materials });
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change from 'initFromFile' to 'initFromData' is making this API much harder to use for everyone that wants to load a mesh. My thinking is that the GLTF loading should be abstracted, the end user of the API should not need to worry about the specifics.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the point you are making. Before this change delve assumed that a GLTF file contains a single mesh with a single material and that is not the case.

Should we have another API like Model that has a initFromFile and behind the scenes uses a lower level Mesh?

pub fn on_cleanup() !void {
material.deinit();
cube1.materials.deinit();
cube1.deinit();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deinitializing a mesh really should deinitialize the materials array inside of it - the end user of the API should not have to worry about the internals of a mesh

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Model could be responsible for doing this.

@Interrupt
Copy link
Owner

Thinking more about this, maybe it would be easier to refactor things to rename the current 'Mesh' struct to 'SubMesh' instead, and then make a new top level 'Mesh' object that is itself an ArrayList of SubMesh objects. It makes sense for each SubMesh to own the one material they are going to draw with anyway, since that seems like a 1:1 mapping.

Then the GLTF loading could pull each SubMesh + Material out of the file, and drawing the Mesh would be as easy as drawing each SubMesh individually with the same shared draw matrices.

@nicoabie
Copy link
Author

nicoabie commented May 7, 2025

Thinking more about this, maybe it would be easier to refactor things to rename the current 'Mesh' struct to 'SubMesh' instead, and then make a new top level 'Mesh' object that is itself an ArrayList of SubMesh objects. It makes sense for each SubMesh to own the one material they are going to draw with anyway, since that seems like a 1:1 mapping.

Then the GLTF loading could pull each SubMesh + Material out of the file, and drawing the Mesh would be as easy as drawing each SubMesh individually with the same shared draw matrices.

Yes, only thing to note here is that there is no concept of SubMesh in GLTF

But if you prefer SubMesh and Mesh instead of Mesh and Model it is fine by me.

@Interrupt
Copy link
Owner

Yeah, to me 'Mesh / SubMesh' is more clear than 'Mesh / Model' as it seems like people use Mesh and Model interchangeably, making it confusing to talk about.

@nicoabie
Copy link
Author

Thinking more about this, maybe it would be easier to refactor things to rename the current 'Mesh' struct to 'SubMesh' instead, and then make a new top level 'Mesh' object that is itself an ArrayList of SubMesh objects. It makes sense for each SubMesh to own the one material they are going to draw with anyway, since that seems like a 1:1 mapping.

Then the GLTF loading could pull each SubMesh + Material out of the file, and drawing the Mesh would be as easy as drawing each SubMesh individually with the same shared draw matrices.

One thing I didn't notice when I read this is that the SubMesh deals with many materials, not only one. Other than that is all good.

"meshes":[
		{
			"name":"gp_2024_sf24evo_lod_c",
			"primitives":[
				{
					"attributes":{
						"POSITION":0,
						"NORMAL":1,
						"TEXCOORD_0":2
					},
					"indices":3,
					"material":0
				},
				{
					"attributes":{
						"POSITION":4,
						"NORMAL":5,
						"TEXCOORD_0":6
					},
					"indices":7,
					"material":1
				},
			]
		},
		{
                   ...
                 }
]
		

That is a partial example of a gltf file where the first mesh (for us the first SubMesh) has many materials through primitives

@Interrupt
Copy link
Owner

I've come around on this, I think keeping Mesh as-is could be fine, and then we could add a new Model like you suggested that is just a collection of Mesh instances to render.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants