How to put ViewPort into Lemur Container?


I want to put the ViewPort into the Lemur Container, is this possible?


In what way should I make the ViewPort perfectly overlap with the UI borders of the Lemur?

After some attempts I can’t seem to get the ViewPort into the Lemur container.

Is there any other way I can position the ViewPort into the lemur UI to overlap?

I believe the normal way, if I understand your question correctly, is to attach a color texture target to the framebuffer of the viewport, and use the resulting texture on whatever gui element.

Camera vpCam = viewPort.getCamera();
Texture2D target = new Texture2D(vpCam.getWidth(), vpCam.getHeight(), Image.Format.RGBA8);
viewPort.getOutputFrameBuffer().addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(target));
Panel guiElement = ...
guiElement.setBackground(new QuadBackgroundComponent(target));

Edit: I can’t remember how to not render the viewport to the screen the way it normally would, but that should be disabled as well, I think. You may need to create a new framebuffer and add that to the viewport.

For placing another texture over the result, you can either write a custom shader or place another gui element over top.

2 Likes

Wouldn’t disabling the normal output be achieved by setting the outputFramebuffer (once it isn’t null I think it doesn’t go to the screen), something like this

    ViewPort minimapViewPort = renderManager.createPreView("Minimap View", minimapCamera);
    minimapViewPort.setClearFlags(true, true, true);
    minimapViewPort.attachScene(minimapRoot);

    int width = ...;
    int height = ...;
    int samples = ...; //1 if don't care about aliasing for a minimap
    FrameBuffer frameBuffer = new FrameBuffer(width, height, samples);
    Texture2D texture = new Texture2D(width, height, samples, Image.Format.RGBA8);
    Texture2D msDepth = new Texture2D(width, height, samples, Image.Format.Depth);
    frameBuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
    frameBuffer.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(msDepth));
    minimapViewPort.setOutputFrameBuffer(frameBuffer);

    // QuadBackgroundComponent as in codex's example
2 Likes

Thank you so much for the idea,I’ll give it a try.

/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package GUI;

import CameraAndMouse.CamAndMouseCur;
import CameraAndMouse.SelectUnit;
import SelectUnit.UnitGlobalData;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import static com.jme3.app.SimpleApplication.INPUT_MAPPING_EXIT;
import com.jme3.app.state.BaseAppState;
import com.jme3.asset.AssetManager;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.font.BitmapFont;
import com.jme3.input.InputManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.util.BufferUtils;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.FillMode;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.PasswordField;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.event.CursorButtonEvent;
import com.simsilica.lemur.event.CursorEventControl;
import com.simsilica.lemur.event.CursorMotionEvent;
import com.simsilica.lemur.event.DefaultCursorListener;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.BaseStyles;
import com.simsilica.lemur.style.Styles;
import java.nio.FloatBuffer;
import java.util.logging.Logger;

/**
 *
 * @author Icyboxs
 */
public class UI extends BaseAppState {

    private static Logger log = Logger.getLogger(UI.class.toString());
    private InputManager inputManager;
    private AssetManager assetManager;
    private SimpleApplication simpleApp;
    private BitmapFont font;
    private Camera cam;
    private Node iMap = new Node("miniMap");
    private Node info = new Node("infowindow");
    private Node directive = new Node("directive");

    public static Node MapUIBox = new Node("MapUIBox");

    private ViewPort vp;

    private Quaternion camQuaternion = new Quaternion();
    private CollisionResults results = new CollisionResults();

    private CollisionResults iresults = new CollisionResults();

    float[] vertices = new float[4 * 3];
    short[] indices = {0, 1, 2, 3, 0};
    private Mesh mesh = new Mesh();
    private Camera cam4;

    @Override
    protected void initialize(Application aplctn) {
        simpleApp = (SimpleApplication) aplctn;
        assetManager = aplctn.getAssetManager();
        inputManager = aplctn.getInputManager();
        inputManager.deleteMapping(INPUT_MAPPING_EXIT);
        cam = simpleApp.getCamera();

        GuiGlobals.initialize(simpleApp);

        simpleApp.getRootNode().attachChild(MapUIBox);
        // Setup fourth view

        float farPlane = 4096.0f;

        cam4 = cam.clone();
        cam4.setViewPort(0.05f, (cam.getWidth() / 4f) / cam.getWidth() + 0.05f, 0.05f, (cam.getWidth() / 4f) / cam.getWidth() + 0.05f);
        cam4.setLocation(new Vector3f(1024, 310, 1024));
        camQuaternion = camQuaternion.fromAngleAxis(90 * FastMath.DEG_TO_RAD, Vector3f.UNIT_X);
        cam4.setRotation(cam4.getRotation().set(camQuaternion));
        cam4.setFrustumFar(farPlane);
        float frustumScale = 8;
        cam4.setFrustumLeft(cam4.getFrustumLeft() * frustumScale);
        cam4.setFrustumRight(cam4.getFrustumRight() * frustumScale);
        cam4.setFrustumTop(cam4.getFrustumTop() * frustumScale);
        cam4.setFrustumBottom(cam4.getFrustumBottom() * frustumScale);
        cam4.resize(cam.getHeight(), cam.getHeight(), true);

        //vp = simpleApp.getRenderManager().createMainView("Top Right", cam4);
        vp = simpleApp.getRenderManager().createPreView("MiniMapCam", cam4);

        vp.setBackgroundColor(ColorRGBA.Blue);
        vp.setClearFlags(true, true, true);
        vp.attachScene(simpleApp.getRootNode().getChild("MapNode"));
        vp.attachScene(MapUIBox);

        simpleApp.getViewPort().detachScene(simpleApp.getRootNode());
        simpleApp.getViewPort().attachScene(simpleApp.getRootNode().getChild("MapNode"));

        for (int i = 0; i < simpleApp.getRootNode().getChildren().size(); i++) {
            System.out.println(simpleApp.getRootNode().getChild(i));
            simpleApp.getViewPort().attachScene(simpleApp.getRootNode().getChild(i));
        }
        simpleApp.getViewPort().detachScene(MapUIBox);

        //Object Url = assetManager.loadAsset("Textures/UI/myCoolStyle.groovy");
        //   Texture t1= assetManager.loadTexture("Textures/UI/Steampunk_UI_Buttons_1.png");
        // BaseStyles.loadStyleResources("groovy/glass-styles.groovy");
        BaseStyles.loadStyleResources("/Textures/UI/myCoolStyle.groovy");
        GuiGlobals.getInstance().getStyles().setDefaultStyle("myglass");

//       BaseStyles.loadGlassStyle();
//       GuiGlobals.getInstance().getStyles().setDefaultStyle("glass");
        font = assetManager.loadFont("Textures/font/FontCJK/fontCJK.fnt");

        GuiGlobals.getInstance().getStyles().setDefault(font);

    }

    @Override
    protected void cleanup(Application app) {

    }

    @Override
    protected void onEnable() {
        //vp.attachScene(simpleApp.getRootNode().getChild("MiniCam"));
        mesh.setMode(Mesh.Mode.LineStrip);
        Geometry gridGeometry = new Geometry("camT", mesh);
        Material Material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Material.setColor("Color", ColorRGBA.White);
        Material.getAdditionalRenderState().setWireframe(true);
        gridGeometry.setMaterial(Material);
        simpleApp.getRootNode().attachChild(gridGeometry);

        vp.attachScene(simpleApp.getRootNode().getChild("camT"));
    

        Container window = new Container("containerStyle");
        int width = 1024;
        int height = 1024;
        int samples = 1;
        FrameBuffer frameBuffer = new FrameBuffer(width, height, samples);
        Texture2D texture = new Texture2D(width, height, samples, Image.Format.RGBA8);
        Texture2D msDepth = new Texture2D(width, height, samples, Image.Format.Depth);
        frameBuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
        frameBuffer.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(msDepth));
        vp.setOutputFrameBuffer(frameBuffer);
        
        Camera vpCam = vp.getCamera();
        Texture2D target = new Texture2D(vpCam.getWidth(), vpCam.getHeight(), Image.Format.RGBA8);
        vp.getOutputFrameBuffer().addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(target));

        Panel guiElement = new Panel();
        guiElement.setBackground(new QuadBackgroundComponent(target));
 
        window.addChild(guiElement);
        window.setPreferredSize(new Vector3f(480, 480, 0));

        iMap.attachChild(window);
        simpleApp.getGuiNode().attachChild(iMap);
        window.setLocalTranslation(0, cam.getHeight() / 2, 0);
        window.addChild(new Container("myglass"));
 
        
        
//信息栏
        Container infoWindow = new Container("containerStyle");
        infoWindow.setPreferredSize(new Vector3f(cam.getWidth() / 2, cam.getHeight() / 4, 0));
        info.attachChild(infoWindow);
        simpleApp.getGuiNode().attachChild(info);
        infoWindow.setLocalTranslation(cam.getWidth() / 4f, cam.getHeight() / 4, 0);
        infoWindow.addChild(new Container("myglass"));

//命令栏
        Container directiveWindow = new Container("containerStyle");
        directiveWindow.setPreferredSize(new Vector3f(cam.getWidth() / 5f, cam.getHeight() / 4, 0));
        directive.attachChild(directiveWindow);
        simpleApp.getGuiNode().attachChild(directive);
        directiveWindow.setLocalTranslation(cam.getWidth() / 1.25f, cam.getHeight() / 4, 0);
        directiveWindow.addChild(new Container("myglass"));
        CursorEventControl.addListenersToSpatial(simpleApp.getGuiNode().getChild("miniMap"), new DefaultCursorListener() {
            @Override
            protected void click(CursorButtonEvent event, Spatial target, Spatial capture) {

            }

            @Override
            public void cursorButtonEvent(CursorButtonEvent event, Spatial target, Spatial capture) {
                // event.isConsumed();

                //System.err.println("isConsumed:" + event.isConsumed());
                System.err.println(event.toString());
            }

            @Override
            public void cursorEntered(CursorMotionEvent event, Spatial target, Spatial capture) {
                // System.err.println(event);//.getCollision().getContactPoint()
            }

            @Override
            public void cursorExited(CursorMotionEvent event, Spatial target, Spatial capture) {

            }

            @Override
            public void cursorMoved(CursorMotionEvent event, Spatial target, Spatial capture) {

            }

        });


    }

    @Override
    protected void onDisable() {

    }

    private void checkCollisionWithRay(Ray ray, CollisionResults results) {
        simpleApp.getRootNode().getChild("MapNode").collideWith(ray, results);
    }

    /**
     * 绘制小地图上的梯形
     *
     * @param results
     * @param vertices
     * @param indexX
     * @param indexY
     * @param indexZ
     */
    private void setMiniMapTrapezoidalMesh(CollisionResults results, float[] vertices, int indexX, int indexY, int indexZ) {
        CollisionResult closest = results.getClosestCollision();
        if (closest != null) {
            Vector3f contactPoint = closest.getContactPoint();
            // System.err.println(contactPoint + "," + indexX + "," + indexY + "," + indexZ);
            if (contactPoint != null) {
                float x = contactPoint.x;
                float z = contactPoint.z;
                if (x > 2048) {
                    x = 2048;
                } else if (x < 0) {
                    x = 0;
                }
                if (z > 2048) {
                    z = 2048;
                } else if (z < 0) {
                    z = 0;
                }
                vertices[indexX] = x;
                vertices[indexY] = 1;
                vertices[indexZ] = z;
            }

        } else {
            if (indexZ == 2) {
                vertices[indexZ] = vertices[5];
            }
            if (indexZ == 5) {
                vertices[indexZ] = vertices[2];
            }
            if (indexZ == 8) {
                vertices[indexZ] = vertices[11];
            }
            if (indexZ == 11) {
                vertices[indexZ] = vertices[8];
            }
            if (indexX == 0) {
                vertices[indexX] = vertices[9];
            }
            if (indexX == 3) {
                vertices[indexX] = vertices[6];
            }
            if (indexX == 6) {
                vertices[indexX] = vertices[3];
            }
            if (indexX == 9) {
                vertices[indexX] = vertices[0];
            }
        }
        iresults.clear();
    }

    @Override
    public void update(float tpf) {
        // 获取屏幕上的点的世界坐标
        Vector3f click3d0 = cam.getWorldCoordinates(new Vector2f(0, 0), 0f);
        Vector3f click3d1 = cam.getWorldCoordinates(new Vector2f(cam.getWidth(), 0), 0f);
        Vector3f click3d2 = cam.getWorldCoordinates(new Vector2f(cam.getWidth(), cam.getHeight()), 0f);
        Vector3f click3d3 = cam.getWorldCoordinates(new Vector2f(0, cam.getHeight()), 0f);

        // 计算方向
        Vector3f dir0 = click3d0.subtract(cam.getLocation());
        Vector3f dir1 = click3d1.subtract(cam.getLocation());
        Vector3f dir2 = click3d2.subtract(cam.getLocation());
        Vector3f dir3 = click3d3.subtract(cam.getLocation());

        dir0.normalizeLocal();
        dir1.normalizeLocal();
        dir2.normalizeLocal();
        dir3.normalizeLocal();

        // 创建射线并进行碰撞检测
        checkCollisionWithRay(new Ray(click3d0, dir0), iresults);
        setMiniMapTrapezoidalMesh(iresults, vertices, 0, 1, 2);

        checkCollisionWithRay(new Ray(click3d1, dir1), iresults);
        setMiniMapTrapezoidalMesh(iresults, vertices, 3, 4, 5);
        checkCollisionWithRay(new Ray(click3d2, dir2), iresults);
        setMiniMapTrapezoidalMesh(iresults, vertices, 6, 7, 8);
        checkCollisionWithRay(new Ray(click3d3, dir3), iresults);
        setMiniMapTrapezoidalMesh(iresults, vertices, 9, 10, 11);

        mesh.setBuffer(VertexBuffer.Type.Position, 3, vertices);
        mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createShortBuffer(indices));
        mesh.updateBound();

    }

}

This part of it sets up the minimap

        Container window = new Container("containerStyle");
        int width = 1024;
        int height = 1024;
        int samples = 1;
        FrameBuffer frameBuffer = new FrameBuffer(width, height, samples);
        Texture2D texture = new Texture2D(width, height, samples, Image.Format.RGBA8);
        Texture2D msDepth = new Texture2D(width, height, samples, Image.Format.Depth);
        frameBuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
        frameBuffer.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(msDepth));
        vp.setOutputFrameBuffer(frameBuffer);
        
        Camera vpCam = vp.getCamera();
        Texture2D target = new Texture2D(vpCam.getWidth(), vpCam.getHeight(), Image.Format.RGBA8);
        vp.getOutputFrameBuffer().addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(target));

        Panel guiElement = new Panel();
        guiElement.setBackground(new QuadBackgroundComponent(target));
 
        window.addChild(guiElement);
        window.setPreferredSize(new Vector3f(480, 480, 0));

        iMap.attachChild(window);
        simpleApp.getGuiNode().attachChild(iMap);
        window.setLocalTranslation(0, cam.getHeight() / 2, 0);
        window.addChild(new Container("myglass"));

I set up the lemur ui like this but why is the buffer not shown on the screen?


The contents of the vp’s buffer are not displayed correctly here

I’m sorry, but this is completely ridiculous and 100% bad practice. RootNode should never ever be seperated from the main viewPort, and you should avoid attaching anything else to it if possible. Please go fix this first.

Here is a working test case for rendering to the gui using a seperate viewport:

public class TestRenderToUI extends SimpleApplication {
    
    
    public static void main(String[] args) {
        new TestRenderToUI().start();
    }
    
    @Override
    public void simpleInitApp() {
        
        GuiGlobals.initialize(this);
        GuiGlobals.getInstance().setCursorEventsEnabled(false);
        flyCam.setMoveSpeed(10);
        viewPort.setBackgroundColor(ColorRGBA.DarkGray);
        
        Geometry g = new Geometry("cube", new Box(1, 1, 1));
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        g.setMaterial(mat);
        rootNode.attachChild(g);
        
        ViewPort vp = renderManager.createPreView("minimapView", cam);
        vp.setClearFlags(true, true, true);
        vp.setBackgroundColor(ColorRGBA.White);
        
        FrameBuffer framebuffer = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1);
        Texture2D texture = new Texture2D(cam.getWidth(), cam.getHeight(), 1, Image.Format.RGBA8);
        framebuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
        vp.setOutputFrameBuffer(framebuffer);
        vp.attachScene(rootNode);
        
        Panel minimap = new Panel();
        minimap.setBackground(new QuadBackgroundComponent(texture));
        minimap.setLocalTranslation(0, cam.getHeight()/2, 0);
        minimap.setPreferredSize(new Vector3f(cam.getWidth()/4, cam.getHeight()/4, 0));
        guiNode.attachChild(minimap);
        
    }
    
}
3 Likes

Thank you for helping me understand.

I need to remember to steal this for the lemur demos unless someone beats me to it.

1 Like