Hacky Sculpt Previewer

/**
 * First attempt at a sculpt previewer
 * Adapted from Sun's ObjLoad
 * BSD license (inherited)
 * */
/**
 * Original Yumi Murakami.
 * 6-5-2007 Strife Onizuka:
 * Fixed centering of object and wrapping around when
 * downsampling.
 * 6-5-2007 Yumi Murakami 
 * Use system native UI, attempted to get shading to work.
 * Now generates surface normals and installs Materials, both
 * of which are required for shading - but shading still does
 * not work :(
 * */
import java.awt.GraphicsConfiguration;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.media.j3d.*;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.vecmath.*;
import com.sun.j3d.loaders.IncorrectFormatException;
import com.sun.j3d.loaders.ParsingErrorException;
import com.sun.j3d.loaders.Scene;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.utils.behaviors.vp.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
public class SculptPreview extends javax.swing.JFrame {
	private boolean left_right = true;
	private boolean top_bottom = false;
 private boolean spin = false;
 private boolean noTriangulate = false;
 private boolean noStripify = false;
 private double creaseAngle = 60.0;
 private URL filename = null;
 private SimpleUniverse univ = null;
 private BranchGroup scene = null;
 double[] RGBtoVertex(int RGB) {
 int blue = RGB & 255;
 int green = (RGB >> 8) & 255;
 int red = (RGB >> 16) & 255;
 double[] result = new double[3];
 result[0] = (((double)red) / 255.0) - 0.5;
 result[1] = (((double)green) / 255.0) - 0.5;
 result[2] = (((double)blue) / 255.0) - 0.5;
 return result;
 }
	
 public BranchGroup createSceneGraph() {
		// Create the root of the branch graph
		BranchGroup objRoot = new BranchGroup();
			// Create a Transformgroup to scale all objects so they
			// appear in the scene.
			TransformGroup objScale = new TransformGroup();
			Transform3D t3d = new Transform3D();
			t3d.setScale(0.7);
			objScale.setTransform(t3d);
			objRoot.addChild(objScale);
		// 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);
		objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		objScale.addChild(objTrans);
		Shape3D s = new Shape3D();
		BufferedImage theImage = null;
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter("Supported Image Formats",ImageIO.getReaderFileSuffixes());
 chooser.setFileFilter(filter);
 boolean ok = false;
 while (!ok) {
 int chooserResult = chooser.showOpenDialog(this);
 if (chooserResult == JFileChooser.CANCEL_OPTION) {
 System.exit(0);
 }
 ok = true;
		 try {
			theImage = ImageIO.read(chooser.getSelectedFile());
 } catch (IOException e) {
 JOptionPane.showMessageDialog(this,"An error occured opening the texture file:\n" + e.getMessage(),"File error",JOptionPane.ERROR_MESSAGE);
			System.out.println("Error opening sculpt texture file");
 ok = false;
 }
 }
		int xStep, yStep;
		int width = theImage.getWidth();
		int height = theImage.getHeight();
		xStep = width / 64;
		yStep = height / 64;
		if (xStep < 1)
			xStep = 1;
		if (yStep < 1)
			yStep = 1;
		if ((xStep > 1) || (yStep > 1)) {
 JOptionPane.showMessageDialog(this,"Sculpt texture dimensions are " + width + "x" + height + ".\nMaximum detail level on Second Life to 64x64,\nso the extra pixel data is being wasted.","Texture Over Resolution",JOptionPane.WARNING_MESSAGE);
		}
		int xmax = width - (left_right?0:xStep);
		int ymax = height - (top_bottom?0:yStep);
		
		int vertices = (xmax/xStep) * (height/yStep);
		System.out.println(vertices);
 TriangleArray ta = new TriangleArray(vertices * 6,GeometryArray.COORDINATES);
		int x, y, r;
		r = 0;
		
		double[] bottom = new double[3];
		double[] top = new double[3];
		
		if(!top_bottom)//find the top center and bottom averages
		{
			r = 0;
			bottom[0] = bottom[1] = bottom[2] = top[0] = top[1] = top[2] = 0.0;
			for (x=0; x<width; x+=xStep) {
				double[] pb = RGBtoVertex(theImage.getRGB(x,0));
				double[] pt = RGBtoVertex(theImage.getRGB(x,height - xStep));
				bottom[0] += pb[0];
				bottom[1] += pb[1];
				bottom[2] += pb[2];
				top[0] += pt[0];
				top[1] += pt[1];
				top[2] += pt[2];
			}
			bottom[0] = (bottom[0] * xStep) / width;
			bottom[1] = (bottom[1] * xStep) / width;
			bottom[2] = (bottom[2] * xStep) / width;
			top[0] = (top[0] * xStep) / width;
			top[1] = (top[1] * xStep) / width;
			top[2] = (top[2] * xStep) / width;
		}
		
		for (x=0; x<xmax; x+=xStep) {//link left to right
			if(!top_bottom)
			{
				int BottomLeftRGB = theImage.getRGB(x,0);
				int BottomRightRGB = theImage.getRGB((x+xStep) % width,0);
				ta.setCoordinates(r,bottom);
 ta.setCoordinates(r+1,RGBtoVertex(BottomRightRGB));
				ta.setCoordinates(r+2,RGBtoVertex(BottomLeftRGB));
				
				r+=3;
			}
			for (y=0; y<ymax; y+=yStep) {
				int topLeftRGB = theImage.getRGB(x,y);
				int topRightRGB = theImage.getRGB((x+xStep) % width,y);
				int bottomLeftRGB = theImage.getRGB(x,(y+yStep) % height);
				int bottomRightRGB = theImage.getRGB((x+xStep) % width,(y+yStep) % height);
				// Top left triangle
				ta.setCoordinates(r,RGBtoVertex(topLeftRGB));
				ta.setCoordinates(r+1,RGBtoVertex(topRightRGB));
				ta.setCoordinates(r+2,RGBtoVertex(bottomLeftRGB));
				// Bottom right triangle
				ta.setCoordinates(r+3,RGBtoVertex(topRightRGB));
				ta.setCoordinates(r+4,RGBtoVertex(bottomRightRGB));
 ta.setCoordinates(r+5,RGBtoVertex(bottomLeftRGB));
				r+=6;
			}
			if(!top_bottom)
			{
				int TopLeftRGB = theImage.getRGB(x,ymax);
				int TopRightRGB = theImage.getRGB((x+xStep) % width,ymax);
				ta.setCoordinates(r,RGBtoVertex(TopLeftRGB));
				ta.setCoordinates(r+1,RGBtoVertex(TopRightRGB));
				ta.setCoordinates(r+2,top);
				
				r+=3;
			}
		}
 GeometryInfo info = new GeometryInfo(ta);
 NormalGenerator gen = new NormalGenerator();
 gen.generateNormals(info);
 s.setGeometry(info.getIndexedGeometryArray());
 Appearance app = new Appearance();
 Material mat = new Material( new Color3f((float)0.0,(float)0.0,(float)0.0), // Ambient 
 new Color3f((float)1.0,(float)1.0,(float)1.0), // Emissive
 new Color3f((float)0.5,(float)0.5,(float)0.5), // Diffuse
 new Color3f((float)1.0,(float)1.0,(float)1.0), // Specular
 (float)10.0 // Shiny
 ); // );
 app.setMaterial(mat);
 s.setAppearance(app);
		objTrans.addChild(s);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
		if (spin) {
		 Transform3D yAxis = new Transform3D();
		 Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
						 0, 0,
						 4000, 0, 0,
						 0, 0, 0);
		 RotationInterpolator rotator =
			 new RotationInterpolator(rotationAlpha, objTrans, yAxis,
						 0.0f, (float) Math.PI*2.0f);
		 rotator.setSchedulingBounds(bounds);
		 objTrans.addChild(rotator);
		} 
 // Set up the background
 Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f);
 Background bgNode = new Background(bgColor);
 bgNode.setApplicationBounds(bounds);
 objRoot.addChild(bgNode);
	return objRoot;
 }
 
 private Canvas3D createUniverse() {
	// Get the preferred graphics configuration for the default screen
		GraphicsConfiguration config =
			SimpleUniverse.getPreferredConfiguration();
		// Create a Canvas3D using the preferred configuration
		Canvas3D canvas3d = new Canvas3D(config);
		// Create simple universe with view branch
		univ = new SimpleUniverse(canvas3d);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
		// add mouse behaviors to the ViewingPlatform
		ViewingPlatform viewingPlatform = univ.getViewingPlatform();
		PlatformGeometry pg = new PlatformGeometry();
		// Set up the ambient light
		Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
		AmbientLight ambientLightNode = new AmbientLight(ambientColor);
		ambientLightNode.setInfluencingBounds(bounds);
		pg.addChild(ambientLightNode);
		// Set up the directional lights
		Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
		Vector3f light1Direction = new Vector3f(1.0f, 1.0f, 1.0f);
		Color3f light2Color = new Color3f(1.0f, 1.0f, 1.0f);
		Vector3f light2Direction = new Vector3f(-1.0f, -1.0f, -1.0f);
		DirectionalLight light1
			= new DirectionalLight(light1Color, light1Direction);
		light1.setInfluencingBounds(bounds);
		pg.addChild(light1);
		DirectionalLight light2
			= new DirectionalLight(light2Color, light2Direction);
		light2.setInfluencingBounds(bounds);
		pg.addChild(light2);
		viewingPlatform.setPlatformGeometry( pg );
		 
		// This will move the ViewPlatform back a bit so the
		// objects in the scene can be viewed.
		viewingPlatform.setNominalViewingTransform();
		if (!spin) {
				OrbitBehavior orbit = new OrbitBehavior(canvas3d,
								OrbitBehavior.REVERSE_ALL);
				orbit.setSchedulingBounds(bounds);
				viewingPlatform.setViewPlatformBehavior(orbit);	 
		} 
			
			// Ensure at least 5 msec per frame (i.e., < 200Hz)
		univ.getViewer().getView().setMinimumFrameCycleTime(5);
		return canvas3d;
 }
 /**
 * Creates new form 
 */
 public SculptPreview(String args[]) {
 // Initialize the GUI components
	initComponents();
	// Create Canvas3D and SimpleUniverse; add canvas to drawing panel
	Canvas3D c = createUniverse();
	drawingPanel.add(c, java.awt.BorderLayout.CENTER);
	// Create the content branch and add it to the universe
	scene = createSceneGraph();
	univ.addBranchGraph(scene);
 }
 // ----------------------------------------------------------------
 
 /** This method is called from within the constructor to
 * initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is
 * always regenerated by the Form Editor.
 */
 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
 private void initComponents() {
 try {
 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
 } catch (Exception e) {
 System.out.println("Error setting UI!");
 }
 drawingPanel = new javax.swing.JPanel();
 setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
 setTitle("SculptPreview");
 drawingPanel.setLayout(new java.awt.BorderLayout());
 drawingPanel.setPreferredSize(new java.awt.Dimension(700, 700));
 getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER);
 pack();
 }// </editor-fold>//GEN-END:initComponents
 
 /**
 * @param args the command line arguments
 */
 public static void main(final String args[]) {
 java.awt.EventQueue.invokeLater(new Runnable() {
 public void run() {
 SculptPreview sp = new SculptPreview(args);
 sp.setVisible(true);
 }
 });
 }
 
 // Variables declaration - do not modify//GEN-BEGIN:variables
 private javax.swing.JPanel drawingPanel;
 // End of variables declaration//GEN-END:variables
 
}