Adding .3DS format to ModelLoader

Another addition to ModelLoader.

This time around, .3DS has been added, and .OBJ has been more refined to turn the model node directly into the imported model when it isn't a trimesh.

A CVS diff patch was sent, though of course I'm not a dev, so I have no means of addings it. :expressionless:

Enjoy!

/**
* This is a utility that will prompt for a model file and then load it into
* a scene.
*
* @author Matthew D. Hicks
* @author Alexander J. Gilpin
*/
public class ModelLoader {
public static void main(String[] args) {
// Store the texture in the binary file
// Texture.DEFAULT_STORE_TEXTURE = true;

try {
JFileChooser chooser = new JFileChooser();
Preferences preferences = Preferences.userNodeForPackage(ModelLoader.class);
File directory = new File(preferences.get("StartDirectory", "."));
chooser.setCurrentDirectory(directory);
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
if (isValidModelFile(file)) {
// Set it in preferences so we remember next time
preferences.put("StartDirectory", file.getAbsolutePath());

StandardGame game = new StandardGame("Model Loader");
try {
game.getSettings().clear();
} catch(Exception exc) {
exc.printStackTrace();
}
game.start();

GameTaskQueueManager.getManager().update(new Callable<Object>() {
public Object call() throws Exception {
//MouseInput.get().setCursorVisible(true);
return null;
}
});

DebugGameState debug = new DebugGameState();
GameStateManager.getInstance().attachChild(debug);
debug.setActive(true);

LoadingGameState loading = new LoadingGameState();
GameStateManager.getInstance().attachChild(loading);
loading.setActive(true);
loading.setProgress(0.5f, "Loading Model: " + file.getName());
long time = System.currentTimeMillis();
final Node modelNode = loadModel(file);
outputElapsed(time);
if (modelNode != null) {
//modelNode.setLocalScale(0.2f);
modelNode.setModelBound(new BoundingBox());
modelNode.updateModelBound();
modelNode.updateRenderState();
debug.getRootNode().attachChild(modelNode);
debug.getRootNode().updateRenderState();
if (file.getName().toLowerCase().endsWith(".jme")) {
loading.setProgress(1.0f, "Loaded Successfully");
} else {
loading.setProgress(0.8f, "Loaded Successfully - Saving");
try {
BinaryExporter.getInstance().save(modelNode, createJMEFile(file.getAbsoluteFile()));
loading.setProgress(1.0f, "Binary File Written Successfully");
} catch(IOException exc) {
exc.printStackTrace();
loading.setProgress(0.9f, "Binary Save Failure");
try {
Thread.sleep(5000);
} catch(InterruptedException exc2) {
exc2.printStackTrace();
}
loading.setProgress(1.0f);
}
}
} else {
loading.setProgress(0.9f, "Model Not Loaded");
try {
Thread.sleep(5000);
} catch(InterruptedException exc) {
exc.printStackTrace();
}
loading.setProgress(1.0f);
}
} else {
JOptionPane.showMessageDialog(null, "Selected file's extension is unknown model type: " + file.getName());
}
}
} catch(Throwable t) {
StringWriter writer = new StringWriter();
PrintWriter stream = new PrintWriter(writer);
t.printStackTrace(stream);
JFrame frame = new JFrame();
frame.setTitle("ModelLoader - StackTrace");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextPane panel = new JTextPane();
panel.setPreferredSize(new Dimension(400, 400));
panel.setContentType("text/plain");
panel.setText(writer.getBuffer().toString());
frame.setContentPane(new JScrollPane(panel));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

private static final File createJMEFile(File f) {
String filename = f.getName();
if (filename.indexOf('.') != -1) {
filename = filename.substring(0, filename.lastIndexOf('.'));
}
filename = filename + ".jme";
return new File(f.getParentFile(), filename);
}

private static final void outputElapsed(long startTime) {
float elapsed = (System.currentTimeMillis() - startTime) / 1000.0f;
System.out.println("Took " + elapsed + " seconds to load the model.");
}

public static final Node loadModel(final File file) throws Exception {
String filename = file.getName().toUpperCase();
Node model = null;
try {
if (filename.endsWith(".DAE")) {
Future<Node> future = GameTaskQueueManager.getManager().update(new Callable<Node>() {
public Node call() throws Exception {
ColladaImporter.load(file.toURI().toURL().openStream(), file.getAbsoluteFile().getParentFile().toURI().toURL(), "Model");
Node model = ColladaImporter.getModel();
return model;
}
});
model = future.get();
} else if (filename.endsWith(".JME")) {
model = (Node)BinaryImporter.getInstance().load(file);
} else if (filename.endsWith(".OBJ")) {
// Note that .OBJ Ambient colors are multiplied. I would strongly suggest making them black.
Future<Node> future = GameTaskQueueManager.getManager().update(new Callable<Node>() {
public Node call() throws Exception {
FormatConverter converter = new ObjToJme();
converter.setProperty("mtllib", file.getParentFile().toURI().toURL());
converter.setProperty("texdir", file.getParentFile().toURI().toURL());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
converter.convert(file.toURI().toURL().openStream(), bos);
if (BinaryImporter.getInstance().load(new ByteArrayInputStream(bos.toByteArray())).getClass() == TriMesh.class) {
Node model = new Node("Imported Model " + file.getName());
System.out.println("Trimesh! Attaching the Trimesh to the model Node!");
model.attachChild((Spatial)BinaryImporter.getInstance().load(new ByteArrayInputStream(bos.toByteArray())));
return model;
} else {
System.out.println("NOT a Trimesh! Directly converting to a Node!");
Node model = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(bos.toByteArray()));
return model;
}
}
});
model = future.get();
} else if (filename.endsWith(".3DS")) {
// Note that some .3DS Animations from Blender may not work. I'd suggest using a version of 3DSMax.
Future<Node> future = GameTaskQueueManager.getManager().update(new Callable<Node>() {
public Node call() throws Exception {
FormatConverter converter = new MaxToJme();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
converter.convert(file.toURI().toURL().openStream(), bos);
Node model = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(bos.toByteArray()));
return model;
}
});
model = future.get();
}
} catch(IOException exc) {
exc.printStackTrace();
}
return model;
}

public static final boolean isValidModelFile(File file) {
String filename = file.getName().toUpperCase();
if (filename.endsWith(".DAE")) return true;
else if (filename.endsWith(".JME")) return true;
else if (filename.endsWith(".OBJ")) return true;
else if (filename.endsWith(".3DS")) return true;
return false;
}
}

This has been done and I'll apply it an update this afternoon.  I have some other changes I'm going to be checking in today so I'm going to wait to push it with them.



Thanks!

Aye, gracias frogger  :smiley:

I was looking into this project and the load method:

ColladaImporter.load(file.toURI().toURL().openStream(), file.getAbsoluteFile().getParentFile().toURI().toURL(), "Model");



I cant see how you can get it work, because the original method takes only 2 parameters:

load(InputStream source, String name) , so my question is, how does it work with 3 parameters? :S