Login  Register

Re: Java3D in batch mode

Posted by philjord on Feb 18, 2019; 7:19am
URL: https://forum.jogamp.org/Java3D-in-batch-mode-tp4039415p4039548.html

Unfortunately I'm no Linux server expert so I really can't help with how to get a GraphicsEnvironment working with teh screen unplugged.

These days I mostly work with my adaptation of Java3D on Android that has no AWT in it at all, and allows me to simply create an OpenGL surface and give it to Java3D to work on.
new Canvas3D(GLWindow.create(new GLCapabilities(null))).

The examples for Java3D 1.6 have an offscreen example .
However it has 2 issues; 1 it’s not very dynamic so it’s hard to see what it can do just from the demo  and ; 2 most of the code in it is focused on coordinating an on screen canvas and an off screen canvas and a raster between them. With a lot of work you could turn this into a sort of “security camera in a 3d shooter” type demo, but it distracts from the power of the off screen rendering.
I would mention that in your case (of not having a screen plugged into your GPU card) I would expect GraphicsDevice.getBestConfiguration to work fine. GraphicsDevice.getBestConfiguration eventually through a chain of calls arrives at JoglPipeline.getBestConfiguration which calls new Frame() and there is a chance that any given configuration may be unable to instantiate that Frame, and hence the init code will fail. In that case you might consider using Java3D 1.7 with the Android (no-AWT) adaptation I made.

You need to manually invoke Canvas3D.renderOffScreenBuffer(); and optionally Canvas3D.waitForOffScreenRendering(). But the scene graph can be updated and used as usual and you can do anything you like with the image you give to Canvas3D.setOffScreenBuffer including (for example) saving multiple images as a video like SweetHome3D  does, using Java3D for the lower quality renders and ray tracing for the higher.

Below I’ve written a very small example of what can be easily and quickly done with the off screen canvas. It requires the examples for Java3D 1.6 because I use the mildly interesting SphereMotion scene, though if you like you can just grab that one file .
For brevity this code has had the license removed, but please note it is derived from the OffScreenTest of the Java3D examples and is covered by that license.

Each time you click the 3D image it will take a copy and display it in the label below it
 
package _testcase;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Screen3D;
import javax.media.j3d.View;
import javax.swing.ImageIcon;
import javax.swing.JLabel;

import com.sun.j3d.utils.universe.SimpleUniverse;

import org.jdesktop.j3d.examples.sphere_motion.SphereMotion;

public class OffScreen extends javax.swing.JFrame {

        private SimpleUniverse univ = null;
        private BranchGroup scene = null;

        private javax.swing.JPanel drawingPanel;

        private Canvas3D createOnScreenCanvasAndUniverse() {
                // Get the preferred graphics configuration for the default screen
                GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

                // Create a Canvas3D using the preferred configuration
                Canvas3D onScrCanvas = new Canvas3D(config, false);

                // Create simple universe with view branch
                univ = new SimpleUniverse(onScrCanvas);

                // This will move the ViewPlatform back a bit so the
                // objects in the scene can be viewed.
                univ.getViewingPlatform().setNominalViewingTransform();

                // Ensure at least 5 msec per frame (i.e., < 200Hz)
                univ.getViewer().getView().setMinimumFrameCycleTime(5);

                return onScrCanvas;
        }

        private OffScreenCanvas3D createOffScreenCanvas() {
                // request an offscreen Canvas3D with a single buffer configuration
                GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
                template.setDoubleBuffer(GraphicsConfigTemplate3D.UNNECESSARY);
                GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
                                .getBestConfiguration(template);

                // Create a offscreen Canvas3D using the single buffer configuration.
                OffScreenCanvas3D offScrCanvas = new OffScreenCanvas3D(gc, true);

                return offScrCanvas;
        }

        public OffScreen() {
                initComponents();

                // Create the content branch and add it to the universe
                scene = new BranchGroup();
                scene.addChild(new SphereMotion(new String[0]).createSceneGraph());

                // Create an OnScreenCanvas3D and SimpleUniverse; add canvas to drawing panel
                Canvas3D onScreenCanvas = createOnScreenCanvasAndUniverse();
                drawingPanel.add(onScreenCanvas, java.awt.BorderLayout.CENTER);

                // build a JLabel to display the image buffer
                ImageIcon output = new ImageIcon();
                JLabel outputJL = new JLabel(output);
                drawingPanel.add(outputJL, java.awt.BorderLayout.SOUTH);

                // Create an OffScreenCanvas3D
                OffScreenCanvas3D offScreenCanvas = createOffScreenCanvas();
                output.setImage(offScreenCanvas.bImage);

                // set the offscreen to match the onscreen
                Screen3D sOn = onScreenCanvas.getScreen3D();
                Screen3D sOff = offScreenCanvas.getScreen3D();
                sOff.setSize(sOn.getSize());
                sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth());
                sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight());

                //Because we have both on screen and off screen we need to attach the offscreen canvas to the universe view
                View view = univ.getViewer().getView();
                view.addCanvas3D(offScreenCanvas);

                //************************ on a click update the image shown in the JLabel
                onScreenCanvas.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e) {
                                offScreenCanvas.renderOffScreenBuffer();
                                offScreenCanvas.waitForOffScreenRendering();
                                // force the image to update
                                outputJL.setIcon(output);
                                outputJL.revalidate();
                                outputJL.repaint();
                        }
                });

                univ.addBranchGraph(scene);
        }

        private void initComponents() {
                drawingPanel = new javax.swing.JPanel();
                setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
                drawingPanel.setLayout(new java.awt.BorderLayout());
                drawingPanel.setPreferredSize(new java.awt.Dimension(500, 500));
                getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER);
                pack();
        }

        public static void main(String args[]) {
                System.setProperty("sun.awt.noerasebackground", "true");
                java.awt.EventQueue.invokeLater(new Runnable() {
                        public void run() {
                                new OffScreen().setVisible(true);
                        }
                });
        }

        class OffScreenCanvas3D extends Canvas3D {
                BufferedImage bImage;
                ImageComponent2D buffer;

                public OffScreenCanvas3D(GraphicsConfiguration gconfig, boolean offscreenflag) {
                        super(gconfig, offscreenflag);
                        bImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);

                        buffer = new ImageComponent2D(ImageComponent.FORMAT_RGB, bImage, true, false); // notice yup = false because we are rendering onto an ImageIcon
                        buffer.setCapability(ImageComponent2D.ALLOW_IMAGE_READ);

                        this.setOffScreenBuffer(buffer);
                }
        }
}