package edu.ups.gamedev.scene;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import com.jme.bounding.BoundingSphere;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.lod.AreaClodMesh;
import com.jme.scene.state.RenderState;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.FormatConverter;
import com.jmex.model.converters.ObjToJme;
/**
* Model is a generic class that loads a model file into a CLOD mesh. This
* class extends node, so a <code>Model</code> can be directly added to a
* scene graph once loaded. This model's level of detail will automatically
* be updated on the fly by the engine based on how far away the camera is.
*
* @author Walker Lindley
* @version $Revision: 1.1 $, $Date: 2008/01/26 08:13:22 $
*
* @see com.jme.scene.Node Node
* @see com.jme.scene.lod.AreaClodMesh AreaClodMesh
*
*/
public class Model extends Node {
private static final long serialVersionUID = 1L;
String name;
URL filename;
Spatial model;
/**
* Constructor taking the filename of the model to load.
*
* @param filename the name of model file to load
* @throws MalformedURLException
*/
public Model(String filename) throws MalformedURLException {
super();
name = filename;
this.filename = new File(filename).toURI().toURL();
model = loadModel(this.filename);
this.attachChild(getClodNodeFromParent((Node)model));
}
/**
* Constructor taking the filename of the model to load and the name
* that should be given to the node that is created.
*
* @param filename the name of model file to load
* @param name the name to give the Node
* @throws MalformedURLException
*/
public Model(String filename, String name) throws MalformedURLException {
super(name);
this.filename = new File(filename).toURI().toURL();
model = loadModel(this.filename);
this.attachChild(getClodNodeFromParent((Node)model));
}
public static Node getModelNode(String filename) throws MalformedURLException {
Node parent = new Node();
parent.attachChild(loadModel(new File(filename).toURI().toURL()));
System.out.println("Model " + filename + " loaded");
if(parent == null) {
System.out.println(" model is null");
}
Node clod = getClodNodeFromParent(parent);
if(clod == null) {
System.out.println(" clod is null");
}
return clod;
}
/**
* Loads and returns the specified model.
*
* @return a <code>Spatial</code> representing the model or <code>null</code> if
* it cannot load the specified model
*/
public static Spatial loadModel(URL name) {
Spatial model;
FormatConverter converter = new ObjToJme();
converter.setProperty("mtllib", name); //tell converter where to find the mtl file
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try {
converter.convert(name.openStream(), outStream);
//now that it's converted, load the model
model = (Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(outStream.toByteArray()));
} catch (IOException e) {
System.err.println("Error while converting or loading the model");
System.out.println("Error while converting or loading the model");
e.printStackTrace();
System.exit(-1);
return null;
}
return model;
}
/**
* Generates and returns an <code>AreaClodMesh</code> for the given model. Note that
* this method was shamelessly stolen from the JME tutorial HelloLOD.
*
* @param meshParent the <code>Node/code> that is the parent of the model
* @return a <code>Node</code> with the generated <code>AreaClodMesh
* </code> as a child
*
*/
public static Node getClodNodeFromParent(Node meshParent) {
// Create a node to hold my cLOD mesh objects
Node clodNode = new Node("Clod node");
// For each mesh in maggie
for (int i = 0; i < meshParent.getQuantity(); i++){
final Spatial child = meshParent.getChild(i);
if(child instanceof Node) {
clodNode.attachChild(getClodNodeFromParent((Node)child));
}
else if ( child instanceof TriMesh ) {
// Create an AreaClodMesh for that mesh. Let it compute records automatically
AreaClodMesh acm = new AreaClodMesh("part"+i,(TriMesh)child, null);
acm.setModelBound(new BoundingSphere());
acm.updateModelBound();
// Allow 1/2 of a triangle in every pixel on the screen in the bounds.
acm.setTrisPerPixel(.5f);
// Force a move of 2 units before updating the mesh geometry
acm.setDistanceTolerance(2);
// Give the clodMesh node the material state that the original had.
acm.setRenderState(child.getRenderState(RenderState.RS_MATERIAL));
// Attach clod node.
clodNode.attachChild(acm);
}
else
{
System.err.println("Unhandled Spatial type: " + child.getClass());
}
}
return clodNode;
}
@Override
public String getName() {
return name;
}
}