Multiple Applications sharing one rootNode

Hello all,



I am currently beginning to make a game that should allow for multiple viewports, preferably on a canvas. Is it possible to have one main Application that does all the resource handling and physics and so on (it would be headless), and additional passive Applications who only render the scene to a awt Canvas?



Is it enough to just give the rootNode to the passive app or do I have to give them the assetManager as well?



I hope you can help me, thanks in advance.



greets,

dasding

You could have multiple views, aka splitscreen with only one application , so why using multiple?

The number of screens should be arbitrary. If I use splitscreen, there is the problem of Layout for an arbitrary number of screens. I would like to make my game-desktop highly customizable. Does anybody know whether this is possible or if there perhaps are performance issues that advise against it? If it is only the programming that is complex, I am willing to bite into it and post a snippet if I succeed.



greets,

dasding

Was jsut an example, actually we use a viewport internally in our gui system for a model panel (imagine a shop where you see the items in 3d) so depending on how much you need there is not that much of a penalty

Ok, I tried and I failed. When I try to start more that one application in a process, I get an exception from lwql about the “current”. So, probably back to the splitscreen.



Does anybody know how one can access the offscreen buffer, since the field pBuffer in LwglOffscreenBuffer.class is declared private and there are no methods to access it?



greets,

dasding

You should not use LWJGL calls directly in jME3.

If you need to get the content of the pbuffer use renderer.readFrameBuffer()

ahh, I see. Is it also possible to get it (savely) done via viewport.getOutputFrameBuffer() ?

viewport.getOutputFrameBuffer() is going to be null unless you have filters/processors on that viewport.

Thank you for your help. I have one question remaining though.



Do I manually have to create a FrameBuffer fb that I first give to the renderer via [java]renderer.setFrameBuffer(fb)[/java] and then after rendering, retrieve it with [java]renderer.readFrameBuffer(fb,someByteBuffer)[/java] ?



greets,

dasding

Specify “null” for the first readFrameBuffer argument if you want to read from the primary framebuffer

Good News, everyone…



I finally succeeded my quest for multiple displays. What I used was the ViewPort.setFrameBuffer() method to channel the output to a FrameBuffer of my choice, read it out via Renderer.readFrameBuffer() and then create an awt - BufferedImage with it.



To use this example, you have to add the GUIState, which is an AppState, to your Application and make sure, it is updated (e.g. if you use SimpleApplication, this is guaranteed). To create a Viewport, use the GUIState.createFreeViewPort() method and place the Canvas you get back in any awt Container, which is visible.



Still missing are input management and methods to create a Viewport, with camera attached to a CameraNode.



I hope, I could help a little.



greets,

dasding



pre type="java"
/


 
 To change this template, choose Tools | Templates


  and open the template in the editor.


 
/





package mygame;





import com.jme3.renderer.Renderer;


import com.jme3.renderer.ViewPort;


import com.jme3.texture.FrameBuffer;


import com.jme3.texture.Image.Format;


import java.awt.Canvas;


import java.awt.Graphics;


import java.awt.Transparency;


import java.awt.color.ColorSpace;


import java.awt.image.;


import java.nio.ByteBuffer;





/**


 



  @author matthias


 
/


public final class AppCanvas extends Canvas


{





    private final static ColorModel colorModel = new ComponentColorModel(


            ColorSpace.getInstance(ColorSpace.CS_sRGB), //ColorSpace


            true,                                       //hasAlpha


            false,                                      //isAlphaPremultiplied


            Transparency.OPAQUE,                        //Transparency


            DataBuffer.TYPE_BYTE);                      //transferType





    private int samples;


    private ViewPort viewPort;


    private FrameBuffer frameBuffer;


    private ByteBuffer byteBuffer;


    private byte[] bytes;


    private DataBuffer dataBuffer;


    private SampleModel sampleModel;


    private WritableRaster raster;


    private BufferedImage image;





    public AppCanvas(com.jme3.renderer.ViewPort vp)


    {


        this(vp,1);


    }





    /


      Constructor deliveres a new AppCanvas, which depicts the ViewPort vp.


     
 samples is needed for the creation of the frameBuffer


      @param vp Viewport to be depicted


     
 @param samples


     */


    public AppCanvas(com.jme3.renderer.ViewPort vp, int samples)


    {


        setSize(vp.getCamera().getWidth(), vp.getCamera().getHeight());


        this.samples = samples;


        viewPort = vp;


        onResize();





        setFocusable(true);


        setIgnoreRepaint(true);


    }





    /



      Copies the content of the viewport’s frameBuffer to a byteBuffer and


     
 updates the image afterwards.


      @param renderer


     
/


    public synchronized void updateBuffer(Renderer renderer)


    {


        byteBuffer.position(0);


        renderer.readFrameBuffer(frameBuffer, byteBuffer);


        byteBuffer.get(bytes);


    }





    @Override


    public void paint(Graphics g)


    {


        g.drawImage(image, 0, 0, null);


        //Additional paint jobs here. The Canvas will not be cleared.


    }





    /


      Should be called if the Canvas changed it’s size


     
/


    public synchronized void onResize()


    {


        getViewPort().getCamera().resize(getWidth(), getHeight(), true);


        getViewPort().getCamera().setFrustumPerspective(45f,


                (float)getWidth() / getHeight(), 1f, 1000f);





        frameBuffer = new FrameBuffer(getWidth(), getHeight(), samples);


        //frameBuffer.setDepthBuffer(Format.Depth);


        frameBuffer.setColorBuffer(Format.RGBA8);


        frameBuffer.setUpdateNeeded();





        getViewPort().setOutputFrameBuffer(frameBuffer);





        byteBuffer = ByteBuffer.allocateDirect(getWidth()*getHeight()*4);





        bytes = new byte[getWidth()*getHeight()*4];





        dataBuffer = new DataBufferByte(bytes, getWidth()*getHeight());


        int[] bandOffsets = {2,1,0,3};


        sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,


                getWidth(), getHeight(), 4, getWidth()*4, bandOffsets);


        raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);





        image = new BufferedImage(colorModel, raster, false, null);


    }





    /



      @return the viewPort


     
/


    public ViewPort getViewPort() {


        return viewPort;


    }


}



/pre



pre type="php"
/


 
 To change this template, choose Tools | Templates


  and open the template in the editor.


 
/





package mygame;





import com.jme3.app.state.AbstractAppState;


import com.jme3.app.Application;


import com.jme3.math.Vector3f;


import com.jme3.renderer.Camera;


import com.jme3.renderer.ViewPort;


import com.jme3.system.AppSettings;


import java.awt.event.ComponentEvent;


import java.awt.event.ComponentListener;


import java.nio.ByteBuffer;


import java.util.ArrayList;








/


 


 
 @author matthias


 */


public class GUIState extends AbstractAppState implements ComponentListener


{


    protected int id;


    protected Application app;


    protected ByteBuffer buf;


    protected ArrayList<AppCanvas> cans;


    protected AppSettings viewportSettings;





    public GUIState(Application app)


    {


        this(app,null);


    }





    /



      Creates a new GUIState which controls the rendering of it’s AppCanvases.


     
 @param app mother application


      @param settings settings for the canvas displays that will be created.


     
/


    public GUIState(Application app, AppSettings settings)


    {


        id = 0;


        this.app = app;


        buf = ByteBuffer.allocate(0);


        cans = new ArrayList<AppCanvas>();


        viewportSettings = (settings==null)? new AppSettings(true) : settings;


    }





    /


      Creates a new AppCanvas that shows the scene rootNode. This method might be tweaked


     
 a little, so far there is no input management included.


      @param rootNode the scene to be depicted


     
 @return


     /


    public AppCanvas createFreeViewport(com.jme3.scene.Node rootNode)


    {


        Camera cam = new Camera(viewportSettings.getWidth(), viewportSettings.getHeight());





        //One might also take a Vector3f as argument for a Location


        cam.setLocation(new Vector3f(id
5f, 0, 10));


        cam.setDirection(new Vector3f(0f, 0f, -1f));


        cam.lookAt(new Vector3f(0f,0f,0f), Vector3f.UNIT_Y);


        


        //This part is crucial


        ViewPort vp = app.getRenderManager().createMainView(“Viewport” + String.valueOf(id), cam);


        vp.attachScene(rootNode);


        int samples = viewportSettings.getSamples();


        AppCanvas ret = new AppCanvas(vp,samples);


        ret.addComponentListener(this);


        cans.add(ret);


        return ret;


    }





    //@TODO: A method that attaches the AppCanvas’ camera to a CameraNode


    //public AppCanvas createNodeViewport()


    //{


    //    return new AppCanvas();


    //}





    /



      Implementation of Interface ComponentListener.


     
 Ensures, that the render output gets the right size.


      @param ce


     
/


    public void componentResized(ComponentEvent ce)


    {


        ((AppCanvas)ce.getComponent()).onResize();


    }





    public void componentMoved(ComponentEvent ce)


    {





    }





    public void componentShown(ComponentEvent ce)


    {





    }





    public void componentHidden(ComponentEvent ce)


    {





    }





    /**


      This method is called after rendering is done. Now all


     
 AppCanvases have to be updated and repainted. If a


      Canvas is not visible anymore (e.g. it was removed from


     
 it’s parent or the parent was hidden), it’s Viewport


      will be destroyed.


     
 Therefore, in your application you should make sure that a


      hidden canvas never will show again, or you modify this


     
 method to your liking.


     */


    @Override


    public void postRender()


    {


        for(int i=0;i<cans.size();i++)


        {


            AppCanvas can = cans.get(i);


            if(can.isShowing())


            {


                can.updateBuffer(app.getRenderer());


                can.paint(can.getGraphics());


            }else{


                app.getRenderManager().removeMainView(can.getViewPort());


                cans.remove(can);


                i–;


            }


        }


    }


}



/pre

Yep, thats basically how jMP does OffView panels too, maybe in the future we can find a global solution for this and add some more performance.

Hi,



On the off chance that anyone re reads this thread, I am really struggling to fill a set of framebuffers (one for each viewport) with the rendered image from each of multiple viewports. This seems to be what is achieved in the above code, but I can’t figure out at what point the framebuffer of each viewport is filled with the appropriate image…



Thanks