/* * Copyright (c) 2006 Greg Rodgers All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of Greg Rodgers, Sun Microsystems, Inc. or the names of * contributors may not be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. GREG , * SUN MICROSYSTEMS, INC. ("SUN"), AND SUN'S LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL GREG * RODGERS, SUN, OR SUN'S LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT * OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF BEN * CHAPPELL OR SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. */ package net.java.joglutils.ThreeDS; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class Loader3DS { // Primary Chunk, at the beginning of each file private static final int PRIMARY = 0x4D4D; // Main Chunks private static final int EDITOR = 0x3D3D; private static final int VERSION = 0x0002; private static final int EDITKEYFRAME = 0xB000; // Sub defines of EDITOR private static final int MATERIAL = 0xAFFF; private static final int OBJECT = 0x4000; // Sub defines of MATERIAL private static final int MATNAME = 0xA000; private static final int MATDIFFUSE = 0xA020; private static final int MATMAP = 0xA200; private static final int MATMAPFILE = 0xA300; private static final int OBJECT_MESH = 0x4100; // Sub defines of OBJECT_MESH private static final int OBJECT_VERTICES = 0x4110; private static final int OBJECT_FACES = 0x4120; private static final int OBJECT_MATERIAL = 0x4130; private static final int OBJECT_UV = 0x4140; // File reader private File file; private boolean loaded = false; private FileInputStream fileInputStream; private DataInputStream dataInputStream; // Global chunks private Chunk currentChunk, tempChunk; // Constructor public Loader3DS() { currentChunk = new Chunk(); tempChunk = new Chunk(); } // Verified public boolean load(Model3DS model, String fileName) { String strMessage; file = new File(fileName); try { fileInputStream = new FileInputStream(file); dataInputStream = new DataInputStream(fileInputStream); } catch (IOException e) { System.err.println("Error: File IO error in: Import 3DS"); return false; } readChunkHeader(currentChunk); if (currentChunk.id != PRIMARY) { System.err.println("Unable to load PRIMARY chuck from file!"); return false; } processNextChunk(model, currentChunk); computeNormals(model); try { dataInputStream.close(); fileInputStream.close(); } catch (IOException e) { System.err.println("Error: File IO error in: Closing File"); return false; } loaded = true; return loaded; } // Verified void processNextChunk(Model3DS model, Chunk previousChunk) { int version = 0; byte buffer[] = null; currentChunk = new Chunk(); try { while (previousChunk.bytesRead < previousChunk.length) { readChunkHeader(currentChunk); switch (currentChunk.id) { case VERSION: version = swap(dataInputStream.readInt()); currentChunk.bytesRead += 4; if (version > 0x03) System.err.println("This 3DS file is over version 3 so it may load incorrectly"); break; case EDITOR: readChunkHeader(tempChunk); buffer = new byte[tempChunk.length - tempChunk.bytesRead]; tempChunk.bytesRead += dataInputStream.read(buffer, 0, tempChunk.length - tempChunk.bytesRead); currentChunk.bytesRead += tempChunk.bytesRead; processNextChunk(model, currentChunk); break; case MATERIAL: Material mat = new Material(); model.addMaterial(mat); processNextMaterialChunk(model, mat, currentChunk); break; case OBJECT: Obj obj = new Obj(); obj.strName = getString(currentChunk); model.addObject(obj); processNextObjectChunk(model, obj, currentChunk); break; // case EDITKEYFRAME: // break; default: buffer = new byte[currentChunk.length - currentChunk.bytesRead]; currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); break; } previousChunk.bytesRead += currentChunk.bytesRead; } } catch (IOException e) { System.err.println("Error: File IO error in: Process Next Chunk"); return; } currentChunk = previousChunk; } // Verified private void readChunkHeader(Chunk chunk) { byte buffer[] = new byte[2]; try { chunk.id = swap(dataInputStream.readShort()); chunk.id &= 0x0000FFFF; chunk.bytesRead = 2; chunk.length = swap(dataInputStream.readInt()); chunk.bytesRead += 4; } catch (IOException e) { System.err.println("Error: File IO error in: Read Chunk Header"); return; } } // Verified private void processNextObjectChunk(Model3DS model, Obj object, Chunk previousChunk) { byte buffer[] = null; currentChunk = new Chunk(); try { while (previousChunk.bytesRead < previousChunk.length) { readChunkHeader(currentChunk); switch (currentChunk.id) { case OBJECT_MESH: processNextObjectChunk(model, object, currentChunk); break; case OBJECT_VERTICES: readVertices(object, currentChunk); break; case OBJECT_FACES: readFaceList(object, currentChunk); break; case OBJECT_MATERIAL: readObjectMaterial(model, object, currentChunk); break; case OBJECT_UV: readUVCoordinates(object, currentChunk); break; default: buffer = new byte[currentChunk.length - currentChunk.bytesRead]; currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); break; } previousChunk.bytesRead += currentChunk.bytesRead; } } catch (IOException e) { System.err.println("Error: File IO error in: Process Next Object Chunk"); return; } currentChunk = previousChunk; } // Verified private void processNextMaterialChunk(Model3DS model, Material material, Chunk previousChunk) { byte buffer[] = null; currentChunk = new Chunk(); try { while (previousChunk.bytesRead < previousChunk.length) { readChunkHeader(currentChunk); switch (currentChunk.id) { case MATNAME: material.strName = getString(currentChunk); buffer = new byte[currentChunk.length - currentChunk.bytesRead]; currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); break; case MATDIFFUSE: readColorChunk(material, currentChunk); break; case MATMAP: processNextMaterialChunk(model, material, currentChunk); break; case MATMAPFILE: material.strFile = getString(currentChunk); buffer = new byte[currentChunk.length - currentChunk.bytesRead]; currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); break; default: buffer = new byte[currentChunk.length - currentChunk.bytesRead]; currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); break; } previousChunk.bytesRead += currentChunk.bytesRead; } } catch (IOException e) { System.err.println("Error: File IO error in: Process Next Material Chunk"); return; } currentChunk = previousChunk; } // Verified private void readObjectMaterial(Model3DS model, Obj object, Chunk previousChunk) { String strMaterial = new String(); byte buffer[] = null; strMaterial = getString(previousChunk); for (int i=0; i 0) object.hasTexture = true; break; } } try { buffer = new byte[previousChunk.length - previousChunk.bytesRead]; previousChunk.bytesRead += dataInputStream.read(buffer, 0, previousChunk.length - previousChunk.bytesRead); } catch (IOException e) { System.err.println("Error: File IO error in: Read Object Material"); return; } } // Verified private void readUVCoordinates(Obj object, Chunk previousChunk) { try { object.numTexVertex = swap(dataInputStream.readShort()); previousChunk.bytesRead += 2; object.texVerts = new Vec3[object.numTexVertex]; for (int i=0; i> 8) & 0xff; return (short) (b1 << 8 | b2 << 0); } private static int swap(int value) { int b1 = (value >> 0) & 0xff; int b2 = (value >> 8) & 0xff; int b3 = (value >> 16) & 0xff; int b4 = (value >> 24) & 0xff; return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0; } private static long swap(long value) { long b1 = (value >> 0) & 0xff; long b2 = (value >> 8) & 0xff; long b3 = (value >> 16) & 0xff; long b4 = (value >> 24) & 0xff; long b5 = (value >> 32) & 0xff; long b6 = (value >> 40) & 0xff; long b7 = (value >> 48) & 0xff; long b8 = (value >> 56) & 0xff; return b1 << 56 | b2 << 48 | b3 << 40 | b4 << 32 | b5 << 24 | b6 << 16 | b7 << 8 | b8 << 0; } private static float swap(float value) { int intValue = Float.floatToIntBits(value); intValue = swap(intValue); return Float.intBitsToFloat(intValue); } private static double swap(double value) { long longValue = Double.doubleToLongBits(value); longValue = swap(longValue); return Double.longBitsToDouble(longValue); } }