offscreen cnavas and Texture3D
Posted by babor on Mar 11, 2014; 3:27pm
URL: https://forum.jogamp.org/offscreen-cnavas-and-Texture3D-tp4031838.html
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>