-
Notifications
You must be signed in to change notification settings - Fork 73
Export shape with multiple disjoint meshes #118
Comments
You want to separate a shape into 'multiple bodies', and you assume that each 'body' corresponds to a single mesh. This isn't quite true. I will define a Mesh as a connected set of triangles that form a single watertight 3D surface that encloses a single 3D shape. Consider a hollow sphere, such as A Model consists of one or more Bulks. The following image is of a geared model with moving parts, consisting of 8 separate bulks. But the model cannot be disassembled, it has to be printed as a single unit: Some 3D printing file formats have the ability to contain multiple Models in a single file. So we have a 3 level hierarchy: a Bulk contains multiple Meshes, a Model contains multiple Bulks (with optional material IDs), and a file contains multiple Models. The only reliable way to decompose a shape into its component Meshes is to render the shape as a triangle set, then traverse the triangle set to identify the separate Meshes. But this probably doesn't solve your problem, because you are probably interested in one of the higher level elements that I mentioned, such as a Bulk or a Model. To fully implement the 3 level hierarchy that I described requires adding additional information to the Curv program. Which file formats support this 3 level hierarchy? That would be 3MF (https://3mf.io/). Finally, you said "I'm just curious about voxels.". This could refer to 2 different things. In Minecraft, a shape is represented as a collection of tiny cubes, called voxels. But in my notes for Curv, I most frequently use "voxel grid" to refer to a discretely sampled signed distance field: a 3 dimensional array where each element is the signed distance from that grid position to the surface of the shape. Trilinear interpolation is used to look up the signed distance for an arbitrary point [x,y,z] using the voxel grid. It's not easy to use a signed distance field (either discrete or continuous) to separate a shape into a set of disconnected components. This would be easier with a Minecraft voxel grid. |
Thanks for the insightful feedback @doug-moen. Yes, my intention was to find unconnected bulks in a model. My original thought was to traverse the voxel grid to find bulks: group each cluster of distance >=0 as a bulk. But consider this program:
You would want the blue and red as two separate bulks for 3d printing. Maybe curv needs a concept of material ID or part ID to correctly separate the bulks in this case. This could be a useful concept for the viewer too - toggle on/off part of a shape to help debugging. |
I have several ideas on how to implement this.
So then the question becomes, how do you render these shapes, and how do you convert them to triangle meshes? One thought is that the Dual Contouring meshing algorithm has direct support for multi-material meshing, whereas the OpenVDB meshing algorithm I'm using right now does not. https://people.engr.tamu.edu/schaefer/research/dualcontour.pdf I don't have access to a multi-material FDM printer, so I haven't really developed these ideas further. More development is required. |
The "part" function proposal for scad is what I was thinking. Instead of overriding parent part name, maybe it can be preserved to form an array for hierarchical parts.
In |
This strikes me as a very "researchy" project, requiring iteration over multiple designs.
Yes, but how do you export a mesh using this representation? One of my goals for mesh export is to support sharp feature detection. A cube exports as a cube (the edges and corners are not rounded off). The current meshing algorithm (from OpenVDB) has the benefit of creating defect-free meshes, and the output looks reasonable for a wide range of models. But it doesn't do sharp feature detection, which is something I want, and which libfive supports. The problem with sharp feature detection is that it can produce defective meshes, in all the algorithms I've surveyed, which means you may need to repair the mesh before importing it into OpenSCAD, and you may also need mesh repair before 3D printing, depending on the slicer and the nature of the defects. So that means I want to provide a choice of meshing algorithms: one is defect free, one does edge detection. The obvious next step is to add support for the libfive meshing algorithm. I've also considered the TMC meshing algorithm, which generates all quads (no triangles), and is defect free. TMC occasionally screws up edges and corners worse than OpenVDB, but the problem is avoided if you mesh at a high enough resolution. The main benefit of TMC is to satisfy the requirements of 3D artists who use Z-Brush for editing meshes: this tool works better if the model is all quads (or so I have been told). TMC is also interesting because it can be executed on a GPU, in which case it is very fast. TMC has a CUDA implementation, which I've fantasized about translating into Curv after I've done the WebGPU port and added support for GPU compute pipelines. So imagine a future where Curv supports OpenVDB, libfive and TMC as the 3 meshing algorithms (defaulting to OpenVDB). Here's my point: However So what is the representation of a shape? The first idea that comes into my head is that a shape contains a collection of [distance function, bounding box] pairs, one for each part. At first glance, this would require the entire shape library to be rewritten, and would add significant complication to every operation that takes shapes as arguments. Because you'd have to add boilerplate to all of these operations to iterate over the part tree. With a sufficiently clever design, it may be possible to abstract away the boilerplate. |
My original idea was to start with the distance voxel grid, segment it into multiple voxel grids using part function, and step through the array replacing bulk edges with 0 to form the isosurface. That's a half baked idea and probably wouldn't work at all with OpenVDB volumeToMesh. I realized under estimated this by a mile. |
I suppose you can also define individual parts in its own curv file. Use |
A signed distance field needs to be a continuous function, and this idea wouldn't create a continuous function. However, a minor variant of your idea creates a minecraft-style voxel grid, which each grid element contains 1 for elements inside a specified bulk, and 0 for elements outside. There are techniques for meshing a minecraft voxel grid, although I don't really know anything about them. My intuition says that a minecraft voxel grid contains less information than a continuous signed distance function, so in theory the quality of the resulting mesh should be worse than a mesh constructed from a signed distance field. In other words, your data structure requires a new meshing algorithm different from the SDF meshing algorithms I've been investigating, and the output quality may be worse. Looking at the OpenVDB documentation, the two voxel grid types are called "level set" (meaning SDF) and "fog volume" (a generalization of minecraft voxel grids where voxel values are density values between 0 and 1). It appears that |
We can start with this idea and automate it a bit. Define a "multipart shape" as a shape value with an additional field called Then you can write some helper functions to make it easier to construct a multipart shape value. |
I kind of see no reason for this. Curv supports importing files, and so you can easily have a makefile or build process which can generate either an assembly, or the individual parts. For other Code CADs my project structure of |
Describe the bug
When exporting a shape with multiple bodies, e.g. two boxes:
curv -o gltf -x row[box, box]
. Both x3d and gltf would export all polygons into a single mesh.I suppose the distance function would have no means of detecting disjoint bodies. Is it possible to see if shape has multiple bodies using the voxel grid, or with OpenVDB volumeToMesh?
Curv Program
curv -o gltf -x row[box, box]
Expected behavior
A mesh for each body.
Additiona context
This is just a nice to have. I'm just curious about voxels.
The text was updated successfully, but these errors were encountered: