offscreen cnavas and Texture3D

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

offscreen cnavas and Texture3D

babor
Hi,

I've encountered some problems using Java3D 1.6-pre9 with updating the Texture3D and offscreen canvas rendering. It works fine on Java3D 1.5.2 so I assume this is some kind of bug. I am running Linux x86_64. The problem is the same on Java 6, Java 7 and all tested JREs.

The case is as follows:
- the universe has some geometry using Texture3D
- a default Canvs3D is used for Swing view and manipulation
- additional offscreen Canvas3D is used for writting JPEGs
- every off screen JPEG is rendered fine, as long as the texture doesn't change
- after texture change with appearance.setTexture method, every offscreen rendered frame is identical to the one saved before texture change

The code to reproduce is below. Set the path to store images.  Left-click to switch texture, right-click to save JPEG.
Reproduce problem by following steps: run application, right click to save image, left click to change texture, rotate, right click to save image again. Both saved images are the same.

This is of course just a simplified extracted example. The full scenario is much more complex and it is required to set texture of different dimensions sometimes, so the code of switching texture could not be much simplified.

Any ideas?

<pre>
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.universe.SimpleUniverse;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.ImageComponent3D;
import javax.media.j3d.Node;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Texture3D;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.View;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.vecmath.Point3d;

public class Texture3DTest {
   
    private static int counter = 0;
    private static int textureType = 1;
    private static String pathPrefix = "/tmp";

    public static void main(String[] args) {
        JFrame frame = new JFrame("test frame");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(0, 0, 640, 480);
        frame.setLayout(new BorderLayout());
        JPanel canvasParent = new JPanel();
        canvasParent.setLayout(new BorderLayout());

        GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
        GraphicsConfiguration gcfg = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getBestConfiguration(template);
        Canvas3D canvas = new Canvas3D(gcfg);

        BoundingSphere universeBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100000.0);
        SimpleUniverse universe = new SimpleUniverse(canvas);
        View view = canvas.getView();
        view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);
        view.setTransparencySortingPolicy(View.TRANSPARENCY_SORT_GEOMETRY);
        OrbitBehavior orbit = new OrbitBehavior(universe.getCanvas(), OrbitBehavior.REVERSE_ALL);
        orbit.setSchedulingBounds(universeBounds);
        universe.getViewingPlatform().setViewPlatformBehavior(orbit);
        BranchGroup scene = new BranchGroup();

        //-----------------add 3D textured planes---------------------------
        Shape3D tr = new Shape3D();
        final Appearance ap = new Appearance();
        ap.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
        tr.setCapability(Shape3D.ALLOW_APPEARANCE_READ);
        tr.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
        tr.setCapability(Shape3D.ALLOW_GEOMETRY_READ);
        tr.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
        tr.setCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ);
        TransparencyAttributes ta = new TransparencyAttributes();
        ta.setTransparencyMode(TransparencyAttributes.BLENDED);
        ta.setCapability(TransparencyAttributes.ALLOW_VALUE_READ);
        ta.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
        ap.setTransparencyAttributes(ta);

        setTexture(ap,textureType);
        tr.setAppearance(ap);

        QuadArray volMesh = new QuadArray(12, GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_3);
        volMesh.setCapability(GeometryArray.ALLOW_TEXCOORD_READ);
        volMesh.setCapability(GeometryArray.ALLOW_TEXCOORD_WRITE);
        volMesh.setCapability(GeometryArray.ALLOW_COORDINATE_READ);
        volMesh.setCapability(GeometryArray.ALLOW_COORDINATE_WRITE);

        float[] textureCoords = new float[]{
            0.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.0f, 0.5f,
            1.0f, 0.0f, 0.5f,
            1.0f, 0.0f, 0.5f,
            0.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 0.0f,
            1.0f, 0.0f, 0.0f,
            1.0f, 0.0f, 0.0f,};

        for (int i = 0; i < textureCoords.length; i++) {
            if (textureCoords[i] == 1.0f) {
                textureCoords[i] -= 0.25f;
            }
            if (textureCoords[i] == 0.0f) {
                textureCoords[i] += 0.25f;
            }
        }

        volMesh.setTextureCoordinates(0, 0, textureCoords);

        float[] coords = new float[]{
            -0.5f, -0.5f, -0.5f,
            0.5f, -0.5f, -0.5f,
            0.5f, 0.5f, -0.5f,
            -0.5f, 0.5f, -0.5f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            0.5f, 0.5f, 0.0f,
            -0.5f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.5f,
            0.5f, -0.5f, 0.5f,
            0.5f, 0.5f, 0.5f,
            -0.5f, 0.5f, 0.5f,};
        volMesh.setCoordinates(0, coords);

        tr.setGeometry(volMesh);

        scene.addChild(tr);
        universe.addBranchGraph(scene);
        universe.getViewingPlatform().setNominalViewingTransform();

        canvasParent.add(canvas, BorderLayout.CENTER);
        frame.add(canvasParent, BorderLayout.CENTER);
        frame.setVisible(true);
       
       
        //------------------add off screen canvas-----------------------
        final Canvas3D offScreenCanvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration(), true);
        int w = canvas.getWidth();
        int h = canvas.getHeight();
        final BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        ImageComponent2D imC = new ImageComponent2D(ImageComponent2D.FORMAT_RGB, im, true, false);
        imC.setCapability(ImageComponent2D.ALLOW_IMAGE_READ);
        offScreenCanvas.setOffScreenLocation(0, 0);
        offScreenCanvas.setOffScreenBuffer(imC);
        offScreenCanvas.getScreen3D().setSize(w, h);
        offScreenCanvas.getScreen3D().setPhysicalScreenWidth(0.0254 / 90.0 * canvas.getWidth());
        offScreenCanvas.getScreen3D().setPhysicalScreenHeight(0.0254 / 90.0 * canvas.getHeight());
        universe.getViewer().getView().addCanvas3D(offScreenCanvas);

       
        canvas.addMouseListener(new MouseListener() {

            @Override
            public void mouseClicked(MouseEvent e) {
                switch(e.getButton()) {
                    case MouseEvent.BUTTON1:
                        //left click to change texture
                        if(textureType == 0) {
                            textureType = 1;                            
                        } else {
                            textureType = 0;                            
                        }
                        setTexture(ap, textureType);
                        break;
                    case MouseEvent.BUTTON3:
                        //right click to write image
                        offScreenCanvas.renderOffScreenBuffer();
                        offScreenCanvas.waitForOffScreenRendering();
                        BufferedImage img = new BufferedImage(im.getWidth(), im.getHeight(), BufferedImage.TYPE_INT_RGB);
                        img.createGraphics().drawImage(im, null, null);
                        String filePath = String.format(pathPrefix+File.separator+"image%03d.jpg",counter++);
                        writeJpeg(img, filePath);
                        break;
                }
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
            }

            @Override
            public void mouseEntered(MouseEvent e) {
            }

            @Override
            public void mouseExited(MouseEvent e) {
            }

        });
    }

    private static void writeJpeg(BufferedImage img, String filePath) {
        try {
            Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
            ImageWriter writer = (ImageWriter) iter.next();
            ImageWriteParam iwp = writer.getDefaultWriteParam();
            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            iwp.setCompressionQuality(1.0f);
            File f = new File(filePath);
            FileImageOutputStream output = new FileImageOutputStream(f);
            writer.setOutput(output);
            IIOImage image = new IIOImage(img, null, null);
            writer.write(null, image, iwp);
            writer.dispose();
        } catch(IOException ex) {
           
        }
       
    }

    private static void setTexture(Appearance ap, int type) {
        BufferedImage[] texImages = new BufferedImage[2];
        if(type == 0) {
            //gray texture
            texImages[0] = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
            WritableRaster raster0 = texImages[0].getRaster();
            raster0.setPixel(0, 0, new int[]{255, 255, 255, 0});
            raster0.setPixel(0, 1, new int[]{255, 255, 255, 0});
            raster0.setPixel(1, 0, new int[]{255, 255, 255, 255});
            raster0.setPixel(1, 1, new int[]{255, 255, 255, 255});

            texImages[1] = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
            WritableRaster raster1 = texImages[1].getRaster();
            raster1.setPixel(0, 0, new int[]{255, 255, 255, 0});
            raster1.setPixel(0, 1, new int[]{255, 255, 255, 0});
            raster1.setPixel(1, 0, new int[]{255, 255, 255, 255});
            raster1.setPixel(1, 1, new int[]{255, 255, 255, 255});
        } else {
            //colored texture
            texImages[0] = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
            WritableRaster raster0 = texImages[0].getRaster();
            raster0.setPixel(0, 0, new int[]{255, 0, 0, 0});
            raster0.setPixel(0, 1, new int[]{255, 0, 0, 0});
            raster0.setPixel(1, 0, new int[]{255, 0, 0, 255});
            raster0.setPixel(1, 1, new int[]{255, 0, 0, 255});

            texImages[1] = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
            WritableRaster raster1 = texImages[1].getRaster();
            raster1.setPixel(0, 0, new int[]{0, 255, 0, 0});
            raster1.setPixel(0, 1, new int[]{0, 255, 0, 0});
            raster1.setPixel(1, 0, new int[]{0, 255, 0, 255});
            raster1.setPixel(1, 1, new int[]{0, 255, 0, 255});
        }
        ImageComponent3D i3d = new ImageComponent3D(ImageComponent3D.FORMAT_RGBA, texImages, true, true);
        Texture3D tx3d = new Texture3D(Texture3D.BASE_LEVEL, Texture3D.RGBA, 2, 2, 2);
        tx3d.setMagFilter(Texture3D.BASE_LEVEL_LINEAR);
        tx3d.setMinFilter(Texture3D.BASE_LEVEL_LINEAR);
        tx3d.setEnable(true);
        tx3d.setImage(0, i3d);
        ap.setTexture(tx3d);
    }

}
</pre>
Reply | Threaded
Open this post in threaded view
|

Re: offscreen cnavas and Texture3D

gouessej
Administrator
Do you reproduce this bug when you create a new Appearance each time you use another texture?
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: offscreen cnavas and Texture3D

babor
gouessej wrote
Do you reproduce this bug when you create a new Appearance each time you use another texture?
Yes. The behaviour is exactly the same.

Reply | Threaded
Open this post in threaded view
|

Re: offscreen cnavas and Texture3D

hharrison
I'm getting the sneaking suspicion this is related to another bug:

http://forum.jogamp.org/Texture2D-and-offscreen-rendering-problem-td4030882.html

I thought this was all related to the offscreen image capture, but I wonder if there is confusion in the joglpipeline
with texture binding, thanks for another breadcrumb!

Harvey
Reply | Threaded
Open this post in threaded view
|

Re: offscreen cnavas and Texture3D

babor
hharrison wrote
I'm getting the sneaking suspicion this is related to another bug:
http://forum.jogamp.org/Texture2D-and-offscreen-rendering-problem-td4030882.html
This one behaves even more strange.
However, the one reported by me with texture 3D has one workaround - captured images look fine when you create new offscreen after setting new Texture3D. This does not fix the bug with 2D texture.