├── LICENSE ├── build.xml ├── dist ├── JUMI.jar └── README.TXT ├── manifest.mf ├── nbproject ├── build-impl.xml ├── genfiles.properties ├── project.properties └── project.xml └── src └── com └── jumi ├── JUMILoader.java ├── data ├── Color.java ├── Vector2.java ├── Vector3.java └── Vector4.java ├── fbx ├── FBXLoader.java ├── node │ ├── FBXConnectionsNode.java │ ├── FBXNode.java │ └── FBXObjectNode.java └── objects │ ├── FBXConnection.java │ ├── FBXProperty.java │ └── definitions │ ├── FBXAnimCurveDefinition.java │ ├── FBXAnimCurveNodeDefinition.java │ ├── FBXAnimLayerDefinition.java │ ├── FBXAnimStackDefinition.java │ ├── FBXCameraDefinition.java │ ├── FBXClusterDefinition.java │ ├── FBXLimbNodeDefinition.java │ ├── FBXMaterialDefinition.java │ ├── FBXMediaDefinition.java │ ├── FBXModelDefinition.java │ ├── FBXObjectDefinition.java │ ├── FBXShapeDefinition.java │ ├── FBXSkinDeformerDefinition.java │ └── FBXTextureDefinition.java ├── obj ├── OBJLoader.java └── objects │ └── definitions │ ├── OBJMatLibDefinition.java │ ├── OBJMaterialDefinition.java │ └── OBJModelDefinition.java └── scene ├── JUMIScene.java └── objects ├── JUMIBone.java ├── JUMIMaterial.java ├── JUMIMesh.java ├── JUMISkinDeformer.java ├── JUMISubDeformer.java └── JUMITexture.java /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 RGreenlees 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project JUMI. 12 | 13 | 73 | 74 | -------------------------------------------------------------------------------- /dist/JUMI.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RGreenlees/JUMI-Java-Model-Importer/701ea41eeec9f7465fa1965f0c6c7a985a68948f/dist/JUMI.jar -------------------------------------------------------------------------------- /dist/README.TXT: -------------------------------------------------------------------------------- 1 | ======================== 2 | BUILD OUTPUT DESCRIPTION 3 | ======================== 4 | 5 | When you build an Java application project that has a main class, the IDE 6 | automatically copies all of the JAR 7 | files on the projects classpath to your projects dist/lib folder. The IDE 8 | also adds each of the JAR files to the Class-Path element in the application 9 | JAR files manifest file (MANIFEST.MF). 10 | 11 | To run the project from the command line, go to the dist folder and 12 | type the following: 13 | 14 | java -jar "JUMI.jar" 15 | 16 | To distribute this project, zip up the dist folder (including the lib folder) 17 | and distribute the ZIP file. 18 | 19 | Notes: 20 | 21 | * If two JAR files on the project classpath have the same name, only the first 22 | JAR file is copied to the lib folder. 23 | * Only JAR files are copied to the lib folder. 24 | If the classpath contains other types of files or folders, these files (folders) 25 | are not copied. 26 | * If a library on the projects classpath also has a Class-Path element 27 | specified in the manifest,the content of the Class-Path element has to be on 28 | the projects runtime path. 29 | * To set a main class in a standard Java project, right-click the project node 30 | in the Projects window and choose Properties. Then click Run and enter the 31 | class name in the Main Class field. Alternatively, you can manually type the 32 | class name in the manifest Main-Class element. 33 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=7e62c94b 2 | build.xml.script.CRC32=90a764a5 3 | build.xml.stylesheet.CRC32=8064a381@1.75.2.48 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=7e62c94b 7 | nbproject/build-impl.xml.script.CRC32=dae1e692 8 | nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 6 | application.title=JUMI 7 | application.vendor=RGreenlees 8 | build.classes.dir=${build.dir}/classes 9 | build.classes.excludes=**/*.java,**/*.form 10 | # This directory is removed when the project is cleaned: 11 | build.dir=build 12 | build.generated.dir=${build.dir}/generated 13 | build.generated.sources.dir=${build.dir}/generated-sources 14 | # Only compile against the classpath explicitly listed here: 15 | build.sysclasspath=ignore 16 | build.test.classes.dir=${build.dir}/test/classes 17 | build.test.results.dir=${build.dir}/test/results 18 | # Uncomment to specify the preferred debugger connection transport: 19 | #debug.transport=dt_socket 20 | debug.classpath=\ 21 | ${run.classpath} 22 | debug.test.classpath=\ 23 | ${run.test.classpath} 24 | # Files in build.classes.dir which should be excluded from distribution jar 25 | dist.archive.excludes= 26 | # This directory is removed when the project is cleaned: 27 | dist.dir=dist 28 | dist.jar=${dist.dir}/JUMI.jar 29 | dist.javadoc.dir=${dist.dir}/javadoc 30 | endorsed.classpath= 31 | excludes= 32 | includes=** 33 | jar.compress=false 34 | javac.classpath= 35 | # Space-separated list of extra javac options 36 | javac.compilerargs= 37 | javac.deprecation=false 38 | javac.processorpath=\ 39 | ${javac.classpath} 40 | javac.source=1.7 41 | javac.target=1.7 42 | javac.test.classpath=\ 43 | ${javac.classpath}:\ 44 | ${build.classes.dir} 45 | javac.test.processorpath=\ 46 | ${javac.test.classpath} 47 | javadoc.additionalparam= 48 | javadoc.author=false 49 | javadoc.encoding=${source.encoding} 50 | javadoc.noindex=false 51 | javadoc.nonavbar=false 52 | javadoc.notree=false 53 | javadoc.private=false 54 | javadoc.splitindex=true 55 | javadoc.use=true 56 | javadoc.version=false 57 | javadoc.windowtitle= 58 | main.class=FBXTest 59 | manifest.file=manifest.mf 60 | meta.inf.dir=${src.dir}/META-INF 61 | mkdist.disabled=false 62 | platform.active=default_platform 63 | run.classpath=\ 64 | ${javac.classpath}:\ 65 | ${build.classes.dir} 66 | run.test.classpath=\ 67 | ${javac.test.classpath}:\ 68 | ${build.test.classes.dir} 69 | source.encoding=UTF-8 70 | src.dir=src 71 | test.src.dir=test 72 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | JUMI 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/com/jumi/JUMILoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi; 20 | 21 | import com.jumi.fbx.FBXLoader; 22 | import com.jumi.obj.OBJLoader; 23 | import com.jumi.scene.JUMIScene; 24 | import java.io.FileNotFoundException; 25 | import java.io.IOException; 26 | 27 | /** 28 | * JUMILoader 29 | * 30 | * This is the main loading class. Determines the file type being loaded and calls the appropriate loader. 31 | * Catches any exceptions thrown during the loading process. 32 | * 33 | * @author Richard Greenlees 34 | */ 35 | public class JUMILoader { 36 | 37 | /** Load the supplied file and return a standardised JUMIScene data structure 38 | * 39 | * @param filename The file to load 40 | * @return JUMIScene - A simplified data structure containing key elements 41 | */ 42 | public static JUMIScene loadModel(String filename) { 43 | try { 44 | String fileExtension = filename.substring(filename.indexOf('.') + 1, filename.length()).toUpperCase(); 45 | switch (fileExtension) { 46 | case "FBX": 47 | return FBXLoader.importModel(filename); 48 | case "OBJ": 49 | return OBJLoader.importModel(filename); 50 | default: 51 | return null; 52 | } 53 | } catch (FileNotFoundException e) { 54 | System.err.println("ERROR: JUMILoader failed to find specified file at " + filename); 55 | } catch (IOException e) { 56 | System.err.println("ERROR: JUMILoader encountered an issue while parsing file " + filename); 57 | } 58 | 59 | return null; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/com/jumi/data/Color.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.data; 20 | 21 | /** 22 | * Color 23 | * 24 | * A simple container for holding RGBA colour data 25 | * 26 | * @author Richard Greenlees 27 | */ 28 | public class Color { 29 | public float r; 30 | public float g; 31 | public float b; 32 | public float a; 33 | 34 | public Color() { 35 | r = 0.0f; 36 | g = 0.0f; 37 | b = 0.0f; 38 | a = 1.0f; 39 | } 40 | 41 | public Color(float newR, float newG, float newB, float newA) { 42 | r = newR; 43 | g = newG; 44 | b = newB; 45 | a = newA; 46 | } 47 | 48 | public Color(float newR, float newG, float newB) { 49 | r = newR; 50 | g = newG; 51 | b = newB; 52 | a = 1.0f; 53 | } 54 | 55 | public String toString() { 56 | return "{ R=" + r + ", G=" + g + ", B=" + b + ", A=" + a + "}"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/com/jumi/data/Vector2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.data; 20 | 21 | /** 22 | * Vector2 23 | * 24 | * A simple container for holding 2-dimensional float data (such as UVs) 25 | * 26 | * @author Richard Greenlees 27 | */ 28 | public class Vector2 { 29 | 30 | public float x; 31 | public float y; 32 | 33 | public Vector2(float newX, float newY) { 34 | x = newX; 35 | y = newY; 36 | } 37 | 38 | public void set(float newX, float newY) { 39 | x = newX; 40 | y = newY; 41 | } 42 | 43 | public Vector2() { 44 | super(); 45 | } 46 | 47 | public String toString() { 48 | return "(" + x + ", " + y + ")"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/com/jumi/data/Vector3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.data; 20 | 21 | /** 22 | * Vector3 23 | * 24 | * A simple container for holding 3-dimensional data (such as vertices) 25 | * 26 | * @author Richard Greenlees 27 | */ 28 | public class Vector3 { 29 | public float x; 30 | public float y; 31 | public float z; 32 | 33 | public Vector3(float newX, float newY, float newZ) { 34 | x = newX; 35 | y = newY; 36 | z = newZ; 37 | } 38 | 39 | public void set(float newX, float newY, float newZ) { 40 | x = newX; 41 | y = newY; 42 | z = newZ; 43 | } 44 | 45 | public Vector3() { 46 | super(); 47 | } 48 | 49 | public String toString() { 50 | return "(" + x + ", " + y + ", " + z + ")"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/com/jumi/data/Vector4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.data; 20 | 21 | /** 22 | * Vector4 23 | * 24 | * A simple container for holding 4-dimensional data (such as Quaternions) 25 | * 26 | * @author RGreenlees 27 | */ 28 | public class Vector4 { 29 | public float x; 30 | public float y; 31 | public float z; 32 | public float w; 33 | 34 | public Vector4() { 35 | super(); 36 | } 37 | 38 | public Vector4(float newX, float newY, float newZ, float newW) { 39 | x = newX; 40 | y = newY; 41 | z = newZ; 42 | w = newW; 43 | } 44 | 45 | public void set(float newX, float newY, float newZ, float newW) { 46 | x = newX; 47 | y = newY; 48 | z = newZ; 49 | w = newW; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/FBXLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx; 20 | 21 | import com.jumi.JUMILoader; 22 | import com.jumi.fbx.node.FBXConnectionsNode; 23 | import static com.jumi.fbx.node.FBXNode.retrieveBytesFrom; 24 | import com.jumi.fbx.node.FBXObjectNode; 25 | import com.jumi.fbx.objects.FBXConnection; 26 | import com.jumi.fbx.objects.definitions.FBXModelDefinition; 27 | import com.jumi.fbx.objects.definitions.FBXObjectDefinition; 28 | import com.jumi.fbx.objects.definitions.FBXTextureDefinition; 29 | import com.jumi.scene.JUMIScene; 30 | import com.jumi.scene.objects.JUMIMesh; 31 | import com.jumi.scene.objects.JUMITexture; 32 | import java.io.BufferedInputStream; 33 | import java.io.File; 34 | import java.io.FileInputStream; 35 | import java.io.IOException; 36 | import java.io.InputStream; 37 | import java.nio.ByteBuffer; 38 | import java.nio.ByteOrder; 39 | import java.util.ArrayList; 40 | 41 | /** 42 | * FBXLoader 43 | * 44 | * The main FBX loading class. Can be invoked directly if the user wishes, but is normally invoked by JUMILoader. 45 | * Parses the supplied FBX binary file and returns a JUMIScene object. 46 | * 47 | * @author Richard Greenlees 48 | */ 49 | public class FBXLoader extends JUMILoader { 50 | 51 | /** Import a FBX binary file, parse it and return a JUMIScene object containing the scene data 52 | * 53 | * @param fileName Location of the FBX file to load 54 | * @return JUMIScene containing scene data 55 | * @throws IOException 56 | */ 57 | public static JUMIScene importModel(String fileName) throws IOException { 58 | FBXObjectNode objectsNode = null; 59 | FBXConnectionsNode connectionsNode = null; 60 | 61 | ArrayList allMeshes = new ArrayList(); 62 | ArrayList allTextures = new ArrayList(); 63 | 64 | byte[] mybytes = readBytes(fileName); 65 | 66 | // Read bytes 23 - 26 to retrieve version number 67 | byte[] versionData = retrieveBytesFrom(mybytes, 4, 23); 68 | int version = ByteBuffer.wrap(versionData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 69 | 70 | float versionID = (float) version / 1000.0f; 71 | 72 | if (version < 7100) { 73 | String file = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length()); 74 | System.out.println("WARNING: Asset " + file + " uses an older version of the FBX SDK (" + versionID + "), animation data will not be imported."); 75 | System.out.println("\tFor best results, please use 7.1 (SDK 2010) or later."); 76 | } else if (version > 7300) { 77 | String file = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length()); 78 | System.out.println("WARNING: Asset " + file + " uses a newer version of the FBX SDK (" + versionID + ") than the max supported version (7.3), this may produce unexpected results."); 79 | System.out.println("\tFor best results, please use 7.3 (SDK 2011)."); 80 | } 81 | 82 | // Start reading the binary data at byte 27, the first byte after the header 83 | int offset = 27; 84 | 85 | while (true) { 86 | int endOffset; 87 | 88 | // Retrieve the end point of the next FBX node 89 | byte[] offsetData = retrieveBytesFrom(mybytes, 4, offset); 90 | endOffset = ByteBuffer.wrap(offsetData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 91 | 92 | // This shouldn't happen but you never know... 93 | if (endOffset <= 0) { 94 | break; 95 | } 96 | 97 | // Retrieve the data for the next node, using the calculated endoffset 98 | // TODO: Handle this better so we're not storing the data twice in memory (once in mybytes and once here) 99 | byte[] nextNodeData = retrieveBytesFrom(mybytes, endOffset - offset, offset); 100 | 101 | // Retrieve the length in bytes of the next node's name 102 | int nextNodeNameLength = nextNodeData[12]; 103 | // Retrieve the name data 104 | byte[] nextNodeNameData = retrieveBytesFrom(nextNodeData, nextNodeNameLength, 13); 105 | 106 | String nextNodeName = new String(nextNodeNameData); 107 | 108 | // The Objects node holds all the definitions 109 | if (nextNodeName.equals("Objects")) { 110 | objectsNode = new FBXObjectNode(nextNodeData, (nextNodeNameLength + 13)); 111 | objectsNode.parseData(nextNodeData, (nextNodeNameLength + 13)); 112 | // The Connections node hooks the objects together to create useful data structures 113 | } else if (nextNodeName.equals("Connections")) { 114 | connectionsNode = new FBXConnectionsNode(nextNodeData, (nextNodeNameLength + 13)); 115 | connectionsNode.parseData(nextNodeData, (nextNodeNameLength + 13)); 116 | // This is the deprecated animation system, but is still present. Once we reach this point we've parsed all useful data 117 | // TODO: Handle this better so we're not reliant on the Takes node to determine when we've finished parsing the file 118 | } else if (nextNodeName.equals("Takes")) { 119 | break; 120 | } 121 | 122 | offset = endOffset; 123 | } 124 | 125 | // For each connection defined in the Connections node, hook them up 126 | for (FBXConnection connection : connectionsNode.connections) { 127 | FBXObjectDefinition a = objectsNode.getConnectable(connection.getLeftUID()); 128 | FBXObjectDefinition b = objectsNode.getConnectable(connection.getRightUID()); 129 | 130 | if (a != null && b != null) { 131 | a.connect(b); 132 | } 133 | } 134 | 135 | // Retrieve all the model definitions and turn them into JUMIMeshes 136 | for (FBXModelDefinition a : objectsNode.getMeshDefinitions()) { 137 | allMeshes.add(a.createMesh()); 138 | } 139 | 140 | // Retrieve all texture definitions that don't have a parent mesh and turn them into JUMITextures 141 | for (FBXTextureDefinition a : objectsNode.getTextureDefinitions()) { 142 | allTextures.add(a.createTexture()); 143 | } 144 | 145 | // Build the scene 146 | JUMIScene result = new JUMIScene(); 147 | result.addMeshes(allMeshes); 148 | result.addTextures(allTextures); 149 | return result; 150 | } 151 | 152 | /** Read the supplied file and extract the binary data from it */ 153 | protected static byte[] readBytes(String aInputFileName) throws IOException { 154 | File file = new File(aInputFileName); 155 | byte[] result = new byte[(int) file.length()]; 156 | 157 | int totalBytesRead = 0; 158 | try (InputStream input = new BufferedInputStream(new FileInputStream(file))) { 159 | while (totalBytesRead < result.length) { 160 | int bytesRemaining = result.length - totalBytesRead; 161 | int bytesRead = input.read(result, totalBytesRead, bytesRemaining); 162 | if (bytesRead > 0) { 163 | totalBytesRead = totalBytesRead + bytesRead; 164 | } 165 | } 166 | } 167 | 168 | return result; 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/node/FBXConnectionsNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.node; 20 | 21 | import com.jumi.fbx.objects.FBXConnection; 22 | import com.jumi.fbx.objects.FBXProperty; 23 | import java.util.ArrayList; 24 | 25 | /** 26 | * FBXConnectionsNode 27 | * 28 | * Parses the FBX Connections node data to produce a set of object connections 29 | * 30 | * @author RGreenlees 31 | */ 32 | public class FBXConnectionsNode extends FBXNode { 33 | 34 | public ArrayList connections = new ArrayList(); 35 | 36 | public FBXConnectionsNode(byte[] inputData, int propertyOffset) { 37 | super(inputData, propertyOffset); 38 | } 39 | 40 | /** Method to parse the binary data for the Connections node 41 | * @param inputData The binary data being parsed 42 | * @param propertyOffset Where to start parsing from */ 43 | @Override 44 | public void parseData(byte[] inputData, int propertyOffset) { 45 | 46 | // Allow for 13 null bytes at the end of the node (standard in FBX files) 47 | while (sizeInBytes - cursorPosition > 13) { 48 | 49 | // Determine the name of the nested node. It should never be anything except "C" or "Connect" but you never know 50 | int nestedNameLength = inputData[cursorPosition + 12]; 51 | byte[] nestedNameData = FBXNode.retrieveBytesFrom(inputData, nestedNameLength, cursorPosition + 13); 52 | String nestedName = new String(nestedNameData); 53 | 54 | 55 | // Find out how many properties we have. Should always be 3 but again, you never know 56 | int nestedProperties = getNumProperties(inputData, cursorPosition); 57 | 58 | 59 | FBXProperty[] connectionProperties = new FBXProperty[nestedProperties]; 60 | cursorPosition += nestedNameLength + 13; 61 | 62 | /* Parse all of the properties of this nested item. The first property indicates if it's 63 | connecting two objects ("OO") or an object to a property ("OP"). The next two properties 64 | are the UIDs of the two objects or object and property to hook up */ 65 | for (int i = 0; i < nestedProperties; i++) { 66 | FBXProperty newProp = new FBXProperty(inputData, cursorPosition); 67 | cursorPosition += newProp.dataLength; 68 | connectionProperties[i] = newProp; 69 | } 70 | 71 | // Determine the two objects to connect and create a connection object for them to be handled later 72 | if (nestedName.equals("C") || nestedName.equals("Connect")) { 73 | switch (connectionProperties[0].asString()) { 74 | case "OO": 75 | if (connectionProperties[1].dataType.equals("Long")) { 76 | connections.add(new FBXConnection(FBXConnection.FBXConnectionType.OBJECT_OBJECT, connectionProperties[1].asLong(), connectionProperties[2].asLong())); 77 | } else { 78 | String leftID = connectionProperties[1].asString().substring(0, connectionProperties[1].asString().indexOf('\0')); 79 | String rightID = connectionProperties[2].asString().substring(0, connectionProperties[2].asString().indexOf('\0')); 80 | connections.add(new FBXConnection(FBXConnection.FBXConnectionType.OBJECT_OBJECT, leftID, rightID)); 81 | } break; 82 | case "OP": 83 | if (connectionProperties[1].dataType.equals("Long")) { 84 | connections.add(new FBXConnection(FBXConnection.FBXConnectionType.OBJECT_PROPERTY, connectionProperties[1].asLong(), connectionProperties[2].asLong())); 85 | } else { 86 | String leftID = connectionProperties[1].asString().substring(0, connectionProperties[1].asString().indexOf('\0')); 87 | String rightID = connectionProperties[2].asString().substring(0, connectionProperties[2].asString().indexOf('\0')); 88 | connections.add(new FBXConnection(FBXConnection.FBXConnectionType.OBJECT_PROPERTY, leftID, rightID)); 89 | } break; 90 | } 91 | } 92 | } 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/node/FBXNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.node; 20 | 21 | 22 | import com.jumi.fbx.objects.FBXProperty; 23 | import java.nio.ByteBuffer; 24 | import java.nio.ByteOrder; 25 | import java.util.ArrayList; 26 | 27 | /** 28 | * FBXNode 29 | * 30 | * Base class for Object and Connection nodes 31 | * 32 | * @author Richard Greenlees 33 | */ 34 | public abstract class FBXNode { 35 | 36 | public int endOffset; 37 | public int numProperties; 38 | public int propertyListLength; 39 | public int cursorPosition; 40 | public int nameLength; 41 | public String name; 42 | public int sizeInBytes; 43 | 44 | public ArrayList properties = new ArrayList(); 45 | 46 | // Constructor. Sets up some useful info about the node 47 | public FBXNode(byte[] inputData, int propertyOffset) { 48 | sizeInBytes = inputData.length; 49 | 50 | numProperties = getNumProperties(inputData); 51 | 52 | int propertyStartOffset = propertyOffset; 53 | 54 | for (int i = 0; i < numProperties; i++) { 55 | FBXProperty newProp = new FBXProperty(inputData, propertyStartOffset); 56 | propertyStartOffset += newProp.dataLength; 57 | properties.add(newProp); 58 | } 59 | 60 | cursorPosition = propertyStartOffset; 61 | } 62 | 63 | public abstract void parseData(byte[] inputData, int propertyOffset); 64 | 65 | // Helper method to extract the data specific to this node 66 | public final byte[] extractRawData(byte[] inputData, int startPosition, int endOffset) { 67 | byte[] rawData = new byte[endOffset - startPosition]; 68 | 69 | for (int i = 0; i < rawData.length; i++) { 70 | rawData[i] = inputData[startPosition + i]; 71 | } 72 | 73 | return rawData; 74 | } 75 | 76 | public final int getNumProperties(byte[] inputData) { 77 | byte[] bytes = retrieveBytesFrom(inputData, 4, 4); 78 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 79 | } 80 | 81 | public final int getNumProperties(byte[] inputData, int startPosition) { 82 | byte[] bytes = retrieveBytesFrom(inputData, 4, 4+startPosition); 83 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 84 | } 85 | 86 | public final String getName(byte[] inputData) { 87 | nameLength = getNameLength(inputData); 88 | return new String(retrieveBytesFrom(inputData, nameLength, 13)); 89 | } 90 | 91 | public final int getNameLength(byte[] inputData) { 92 | return inputData[12]; 93 | } 94 | 95 | public final int getPropertyListLength(byte[] inputData) { 96 | byte[] bytes = retrieveBytesFrom(inputData, 4, 8); 97 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 98 | } 99 | 100 | public final int getEndOffset(byte[] inputData, int startPosition) { 101 | byte[] result = retrieveBytesFrom(inputData, 4, startPosition); 102 | return ByteBuffer.wrap(result).order(ByteOrder.LITTLE_ENDIAN).getInt(); 103 | } 104 | 105 | public final static byte[] retrieveBytesFrom(byte[] inputData, int numBytes, int offSet) { 106 | byte[] result = new byte[numBytes]; 107 | 108 | for (int i = offSet; i < numBytes + offSet; i++) { 109 | result[i - offSet] = inputData[i]; 110 | } 111 | 112 | return result; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/node/FBXObjectNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.node; 20 | 21 | import com.jumi.fbx.objects.definitions.FBXAnimCurveDefinition; 22 | import com.jumi.fbx.objects.definitions.FBXAnimCurveNodeDefinition; 23 | import com.jumi.fbx.objects.definitions.FBXAnimLayerDefinition; 24 | import com.jumi.fbx.objects.definitions.FBXAnimStackDefinition; 25 | import com.jumi.fbx.objects.definitions.FBXCameraDefinition; 26 | import com.jumi.fbx.objects.definitions.FBXClusterDefinition; 27 | import com.jumi.fbx.objects.definitions.FBXLimbNodeDefinition; 28 | import com.jumi.fbx.objects.definitions.FBXMaterialDefinition; 29 | import com.jumi.fbx.objects.definitions.FBXMediaDefinition; 30 | import com.jumi.fbx.objects.definitions.FBXModelDefinition; 31 | import com.jumi.fbx.objects.definitions.FBXObjectDefinition; 32 | import com.jumi.fbx.objects.FBXProperty; 33 | import com.jumi.fbx.objects.definitions.FBXShapeDefinition; 34 | import com.jumi.fbx.objects.definitions.FBXSkinDeformerDefinition; 35 | import com.jumi.fbx.objects.definitions.FBXTextureDefinition; 36 | import java.nio.ByteBuffer; 37 | import java.nio.ByteOrder; 38 | import java.util.ArrayList; 39 | import java.util.HashMap; 40 | import java.util.Map.Entry; 41 | 42 | /** 43 | * FBXObjectNode 44 | * 45 | * Parses data stored in the Objects node. 46 | * 47 | * @author Richard Greenlees 48 | */ 49 | public class FBXObjectNode extends FBXNode { 50 | 51 | public HashMap connectableObjects = new HashMap(); 52 | 53 | public FBXObjectNode(byte[] inputData, int propertyOffset) { 54 | super(inputData, propertyOffset); 55 | } 56 | 57 | @Override 58 | public void parseData(byte[] inputData, int propertyOffset) { 59 | 60 | long objectUID; 61 | FBXObjectDefinition newObject; 62 | 63 | while (sizeInBytes - cursorPosition > 13) { 64 | 65 | objectUID = 0; 66 | newObject = null; 67 | 68 | int nestedNameLength = inputData[cursorPosition + 12]; 69 | 70 | byte[] nestedNameData = FBXNode.retrieveBytesFrom(inputData, nestedNameLength, cursorPosition + 13); 71 | String nestedName = new String(nestedNameData); 72 | 73 | byte[] numPropertiesData = FBXNode.retrieveBytesFrom(inputData, 4, cursorPosition + 4); 74 | int numNestedProperties = ByteBuffer.wrap(numPropertiesData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 75 | 76 | FBXProperty[] nestedProperties = new FBXProperty[numNestedProperties]; 77 | 78 | cursorPosition += nestedNameLength + 13; 79 | 80 | for (int i = 0; i < nestedProperties.length; i++) { 81 | FBXProperty newProp = new FBXProperty(inputData, cursorPosition); 82 | cursorPosition += newProp.dataLength; 83 | nestedProperties[i] = newProp; 84 | } 85 | 86 | if (nestedProperties.length > 0 && nestedProperties[0].dataType.equals("Long")) { 87 | objectUID = nestedProperties[0].asLong(); 88 | } 89 | 90 | // What kind of nested item is this? 91 | switch (nestedName) { 92 | case "Model": 93 | case "Geometry": 94 | String modelType = nestedProperties[nestedProperties.length - 1].asString(); 95 | 96 | switch (modelType) { 97 | // Bone definition. Name and local translation 98 | case "LimbNode": 99 | newObject = new FBXLimbNodeDefinition(objectUID); 100 | break; 101 | // Morph target 102 | case "Shape": 103 | newObject = new FBXShapeDefinition(objectUID); 104 | break; 105 | // Mesh geometry or model attributes (local translation etc...) 106 | case "Mesh": 107 | newObject = new FBXModelDefinition(objectUID); 108 | break; 109 | // TODO: Look at adding support for cameras 110 | default: break; 111 | } 112 | break; 113 | // Material definition. Doesn't contain texture info 114 | case "Material": 115 | newObject = new FBXMaterialDefinition(objectUID); 116 | break; 117 | // A texture definition, filename, format etc. 118 | case "Texture": 119 | newObject = new FBXTextureDefinition(objectUID); 120 | break; 121 | // Seems to do the same job as a texture object, not sure why it's needed but it's used so keep it 122 | case "Video": 123 | newObject = new FBXMediaDefinition(objectUID); 124 | break; 125 | // Bone definition. Affected vertices and weights 126 | case "Deformer": 127 | String deformerType = nestedProperties[nestedProperties.length - 1].asString(); 128 | if (deformerType.equals("Skin")) { 129 | newObject = new FBXSkinDeformerDefinition(objectUID); 130 | } else if (deformerType.equals("Cluster")) { 131 | newObject = new FBXClusterDefinition(objectUID); 132 | } break; 133 | // A container for animations, allowing for blending 134 | case "AnimationStack": 135 | newObject = new FBXAnimStackDefinition(objectUID); 136 | break; 137 | // A single animation 138 | case "AnimationLayer": 139 | newObject = new FBXAnimLayerDefinition(objectUID); 140 | break; 141 | // Contains the actual animation data 142 | case "AnimationCurveNode": 143 | newObject = new FBXAnimCurveNodeDefinition(objectUID); 144 | break; 145 | // Data for a single keyframe 146 | case "AnimationCurve": 147 | newObject = new FBXAnimCurveDefinition(objectUID); 148 | break; 149 | } 150 | 151 | // If our new object is one of the above then add it to our connectable objects ready to hook up 152 | if (newObject != null) { 153 | 154 | String newObjectName = nestedProperties[nestedProperties.length - 2].asString(); 155 | newObjectName = newObjectName.substring(0, newObjectName.indexOf('\0')); 156 | 157 | newObject.setName(newObjectName); 158 | newObject.parseData(inputData, cursorPosition); 159 | // Some older FBX files use String names as IDs instead of longs. Performance isn't affected too badly so let's allow it 160 | connectableObjects.put((objectUID > 0) ? String.valueOf(objectUID) : newObjectName, newObject); 161 | cursorPosition = newObject.endOffset; 162 | } 163 | 164 | } 165 | } 166 | 167 | /** Returns a specific connectable object using the supplied key */ 168 | public FBXObjectDefinition getConnectable(String key) { 169 | return connectableObjects.get(key); 170 | } 171 | 172 | /** Retrieves all the individual mesh objects in the scene. Only returns parent models that have geometry assigned */ 173 | public ArrayList getMeshDefinitions() { 174 | ArrayList result = new ArrayList(); 175 | for (Entry entry : connectableObjects.entrySet()) { 176 | if (entry.getValue() instanceof FBXModelDefinition) { 177 | FBXModelDefinition test = (FBXModelDefinition) entry.getValue(); 178 | if (test.isRoot() && test.containsGeometryDefinition()) { 179 | result.add(test); 180 | } 181 | } 182 | } 183 | return result; 184 | } 185 | 186 | /** Retrieves all "orphan" textures which aren't assigned to a mesh for some reason */ 187 | public ArrayList getTextureDefinitions() { 188 | ArrayList result = new ArrayList(); 189 | for (Entry entry : connectableObjects.entrySet()) { 190 | if (entry.getValue() instanceof FBXTextureDefinition) { 191 | FBXTextureDefinition test = (FBXTextureDefinition) entry.getValue(); 192 | if (test.parent == null) { 193 | result.add(test); 194 | } 195 | } 196 | } 197 | return result; 198 | } 199 | 200 | /** Get the root node for the mesh */ 201 | public FBXLimbNodeDefinition getRootNode() { 202 | for (Entry entry : connectableObjects.entrySet()) { 203 | if (entry.getValue() instanceof FBXLimbNodeDefinition) { 204 | FBXLimbNodeDefinition test = (FBXLimbNodeDefinition) entry.getValue(); 205 | if (test.isRoot) { 206 | return test; 207 | } 208 | } 209 | } 210 | return null; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/FBXConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects; 20 | 21 | 22 | /** 23 | * FBXConnection 24 | * 25 | * A container for an FBX connection definition. 26 | * 27 | * @author Richard Greenlees 28 | */ 29 | public class FBXConnection { 30 | public long leftObjectUID; 31 | public long rightObjectUID; 32 | 33 | public String leftObjectName; 34 | public String rightObjectName; 35 | 36 | public static enum FBXConnectionType {OBJECT_OBJECT, OBJECT_PROPERTY }; 37 | 38 | public FBXConnectionType connectionType; 39 | 40 | public FBXConnection(FBXConnectionType newConnectionType, long leftUID, long rightUID) { 41 | connectionType = newConnectionType; 42 | leftObjectUID = leftUID; 43 | rightObjectUID = rightUID; 44 | } 45 | 46 | public FBXConnection(FBXConnectionType newConnectionType, String leftUID, String rightUID) { 47 | connectionType = newConnectionType; 48 | leftObjectName = leftUID; 49 | rightObjectName = rightUID; 50 | } 51 | 52 | /** Does this connection reference the given long ID? */ 53 | public boolean containsReferenceTo(long ref) { 54 | return leftObjectUID == ref || rightObjectUID == ref; 55 | } 56 | 57 | /** Does this connection reference the given String ID? */ 58 | public boolean containsReferenceTo(String ref) { 59 | return leftObjectName.equals(ref) || rightObjectName.equals(ref); 60 | } 61 | 62 | public String toString() { 63 | String result = "Connection: "; 64 | 65 | if (leftObjectUID > 0) { 66 | result = result + leftObjectUID + " to "; 67 | } else { 68 | result = result + leftObjectName + " to "; 69 | } 70 | 71 | if (rightObjectUID > 0) { 72 | result = result + rightObjectUID; 73 | } else { 74 | result = result + rightObjectName; 75 | } 76 | 77 | return result; 78 | } 79 | 80 | public boolean hasLeftUID() { 81 | return leftObjectUID > 0; 82 | } 83 | 84 | public boolean hasRightUID() { 85 | return rightObjectUID > 0; 86 | } 87 | 88 | public String getLeftUID() { 89 | if (leftObjectUID > 0) { 90 | return String.valueOf(leftObjectUID); 91 | } else { 92 | return leftObjectName; 93 | } 94 | } 95 | 96 | public String getRightUID() { 97 | if (rightObjectUID > 0) { 98 | return String.valueOf(rightObjectUID); 99 | } else { 100 | return rightObjectName; 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/FBXProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects; 20 | 21 | import com.jumi.fbx.node.FBXNode; 22 | import java.io.ByteArrayOutputStream; 23 | import java.io.IOException; 24 | import java.nio.ByteBuffer; 25 | import java.nio.ByteOrder; 26 | import java.nio.DoubleBuffer; 27 | import java.nio.IntBuffer; 28 | import java.nio.LongBuffer; 29 | import java.util.zip.DataFormatException; 30 | import java.util.zip.Inflater; 31 | 32 | /** 33 | * FBXProperty 34 | * 35 | * A single property definition, belonging to a nested item or node 36 | * 37 | * @author Richard Greenlees 38 | */ 39 | public class FBXProperty { 40 | 41 | public char typeCode; 42 | public String dataType; 43 | public int dataLength; 44 | private byte[] binaryData; 45 | private int dataSizeInBytes; 46 | private int startPosition; 47 | 48 | public FBXProperty(byte[] inputData, int cursorPosition) { 49 | startPosition = cursorPosition; 50 | typeCode = (char) (inputData[startPosition] & 0xFF); 51 | 52 | // Every FBX Property starts with a char indicating what data type it is. Self-explanatory hopefully. 53 | switch (typeCode) { 54 | case 'Y': 55 | dataType = "Short"; 56 | dataSizeInBytes = 2; 57 | parseValue(inputData); 58 | break; 59 | case 'C': 60 | dataType = "Boolean"; 61 | dataSizeInBytes = 1; 62 | parseValue(inputData); 63 | break; 64 | case 'F': 65 | dataType = "Float"; 66 | dataSizeInBytes = 4; 67 | parseValue(inputData); 68 | break; 69 | case 'I': 70 | dataType = "Integer"; 71 | dataSizeInBytes = 4; 72 | parseValue(inputData); 73 | break; 74 | case 'D': 75 | dataType = "Double"; 76 | dataSizeInBytes = 8; 77 | parseValue(inputData); 78 | break; 79 | case 'L': 80 | dataType = "Long"; 81 | dataSizeInBytes = 8; 82 | parseValue(inputData); 83 | break; 84 | case 'f': 85 | case 'i': 86 | dataType = "Integer Array"; 87 | dataSizeInBytes = 4; 88 | parseArray(inputData); 89 | break; 90 | case 'd': 91 | dataType = "Double Array"; 92 | dataSizeInBytes = 8; 93 | parseArray(inputData); 94 | break; 95 | case 'l': 96 | dataType = "Long Array"; 97 | dataSizeInBytes = 8; 98 | parseArray(inputData); 99 | break; 100 | case 'b': 101 | dataType = "Boolean Array"; 102 | dataSizeInBytes = 1; 103 | parseArray(inputData); 104 | break; 105 | case 'S': 106 | dataType = "String"; 107 | parseBinary(inputData); 108 | break; 109 | case 'R': 110 | dataType = "Raw Binary Data"; 111 | parseBinary(inputData); 112 | break; 113 | default: 114 | System.err.println("Unknown property type! " + typeCode); 115 | System.exit(-1); 116 | break; 117 | } 118 | } 119 | 120 | /* Simply extract the binary data based on the data size */ 121 | private void parseValue(byte[] inputData) { 122 | binaryData = FBXNode.retrieveBytesFrom(inputData, dataSizeInBytes, startPosition + 1); 123 | dataLength = dataSizeInBytes + 1; 124 | } 125 | 126 | /* Slightly different, we need to determine the length of the binary data before we store it */ 127 | private void parseBinary(byte[] inputData) { 128 | byte[] binaryLength = FBXNode.retrieveBytesFrom(inputData, 4, startPosition + 1); 129 | int binarySize = ByteBuffer.wrap(binaryLength).order(ByteOrder.LITTLE_ENDIAN).getInt(); 130 | 131 | binaryData = FBXNode.retrieveBytesFrom(inputData, binarySize, startPosition + 5); 132 | 133 | dataLength = 5 + binarySize; 134 | } 135 | 136 | /* Slightly tricker. We need to determine the length of the data and whether it is compressed or not. Decompress if necessary */ 137 | private void parseArray(byte[] inputData) { 138 | byte[] arrayData = null; 139 | 140 | byte[] arrayLengthData = FBXNode.retrieveBytesFrom(inputData, 4, startPosition + 1); 141 | int arrayLength = ByteBuffer.wrap(arrayLengthData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 142 | 143 | byte[] encodingBytes = FBXNode.retrieveBytesFrom(inputData, 4, startPosition + 5); 144 | int encoding = ByteBuffer.wrap(encodingBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 145 | 146 | byte[] compressionSizeBytes = FBXNode.retrieveBytesFrom(inputData, 4, startPosition + 9); 147 | int compressedSize = ByteBuffer.wrap(compressionSizeBytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 148 | 149 | if (encoding == 1) { 150 | byte[] compressedData = FBXNode.retrieveBytesFrom(inputData, compressedSize, startPosition + 13); 151 | arrayData = decompressData(compressedData); 152 | 153 | } else { 154 | arrayData = FBXNode.retrieveBytesFrom(inputData, arrayLength * dataSizeInBytes, startPosition + 13); 155 | } 156 | 157 | binaryData = arrayData; 158 | 159 | if (encoding == 1) { 160 | dataLength = compressedSize + 13; 161 | } else { 162 | dataLength = (arrayLength * dataSizeInBytes) + 13; 163 | } 164 | } 165 | 166 | /* Inflates compressed binary data to give you the final binary data in an array */ 167 | private byte[] decompressData(byte[] compressedData) { 168 | 169 | Inflater decompressor = new Inflater(); 170 | decompressor.setInput(compressedData); 171 | 172 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(compressedData.length); 173 | byte[] buffer = new byte[1024]; 174 | while (!decompressor.finished()) { 175 | try { 176 | int count = decompressor.inflate(buffer); 177 | outputStream.write(buffer, 0, count); 178 | } catch (DataFormatException e) { 179 | e.printStackTrace(); 180 | } 181 | } 182 | try { 183 | outputStream.close(); 184 | } catch (IOException e) { 185 | e.printStackTrace(); 186 | } 187 | decompressor.end(); 188 | return outputStream.toByteArray(); 189 | 190 | } 191 | 192 | /** Express this property as an array of integers */ 193 | public int[] asIntArray() { 194 | IntBuffer intBuffer = ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer(); 195 | int[] result = new int[intBuffer.remaining()]; 196 | intBuffer.get(result); 197 | 198 | return result; 199 | } 200 | 201 | /** Express this property as an array of longs */ 202 | public long[] asLongArray() { 203 | LongBuffer longBuffer = ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).asLongBuffer(); 204 | long[] result = new long[longBuffer.remaining()]; 205 | longBuffer.get(result); 206 | 207 | return result; 208 | } 209 | 210 | /** Express this property as an array of doubles */ 211 | public double[] asDoubleArray() { 212 | DoubleBuffer doubleBuffer = ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer(); 213 | double[] result = new double[doubleBuffer.remaining()]; 214 | doubleBuffer.get(result); 215 | 216 | return result; 217 | } 218 | 219 | /** Express this property as an array of floats */ 220 | public float[] asFloatArray() { 221 | float[] result; 222 | DoubleBuffer doubleBuffer = ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).asDoubleBuffer(); 223 | double[] interim = new double[doubleBuffer.remaining()]; 224 | doubleBuffer.get(interim); 225 | 226 | result = new float[interim.length]; 227 | 228 | for (int i = 0; i < interim.length; i++) { 229 | result[i] = (float) interim[i]; 230 | } 231 | 232 | return result; 233 | } 234 | 235 | /** Express this property as a String */ 236 | public String asString() { 237 | return new String(binaryData); 238 | } 239 | 240 | /** Express this property as a short */ 241 | public short asShort() { 242 | return ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getShort(); 243 | } 244 | 245 | /** Express this property as a long */ 246 | public long asLong() { 247 | return ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getLong(); 248 | } 249 | 250 | /** Express this property as a double */ 251 | public double asDouble() { 252 | return ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getDouble(); 253 | } 254 | 255 | /** Express this property as a float */ 256 | public float asFloat() { 257 | if (dataType.equals("Float")) { 258 | return ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getFloat(); 259 | } else { 260 | return (float) ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getDouble(); 261 | } 262 | } 263 | 264 | /** Express this property as a boolean */ 265 | public boolean asBoolean() { 266 | return (binaryData[0] != 0); 267 | } 268 | 269 | /** Express this property as an array of booleans */ 270 | public boolean[] asBooleanArray() { 271 | boolean[] result = new boolean[binaryData.length]; 272 | for (int i = 0; i < binaryData.length; i++) { 273 | result[i] = (binaryData[i] != 0); 274 | } 275 | return result; 276 | } 277 | 278 | /** Express this property as raw binary */ 279 | public byte[] asByteArray() { 280 | return binaryData; 281 | } 282 | 283 | /** Express this property as an integer */ 284 | public int asInteger() { 285 | return ByteBuffer.wrap(binaryData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 286 | } 287 | 288 | public String toString() { 289 | String result = dataType + ": "; 290 | 291 | switch (typeCode) { 292 | case 'Y': 293 | result = result + asShort(); 294 | break; 295 | case 'C': 296 | result = result + asBoolean(); 297 | break; 298 | case 'I': 299 | result = result + asInteger(); 300 | break; 301 | case 'F': 302 | result = result + asFloat(); 303 | break; 304 | case 'D': 305 | result = result + asDouble(); 306 | break; 307 | case 'L': 308 | result = result + asLong(); 309 | break; 310 | case 'f': 311 | case 'i': 312 | result = result + "{ " + asIntArray()[0] + "...}"; 313 | break; 314 | case 'd': 315 | result = result + "{ " + asDoubleArray()[0] + "...}"; 316 | break; 317 | case 'l': 318 | result = result + "{ " + asLongArray()[0] + " ...}"; 319 | break; 320 | case 'b': 321 | result = result + "{ " + asBooleanArray()[0] + " ...}"; 322 | break; 323 | case 'S': 324 | result = result + asString(); 325 | break; 326 | case 'R': 327 | result = result + "{ BINARY ... (" + binaryData.length + " bytes) }"; 328 | break; 329 | default: 330 | result = result + "ERROR! Unknown type"; 331 | break; 332 | } 333 | return result; 334 | } 335 | 336 | } 337 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXAnimCurveDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | 23 | /** 24 | * FBXAnimCurveDefinition 25 | * 26 | * Container for FBX data related to an Animation Curve. Not currently used for anything. 27 | * 28 | * @author Richard Greenlees 29 | */ 30 | public class FBXAnimCurveDefinition extends FBXObjectDefinition { 31 | 32 | public static final long FBX_TC_MILLIS = 46186158L; 33 | 34 | long[] keyTime = new long[0]; 35 | int[] keyValueFloat = new int[0]; 36 | int[] keyAttrFlags = new int[0]; 37 | int[] keyAttrDataFloat = new int[0]; 38 | int[] keyAttrRefCount = new int[0]; 39 | 40 | public FBXAnimCurveDefinition(long UID, String inName) { 41 | super(UID, inName); 42 | } 43 | 44 | public FBXAnimCurveDefinition(long inUID) { 45 | super(inUID); 46 | } 47 | 48 | @Override 49 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 50 | if (nestedName.equals("KeyTime")) { 51 | keyTime = properties[0].asLongArray(); 52 | } else if (nestedName.equals("KeyValueFloat")) { 53 | keyValueFloat = properties[0].asIntArray(); 54 | } else if (nestedName.equals("KeyAttrFlags")) { 55 | keyAttrFlags = properties[0].asIntArray(); 56 | } else if (nestedName.equals("KeyAttrDataFloat")) { 57 | keyAttrDataFloat = properties[0].asIntArray(); 58 | } else if (nestedName.equals("KeyAttrRefCount")) { 59 | keyAttrRefCount = properties[0].asIntArray(); 60 | } 61 | } 62 | 63 | @Override 64 | public void readEmbeddedProperty(FBXProperty[] properties) { 65 | 66 | } 67 | 68 | 69 | @Override 70 | public void connect(FBXAnimCurveNodeDefinition inAnimCurveNode) { 71 | inAnimCurveNode.animationCurve = this; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXAnimCurveNodeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | 23 | /** 24 | * FBXAnimCurveNodeDefinition 25 | * 26 | * A container for storing key frame data. Not currently used for anything. 27 | * 28 | * @author Richard Greenlees 29 | */ 30 | public class FBXAnimCurveNodeDefinition extends FBXObjectDefinition { 31 | 32 | public float dX = 0.0f; 33 | public float dY = 0.0f; 34 | public float dZ = 0.0f; 35 | 36 | public FBXAnimCurveDefinition animationCurve; 37 | 38 | public FBXAnimCurveNodeDefinition(long UID, String inName) { 39 | super(UID, inName); 40 | } 41 | 42 | public FBXAnimCurveNodeDefinition(long inUID) { 43 | super(inUID); 44 | } 45 | 46 | @Override 47 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 48 | 49 | } 50 | 51 | @Override 52 | public void readEmbeddedProperty(FBXProperty[] properties) { 53 | String propertyName = properties[0].asString(); 54 | if (propertyName.equals("d|X")) { 55 | dX = properties[properties.length-1].asFloat(); 56 | } else if (propertyName.equals("d|Y")) { 57 | dY = properties[properties.length-1].asFloat(); 58 | } else if (propertyName.equals("d|Z")) { 59 | dZ = properties[properties.length-1].asFloat(); 60 | } 61 | } 62 | 63 | @Override 64 | public void connect(FBXAnimCurveDefinition inAnimCurve) { 65 | //.out.println("Joining this Animation Curve Node " + UID + " to Animation Curve " + inAnimCurve.name); 66 | animationCurve = inAnimCurve; 67 | } 68 | 69 | @Override 70 | public void connect(FBXAnimLayerDefinition inAnimLayer) { 71 | //System.out.println("Joining this Animation Curve Node " + UID + " to Animation Layer " + inAnimLayer.name); 72 | inAnimLayer.curveNodes.add(this); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXAnimLayerDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | import java.util.ArrayList; 23 | 24 | /** 25 | * FBXAnimLayerDefinition 26 | * 27 | * Container for holding information for a single animation. Not currently in use. 28 | * 29 | * @author Richard Greenlees 30 | */ 31 | public class FBXAnimLayerDefinition extends FBXObjectDefinition { 32 | 33 | public ArrayList curveNodes = new ArrayList(); 34 | 35 | public FBXAnimLayerDefinition(long UID, String inName) { 36 | super(UID, inName); 37 | } 38 | 39 | public FBXAnimLayerDefinition(long inUID) { 40 | super(inUID); 41 | } 42 | 43 | @Override 44 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 45 | 46 | } 47 | 48 | @Override 49 | public void readEmbeddedProperty(FBXProperty[] properties) { 50 | 51 | } 52 | 53 | @Override 54 | public void connect(FBXAnimCurveNodeDefinition inAnimCurveNode) { 55 | curveNodes.add(inAnimCurveNode); 56 | } 57 | 58 | @Override 59 | public void connect(FBXAnimStackDefinition inAnimStack) { 60 | inAnimStack.animationLayers.add(this); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXAnimStackDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | import java.util.ArrayList; 23 | 24 | /** 25 | * FBXAnimStackDefinition 26 | * 27 | * Container for holding information on an animation set. Not currently used for anything. 28 | * 29 | * @author Richard Greenlees 30 | */ 31 | public class FBXAnimStackDefinition extends FBXObjectDefinition { 32 | 33 | public ArrayList animationLayers = new ArrayList(); 34 | 35 | public FBXAnimStackDefinition(long UID, String inName) { 36 | super(UID, inName); 37 | } 38 | 39 | public FBXAnimStackDefinition(long inUID) { 40 | super(inUID); 41 | } 42 | 43 | @Override 44 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 45 | 46 | } 47 | 48 | @Override 49 | public void readEmbeddedProperty(FBXProperty[] properties) { 50 | 51 | } 52 | 53 | @Override 54 | public void connect(FBXAnimLayerDefinition inAnimLayer) { 55 | animationLayers.add(inAnimLayer); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXCameraDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | 23 | /** 24 | * FBXCameraDefinition 25 | * 26 | * Container for holding camera information. Not currently used for anything. 27 | * 28 | * @author RGreenlees 29 | */ 30 | public class FBXCameraDefinition extends FBXObjectDefinition { 31 | 32 | public FBXCameraDefinition(long inUID, String inName) { 33 | super(inUID, inName); 34 | } 35 | 36 | public FBXCameraDefinition(long inUID) { 37 | super(inUID); 38 | } 39 | 40 | @Override 41 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 42 | 43 | } 44 | 45 | @Override 46 | public void readEmbeddedProperty(FBXProperty[] properties) { 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXClusterDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.scene.objects.JUMISubDeformer; 22 | import com.jumi.fbx.objects.FBXProperty; 23 | 24 | /** 25 | * FBXClusterDefinition 26 | * 27 | * A container for holding information on the weights and indices for a bone. 28 | * 29 | * @author Richard Greenlees 30 | */ 31 | public class FBXClusterDefinition extends FBXObjectDefinition { 32 | 33 | public int[] indexes = new int[0]; 34 | public float[] weights = new float[0]; 35 | public float[] transforms = new float[0]; 36 | public float[] transformLinks = new float[0]; 37 | 38 | public FBXLimbNodeDefinition limbNode; 39 | 40 | public FBXClusterDefinition(long UID, String name) { 41 | super(UID, name); 42 | } 43 | 44 | public FBXClusterDefinition(long inUID) { 45 | super(inUID); 46 | } 47 | 48 | @Override 49 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 50 | if (nestedName.equals("Indexes") && properties.length > 0) { 51 | if (properties[0].typeCode == 'i' || properties[0].typeCode == 'f') { 52 | indexes = properties[0].asIntArray(); 53 | } else if (properties[0].typeCode == 'I' || properties[0].typeCode == 'F') { 54 | indexes = new int[properties.length]; 55 | for (int i = 0; i < properties.length; i++) { 56 | indexes[i] = properties[i].asInteger(); 57 | } 58 | } else { 59 | System.err.println("Invalid indices data type! Expected: integer or array of integers, actual: " + properties[0].dataType); 60 | } 61 | } else if (nestedName.equals("Weights") && properties.length > 0) { 62 | if (properties[0].typeCode == 'd') { 63 | weights = properties[0].asFloatArray(); 64 | } else if (properties[0].typeCode == 'D') { 65 | weights = new float[properties.length]; 66 | for (int i = 0; i < properties.length; i++) { 67 | weights[i] = (float) properties[i].asDouble(); 68 | } 69 | } else { 70 | System.err.println("Invalid UV data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 71 | } 72 | } else if (nestedName.equals("Transform") && properties.length > 0) { 73 | if (properties[0].typeCode == 'd') { 74 | transforms = properties[0].asFloatArray(); 75 | } else if (properties[0].typeCode == 'D') { 76 | transforms = new float[properties.length]; 77 | for (int i = 0; i < properties.length; i++) { 78 | transforms[i] = (float) properties[i].asDouble(); 79 | } 80 | } else { 81 | System.err.println("Invalid UV data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 82 | } 83 | } else if (nestedName.equals("TransformLink") && properties.length > 0) { 84 | if (properties[0].typeCode == 'd') { 85 | transformLinks = properties[0].asFloatArray(); 86 | } else if (properties[0].typeCode == 'D') { 87 | transformLinks = new float[properties.length]; 88 | for (int i = 0; i < properties.length; i++) { 89 | transformLinks[i] = (float) properties[i].asDouble(); 90 | } 91 | } else { 92 | System.err.println("Invalid UV data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 93 | } 94 | } 95 | } 96 | 97 | public JUMISubDeformer createSubDeformer() { 98 | JUMISubDeformer result = new JUMISubDeformer(name); 99 | 100 | result.indexes = indexes; 101 | result.weights = weights; 102 | result.transforms = transforms; 103 | result.transformLinks = transformLinks; 104 | 105 | return result; 106 | } 107 | 108 | @Override 109 | public void readEmbeddedProperty(FBXProperty[] properties) { 110 | 111 | } 112 | 113 | @Override 114 | public void connect(FBXLimbNodeDefinition inLimbNode) { 115 | limbNode = inLimbNode; 116 | limbNode.cluster = this; 117 | } 118 | 119 | @Override 120 | public void connect(FBXSkinDeformerDefinition inSkinDeformer) { 121 | inSkinDeformer.clusters.add(this); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXLimbNodeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.data.Vector3; 22 | import com.jumi.fbx.objects.FBXProperty; 23 | import com.jumi.scene.objects.JUMIBone; 24 | import java.util.ArrayList; 25 | 26 | /** 27 | * FBXLimbNodeDefinition 28 | * 29 | * Container for holding information on the translation and rotation of a bone 30 | * 31 | * @author Richard Greenlees 32 | */ 33 | public class FBXLimbNodeDefinition extends FBXObjectDefinition { 34 | 35 | public Vector3 localTranslation = new Vector3(0, 0, 0); 36 | public Vector3 localRotation = new Vector3(0, 0, 0); 37 | public Vector3 localScaling = new Vector3(0, 0, 0); 38 | 39 | public ArrayList children = new ArrayList(); 40 | public FBXLimbNodeDefinition parent = null; 41 | 42 | public FBXClusterDefinition cluster = null; 43 | 44 | public boolean isRoot = true; 45 | 46 | public FBXLimbNodeDefinition(long inUID, String inName) { 47 | super(inUID, inName); 48 | } 49 | 50 | public FBXLimbNodeDefinition(long inUID) { 51 | super(inUID); 52 | } 53 | 54 | public JUMIBone createSkeleton() { 55 | JUMIBone result = getRoot().createBone(); 56 | createBoneHierarchy(result); 57 | return result; 58 | } 59 | 60 | public void createBoneHierarchy(JUMIBone parent) { 61 | for (FBXLimbNodeDefinition child : children) { 62 | JUMIBone newBone = child.createBone(); 63 | newBone.setParent(parent); 64 | parent.addChild(newBone); 65 | child.createBoneHierarchy(newBone); 66 | } 67 | } 68 | 69 | public void createChildBones(JUMIBone parent) { 70 | for (FBXLimbNodeDefinition a : children) { 71 | parent.addChild(a.createBone()); 72 | } 73 | } 74 | 75 | public void createDescendantBones(JUMIBone parent) { 76 | 77 | } 78 | 79 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 80 | 81 | } 82 | 83 | public void setName(String newName) { 84 | super.setName(newName); 85 | } 86 | 87 | public void readEmbeddedProperty(FBXProperty[] properties) { 88 | String propertyName = properties[0].asString(); 89 | 90 | if (propertyName.equals("Lcl Translation")) { 91 | float x = properties[properties.length - 3].asFloat(); 92 | float y = properties[properties.length - 2].asFloat(); 93 | float z = properties[properties.length - 1].asFloat(); 94 | 95 | localTranslation.set(x, y, z); 96 | } else if (propertyName.equals("Lcl Rotation")) { 97 | float x = properties[properties.length - 3].asFloat(); 98 | float y = properties[properties.length - 2].asFloat(); 99 | float z = properties[properties.length - 1].asFloat(); 100 | 101 | localRotation.set(x, y, z); 102 | } else if (propertyName.equals("Lcl Scaling")) { 103 | float x = properties[properties.length - 3].asFloat(); 104 | float y = properties[properties.length - 2].asFloat(); 105 | float z = properties[properties.length - 1].asFloat(); 106 | 107 | localScaling.set(x, y, z); 108 | } 109 | } 110 | 111 | public JUMIBone createBone() { 112 | if (cluster != null) { 113 | return new JUMIBone(name, cluster.indexes, cluster.weights, cluster.transforms, cluster.transformLinks, localTranslation, localRotation, localScaling); 114 | } else { 115 | return new JUMIBone(name, localTranslation, localRotation, localScaling); 116 | } 117 | } 118 | 119 | @Override 120 | public void connect(FBXModelDefinition inModel) { 121 | //System.out.println("Joining this Limb Node " + name + " to Model " + inModel.name); 122 | inModel.rootNode = this; 123 | } 124 | 125 | public void connect(FBXLimbNodeDefinition inLimbNode) { 126 | //System.out.println("Joining this Limb Node " + name + " to Limb Node " + inLimbNode.name); 127 | inLimbNode.children.add(this); 128 | parent = inLimbNode; 129 | isRoot = false; 130 | } 131 | 132 | @Override 133 | public void connect(FBXTextureDefinition inTexture) { 134 | //System.out.println("Joining this Limb Node " + name + " to Texture " + inTexture.name); 135 | } 136 | 137 | @Override 138 | public void connect(FBXMaterialDefinition inMaterial) { 139 | //System.out.println("Joining this Limb Node " + name + " to Material " + inMaterial.name); 140 | } 141 | 142 | @Override 143 | public void connect(FBXMediaDefinition inMedia) { 144 | //System.out.println("Joining this Limb Node " + name + " to Model " + inMedia.name); 145 | } 146 | 147 | @Override 148 | public void connect(FBXSkinDeformerDefinition inSkinDeformer) { 149 | //System.out.println("Connecting this Limb Node " + name + " to Skin Deformer " + inSkinDeformer.name); 150 | } 151 | 152 | @Override 153 | public void connect(FBXCameraDefinition inCamera) { 154 | //System.out.println("Connecting this Limb Node " + name + " to Camera " + inCamera.name); 155 | } 156 | 157 | @Override 158 | public void connect(FBXShapeDefinition inShape) { 159 | //System.out.println("Connecting this Limb Node " + name + " to Shape Key " + inShape.name); 160 | } 161 | 162 | @Override 163 | public void connect(FBXClusterDefinition inCluster) { 164 | //System.out.println("Connecting this Limb Node " + name + " to Cluster " + inCluster.name); 165 | inCluster.limbNode = this; 166 | cluster = inCluster; 167 | } 168 | 169 | @Override 170 | public void connect(FBXAnimCurveDefinition inAnimCurve) { 171 | //System.out.println("Joining this Limb Node " + name + " to Animation Curve " + inAnimCurve.name); 172 | } 173 | 174 | @Override 175 | public void connect(FBXAnimCurveNodeDefinition inAnimCurveNode) { 176 | //System.out.println("Joining this Limb Node " + name + " to Animation Curve Node " + inAnimCurveNode.name); 177 | } 178 | 179 | @Override 180 | public void connect(FBXAnimLayerDefinition inAnimLayer) { 181 | //System.out.println("Joining this Limb Node " + name + " to Animation Layer " + inAnimLayer.name); 182 | } 183 | 184 | @Override 185 | public void connect(FBXAnimStackDefinition inAnimStack) { 186 | //System.out.println("Joining this Limb Node " + name + " to Animation Stack " + inAnimStack.name); 187 | } 188 | 189 | public String toString() { 190 | return "Limb Node: " + name + "\n\tTranslation: " + localTranslation + "\n\tRotation: " + localRotation + "\n\tScaling: " + localScaling; 191 | } 192 | 193 | public FBXLimbNodeDefinition getRoot() { 194 | if (isRoot || parent == null) { 195 | return this; 196 | } else { 197 | return parent.getRoot(); 198 | } 199 | } 200 | 201 | public void printSkeleton() { 202 | if (isRoot || parent == null) { 203 | printNodeRecursive(0); 204 | } else { 205 | getRoot().printNodeRecursive(0); 206 | } 207 | } 208 | 209 | public void printNodeRecursive(int level) { 210 | for (int i = 0; i < level; i++) { 211 | System.out.print(" "); 212 | } 213 | 214 | System.out.println("- " + name); 215 | 216 | for (FBXLimbNodeDefinition child : children) { 217 | child.printNodeRecursive(level + 1); 218 | } 219 | 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXMaterialDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.data.Color; 22 | import com.jumi.scene.objects.JUMIMaterial; 23 | import com.jumi.scene.objects.JUMITexture; 24 | import com.jumi.fbx.objects.FBXProperty; 25 | import java.util.ArrayList; 26 | 27 | /** 28 | * FBXMaterialDefinition 29 | * 30 | * Container for holding information on a material. 31 | * 32 | * @author Richard Greenlees 33 | */ 34 | public class FBXMaterialDefinition extends FBXObjectDefinition { 35 | 36 | public Color ambientColor = new Color(1.f, 1.f, 1.f); 37 | public Color diffuseColor = new Color(1.f, 1.f, 1.f); 38 | public Color specularColor = new Color(1.f, 1.f, 1.f); 39 | public Color emissiveColor = new Color(1.f, 1.f, 1.f); 40 | 41 | public float specularFactor = 1.0f; 42 | public float shininessExponent = 1.0f; 43 | public float transparencyFactor = 1.0f; 44 | public float emissiveFactor = 1.0f; 45 | public float reflectionFactor = 1.0f; 46 | public float shininess = 1.0f; 47 | public float reflectivity = 1.0f; 48 | public float opacity = 1.0f; 49 | 50 | public ArrayList textures = new ArrayList(); 51 | 52 | public FBXMaterialDefinition(long inUID, String inName) { 53 | super(inUID, inName); 54 | } 55 | 56 | public FBXMaterialDefinition(long inUID) { 57 | super(inUID); 58 | } 59 | 60 | @Override 61 | public void readEmbeddedProperty(FBXProperty[] properties) { 62 | String propertyName = properties[0].asString(); 63 | String propertyType = properties[1].asString(); 64 | 65 | switch (propertyType) { 66 | case "Color": 67 | case "ColorRGB": 68 | float r = properties[properties.length - 3].asFloat(); 69 | float g = properties[properties.length - 2].asFloat(); 70 | float b = properties[properties.length - 1].asFloat(); 71 | 72 | Color newColor = new Color(r, g, b); 73 | 74 | switch(propertyName) { 75 | case "Ambient": 76 | case "AmbientColor": 77 | ambientColor = newColor; break; 78 | case "Specular": 79 | case "SpecularColor": 80 | specularColor = newColor; break; 81 | case "Diffuse": 82 | case "DiffuseColor": 83 | diffuseColor = newColor; break; 84 | case "Emissive": 85 | case "EmissiveColor": 86 | emissiveColor = newColor; break; 87 | default: break; 88 | } 89 | break; 90 | case "TransparencyFactor": 91 | transparencyFactor = properties[properties.length - 1].asFloat(); break; 92 | case "SpecularFactor": 93 | specularFactor = properties[properties.length - 1].asFloat(); break; 94 | case "ReflectionFactor": 95 | reflectionFactor = properties[properties.length - 1].asFloat(); break; 96 | case "Shininess": 97 | shininess = properties[properties.length - 1].asFloat(); break; 98 | case "Opacity": 99 | opacity = properties[properties.length - 1].asFloat(); break; 100 | case "Reflectivity": 101 | reflectivity = properties[properties.length - 1].asFloat(); break; 102 | default: break; 103 | } 104 | } 105 | 106 | @Override 107 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 108 | 109 | } 110 | 111 | public JUMIMaterial createMaterial() { 112 | JUMIMaterial result = new JUMIMaterial(); 113 | 114 | result.name = name; 115 | result.ambientColor = ambientColor; 116 | result.diffuseColor = diffuseColor; 117 | result.emissiveColor = emissiveColor; 118 | result.emissiveFactor = emissiveFactor; 119 | result.opacity = opacity; 120 | result.reflectionFactor = reflectionFactor; 121 | result.reflectivity = reflectivity; 122 | result.shininess = shininess; 123 | result.shininessExponent = shininessExponent; 124 | result.specularColor = specularColor; 125 | result.specularFactor = specularFactor; 126 | result.transparencyFactor = transparencyFactor; 127 | 128 | result.textures = new JUMITexture[textures.size()]; 129 | 130 | for (int i = 0; i < textures.size(); i++) { 131 | result.textures[i] = textures.get(i).createTexture(); 132 | } 133 | return result; 134 | } 135 | 136 | @Override 137 | public void connect(FBXModelDefinition inModel) { 138 | inModel.addMaterialDefinition(this); 139 | } 140 | 141 | @Override 142 | public void connect(FBXTextureDefinition inTexture) { 143 | textures.add(inTexture); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXMediaDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | 23 | /** 24 | * FBXMediaDefinition 25 | * 26 | * Container for holding information on a texture file. Currently just used for embedded data. 27 | * 28 | * @author Richard Greenlees 29 | */ 30 | public class FBXMediaDefinition extends FBXObjectDefinition { 31 | 32 | byte[] content = new byte[0]; 33 | 34 | public FBXMediaDefinition(long inUID, String inName) { 35 | super(inUID, inName); 36 | } 37 | 38 | public FBXMediaDefinition(long inUID) { 39 | super(inUID); 40 | } 41 | 42 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 43 | if (nestedName.equals("Content")) { 44 | if (properties.length > 0) { 45 | content = properties[0].asByteArray(); 46 | } 47 | } 48 | } 49 | 50 | public void readEmbeddedProperty(FBXProperty[] properties) { 51 | 52 | } 53 | 54 | @Override 55 | public void connect(FBXTextureDefinition inTexture) { 56 | if (content != null && content.length > 0) { 57 | inTexture.textureData = content; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXModelDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.scene.objects.JUMIMaterial; 22 | import com.jumi.scene.objects.JUMIMesh; 23 | import com.jumi.scene.objects.JUMIMesh.FaceType; 24 | import com.jumi.scene.objects.JUMITexture; 25 | import com.jumi.fbx.objects.FBXProperty; 26 | import java.util.ArrayList; 27 | 28 | /** 29 | * FBXModelDefinition 30 | * 31 | * Container for model information. A model can either be a geometry definition, or contain other data like 32 | * local translation/rotation etc. Models can be joined in hierarchies, but the final JUMIMesh will flatten 33 | * it into a single JUMIMesh object, merging the various models together. 34 | * 35 | * @author Richard Greenlees 36 | */ 37 | public class FBXModelDefinition extends FBXObjectDefinition { 38 | 39 | public FaceType faceType = FaceType.NONE; 40 | 41 | public float[] vertices = new float[0]; 42 | public float[] normals = new float[0]; 43 | public float[] uvs = new float[0]; 44 | public int[] UVIndices = new int[0]; 45 | public int[] indices = new int[0]; 46 | public int[] edges = new int[0]; 47 | public float[] binormals = new float[0]; 48 | 49 | public boolean bDiscard; 50 | 51 | public ArrayList textures = new ArrayList(); 52 | public ArrayList materials = new ArrayList(); 53 | public ArrayList models = new ArrayList(); 54 | public FBXSkinDeformerDefinition deformer = null; 55 | 56 | public FBXLimbNodeDefinition rootNode = null; 57 | 58 | public FBXModelDefinition parent = null; 59 | 60 | public FBXModelDefinition(long inUID, String inName) { 61 | super(inUID, inName); 62 | } 63 | 64 | public FBXModelDefinition(long inUID) { 65 | super(inUID); 66 | } 67 | 68 | public boolean isRoot() { 69 | return (parent == null); 70 | } 71 | 72 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 73 | switch (nestedName) { 74 | // Vertex information 75 | case "Vertices": 76 | // Just to make life difficult, some models express vertices as a single array of doubles (correct), 77 | // and some express them as a load of individual double primitives (BAD!). Should provide support for both I guess 78 | if (properties[0].typeCode == 'd') { 79 | vertices = properties[0].asFloatArray(); 80 | } else if (properties[0].typeCode == 'D') { 81 | vertices = new float[properties.length]; 82 | for (int i = 0; i < properties.length; i++) { 83 | vertices[i] = (float) properties[i].asDouble(); 84 | } 85 | } else { 86 | // Vertices should be either a tonne of individual double values or an array of doubles. Anything else just ain't right 87 | System.err.println("Invalid vertex data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 88 | } break; 89 | case "Normals": 90 | // Same as vertices, normals can either be an array or lots of single doubles 91 | if (properties[0].typeCode == 'd') { 92 | normals = properties[0].asFloatArray(); 93 | } else if (properties[0].typeCode == 'D') { 94 | normals = new float[properties.length]; 95 | for (int i = 0; i < properties.length; i++) { 96 | normals[i] = (float) properties[i].asDouble(); 97 | } 98 | } else { 99 | System.err.println("Invalid normal data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 100 | } break; 101 | // Indices 102 | case "PolygonVertexIndex": 103 | // Same as vertices, indices can either be an array or lots of single integers 104 | if (properties[0].typeCode == 'i' || properties[0].typeCode == 'f') { 105 | indices = properties[0].asIntArray(); 106 | } else if (properties[0].typeCode == 'I' || properties[0].typeCode == 'F') { 107 | indices = new int[properties.length]; 108 | for (int i = 0; i < properties.length; i++) { 109 | indices[i] = properties[i].asInteger(); 110 | } 111 | } else { 112 | System.err.println("Invalid indices data type! Expected: integer or array of integers, actual: " + properties[0].dataType); 113 | } int faceSize = 0; 114 | for (int i = 0; i < indices.length; i++) { 115 | if (indices[i] < 0) { 116 | // FBX denotes the end of a single face with a negated index, so we need to restore it 117 | indices[i] = ~indices[i]; 118 | // If we don't know whether we're working with QUADS or TRIANGLES yet, we do now! 119 | if (faceSize == 0) { 120 | faceSize = i + 1; 121 | } 122 | } 123 | } if (faceSize == 3) { 124 | faceType = JUMIMesh.FaceType.TRIANGLES; 125 | } else if (faceSize == 4) { 126 | faceType = JUMIMesh.FaceType.QUADS; 127 | } break; 128 | // TODO: Add something in case we're dealing with polygons or some other rubbish 129 | case "UV": 130 | // See my comments for vertices/normals 131 | if (properties[0].typeCode == 'd') { 132 | uvs = properties[0].asFloatArray(); 133 | } else if (properties[0].typeCode == 'D') { 134 | uvs = new float[properties.length]; 135 | for (int i = 0; i < properties.length; i++) { 136 | uvs[i] = (float) properties[i].asDouble(); 137 | } 138 | } else { 139 | System.err.println("Invalid UV data type! Expected: double or array of doubles, actual: " + properties[0].dataType); 140 | } break; 141 | case "UVIndex": 142 | // See my comments for vertices/normals 143 | if (properties[0].typeCode == 'i' || properties[0].typeCode == 'f') { 144 | UVIndices = properties[0].asIntArray(); 145 | } else if (properties[0].typeCode == 'I' || properties[0].typeCode == 'F') { 146 | UVIndices = new int[properties.length]; 147 | for (int i = 0; i < properties.length; i++) { 148 | UVIndices[i] = properties[i].asInteger(); 149 | } 150 | } else { 151 | System.err.println("Invalid UV indices data type! Expected: integer or array of integers, actual: " + properties[0].dataType); 152 | } break; 153 | } 154 | } 155 | 156 | public void addChildModel(FBXModelDefinition newChild) { 157 | models.add(newChild); 158 | } 159 | 160 | @Override 161 | public void readEmbeddedProperty(FBXProperty[] properties) { 162 | 163 | } 164 | 165 | public boolean containsGeometryDefinition() { 166 | return findMeshData() != null; 167 | } 168 | 169 | public boolean hasGeometry() { 170 | return (vertices != null && vertices.length > 0); 171 | } 172 | 173 | public void addMaterialDefinition(FBXMaterialDefinition newMat) { 174 | materials.add(newMat); 175 | } 176 | 177 | public void connect(FBXModelDefinition inModel) { 178 | inModel.addChildModel(this); 179 | parent = inModel; 180 | } 181 | 182 | public void connect(FBXTextureDefinition inTexture) { 183 | textures.add(inTexture); 184 | } 185 | 186 | @Override 187 | public void connect(FBXMaterialDefinition inMaterial) { 188 | materials.add(inMaterial); 189 | } 190 | 191 | public void connect(FBXLimbNodeDefinition inLimbNode) { 192 | rootNode = inLimbNode; 193 | } 194 | 195 | @Override 196 | public void connect(FBXSkinDeformerDefinition inSkinDeformer) { 197 | deformer = inSkinDeformer; 198 | } 199 | 200 | /* UVs are expressed as indices referencing the doubles we extracted. Why make things simple when they can be needlessly complex? */ 201 | private float[] generateUVs() { 202 | if (UVIndices == null || UVIndices.length == 0) { 203 | return uvs; 204 | } else { 205 | float[] uvResult = new float[UVIndices.length * 2]; 206 | 207 | for (int i = 0; i < indices.length; i++) { 208 | uvResult[indices[i] * 2] = uvs[UVIndices[i] * 2]; 209 | uvResult[indices[i] * 2 + 1] = uvs[UVIndices[i] * 2 + 1]; 210 | } 211 | 212 | return uvResult; 213 | } 214 | } 215 | 216 | /** Check this model and its children to find where we're keeping our geometry data */ 217 | public FBXModelDefinition findMeshData() { 218 | FBXModelDefinition result = null; 219 | if (this.hasGeometry()) { 220 | result = this; 221 | } else { 222 | for (FBXModelDefinition a : models) { 223 | result = a.findMeshData(); 224 | if (result != null) { 225 | break; 226 | } 227 | } 228 | } 229 | return result; 230 | } 231 | 232 | /** Do we have a root bone and if so, what is it? */ 233 | public FBXLimbNodeDefinition findRootNode() { 234 | FBXLimbNodeDefinition result = null; 235 | if (rootNode != null) { 236 | return rootNode; 237 | } else { 238 | for (FBXModelDefinition a : models) { 239 | if (a.deformer != null) { 240 | return a.deformer.getRootNode(); 241 | } 242 | } 243 | } 244 | return result; 245 | } 246 | 247 | public void buildMaterialList(ArrayList list) { 248 | for (FBXMaterialDefinition a : materials) { 249 | list.add(a); 250 | } 251 | 252 | for (FBXModelDefinition a : models) { 253 | a.buildMaterialList(list); 254 | } 255 | } 256 | 257 | public void buildTextureList(ArrayList list) { 258 | for (FBXTextureDefinition a : textures) { 259 | list.add(a); 260 | } 261 | 262 | for (FBXModelDefinition a : models) { 263 | a.buildTextureList(list); 264 | } 265 | } 266 | 267 | /** Generate a JUMIMesh using this model definition */ 268 | public JUMIMesh createMesh() { 269 | JUMIMesh result = new JUMIMesh(name); 270 | 271 | FBXModelDefinition meshData = findMeshData(); 272 | FBXLimbNodeDefinition rootNode = findRootNode(); 273 | 274 | result.vertices = meshData.vertices; 275 | result.normals = meshData.normals; 276 | result.binormals = meshData.binormals; 277 | result.indices = meshData.indices; 278 | result.uvs = meshData.generateUVs(); 279 | result.edges = meshData.edges; 280 | result.faceType = meshData.faceType; 281 | 282 | ArrayList textureList = new ArrayList(); 283 | buildTextureList(textureList); 284 | result.textures = new JUMITexture[textureList.size()]; 285 | 286 | for (int i = 0; i < textures.size(); i++) { 287 | result.textures[i] = textureList.get(i).createTexture(); 288 | } 289 | 290 | ArrayList materialList = new ArrayList(); 291 | buildMaterialList(materialList); 292 | 293 | result.materials = new JUMIMaterial[materialList.size()]; 294 | 295 | for (int i = 0; i < materialList.size(); i++) { 296 | 297 | result.materials[i] = materialList.get(i).createMaterial(); 298 | } 299 | 300 | if (rootNode != null) { 301 | result.rootBone = rootNode.createSkeleton(); 302 | } 303 | 304 | 305 | return result; 306 | } 307 | 308 | } 309 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXObjectDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | import com.jumi.fbx.node.FBXNode; 23 | import java.nio.ByteBuffer; 24 | import java.nio.ByteOrder; 25 | 26 | /** 27 | * FBXObjectDefinition 28 | * 29 | * Base class for all object definitions. Contains hooks for connecting them to each other and for parsing data. 30 | * Please note this is still in a rough shape, but it seems to work which is always useful. 31 | * 32 | * @author Richard Greenlees 33 | */ 34 | public abstract class FBXObjectDefinition { 35 | public long UID; 36 | public String name; 37 | public int endOffset; 38 | 39 | public FBXObjectDefinition(long inUID, String inName) { 40 | UID = inUID; 41 | if (inName.equals("")) { 42 | name = "NULL"; 43 | } else { 44 | name = inName; 45 | } 46 | } 47 | 48 | public FBXObjectDefinition(long inUID) { 49 | UID = inUID; 50 | name = "NULL"; 51 | } 52 | 53 | public void setName(String newName) { 54 | name = newName; 55 | } 56 | 57 | public final void parseData(byte[] inputData, int startPosition) { 58 | endOffset = startPosition; 59 | 60 | byte scope = 1; 61 | 62 | while (scope > 0) { 63 | int nestedNameLength = inputData[endOffset + 12]; 64 | byte[] nestedNameData = FBXNode.retrieveBytesFrom(inputData, nestedNameLength, endOffset + 13); 65 | String nestedName = new String(nestedNameData); 66 | 67 | byte[] numPropertiesData = FBXNode.retrieveBytesFrom(inputData, 4, endOffset + 4); 68 | 69 | int numProperties = ByteBuffer.wrap(numPropertiesData).order(ByteOrder.LITTLE_ENDIAN).getInt(); 70 | 71 | endOffset += nestedNameLength + 13; 72 | 73 | if (nestedName.equals("")) { 74 | scope--; 75 | if (scope <= 0) { 76 | break; 77 | } 78 | } else { 79 | 80 | // TODO: Figure out a better way of deciding which nested nodes contain MORE nested nodes. 81 | // Tracking the size of each nested element vs. its properties doesn't seem to be reliable 82 | if (nestedName.equals("Properties60") 83 | || nestedName.equals("Properties70") 84 | || nestedName.equals("LayerElementUV") 85 | || nestedName.equals("LayerElementMaterial") 86 | || nestedName.equals("LayerElementTexture") 87 | || nestedName.equals("Layer") 88 | || nestedName.equals("LayerElement") 89 | || nestedName.equals("LayerElementBinormal") 90 | || nestedName.equals("LayerElementNormal") 91 | || nestedName.equals("LayerElementTangent") 92 | || nestedName.equals("LayerElementSmoothing") 93 | || nestedName.equals("LayerElementColor") 94 | || nestedName.equals("LayerElementVisibility")) { 95 | scope++; 96 | } 97 | 98 | } 99 | 100 | FBXProperty[] properties = new FBXProperty[numProperties]; 101 | 102 | // Get all the properties this nested item contains 103 | for (int i = 0; i < properties.length; i++) { 104 | FBXProperty newProp = new FBXProperty(inputData, endOffset); 105 | endOffset += newProp.dataLength; 106 | properties[i] = newProp; 107 | } 108 | 109 | // "P" or "Property" means we're inside a Properties60 or Properties70 node, so it's an embedded property 110 | if (nestedName.equals("P") || nestedName.equals("Property")) { 111 | readEmbeddedProperty(properties); 112 | } else { 113 | readNestedObject(nestedName, properties); 114 | } 115 | 116 | } 117 | } 118 | 119 | /** Called every time a new nested node is parsed */ 120 | protected abstract void readNestedObject(String nestedName, FBXProperty[] properties); 121 | 122 | /** Called whenever an embedded property (via Properties60 or Properties70) is encountered */ 123 | protected abstract void readEmbeddedProperty(FBXProperty[] properties); 124 | 125 | public void connect(FBXModelDefinition inModel) { 126 | 127 | } 128 | 129 | public void connect(FBXTextureDefinition inTexture) { 130 | 131 | } 132 | 133 | public void connect(FBXMaterialDefinition inMaterial) { 134 | 135 | } 136 | 137 | public void connect(FBXMediaDefinition inMedia) { 138 | 139 | } 140 | 141 | public void connect(FBXLimbNodeDefinition inLimbNode) { 142 | 143 | } 144 | 145 | public void connect(FBXSkinDeformerDefinition inSkinDeformer) { 146 | 147 | } 148 | 149 | public void connect(FBXCameraDefinition inCamera) { 150 | 151 | } 152 | 153 | public void connect(FBXShapeDefinition inShape) { 154 | 155 | } 156 | 157 | public void connect(FBXClusterDefinition inCluster) { 158 | 159 | } 160 | 161 | public void connect(FBXAnimCurveDefinition inAnimCurve) { 162 | 163 | } 164 | 165 | public void connect(FBXAnimCurveNodeDefinition inAnimCurveNode) { 166 | 167 | } 168 | 169 | public void connect(FBXAnimLayerDefinition inAnimLayer) { 170 | 171 | } 172 | 173 | public void connect(FBXAnimStackDefinition inAnimStack) { 174 | 175 | } 176 | 177 | // TODO: Sort this mess out. Quick hack until I'm happy with how everything connects up and put a proper process together 178 | public void connect(FBXObjectDefinition inObject) { 179 | if (inObject instanceof FBXModelDefinition) { 180 | connect((FBXModelDefinition) inObject); 181 | } else if (inObject instanceof FBXTextureDefinition) { 182 | connect((FBXTextureDefinition) inObject); 183 | } else if (inObject instanceof FBXMediaDefinition) { 184 | connect((FBXMediaDefinition) inObject); 185 | } else if (inObject instanceof FBXMaterialDefinition) { 186 | connect((FBXMaterialDefinition) inObject); 187 | } else if (inObject instanceof FBXLimbNodeDefinition) { 188 | connect((FBXLimbNodeDefinition) inObject); 189 | } else if (inObject instanceof FBXSkinDeformerDefinition) { 190 | connect((FBXSkinDeformerDefinition) inObject); 191 | } else if (inObject instanceof FBXCameraDefinition) { 192 | connect((FBXCameraDefinition) inObject); 193 | } else if (inObject instanceof FBXShapeDefinition) { 194 | connect((FBXShapeDefinition) inObject); 195 | } else if (inObject instanceof FBXClusterDefinition) { 196 | connect((FBXClusterDefinition) inObject); 197 | } else if (inObject instanceof FBXAnimCurveDefinition) { 198 | connect((FBXAnimCurveDefinition) inObject); 199 | } else if (inObject instanceof FBXAnimCurveNodeDefinition) { 200 | connect((FBXAnimCurveNodeDefinition) inObject); 201 | } else if (inObject instanceof FBXAnimLayerDefinition) { 202 | connect((FBXAnimLayerDefinition) inObject); 203 | } else if (inObject instanceof FBXAnimStackDefinition) { 204 | connect((FBXAnimStackDefinition) inObject); 205 | } 206 | } 207 | 208 | public final boolean hasUID() { 209 | return UID > 0; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXShapeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.fbx.objects.FBXProperty; 22 | 23 | /** 24 | * FBXShapeDefinition 25 | * 26 | * Container for holding information on a morph target. Not currently in use. 27 | * 28 | * @author RGreenlees 29 | */ 30 | public class FBXShapeDefinition extends FBXObjectDefinition { 31 | 32 | public FBXShapeDefinition(long inUID, String inName) { 33 | super(inUID, inName); 34 | } 35 | 36 | public FBXShapeDefinition(long inUID) { 37 | super(inUID); 38 | } 39 | 40 | @Override 41 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 42 | 43 | } 44 | 45 | @Override 46 | public void readEmbeddedProperty(FBXProperty[] properties) { 47 | 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXSkinDeformerDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.scene.objects.JUMISkinDeformer; 22 | import com.jumi.scene.objects.JUMISubDeformer; 23 | import com.jumi.fbx.objects.FBXProperty; 24 | import java.util.ArrayList; 25 | 26 | /** 27 | * FBXSkinDeformerDefinition 28 | * 29 | * Container for holding information on a model's skin. 30 | * 31 | * @author Richard Greenlees 32 | */ 33 | public class FBXSkinDeformerDefinition extends FBXObjectDefinition { 34 | 35 | public float linkDeformAccuracy = 0.f; 36 | 37 | public ArrayList clusters = new ArrayList(); 38 | 39 | public FBXSkinDeformerDefinition(long inUID, String inName) { 40 | super(inUID, inName); 41 | } 42 | 43 | public FBXSkinDeformerDefinition(long inUID) { 44 | super(inUID); 45 | } 46 | 47 | @Override 48 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 49 | if (nestedName.equals("Link_DeformAcuracy")) { 50 | linkDeformAccuracy = properties[0].asLong(); 51 | } 52 | } 53 | 54 | @Override 55 | public void readEmbeddedProperty(FBXProperty[] properties) { 56 | 57 | } 58 | 59 | public FBXLimbNodeDefinition getRootNode() { 60 | for (FBXClusterDefinition a : clusters) { 61 | if (a.limbNode != null) { 62 | return a.limbNode.getRoot(); 63 | } 64 | } 65 | return null; 66 | } 67 | 68 | public JUMISkinDeformer createSkinDeformer() { 69 | JUMISkinDeformer result = new JUMISkinDeformer(name); 70 | 71 | result.deformers = new JUMISubDeformer[clusters.size()]; 72 | 73 | for (int i = 0; i < clusters.size(); i++) { 74 | result.deformers[i] = clusters.get(i).createSubDeformer(); 75 | } 76 | 77 | return result; 78 | } 79 | 80 | @Override 81 | public void connect(FBXModelDefinition inModel) { 82 | inModel.deformer = this; 83 | } 84 | 85 | @Override 86 | public void connect(FBXClusterDefinition inCluster) { 87 | clusters.add(inCluster); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/com/jumi/fbx/objects/definitions/FBXTextureDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.fbx.objects.definitions; 20 | 21 | import com.jumi.scene.objects.JUMITexture; 22 | import com.jumi.fbx.objects.FBXProperty; 23 | 24 | /** 25 | * FBXTextureDefinition 26 | * 27 | * Container for holding information on a texture definition. 28 | * 29 | * @author Richard Greenlees 30 | */ 31 | public class FBXTextureDefinition extends FBXObjectDefinition { 32 | 33 | public String textureName = ""; 34 | public String fileLocation = ""; 35 | 36 | public String mediaType = ""; 37 | public String fileName = ""; 38 | public String relativeFilename = ""; 39 | 40 | public byte[] textureData = new byte[0]; 41 | 42 | public FBXObjectDefinition parent = null; 43 | 44 | public FBXTextureDefinition(long inUID, String inName) { 45 | super(inUID, inName); 46 | } 47 | 48 | public FBXTextureDefinition(long inUID) { 49 | super(inUID); 50 | } 51 | 52 | public void readNestedObject(String nestedName, FBXProperty[] properties) { 53 | if (nestedName.equals("Type")) { 54 | mediaType = properties[0].asString(); 55 | } else if (nestedName.equals("TextureName")) { 56 | textureName = properties[0].asString().substring(0, properties[0].asString().indexOf('\0')); 57 | } else if (nestedName.equals("FileName")) { 58 | fileName = properties[0].asString(); 59 | } else if (nestedName.equals("RelativeFilename")) { 60 | relativeFilename = properties[0].asString(); 61 | } 62 | } 63 | 64 | public void readEmbeddedProperty(FBXProperty[] properties) { 65 | 66 | } 67 | 68 | public JUMITexture createTexture() { 69 | JUMITexture result = new JUMITexture(); 70 | result.name = name; 71 | result.fullFilePath = fileName; 72 | result.relativeFilename = relativeFilename; 73 | result.fileName = fileName.substring(fileName.lastIndexOf("\\") + 1, fileName.length()); 74 | result.setTextureData(textureData); 75 | return result; 76 | } 77 | 78 | @Override 79 | public void connect(FBXModelDefinition inModel) { 80 | inModel.textures.add(this); 81 | parent = inModel; 82 | } 83 | 84 | @Override 85 | public void connect(FBXTextureDefinition inTexture) { 86 | parent = inTexture; 87 | } 88 | 89 | public void connect(FBXLimbNodeDefinition inLimbNode) { 90 | parent = inLimbNode; 91 | } 92 | 93 | @Override 94 | public void connect(FBXMaterialDefinition inMaterial) { 95 | inMaterial.textures.add(this); 96 | parent = inMaterial; 97 | } 98 | 99 | @Override 100 | public void connect(FBXMediaDefinition inMedia) { 101 | if (inMedia.content != null && inMedia.content.length > 0) { 102 | textureData = inMedia.content; 103 | } 104 | parent = inMedia; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/com/jumi/obj/OBJLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.obj; 20 | 21 | import com.jumi.JUMILoader; 22 | import com.jumi.data.Vector2; 23 | import com.jumi.data.Vector3; 24 | import com.jumi.obj.objects.definitions.OBJMatLibDefinition; 25 | import com.jumi.obj.objects.definitions.OBJModelDefinition; 26 | import com.jumi.scene.JUMIScene; 27 | import com.jumi.scene.objects.JUMIMesh; 28 | import java.io.BufferedReader; 29 | import java.io.FileNotFoundException; 30 | import java.io.FileReader; 31 | import java.io.IOException; 32 | import java.util.ArrayList; 33 | 34 | /** 35 | * 36 | * @author RGreenlees 37 | */ 38 | public class OBJLoader extends JUMILoader { 39 | 40 | public static JUMIScene importModel(String fileName) throws IOException { 41 | ArrayList vertices = new ArrayList(); 42 | ArrayList normals = new ArrayList(); 43 | ArrayList uvs = new ArrayList(); 44 | ArrayList modelContexts = new ArrayList(); 45 | 46 | OBJMatLibDefinition materialLibrary = null; 47 | 48 | BufferedReader reader = null; 49 | 50 | OBJModelDefinition defaultModel = new OBJModelDefinition("default"); 51 | modelContexts.add(defaultModel); 52 | OBJModelDefinition currentModelContext = defaultModel; 53 | 54 | try { 55 | reader = new BufferedReader(new FileReader(fileName)); 56 | String line; 57 | 58 | while ((line = reader.readLine()) != null) { 59 | // Split up the string by space or tab 60 | String[] lineTokens = line.split("\\s+"); 61 | 62 | // Obviously ignore blank lines 63 | if (lineTokens.length == 0) { 64 | continue; 65 | } 66 | 67 | // Make sure we remove leading/trailing white space! 68 | switch (lineTokens[0].trim()) { 69 | case "mtllib": // Define our material library 70 | // Some OBJ files like to include hard-coded paths, let's get rid of that and get just the name of the file 71 | String libName = line.substring(line.indexOf(" ") + 1, line.length()).replace("\\", "/"); 72 | 73 | if (libName.contains("/")) { 74 | libName = libName.substring(libName.lastIndexOf("/") + 1, libName.length()); 75 | } 76 | 77 | // Look for our MTL file in the same folder as the OBJ file 78 | String libLocation = fileName.substring(0, fileName.lastIndexOf("/") + 1) + libName; 79 | try { 80 | // Create the material library and parse the MTL file separately 81 | materialLibrary = new OBJMatLibDefinition(); 82 | materialLibrary.parseMTL(libLocation); 83 | } catch (FileNotFoundException e) { 84 | System.err.println("WARNING: Could not find requested material file " + libLocation); 85 | } catch (IOException e) { 86 | System.out.println("WARNING: Error while parsing requested material file " + libLocation); 87 | } 88 | break; 89 | case "V": 90 | case "v": 91 | // OBJ indices refer to the vertices/UVs/normals as groups of 3, so let's do the same 92 | vertices.add(new Vector3(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]), Float.valueOf(lineTokens[3]))); 93 | break; 94 | case "VN": 95 | case "vn": 96 | // OBJ indices refer to the vertices/UVs/normals as groups of 3, so let's do the same 97 | normals.add(new Vector3(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]), Float.valueOf(lineTokens[3]))); 98 | break; 99 | case "VT": 100 | case "vt": 101 | // OBJ indices refer to the vertices/UVs/normals as groups of 3, so let's do the same 102 | uvs.add(new Vector2(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]))); 103 | break; 104 | case "F": 105 | case "f": 106 | // Only allow support for Triangles or Quads for now 107 | if (lineTokens.length > 5 || lineTokens.length < 4) { 108 | System.err.println("Invalid face definition! Expected 3 (Triangles) or 4 (Quads), actual = " + (lineTokens.length - 1)); 109 | } else { 110 | // Some OBJ models use relative positions (negative indices). The aim here is to convert them to regular face definitions 111 | // First check if we have a negative index 112 | if (lineTokens[1].charAt(0) == '-') { 113 | for (int i = 1; i < lineTokens.length; i++) { 114 | // Split the face definition, we're going to redefine each index 115 | String[] lineToken = lineTokens[i].split("/"); 116 | 117 | // Check that model has UVs and normals defined before converting them 118 | int vIndex = Integer.valueOf(lineToken[0]); 119 | int vtIndex = (lineToken.length > 1 && !lineToken[1].equals("")) ? Integer.valueOf(lineToken[1]) : 0; 120 | int nIndex = (lineToken.length > 2 && !lineToken[2].equals("")) ? Integer.valueOf(lineToken[2]) : 0; 121 | 122 | // Convert each index as the current number of definitions - index. We use + as the index is negative 123 | // Also add +1 as OBJ definitions go from 1...n while ArrayLists go 0...n 124 | vIndex = vertices.size() + vIndex + 1; 125 | vtIndex = (vtIndex < 0) ? (uvs.size() + vtIndex + 1) : 0; 126 | nIndex = (nIndex < 0) ? (normals.size() + nIndex + 1) : 0; 127 | 128 | // Reconstruct the face definition so it can be processed as normal when constructing the mesh 129 | lineTokens[i] = vIndex + "/" + ((vtIndex > 0) ? vtIndex : "") + "/" + ((nIndex > 0) ? nIndex : ""); 130 | } 131 | } 132 | 133 | // If we're dealing with a quad, triangulate it. Some models use a mix of triangles and quads, and it's easier 134 | // to just triangulate everything rather than try and switch back and forth 135 | if (lineTokens.length == 5) { 136 | 137 | currentModelContext.uniqueIndexDefinitions.add(lineTokens[1]); 138 | currentModelContext.uniqueIndexDefinitions.add(lineTokens[2]); 139 | currentModelContext.uniqueIndexDefinitions.add(lineTokens[3]); 140 | currentModelContext.uniqueIndexDefinitions.add(lineTokens[4]); 141 | 142 | currentModelContext.allIndexDefinitions.add(lineTokens[1]); 143 | currentModelContext.allIndexDefinitions.add(lineTokens[2]); 144 | currentModelContext.allIndexDefinitions.add(lineTokens[3]); 145 | currentModelContext.allIndexDefinitions.add(lineTokens[3]); 146 | currentModelContext.allIndexDefinitions.add(lineTokens[4]); 147 | currentModelContext.allIndexDefinitions.add(lineTokens[1]); 148 | } else { 149 | // Keep a separate list of unique index definitions and the actual defined indices. These will be used later 150 | // to create a final indices array 151 | for (int i = 1; i < lineTokens.length; i++) { 152 | currentModelContext.uniqueIndexDefinitions.add(lineTokens[i]); 153 | currentModelContext.allIndexDefinitions.add(lineTokens[i]); 154 | } 155 | } 156 | } 157 | 158 | break; 159 | case "usemtl": // Use a material from the defined library 160 | // Only do stuff if we've already had a material library defined (via mtllib) 161 | if (materialLibrary != null && lineTokens.length > 1) { 162 | currentModelContext.materialDefinitions.add(materialLibrary.getMaterialDefinition(lineTokens[1])); 163 | } 164 | break; 165 | case "O": 166 | case "o": 167 | case "G": 168 | case "g": 169 | // If the group or object has no name defined, give it a default one 170 | String modelContextName = "default"; 171 | if (lineTokens.length > 1) { 172 | modelContextName = lineTokens[1]; 173 | } 174 | 175 | // First check if we already have a model with that name defined, and use that instead 176 | OBJModelDefinition newContext = null; 177 | 178 | for (OBJModelDefinition a : modelContexts) { 179 | if (a.name.equals(modelContextName)) { 180 | newContext = a; 181 | break; 182 | } 183 | } 184 | 185 | // Otherwise, create a new model and use it 186 | if (newContext == null) { 187 | newContext = new OBJModelDefinition(modelContextName); 188 | modelContexts.add(newContext); 189 | } 190 | 191 | currentModelContext = newContext; 192 | 193 | break; 194 | 195 | } 196 | } 197 | } catch (IOException e) { 198 | e.printStackTrace(); 199 | return null; 200 | } finally { 201 | if (reader != null) { 202 | // Don't forget to close the reader! 203 | reader.close(); 204 | } 205 | } 206 | 207 | ArrayList allMeshes = new ArrayList(); 208 | 209 | JUMIScene result = new JUMIScene(); 210 | 211 | // Gather all the models we have defined and turn them into JUMI Meshes for inclusion in the scene 212 | for (OBJModelDefinition a : modelContexts) { 213 | if (a.allIndexDefinitions.size() > 0) { 214 | allMeshes.add(a.createMesh(vertices, normals, uvs)); 215 | } 216 | } 217 | 218 | 219 | result.addMeshes(allMeshes); 220 | 221 | return result; 222 | 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/com/jumi/obj/objects/definitions/OBJMatLibDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.obj.objects.definitions; 20 | 21 | import com.jumi.data.Color; 22 | import java.io.BufferedReader; 23 | import java.io.FileReader; 24 | import java.io.IOException; 25 | import java.util.ArrayList; 26 | 27 | /** 28 | * 29 | * @author RGreenlees 30 | */ 31 | public class OBJMatLibDefinition { 32 | 33 | public ArrayList materials = new ArrayList(); 34 | private OBJMaterialDefinition currentMaterial = null; 35 | 36 | public OBJMatLibDefinition() { 37 | super(); 38 | } 39 | 40 | // Parses the supplied MTL 41 | public void parseMTL(String mtlLocation) throws IOException { 42 | BufferedReader reader = new BufferedReader(new FileReader(mtlLocation)); 43 | 44 | String line; 45 | 46 | while ((line = reader.readLine()) != null) { 47 | String[] lineTokens = line.split(" "); 48 | 49 | if (lineTokens.length == 0) { 50 | continue; 51 | } 52 | 53 | // Get rid of those whitespaces! 54 | switch (lineTokens[0].trim()) { 55 | // New material definition, add it to the list and set it to be the current material 56 | case "newmtl": 57 | if (lineTokens.length > 1) { 58 | OBJMaterialDefinition newMat = new OBJMaterialDefinition(lineTokens[1]); 59 | materials.add(newMat); 60 | currentMaterial = newMat; 61 | } 62 | break; 63 | // Various attributes to be applied to whichever material is current 64 | case "Ka": currentMaterial.ambientColour = new Color(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]), Float.valueOf(lineTokens[3])); break; 65 | case "Kd": currentMaterial.diffuseColour = new Color(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]), Float.valueOf(lineTokens[3])); break; 66 | case "Ks": currentMaterial.specularColour = new Color(Float.valueOf(lineTokens[1]), Float.valueOf(lineTokens[2]), Float.valueOf(lineTokens[3])); break; 67 | case "Ns": currentMaterial.specularWeight = Float.valueOf(lineTokens[1]); break; 68 | case "d": 69 | case "Tr": currentMaterial.alpha = Float.valueOf(lineTokens[1]); break; 70 | case "illum": currentMaterial.illum = Integer.valueOf(lineTokens[1]); break; 71 | // Various specific textures like diffuse, specular etc. Take the whole line incase the filename contains spaces 72 | case "map_Ka": 73 | currentMaterial.alphaMap = line.substring(line.indexOf(" ") + 1, line.length()); 74 | case "map_Kd": 75 | currentMaterial.diffuseMap = line.substring(line.indexOf(" ") + 1, line.length()); 76 | break; 77 | case "map_Ks": 78 | currentMaterial.specularMap = line.substring(line.indexOf(" ") + 1, line.length()); 79 | break; 80 | case "map_Ns": 81 | currentMaterial.specularHighlightMap = line.substring(line.indexOf(" ") + 1, line.length()); 82 | break; 83 | case "bump": 84 | case "map_bump": 85 | currentMaterial.bumpMap = line.substring(line.indexOf(" ") + 1, line.length()); 86 | break; 87 | case "disp": 88 | case "map_disp": 89 | currentMaterial.displaceMap = line.substring(line.indexOf(" ") + 1, line.length()); 90 | break; 91 | case "decal": 92 | case "map_decal": 93 | currentMaterial.decalMap = line.substring(line.indexOf(" ") + 1, line.length()); 94 | break; 95 | default: break; 96 | } 97 | } 98 | } 99 | 100 | public OBJMaterialDefinition getMaterialDefinition(String matName) { 101 | for (OBJMaterialDefinition a : materials) { 102 | if (a.name.equals(matName)) { 103 | return a; 104 | } 105 | } 106 | 107 | return null; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/com/jumi/obj/objects/definitions/OBJMaterialDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.obj.objects.definitions; 20 | 21 | import com.jumi.data.Color; 22 | 23 | /** 24 | * 25 | * @author RGreenlees 26 | */ 27 | public class OBJMaterialDefinition { 28 | 29 | public Color ambientColour = new Color(); 30 | public Color diffuseColour = new Color(); 31 | public Color specularColour = new Color(); 32 | 33 | public float specularWeight = 0.0f; 34 | public float alpha = 1.0f; 35 | 36 | public int illum = 0; 37 | 38 | public String name; 39 | public String ambientMap = ""; 40 | public String diffuseMap = ""; 41 | public String alphaMap = ""; 42 | public String bumpMap = ""; 43 | public String displaceMap = ""; 44 | public String decalMap = ""; 45 | public String specularMap = ""; 46 | public String specularHighlightMap = ""; 47 | 48 | public OBJMaterialDefinition(String inName) { 49 | super(); 50 | name = inName; 51 | } 52 | 53 | public OBJMaterialDefinition() { 54 | super(); 55 | name = "NULL"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/jumi/obj/objects/definitions/OBJModelDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.obj.objects.definitions; 20 | 21 | import com.jumi.data.Vector2; 22 | import com.jumi.data.Vector3; 23 | import com.jumi.scene.objects.JUMIMaterial; 24 | import com.jumi.scene.objects.JUMIMesh; 25 | import com.jumi.scene.objects.JUMIMesh.FaceType; 26 | import com.jumi.scene.objects.JUMITexture; 27 | import java.util.ArrayList; 28 | import java.util.HashMap; 29 | import java.util.HashSet; 30 | 31 | /** 32 | * 33 | * @author RGreenlees 34 | */ 35 | public class OBJModelDefinition { 36 | 37 | public String name = ""; 38 | public ArrayList materialDefinitions = new ArrayList(); 39 | 40 | float[] vertices = new float[0]; 41 | float[] normals = new float[0]; 42 | float[] uvs = new float[0]; 43 | int[] indices = new int[0]; 44 | 45 | public HashSet uniqueIndexDefinitions = new HashSet(); 46 | public ArrayList allIndexDefinitions = new ArrayList(); 47 | 48 | public OBJModelDefinition(String inName) { 49 | super(); 50 | name = inName; 51 | } 52 | 53 | /** Generates a JUMIMesh from this OBJModelDefinition 54 | * 55 | * @param vertexPool All the defined vertices for this OBJ 56 | * @param normalPool All the defined normals for this OBJ 57 | * @param uvPool All the defined UVs for this OBJ 58 | * @return 59 | */ 60 | public JUMIMesh createMesh(ArrayList vertexPool, ArrayList normalPool, ArrayList uvPool) { 61 | JUMIMesh result = new JUMIMesh(name); 62 | 63 | // We're going to take the total vertices/normals/UVs and divide them up among each JUMIMesh in the scene 64 | ArrayList modelVertices = new ArrayList(); 65 | ArrayList modelNormals = new ArrayList(); 66 | ArrayList modelUVs = new ArrayList(); 67 | 68 | // First create the set of vertices, UVs and normals relevant to this model. Do this using the unique indices associated with it 69 | for (String a : uniqueIndexDefinitions) { 70 | String[] indexDef = a.split("/"); 71 | int vindex = Integer.valueOf(indexDef[0]); 72 | 73 | // Retrieve from the wider vertex pool, the vertices for this model. -1 as OBJ goes from 1...n 74 | modelVertices.add(vertexPool.get(vindex - 1)); 75 | 76 | // If UVs are defined, retrieve from the wider UV pool the UVs for this model. -1 as OBJ goes from 1...n 77 | if (indexDef.length > 1 && !indexDef[1].equals("")) { 78 | int uvIndex = Integer.valueOf(indexDef[1]); 79 | modelUVs.add(uvPool.get(uvIndex - 1)); 80 | } 81 | 82 | // If normals are defined, retrieve from the wider normal pool the normals for this model. -1 as OBJ goes from 1...n 83 | if (indexDef.length > 2 && !indexDef[2].equals("")) { 84 | int nIndex = Integer.valueOf(indexDef[2]); 85 | modelNormals.add(normalPool.get(nIndex - 1)); 86 | } 87 | } 88 | 89 | // Create the arrays ready to hold the raw float data 90 | result.vertices = new float[modelVertices.size() * 3]; 91 | result.normals = new float[modelNormals.size() * 3]; 92 | result.uvs = new float[modelUVs.size() * 2]; 93 | result.indices = new int[allIndexDefinitions.size()]; 94 | 95 | int arrayCounter = 0; 96 | 97 | // Create a hashmap with each unique face mapped against the order in which it appears. Lookup is MUCH faster than ArrayList for large models 98 | HashMap uniqueIndices = new HashMap(); 99 | 100 | for (String a : uniqueIndexDefinitions) { 101 | uniqueIndices.put(a, arrayCounter++); 102 | } 103 | 104 | arrayCounter = 0; 105 | 106 | // For every index defined, retrieve the index within the hashmap to get our final index! 107 | for (String a : allIndexDefinitions) { 108 | result.indices[arrayCounter++] = uniqueIndices.get(a); 109 | } 110 | 111 | arrayCounter = 0; 112 | 113 | // Now populate the raw float data for vertices, UVs and normals (if applicable) 114 | for (Vector3 a : modelVertices) { 115 | result.vertices[arrayCounter++] = a.x; 116 | result.vertices[arrayCounter++] = a.y; 117 | result.vertices[arrayCounter++] = a.z; 118 | } 119 | 120 | arrayCounter = 0; 121 | 122 | for (Vector3 a : modelNormals) { 123 | result.normals[arrayCounter++] = a.x; 124 | result.normals[arrayCounter++] = a.y; 125 | result.normals[arrayCounter++] = a.z; 126 | } 127 | 128 | arrayCounter = 0; 129 | 130 | for (Vector2 a : modelUVs) { 131 | result.uvs[arrayCounter++] = a.x; 132 | result.uvs[arrayCounter++] = a.y; 133 | } 134 | 135 | // Now create all the materials for the mesh 136 | result.materials = new JUMIMaterial[materialDefinitions.size()]; 137 | 138 | int materialCounter = 0; 139 | 140 | // TODO: What happens if a .obj references a material by its diffuse map rather than name? (see Ax.obj) 141 | for (OBJMaterialDefinition a : materialDefinitions) { 142 | if (a == null) { 143 | continue; 144 | } 145 | JUMIMaterial newMat = new JUMIMaterial(); 146 | newMat.name = a.name; 147 | newMat.ambientColor = a.ambientColour; 148 | newMat.diffuseColor = a.diffuseColour; 149 | newMat.specularColor = a.specularColour; 150 | newMat.specularFactor = a.specularWeight; 151 | newMat.opacity = a.alpha; 152 | 153 | // If a diffuse/specular/normal map is defined, make sure it's included in the JUMIMaterial! 154 | if (!a.diffuseMap.equals("")) { 155 | JUMITexture newTexture = new JUMITexture(); 156 | newTexture.fileName = a.diffuseMap; 157 | newTexture.name = a.diffuseMap; 158 | newMat.diffuseTexture = newTexture; 159 | } 160 | 161 | if (!a.specularMap.equals("")) { 162 | JUMITexture newTexture = new JUMITexture(); 163 | newTexture.fileName = a.specularMap; 164 | newTexture.name = a.specularMap; 165 | newMat.specularTexture = newTexture; 166 | } 167 | 168 | if (!a.bumpMap.equals("")) { 169 | JUMITexture newTexture = new JUMITexture(); 170 | newTexture.fileName = a.bumpMap; 171 | newTexture.name = a.bumpMap; 172 | newMat.normalTexture = newTexture; 173 | } 174 | 175 | result.materials[materialCounter++] = newMat; 176 | 177 | } 178 | 179 | result.faceType = FaceType.TRIANGLES; 180 | 181 | return result; 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/com/jumi/scene/JUMIScene.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene; 20 | 21 | 22 | import com.jumi.scene.objects.JUMIMesh; 23 | import com.jumi.scene.objects.JUMITexture; 24 | import java.util.ArrayList; 25 | import java.util.EnumSet; 26 | 27 | /* 28 | * To change this license header, choose License Headers in Project Properties. 29 | * To change this template file, choose Tools | Templates 30 | * and open the template in the editor. 31 | */ 32 | /** 33 | * 34 | * @author RGreenlees 35 | */ 36 | public class JUMIScene { 37 | 38 | JUMIMesh[] meshes = new JUMIMesh[0]; 39 | JUMITexture[] textures = new JUMITexture[0]; 40 | 41 | public static enum MeshAttributes { 42 | VERTICES, NORMALS, TEXTURECOORDINATES, TANGENTS; 43 | EnumSet ALLATTRIBUTES = EnumSet.allOf(MeshAttributes.class); 44 | } 45 | 46 | public void addMeshes(ArrayList newMeshes) { 47 | meshes = new JUMIMesh[newMeshes.size()]; 48 | for (int i = 0; i < meshes.length; i++) { 49 | meshes[i] = newMeshes.get(i); 50 | } 51 | } 52 | 53 | public void addTextures(ArrayList newTextures) { 54 | textures = new JUMITexture[newTextures.size()]; 55 | for (int i = 0; i < textures.length; i++) { 56 | textures[i] = newTextures.get(i); 57 | } 58 | } 59 | 60 | public JUMIMesh getMeshByName(String name) { 61 | for (JUMIMesh i : meshes) { 62 | if (i.name.equals(name)) { 63 | return i; 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | public JUMIMesh getMeshByIndex(int index) { 70 | if (index >= meshes.length) { 71 | return null; 72 | } 73 | return meshes[index]; 74 | } 75 | 76 | public JUMITexture getTextureByIndex(int index) { 77 | if (index >= textures.length) { 78 | return null; 79 | } 80 | return textures[index]; 81 | } 82 | 83 | public JUMIMesh[] getAllMeshes() { 84 | return meshes; 85 | } 86 | 87 | public String toString() { 88 | String result = "JUMIScene: " + "\n\tMeshes: "; 89 | for (int i = 0; i < meshes.length; i++) { 90 | result = result + "\n\t\t" + meshes[i].name; 91 | } 92 | 93 | result = result + "\n\tTextures:"; 94 | 95 | for (int i = 0; i < textures.length; i++) { 96 | result = result + "\n\t\t" + textures[i].name; 97 | } 98 | 99 | return result; 100 | } 101 | 102 | public int numMeshes() { 103 | return meshes.length; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMIBone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | import com.jumi.data.Vector3; 22 | import java.util.ArrayList; 23 | 24 | /** 25 | * 26 | * @author RGreenlees 27 | */ 28 | public class JUMIBone { 29 | 30 | private int[] indices = new int[0]; 31 | private float[] weights = new float[0]; 32 | private float[] transforms = new float[0]; 33 | private float[] transformLinks = new float[0]; 34 | 35 | private JUMIBone parent; 36 | private final ArrayList children = new ArrayList(); 37 | 38 | private Vector3 localTranslation = new Vector3(); 39 | private Vector3 localRotation = new Vector3(); 40 | private Vector3 localScaling = new Vector3(); 41 | 42 | private String name; 43 | 44 | public JUMIBone() { 45 | super(); 46 | } 47 | 48 | public JUMIBone(String inName) { 49 | name = inName; 50 | } 51 | 52 | public JUMIBone(String inName, int[] inIndices, float[] inWeights, float[] inTransforms, float[] inTransformLinks, Vector3 inTranslation, Vector3 inRotation, Vector3 inScaling) { 53 | name = inName; 54 | indices = inIndices; 55 | weights = inWeights; 56 | transforms = inTransforms; 57 | transformLinks = inTransformLinks; 58 | localTranslation = inTranslation; 59 | localRotation = inRotation; 60 | localScaling = inScaling; 61 | } 62 | 63 | public Vector3 getLocalScaling() { 64 | return localScaling; 65 | } 66 | 67 | public JUMIBone(String inName, Vector3 inTranslation, Vector3 inRotation, Vector3 inScaling) { 68 | name = inName; 69 | localTranslation = inTranslation; 70 | localRotation = inRotation; 71 | localScaling = inScaling; 72 | } 73 | 74 | public Vector3 getLocalTranslation() { 75 | return localTranslation; 76 | } 77 | 78 | public Vector3 getLocalRotation() { 79 | return localRotation; 80 | } 81 | 82 | public float[] getTransforms() { 83 | return transforms; 84 | } 85 | 86 | public float[] getTransformLinks() { 87 | return transformLinks; 88 | } 89 | 90 | public void setParent(JUMIBone newParent) { 91 | parent = newParent; 92 | } 93 | 94 | public void addChild(JUMIBone newChild) { 95 | children.add(newChild); 96 | newChild.setParent(this); 97 | } 98 | 99 | public void removeChild(JUMIBone childToRemove) { 100 | children.remove(childToRemove); 101 | childToRemove.setParent(null); 102 | } 103 | 104 | public void removeChild(String childName) { 105 | int index = -1; 106 | for (JUMIBone a : children) { 107 | if (a.name.equals(childName)) { 108 | index = children.indexOf(a); 109 | break; 110 | } 111 | } 112 | 113 | if (index > -1) { 114 | children.remove(index); 115 | } 116 | } 117 | 118 | public String getName() { 119 | return name; 120 | } 121 | 122 | public void setName(String newName) { 123 | name = newName; 124 | } 125 | 126 | public int[] getVertexIndices() { 127 | return indices; 128 | } 129 | 130 | public float[] getWeights() { 131 | return weights; 132 | } 133 | 134 | public JUMIBone getParent() { 135 | return parent; 136 | } 137 | 138 | public JUMIBone[] getChildren() { 139 | return children.toArray(new JUMIBone[children.size()]); 140 | } 141 | 142 | public JUMIBone[] getDescendants() { 143 | ArrayList allBones = new ArrayList(); 144 | 145 | addDescendantsToList(allBones); 146 | 147 | JUMIBone[] result = new JUMIBone[allBones.size()]; 148 | allBones.toArray(result); 149 | return result; 150 | } 151 | 152 | public JUMIBone[] getFullSkeleton() { 153 | ArrayList allBones = new ArrayList(); 154 | JUMIBone root = getRoot(); 155 | allBones.add(root); 156 | root.addDescendantsToList(allBones); 157 | 158 | JUMIBone[] result = new JUMIBone[allBones.size()]; 159 | allBones.toArray(result); 160 | return result; 161 | } 162 | 163 | public JUMIBone getRoot() { 164 | if (parent == null) { 165 | return this; 166 | } else { 167 | return parent.getRoot(); 168 | } 169 | } 170 | 171 | private void addDescendantsToList(ArrayList boneList) { 172 | for (JUMIBone a : children) { 173 | boneList.add(a); 174 | a.addDescendantsToList(boneList); 175 | } 176 | } 177 | 178 | public JUMIBone findDescendantByName(String inName) { 179 | for (JUMIBone a : children) { 180 | if (a.getName().equals(inName)) { 181 | return a; 182 | } 183 | a.findDescendantByName(inName); 184 | } 185 | return null; 186 | } 187 | 188 | public String toString() { 189 | return "JUMIBone:\n\tName: " + name + "\n\tParent: " + ((parent != null) ? parent.name : "None") + "\n\tChildren: " + children.size() + "\n\tDescendants: " + getDescendants().length; 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMIMaterial.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | import com.jumi.data.Color; 22 | 23 | /** 24 | * 25 | * @author RGreenlees 26 | */ 27 | public class JUMIMaterial { 28 | 29 | public String name; 30 | 31 | public Color ambientColor = new Color(); 32 | public Color diffuseColor = new Color(); 33 | public Color specularColor = new Color(); 34 | public Color emissiveColor = new Color(); 35 | 36 | public float specularFactor; 37 | public float shininessExponent; 38 | public float transparencyFactor; 39 | public float emissiveFactor; 40 | public float reflectionFactor; 41 | public float shininess; 42 | public float reflectivity; 43 | public float opacity; 44 | 45 | public JUMITexture diffuseTexture = null; 46 | public JUMITexture normalTexture = null; 47 | public JUMITexture specularTexture = null; 48 | 49 | public JUMITexture[] textures = new JUMITexture[0]; 50 | 51 | public String toString() { 52 | String result = "JUMIMaterial:\n\tName: " + name 53 | + "\n\tAmbient Color: " + ambientColor 54 | + "\n\tDiffuse Color: " + diffuseColor 55 | + "\n\tSpecular Color: " + specularColor + ", Factor = " + specularFactor 56 | + "\n\tEmissive Color: " + emissiveColor 57 | + "\n\tDiffuse Texture: " + ((diffuseTexture != null) ? diffuseTexture.name : "None") 58 | + "\n\tNormal Texture: " + ((normalTexture != null) ? normalTexture.name : "None") 59 | + "\n\tSpecular Texture: " + ((specularTexture != null) ? specularTexture.name : "None") 60 | + "\n\tUnassociated Textures: " + textures.length; 61 | 62 | for (JUMITexture a : textures) { 63 | result = result + "\n\t\t" + a.name; 64 | } 65 | 66 | return result; 67 | } 68 | 69 | /** Returns the texture at the supplied index for the unsorted textures only */ 70 | public JUMITexture getTextureByIndex(int index) { 71 | if (index >= textures.length) { 72 | return null; 73 | } 74 | return textures[index]; 75 | } 76 | 77 | /** Returns the diffuse texture for this material, or null if none exists */ 78 | public JUMITexture getDiffuseTexture() { 79 | return diffuseTexture; 80 | } 81 | 82 | /** Returns the specular texture for this material, or null if none exists */ 83 | public JUMITexture getSpecularTexture() { 84 | return specularTexture; 85 | } 86 | 87 | /** Returns the normal texture for this material, or null if none exists */ 88 | public JUMITexture getNormalTexture() { 89 | return normalTexture; 90 | } 91 | 92 | /** Searches all textures, including unsorted, for the given name */ 93 | public JUMITexture getTextureByName(String name) { 94 | 95 | if (diffuseTexture.name.equals(name)) { 96 | return diffuseTexture; 97 | } 98 | 99 | if (specularTexture.name.equals(name)) { 100 | return specularTexture; 101 | } 102 | 103 | if (normalTexture.name.equals(name)) { 104 | return normalTexture; 105 | } 106 | 107 | for (JUMITexture a : textures) { 108 | if (a.name.equals(name)) { 109 | return a; 110 | } 111 | } 112 | return null; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMIMesh.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | import static java.lang.Math.abs; 22 | 23 | /* 24 | * To change this license header, choose License Headers in Project Properties. 25 | * To change this template file, choose Tools | Templates 26 | * and open the template in the editor. 27 | */ 28 | /** 29 | * 30 | * @author RGreenlees 31 | */ 32 | public class JUMIMesh { 33 | 34 | public static enum FaceType { 35 | 36 | NONE, TRIANGLES, QUADS 37 | }; 38 | public FaceType faceType = FaceType.NONE; 39 | 40 | public String name; 41 | 42 | public float[] vertices = new float[0]; 43 | public float[] normals = new float[0]; 44 | public float[] binormals = new float[0]; 45 | public float[] tangents = new float[0]; 46 | public float[] uvs = new float[0]; 47 | 48 | public int[] indices = new int[0]; 49 | public int[] edges = new int[0]; 50 | 51 | public JUMITexture[] textures = new JUMITexture[0]; 52 | public JUMIMaterial[] materials = new JUMIMaterial[0]; 53 | 54 | public JUMIBone rootBone; 55 | 56 | public JUMIMesh(String inputName) { 57 | name = inputName; 58 | } 59 | 60 | public int numTextures() { 61 | return textures.length; 62 | } 63 | 64 | /** Does this mesh have skeletal information? */ 65 | public boolean hasSkeleton() { 66 | return rootBone != null; 67 | } 68 | 69 | /** Searches for the supplied bone name within the hierarchy, returning null if not found */ 70 | public JUMIBone getBoneByName(String boneName) { 71 | return findBoneInHierarchy(rootBone, boneName); 72 | } 73 | 74 | private JUMIBone findBoneInHierarchy(JUMIBone root, String boneName) { 75 | JUMIBone result = null; 76 | if (root.getName().equals(boneName)) { 77 | return root; 78 | } else { 79 | for (JUMIBone child : root.getChildren()) { 80 | result = findBoneInHierarchy(child, boneName) ; 81 | if (result != null) { 82 | return result; 83 | } 84 | } 85 | return result; 86 | } 87 | } 88 | 89 | /** Returns the texture at the supplied index, or null if it does not exist. Only for textures without a parent material */ 90 | public JUMITexture getTextureByIndex(int index) { 91 | if (index >= textures.length) { 92 | return null; 93 | } 94 | return textures[index]; 95 | } 96 | 97 | /** Searches for the supplied texture name and returns any matches, or null if none found */ 98 | public JUMITexture getTextureByName(String inName) { 99 | for (JUMITexture a : textures) { 100 | if (a.name.equals(inName)) { 101 | return a; 102 | } 103 | } 104 | return null; 105 | } 106 | 107 | /** Returns the material at the supplied index, or null if it does not exist */ 108 | public JUMIMaterial getMaterialByIndex(int index) { 109 | if (index >= materials.length) { 110 | return null; 111 | } 112 | return materials[index]; 113 | } 114 | 115 | /** Searches for the supplied material name and returns any matches, or null if none found */ 116 | public JUMIMaterial getMaterialByName(String inName) { 117 | for (JUMIMaterial a : materials) { 118 | if (a.name.equals(inName)) { 119 | return a; 120 | } 121 | } 122 | return null; 123 | } 124 | 125 | /** Return an array of all bones for this mesh */ 126 | public JUMIBone[] getAllBones() { 127 | return rootBone.getFullSkeleton(); 128 | } 129 | 130 | /** Returns the distance of the furthest point from the model's origin */ 131 | public float getMaxExtent() { 132 | float result = 0.0f; 133 | for (int i = 0; i < vertices.length; i++) { 134 | if (abs(vertices[i]) > result) { 135 | result = abs(vertices[i]); 136 | } 137 | } 138 | return result; 139 | } 140 | 141 | /** Scales the model so that the furthest vertex from the model's origin matches the supplied extent */ 142 | public void setMaxExtent(float newExtent) { 143 | float scale = newExtent / getMaxExtent(); 144 | 145 | setScale(scale); 146 | 147 | } 148 | 149 | /** Scales the mesh by the supplied scalar */ 150 | public void setScale(float newScale) { 151 | for (int i = 0; i < vertices.length; i++) { 152 | vertices[i] *= newScale; 153 | } 154 | } 155 | 156 | /** Triangulates the mesh (Currently only works for QUADS) */ 157 | public void triangulate() { 158 | if (faceType == FaceType.QUADS) { 159 | int[] newIndices = new int[(int) (indices.length * 1.5f)]; 160 | int newIndexCounter = 0; 161 | 162 | for (int i = 0; i < indices.length - 4; i += 4) { 163 | newIndices[newIndexCounter++] = indices[i]; 164 | newIndices[newIndexCounter++] = indices[i + 1]; 165 | newIndices[newIndexCounter++] = indices[i + 2]; 166 | newIndices[newIndexCounter++] = indices[i + 2]; 167 | newIndices[newIndexCounter++] = indices[i + 3]; 168 | newIndices[newIndexCounter++] = indices[i]; 169 | } 170 | 171 | indices = newIndices; 172 | faceType = FaceType.TRIANGLES; 173 | } 174 | } 175 | 176 | public String toString() { 177 | String result = "JUMIMesh: " + name + "\n\tVertices: " + ((vertices != null) ? (vertices.length / 3) : 0) 178 | + "\n\tNormals: " + ((normals != null) ? (normals.length / 3) : 0) 179 | + "\n\tUVs: " + ((uvs != null) ? (uvs.length / 2) : 0) 180 | + "\n\tTextures: " + ((textures != null) ? textures.length : 0); 181 | for (int i = 0; i < textures.length; i++) { 182 | result = result + "\n\t\t" + textures[i].name; 183 | } 184 | result = result + "\n\tMaterials: " + ((materials != null) ? materials.length : 0); 185 | for (int i = 0; i < materials.length; i++) { 186 | result = result + "\n\t\t" + materials[i].name; 187 | } 188 | 189 | result = result + "\n\tFace Type: " + faceType.toString() 190 | + "\n\tMax Extent: " + this.getMaxExtent(); 191 | result = result + "\n\tRoot Bone: " + ((rootBone != null) ? rootBone.getName() : "None"); 192 | return result; 193 | } 194 | 195 | /** Returns the vertices for this mesh in float[] form */ 196 | public float[] getVertices() { 197 | return vertices; 198 | } 199 | 200 | /** Returns the normals for this mesh in float[] form */ 201 | public float[] getNormals() { 202 | return normals; 203 | } 204 | 205 | /** Returns the UVs for this mesh in float[] form */ 206 | public float[] getUVs() { 207 | return uvs; 208 | } 209 | 210 | /** Returns the indices for this mesh in int[] form */ 211 | public int[] getVertexIndices() { 212 | return indices; 213 | } 214 | 215 | /** Returns the edges for this mesh in int[] form (NOT IMPLEMENTED YET!) */ 216 | public int[] getEdges() { 217 | return edges; 218 | } 219 | 220 | /** Horizontally flips the UV coordinates */ 221 | public void flipUVX() { 222 | for (int i = 0; i < uvs.length; i += 2) { 223 | uvs[i] = 1.0f - uvs[i]; 224 | } 225 | } 226 | 227 | /** Vertically flips the UV coordinates */ 228 | public void flipUVY() { 229 | for (int i = 1; i < uvs.length; i += 2) { 230 | uvs[i] = 1.0f - uvs[i]; 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMISkinDeformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | /** 22 | * 23 | * @author RGreenlees 24 | */ 25 | public class JUMISkinDeformer { 26 | 27 | String name; 28 | 29 | public JUMISubDeformer[] deformers = new JUMISubDeformer[0]; 30 | 31 | public JUMISkinDeformer(String inName) { 32 | name = inName; 33 | } 34 | 35 | public String toString() { 36 | String result = "Skin Deformer " + name + ":"; 37 | for (int i = 0; i < deformers.length; i++) { 38 | result = result + "\n\t" + deformers[i].name; 39 | } 40 | return result; 41 | } 42 | 43 | public JUMISubDeformer getSubDeformerByIndex(int index) { 44 | if (index >= deformers.length) { 45 | return null; 46 | } 47 | return deformers[index]; 48 | } 49 | 50 | public JUMISubDeformer getSubDeformerByName(String inName) { 51 | for (JUMISubDeformer a : deformers) { 52 | if (a.name.equals(inName)) { 53 | return a; 54 | } 55 | } 56 | return null; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMISubDeformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | /** 22 | * 23 | * @author RGreenlees 24 | */ 25 | public class JUMISubDeformer { 26 | 27 | public String name; 28 | 29 | public int[] indexes = new int[0]; 30 | public float[] weights = new float[0]; 31 | public float[] transforms = new float[0]; 32 | public float[] transformLinks = new float[0]; 33 | 34 | public JUMISubDeformer(String inName) { 35 | name = inName; 36 | } 37 | 38 | public String toString() { 39 | return "Sub-Deformer " + name + ":\n\tIndexes: " + indexes.length + "\n\tWeights: " + weights.length; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/com/jumi/scene/objects/JUMITexture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2015 Richard Greenlees 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 5 | * and associated documentation files (the "Software"), to deal in the Software without restriction, 6 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 8 | * subject to the following conditions: 9 | * 10 | * 1) The above copyright notice and this permission notice shall be included 11 | * in all copies or substantial portions of the Software. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | */ 19 | package com.jumi.scene.objects; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * 25 | * @author RGreenlees 26 | */ 27 | public class JUMITexture { 28 | public String fileName; 29 | public String relativeFilename; 30 | public String fullFilePath; 31 | public String type; 32 | public String name; 33 | 34 | private byte[] textureData = new byte[0]; 35 | 36 | public int textureDataSize() { 37 | return textureData.length; 38 | } 39 | 40 | public void setTextureData(byte[] data) { 41 | textureData = data; 42 | } 43 | 44 | /** Return the raw binary data for embedded textures (currently FBX only) */ 45 | public byte[] getEmbeddedData() { 46 | if (textureData.length > 0) { 47 | return textureData; 48 | } else { 49 | return null; 50 | } 51 | } 52 | 53 | /** Buffer the raw binary data for embedded textures into the supplied ByteBuffer (Currently FBX only) */ 54 | public void bufferData(ByteBuffer buf) { 55 | for (int i = 0; i < textureData.length; i++) { 56 | buf.put(textureData[i]); 57 | } 58 | } 59 | 60 | public String toString() { 61 | boolean embeddedData = (textureData != null && textureData.length > 0); 62 | return "Texture:" + "\n\tName: " + name 63 | + "\n\tFile Name: " + fileName 64 | + "\n\tRelative File Location: " + relativeFilename 65 | + "\n\tFull Path: " + fullFilePath 66 | + "\n\tEmbedded Data: " + embeddedData + " " + ((embeddedData) ? "(" + textureData.length + " bytes)" : ""); 67 | } 68 | 69 | } 70 | --------------------------------------------------------------------------------