Clipping/Compressing parts of FBX object outside of another FBX object

I have a scene with a fish in a bowl. Both objects are two separate FBXs.

bowl.fbx (121.3 KB)
fish.fbx (1.8 MB)

The fish is bigger than the bowl, and some parts are sticking out (colliding with the bowl), I need to remove these parts.


I saw that the way to do the clipping is thorough clipping planes with multiple examples in the docs but I am not sure where to start with two FBX files.

Could someone please point me in the right direction?

I think the excellent three-mesh-bvh
library may be what you want. :slight_smile:

1 Like

Although! It won’t work exactly like you want with the meshes you’ve shown. You will need to use another mesh that is just the interior portion of the bowl… (think like… the solid dome shape of water that fills the bowl… )
and that mesh will be used to clip the fish to the bowl interior.

2 Likes

Ok so if I create a mesh that represents the interior of the bowl, how can I use it as clippingPlanes of the fish material?

For instance, this is how I load the fish FBX:

const Fish = () => {
  const fbx = useFBX("/assets/3d/fish.fbx");

  useEffect(() => {
    fbx.traverse((child) => {
      const obj = child as THREE.Mesh;
      if (obj.isMesh) {
        obj.material = new THREE.MeshStandardMaterial({
          roughness: 0,
          color: "lightblue",
          clippingPlanes: // Bowl interior mesh?
        });
      }
    });
  }, [fbx]);

  return <primitive object={fbx} />;
};

Can I create x amount of planes and position them and shape them as the interior of the bowl? In theory yes, but I would need this to be done programmatically, based on the shape and size of the bowl.

I need to change the size and shape of the bowl in real time and clip the outstanding pieces of the fish accordingly.

I’m not sure that clipping planes will work. There are a limited number of them available, and it would be difficult to generate them… and the clipping doesn’t produce “caps” so you would see through to the interior of the fish model.

I was suggesting you create the bowl interior as a mesh.
Then take the fish and the bowl… and create a new mesh from the intersection between the bowl interior mesh and fish mesh, using mesh-bvh-csg… as in this example:

https://gkjohnson.github.io/three-bvh-csg/examples/bundle/geometry.html

“Complex model CSG”

from here: GitHub - gkjohnson/three-bvh-csg: A flexible, memory compact, fast and dynamic CSG implementation on top of three-mesh-bvh

2 Likes

I’m sorry to piggy-back this discussion.

In some (rare) cases, if the outside object has a shape, that can be expressed as an equation, it might be possible to squash the internal object. I understand, this is different from clipping. Nevertheless, I had fun making this demo and it might be helpful if the outer object is a pre-fixed-sized opaque bowl.

https://codepen.io/boytchev/full/JjVWZxX

image

5 Likes

haha this is excellent. also has the nice property of preserving the surface details.

1 Like

Thank you for sharing this interesting approach which might work for me!
I recreated your example in React Three and worked on understanding it, but I’m still missing something.
https://codesandbox.io/p/sandbox/fbx-object-compression-hz5zdl

  • I removed the bowl and saw that it was not used as the reference object to “compress shape” the tractor, but the tractor still gets compressed following the bowl shape.

  • I can see that you’re setting a constant after which the tractor should stop moving down, which I rewrote in my code like this.
      if (tractorRef.current.position.y > -1.1) {
        tractorRef.current.position.y -= 0.001;
      }
  • This is then directly connected to the main logic which is this one.
    scene.traverse((child) => {
      const position = (child as THREE.Mesh).geometry?.getAttribute("position");
      if (position) {
        for (let i = 0; i < position.count; i++) {
          v.fromBufferAttribute(position, i);
          child.localToWorld(v);
          if (v.y > 0) {
            continue;
          }
          if (v.length() <= 1) {
            continue;
          }
          v.setLength(1);
          child.worldToLocal(v);
          position.setXYZ(i, v.x, v.y, v.z);
        }
        position.needsUpdate = true;
      }
    });

You loop through each vertex in the position of the mesh. And for each vertex you

  • Get its relative position to the world with child.localToWorld(v)
  • Check if the y of the vertex now is greater than 0 and skip the loop step (this is because that vertex position must have already been changed?)
  • It checks if the length of the vertex’s position vector is less than or equal to 1 (here I start getting lost).

The next code lines move the vertexes:

          v.setLength(1);
          child.worldToLocal(v);
          position.setXYZ(i, v.x, v.y, v.z);
  1. But how are you achieving the bowl-like shape of it?

  2. Is it possible to have a dynamic shape if the bowl is for example squared?

  3. Is it possible also to compress the parts that are exiting a dynamic value based on the x-axis, similar to what you’re doing on (v.y > 0), for example if the tractor would be way bigger than the bowl and touch the walls and not only the bottom?

Thank you in advance!

https://gkjohnson.github.io/three-bvh-csg/examples/bundle/geometry.html

The bowl is not used. The model is compresses in respect to an imaginary sphere (as big as the bowl is). Vertices outside the imaginary sphere are pushed towards its center, so they are not outside.

It depends what does “dynamic” mean. If the shape is animated in real-time, then the calculation (pushing) of vertices must be done each frame. If the shape is different, you have to make different calculations. Generally, the algorithm is: if a vertex it outside some shape, then move it to be inside this shape.

It is up to you to decide how to compress the object. Here is a bigger tractor, smashed into a ball (like a pokemon). Not a beautiful tractor. It must be painful to it. I feel sorry for it.

https://codepen.io/boytchev/full/OJGgGyr

image

2 Likes

Exactly, you articulated it perfectly. This is what I am trying to achieve. I will be loading more complex models (FBX files) with different shapes that will play the role of the sphere in your example.

I am still blind to where exactly this imaginary sphere geometry is defined in your code.

It’s a small price to pay for the acquirement of knowledge.

I tried with the following result:
https://codesandbox.io/p/sandbox/fish-bowl-csg-5m6x2q

In my specific case, I won’t have a third mesh, it’s always two different FBX files, one is the wrapper/container (the bowl in our example) and the other one is the content (the fish).

Then you don’t really have enough information to solve the problem. You can subtract a bowl from the fish, but the parts of the fish that are outside the bowl will remain. You have to have some sort of representation of just the volume you want to clip something to.

If you could describe what/why you are actually building, perhaps we could guide you toward a better solution.
I don’t think you’re really trying to clip fish to bowls, are you?

Fish clipping happens in real life:

4 Likes

fair point. closest solution to that I can think of is like… ammo softbody or something?

1 Like

Nowhere. There is no any geometry for this. That’s why I call it “imaginary”. The sphere is at (0,0,0) and has radius=1. The sphere is used here:

Lem me translate this code into English:

2 Likes

Guilty as charged. I can’t reveal specific details… yet.
But let me try to do my best.

  • I will be loading two different models with an FBX/GLB file.
  • I will position both models one over the other.
  • One model (the container) is always going to be bigger than the other model (and I already know which one is the container on load).
  • The smaller model will always have a shape with some parts that will come out of the bigger model.

  • Users can rotate the scene and see the smaller model inside of the holes of the bigger one.

Screenshot 2024-03-27 at 18.04.07

  • The models will be way more complex than these examples, each model around 200k vertices.

Considerations:

  1. I am achieving “some results” with CSG when using a third model (basically duplicating the container model) but in my case, the shapes are complex and rounded, therefore I get some rough imprecise cuts (some parts not being cut and some still being there). Similar to what you see in my fish example here.

  1. The idea that PavelBoytchev proposed actually might be the most precise one since I get to push, one by one, the “outside” vertices of the smaller model inside the inner space of the bigger mode.

  2. Thank you for clarifying this and for explaining your code.

But how can I know if a vertex v, when looping through the geometry of the smaller model, is outside the bigger model?

As I said in the beginning of my first post here:

So, I’m using the equation of the shape, not the shape itself.

For your case, one not robust approach is to cast a ray from each vertex towards the center of the outer object (but not beyond the center). If there is intersection, move the vertex at the point of intersection. If there is no intersection, keep the vertex as it is. It may not work with any shape, but is slow.

1 Like