/*
package print_canvas3d;
import com.sun.j3d.utils.universe.*;
import java.net.MalformedURLException;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.GraphicsConfiguration;
import javax.swing.JPopupMenu;
import com.sun.j3d.loaders.objectfile.ObjectFile;
import com.sun.j3d.loaders.ParsingErrorException;
import com.sun.j3d.loaders.IncorrectFormatException;
import com.sun.j3d.loaders.Scene;
import java.awt.image.BufferedImage;
import java.io.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import java.net.URL;
import com.sun.j3d.utils.scenegraph.io.SceneGraphFileReader;
import com.sun.j3d.utils.geometry.GeometryInfo;
import javax.media.j3d.GeometryArray;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.TexCoordGeneration;
import javax.media.j3d.Texture;
import javax.media.j3d.Texture2D;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.TextureUnitState;
import javax.media.j3d.WakeupOnElapsedFrames;
import java.util.Enumeration;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import javax.imageio.*;
import com.sun.j3d.loaders.SceneBase;
import com.sun.j3d.utils.geometry.NormalGenerator;
import java.util.*;
public class PrintCanvas3D extends javax.swing.JFrame {
private static final boolean noTriangulate = false;
private static final boolean noStripify = false;
private static final double creaseAngle = 60.0;
private Canvas3D onScreenCanvas3D;
private OffScreenCanvas3D offScreenCanvas3D;
private URL filename = null;
private static final int OFF_SCREEN_SCALE = 3;
private SimpleUniverse univ = null;
TransformGroup[] viewGroups;
Hashtable hashtable;
TextureUnitState[] tusArr;
Appearance appearance;
/** polygonAttributes will be changed at runtime **/
PolygonAttributes polygonAttributes;
int tsize=0;
long tnormals=0;
long tcoord=0;
List<Point3f> vertices;
List<TexCoord2f> textureCoodinates;
List<Vector3f> normals;
Map<String, Group> groups;
Group currentGroup;
String currentMaterial;
Map<String, Appearance> appearances;
private Map<String, Appearance> DEFAULT_APPEARANCES;
//******************************************************
List<Point3f> texvertices;
List<TexCoord2f> textextureCoodinates;
List<Vector3f> texnormals;
private SceneGraphFileReader reader = null;
private java.net.URL texImage = null;
private boolean allowNonPowerOfTwo = true;
private boolean mipmap = true;
// TextureUnitStates used in this application
TextureUnitState tuLightMap;
TextureUnitState tuDOT3NormalMap;
TextureUnitState tuColor;
Texture textureColor;
Texture textureDOT3NormalMap;
Texture2D textureLightMap;
// needs for runtime updates on lightMap
ImageComponent2D imageLightMap;
Shape3D shape = null;
// default texture names used
String textureColorName= "wood.jpg";
String textureDOT3NormalMapName = "Java3Ddot3.jpg";
public BranchGroup createSceneGraph(String args[]) {
// 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);
int flags = ObjectFile.RESIZE;
if (!noTriangulate) flags |= ObjectFile.TRIANGULATE;
if (!noStripify) flags |= ObjectFile.STRIPIFY;
ObjectFile f =
new ObjectFile(flags,
(float)(creaseAngle * Math.PI / 180.0));
Scene scene = null;
try {
String JAVA_3D_MATERIALS=readMaterial();
DEFAULT_APPEARANCES = parseMaterialStream(new StringReader(JAVA_3D_MATERIALS), null);
} catch (IOException ex) {
// Can't happen because materials are read from a string
throw new InternalError("Can't access to default materials");
}
try
{
//URL uname=new URL("d:\\Solidview");
File fname=new File("d:\\Solidview\\MultipleObject.obj");
Reader r=new FileReader(fname);
try {
[COLOR="Red"] scene=load(r,fname.toURL()); //f.load(filename[/COLOR]);
}
objTrans.addChild(scene.getSceneGroup());
}
catch (FileNotFoundException e) {
System.err.println(e);
System.exit(1);
}
catch (ParsingErrorException e) {
System.err.println(e);
System.exit(1);
}
catch (IncorrectFormatException e) {
System.err.println(e);
System.exit(1);
}
// BranchGroup resultedobject=obj.getSceneGroup();
// objRoot.addChild(resultedobject);
// objRoot.compile();
}catch(MalformedURLException ex)
{
ex.printStackTrace();
}catch(FileNotFoundException fe)
{
fe.printStackTrace();
}catch(IOException ie)
{
ie.printStackTrace();
}
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,20.0,0.0), 100.0);
// Create the rotate behavior node
MouseRotate behavior = new MouseRotate();
behavior.setTransformGroup(objTrans);
objTrans.addChild(behavior);
behavior.setSchedulingBounds(bounds);
// Create the zoom behavior node
MouseZoom behavior2 = new MouseZoom();
behavior2.setTransformGroup(objTrans);
objTrans.addChild(behavior2);
behavior2.setSchedulingBounds(bounds);
// Create the translate behavior node
MouseTranslate behavior3 = new MouseTranslate();
behavior3.setTransformGroup(objTrans);
objTrans.addChild(behavior3);
behavior3.setSchedulingBounds(bounds);
// 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);
// Set up the ambient light
Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
AmbientLight ambientLightNode = new AmbientLight(ambientColor);
ambientLightNode.setInfluencingBounds(bounds);
objRoot.addChild(ambientLightNode);
// Set up the directional lights
Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
Vector3f light1Direction = new Vector3f(4.0f, -7.0f, -12.0f);
Color3f light2Color = new Color3f(0.3f, 0.3f, 0.4f);
Vector3f light2Direction = new Vector3f(-6.0f, -2.0f, -1.0f);
DirectionalLight light1
= new DirectionalLight(light1Color, light1Direction);
light1.setInfluencingBounds(bounds);
objRoot.addChild(light1);
DirectionalLight light2
= new DirectionalLight(light2Color, light2Direction);
light2.setInfluencingBounds(bounds);
objRoot.addChild(light2);
//objTrans.addChild(scene.getSceneGroup());
// objRoot=scene.getSceneGroup();
return objRoot;
}
private void usage() {
System.out.println("Usage: java PrintCanvas3D <.obj file>");
System.exit(0);
} // End of usage
private OffScreenCanvas3D createOffScreenCanvas(Canvas3D onScreenCanvas3D) {
// Create the off-screen Canvas3D object
// request an offscreen Canvas3D with a single buffer configuration
GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();
template.setDoubleBuffer(GraphicsConfigTemplate3D.UNNECESSARY);
GraphicsConfiguration gc =
GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getBestConfiguration(template);
offScreenCanvas3D = new OffScreenCanvas3D(gc, true);
// Set the off-screen size based on a scale factor times the
// on-screen size
Screen3D sOn = onScreenCanvas3D.getScreen3D();
Screen3D sOff = offScreenCanvas3D.getScreen3D();
Dimension dim = sOn.getSize();
dim.width *= OFF_SCREEN_SCALE;
dim.height *= OFF_SCREEN_SCALE;
sOff.setSize(dim);
sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth() *
OFF_SCREEN_SCALE);
sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight() *
OFF_SCREEN_SCALE);
// attach the offscreen canvas to the view
univ.getViewer().getView().addCanvas3D(offScreenCanvas3D);
return offScreenCanvas3D;
}
private Canvas3D createUniverse() {
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
Canvas3D c = new Canvas3D(config);
univ = new SimpleUniverse(c);
// This will move the ViewPlatform back a bit so the
// objects in the scene can be viewed.
univ.getViewingPlatform().setNominalViewingTransform();
// Ensure at least 5 msec per frame (i.e., < 200Hz)
univ.getViewer().getView().setMinimumFrameCycleTime(5);
return c;
}
/**
* Creates new form PrintCanvas3D
*/
public PrintCanvas3D(String args[]) {
System.out.println(args.length);
if (args.length == 0) {
//filename = getResource("resources/geometry/dinasaur.obj");
//filename=getResource("ImagePrinter.java");
try
{
File fname=new File("c:\\dinasaur.obj");
filename=fname.toURL();
}
catch(MalformedURLException e)
{
e.printStackTrace();
}
if (filename == null) {
System.out.println(filename.getFile());
System.err.println("resources/geometry/beethoven.obj not found");
System.exit(1);
}
} else {
for (int i = 0 ; i < args.length ; i++) {
if (args[i].startsWith("-")) {
System.err.println("Argument '" + args[i] + "' ignored.");
} else {
try{
//filename = new URL(args[i]);
File fname=new File(args[i]);
filename=fname.toURL();
//filename=new URL()
}
catch (MalformedURLException e) {
System.out.println(args[i]);
System.err.println(e.getMessage());
System.exit(1);
}
}
}
}
if (filename == null) {
usage();
}
// Initialize the GUI components
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
initComponents();
// Create Canvas3D and SimpleUniverse; add canvas to drawing panel
onScreenCanvas3D = createUniverse();
drawingPanel.add(onScreenCanvas3D, java.awt.BorderLayout.CENTER);
// Create the content branch and add it to the universe
BranchGroup scene = createSceneGraph(args);
// BranchGroup scene =buildShape();
// Create the off-screen Canvas3D object
createOffScreenCanvas(onScreenCanvas3D);
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();
jMenuBar1 = new javax.swing.JMenuBar();
fileMenu = new javax.swing.JMenu();
snapShotMenuItem = new javax.swing.JMenuItem();
printMenuItem = new javax.swing.JMenuItem();
exitMenuItem = new javax.swing.JMenuItem();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Window Title");
drawingPanel.setLayout(new java.awt.BorderLayout());
drawingPanel.setPreferredSize(new java.awt.Dimension(500, 500));
getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER);
fileMenu.setText("File");
snapShotMenuItem.setText("Snapshot");
snapShotMenuItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
snapShotMenuItemActionPerformed(evt);
}
});
fileMenu.add(snapShotMenuItem);
printMenuItem.setText("Print");
printMenuItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
printMenuItemActionPerformed(evt);
}
});
fileMenu.add(printMenuItem);
exitMenuItem.setText("Exit");
exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exitMenuItemActionPerformed(evt);
}
});
fileMenu.add(exitMenuItem);
jMenuBar1.add(fileMenu);
setJMenuBar(jMenuBar1);
pack();
}// </editor-fold>//GEN-END:initComponents
private void printMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_printMenuItemActionPerformed
Point loc = onScreenCanvas3D.getLocationOnScreen();
offScreenCanvas3D.setOffScreenLocation(loc);
Dimension dim = onScreenCanvas3D.getSize();
dim.width *= OFF_SCREEN_SCALE;
dim.height *= OFF_SCREEN_SCALE;
BufferedImage bImage =
offScreenCanvas3D.doRender(dim.width, dim.height);
new ImagePrinter(bImage).print();
}//GEN-LAST:event_printMenuItemActionPerformed
private void snapShotMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_snapShotMenuItemActionPerformed
Point loc = onScreenCanvas3D.getLocationOnScreen();
offScreenCanvas3D.setOffScreenLocation(loc);
Dimension dim = onScreenCanvas3D.getSize();
dim.width *= OFF_SCREEN_SCALE;
dim.height *= OFF_SCREEN_SCALE;
BufferedImage bImage =
offScreenCanvas3D.doRender(dim.width, dim.height);
new ImageDisplayer(bImage);
}//GEN-LAST:event_snapShotMenuItemActionPerformed
private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
System.exit(0);
}//GEN-LAST:event_exitMenuItemActionPerformed
// public static URL getResource(String filename) {
// URL url = Resources.class.getResource(filename);
// return url;
// }
public String readMaterial()
{
String mtlcontent="";
String temp="";
int i=0;
File file = new File("d:\\SolidView\\MultipleObject.mtl");
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(file);
// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
// dis.available() returns 0 if the file does not have more lines.
while (dis.available() != 0) {
// this statement reads the line from the file and print it to
// the console.
// System.out.println(dis.readLine());
//temp=dis.readLine();
if(i>2)
{
mtlcontent+=dis.readLine();
mtlcontent+="\n";
}
else
{
dis.readLine();
i++;
}
}
// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return mtlcontent;
}
/**
* Parses a map of appearances parsed from the given stream.
*/
private static Map<String, Appearance> parseMaterialStream(Reader reader,
URL baseUrl) throws IOException {
Map<String, Appearance> appearances = new HashMap<String, Appearance>();
Appearance currentAppearance = null;
StreamTokenizer tokenizer = createTokenizer(reader);
while (tokenizer .nextToken() != StreamTokenizer.TT_EOF) {
switch (tokenizer.ttype) {
case StreamTokenizer.TT_WORD :
currentAppearance = parseMaterialLine(tokenizer,
appearances, currentAppearance, baseUrl);
break;
case StreamTokenizer.TT_EOL:
break;
default :
throw new IncorrectFormatException("Unexpected token " + tokenizer.sval
+ " at row " + tokenizer.lineno());
}
}
return appearances;
}
/**
* Returns a new tokenizer for an OBJ or MTL stream.
*/
private static StreamTokenizer createTokenizer(Reader reader) {
StreamTokenizer tokenizer = new StreamTokenizer(reader);
tokenizer.resetSyntax();
tokenizer.eolIsSignificant(true);
// All printable ASCII characters
tokenizer.wordChars('!', '~');
// Let's tolerate other ISO-8859-1 characters
tokenizer.wordChars(0x80, 0xFF);
tokenizer.whitespaceChars(' ', ' ');
tokenizer.whitespaceChars('\n', '\n');
tokenizer.whitespaceChars('\r', '\r');
tokenizer.whitespaceChars('\t', '\t');
return tokenizer;
}
/**
* Parses the line starting with a word describing a material.
*/
private static Appearance parseMaterialLine(StreamTokenizer tokenizer,
Map<String, Appearance> appearances,
Appearance currentAppearance,
URL baseUrl) throws IOException {
if ("newmtl".equals(tokenizer.sval)) {
// Read material name newmtl name
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
currentAppearance = new Appearance();
appearances.put(tokenizer.sval, currentAppearance);
} else {
throw new IncorrectFormatException("Expected material name at line " + tokenizer.lineno());
}
} else if ("Ka".equals(tokenizer.sval)) {
// Read ambient color Ka r g b
Color3f ambiantColor = new Color3f(parseNumber(tokenizer),
parseNumber(tokenizer), parseNumber(tokenizer));
if (currentAppearance != null) {
Material material = getMaterial(currentAppearance);
material.setAmbientColor(ambiantColor);
}
} else if ("Kd".equals(tokenizer.sval)) {
// Read diffuse or emissive color Kd r g b
Color3f diffuseColor = new Color3f(parseNumber(tokenizer),
parseNumber(tokenizer), parseNumber(tokenizer));
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
material.setDiffuseColor(diffuseColor);
currentAppearance.setColoringAttributes(
new ColoringAttributes(diffuseColor, ColoringAttributes.SHADE_GOURAUD));
}
} else if ("Ks".equals(tokenizer.sval)) {
// Read specular color Ks r g b
Color3f specularColor = new Color3f(parseNumber(tokenizer),
parseNumber(tokenizer), parseNumber(tokenizer));
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
if (!material.isIlluminationModelSet()
|| material.getIlluminationModel() >= 2) {
material.setSpecularColor(specularColor);
} else {
material.setSpecularColor(0, 0, 0);
}
}
} else if ("Ns".equals(tokenizer.sval)) {
// Read shininess Ns val with 0 <= val <= 1000
float shininess = parseNumber(tokenizer);
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
if (!material.isIlluminationModelSet()
|| material.getIlluminationModel() >= 2) {
// Use shininess at a max value equal to 128
material.setShininess(Math.max(1f, Math.min(shininess, 128f)));
} else {
material.setShininess(1f);
}
}
} else if ("Ni".equals(tokenizer.sval)) {
// Read optical density Ni val
float opticalDensity = parseNumber(tokenizer);
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
material.setOpticalDensity(opticalDensity);
}
} else if ("sharpness".equals(tokenizer.sval)) {
// Read sharpness sharpness val
float sharpness = parseNumber(tokenizer);
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
material.setSharpness(sharpness);
}
} else if ("d".equals(tokenizer.sval)) {
// Read transparency d val with 0 <= val <= 1
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
if ("-halo".equals(tokenizer.sval)) {
// Ignore halo transparency
parseNumber(tokenizer);
} else {
tokenizer.pushBack();
float transparency = parseNumber(tokenizer);
if (currentAppearance != null) {
if (transparency >= 1) {
currentAppearance.setTransparencyAttributes(null);
} else {
currentAppearance.setTransparencyAttributes(new TransparencyAttributes(
TransparencyAttributes.NICEST, 1f - Math.max(0f, transparency)));
}
}
}
} else {
throw new IncorrectFormatException("Expected transparency factor at line " + tokenizer.lineno());
}
} else if ("illum".equals(tokenizer.sval)) {
// Read illumination setting illum n
int illumination = parseInteger(tokenizer);
if (currentAppearance != null) {
OBJMaterial material = getMaterial(currentAppearance);
material.setIlluminationModel(illumination);
material.setLightingEnable(illumination >= 1);
if (illumination <= 1) {
material.setSpecularColor(0, 0, 0);
material.setShininess(1f);
}
}
} else if ("map_Kd".equals(tokenizer.sval)) {
// Read material texture map_Kd name
// Search last parameter that matches image file name
String imageFileName = null;
while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
imageFileName = tokenizer.sval;
}
}
if (imageFileName != null) {
BufferedImage textureImage = null;
try {
textureImage = baseUrl != null
? ImageIO.read(new URL(baseUrl, imageFileName))
: ImageIO.read(new File(imageFileName));
} catch (IOException ex) {
ex.printStackTrace();
// Ignore images at other format
}
if (textureImage != null) {
TextureLoader textureLoader = new TextureLoader(textureImage, TextureLoader.GENERATE_MIPMAP);
currentAppearance.setTexture(textureLoader.getTexture());
System.out.println(imageFileName);
}
} else {
throw new IncorrectFormatException("Expected image file name at line " + tokenizer.lineno());
}
tokenizer.pushBack();
} else {
// Skip other lines (including comment lines starting by #)
while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
}
tokenizer.pushBack();
}
if (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
throw new IncorrectFormatException("Expected end of line at line " + tokenizer.lineno());
}
return currentAppearance;
}
/**
* Returns the material stored in the given <code>appearance</code>.
*/
private static OBJMaterial getMaterial(Appearance appearance) {
OBJMaterial material = (OBJMaterial)appearance.getMaterial();
if (material == null) {
material = new OBJMaterial();
appearance.setMaterial(material);
}
return material;
}
/**
* Returns the number contained in the next token.
*/
private static float parseNumber(StreamTokenizer tokenizer) throws IOException {
if (tokenizer.nextToken() != StreamTokenizer.TT_WORD) {
throw new IncorrectFormatException("Expected a number at line " + tokenizer.lineno());
} else {
try {
return Float.parseFloat(tokenizer.sval);
} catch (NumberFormatException ex) {
throw new IncorrectFormatException("Found " + tokenizer.sval +
" instead of a number at line " + tokenizer.lineno());
}
}
}
/**
* Returns the integer contained in the next token.
*/
private static int parseInteger(StreamTokenizer tokenizer) throws IOException {
if (tokenizer.nextToken() != StreamTokenizer.TT_WORD) {
throw new IncorrectFormatException("Expected an integer at line " + tokenizer.lineno());
} else {
try {
return Integer.parseInt(tokenizer.sval);
} catch (NumberFormatException ex) {
throw new IncorrectFormatException("Found " + tokenizer.sval +
" instead of an integer at line " + tokenizer.lineno());
}
}
}
/**
* Returns the appearance matching a given <code>material</code>.
*/
private Appearance getAppearance(String material) {
Appearance appearance = null;
if (material != null) {
appearance = this.appearances.get(material);
}
if (appearance == null) {
appearance = DEFAULT_APPEARANCES.get("default");
}
return appearance;
}
private static class Face {
private int [] vertexIndices;
private int [] textureCoordinateIndices;
private int [] normalIndices;
private String material;
public Face(List<Integer> vertexIndices,
List<Integer> textureCoordinateIndices,
List<Integer> normalIndices,
String material) {
this.vertexIndices = new int [vertexIndices.size()];
for (int i = 0; i < this.vertexIndices.length; i++) {
this.vertexIndices [i] = vertexIndices.get(i);
}
if (textureCoordinateIndices.size() != 0) {
this.textureCoordinateIndices = new int [textureCoordinateIndices.size()];
for (int i = 0; i < this.textureCoordinateIndices.length; i++) {
this.textureCoordinateIndices [i] = textureCoordinateIndices.get(i);
}
}
if (normalIndices.size() != 0) {
this.normalIndices = new int [normalIndices.size()];
for (int i = 0; i < this.normalIndices.length; i++) {
this.normalIndices [i] = normalIndices.get(i);
}
}
this.material = material;
}
public int [] getVertexIndices() {
return this.vertexIndices;
}
public int [] getTextureCoordinateIndices() {
return this.textureCoordinateIndices;
}
public boolean hasTextureCoordinateIndices() {
return this.textureCoordinateIndices != null
&& this.textureCoordinateIndices.length > 0;
}
public int [] getNormalIndices() {
return this.normalIndices;
}
public boolean hasNormalIndices() {
return this.normalIndices != null
&& this.normalIndices.length > 0;
}
public String getMaterial() {
return this.material;
}
}
private static class Group {
private final String name;
private boolean smooth;
private List<Face> faces;
public Group(String name) {
this.name = name;
this.faces = new ArrayList<Face>();
}
public String getName() {
return this.name;
}
public void setSmooth(boolean smooth) {
this.smooth = smooth;
}
public boolean isSmooth() {
return this.smooth;
}
public void addFace(Face face) {
this.faces.add(face);
}
public List<Face> getFaces() {
return this.faces;
}
}
/**
* Returns the scene parsed from a stream.
*/
private Scene parseObjectStream(Reader reader,
URL baseUrl) throws IOException {
this.vertices = new ArrayList<Point3f>();
this.textureCoodinates = new ArrayList<TexCoord2f>();
this.normals = new ArrayList<Vector3f>();
this.groups = new LinkedHashMap<String, Group>();
this.currentGroup = new Group("default");
this.groups.put("default", this.currentGroup);
this.currentMaterial = "default";
this.appearances = new HashMap<String, Appearance>(DEFAULT_APPEARANCES);
StreamTokenizer tokenizer = createTokenizer(reader);
while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
switch (tokenizer.ttype) {
case StreamTokenizer.TT_WORD :
parseObjectLine(tokenizer, baseUrl);
break;
case StreamTokenizer.TT_EOL:
break;
default :
throw new IncorrectFormatException("Unexpected token " + tokenizer.sval
+ " at row " + tokenizer.lineno());
}
}
try {
return createScene();
} finally {
this.vertices = null;
this.textureCoodinates = null;
this.normals = null;
this.groups = null;
this.appearances = null;
}
}
/**
* Parses the line starting with a word.
*/
private void parseObjectLine(StreamTokenizer tokenizer,
URL baseUrl) throws IOException {
if ("v".equals(tokenizer.sval)) {
// Read vertex v x y z
this.vertices.add(new Point3f(
parseNumber(tokenizer), parseNumber(tokenizer), parseNumber(tokenizer)));
} else if ("vn".equals(tokenizer.sval)) {
// Read normal vn x y z
this.normals.add(new Vector3f(
parseNumber(tokenizer), parseNumber(tokenizer), parseNumber(tokenizer)));
} else if ("vt".equals(tokenizer.sval)) {
// Read texture coordinate vt x y
// or vt x y z
this.textureCoodinates.add(new TexCoord2f(
parseNumber(tokenizer), parseNumber(tokenizer)));
// Skip next number if it exists
if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
tokenizer.pushBack();
}
} else if ("f".equals(tokenizer.sval)) {
tokenizer.ordinaryChar('/');
// Read face f v v v ...
// or f v//vn v//vn v//vn ...
// or f v/vt v/vt v/vt ...
// or f v/vt/vn v/vt/vn v/vt/vn ...
List<Integer> vertexIndices = new ArrayList<Integer>(4);
List<Integer> textureCoordinateIndices = new ArrayList<Integer>(4);
List<Integer> normalIndices = new ArrayList<Integer>(4);
while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
tokenizer.pushBack();
// Read vertex index
int vertexIndex = parseInteger(tokenizer) - 1;
if (vertexIndex < 0) {
vertexIndex += this.vertices.size() + 1;
}
vertexIndices.add(vertexIndex);
if (tokenizer.nextToken() != '/') {
// f v
tokenizer.pushBack();
} else {
if (tokenizer.nextToken() != '/') {
// f v/vt : read texture coordinate index
tokenizer.pushBack();
int textureCoordinateIndex = parseInteger(tokenizer) - 1;
if (textureCoordinateIndex < 0) {
textureCoordinateIndex += this.textureCoodinates.size() + 1;
}
textureCoordinateIndices.add(textureCoordinateIndex);
tokenizer.nextToken();
}
if (tokenizer.ttype == '/') {
// f v//vn
// or f v/vt/vn : read normal index
int normalIndex = parseInteger(tokenizer) - 1;
if (normalIndex < 0) {
normalIndex += this.normals.size() + 1;
}
normalIndices.add(normalIndex);
} else {
tokenizer.pushBack();
}
}
}
tokenizer.pushBack();
tokenizer.wordChars('/', '/');
if (textureCoordinateIndices.size() != 0
&& textureCoordinateIndices.size() != vertexIndices.size()) {
// Ignore unconsistent texture coordinate
textureCoordinateIndices.clear();
}
if (normalIndices.size() != 0
&& normalIndices.size() != vertexIndices.size()) {
// Ignore unconsistent normals
normalIndices.clear();
}
if (vertexIndices.size() > 2) {
this.currentGroup.addFace(new Face(vertexIndices, textureCoordinateIndices, normalIndices,
this.currentMaterial));
}
} else if ("g".equals(tokenizer.sval)
|| "o".equals(tokenizer.sval)) {
// Read group name g name
// or object name o name
boolean smoothingGroup = this.currentGroup.isSmooth();
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
this.currentGroup = this.groups.get(tokenizer.sval);
if (this.currentGroup == null) {
this.currentGroup = new Group(tokenizer.sval);
this.groups.put(this.currentGroup.getName(), this.currentGroup);
}
} else if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
// Use default group
this.currentGroup = this.groups.get("default");
tokenizer.pushBack();
} else {
throw new IncorrectFormatException("Expected group or object name at line " + tokenizer.lineno());
}
this.currentGroup.setSmooth(smoothingGroup);
// Skip other names
while (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
}
tokenizer.pushBack();
} else if ("s".equals(tokenizer.sval)) {
// Read smoothing group s n
// or s off
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
this.currentGroup.setSmooth(!"off".equals(tokenizer.sval));
} else {
throw new IncorrectFormatException("Expected smoothing group or off at line " + tokenizer.lineno());
}
} else if ("usemtl".equals(tokenizer.sval)) {
// Read the material name usemtl name
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
this.currentMaterial = tokenizer.sval;
} else {
throw new IncorrectFormatException("Expected material name at line " + tokenizer.lineno());
}
} else if ("mtllib".equals(tokenizer.sval)) {
int libCount = 0;
do {
if (tokenizer.nextToken() == StreamTokenizer.TT_WORD) {
parseMaterial(tokenizer.sval, baseUrl);
libCount++;
}
}
while (tokenizer.nextToken() != StreamTokenizer.TT_EOL);
if (libCount == 0) {
throw new IncorrectFormatException("Expected material library at line " + tokenizer.lineno());
}
tokenizer.pushBack();
} else {
// Skip other lines (including comment lines starting by #)
while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
}
tokenizer.pushBack();
}
if (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
throw new IncorrectFormatException("Expected end of line at line " + tokenizer.lineno());
}
}
/**
* Parses appearances from the given material file.
*/
private void parseMaterial(String file, URL baseUrl) {
InputStream in = null;
try {
if (baseUrl != null) {
in = new URL(baseUrl, file).openStream();
} else {
in = new FileInputStream(file);
}
} catch (IOException ex) {
// Ignore material files not found
}
if (in != null) {
try {
this.appearances.putAll(parseMaterialStream(
new BufferedReader(new InputStreamReader(in, "ISO-8859-1")), baseUrl));
} catch (IOException ex) {
throw new ParsingErrorException(ex.getMessage());
} finally {
try {
in.close();
} catch (IOException ex) {
throw new ParsingErrorException(ex.getMessage());
}
}
}
}
private SceneBase createScene() {
Point3f [] vertices = this.vertices.toArray(new Point3f [this.vertices.size()]);
TexCoord2f [] textureCoodinates =
this.textureCoodinates.toArray(new TexCoord2f [this.textureCoodinates.size()]);
Vector3f [] normals = this.normals.toArray(new Vector3f [this.normals.size()]);
SceneBase scene = new SceneBase();
BranchGroup sceneRoot = new BranchGroup();
scene.setSceneGroup(sceneRoot);
System.out.println(this.groups.values());
for (Group group : this.groups.values()) {
List<Face> faces = group.getFaces();
if (faces != null
&& !faces.isEmpty()) {
int i = 0;
while (i < faces.size()) {
Face firstFace = faces.get(i);
GeometryInfo geometry = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
boolean firstFaceHasTextureCoordinateIndices = firstFace.hasTextureCoordinateIndices();
boolean firstFaceHasNormalIndices = firstFace.hasNormalIndices();
String firstFaceMaterial = firstFace.getMaterial();
Appearance appearance = getAppearance(firstFaceMaterial);
System.out.println(firstFaceMaterial);
// Search how many faces share the same characteristics
int max = i;
while (++max < faces.size()) {
Face face = faces.get(max);
String material = face.getMaterial();
if (material == null && firstFaceMaterial != null
|| material != null && getAppearance(material) != appearance
|| (firstFaceHasTextureCoordinateIndices ^ face.hasTextureCoordinateIndices())
|| (firstFaceHasNormalIndices ^ face.hasNormalIndices())) {
break;
}
}
// Create indices arrays for the faces with an index between i and max
int faceCount = max - i;
int indexCount = 0;
for (int j = 0; j < faceCount; j++) {
indexCount += faces.get(i + j).getVertexIndices().length;
}
int [] coordinatesIndices = new int [indexCount];
int [] stripCounts = new int [faceCount];
for (int j = 0, destIndex = 0; j < faceCount; j++) {
int [] faceVertexIndices = faces.get(i + j).getVertexIndices();
System.arraycopy(faceVertexIndices, 0, coordinatesIndices, destIndex, faceVertexIndices.length);
stripCounts [j] = faceVertexIndices.length;
destIndex += faceVertexIndices.length;
}
geometry.setCoordinates(vertices);
geometry.setCoordinateIndices(coordinatesIndices);
geometry.setStripCounts(stripCounts);
if (firstFaceHasTextureCoordinateIndices) {
int [] textureCoordinateIndices = new int [indexCount];
for (int j = 0, destIndex = 0; j < faceCount; j++) {
int [] faceTextureCoordinateIndices = faces.get(i + j).getTextureCoordinateIndices();
System.arraycopy(faceTextureCoordinateIndices, 0, textureCoordinateIndices, destIndex, faceTextureCoordinateIndices.length);
destIndex += faceTextureCoordinateIndices.length;
}
geometry.setTextureCoordinateParams(1, 2);
geometry.setTextureCoordinates(0, textureCoodinates);
geometry.setTextureCoordinateIndices(0, textureCoordinateIndices);
}
if (firstFaceHasNormalIndices) {
int [] normalIndices = new int [indexCount];
for (int j = 0, destIndex = 0; j < faceCount; j++) {
int [] faceNormalIndices = faces.get(i + j).getNormalIndices();
System.arraycopy(faceNormalIndices, 0, normalIndices, destIndex, faceNormalIndices.length);
destIndex += faceNormalIndices.length;
}
geometry.setNormals(normals);
geometry.setNormalIndices(normalIndices);
} else {
NormalGenerator normalGenerator = new NormalGenerator(Math.PI / 2);
if (!group.isSmooth()) {
normalGenerator.setCreaseAngle(0);
}
normalGenerator.generateNormals(geometry);
}
// Clone appearance to avoid sharing it
if (appearance != null) {
appearance = (Appearance)appearance.cloneNodeComponent(false);
// Create texture coordinates if geometry doesn't define its own coordinates
// and appearance contains a texture
if (!firstFaceHasTextureCoordinateIndices
&& appearance.getTexture() != null) {
appearance.setTexCoordGeneration(new TexCoordGeneration());
}
}
Shape3D shape = new Shape3D(geometry.getGeometryArray(true, true, false), appearance);
sceneRoot.addChild(shape);
scene.addNamedObject(group.getName() + (i == 0 ? "" : String.valueOf(i)), shape);
i = max;
}
}
}
return scene;
}
private Scene load(Reader reader, URL baseUrl) throws FileNotFoundException {
if (!(reader instanceof BufferedReader)) {
reader = new BufferedReader(reader);
}
try {
return parseObjectStream(reader, baseUrl);
} catch (IOException ex) {
throw new ParsingErrorException(ex.getMessage());
} finally {
try {
reader.close();
} catch (IOException ex) {
throw new ParsingErrorException(ex.getMessage());
}
}
}
/**
* @param args the command line arguments
*/
public static void main(final String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new PrintCanvas3D(args).setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel drawingPanel;
private javax.swing.JMenuItem exitMenuItem;
private javax.swing.JMenu fileMenu;
private javax.swing.JMenuBar jMenuBar1;
private javax.swing.JMenuItem printMenuItem;
private javax.swing.JMenuItem snapShotMenuItem;
// End of variables declaration//GEN-END:variables
}