Limited view/Scene disappearing when adding Box to RenParticleEditor

Hi! I have a encountered a problem trying to add simple Objects to RenParticleEditor.java. I simplified the code to 300 Lines however for testing purpose. The problem is: when I'm trying to add a spatial object (like an arrow or box) to the scene - the object is added - unfortunately I get some kind of limited view and am not able to see the grid or the object anymore. Only by moving carefully with the MouseLook view I can see the grid and the object again - but the sight is limited somehow… :?



You can try it out with this code (I marked the line - it's on the bottom of the code - with "TODO" where to comment/uncomment the specified line and can use keyboad and hold mousebutton1 to steer the camera (thanks to Targ in another thread in the forum) and see what the problem is.

enable the line: limited view

disable the line: no problems to view grid



I tried updateRenderState(), updateGeometricState both for the sceneNode and for the rootNode, tried to "Inject" with GameTaskQueueManager but nothing worked.

Any idea what I got wrong?




import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.util.concurrent.Callable;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import com.jme.bounding.BoundingBox;
import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.InputSystem;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.scene.SceneElement;
import com.jme.scene.Text;
import com.jme.scene.shape.Arrow;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jmex.awt.JMECanvas;
import com.jmex.awt.SimpleCanvasImpl;
import com.jmex.awt.input.AWTMouseInput;


public class CopyOfRenParticleEditor extends JFrame
{
    private static final Logger logger = Logger.getLogger(CopyOfRenParticleEditor.class.getName());
    public Node sceneNode;
    Node unshaded = new Node();
    private static final long serialVersionUID = 1L;
    int width = 640, height = 480;
    MyImplementor impl;
    private Canvas glCanvas;
    private Node root;
    public Node translationNode = new Node();
    Arrow xArrow;   
    InputHandler input;
   InputHandler inputModes;   
   Camera cam;
   int camspeed = 50;
   Vector3f loc,dir,left,up;
   

    /**
     * Main Entry point...
     */
    public static void main(String[] args)
    {
       new CopyOfRenParticleEditor();
    }

    public CopyOfRenParticleEditor()
    {
        try
        {
            init();
            setLocationRelativeTo(null);
            setVisible(true);

            while (glCanvas == null)
                ;

            // MAKE SURE YOU REPAINT SOMEHOW OR YOU WON'T SEE THE UPDATES...
            new Thread()
            {
                {
                    setDaemon(true);
                }
                public void run()
                {
                    try
                    {
                        while(true)
                        {
                            if(isVisible())
                               glCanvas.repaint();
                            Thread.sleep(2);
                        }
                    }
                    catch (InterruptedException e)
                    {
                        logger.throwing(this.getClass().toString(),"run()",e);
                    }
                }
            }.start();

        }
        catch (Exception ex)
        {
            logger.throwing(this.getClass().toString(),"RenParticleEditor()",ex);
        }
    }

    private void init() throws Exception
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setFont(new Font("Arial", 0, 12));

        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.setPreferredSize(new Dimension(215, 10));

        JPanel canvasPanel = new JPanel();
        canvasPanel.setLayout(new BorderLayout());
        canvasPanel.add(getGlCanvas(), BorderLayout.CENTER);

        JSplitPane mainSplit = new JSplitPane();
        mainSplit.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
        mainSplit.setLeftComponent(tabbedPane);
        mainSplit.setRightComponent(canvasPanel);
       
        Dimension minimumSize = new Dimension(100, 50);
        tabbedPane.setMinimumSize(minimumSize);
        canvasPanel.setMinimumSize(minimumSize);

        getContentPane().add(mainSplit, BorderLayout.CENTER);
       
        setSize(new Dimension(1024, 768));
    }
       
    protected Canvas getGlCanvas()
    {
        if (glCanvas == null)
        {

            //


GL STUFF
           
           // create canvas
            glCanvas = DisplaySystem.getDisplaySystem().createCanvas(width,height);
            glCanvas.setMinimumSize(new Dimension(100, 100));

            // if window is resized, we can do something about
            glCanvas.addComponentListener(new ComponentAdapter()
            {
                public void componentResized(ComponentEvent ce)
                {
                    doResize();
                }
            });

            // FirstPersonHandling
            if (!KeyInput.isInited())
                KeyInput.setProvider(InputSystem.INPUT_SYSTEM_AWT);
            KeyListener kl = (KeyListener) KeyInput.get();
            glCanvas.addKeyListener(kl);

            if (!MouseInput.isInited())
                MouseInput.setProvider(InputSystem.INPUT_SYSTEM_AWT);
            ((AWTMouseInput) MouseInput.get()).setDragOnly(true);
            glCanvas.addMouseListener((MouseListener) MouseInput.get());
            glCanvas.addMouseWheelListener((MouseWheelListener) MouseInput.get());
            glCanvas.addMouseMotionListener((MouseMotionListener) MouseInput.get());
                       
            // Important! Here is where we add the guts to the canvas:
            impl = new MyImplementor(width, height);

            ((JMECanvas) glCanvas).setImplementor(impl);

            //
END OF GL STUFF

            Callable<?> exe = new Callable()
            {
                public Object call()
                {
                    forceUpdateToSize();
                    return null;
                }
            };
            GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(exe);
        }
        return glCanvas;
    }

    public void forceUpdateToSize()
    {
        // force a resize to ensure proper canvas size.
        glCanvas.setSize(glCanvas.getWidth(), glCanvas.getHeight() + 1);
        glCanvas.setSize(glCanvas.getWidth(), glCanvas.getHeight() - 1);
    }

    protected void doResize() {
        if (impl != null) {
            impl.resizeCanvas(glCanvas.getWidth(), glCanvas.getHeight());
            if (impl.getCamera() != null) {
                Callable<?> exe = new Callable()
                {
                    public Object call()
                    {
                        impl.getCamera().setFrustumPerspective(45.0f,(float) glCanvas.getWidth()/(float) glCanvas.getHeight(),1,10000);
                        return null;
                    }
                };
                GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(exe);
            }
        }
    }

    // IMPLEMENTING THE SCENE:
    class MyImplementor extends SimpleCanvasImpl
    {
        private static final int GRID_LINES = 51;
        private static final float GRID_SPACING = 10;
        protected Node fpsNode;
        protected Text fps;
        protected StringBuffer tempBuffer = new StringBuffer();
        protected StringBuffer updateBuffer = new StringBuffer(30);

        public MyImplementor(int width, int height)
        {
            super(width, height);
        }
       
        public void simpleSetup()
        {
            renderer.setBackgroundColor(ColorRGBA.black);
            cam.setFrustumPerspective(45.0f, (float) glCanvas.getWidth() / (float) glCanvas.getHeight(), 1, 10000);

            loc = new Vector3f(0, 50, -50);
            left = new Vector3f(1, 0, 0);
            up = new Vector3f(0, 1,0);
            dir = new Vector3f(0, -0.7071f, 0.7071f);
            cam.setFrame(loc, left, up, dir);           
            input = new FirstPersonHandler(cam,camspeed, 1);

            root = rootNode;

            // Then our font Text object
            fps = Text.createDefaultTextLabel("FPS label");
            fps.setCullMode(SceneElement.CULL_NEVER);
            fps.setTextureCombineMode(TextureState.REPLACE);

            // Finally, a stand alone node (not attached to root on purpose)
            fpsNode = new Node("FPS node");
            fpsNode.setRenderState(fps.getRenderState(RenderState.RS_ALPHA));
            fpsNode.setRenderState(fps.getRenderState(RenderState.RS_TEXTURE));
            fpsNode.attachChild(fps);
            fpsNode.setCullMode(SceneElement.CULL_NEVER);

            renderer.enableStatistics(true);
           
            createUnshaded();                       
            root.attachChild(unshaded);
           
            sceneNode = new Node("Scene");
            root.attachChild(sceneNode);
           
            sceneNode.updateRenderState();

            fpsNode.updateGeometricState(0, true);
            fpsNode.updateRenderState();
        };

        public void simpleUpdate()
        {
           InputSystem.update();
           input.update(tpf);
                      
           updateBuffer.setLength(0);
            updateBuffer.append("FPS: ").append((int) timer.getFrameRate()).append(" - ");
            updateBuffer.append(renderer.getStatistics(tempBuffer));
            fps.print(updateBuffer);
        }

        @Override
        public void simpleRender()
        {
            fpsNode.draw(renderer);
            renderer.clearStatistics();
        }
       
        public void createUnshaded() // Node not influenced by any lights
        {
           //Create Grid
            Vector3f[] vertices = new Vector3f[GRID_LINES * 2 * 2];
            float edge = GRID_LINES / 2 * GRID_SPACING;
            for (int ii = 0, idx = 0; ii < GRID_LINES; ii++)
            {
                float coord = (ii - GRID_LINES / 2) * GRID_SPACING;
                vertices[idx++] = new Vector3f(-edge, 0f, coord);
                vertices[idx++] = new Vector3f(+edge, 0f, coord);
                vertices[idx++] = new Vector3f(coord, 0f, -edge);
                vertices[idx++] = new Vector3f(coord, 0f, +edge);
            }
            Geometry grid = new com.jme.scene.Line("grid",vertices, null,null,null);
            grid.getBatch(0).getDefaultColor().set(ColorRGBA.gray);
            unshaded.attachChild(grid);
                     
            xArrow = new Arrow("xArrow",4.0f,0.25f);
          xArrow.setModelBound(new BoundingBox());
          xArrow.setSolidColor(ColorRGBA.red);
          xArrow.updateModelBound();
          
          unshaded.attachChild(xArrow); //TODO: toggle/untoggle this line
        }
    }
}

Now I finally figured out that this (bold) line causes trouble and weird view behavior:



Arrow xArrow = new Arrow("xArrow",4.0f,0.25f);

xArrow.setModelBound(new BoundingBox());

xArrow.setSolidColor(ColorRGBA.red);

xArrow.updateModelBound();

   

root.attachChild(xArrow);



How come that? I thought it is always necessary to use ModelBounds. If I now delete this line everything works normal - but only on the first view: when I use mousepicking later on, I cannot select the object anymore! Obviously mousepicking requires modelbounds… so now what can I do to solve this dilemma?

:expressionless:

maybe you forgot


xArrow.updateModelBound()

Sadly it doesn't work (I already used updateModelBound() above in the sample code :frowning: ). I thought maybe that error happenend because I "simplified" the original code too much and tried it out on the original 1)  JMESwingTest.java and 2) RenParticleEditor.java from Koders.com. Nothing changed - the same error appears.



Therefore I tried to update to the newest SVN (until now I have worked with JME 1.0) and got JME 2 finally to work. I got the new RenParticleEditor from the SVN, started it and implemented again these lines in simpleSetup():


Box box = new Box("Box",new Vector3f(150,0,0),5,8,12);
            box.setModelBound(new BoundingBox());
            box.updateModelBound();
            box.setRandomColors();
            root.attachChild(box);



And the error appears again! Each time you drag the camera (while holding right MouseButton) the whole scene (grid, particles in this example) disappears - as soon as the Box gets out of sight (I figured out that it always happens when the box vanishes behind the edges of the JFrame/Panel).
So... either there is something wrong in the way the Box is added, or there is some other issue going on.
I am completely stuck on this and really need some help to solve this issue. Also I wonder, why no one seemed to have experienced this too :?

Most likely the issue is not your box, but the fact that the grid and particles do not have bounds set, so by adding your box, the whole scene inherits your box's bounds as the "world" bounds.

Thanks man… that finally brought me to the solution. It took me a while because I already coded another sample using no Swing but a StandardGame containing 2 different GameStates: one GameState was the Grid and the other GameState consists of the interactive Scene Elements. In there everything worked "perfect" - I didn't even apply ModelBounds to the grid and it worked - but now I know it only worked because they were different GameStates. In the Swing Sample of course there are no different GameStates and therefore it didn't work.



Anyways - let's go back to the (old) RenParticleEditor Sample:

Unfortunately the problem was still not solved after adding Bounds to the grid. So after simplifying the code to 200 lines and deleting almost everything I thought is removable I figured out that another thing was causing trouble:



Looking at the simpleSetup() method I found some lines that configure the camera:

cam = impl.getRenderer().getCamera();
            renderer.setBackgroundColor(ColorRGBA.lightGray);
            cam.setFrustumPerspective(45.0f, (float) glCanvas.getWidth() / (float) glCanvas.getHeight(), 1, 10000);
//            Vector3f loc = new Vector3f(0, 50, -50);
//            Vector3f left = new Vector3f(1, 0, 0);
//            Vector3f up = new Vector3f(0, 1,0);   // formerly (0, 0.7071f, 0.7071f)
//            Vector3f dir = new Vector3f(0, -0.7071f, 0.7071f);
//            cam.setFrame(loc, left, up, dir);          
            cam.update();
            input = new FirstPersonHandler(cam,50,1);




...So now I wanted the camera to be upright, because using the FirstPersonHandler caused the camera to act on a weird angle (caused by (0, 0.7071f, 0.7071f)). Therefore I set the up-Vector of the camera to (0,1,0); The camera was now upright, but then too I experienced sometimes objects disappearing when moving with the camera.

Exactly this change of up-Vector caused some additional trouble. Why is that the case? I found out however that  in the new ParticleEditor (from JME 2.0) these 5 lines of code are completely removed. So obviously these lines aren't necessary. I did remove them myself for the ParticleEditor (from JME 1.0) and voil