Hacky Sculpt Previewer

From Second Life Wiki
Jump to navigation Jump to search

/**
 *  First attempt at a sculpt previewer
 *  Adapted from Sun's ObjLoad
 *  BSD license (inherited)
 * */

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.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.universe.*;

public class SculptPreview extends javax.swing.JFrame {

    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;
        result[1] = ((double)green) / 255.0;
        result[2] = ((double)blue) / 255.0;
        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);
        int chooserResult = chooser.showOpenDialog(this);
        if (chooserResult == JFileChooser.CANCEL_OPTION) {
            System.out.println("Cancelled");
            System.exit(0);
        }
      
        try {
            theImage = ImageIO.read(chooser.getSelectedFile());
        } catch (IOException e) {
            System.out.println("Error opening sculpt texture file");
            e.printStackTrace();
            System.exit(0);
        }
        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) xStep = 1;
        if ((xStep > 1) || (yStep > 1)) {
            System.out.println("Sculpt texture being downsampled, please limit to 64x64");
        }

        int vertices = (width / xStep) * (height / yStep);
        System.out.println(vertices);

        TriangleArray ta = new TriangleArray(vertices * 6,GeometryArray.COORDINATES);

        int x, y, r;
        r=0;
        for (x=0; x<width; x+=xStep) {
            for (y=0; y<height; 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;
            }
        }

        s.setGeometry(ta);
        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;
    }

    private void usage() {
        System.out.println(
                "Usage: java SculptPreview [-s] [-n] [-t] [-c degrees] <.obj file>");
        System.out.println("  -s Spin (no user interaction)");
        System.out.println("  -n No triangulation");
        System.out.println("  -t No stripification");
        System.out.println(
                "  -c Set crease angle for normal generation (default is 60 without");
        System.out.println(
                "     smoothing group info, otherwise 180 within smoothing groups)");
        System.exit(0);
    } // End of usage

    /**
     * Creates new form 
     */
    public SculptPreview(String args[]) {
        if (args.length != 0) {
            for (int i = 0 ; i < args.length ; i++) {
                if (args[i].startsWith("-")) {
                    if (args[i].equals("-s")) {
                        spin = true;
                    } else if (args[i].equals("-n")) {
                        noTriangulate = true;
                    } else if (args[i].equals("-t")) {
                        noStripify = true;
                    } else if (args[i].equals("-c")) {
                        if (i < args.length - 1) {
                            creaseAngle = (new Double(args[++i])).doubleValue();
                        } else usage();
                    } else {
                        usage();
                    }
                } else {
                    try {
                        if ((args[i].indexOf("file:") == 0) ||
                                (args[i].indexOf("http") == 0)) {
                            filename = new URL(args[i]);
                        } else if (args[i].charAt(0) != '/') {
                            filename = new URL("file:./" + args[i]);
                        } else {
                            filename = new URL("file:" + args[i]);
                        }
                    } catch (MalformedURLException e) {
                        System.err.println(e);
                        System.exit(1);
                    }
                }
            }       
        }
            
       
	// 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() {
        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
    
}