Hacky Sculpt Previewer

From Second Life Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
/**
 *  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
    
}