[Committed] TestClipState update

Hey all,



So I had to figure out exactly how to use a clip state; and it was surprisingly hard to find a straight answer, even from 3 books, the jME test and google…   My next decision was to take the ClipStateTest and modify it so I could play around with the variables and try to figure it out; surprise surprise its just the plane normal and distance (from orgin).  Anyways, it is probably WAY overkill but this modified test shows exactly how to use a clip plane and I thought it might be good to update the current test to help others.  What do you think??


Index: TestClipState.java
--- TestClipState.java Base (BASE)
+++ TestClipState.java Locally Modified (Based On LOCAL)
@@ -35,64 +35,185 @@
 import com.jme.app.SimpleGame;
 import com.jme.bounding.BoundingSphere;
 import com.jme.image.Texture;
+import com.jme.input.FirstPersonHandler;
+import com.jme.input.KeyBindingManager;
+import com.jme.input.KeyInput;
+import com.jme.input.MouseInput;
+import com.jme.math.FastMath;
 import com.jme.math.Vector3f;
-import com.jme.scene.TriMesh;
+import com.jme.renderer.ColorRGBA;
+import com.jme.renderer.Renderer;
 import com.jme.scene.shape.Box;
+import com.jme.scene.shape.Quad;
+import com.jme.scene.state.BlendState;
+import com.jme.scene.state.BlendState.DestinationFunction;
+import com.jme.scene.state.BlendState.SourceFunction;
 import com.jme.scene.state.ClipState;
+import com.jme.scene.state.CullState;
 import com.jme.scene.state.TextureState;
 import com.jme.util.TextureManager;
 
+
+
 /**
  * <code>TestClipState</code>
  * @author Mark Powell
  * @version $Id: TestClipState.java,v 1.3 2006/01/13 19:37:15 renanse Exp $
+ * @version $Id: TestClipState.java,v 1.4 2008/12/29 23:44:21 basixs Exp $
  */
 public class TestClipState extends SimpleGame {
-    private TriMesh t;
 
+    private ClipState clipState = null;
+    private final Vector3f planeNormal = new Vector3f( 1, 0, 0 );
+    private float dist = 50;
+    //
+    private final Quad psuedoPlane = new Quad( "", 1000, 1000 );
+
     /**
      * Entry point for the test,
      * @param args
      */
-    public static void main(String[] args) {
+    public static void main( String[] args ) {
         TestClipState app = new TestClipState();
-        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
+        app.setConfigShowMode( ConfigShowMode.AlwaysShow );
         app.start();
     }
 
-    /**
-     * builds the trimesh.
-     * @see com.jme.app.SimpleGame#initGame()
-     */
+    @Override
+    protected void simpleUpdate() {
+        boolean update = false;
+        double change = 0;
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "UP", true ) ){
+            change = 0.1;
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "DOWN", true ) ){
+            change = -0.1;
+            update = true;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "X", false ) ){
+            planeNormal.set( 1, 0, 0 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "Y", false ) ){
+            planeNormal.set( 0, 1, 0 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "Z", false ) ){
+            planeNormal.set( 0, 0, 1 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "NEGATE", false ) ){
+            planeNormal.negateLocal();
+            update = true;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "RESET", false ) ){
+            planeNormal.set( 1, 0, 0 );
+            dist = 50;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "TOGGLE", false ) ){
+            clipState.setEnabled( !clipState.isEnabled() );
+        }
+
+        if( update == true ){
+            dist += change;
+            clipState.setClipPlaneEquation( ClipState.CLIP_PLANE0, planeNormal.x, planeNormal.y, planeNormal.z, dist );
+            updateQuadPosition();
+            System.out.println( "Plane Equation: " + planeNormal + "tDist: " + dist );
+        }
+    }
+
     protected void simpleInitGame() {
-        display.setTitle("Clip State Test");
-        cam.setLocation(new Vector3f(-10,0,40));
-        cam.update();
 
-        Vector3f max = new Vector3f(100,100,100);
-        Vector3f min = new Vector3f(0,0,0);
+        KeyBindingManager.getKeyBindingManager().set( "UP", KeyInput.KEY_PERIOD );
+        KeyBindingManager.getKeyBindingManager().set( "DOWN", KeyInput.KEY_COMMA );
+        //
+        KeyBindingManager.getKeyBindingManager().set( "X", KeyInput.KEY_X );
+        KeyBindingManager.getKeyBindingManager().set( "Y", KeyInput.KEY_Y );
+        KeyBindingManager.getKeyBindingManager().set( "Z", KeyInput.KEY_Z );
+        KeyBindingManager.getKeyBindingManager().set( "NEGATE", KeyInput.KEY_SPACE );
+        KeyBindingManager.getKeyBindingManager().set( "RESET", KeyInput.KEY_R );
+        KeyBindingManager.getKeyBindingManager().set( "TOGGLE", KeyInput.KEY_RETURN );
 
-        t = new Box("Box", min,max);
-        t.setModelBound(new BoundingSphere());
-        t.updateModelBound();
+        display.setTitle( "Clip State Test" );
+        cam.setLocation( new Vector3f( -300, 300, 300 ) );
+        cam.lookAt( Vector3f.ZERO, Vector3f.UNIT_Y );
+        cam.update();
 
-        t.setLocalTranslation(new Vector3f(-50,0,-75));
+        lightState.setEnabled( false );
+        MouseInput.get().setCursorVisible( true );
+        ((FirstPersonHandler) input ).setButtonPressRequired( true );
 
-        rootNode.attachChild(t);
+        Vector3f max = new Vector3f( 100, 100, 100 );
+        Vector3f min = new Vector3f( -100, -100, -100 );
 
+        final Box box = new Box( "Box", min, max );
+        box.setModelBound( new BoundingSphere() );
+        box.updateModelBound();
+
         TextureState ts = display.getRenderer().createTextureState();
-                ts.setEnabled(true);
+        ts.setEnabled( true );
                 ts.setTexture(
                     TextureManager.loadTexture(
-                        TestClipState.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"),
+                TestClipState.class.getClassLoader().getResource( "jmetest/data/images/Monkey.jpg" ),
                         Texture.MinificationFilter.Trilinear,
-                        Texture.MagnificationFilter.Bilinear));
+                Texture.MagnificationFilter.Bilinear ) );
 
-        rootNode.setRenderState(ts);
+        box.setRenderState( ts );
         
-        ClipState clipState = display.getRenderer().createClipState();
-        clipState.setEnableClipPlane(ClipState.CLIP_PLANE0, true);
-        clipState.setClipPlaneEquation(ClipState.CLIP_PLANE0, 0.5, 0.5, 0.0, 0.0);
-        rootNode.setRenderState(clipState);
+        rootNode.attachChild( box );
+
+        clipState = display.getRenderer().createClipState();
+        clipState.setEnableClipPlane( ClipState.CLIP_PLANE0, true );
+        clipState.setClipPlaneEquation( ClipState.CLIP_PLANE0, planeNormal.x, planeNormal.y, planeNormal.z, dist );
+        rootNode.setRenderState( clipState );
+       
+        initPseudoPlane();
+        rootNode.attachChild( psuedoPlane );
     }
+
+    private void updateQuadPosition() {
+        final Vector3f tempVector = new Vector3f();
+               
+        if( planeNormal.x ==  1 || planeNormal.x == -1  ){
+            tempVector.set( 0, 1, 0 );
+           
+        } else if( planeNormal.y ==  1 || planeNormal.y == -1  ){
+            tempVector.set( 1, 0, 0 );
+
+        } else if( planeNormal.z ==  1 || planeNormal.z == -1  ){
+            tempVector.set( 0, 0, 1 );
 }
+        psuedoPlane.getLocalRotation().fromAngleNormalAxis( FastMath.HALF_PI, tempVector );
+        psuedoPlane.getLocalTranslation().set( planeNormal ).multLocal( ( dist - 0.1f ) ).negateLocal();
+        psuedoPlane.updateGeometricState( 0, true );
+    }
+
+    private void initPseudoPlane() {
+       
+        psuedoPlane.setDefaultColor( new ColorRGBA( 1, 0, 0, 0.5f ) );
+       
+       
+        final CullState cullState = display.getRenderer().createCullState();
+        cullState.setCullFace( CullState.Face.None );
+       
+        final BlendState blendState = display.getRenderer().createBlendState();
+        blendState.setSourceFunctionAlpha( SourceFunction.SourceAlpha );
+        blendState.setDestinationFunctionAlpha( DestinationFunction.OneMinusSourceAlpha );
+        blendState.setEnabled( true );
+        blendState.setTestEnabled( true );
+        blendState.setBlendEnabled( true );
+        blendState.setTestFunction( BlendState.TestFunction.NotEqualTo );
+
+        psuedoPlane.setRenderState( blendState );
+        psuedoPlane.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT );
+        psuedoPlane.updateRenderState();
+       
+        updateQuadPosition();
+
+    }
+}


I cant seem to apply your patch, how did you create it?

NetBeans…



What error did you get?

Hehe sorry for the lengthy error description in my first post  :wink:



To make your patch useable in eclipse, i had to add package information to the filename like this:



Index: TestClipState.java

— jmetest.renderer.state.TestClipState.java Base (BASE)

+++ jmetest.renderer.state.TestClipState.java Locally Modified (Based On LOCAL)



Now it worked.



I think the patch is great!

If you can play with the clip state (or any other Renderstate), its much easier to understand how it works.



I modified 3 things:

  • added a few lines of Text to the graphNode, to show the user which keys do something.
  • mapped the 'RESET' functionality to KEY_HOME because KEY_R is already used in SimpleGame.
  • added a BoundingBox to the Plane, so it doesn't vanish suddenly



    If you could add a few lines of comments, to the relevant lines where the Clipping plane is set and such, that would be cool.



    Otherwise, i think go ahead and commit it.



    The complete Patch with modifications:


Index: src/jmetest/renderer/state/TestClipState.java
===================================================================
--- src/jmetest/renderer/state/TestClipState.java   (revision 4080)
+++ src/jmetest/renderer/state/TestClipState.java   (working copy)
@@ -33,66 +33,208 @@
 package jmetest.renderer.state;
 
 import com.jme.app.SimpleGame;
+import com.jme.bounding.BoundingBox;
 import com.jme.bounding.BoundingSphere;
 import com.jme.image.Texture;
+import com.jme.input.FirstPersonHandler;
+import com.jme.input.KeyBindingManager;
+import com.jme.input.KeyInput;
+import com.jme.input.MouseInput;
+import com.jme.math.FastMath;
 import com.jme.math.Vector3f;
-import com.jme.scene.TriMesh;
+import com.jme.renderer.ColorRGBA;
+import com.jme.renderer.Renderer;
+import com.jme.scene.Text;
 import com.jme.scene.shape.Box;
+import com.jme.scene.shape.Quad;
+import com.jme.scene.state.BlendState;
 import com.jme.scene.state.ClipState;
+import com.jme.scene.state.CullState;
 import com.jme.scene.state.TextureState;
+import com.jme.scene.state.BlendState.DestinationFunction;
+import com.jme.scene.state.BlendState.SourceFunction;
 import com.jme.util.TextureManager;
 
+
+
 /**
  * <code>TestClipState</code>
  * @author Mark Powell
  * @version $Id: TestClipState.java,v 1.3 2006/01/13 19:37:15 renanse Exp $
+ * @version $Id: TestClipState.java,v 1.4 2008/12/29 23:44:21 basixs Exp $
  */
 public class TestClipState extends SimpleGame {
-    private TriMesh t;
 
+    private ClipState clipState = null;
+    private final Vector3f planeNormal = new Vector3f( 1, 0, 0 );
+    private float dist = 50;
+    //
+    private final Quad psuedoPlane = new Quad( "", 1000, 1000 );
+
     /**
      * Entry point for the test,
      * @param args
      */
-    public static void main(String[] args) {
+    public static void main( String[] args ) {
         TestClipState app = new TestClipState();
-        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
+        app.setConfigShowMode( ConfigShowMode.AlwaysShow );
         app.start();
     }
 
-    /**
-     * builds the trimesh.
-     * @see com.jme.app.SimpleGame#initGame()
-     */
+    @Override
+    protected void simpleUpdate() {
+        boolean update = false;
+        double change = 0;
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "UP", true ) ){
+            change = 0.1;
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "DOWN", true ) ){
+            change = -0.1;
+            update = true;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "X", false ) ){
+            planeNormal.set( 1, 0, 0 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "Y", false ) ){
+            planeNormal.set( 0, 1, 0 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "Z", false ) ){
+            planeNormal.set( 0, 0, 1 );
+            update = true;
+        }
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "NEGATE", false ) ){
+            planeNormal.negateLocal();
+            update = true;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "RESET", false ) ){
+            planeNormal.set( 1, 0, 0 );
+            dist = 50;
+            update = true;
+        }
+
+        if( KeyBindingManager.getKeyBindingManager().isValidCommand( "TOGGLE", false ) ){
+            clipState.setEnabled( !clipState.isEnabled() );
+        }
+
+        if( update == true ){
+            dist += change;
+            clipState.setClipPlaneEquation( ClipState.CLIP_PLANE0, planeNormal.x, planeNormal.y, planeNormal.z, dist );
+            updateQuadPosition();
+            System.out.println( "Plane Equation: " + planeNormal + "tDist: " + dist );
+        }
+    }
+
     protected void simpleInitGame() {
-        display.setTitle("Clip State Test");
-        cam.setLocation(new Vector3f(-10,0,40));
+        statNode.attachChild(addText("Press . to move the plane up"));
+        statNode.attachChild(addText("Press , to move the plane down"));
+        statNode.attachChild(addText("Press X Y or Z to change the plane orientation"));
+        statNode.attachChild(addText("Press SPACE to negate the plane normal"));
+        statNode.attachChild(addText("Press RETURN to toggle the clip state"));
+        statNode.attachChild(addText("Press HOME to reset the plane"));
+       
+        KeyBindingManager.getKeyBindingManager().set( "UP", KeyInput.KEY_PERIOD );
+        KeyBindingManager.getKeyBindingManager().set( "DOWN", KeyInput.KEY_COMMA );
+        //
+        KeyBindingManager.getKeyBindingManager().set( "X", KeyInput.KEY_X );
+        KeyBindingManager.getKeyBindingManager().set( "Y", KeyInput.KEY_Y );
+        KeyBindingManager.getKeyBindingManager().set( "Z", KeyInput.KEY_Z );
+        KeyBindingManager.getKeyBindingManager().set( "NEGATE", KeyInput.KEY_SPACE );
+        KeyBindingManager.getKeyBindingManager().set( "RESET", KeyInput.KEY_HOME );
+        KeyBindingManager.getKeyBindingManager().set( "TOGGLE", KeyInput.KEY_RETURN );
+
+        display.setTitle( "Clip State Test" );
+        cam.setLocation( new Vector3f( -300, 300, 300 ) );
+        cam.lookAt( Vector3f.ZERO, Vector3f.UNIT_Y );
         cam.update();
 
-        Vector3f max = new Vector3f(100,100,100);
-        Vector3f min = new Vector3f(0,0,0);
+        lightState.setEnabled( false );
+        MouseInput.get().setCursorVisible( true );
+        ((FirstPersonHandler) input ).setButtonPressRequired( true );
 
-        t = new Box("Box", min,max);
-        t.setModelBound(new BoundingSphere());
-        t.updateModelBound();
+        Vector3f max = new Vector3f( 100, 100, 100 );
+        Vector3f min = new Vector3f( -100, -100, -100 );
 
-        t.setLocalTranslation(new Vector3f(-50,0,-75));
+        final Box box = new Box( "Box", min, max );
+        box.setModelBound( new BoundingSphere() );
+        box.updateModelBound();
 
-        rootNode.attachChild(t);
-
         TextureState ts = display.getRenderer().createTextureState();
-                ts.setEnabled(true);
+        ts.setEnabled( true );
                 ts.setTexture(
                     TextureManager.loadTexture(
-                        TestClipState.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"),
+                TestClipState.class.getClassLoader().getResource( "jmetest/data/images/Monkey.jpg" ),
                         Texture.MinificationFilter.Trilinear,
-                        Texture.MagnificationFilter.Bilinear));
+                Texture.MagnificationFilter.Bilinear ) );
 
-        rootNode.setRenderState(ts);
+        box.setRenderState( ts );
         
-        ClipState clipState = display.getRenderer().createClipState();
-        clipState.setEnableClipPlane(ClipState.CLIP_PLANE0, true);
-        clipState.setClipPlaneEquation(ClipState.CLIP_PLANE0, 0.5, 0.5, 0.0, 0.0);
-        rootNode.setRenderState(clipState);
+        rootNode.attachChild( box );
+
+        clipState = display.getRenderer().createClipState();
+        clipState.setEnableClipPlane( ClipState.CLIP_PLANE0, true );
+        clipState.setClipPlaneEquation( ClipState.CLIP_PLANE0, planeNormal.x, planeNormal.y, planeNormal.z, dist );
+        rootNode.setRenderState( clipState );
+       
+        initPseudoPlane();
+        rootNode.attachChild( psuedoPlane );
     }
+
+    private void updateQuadPosition() {
+        final Vector3f tempVector = new Vector3f();
+               
+        if( planeNormal.x ==  1 || planeNormal.x == -1  ){
+            tempVector.set( 0, 1, 0 );
+           
+        } else if( planeNormal.y ==  1 || planeNormal.y == -1  ){
+            tempVector.set( 1, 0, 0 );
+
+        } else if( planeNormal.z ==  1 || planeNormal.z == -1  ){
+            tempVector.set( 0, 0, 1 );
 }
+        psuedoPlane.getLocalRotation().fromAngleNormalAxis( FastMath.HALF_PI, tempVector );
+        psuedoPlane.getLocalTranslation().set( planeNormal ).multLocal( ( dist - 0.1f ) ).negateLocal();
+        psuedoPlane.updateGeometricState( 0, true );
+    }
+
+    private void initPseudoPlane() {
+       
+        psuedoPlane.setDefaultColor( new ColorRGBA( 1, 0, 0, 0.5f ) );
+       
+       
+        final CullState cullState = display.getRenderer().createCullState();
+        cullState.setCullFace( CullState.Face.None );
+       
+        final BlendState blendState = display.getRenderer().createBlendState();
+        blendState.setSourceFunctionAlpha( SourceFunction.SourceAlpha );
+        blendState.setDestinationFunctionAlpha( DestinationFunction.OneMinusSourceAlpha );
+        blendState.setEnabled( true );
+        blendState.setTestEnabled( true );
+        blendState.setBlendEnabled( true );
+        blendState.setTestFunction( BlendState.TestFunction.NotEqualTo );
+
+        psuedoPlane.setModelBound(new BoundingBox());
+        psuedoPlane.updateModelBound();
+        psuedoPlane.setRenderState( blendState );
+        psuedoPlane.setRenderQueueMode( Renderer.QUEUE_TRANSPARENT );
+        psuedoPlane.updateRenderState();
+       
+        updateQuadPosition();
+
+    }
+   
+    private int textY = 10;
+    private Text addText(String s) {
+        Text text = new Text(s, s);
+        text.setRenderState( Text.getDefaultFontTextureState() );
+        text.setRenderState( Text.getFontBlend() );
+        text.setLocalTranslation(10, display.getRenderer().getHeight() - text.getHeight() - textY, 0);
+        textY += text.getHeight()*1.5f;
+        return text;
+    }
+}
No newline at end of file

Thanx Core-Dump, I too like Nate Robins type demos :slight_smile:



Just thought I would post my comments and make sure they make sense to anyone actually reading them…


 * Demonstrats the use of clips planes in an interactive demo.  A clip plane splits
 * the rendering 'area' in 2, one side is rendered unchanged while the other is not
 * rendered.  'Under the hood' the verticies must still be calculated and additional verticies
 * may be 'inserted', but there should be a considerable performance increase
 * in direct relation to the number of 'clipped' faces.
 * A clip plane is constructed by supplying the planes normal vector (X,Y,Z) and
 * the distance from the origin (W).
 *
 * Through use of jME RenderingPasses (and enabling/disabling clip planes) it should
 * be possible to complete rendering techniques like the following URL with ease:
 * http://glbook.gamedev.net/moglgp/advclip.asp

sounds good to me

I love interactive test code like this, illustrates the point clearly and simply :slight_smile:

Just 1 tiny change to add - when you flip the normal for the plane, it doesn't flip the distance too, so the clip point seems to move.  Just needs a dist=-dist added to the negate keyhandler.

Thanx JOC



committed…