Transparency Not Working with Canvas?

Transparency (using BlendState), doesn’t seem to be working on Applications that use a Swing Canvas.



After having difficulty with my own Application, I did a test to come to this conclusion.

Here’s what I did…

Using the TestManyLights.java example bundled with JME I applied some BlendStates to the sphere to make them transparent. You can barely make them out in the screen shot. This code is almost exactly as the code distributed with JME, just modified to have transparent spheres.



Then, I took this code and copied it into the JMESwingTest application, I have provided the resulting screenshot and the code that I used to produce this. (I removed the input handlers). Notice that the spheres which were formerly almost completely transparent are now solid white. My best guess: SimpleGame must afford something with BlendStates that is not available in a JMECanvas ?











FIRST SHOT: SIMPLEGAME (WORKS AS EXPECTED)







SECOND SHOT: JMESWINGTEST





Code for JMESwingTest



import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;

import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.light.PointLight;
import com.jme.light.SimpleLightNode;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.JMECanvas;
import com.jme.system.canvas.JMECanvasImplementor;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;

/**
 * <code>JMESwingTest</code> is a test demoing the JMEComponent and
 * HeadlessDelegate integration classes allowing jME generated graphics to be
 * displayed in a AWT/Swing interface. Note the Repaint thread and how you grab
 * a canvas and add an implementor to it.
 *
 * @author Joshua Slack
 * @version $Id: JMESwingTest.java,v 1.18 2007/08/17 10:34:35 rherlitz Exp $
 */

public class JMESwingTest {
    private static final Logger logger = Logger.getLogger(JMESwingTest.class
            .getName());

    int width = 640, height = 480;

    Node colornode; //The node that stores the lights.

    static final float worldsize = 20;//The size of the world
   
    // Swing frame
    private SwingFrame frame;

    public JMESwingTest() {
        frame = new SwingFrame();
        // center the frame
        frame.setLocationRelativeTo(null);
        // show frame
        frame.setVisible(true);
    }

    /**
     * Main Entry point...
     *
     * @param args
     *            String[]
     */
    public static void main(String[] args)
    {
       initializePaths();
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            logger.logp(Level.SEVERE, JMESwingTest.class.toString(),
                    "main(args)", "Exception", e);
        }
        new JMESwingTest();
    }


   public static void initializePaths() {
      String curDir = System.getProperty("user.dir");

      String libraryPath = System.getProperty("java.library.path");
      libraryPath = curDir + ":.:" + libraryPath; // put the prepend the
                                       // current directory to the
                                       // library path
      System.setProperty("java.library.path", libraryPath);
      // System.out.println("CURRENT LIBRARY PATH: " +
      // System.getProperty("java.library.path"));

      String libraryPath2 = curDir;
      System.setProperty("org.lwjgl.librarypath", libraryPath2);
      // System.out.println("CURRENT LWJGL LIBRARY PATH: " +
      // System.getProperty("org.lwjgl.librarypath"));
   }
   
    // **************** SWING FRAME ****************

    // Our custom Swing frame... Nothing really special here.
    class SwingFrame extends JFrame {
        private static final long serialVersionUID = 1L;

        JPanel contentPane;
        JPanel mainPanel = new JPanel();
        LWJGLCanvas canvas = null;
        JButton coolButton = new JButton();
        JButton uncoolButton = new JButton();
        JPanel spPanel = new JPanel();
        JScrollPane scrollPane = new JScrollPane();
        JTree jTree1 = new JTree();
        JCheckBox scaleBox = new JCheckBox("Scale GL Image");
        JPanel colorPanel = new JPanel();
        JLabel colorLabel = new JLabel("BG Color:");
        JMECanvasImplementor impl;

        // Construct the frame
        public SwingFrame() {
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    dispose();
                }
            });

            init();
            pack();
        }

        // Component initialization
        private void init() {
            contentPane = (JPanel) this.getContentPane();
            contentPane.setLayout(new BorderLayout());

            mainPanel.setLayout(new GridBagLayout());

            setTitle("JME - SWING INTEGRATION TEST");

            //


GL STUFF

            // make the canvas:
            DisplaySystem display = DisplaySystem.getDisplaySystem(LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER);
            display.registerCanvasConstructor("AWT", LWJGLAWTCanvasConstructor.class);
            canvas = (LWJGLCanvas)display.createCanvas(width, height);
            canvas.setUpdateInput(true);
            canvas.setTargetRate(60);

            // add a listener... if window is resized, we can do something about
            // it.
            canvas.addComponentListener(new ComponentAdapter() {
                public void componentResized(ComponentEvent ce) {
                    doResize();
                }
            });
           
            // Setup key and mouse input
            KeyInput.setProvider(KeyInput.INPUT_AWT);
            KeyListener kl = (KeyListener) KeyInput.get();
            canvas.addKeyListener(kl);
            AWTMouseInput.setup(canvas, false);

            // Important! Here is where we add the guts to the panel:
            impl = new MyImplementor(width, height);
            canvas.setImplementor(impl);

            //
END OF GL STUFF

            coolButton.setText("Cool Button");
            uncoolButton.setText("Uncool Button");

            colorPanel.setBackground(java.awt.Color.black);
            colorPanel.setToolTipText("Click here to change Panel BG color.");
            colorPanel.setBorder(BorderFactory.createRaisedBevelBorder());
            colorPanel.addMouseListener(new java.awt.event.MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    final java.awt.Color color = JColorChooser.showDialog(
                            SwingFrame.this, "Choose new background color:",
                            colorPanel.getBackground());
                    if (color == null)
                        return;
                    colorPanel.setBackground(color);
                    Callable<?> call = new Callable<Object>() {
                        public Object call() throws Exception {
                            canvas.setBackground(color);
                            return null;
                        }
                    };
                    GameTaskQueueManager.getManager().render(call);
                }
            });

            scaleBox.setOpaque(false);
            scaleBox.setSelected(true);
            scaleBox.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (canvas != null)
                        doResize();
                }
            });

            spPanel.setLayout(new BorderLayout());
            contentPane.add(mainPanel, BorderLayout.WEST);
            mainPanel.add(scaleBox,
                    new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
                            GridBagConstraints.CENTER,
                            GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
                                    5), 0, 0));
            mainPanel.add(colorLabel,
                    new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
                            GridBagConstraints.CENTER,
                            GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
                                    5), 0, 0));
            mainPanel.add(colorPanel, new GridBagConstraints(0, 2, 1, 1, 0.0,
                    0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
                    new Insets(5, 5, 0, 5), 25, 25));
            mainPanel.add(coolButton,
                    new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0,
                            GridBagConstraints.CENTER,
                            GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
                                    5), 0, 0));
            mainPanel.add(uncoolButton,
                    new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0,
                            GridBagConstraints.CENTER,
                            GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
                                    5), 0, 0));
            mainPanel.add(spPanel, new GridBagConstraints(0, 5, 1, 1, 1.0, 1.0,
                    GridBagConstraints.CENTER, GridBagConstraints.BOTH,
                    new Insets(5, 5, 0, 5), 0, 0));
            spPanel.add(scrollPane, BorderLayout.CENTER);

            scrollPane.setViewportView(jTree1);
            canvas.setBounds(0, 0, width, height);
            contentPane.add(canvas, BorderLayout.CENTER);
        }

        protected void doResize() {
            if (scaleBox != null && scaleBox.isSelected()) {
                impl.resizeCanvas(canvas.getWidth(), canvas.getHeight());
            } else {
                impl.resizeCanvas(width, height);
            }
            ((JMECanvas)canvas).makeDirty();
        }

        // Overridden so we can exit when window is closed
        protected void processWindowEvent(WindowEvent e) {
            super.processWindowEvent(e);
            if (e.getID() == WindowEvent.WINDOW_CLOSING) {
                System.exit(0);
            }
        }
    }

    // IMPLEMENTING THE SCENE:

    class MyImplementor extends SimpleCanvasImpl {

        private Quaternion rotQuat;
        private float angle = 0;
        private Vector3f axis;
        private Box box;
        long startTime = 0;
        long fps = 0;
        private InputHandler input;

        public MyImplementor(int width, int height) {
            super(width, height);
        }

       
       
        private void initializeScene()
        {
             //First we remove all the lights from the light state. And create a


            FastMath.rand.setSeed(1337);
           
            //Now add all the lights.
            colornode = new Node("LightNode");
            for (int i = 0; i < 40; i++) {
                this.randomLight(i);
            }
            //Add the spheres.
            for (int i = 0; i < 30; i++) {
                this.randomSphere(i);
            }
           
            rootNode.updateGeometricState(0.0f, true);
           
            //We do not want to use lighting on the spears that represent lights so
            // we add a disabled render state.
            LightState nl = com.jme.system.DisplaySystem.getDisplaySystem()
                    .getRenderer().createLightState();
            nl.setEnabled(false);
            colornode.setRenderState(nl);
            rootNode.attachChild(colornode);
            rootNode.updateRenderState();
           
           
        }
        void randomSphere(int i) {
            //Crate a sphere and position it.
            Sphere newSphere = new Sphere("sp" + i, 10, 10, 2);
            newSphere.setModelBound(new BoundingSphere());
            newSphere.updateModelBound();
            newSphere.setLocalTranslation(new Vector3f(FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize));

//            newSphere.setLightCombineMode(Spatial.LightCombineMode.Replace);

           
           
            applyTransparency(newSphere,0.1f);
           

            rootNode.attachChild(newSphere);
        }

        void randomLight(int i) {
            //Chose the color for the lights.
            ColorRGBA LightColor = ColorRGBA.randomColor();

            //Create a sphere to show where the light is in the demo.
            Sphere LightSphere = new Sphere("lp" + i, 10, 10, .1f);
            LightSphere.setModelBound(new BoundingSphere());
            LightSphere.updateModelBound();
            LightSphere.setLightCombineMode(Spatial.LightCombineMode.Off);
            LightSphere.setDefaultColor(LightColor);

          
           
           
            //Create a new point light and fill out the properties
            PointLight pointLight = new PointLight();
            pointLight.setAttenuate(true);
            pointLight.setConstant(/* FastMath.rand.nextFloat() */.1f);
            pointLight.setLinear(/* FastMath.rand.nextFloat()* */.01f);
            pointLight.setQuadratic(/* FastMath.rand.nextFloat() */.1f);
            pointLight.setEnabled(true);
            pointLight.setDiffuse(LightColor);
            //pointLight.setSpecular(LightColor);
            pointLight
                    .setAmbient(new com.jme.renderer.ColorRGBA(.1f, .1f, .1f, .1f));

            LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
         lightState.attach(pointLight);

            //Create a node to hold the light and add a light node with this light.
            final Node mnod = new Node("P" + i + " Light pos");
            SimpleLightNode ln = new SimpleLightNode("ln" + i, pointLight);
            mnod.setLocalTranslation(new Vector3f(FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                    * worldsize * 2 - worldsize));
            mnod.attachChild(LightSphere);

            mnod.addController(new Controller() {
               
                private static final long serialVersionUID = 1L;

                float timeX = FastMath.rand.nextFloat() * FastMath.PI * 8;
                float timeY = FastMath.rand.nextFloat() * FastMath.PI * 8;
                float timeZ = FastMath.rand.nextFloat() * FastMath.PI * 8;

                @Override
                public void update(float tpf) {
                    this.timeX += tpf;
                    this.timeY += tpf;
                    this.timeZ += tpf;
                   
                   
                    mnod.getLocalTranslation().set(FastMath.sin(this.timeX*0.4f) * worldsize * 1,
                            FastMath.cos(this.timeY*0.5f) * worldsize * 1,
                            FastMath.sin(this.timeZ*0.6f) * worldsize * 1);
                                   
                }
            });
           
            mnod.attachChild(ln);

            //Add the light to the world part 2.
            colornode.attachChild(mnod);
        }
       
        public void simpleSetup()
        {

           initializeScene();
           
           
           

        }

       
        /**
         * Makes the target mesh be semi-transparent
         * @param target
         * @return True if the apply was successful, False is unsuccessful
         */
        public boolean applyTransparency(TriMesh mesh, float opacity)
        {
           try
           {
              
               
                MaterialState matState = DisplaySystem.getDisplaySystem().getRenderer().createMaterialState();
               
             matState.setAmbient(new ColorRGBA(0.0f, 0.0f, 0.0f, opacity));
                matState.setDiffuse(new ColorRGBA(0.91f, 0.91f, 0.91f, opacity));
                matState.setSpecular(new ColorRGBA(1.0f, 1.0f, 1.0f, opacity));
                matState.setShininess(15.0f);
                matState.setEmissive(new ColorRGBA(0.0f, 0.0f, 0.0f, opacity));
                matState.setEnabled(true);
               
                // IMPORTANT: this is used to handle the internal sphere faces when
                matState.setMaterialFace(MaterialState.MaterialFace.FrontAndBack);
                mesh.setRenderState(matState);
        
                BlendState alphaState = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
             //create an alphaness for the spheres
                alphaState.setBlendEnabled(true);
                alphaState.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
                alphaState.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
                alphaState.setTestEnabled(true);
                alphaState.setTestFunction(BlendState.TestFunction.GreaterThan);
                alphaState.setEnabled(true);
        
                mesh.setRenderState(alphaState);
                mesh.updateRenderState();
        
                // IMPORTANT: since the sphere will be transparent, place it
                // in the transparent render queue!
                mesh.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);   
              
               
           }
           catch (Exception e)
           {
              System.err.println("Transparency effect failed");
              e.printStackTrace();
              return false;
          }
           
           
          return true;           
        }
       
        public void updateScene()
        {
           rootNode.sortLights();   
           
        }
       
        public void simpleUpdate()
        {
           updateScene();
        }
    }
}


you did not apply the lightstate to your root node.


void randomLight(int i) {
....
    rootNode.setRenderState(lightState);
}

1 Like

The Test classes inherit from BasicSimpleGame, and there already exists a lightstate which is applied to the rootNode.

In TestManyLights the lights are aded to this already existing lightstate.

1 Like

Thank you! That solved my problems!



Why is it not necessary to apply the lightstate to the rootNode of TestManyLights?