Hello guys,
I am newbie in Java3D and I am facing some problems while designing a 3d shape. Actually I have to design a 3d shape of a 'Carpet' placed on the ground in such a way, it looks like a Road or Pathway from "First-Person-View". That is, like a Trapezoid. The Carpet itself is made up of several segments or pieces, each of different color, width and height. Finally, exporting the final Carpet-shape as Buffered Image. Though I am able to do the later part very easily, that is, creating an image using BufferdImage from JFrame, On & Off Screen Canvas but the designing part is where I am facing some difficulties.
I am creating a Group (carpetShapeGroup) of Polygons (with QuadArray: 4) for each segment/piece and place them one over another. so it looks like Rectangular Boxes placed one over another. Then with the Transformation3D (rot), I rotate this carpetShapeGroup with rot.rotX(-Math.PI/4) to make it like a Trapezoid shape. However, I am able to draw the image but there are several problems which I am not able to solve anymore and I need some help. I had uploaded the code on Pastie and attached it here as well with inline comments to illustrate the details.
Problem-1. I can create a Carpet with 3 segments/pieces successfully but as soon as I add fourth segment, the image gets distorted and the segments overlaps each other. I don't know why is it happening and how to resolve this issue. (Uncomment line:227)
Please let me know if something is unclear here, I will try to explain my problem again. Looking forward to see your replies.
thanks and regards
Code:
package me.mmw.test.java3d;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Screen3D;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JFrame;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class CarpetImageRenderer {
private double screenWidth;
private double screenHeight;
private SimpleUniverse universe;
public double getScreenWidth() {
return screenWidth;
}
public void setScreenWidth(double screenWidth) {
this.screenWidth = screenWidth;
}
public double getScreenHeight() {
return screenHeight;
}
public void setScreenHeight(int screenHeight) {
this.screenHeight = screenHeight;
}
public BufferedImage render(int width, int height, PolygonDetails[] polygonDetails) {
setScreenWidth(width);
setScreenHeight(height);
BranchGroup scene = createSceneGraph(polygonDetails);
Canvas3D onScreenCanvas = new Canvas3D(
SimpleUniverse.getPreferredConfiguration());
universe = new SimpleUniverse(onScreenCanvas);
ViewingPlatform viewingPlatform = universe.getViewingPlatform();
viewingPlatform.setNominalViewingTransform();
scene.compile();
universe.addBranchGraph(scene);
OffScreenCanvas3D offScreenCanvas = createOffScreenCanvas();
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.setLocation(GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setTitle("3D Carpet Renderer");
frame.add(onScreenCanvas);
BufferedImage output = offScreenCanvas.doRender((int)getScreenWidth(), (int)getScreenHeight());
frame.setSize(output.getWidth(), output.getHeight());
setScreenWidth(output.getWidth());
setScreenHeight(output.getHeight());
return output;
}
private OffScreenCanvas3D createOffScreenCanvas() {
/**
* Setup graphics
*/
GraphicsConfiguration configuration = SimpleUniverse
.getPreferredConfiguration();
/**
* Create the capturing canvas
*/
OffScreenCanvas3D canvas = new OffScreenCanvas3D(configuration);
/**
* Set the size, width and height so that off-screen renderer knows at
* what dimensions it needs to render.
*/
Screen3D screen3D = canvas.getScreen3D();
screen3D.setSize((int)screenWidth, (int)screenHeight);
/**
* Physical dimension of the Screen3D is supposed to be size of the physical screen in meters.
* The values are from the top of the Screen3D physical screen in meters.
* The values are from the top of the Screen3D.
* Setting the wrong physical dimension may also change the aspect ratio of the rendered image
* and you are end up with a Blackboard
*/
screen3D.setPhysicalScreenWidth(0.0254 / 90.0 * screenWidth);
screen3D.setPhysicalScreenHeight(0.0254 / 90.0 * screenHeight);
universe.getViewer().getView().addCanvas3D(canvas);
return canvas;
}
public BranchGroup createSceneGraph(PolygonDetails[] polygonDetails) {
BranchGroup objRoot = new BranchGroup();
Group carpetShapeGroup = new Group();
/**
* Setting Transformation/Postion Vector to display the Carpet from the Bottom of the screen
* Problem-2: Not able to derive a constant factor for positioning the Carpet with
* different Width and Height of Screen
*/
final float TRANSFORMATION_VECTOR_Y = -0.0014f;
Vector3f vector = new Vector3f(0.0f, (float)(TRANSFORMATION_VECTOR_Y * getScreenHeight()), 0.0f);
double previousElementHeight = 0.0d;
double baseCoordinateY = 0.0d;
double elementHeight = 0.0d;
double elementWidth = 1.0d;
for (PolygonDetails element : polygonDetails) {
elementHeight = (double) getScreenHeight()/100 * element.getPercentage() /100 /2;
previousElementHeight += elementHeight;
//System.out.println("baseCoordinateY:"+baseCoordinateY+" previousElementHeight"+previousElementHeight);
QuadArray polygon = new QuadArray(4, QuadArray.COORDINATES);
// X:WIDTH, Y:HEIGHT, Z:LENGTH
polygon.setCoordinate(0, new Point3d(-elementWidth, baseCoordinateY, 0d));
polygon.setCoordinate(1, new Point3d(elementWidth, baseCoordinateY, 0d));
polygon.setCoordinate(2, new Point3d(elementWidth, previousElementHeight, 0d));
polygon.setCoordinate(3, new Point3d(-elementWidth, previousElementHeight, 0d));
Shape3D polygonShape = new Shape3D(polygon, createAppearance(element.getColor()));
polygonShape.setGeometry(polygon);
//System.out.println(polygonShape.getBounds());
polygonShape.setCollidable(false);
/**
* Adding each polygon-shape in a group and finally add this group in the BranchGroup
* Problem-3: Is there any way I can create set of polygons as One Shape?
*/
carpetShapeGroup.addChild(polygonShape);
baseCoordinateY = elementHeight;
}
TransformGroup tg = new TransformGroup();
Transform3D transform = new Transform3D();
Transform3D rot = new Transform3D();
/**
* transforming the final shape as Trapezoid
*/
rot.rotX(-Math.PI / 7);
transform.mul(rot);
//transform.setScale(0.7);
transform.setTranslation(vector);
tg.setTransform(transform);
tg.addChild(carpetShapeGroup);
objRoot.addChild(tg);
createLights(objRoot);
return objRoot;
}
private Appearance createAppearance(Color color){
Appearance polygon2Appearance = new Appearance();
Color3f c = new Color3f ();
c.set(color);
ColoringAttributes yellowCA = new ColoringAttributes (c, 1);
polygon2Appearance.setColoringAttributes(yellowCA);
return polygon2Appearance;
}
private void createLights(BranchGroup branchGroup) {
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
100.0);
Color3f ambientLightColour = new Color3f(0.9f, 0.9f, 0.9f);
AmbientLight ambientLight = new AmbientLight(ambientLightColour);
ambientLight.setInfluencingBounds(bounds);
Color3f directionLightColour = new Color3f(1.0f, 1.0f, 1.0f);
Vector3f directionLightDir = new Vector3f(4.0f, -7.0f, -12.0f);// (-1.0f,
// -100.0f,
// -1.0f);
DirectionalLight directionLight = new DirectionalLight(
directionLightColour, directionLightDir);
directionLight.setInfluencingBounds(bounds);
branchGroup.addChild(ambientLight);
branchGroup.addChild(directionLight);
}
public static void main(String... args) {
/**
* Segments/Pieces of Carpet
* 1st arg: size of segment as %tage value
* 2nd arg: Color of segment
* Problem-1: when I add Fourth segment, the Image gets weird
*/
PolygonDetails[] polygonDetails = new PolygonDetails[] {
new PolygonDetails(10, Color.YELLOW),
new PolygonDetails(60, Color.GREEN),
new PolygonDetails(5, Color.RED),
new PolygonDetails(5, Color.YELLOW),
//new PolygonDetails(10, Color.GREEN),
};
/**
* Create a Scene, render the Carpet and finally return a buffered image.
* Takes screen width, height and set of carpet-segments as args
*/
BufferedImage bi = new CarpetImageRenderer().render(400, 400, polygonDetails);
try {
ImageIO.write(bi, "PNG", new File("carpet3d.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("serial")
class OffScreenCanvas3D extends Canvas3D {
public OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration) {
super(graphicsConfiguration, true);
}
public BufferedImage doRender(int width, int height) {
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
ImageComponent2D buffer = new ImageComponent2D(
ImageComponent.FORMAT_RGB, bImage);
buffer.setCapability(ImageComponent.ALLOW_FORMAT_READ);
setOffScreenBuffer(buffer);
renderOffScreenBuffer();
waitForOffScreenRendering();
bImage = getOffScreenBuffer().getImage();
// Release the buffer
setOffScreenBuffer(null);
return bImage;
}
}
class PolygonDetails {
private Color color;
private double percentage;
public PolygonDetails(double percentage, Color color) {
super();
this.color = color;
this.percentage = percentage;
}
public PolygonDetails() {
}
public double getPercentage() {
return percentage;
}
public Color getColor() {
return color;
}
}