Issue with clip planes

I’m trying to add clip plane support to the shader generator (because I urgently need it) so I’m also adding a clipplane_# shader parameter (like tex_#) where # is the number of the clip plane.

When I do this via the ClipPlaneAttrib interface, however, it seems that get_num_on_planes() suddenly returns 0 when viewing at certain angles.
Here’s my code in GSG::fetch_specified_part:

  case Shader::SMO_clipplane_x: {
    const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot()));
    int planenr = atoi(name->get_name().c_str());
    const NodePath &np = cpa->get_on_plane(planenr);
    nassertr(!np.is_empty(), &LMatrix4f::zeros_mat());
    nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4f::zeros_mat());
    Planef p = DCAST(PlaneNode, np.node())->get_plane();
    t = LMatrix4f(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
    return &t;
  }

I made a test shader that calculates if a certain pixel is being clipped based on the plane equation (which is passed through clipplane_0 parameter).
This works great. My model is being clipped perfectly - but only when looking from certain angles. From other angles (even with the model and its clipped part visible) I get an assertion failure here that on plane 0 does not exist.

When I pass the Plane object directly (without doing it through ClipPlaneAttrib interface) it always works.

Is there something about the ClipPlaneAttrib interface I should know?

Also, when adding this code:

render.node().setBounds(OmniBoundingVolume())
render.node().setFinal(1)

It always fails - it says no on-planes are there.

Maybe frustrum culling is interfering with the clipping somehow?

Yes, frustum culling tries to be smart with respect to the clip planes: if it determines that an object’s bounding volume is entirely outside the clip plane, it removes that particular plane from the attrib. Similarly, if it determines that the bounding volume is entirely inside the clip plane (and fully clipped), it doesn’t bother drawing the object.

Though I’m not sure I understand the second case: after basically disabling frustum culling, it should have determine that all objects intersect all clip planes partially, and it should have left the clip plane attribs alone. That might be a bug. Does it appear to work correctly when the shader generator is not in use?

Still, all this works by modifying the RenderState, so I don’t see how the shader generator is getting burned here–it should simply end up recompiling a new shader every time it changes the RenderState. In each case, the shader generator should end up with the same number of planes it had to start with. Of course, you will need to write the shader generator to properly handle a ClipPlaneAttrib that has 0 planes (which should be handled the same as no planes at all).

David

The problem is that the clip plane also disappears while the clipped surface is still in view, resulting in part of the model ‘popping’ in.
Not using the ShaderGenerator, I made a sample shader with a clipplane_0 input. I can’t seem to reproduce this behavior without.

Of course I will write the ShaderGenerator to handle 0 planes correctly - but that still results in parts popping on and off.

Are you sure that parts pop on and off? If the plane is clipping properly, you shouldn’t see any popping, because the plane is not removed until it is determined to be no longer intersecting the object. I don’t see any popping when there is no shader in effect at all, for instance. Isn’t it the same for you?

Other than the popping you report, I guess the problem with the automatic clip plane removal is that when you have a shader that accepts a clipplane_0 input, that shader will require that clip plane to be always present, even if it was determined not to be relevant; but there’s no guarantee that the clip plane will still be there by the time it reaches the bottom of the traversal.

You can configure “clip-plane-cull 0” to disable this automatic removal of irrelevant clip planes. We could make the cull traverser disable this in general whenever a shader is applied, though that wouldn’t help if the shader is applied at a level below the clip plane itself.

David

With the config var you suggested, it works fine, thanks.
I am 100% sure that the shader is handling all transforms and plane calculations well, since the clipping is performed exactly the same when the shader is disabled (from some angles, that is)
Here’s when looking from the panda model closeup: (correct)
pro-rsoft.com/screens/clip-closeup.png
When looking far away, without the no-cull config var: (incorrect)
pro-rsoft.com/screens/clip-closeup-cull.png
When looking far away, with your suggested config var: (correct)
pro-rsoft.com/screens/clip-closeup-nocull.png

Could it be that the clip plane culling is not handling the transforms correctly?

It’s certainly possible that the clip plane culling is doing something wrong; there have been bugs with this code in the past. But I’m pretty sure it’s been working correctly for at least the past 12 months or so; we’ve been using clip planes at various transforms with no problems.

Does it exhibit the same popping behavior when you run without shaders at all?

David

Nope. I can’t reproduce it without the shader. That’s the weird thing.
Here’s my code now in GSG::fetch_specified_part:

  case Shader::SMO_clipplane_x: {
    const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot()));
    int planenr = atoi(name->get_name().c_str());
    if (planenr >= cpa->get_num_on_planes()) {
      cerr << "No clip\n";
      t = LMatrix4f::zeros_mat();
    } else {
      cerr << "Yes clip\n";
      const NodePath &np = cpa->get_on_plane(planenr);
      nassertr(!np.is_empty(), &LMatrix4f::zeros_mat());
      nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4f::zeros_mat());
      Planef p = DCAST(PlaneNode, np.node())->get_plane();
      t = LMatrix4f(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
    }
    return &t;
  }

(Which happens to run every frame, btw, for some weird reason.)
But anyways, it says “No clip” when the camera is zoomed out, while it says “Yes clip” when the camera is zoomed in. (Yes, I’m sure ‘name’ is always ‘0’.)
So, for some reason, get_num_on_planes on the _target_rs decides to be returning 0 from certain angles.

Are there multiple Geoms in your model? Maybe the fetch_specified code is only getting called for the first Geom, instead of for each Geom, and thus it fails according to the placement of the first Geom encountered, relative to the clip plane.

David

Yes - a flattenStrong on the panda seems to work.
How can I check if that is the issue? fetch_specified is invoked by glShaderContext.cxx which knows nothing about geoms.

Well, this might be a fundamental problem with fetch_specified pulling attribs out of the RenderState. If fetch_specified is called only once per shader and then cached, it will fail if the shader is applied again with a different RenderState, since the result will be different with different RenderStates.

Is this the case? I haven’t looked at the logic for this, but it seems likely.

If this is indeed the case, we’d have to either make the caching a whole lot smarter, or forbid fetching items directly out of the state like this.

David

I don’t think so. The caching is pretty smart.
When set_state_and_transform detects a change in the attribs, it tells the shader context to re-issue the parameters, (e.g. if MaterialAttrib has changed, current_shader_context->issue_parameters(this, Shader::SSD_material) is called).
Then, the shader context loops through the parameter that the shader expects, and if it finds one that matches the SSD_material, it asks the GSG to fetch
the _specified value and sends that to the GPU.

But at least I can continue working on the clip plane support with the config var workaround. Still, there must be a bug somewhere.

Hmm. I just integrated clip planes entirely into the Shader Generator, and suddenly can’t seem to reproduce the evil disappearing-bug. That’s weird - considering the shader looks almost the same.
Anyways, it seems to work now, so I guess I won’t complain until I run into the issue again. Thanks for your help!

Just in case you might be for some curious reason interested in seeing a panda that was viciously beheaded using generated shaders:
pro-rsoft.com/screens/clip-planes.png