0
$\begingroup$

I have a model that I need to simplify. The issue is that some of the cylinders/tubes that make the model have over 1000 vertices and 3000 edges. (like the one in the image bellow)

This object have 1069 vertices, 3110 edges, and 2036 faces and tris

This wasn't modeled by me. The model was originally made on CAD (if I am not mistaken) and then exported as a FBX file to be used on Blender. Another important think is that this pipe has a thickness to it, that it is not necessary. So, I need to simplify it the most possible (if I can make it a simple one, with only vertices being in the holes, the better). If it where only one, I would change it, but there are about a 1000 of this tubes, each one varying in angles, size and thickness.

What do you recommend that I do? Is the only solution really to Decimate Geometry/Decimate Modifier and Degenerate Dissolve, or can I do something that reduces more, while maintaining the appearance of a cylinder?

Edit: some of the pipes are here

$\endgroup$
5
  • 1
    $\begingroup$ Do you have as many objects as cylinders, or just a few objects made of many cylinders ? In the second case, can you separate cylinders to have just one per object ? This could help in designing a GeometryNodes modifier replacing "from CAD cylinders" in "Blender cylinders". If you would like help on this way, could you provide a simplified (just a few objects) version of your Blender file using blend-exchange.com ? $\endgroup$ Commented May 10 at 14:13
  • $\begingroup$ Hmmm .. a representative sample of your pathological cylinders shared on blend-exchange.com would help here as Stef suggests -- pick out the hardest group you can .. eg.. ones that overlap, if any.. $\endgroup$
    – Robin Betts
    Commented May 10 at 18:37
  • $\begingroup$ can you share the blend file with these thousands of cylinders? or share a few cylinders and maybe we can automate it with a python script to simplify those meshes. $\endgroup$
    – Harry McKenzie
    Commented May 11 at 8:23
  • $\begingroup$ Sorry I didn't answer before. I can't upload all the pipes, but I've uploaded a few that might be a good sample of what Its happening. $\endgroup$ Commented May 13 at 12:04
  • $\begingroup$ @StefLAncien Thanks a lot! $\endgroup$ Commented May 14 at 12:05

1 Answer 1

4
$\begingroup$

(Using Blender 3.6.8)

(NB: documentation to be continued...)

Objective

The initial configuration is a collection of hollow pipes. Each pipe is an object modelled with a dense triangular mesh, as illustrated in the following figure: Results before

The objective is to replace each mesh by a full Cylinder, keeping its dimensions (length and radius), position and orientation, as shown below: Results after

Approach

A GeometryNodes modifier is attached to a pipe object. After setup, it can be easily transferred to all the others using the menu entry Object > Link/Transfer Data > Copy Modifiers in Object Mode.

GN Main

1. Because pipes are varying in aspect ratio and size, the original mesh is centred on the object origin and it is scaled such that its largest dimension is about unity.
2. The inner surface is extracted to compute the axis of the pipe.
3. The normalized pipe is aligned with the world X axis to compute its length and radius.
4. A Cylinder Mesh Primitive node is used to make a new mesh, that is scaled, rotated and translated to replace the original one.
5. The cylinder number of vertices is a user-defined parameter as an Input attribute. For the demonstration, it is set to 8.

GeometryNodes modifier

(NB: In the following pictures of node groups, dark blue frames are for visualisation only. They are not part of the process.)

Normalizing the data

GN Normalize

1. The Bounding Box of the tube is used to centre, then to normalize its vertices position, to minimize round-off errors. Because Translation is applied after Scaling by a Transform Geometry node, two such nodes are required.
2. The centre of the BoundingBox (labelled $C$) is computed as the mid-point between opposite corners. This point should move to the object origin (labelled $O$) (NB: Objects origin is set at the world origin in the provided test-case). This is why the scaling factor is negative, to compute the Translation (i.e. $\vec{CO}$) applied by the first Transform Geometry node.
3. A characteristic dimension of the tube is computed as the Length (labelled $\lambda$) between the BoundingBox opposite corners. Vertices position is scaled by $\frac{1}{\lambda}$, such that (X,Y,Z) coordinates are between -1 and +1, using the second Transform Geometry node.
4. The tube axis identification requires one point of this axis (labelled $G$). Assuming that the tube inner and outer surfaces are evenly discretized, such a point position can be reasonably approximated as the averaged Position of all vertices. So $\vec{OG}$ is computed as the Mean value returned by an Attribute Statistic node.
5. Using a third Transform Geometry node, the scaled tube vertices are Translated by $\vec{GO}$, such that $G$ moves to the object origin ($O$). Consequently the Output Geometry is such that the sought axis goes through the origin of the original tube.
6. To move the vertices of the cylinder generated at the end of the process back to the position of the original tube, the overall Translation vector $\vec{OG}$ must be preserved. It is computed by a Multiply Add followed by a Scale vector math node as $-(\vec{CO}+\lambda \vec{GO})$, as a consequence of the two successive translations. The $\lambda$ factor is required because $\vec{GO}$ is computed at step 4 after the scaling by $\frac{1}{\lambda}$.
7. To scale the cylinder generated at the end of the process back to the size of the original tube, $\lambda$ must be preserved also.

Extracting the inner surface

GN Extract

1. Only faces inside the tube are kept using a Delete Geometry node set in Face domain.
2. Its Selection mask is based on a Raycast node applied to the tube onto itself, specifying the Normal as Ray Direction. Because the normal of top, bottom and outer faces are pointing away from the object, the output socket Is Hit is False for such faces. So a Not boolean node is inserted to tag these as True for deletion.
3. The Source Position must be slightly shifted along the Normal (labelled $\vec{n}$) to avoid confusion between source and target. It is computed by a Multiply Add vector math node adding $\varepsilon \vec{n}$ to the face centre Position, with $\varepsilon=10^{-5}$.
4. Because the geometry is previously normalized, the Ray Length can not exceed 1. This value is hardcoded.

Extracting the axis

GN Axis

It is assumed that after the data normalization, the tube axis is going through the object origin $O$. Let $\vec{d}$ be a unitary vector aligned with this axis. Let $M_i$ be the centre of a face extracted previously, and $\vec{n_i}$ be the normal to this face. $\vec{d}$ and $\vec{n_i}$ are orthogonal. Consequently, $\vec{OM_i} = (\vec{OM_i} \cdot \vec{d})\ \vec{d} + (\vec{OM_i} \cdot \vec{n_i})\ \vec{n_i}$. Let $D_i$ be the point defined as $\vec{OD_i} = (\vec{OM_i} \cdot \vec{d})\ \vec{d} = \vec{OM_i} - (\vec{OM_i} \cdot \vec{n_i})\ \vec{n_i}$. It would be on the tube axis, notwithstanding discretization and round-off errors. So an averaged direction $\vec{OD}$ is favoured. It is defined as $\vec{OD} = \sum_i^N s_i \vec{OD_i}$, with $N$ the number of faces and $s_i=\pm 1$. The factor $s_i$ is required because point $O$ being at half of the axis, vectors $\vec{OD_i}$ of both halves of the tube are pointing in opposite directions. Without this, the average could be zero. (NB: If needed, $\vec{d}$ could be computed as $\vec{d} = {\vec{OD}}/{\|\vec{OD}\|}$).
1. $\vec{OD_i}$ is computed by a sequence of Dot Product, Multiply and Multiply Add nodes. This computation is triggered by a Capture Attribute node set in Face domain.
2. To compute $s_i$, the opposite corners of the box bounding the $D_i$ point cloud are recovered with an Attribute Statistic node set in Face domain. By subtracting these, a reference direction $\vec{a}$, which might be different from $\vec{d}$, is computed. Eventually, $s_i$ is defined as the sign of $\vec{a} \cdot \vec{OD_i}$.
3. $\vec{OD} = \sum_i^N s_i \vec{OD_i}$ is computed by an Accumulate Field node set in Face domain through its Total socket. As this output is the same for all faces, a single value is captured using a Sample Index node set in Face domain, where the Index being irrelevant is hardcoded as 0.
4. To align the cylinder of X axis generated at the end of the process back with the axis of the original tube, $\vec{d}$ is converted in a Rotation using an Align Euler to Vector node. Its setup is such that the X axis is aligned with $\vec{d}$.

Aligning along X axis

GN Align

Fitting a cylinder and transforming it back

GN fit

Resources

$\endgroup$
3
  • $\begingroup$ Thanks a lot for your help! :D $\endgroup$ Commented May 17 at 12:00
  • $\begingroup$ One more question: can I use any object for this or it has to be a basic form (like a cylinder or a cube)? Either way, this was of a great help and worked beautifully! Thanks again! $\endgroup$ Commented May 20 at 11:36
  • $\begingroup$ Some ideas are general, but others are specific. For example, it helps to know if there is a symmetry axis, or a mirror plane. I am afraid that this proposal relies too much on the cylindrical shape to be easily extended to any object. But feel free to ask others questions for other shapes, if needed. $\endgroup$ Commented May 20 at 11:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .