PolygonOffset
/*
* %Z%%M% %I% %E% %U%
*
* ************************************************************** "Copyright (c)
* 2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility."
*
* ***************************************************************************
*/
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.text.NumberFormat;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Hashtable;
import java.util.Vector;
import javax.media.j3d.Alpha;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Screen3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class PolygonOffset extends Applet implements Java3DExplorerConstants {
SimpleUniverse u;
boolean isApplication;
Canvas3D canvas;
OffScreenCanvas3D offScreenCanvas;
View view;
PolygonAttributes solidPa;
PolygonAttributes wirePa;
float dynamicOffset = 1.0f;
float staticOffset = 1.0f;
ViewingPlatform viewingPlatform;
float innerScale = 0.94f;
TransformGroup innerTG;
Transform3D scale;
float sphereRadius = 0.9f;
String outFileBase = "offset";
int outFileSeq = 0;
float offScreenScale = 1.0f;
public BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();
// Create the transform group node and initialize it to the
// identity. Enable the TRANSFORM_WRITE capability so that
// our behavior code can modify it at runtime. Add it to the
// root of the subgraph.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);
// Create a Sphere. We will display this as both wireframe and
// solid to make a hidden line display
// wireframe
Appearance wireApp = new Appearance();
ColoringAttributes wireCa = new ColoringAttributes();
wireCa.setColor(black);
wireApp.setColoringAttributes(wireCa);
wirePa = new PolygonAttributes(PolygonAttributes.POLYGON_LINE,
PolygonAttributes.CULL_BACK, 0.0f);
wireApp.setPolygonAttributes(wirePa);
Sphere outWireSphere = new Sphere(sphereRadius, 0, 15, wireApp);
objTrans.addChild(outWireSphere);
// solid
ColoringAttributes outCa = new ColoringAttributes(red,
ColoringAttributes.SHADE_FLAT);
Appearance outSolid = new Appearance();
outSolid.setColoringAttributes(outCa);
solidPa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
PolygonAttributes.CULL_BACK, 0.0f);
solidPa.setPolygonOffsetFactor(dynamicOffset);
solidPa.setPolygonOffset(staticOffset);
solidPa.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);
outSolid.setPolygonAttributes(solidPa);
Sphere outSolidSphere = new Sphere(sphereRadius, 0, 15, outSolid);
objTrans.addChild(outSolidSphere);
innerTG = new TransformGroup();
innerTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
scale = new Transform3D();
updateInnerScale();
objTrans.addChild(innerTG);
// Create a smaller sphere to go inside. This sphere has a different
// tesselation and color
Sphere inWireSphere = new Sphere(sphereRadius, 0, 10, wireApp);
innerTG.addChild(inWireSphere);
// inside solid
ColoringAttributes inCa = new ColoringAttributes(blue,
ColoringAttributes.SHADE_FLAT);
Appearance inSolid = new Appearance();
inSolid.setColoringAttributes(inCa);
inSolid.setPolygonAttributes(solidPa);
Sphere inSolidSphere = new Sphere(sphereRadius, 0, 10, inSolid);
innerTG.addChild(inSolidSphere);
// Create a new Behavior object that will perform the desired
// operation on the specified transform object and add it into
// the scene graph.
AxisAngle4f axisAngle = new AxisAngle4f(0.0f, 0.0f, 1.0f,
-(float) Math.PI / 2.0f);
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
80000, 0, 0, 0, 0, 0);
RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,
objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);
BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
100.0);
rotator.setSchedulingBounds(bounds);
objTrans.addChild(rotator);
// set up a white background
Background bgWhite = new Background(new Color3f(1.0f, 1.0f, 1.0f));
bgWhite.setApplicationBounds(bounds);
objTrans.addChild(bgWhite);
// Have Java 3D perform optimizations on this scene graph.
objRoot.compile();
return objRoot;
}
void updateInnerScale() {
scale.set(innerScale);
innerTG.setTransform(scale);
}
public PolygonOffset() {
this(false);
}
public PolygonOffset(boolean isApplication) {
this.isApplication = isApplication;
}
public void init() {
setLayout(new BorderLayout());
GraphicsConfiguration config = SimpleUniverse
.getPreferredConfiguration();
JPanel canvasPanel = new JPanel();
GridBagLayout gridbag = new GridBagLayout();
canvasPanel.setLayout(gridbag);
canvas = new Canvas3D(config);
canvas.setSize(600, 600);
add(canvas, BorderLayout.CENTER);
u = new SimpleUniverse(canvas);
if (isApplication) {
offScreenCanvas = new OffScreenCanvas3D(config, true);
// set the size of the off-screen canvas based on a scale
// of the on-screen size
Screen3D sOn = canvas.getScreen3D();
Screen3D sOff = offScreenCanvas.getScreen3D();
Dimension dim = sOn.getSize();
dim.width *= offScreenScale;
dim.height *= offScreenScale;
sOff.setSize(dim);
sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
* offScreenScale);
sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
* offScreenScale);
// attach the offscreen canvas to the view
u.getViewer().getView().addCanvas3D(offScreenCanvas);
}
// Create a simple scene and attach it to the virtual universe
BranchGroup scene = createSceneGraph();
// set the eye at z = 2.0
viewingPlatform = u.getViewingPlatform();
Transform3D vpTrans = new Transform3D();
vpTrans.set(new Vector3f(0.0f, 0.0f, 2.0f));
viewingPlatform.getViewPlatformTransform().setTransform(vpTrans);
// set up a parallel projection with clip limits at 1 and -1
view = u.getViewer().getView();
view.setProjectionPolicy(View.PARALLEL_PROJECTION);
view.setFrontClipPolicy(View.VIRTUAL_EYE);
view.setBackClipPolicy(View.VIRTUAL_EYE);
view.setFrontClipDistance(1.0f);
view.setBackClipDistance(3.0f);
u.addBranchGraph(scene);
// set up the sliders
JPanel guiPanel = new JPanel();
guiPanel.setLayout(new GridLayout(0, 1));
FloatLabelJSlider dynamicSlider = new FloatLabelJSlider(
"Dynamic Offset", 0.1f, 0.0f, 2.0f, dynamicOffset);
dynamicSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
dynamicOffset = e.getValue();
solidPa.setPolygonOffsetFactor(dynamicOffset);
}
});
guiPanel.add(dynamicSlider);
LogFloatLabelJSlider staticSlider = new LogFloatLabelJSlider(
"Static Offset", 0.1f, 10000.0f, staticOffset);
staticSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
staticOffset = e.getValue();
solidPa.setPolygonOffset(staticOffset);
}
});
guiPanel.add(staticSlider);
FloatLabelJSlider innerSphereSlider = new FloatLabelJSlider(
"Inner Sphere Scale", 0.001f, 0.90f, 1.0f, innerScale);
innerSphereSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
innerScale = e.getValue();
updateInnerScale();
}
});
guiPanel.add(innerSphereSlider);
if (isApplication) {
JButton snapButton = new JButton("Snap Image");
snapButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Point loc = canvas.getLocationOnScreen();
offScreenCanvas.setOffScreenLocation(loc);
Dimension dim = canvas.getSize();
dim.width *= offScreenScale;
dim.height *= offScreenScale;
nf.setMinimumIntegerDigits(3);
offScreenCanvas.snapImageFile(outFileBase
+ nf.format(outFileSeq++), dim.width, dim.height);
nf.setMinimumIntegerDigits(0);
}
});
guiPanel.add(snapButton);
}
add(guiPanel, BorderLayout.EAST);
}
public void destroy() {
u.removeAllLocales();
}
//
// The following allows PolygonOffset to be run as an application
// as well as an applet
//
public static void main(String[] args) {
new MainFrame(new PolygonOffset(true), 950, 600);
}
}
interface Java3DExplorerConstants {
// colors
static Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
static Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
static Color3f green = new Color3f(0.0f, 1.0f, 0.0f);
static Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);
static Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);
static Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);
static Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);
static Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);
static Color3f brightWhite = new Color3f(1.0f, 1.5f, 1.5f);
static Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
static Color3f darkGrey = new Color3f(0.15f, 0.15f, 0.15f);
static Color3f medGrey = new Color3f(0.3f, 0.3f, 0.3f);
static Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);
static Color3f lightGrey = new Color3f(0.75f, 0.75f, 0.75f);
// infinite bounding region, used to make env nodes active everywhere
BoundingSphere infiniteBounds = new BoundingSphere(new Point3d(),
Double.MAX_VALUE);
// common values
static final String nicestString = "NICEST";
static final String fastestString = "FASTEST";
static final String antiAliasString = "Anti-Aliasing";
static final String noneString = "NONE";
// light type constants
static int LIGHT_AMBIENT = 1;
static int LIGHT_DIRECTIONAL = 2;
static int LIGHT_POSITIONAL = 3;
static int LIGHT_SPOT = 4;
// screen capture constants
static final int USE_COLOR = 1;
static final int USE_BLACK_AND_WHITE = 2;
// number formatter
NumberFormat nf = NumberFormat.getInstance();
}
class OffScreenCanvas3D extends Canvas3D {
OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,
boolean offScreen) {
super(graphicsConfiguration, offScreen);
}
private 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.setYUp(true);
setOffScreenBuffer(buffer);
renderOffScreenBuffer();
waitForOffScreenRendering();
bImage = getOffScreenBuffer().getImage();
return bImage;
}
void snapImageFile(String filename, int width, int height) {
BufferedImage bImage = doRender(width, height);
/*
* JAI: RenderedImage fImage = JAI.create("format", bImage,
* DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +
* ".tif", "tiff", null);
*/
/* No JAI: */
try {
FileOutputStream fos = new FileOutputStream(filename + ".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos);
JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);
JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);
param.setQuality(1.0f, true);
jie.setJPEGEncodeParam(param);
jie.encode(bImage);
bos.flush();
fos.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
class FloatLabelJSlider extends JPanel implements ChangeListener,
Java3DExplorerConstants {
JSlider slider;
JLabel valueLabel;
Vector listeners = new Vector();
float min, max, resolution, current, scale;
int minInt, maxInt, curInt;;
int intDigits, fractDigits;
float minResolution = 0.001f;
// default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
// 0.5
FloatLabelJSlider(String name) {
this(name, 0.1f, 0.0f, 1.0f, 0.5f);
}
FloatLabelJSlider(String name, float resolution, float min, float max,
float current) {
this.resolution = resolution;
this.min = min;
this.max = max;
this.current = current;
if (resolution < minResolution) {
resolution = minResolution;
}
// round scale to nearest integer fraction. i.e. 0.3 => 1/3 = 0.33
scale = (float) Math.round(1.0f / resolution);
resolution = 1.0f / scale;
// get the integer versions of max, min, current
minInt = Math.round(min * scale);
maxInt = Math.round(max * scale);
curInt = Math.round(current * scale);
// sliders use integers, so scale our floating point value by "scale"
// to make each slider "notch" be "resolution". We will scale the
// value down by "scale" when we get the event.
slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
slider.addChangeListener(this);
valueLabel = new JLabel(" ");
// set the initial value label
setLabelString();
// add min and max labels to the slider
Hashtable labelTable = new Hashtable();
labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
slider.setLabelTable(labelTable);
slider.setPaintLabels(true);
/* layout to align left */
setLayout(new BorderLayout());
Box box = new Box(BoxLayout.X_AXIS);
add(box, BorderLayout.WEST);
box.add(new JLabel(name));
box.add(slider);
box.add(valueLabel);
}
public void setMinorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMinorTickSpacing(intSpacing);
}
public void setMajorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMajorTickSpacing(intSpacing);
}
public void setPaintTicks(boolean paint) {
slider.setPaintTicks(paint);
}
public void addFloatListener(FloatListener listener) {
listeners.add(listener);
}
public void removeFloatListener(FloatListener listener) {
listeners.remove(listener);
}
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
// get the event type, set the corresponding value.
// Sliders use integers, handle floating point values by scaling the
// values by "scale" to allow settings at "resolution" intervals.
// Divide by "scale" to get back to the real value.
curInt = source.getValue();
current = curInt / scale;
valueChanged();
}
public void setValue(float newValue) {
boolean changed = (newValue != current);
current = newValue;
if (changed) {
valueChanged();
}
}
private void valueChanged() {
// update the label
setLabelString();
// notify the listeners
FloatEvent event = new FloatEvent(this, current);
for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
FloatListener listener = (FloatListener) e.nextElement();
listener.floatChanged(event);
}
}
void setLabelString() {
// Need to muck around to try to make sure that the width of the label
// is wide enough for the largest value. Pad the string
// be large enough to hold the largest value.
int pad = 5; // fudge to make up for variable width fonts
float maxVal = Math.max(Math.abs(min), Math.abs(max));
intDigits = Math.round((float) (Math.log(maxVal) / Math.log(10))) + pad;
if (min < 0) {
intDigits++; // add one for the '-'
}
// fractDigits is num digits of resolution for fraction. Use base 10 log
// of scale, rounded up, + 2.
fractDigits = (int) Math.ceil((Math.log(scale) / Math.log(10)));
nf.setMinimumFractionDigits(fractDigits);
nf.setMaximumFractionDigits(fractDigits);
String value = nf.format(current);
while (value.length() < (intDigits + fractDigits)) {
value = value + " ";
}
valueLabel.setText(value);
}
}
class FloatEvent extends EventObject {
float value;
FloatEvent(Object source, float newValue) {
super(source);
value = newValue;
}
float getValue() {
return value;
}
}
interface FloatListener extends EventListener {
void floatChanged(FloatEvent e);
}
class LogFloatLabelJSlider extends JPanel implements ChangeListener,
Java3DExplorerConstants {
JSlider slider;
JLabel valueLabel;
Vector listeners = new Vector();
float min, max, resolution, current, scale;
double minLog, maxLog, curLog;
int minInt, maxInt, curInt;;
int intDigits, fractDigits;
NumberFormat nf = NumberFormat.getInstance();
float minResolution = 0.001f;
double logBase = Math.log(10);
// default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
// 0.5
LogFloatLabelJSlider(String name) {
this(name, 0.1f, 100.0f, 10.0f);
}
LogFloatLabelJSlider(String name, float min, float max, float current) {
this.resolution = resolution;
this.min = min;
this.max = max;
this.current = current;
if (resolution < minResolution) {
resolution = minResolution;
}
minLog = log10(min);
maxLog = log10(max);
curLog = log10(current);
// resolution is 100 steps from min to max
scale = 100.0f;
resolution = 1.0f / scale;
// get the integer versions of max, min, current
minInt = (int) Math.round(minLog * scale);
maxInt = (int) Math.round(maxLog * scale);
curInt = (int) Math.round(curLog * scale);
slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
slider.addChangeListener(this);
valueLabel = new JLabel(" ");
// Need to muck around to make sure that the width of the label
// is wide enough for the largest value. Pad the initial string
// be large enough to hold the largest value.
int pad = 5; // fudge to make up for variable width fonts
intDigits = (int) Math.ceil(maxLog) + pad;
if (min < 0) {
intDigits++; // add one for the '-'
}
if (minLog < 0) {
fractDigits = (int) Math.ceil(-minLog);
} else {
fractDigits = 0;
}
nf.setMinimumFractionDigits(fractDigits);
nf.setMaximumFractionDigits(fractDigits);
String value = nf.format(current);
while (value.length() < (intDigits + fractDigits)) {
value = value + " ";
}
valueLabel.setText(value);
// add min and max labels to the slider
Hashtable labelTable = new Hashtable();
labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
slider.setLabelTable(labelTable);
slider.setPaintLabels(true);
// layout to align left
setLayout(new BorderLayout());
Box box = new Box(BoxLayout.X_AXIS);
add(box, BorderLayout.WEST);
box.add(new JLabel(name));
box.add(slider);
box.add(valueLabel);
}
public void setMinorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMinorTickSpacing(intSpacing);
}
public void setMajorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMajorTickSpacing(intSpacing);
}
public void setPaintTicks(boolean paint) {
slider.setPaintTicks(paint);
}
public void addFloatListener(FloatListener listener) {
listeners.add(listener);
}
public void removeFloatListener(FloatListener listener) {
listeners.remove(listener);
}
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
curInt = source.getValue();
curLog = curInt / scale;
current = (float) exp10(curLog);
valueChanged();
}
public void setValue(float newValue) {
boolean changed = (newValue != current);
current = newValue;
if (changed) {
valueChanged();
}
}
private void valueChanged() {
String value = nf.format(current);
valueLabel.setText(value);
// notify the listeners
FloatEvent event = new FloatEvent(this, current);
for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
FloatListener listener = (FloatListener) e.nextElement();
listener.floatChanged(event);
}
}
double log10(double value) {
return Math.log(value) / logBase;
}
double exp10(double value) {
return Math.exp(value * logBase);
}
}
Related examples in the same category