MeshGroups. (Again) - #120
Conversation
Three parts: - Testing whether a point is in a triangle. - Efficient (but not guaranteed correct) testing of a point is in which triangle among those in a given mesh, with a bitmask constructed for the mesh. - Given parent mesh and parent deformation, deform child mesh. Essentially letting one parent triangle drag a child vertex that is contained in it, and this drag can be chacterized by an inverse matrix, which can be reused for efficiency. Intensive test for bitmask included (with a .png depicting the base test case), testing that bitmask yields correct test results for points with the whole test space transforming. However, as mentioned, this bitmask method is not guaranteed to be correct, thus it is hard to define a "good enough" test case. It is easy to construct counterexamples. An experimental attempt on dynamic bitmask step size is commented out cause test suites do not like it. A step size of `1` should be usable in actual rendering anyways.
I have a Puppet file with meshgroups that do not have valid mesh data. Inochi appears to handle this by creating a Mesh block with empty verts/indices, but NO uvs. Inox can handle the former but not the latter. (The origin field is unaffected, if you're wondering.)
|
So, for some reason it turns out most of me works with this PR, but not all. More notably, none of the maths stuff added by the MeshGroups PR appears to actually be used? I habitually apply all my deforms to MeshGroups, so I feel like I should be way more broken than I am without MeshGroup deforms actually applying to their children. Either that, or it's just the "dynamic" deforms that are unimplemented (which the magic circles in my eyes use, and which don't work) and the "static" ones just so happen to work by some other mechanism? Either way, I'm marking this as draft until I figure out what's up with that. Probably will need to trawl the Inochi2D source as well... |
machinery. This still doesn't work, because while MeshGroups have a DeformStack, there is nowhere to push the deforms so they never get combined and the individual Mesh parts don't get to read them. We'll need to create a separate deform scratch area for MeshGroups that the DeformStack can read and write instead. Additionall, I may want to add functionality for renderers to compute the deforms themselves, so that it can be done with GPU compute. AFAIK, nothing but the vertex shaders actually read the deforms.
* We create a DeformStack for all children of a deformable node, just in case. * We now account for child node transforms when deforming them with their parents' mesh deformations. * Mesh bitmasks used for testing are now cached across frames. * Only dynamic MeshGroups actually trigger dynamic deformation. (This is probably not true for INX files - which by the way, did you know those just load in Inox2D?) Additionally: * We account for meshes that are children of other meshes. I don't know if this actually uses the dynamic deformation code, I don't have a test case for this, but I've done this in my model. * Vertices that are on the edge of a triangle are treated as being part of the triangle in Inochi2D, but our triangle test code rejects those vertices. I HAD a test case for this as I'd accidentally triggered this behavior, but I deliberately removed it from the test to verify the behavior.
| // Meshes can impose a deform on their children anyway | ||
| comps.get::<Mesh>(ancestor).is_some() |
There was a problem hiding this comment.
A Part with Mesh doens't impose a deform but their transform on their chirldren how I understand it. Otherwise it would make any Part behave like a Meshgroup. It currently triggers
/// deform_stack.rs L100-117
if let Some(foreign_render_ctx) = node_comps.get::<TexturedMeshRenderCtx>(*source_node) {
let foreign_offset = foreign_render_ctx.vert_offset as usize;
let foreign_len = foreign_render_ctx.vert_len;
let Some((my_deform, foreign_deform)) = split_ranges(
result,
vert_offset..(vert_offset + vert_len),
foreign_offset..(foreign_offset + foreign_len),
) else {
eprintln!(
"They're disjoint?! {:?}, {:?}, {:?}",
vert_offset..(vert_offset + vert_len),
foreign_offset..(foreign_offset + foreign_len),
*source_node,
);
continue;
};
Some((my_deform, &*foreign_deform))which often gives boundary checking error inside split_ranges because vert_offset is 0.
/// render.rs L231-234
if let Some(deform_stack) = comps.get::<DeformStack>(node.uuid) {
let len = deform.len();
deform_stack.combine(node.uuid, nodes, comps, &mut deform, 0, len);
}Would you mind sharing what kind of scenario this is supposed to handle?
There was a problem hiding this comment.
So, a couple days ago, I was adding art to my model when I realized I'd made the mistake of applying a param deform directly to a Mesh. Inochi Creator doesn't have a way to convert a Mesh to a MeshGroup (that I know of) nor a way to copy the mesh data and deforms over to a new MeshGroup; so I'd have to re-rig all the art just to add a second layer to it. In desperation I just put the new art as a child Mesh of the original one and... it worked exactly as you'd expect a MeshGroup to act.
Granted, I haven't made a test case for this yet, it could just be a bug in Creator 0.8. I also don't know if it is applying static or dynamic deformation, if it's the former we technically don't need to account for it (and if so, that part of my model will probably break when 0.9 comes out).
There was a problem hiding this comment.
After further testing with my deform test I don't actually think this behavior actually happens and I'll be removing the code to support it.
The thing I saw happening in Inochi Creator had nothing to do with deforms and I confused the two things. Sorry!
|
@Speykious @mikz30 Ugh. It turns out I've hit a bit of an architectural problem. MeshGroups not only deform mesh vertices, they also deform node translations. The natural place to put such code would be in In #116 I added a partitioning scheme specifically to allow multithreading param application, but it would also work in this case: you could partition the world into "just If you don't like this idea please tell me now. |
|
...actually, to make matters worse, we're computing deforms AFTER the transform context is updated, which is too late to change transforms! Deforms have to be computed BEFORE the absolute transforms are calculated. So I have to move deform processing out of This also has some performance implications: deforms will get computed twice (on my model they take several ms alone), and since deforms can influence physics now moving them over to GPU compute (something I wanted to do eventually) is going to be way more difficult, if not impossible. (For context as to why it's difficult: moving work from the GPU to the CPU means the CPU has to wait, and GPUs aren't great at finishing things quickly. Ideally you only ever send work to the GPU. So I'd either have to move all the physics to the GPU, which is overkill, or download the deforms back to the CPU and hope the performance hit is less.) |
|
I have the MeshGroup "translate children" behavior ostensibly implemented, but it doesn't actually work because all the MeshGroups in my face that have it enabled in the .INX get exported with it disabled in the .INP. It just happens to magically work in Session. I don't know what the hell is even going on anymore 🤪 All I know is that I have magic circles in my eyes that deform correctly, until they rotate, and then moving my head left or right makes them fly wildly off course. It's like their center of rotation doesn't move along with the mesh, which is why I looked at the translation children behavior - but it CAN'T be translate children, because Inochi2D turns that off on export. |

This is an extension of #103 as the original author confirmed they are no longer interested in continuing their version of the PR.
Most of my additions are work-arounds for various error states caused by the output of Inochi Creator's exports. Specifically, if you do not add a mesh to a MeshGroup (which I do a lot), Inochi Creator will create incomplete mesh information for that MeshGroup. Inochi Session will ignore the information but Inox2D will complain and panic.
I'm not sure if this is 100% accurate, but it seems to work for what I'm doing so far. I may have to come back to this with more stringent tests.