Transparency does not work with my own custom camera

Hello!



My last try posting this was a double post and both posts got removed, so i am trying once again…



I have a problem with transparency and my own custom camera.



As long as i use the FlyByCamera, the following works as expected (the Sphere is visible through the transparent parts of the Box).



[java] Box boxshape3 = new Box(new Vector3f(0f, 0f, 0f), 1f, 1f, 0.01f);

Geometry window_frame = new Geometry("window frame", boxshape3);

Material mat_tt = new Material(assetManager,

"Common/MatDefs/Misc/SimpleTextured.j3md");

mat_tt.setTexture("m_ColorMap", assetManager

.loadTexture("Textures/ColoredTex/Monkey.png"));

mat_tt.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

window_frame.setMaterial(mat_tt);

rootNode.attachChild(window_frame);



Sphere s1 = new Sphere(20, 40, 3f);

Geometry geoms1 = new Geometry("Sphere", s1);

Material mats1 = new Material(assetManager,

"Common/MatDefs/Misc/SimpleTextured.j3md");

mats1.setTexture("m_ColorMap", assetManager

.loadTexture("Textures/ColoredTex/Monkey.png"));

geoms1.setLocalTranslation(new Vector3f(0f, 20f, -30f));

geoms1.setMaterial(mats1);



rootNode.attachChild(geoms1);[/java]



However, when I use my own slightly modified SimpleApplication (SimpleGame2D) along with my own camera (StrategyCamera), the transparency ceases to work. Instead its all black in the transparent parts.



SimpleGame2D:



[java]package de.fha.jme3;



import com.jme3.app.Application;

import com.jme3.app.StatsView;

import com.jme3.font.BitmapFont;

import com.jme3.font.BitmapText;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.queue.RenderQueue.Bucket;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial.CullHint;

import com.jme3.system.AppSettings;

import com.jme3.system.JmeSystem;

import com.jme3.system.JmeContext.Type;

import com.jme3.util.BufferUtils;



public abstract class SimpleGame2D extends Application {



protected Node rootNode = new Node("Root Node");

protected Node guiNode = new Node("Gui Node");



protected float secondCounter = 0.0f;

protected BitmapText fpsText;

protected BitmapFont guiFont;

protected StatsView statsView;



protected StrategyCamera stratCam;

protected boolean showSettings = true;



private AppActionListener actionListener = new AppActionListener();



private class AppActionListener implements ActionListener {

public void onAction(String name, boolean value, float tpf) {

if (!value)

return;



if (name.equals("SIMPLEAPP_Exit")) {

stop();

} else if (name.equals("SIMPLEAPP_CameraPos")) {

if (cam != null) {

Vector3f loc = cam.getLocation();

Quaternion rot = cam.getRotation();

System.out.println("Camera Position: (" + loc.x + ", "

  • loc.y + ", " + loc.z + ")");

    System.out.println("Camera Rotation: " + rot);

    System.out.println("Camera Direction: "
  • cam.getDirection());

    }

    } else if (name.equals("SIMPLEAPP_Memory")) {

    BufferUtils.printCurrentDirectMemory(null);

    }

    }

    }



    public SimpleGame2D() {

    super();

    }



    @Override

    public void start() {

    // set some default settings in-case

    // settings dialog is not shown

    if (settings == null) {

    settings = new AppSettings(false);

    settings.put("Width", 1920);

    settings.put("Height", 1080);

    settings.put("BitsPerPixel", 32);

    settings.put("Frequency", 60);

    settings.put("DepthBits", 24);

    settings.put("StencilBits", 0);

    settings.put("Samples", 4);

    settings.put("Fullscreen", true);

    settings.put("Title", "jMonkey Engine 3.0");

    settings.put("Renderer", AppSettings.LWJGL_OPENGL2);

    settings.put("AudioRenderer", AppSettings.LWJGL_OPENAL);

    settings.put("DisableJoysticks", true);

    settings.put("UseInput", true);



    settings.put("VSync", true);

    settings.put("FrameRate", 60);

    }



    // show settings dialog

    if (showSettings) {

    if (!JmeSystem.showSettingsDialog(settings))

    return;

    }



    super.start();

    }



    public StrategyCamera getStrategyCamera() {

    return stratCam;

    }



    public Node getGuiNode() {

    return guiNode;

    }



    public Node getRootNode() {

    return rootNode;

    }



    public boolean isShowSettings() {

    return showSettings;

    }



    public void setShowSettings(boolean showSettings) {

    this.showSettings = showSettings;

    }



    public void loadFPSText() {

    guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");

    fpsText = new BitmapText(guiFont, false);

    fpsText.setSize(guiFont.getCharSet().getRenderedSize());

    fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);

    fpsText.setText("Frames per second");

    guiNode.attachChild(fpsText);

    }



    public void loadStatsView() {

    statsView = new StatsView("Statistics View", assetManager, renderer

    .getStatistics());

    // move it up so it appears above fps text

    statsView.setLocalTranslation(0, fpsText.getLineHeight(), 0);

    guiNode.attachChild(statsView);

    }



    @Override

    public void initialize() {

    super.initialize();



    guiNode.setQueueBucket(Bucket.Gui);

    guiNode.setCullHint(CullHint.Never);

    loadFPSText();

    loadStatsView();

    viewPort.attachScene(rootNode);

    guiViewPort.attachScene(guiNode);



    if (inputManager != null) {

    stratCam = new StrategyCamera(cam);

    stratCam.registerWithInput(inputManager);



    if (context.getType() == Type.Display)

    inputManager.addMapping("SIMPLEAPP_Exit", new KeyTrigger(

    KeyInput.KEY_ESCAPE));



    inputManager.addMapping("SIMPLEAPP_CameraPos", new KeyTrigger(

    KeyInput.KEY_C));

    inputManager.addMapping("SIMPLEAPP_Memory", new KeyTrigger(

    KeyInput.KEY_M));

    inputManager.addListener(actionListener, "SIMPLEAPP_Exit",

    "SIMPLEAPP_CameraPos", "SIMPLEAPP_Memory");

    }



    // call user code

    simpleInitApp();

    }



    @Override

    public void update() {

    if (speed == 0 || paused)

    return;



    super.update();

    float tpf = timer.getTimePerFrame() * speed;



    secondCounter += timer.getTimePerFrame();

    int fps = (int) timer.getFrameRate();

    if (secondCounter >= 1.0f) {

    fpsText.setText("Frames per second: " + fps);

    secondCounter = 0.0f;

    }



    // update camera

    stratCam.update(tpf);



    // update states

    stateManager.update(tpf);



    // simple update and root node

    simpleUpdate(tpf);

    rootNode.updateLogicalState(tpf);

    guiNode.updateLogicalState(tpf);

    rootNode.updateGeometricState();

    guiNode.updateGeometricState();



    // render states

    stateManager.render(renderManager);

    renderManager.render(tpf);

    simpleRender(renderManager);

    }



    public abstract void simpleInitApp();



    public void simpleUpdate(float tpf) {

    }



    public void simpleRender(RenderManager rm) {

    }



    }

    [/java]





    StrategyCamera:



    [java]package de.fha.jme3;



    import com.jme3.input.FlyByCamera;

    import com.jme3.input.InputManager;

    import com.jme3.input.MouseInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.AnalogListener;

    import com.jme3.input.controls.MouseAxisTrigger;

    import com.jme3.input.controls.MouseButtonTrigger;

    import com.jme3.math.FastMath;

    import com.jme3.math.Matrix3f;

    import com.jme3.math.Quaternion;

    import com.jme3.math.Vector2f;

    import com.jme3.math.Vector3f;

    import com.jme3.renderer.Camera;



    public class StrategyCamera implements AnalogListener, ActionListener {



    private static final String COMMAND_MOUSE_IN = "STRATCAM_Mouse_In";

    private static final String COMMAND_MOUSE_OUT = "STRATCAM_Mouse_Out";

    private static final String COMMAND_MOUSE_LEFT = "STRATCAM_Mouse_Left";

    private static final String COMMAND_MOUSE_RIGHT = "STRATCAM_Mouse_Right";

    private static final String COMMAND_MOUSE_UP = "STRATCAM_Mouse_Up";

    private static final String COMMAND_MOUSE_DOWN = "STRATCAM_Mouse_Down";

    private static final String COMMAND_MOUSE_MIDDLE_DRAG = "STRATCAM_Mouse_Middle_Drag";



    private InputManager inputManager;



    private Camera cam;

    private boolean enabled = true;



    private Vector3f initialUpVector = new Vector3f(0f, 0f, 1f);

    private boolean canRotate;

    private float rotationSpeed = 3f;



    private Vector3f initialLocation = new Vector3f(0f, -20f, 10f);

    private Vector3f initialViewpoint = new Vector3f(0f, 0f, 0f);



    private float maxY = 240f;

    private float minY = -240f;

    private float maxX = 240f;

    private float minX = -240f;



    private float moveSpeed = 1.35f;

    private float movingUp = 0f;

    private float movingDown = 0f;

    private float movingLeft = 0f;

    private float movingRight = 0f;

    private float actualMovingUp = 0f;

    private float actualMovingDown = 0f;

    private float actualMovingLeft = 0f;

    private float actualMovingRight = 0f;

    private float moveAnimationSpeed = 0.35f;



    private float zoomSpeed = 0.3f;

    private float zoom = 0f;

    private float actualZoom = 1f;

    private float zoomAnimationSpeed = 0.2f;

    private float maxZoomOut = 22f;

    private float minZoomOut = 0f;

    private boolean zoomEnabled = true;



    private boolean rotatingAllowed = true;

    private boolean bankingAllowed = false;



    private Vector3f leftVector = new Vector3f(-1f, 0f, 0f);

    private Vector3f upVector = new Vector3f(0f, 1f, 0f);



    private Vector3f camToViewpoint;

    private Vector3f camToCursorpoint;



    public StrategyCamera(Camera cam) {

    this.cam = cam;

    this.cam.setFrustumPerspective(45, ((float) cam.getWidth())

    / ((float) this.cam.getHeight()), 0.01f, 10000f);

    this.cam.setLocation(initialLocation);

    zoom = FastMath.log(cam.getLocation().z) / zoomSpeed;

    actualZoom = zoom;

    camToViewpoint = initialViewpoint.subtract(cam.getLocation());

    this.cam.setDirection(camToViewpoint.normalize());

    }



    private void updateCamToViewpoint() {

    Vector3f camLocation = cam.getLocation();

    Vector3f camDirection = cam.getDirection();

    camToViewpoint = camDirection.mult(-camLocation.z / camDirection.z);

    }



    private void updateCamToCursorpoint() {

    Vector2f cursor = inputManager.getCursorPosition();

    Vector3f direction = cam.getLocation().subtract(

    cam.getWorldCoordinates(cursor, 1f));

    direction = direction.mult(-cam.getLocation().z / direction.z);

    camToCursorpoint = direction;

    }



    public void update(float tpf) {

    actualMovingUp += (movingUp - actualMovingUp) * moveAnimationSpeed;

    actualMovingDown += (movingDown - actualMovingDown)
  • moveAnimationSpeed;

    actualMovingLeft += (movingLeft - actualMovingLeft)
  • moveAnimationSpeed;

    actualMovingRight += (movingRight - actualMovingRight)
  • moveAnimationSpeed;

    if (actualMovingUp > 0.01f) {

    keyUp(tpf * actualMovingUp);

    } else if (actualMovingDown > 0.1f) {

    keyDown(tpf * actualMovingDown);

    }

    if (actualMovingLeft > 0.01f) {

    keyLeft(tpf * actualMovingLeft);

    } else if (actualMovingRight > 0.01f) {

    keyRight(tpf * actualMovingRight);

    }



    updateCamToCursorpoint();

    updateCamToViewpoint();



    float z = cam.getLocation().z;

    float zoomDifference = zoom - actualZoom;

    if (zoomDifference < -0.01f) {

    actualZoom += zoomDifference * zoomAnimationSpeed;

    float targetZ = FastMath.exp(actualZoom * zoomSpeed);

    float factor = 1 - (targetZ / z);

    cam.setLocation(cam.getLocation()

    .add(camToCursorpoint.mult(factor)));

    } else if (zoomDifference > 0.01f) {

    actualZoom += zoomDifference * zoomAnimationSpeed;

    float targetZ = FastMath.exp(actualZoom * zoomSpeed);

    float factor = 1 - (targetZ / z);

    cam.setLocation(cam.getLocation().add(camToViewpoint.mult(factor)));

    }

    }



    private void rotateCamera(float value, Vector3f axis) {



    updateCamToViewpoint();



    float length = camToViewpoint.length();



    Matrix3f mat = new Matrix3f();

    mat.fromAngleNormalAxis(rotationSpeed * value, axis);



    Vector3f up = cam.getUp();

    Vector3f left = cam.getLeft();

    Vector3f dir = cam.getDirection();



    mat.mult(up, up);

    mat.mult(left, left);

    mat.mult(dir, dir);



    Quaternion q = new Quaternion();

    q.fromAxes(left, up, dir);

    q.normalize();



    cam.setAxes(q);



    up = cam.getUp();

    left = cam.getLeft();



    Vector3f viewpoint = cam.getLocation().add(camToViewpoint);

    Vector3f expectedCamLocation = viewpoint.add(cam.getDirection()

    .normalize().mult(-length));

    if (expectedCamLocation.z < 1f) {

    expectedCamLocation.z = 1f;

    }

    cam.setLocation(expectedCamLocation);

    cam.lookAt(viewpoint, cam.getUp());



    zoom = FastMath.log(cam.getLocation().z) / zoomSpeed;

    actualZoom = zoom;



    leftVector = cam.getLeft().normalize();

    upVector = new Vector3f(cam.getUp().x, cam.getUp().y, 0f).normalize();

    if (upVector.equals(new Vector3f(0f, 0f, 0f))) {

    upVector = new Vector3f(cam.getDirection().x, cam.getDirection().y,

    0f).normalize();

    }

    }



    /**
  • Sets the move speed. The speed is given in world units per second.

    *
  • @param moveSpeed

    */

    public void setMoveSpeed(float moveSpeed) {

    this.moveSpeed = moveSpeed;

    }



    /**
  • @param enable
  •        If false, the camera will ignore input.<br />
    

*/

public void setEnabled(boolean enable) {

enabled = enable;

}



/**

  • @return If enabled
  • @see FlyByCamera#setEnabled(boolean)

    */

    public boolean isEnabled() {

    return enabled;

    }



    /**
  • Registers the FlyByCamera to receive input events from the provided
  • Dispatcher.

    *
  • @param dispacher

    */

    public void registerWithInput(InputManager inputManager) {



    this.inputManager = inputManager;



    String[] mappings = new String[] { COMMAND_MOUSE_IN, COMMAND_MOUSE_OUT,

    COMMAND_MOUSE_LEFT, COMMAND_MOUSE_RIGHT, COMMAND_MOUSE_UP,

    COMMAND_MOUSE_DOWN, COMMAND_MOUSE_MIDDLE_DRAG };



    inputManager.addMapping(COMMAND_MOUSE_UP,

    new MouseAxisTrigger(1, false));

    inputManager.addMapping(COMMAND_MOUSE_DOWN, new MouseAxisTrigger(1,

    true));

    inputManager.addMapping(COMMAND_MOUSE_RIGHT, new MouseAxisTrigger(0,

    false));

    inputManager.addMapping(COMMAND_MOUSE_LEFT, new MouseAxisTrigger(0,

    true));

    inputManager.addMapping(COMMAND_MOUSE_OUT,

    new MouseAxisTrigger(2, true));

    inputManager.addMapping(COMMAND_MOUSE_IN,

    new MouseAxisTrigger(2, false));

    inputManager.addMapping(COMMAND_MOUSE_MIDDLE_DRAG,

    new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));



    inputManager.addListener(this, mappings);

    }



    public void onAction(String name, boolean value, float tpf) {

    if (name.equals(COMMAND_MOUSE_MIDDLE_DRAG)) {

    canRotate = value;

    }

    }



    private void mouseWheel(float value) {

    if (zoomEnabled) {

    updateCamToCursorpoint();

    Vector3f cursorpoint = cam.getLocation().add(camToCursorpoint);

    if (isInBounds(cursorpoint) || value > 0f) {

    zoom += value;

    if (zoom > maxZoomOut) {

    zoom = maxZoomOut;

    } else if (zoom < minZoomOut) {

    zoom = minZoomOut;

    }

    }

    }

    }



    private boolean isInBounds(Vector3f vector) {

    return !(vector.x > maxX || vector.x < minX || vector.y > maxY || vector.y < minY);

    }



    private void moveCamera(float value, Vector3f direction) {

    updateCamToViewpoint();

    Vector3f movement = direction.normalize().mult(

    value * moveSpeed * camToViewpoint.length());

    Vector3f viewpoint = cam.getLocation().add(camToViewpoint);

    Vector3f newViewpoint = viewpoint.add(movement);

    if (!isInBounds(newViewpoint)) {

    if (newViewpoint.x > maxX) {

    newViewpoint.x = maxX;

    } else if (newViewpoint.x < minX) {

    newViewpoint.x = minX;

    }

    if (newViewpoint.y > maxY) {

    newViewpoint.y = maxY;

    } else if (newViewpoint.y < minY) {

    newViewpoint.y = minY;

    }

    System.out.println(newViewpoint);

    movement = newViewpoint.subtract(viewpoint);

    }

    cam.setLocation(cam.getLocation().add(movement));

    }



    private void keyUp(float value) {

    moveCamera(value, upVector);

    }



    private void keyDown(float value) {

    moveCamera(value, upVector.negate());

    }



    private void keyLeft(float value) {

    moveCamera(value, leftVector);

    }



    private void keyRight(float value) {

    moveCamera(value, leftVector.negate());

    }



    private void mouseLeft() {

    float x = inputManager.getCursorPosition().x;

    if (x <= cam.getWidth() - 2) {

    movingRight = 0f;

    if (x < 2) {

    movingLeft = 1f;

    }

    }

    }



    private void mouseRight() {

    float x = inputManager.getCursorPosition().x;

    if (x >= 2) {

    movingLeft = 0f;

    if (x > cam.getWidth() - 2) {

    movingRight = 1f;

    }

    }

    }



    private void mouseUp() {

    float y = inputManager.getCursorPosition().y;

    if (y >= 2) {

    movingDown = 0f;

    if (y > cam.getHeight() - 2) {

    movingUp = 1f;

    }

    }

    }



    private void mouseDown() {

    float y = inputManager.getCursorPosition().y;

    if (y <= cam.getHeight() - 2) {

    movingUp = 0f;

    if (y < 2) {

    movingDown = 1f;

    }

    }

    }



    public void onAnalog(String name, float value, float tpf) {

    if (!enabled) {

    return;

    }



    if (name.equals(COMMAND_MOUSE_IN)) {

    mouseWheel(-value);

    } else if (name.equals(COMMAND_MOUSE_OUT)) {

    mouseWheel(value);

    } else if (name.equals(COMMAND_MOUSE_LEFT)) {

    if (!canRotate) {

    mouseLeft();

    } else if (rotatingAllowed) {

    rotateCamera(value, initialUpVector);

    }

    } else if (name.equals(COMMAND_MOUSE_RIGHT)) {

    if (!canRotate) {

    mouseRight();

    } else if (rotatingAllowed) {

    rotateCamera(-value, initialUpVector);

    }

    } else if (name.equals(COMMAND_MOUSE_UP)) {

    if (!canRotate) {

    mouseUp();

    } else if (bankingAllowed) {

    rotateCamera(-value, cam.getLeft());

    }

    } else if (name.equals(COMMAND_MOUSE_DOWN)) {

    if (!canRotate) {

    mouseDown();

    } else if (bankingAllowed) {

    rotateCamera(value, cam.getLeft());

    }

    }

    }



    }

    [/java]



    The only substantial difference between this and the original combination of SimpleApplication and FlyByCamera that i could find is that my StrategyCamera (which I’d happily share with you once i get it to work properly) has its own update()-method that is called by the update()-method of SimpleGame2D.



    I tried to put the update()-call in different lines in SimpleGame2D.update() but to no effect. I guess i must have omitted some crucial updating stuff, but I have no idea what it is. I hope you can help me.



    By the way: I need this update() on the StrategyCamera in order to smooth zooming and moving around. If there is a better way to do that, i am all ears (or rather eyes).
hogi said:
By the way: I need this update() on the StrategyCamera in order to smooth zooming and moving around. If there is a better way to do that, i am all ears (or rather eyes).

Yes, your camera could implement control (or extend AbstractControl), but it would require it to be associated with a spatial of the scene.
Another solution (better in your case) would be to make it an AppState. So you would have an update method and the engine would take care of calling it.
The camera would be completely autonomous, and you could use the simple application.

I don't know however if this is gonna fix th transparency issue.

Hm as far as I look at the code I cannot really see what could possible cause this. The application seems to be mostly identicall with simpleapplication and the camera only moves the same cam object the flycam also uses.

thanks for your answers!



ok… i detected my problem - it was a little line missing in the code for the transparent geometry:



[java]geometry.setQueueBucket(Bucket.Transparent);[/java]



somehow i managed to lose it on the way from the HelloMaterial example to my own code… the difference between FlyByCamera and StrategyCamera was simply location and direction relative to the transparent geometry - the former had both the transparent and the background geometries in sight right from the beginning, while the latter had to be moved first to see both geometries. this resulted in a seemingly functional transparency in the former case (strangely enough), and an obviously non-functional transparency in the latter.



i will now improve readability and usability for my StrategyCamera and post it again for anybody to use.