From f65dd517c904480239152826d7bbc4f36afdc8d3 Mon Sep 17 00:00:00 2001
From: Eirenliel Allier
Date: Tue, 26 Apr 2016 10:10:26 +0300
Subject: [PATCH 1/4] Reworked original FBX importer
---
.../jme3/scene/plugins/fbx/RotationOrder.java | 102 +
.../jme3/scene/plugins/fbx/SceneLoader.java | 1641 +++--------------
.../plugins/fbx/objects/FbxAnimCurve.java | 49 +
.../plugins/fbx/objects/FbxAnimNode.java | 86 +
.../plugins/fbx/objects/FbxBindPose.java | 55 +
.../scene/plugins/fbx/objects/FbxCluster.java | 42 +
.../scene/plugins/fbx/objects/FbxImage.java | 123 ++
.../plugins/fbx/objects/FbxMaterial.java | 113 ++
.../scene/plugins/fbx/objects/FbxMesh.java | 537 ++++++
.../scene/plugins/fbx/objects/FbxNode.java | 274 +++
.../scene/plugins/fbx/objects/FbxObject.java | 40 +
.../scene/plugins/fbx/objects/FbxSkin.java | 150 ++
.../scene/plugins/fbx/objects/FbxTexture.java | 42 +
13 files changed, 1875 insertions(+), 1379 deletions(-)
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimCurve.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimNode.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxBindPose.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxCluster.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMaterial.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxObject.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java
create mode 100644 jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
new file mode 100644
index 000000000..94940609f
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
@@ -0,0 +1,102 @@
+package com.jme3.scene.plugins.fbx;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
+// TODO This class has some potential in it... Should investigate
+public enum RotationOrder {
+
+ EULER_XYZ, EULER_XZY, EULER_YZX, EULER_YXZ, EULER_ZXY, EULER_ZYX, SPHERIC_XYZ;
+
+ public static final RotationOrder[] values = values();
+
+ private RotationOrder() {
+ }
+
+ public Quaternion rotate(Vector3f vec) {
+ return fromEuler(vec.x * FastMath.DEG_TO_RAD, vec.y * FastMath.DEG_TO_RAD, vec.z * FastMath.DEG_TO_RAD, this);
+ }
+
+ public Quaternion rotate(float x, float y, float z) {
+ return fromEuler(x * FastMath.DEG_TO_RAD, y * FastMath.DEG_TO_RAD, z * FastMath.DEG_TO_RAD, this);
+ }
+
+ private static Quaternion fromEuler(float x, float y, float z, RotationOrder order) {
+ switch(order) {
+ case EULER_XYZ:
+ return toQuat(x, Vector3f.UNIT_X, y, Vector3f.UNIT_Y, z, Vector3f.UNIT_Z);
+ case EULER_YXZ:
+ return toQuat(y, Vector3f.UNIT_Y, x, Vector3f.UNIT_X, z, Vector3f.UNIT_Z);
+ case EULER_ZXY:
+ return toQuat(z, Vector3f.UNIT_Z, x, Vector3f.UNIT_X, y, Vector3f.UNIT_Y);
+ case EULER_ZYX:
+ return toQuat(z, Vector3f.UNIT_Z, y, Vector3f.UNIT_Y, x, Vector3f.UNIT_X);
+ case EULER_YZX:
+ return toQuat(y, Vector3f.UNIT_Y, z, Vector3f.UNIT_Z, x, Vector3f.UNIT_X);
+ case EULER_XZY:
+ return toQuat(x, Vector3f.UNIT_X, z, Vector3f.UNIT_Z, y, Vector3f.UNIT_Y);
+ case SPHERIC_XYZ:
+ throw new IllegalArgumentException("Spheric rotation is unsupported in this importer");
+ }
+ /*float c1 = FastMath.cos( x / 2 );
+ float c2 = FastMath.cos( y / 2 );
+ float c3 = FastMath.cos( z / 2 );
+ float s1 = FastMath.sin( x / 2 );
+ float s2 = FastMath.sin( y / 2 );
+ float s3 = FastMath.sin( z / 2 );
+
+ float _x;
+ float _y;
+ float _z;
+ float _w;
+
+ switch(order) {
+ case EULER_XYZ:
+ _x = s1 * c2 * c3 + c1 * s2 * s3;
+ _y = c1 * s2 * c3 - s1 * c2 * s3;
+ _z = c1 * c2 * s3 + s1 * s2 * c3;
+ _w = c1 * c2 * c3 - s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ case EULER_YXZ:
+ _x = s1 * c2 * c3 + c1 * s2 * s3;
+ _y = c1 * s2 * c3 - s1 * c2 * s3;
+ _z = c1 * c2 * s3 - s1 * s2 * c3;
+ _w = c1 * c2 * c3 + s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ case EULER_ZXY:
+ _x = s1 * c2 * c3 - c1 * s2 * s3;
+ _y = c1 * s2 * c3 + s1 * c2 * s3;
+ _z = c1 * c2 * s3 + s1 * s2 * c3;
+ _w = c1 * c2 * c3 - s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ case EULER_ZYX:
+ _x = s1 * c2 * c3 - c1 * s2 * s3;
+ _y = c1 * s2 * c3 + s1 * c2 * s3;
+ _z = c1 * c2 * s3 - s1 * s2 * c3;
+ _w = c1 * c2 * c3 + s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ case EULER_YZX:
+ _x = s1 * c2 * c3 + c1 * s2 * s3;
+ _y = c1 * s2 * c3 + s1 * c2 * s3;
+ _z = c1 * c2 * s3 - s1 * s2 * c3;
+ _w = c1 * c2 * c3 - s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ case EULER_XZY:
+ _x = s1 * c2 * c3 - c1 * s2 * s3;
+ _y = c1 * s2 * c3 - s1 * c2 * s3;
+ _z = c1 * c2 * s3 + s1 * s2 * c3;
+ _w = c1 * c2 * c3 + s1 * s2 * s3;
+ return new Quaternion(_x, _y, _z, _w);
+ }
+ */
+ throw new AssertionError("Impossible");
+ }
+
+ private static Quaternion toQuat(float ax1v, Vector3f ax1, float ax2v, Vector3f ax2, float ax3v, Vector3f ax3) {
+ Quaternion q1 = new Quaternion().fromAngleNormalAxis(ax1v, ax1);
+ Quaternion q2 = new Quaternion().fromAngleNormalAxis(ax2v, ax2);
+ Quaternion q3 = new Quaternion().fromAngleNormalAxis(ax3v, ax3);
+ return q1.multLocal(q2).multLocal(q3);
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
index 87e13abed..cf4af7682 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
@@ -1,46 +1,13 @@
-/*
- * Copyright (c) 2009-2014 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
package com.jme3.scene.plugins.fbx;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -50,36 +17,29 @@ import com.jme3.animation.Bone;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
+import com.jme3.animation.Track;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
-import com.jme3.asset.AssetLoadException;
import com.jme3.asset.AssetLoader;
import com.jme3.asset.AssetManager;
-import com.jme3.asset.ModelKey;
-import com.jme3.material.Material;
-import com.jme3.material.RenderState.BlendMode;
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.FastMath;
-import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
-import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.Mesh.Mode;
-import com.jme3.scene.VertexBuffer.Type;
-import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.plugins.fbx.AnimationList.AnimInverval;
import com.jme3.scene.plugins.fbx.file.FbxElement;
import com.jme3.scene.plugins.fbx.file.FbxFile;
import com.jme3.scene.plugins.fbx.file.FbxReader;
-import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.util.BufferUtils;
+import com.jme3.scene.plugins.fbx.objects.FbxAnimCurve;
+import com.jme3.scene.plugins.fbx.objects.FbxAnimNode;
+import com.jme3.scene.plugins.fbx.objects.FbxBindPose;
+import com.jme3.scene.plugins.fbx.objects.FbxCluster;
+import com.jme3.scene.plugins.fbx.objects.FbxImage;
+import com.jme3.scene.plugins.fbx.objects.FbxMaterial;
+import com.jme3.scene.plugins.fbx.objects.FbxMesh;
+import com.jme3.scene.plugins.fbx.objects.FbxNode;
+import com.jme3.scene.plugins.fbx.objects.FbxObject;
+import com.jme3.scene.plugins.fbx.objects.FbxSkin;
+import com.jme3.scene.plugins.fbx.objects.FbxTexture;
/**
* FBX file format loader
@@ -91,53 +51,50 @@ import com.jme3.util.BufferUtils;
public class SceneLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
+ private static final double secondsPerUnit = 1 / 46186158000d; // Animation speed factor
+ public static final boolean WARN_IGNORED_ATTRIBUTES = false;
- private AssetManager assetManager;
+ // Scene loading data
+ private List warnings = new ArrayList<>();
private AnimationList animList;
-
private String sceneName;
- private String sceneFilename;
- private String sceneFolderName;
- private float unitSize;
- private float animFrameRate;
- private final double secondsPerUnit = 1 / 46186158000d; // Animation speed factor
+ public String sceneFilename;
+ public String sceneFolderName;
+ public AssetManager assetManager;
+ public AssetInfo currentAssetInfo;
- // Loaded objects data
- private Map meshDataMap = new HashMap();
- private Map matDataMap = new HashMap();
- private Map texDataMap = new HashMap();
- private Map imgDataMap = new HashMap(); // Video clips
- private Map modelDataMap = new HashMap(); // Mesh nodes and limb nodes
- private Map poseDataMap = new HashMap(); // Node bind poses
- private Map skinMap = new HashMap(); // Skin for bone clusters
- private Map clusterMap = new HashMap(); // Bone skinning cluster
- private Map acurveMap = new HashMap(); // Animation curves
- private Map anodeMap = new HashMap(); // Animation nodes
- private Map alayerMap = new HashMap(); // Amination layers
- private Map> refMap = new HashMap>(); // Object links
- private Map> propMap = new HashMap>(); // Property links
+ // Scene global settings
+ private float animFrameRate;
+ public float unitSize;
+ public int xAxis = 1;
+ public int yAxis = 1;
+ public int zAxis = 1;
// Scene objects
- private Map modelMap = new HashMap(); // Mesh nodes
- private Map limbMap = new HashMap(); // Bones
- private Map bindMap = new HashMap(); // Node bind poses
- private Map geomMap = new HashMap(); // Mesh geometries
- private Map matMap = new HashMap();
- private Map texMap = new HashMap();
- private Map imgMap = new HashMap();
+ private Map allObjects = new HashMap<>(); // All supported FBX objects
+ private Map skinMap = new HashMap<>(); // Skin for bone clusters
+ private Map alayerMap = new HashMap<>(); // Amination layers
+ public Map modelMap = new HashMap<>(); // Nodes
+ private Map limbMap = new HashMap<>(); // Nodes that are actually bones
+ private Map bindMap = new HashMap<>(); // Node bind poses
+ private Map geomMap = new HashMap<>(); // Mesh geometries
private Skeleton skeleton;
private AnimControl animControl;
+ public Node sceneNode;
+
+ public void warning(String warning) {
+ warnings.add(warning);
+ }
@Override
public Object load(AssetInfo assetInfo) throws IOException {
+ this.currentAssetInfo = assetInfo;
this.assetManager = assetInfo.getManager();
AssetKey> assetKey = assetInfo.getKey();
if(assetKey instanceof SceneKey)
animList = ((SceneKey) assetKey).getAnimations();
- else if(!(assetKey instanceof ModelKey))
- throw new AssetLoadException("Invalid asset key");
InputStream stream = assetInfo.openStream();
- Node sceneNode = null;
+ final Node sceneNode = this.sceneNode = new Node(sceneName + "-scene");
try {
sceneFilename = assetKey.getName();
sceneFolderName = assetKey.getFolder();
@@ -145,413 +102,131 @@ public class SceneLoader implements AssetLoader {
sceneName = sceneFilename.substring(0, sceneFilename.length() - ext.length() - 1);
if(sceneFolderName != null && sceneFolderName.length() > 0)
sceneName = sceneName.substring(sceneFolderName.length());
- reset();
loadScene(stream);
- sceneNode = linkScene();
+ linkScene();
+ if(warnings.size() > 0)
+ logger.log(Level.WARNING, "Model load finished with warnings:\n" + String.join("\n", warnings));
} finally {
releaseObjects();
- if(stream != null) {
+ if(stream != null)
stream.close();
- }
}
return sceneNode;
}
- private void reset() {
- unitSize = 1;
- animFrameRate = 30;
- }
-
private void loadScene(InputStream stream) throws IOException {
logger.log(Level.FINE, "Loading scene {0}", sceneFilename);
long startTime = System.currentTimeMillis();
FbxFile scene = FbxReader.readFBX(stream);
for(FbxElement e : scene.rootElements) {
- if(e.id.equals("GlobalSettings"))
+ // Is it possible for elements to be in wrong order?
+ switch(e.id) {
+ case "GlobalSettings":
loadGlobalSettings(e);
- else if(e.id.equals("Objects"))
+ break;
+ case "Objects":
loadObjects(e);
- else if(e.id.equals("Connections"))
+ break;
+ case "Connections":
loadConnections(e);
+ break;
+ }
}
long estimatedTime = System.currentTimeMillis() - startTime;
logger.log(Level.FINE, "Loading done in {0} ms", estimatedTime);
}
private void loadGlobalSettings(FbxElement element) {
+ for(FbxElement e2 : element.getFbxProperties()) {
+ String propName = (String) e2.properties.get(0);
+ switch(propName) {
+ case "UnitScaleFactor":
+ this.unitSize = ((Double) e2.properties.get(4)).floatValue();
+ break;
+ case "CustomFrameRate":
+ float framerate = ((Double) e2.properties.get(4)).floatValue();
+ if(framerate != -1)
+ this.animFrameRate = framerate;
+ break;
+ case "UpAxisSign":
+ this.yAxis = ((Integer) e2.properties.get(4)).intValue();
+ break;
+ case "FrontAxisSign":
+ this.zAxis = ((Integer) e2.properties.get(4)).intValue();
+ break;
+ case "CoordAxisSign":
+ this.xAxis = ((Integer) e2.properties.get(4)).intValue();
+ break;
+ }
+ }
+ }
+
+ private void loadObjects(FbxElement element) throws IOException {
+ FbxObject obj = null;
for(FbxElement e : element.children) {
- if(e.id.equals("Properties70")) {
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("P")) {
- String propName = (String) e2.properties.get(0);
- if(propName.equals("UnitScaleFactor"))
- this.unitSize = ((Double) e2.properties.get(4)).floatValue();
- else if(propName.equals("CustomFrameRate")) {
- float framerate = ((Double) e2.properties.get(4)).floatValue();
- if(framerate != -1)
- this.animFrameRate = framerate;
- }
- }
- }
- }
- }
- }
-
- private void loadObjects(FbxElement element) {
- for(FbxElement e : element.children) {
- if(e.id.equals("Geometry"))
- loadGeometry(e);
- else if(e.id.equals("Material"))
- loadMaterial(e);
- else if(e.id.equals("Model"))
- loadModel(e);
- else if(e.id.equals("Pose"))
- loadPose(e);
- else if(e.id.equals("Texture"))
- loadTexture(e);
- else if(e.id.equals("Video"))
- loadImage(e);
- else if(e.id.equals("Deformer"))
- loadDeformer(e);
- else if(e.id.equals("AnimationLayer"))
- loadAnimLayer(e);
- else if(e.id.equals("AnimationCurve"))
- loadAnimCurve(e);
- else if(e.id.equals("AnimationCurveNode"))
- loadAnimNode(e);
- }
- }
-
- private void loadGeometry(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String type = (String) element.properties.get(2);
- if(type.equals("Mesh")) {
- MeshData data = new MeshData();
- for(FbxElement e : element.children) {
- if(e.id.equals("Vertices"))
- data.vertices = (double[]) e.properties.get(0);
- else if(e.id.equals("PolygonVertexIndex"))
- data.indices = (int[]) e.properties.get(0);
- // TODO edges are not used now
- //else if(e.id.equals("Edges"))
- // data.edges = (int[]) e.properties.get(0);
- else if(e.id.equals("LayerElementNormal"))
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("MappingInformationType")) {
- data.normalsMapping = (String) e2.properties.get(0);
- if(!data.normalsMapping.equals("ByVertice") && !data.normalsMapping.equals("ByPolygonVertex"))
- throw new AssetLoadException("Not supported LayerElementNormal.MappingInformationType = " + data.normalsMapping);
- } else if(e2.id.equals("ReferenceInformationType")) {
- data.normalsReference = (String) e2.properties.get(0);
- if(!data.normalsReference.equals("Direct"))
- throw new AssetLoadException("Not supported LayerElementNormal.ReferenceInformationType = " + data.normalsReference);
- } else if(e2.id.equals("Normals"))
- data.normals = (double[]) e2.properties.get(0);
- }
- else if(e.id.equals("LayerElementTangent"))
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("MappingInformationType")) {
- data.tangentsMapping = (String) e2.properties.get(0);
- if(!data.tangentsMapping.equals("ByVertice") && !data.tangentsMapping.equals("ByPolygonVertex"))
- throw new AssetLoadException("Not supported LayerElementTangent.MappingInformationType = " + data.tangentsMapping);
- } else if(e2.id.equals("ReferenceInformationType")) {
- data.tangentsReference = (String) e2.properties.get(0);
- if(!data.tangentsReference.equals("Direct"))
- throw new AssetLoadException("Not supported LayerElementTangent.ReferenceInformationType = " + data.tangentsReference);
- } else if(e2.id.equals("Tangents"))
- data.tangents = (double[]) e2.properties.get(0);
- }
- else if(e.id.equals("LayerElementBinormal"))
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("MappingInformationType")) {
- data.binormalsMapping = (String) e2.properties.get(0);
- if(!data.binormalsMapping.equals("ByVertice") && !data.binormalsMapping.equals("ByPolygonVertex"))
- throw new AssetLoadException("Not supported LayerElementBinormal.MappingInformationType = " + data.binormalsMapping);
- } else if(e2.id.equals("ReferenceInformationType")) {
- data.binormalsReference = (String) e2.properties.get(0);
- if(!data.binormalsReference.equals("Direct"))
- throw new AssetLoadException("Not supported LayerElementBinormal.ReferenceInformationType = " + data.binormalsReference);
- } else if(e2.id.equals("Tangents"))
- data.binormals = (double[]) e2.properties.get(0);
- }
- else if(e.id.equals("LayerElementUV"))
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("MappingInformationType")) {
- data.uvMapping = (String) e2.properties.get(0);
- if(!data.uvMapping.equals("ByPolygonVertex"))
- throw new AssetLoadException("Not supported LayerElementUV.MappingInformationType = " + data.uvMapping);
- } else if(e2.id.equals("ReferenceInformationType")) {
- data.uvReference = (String) e2.properties.get(0);
- if(!data.uvReference.equals("IndexToDirect"))
- throw new AssetLoadException("Not supported LayerElementUV.ReferenceInformationType = " + data.uvReference);
- } else if(e2.id.equals("UV"))
- data.uv = (double[]) e2.properties.get(0);
- else if(e2.id.equals("UVIndex"))
- data.uvIndex = (int[]) e2.properties.get(0);
- }
- // TODO smoothing is not used now
- //else if(e.id.equals("LayerElementSmoothing"))
- // for(FbxElement e2 : e.children) {
- // if(e2.id.equals("MappingInformationType")) {
- // data.smoothingMapping = (String) e2.properties.get(0);
- // if(!data.smoothingMapping.equals("ByEdge"))
- // throw new AssetLoadException("Not supported LayerElementSmoothing.MappingInformationType = " + data.smoothingMapping);
- // } else if(e2.id.equals("ReferenceInformationType")) {
- // data.smoothingReference = (String) e2.properties.get(0);
- // if(!data.smoothingReference.equals("Direct"))
- // throw new AssetLoadException("Not supported LayerElementSmoothing.ReferenceInformationType = " + data.smoothingReference);
- // } else if(e2.id.equals("Smoothing"))
- // data.smoothing = (int[]) e2.properties.get(0);
- // }
- else if(e.id.equals("LayerElementMaterial"))
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("MappingInformationType")) {
- data.materialsMapping = (String) e2.properties.get(0);
- if(!data.materialsMapping.equals("AllSame"))
- throw new AssetLoadException("Not supported LayerElementMaterial.MappingInformationType = " + data.materialsMapping);
- } else if(e2.id.equals("ReferenceInformationType")) {
- data.materialsReference = (String) e2.properties.get(0);
- if(!data.materialsReference.equals("IndexToDirect"))
- throw new AssetLoadException("Not supported LayerElementMaterial.ReferenceInformationType = " + data.materialsReference);
- } else if(e2.id.equals("Materials"))
- data.materials = (int[]) e2.properties.get(0);
- }
- }
- meshDataMap.put(id, data);
- }
- }
-
- private void loadMaterial(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("")) {
- MaterialData data = new MaterialData();
- data.name = path.substring(0, path.indexOf(0));
- for(FbxElement e : element.children) {
- if(e.id.equals("ShadingModel")) {
- data.shadingModel = (String) e.properties.get(0);
- } else if(e.id.equals("Properties70")) {
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("P")) {
- String propName = (String) e2.properties.get(0);
- if(propName.equals("AmbientColor")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.ambientColor.set((float) x, (float) y, (float) z);
- } else if(propName.equals("AmbientFactor")) {
- double x = (Double) e2.properties.get(4);
- data.ambientFactor = (float) x;
- } else if(propName.equals("DiffuseColor")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.diffuseColor.set((float) x, (float) y, (float) z);
- } else if(propName.equals("DiffuseFactor")) {
- double x = (Double) e2.properties.get(4);
- data.diffuseFactor = (float) x;
- } else if(propName.equals("SpecularColor")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.specularColor.set((float) x, (float) y, (float) z);
- } else if(propName.equals("Shininess") || propName.equals("ShininessExponent")) {
- double x = (Double) e2.properties.get(4);
- data.shininessExponent = (float) x;
- }
- }
- }
- }
- }
- matDataMap.put(id, data);
- }
- }
-
- private void loadModel(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- ModelData data = new ModelData();
- data.name = path.substring(0, path.indexOf(0));
- data.type = type;
- for(FbxElement e : element.children) {
- if(e.id.equals("Properties70")) {
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("P")) {
- String propName = (String) e2.properties.get(0);
- if(propName.equals("Lcl Translation")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.localTranslation.set((float) x, (float) y, (float) z).divideLocal(unitSize);
- } else if(propName.equals("Lcl Rotation")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.localRotation.fromAngles((float) x * FastMath.DEG_TO_RAD, (float) y * FastMath.DEG_TO_RAD, (float) z * FastMath.DEG_TO_RAD);
- } else if(propName.equals("Lcl Scaling")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.localScale.set((float) x, (float) y, (float) z).multLocal(unitSize);
- } else if(propName.equals("PreRotation")) {
- double x = (Double) e2.properties.get(4);
- double y = (Double) e2.properties.get(5);
- double z = (Double) e2.properties.get(6);
- data.preRotation = quatFromBoneAngles((float) x * FastMath.DEG_TO_RAD, (float) y * FastMath.DEG_TO_RAD, (float) z * FastMath.DEG_TO_RAD);
- }
- }
- }
- }
- }
- modelDataMap.put(id, data);
- }
-
- private void loadPose(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("BindPose")) {
- BindPoseData data = new BindPoseData();
- data.name = path.substring(0, path.indexOf(0));
- for(FbxElement e : element.children) {
- if(e.id.equals("PoseNode")) {
- NodeTransformData item = new NodeTransformData();
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("Node"))
- item.nodeId = (Long) e2.properties.get(0);
- else if(e2.id.equals("Matrix"))
- item.transform = (double[]) e2.properties.get(0);
- }
- data.list.add(item);
- }
- }
- poseDataMap.put(id, data);
- }
- }
-
- private void loadTexture(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("")) {
- TextureData data = new TextureData();
- data.name = path.substring(0, path.indexOf(0));
- for(FbxElement e : element.children) {
- if(e.id.equals("Type"))
- data.bindType = (String) e.properties.get(0);
- else if(e.id.equals("FileName"))
- data.filename = (String) e.properties.get(0);
- }
- texDataMap.put(id, data);
- }
- }
-
- private void loadImage(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("Clip")) {
- ImageData data = new ImageData();
- data.name = path.substring(0, path.indexOf(0));
- for(FbxElement e : element.children) {
- if(e.id.equals("Type"))
- data.type = (String) e.properties.get(0);
- else if(e.id.equals("FileName"))
- data.filename = (String) e.properties.get(0);
- else if(e.id.equals("RelativeFilename"))
- data.relativeFilename = (String) e.properties.get(0);
- else if(e.id.equals("Content")) {
- if(e.properties.size() > 0)
- data.content = (byte[]) e.properties.get(0);
- }
- }
- imgDataMap.put(id, data);
- }
- }
-
- private void loadDeformer(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String type = (String) element.properties.get(2);
- if(type.equals("Skin")) {
- SkinData skinData = new SkinData();
- for(FbxElement e : element.children) {
- if(e.id.equals("SkinningType"))
- skinData.type = (String) e.properties.get(0);
- }
- skinMap.put(id, skinData);
- } else if(type.equals("Cluster")) {
- ClusterData clusterData = new ClusterData();
- for(FbxElement e : element.children) {
- if(e.id.equals("Indexes"))
- clusterData.indexes = (int[]) e.properties.get(0);
- else if(e.id.equals("Weights"))
- clusterData.weights = (double[]) e.properties.get(0);
- else if(e.id.equals("Transform"))
- clusterData.transform = (double[]) e.properties.get(0);
- else if(e.id.equals("TransformLink"))
- clusterData.transformLink = (double[]) e.properties.get(0);
- }
- clusterMap.put(id, clusterData);
- }
- }
-
- private void loadAnimLayer(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("")) {
- AnimLayer layer = new AnimLayer();
- layer.name = path.substring(0, path.indexOf(0));
- alayerMap.put(id, layer);
- }
- }
-
- private void loadAnimCurve(FbxElement element) {
- long id = (Long) element.properties.get(0);
+ switch(e.id) {
+ case "Geometry":
+ FbxMesh mesh = new FbxMesh(this, e);
+ obj = mesh;
+ if(mesh.geometries != null)
+ geomMap.put(mesh.id, mesh);
+ break;
+ case "Material":
+ obj = new FbxMaterial(this, e);
+ break;
+ case "Model":
+ FbxNode node = new FbxNode(this, e);
+ obj = node;
+ modelMap.put(node.id, node);
+ if(node.isLimb())
+ limbMap.put(node.id, node);
+ break;
+ case "Pose":
+ FbxBindPose pose = new FbxBindPose(this, e);
+ obj = pose;
+ bindMap.put(pose.id, pose);
+ break;
+ case "Texture":
+ obj = new FbxTexture(this, e);
+ break;
+ case "Video":
+ obj = new FbxImage(this, e);
+ break;
+ case "Deformer":
+ obj = loadDeformer(e);
+ break;
+ case "AnimationLayer":
+ FbxObject layer = new FbxObject(this, e);
+ obj = layer;
+ alayerMap.put(layer.id, layer);
+ break;
+ case "AnimationCurve":
+ obj = new FbxAnimCurve(this, e);
+ break;
+ case "AnimationCurveNode":
+ obj = new FbxAnimNode(this, e);
+ break;
+ default:
+ obj = null;
+ //warnings.add("Object with id '" + e.id + "' was ignored");
+ }
+ if(obj != null)
+ allObjects.put(obj.id, obj);
+ }
+ }
+
+ private FbxObject loadDeformer(FbxElement element) {
String type = (String) element.properties.get(2);
- if(type.equals("")) {
- AnimCurveData data = new AnimCurveData();
- for(FbxElement e : element.children) {
- if(e.id.equals("KeyTime"))
- data.keyTimes = (long[]) e.properties.get(0);
- else if(e.id.equals("KeyValueFloat"))
- data.keyValues = (float[]) e.properties.get(0);
- }
- acurveMap.put(id, data);
- }
- }
-
- private void loadAnimNode(FbxElement element) {
- long id = (Long) element.properties.get(0);
- String path = (String) element.properties.get(1);
- String type = (String) element.properties.get(2);
- if(type.equals("")) {
- Double x = null, y = null, z = null;
- for(FbxElement e : element.children) {
- if(e.id.equals("Properties70")) {
- for(FbxElement e2 : e.children) {
- if(e2.id.equals("P")) {
- String propName = (String) e2.properties.get(0);
- if(propName.equals("d|X"))
- x = (Double) e2.properties.get(4);
- else if(propName.equals("d|Y"))
- y = (Double) e2.properties.get(4);
- else if(propName.equals("d|Z"))
- z = (Double) e2.properties.get(4);
- }
- }
- }
- }
- // Load only T R S curve nodes
- if(x != null && y != null && z != null) {
- AnimNode node = new AnimNode();
- node.value = new Vector3f(x.floatValue(), y.floatValue(), z.floatValue());
- node.name = path.substring(0, path.indexOf(0));
- anodeMap.put(id, node);
- }
+ switch(type) {
+ case "Skin":
+ FbxSkin skinData = new FbxSkin(this, element);
+ skinMap.put(skinData.id, skinData);
+ return skinData;
+ case "Cluster":
+ FbxCluster clusterData = new FbxCluster(this, element);
+ return clusterData;
}
+ return null;
}
private void loadConnections(FbxElement element) {
@@ -559,698 +234,65 @@ public class SceneLoader implements AssetLoader {
if(e.id.equals("C")) {
String type = (String) e.properties.get(0);
long objId, refId;
- if(type.equals("OO")) {
+ FbxObject obj, ref;
+ switch(type) {
+ case "OO":
objId = (Long) e.properties.get(1);
refId = (Long) e.properties.get(2);
- List links = refMap.get(objId);
- if(links == null) {
- links = new ArrayList();
- refMap.put(objId, links);
+ obj = allObjects.get(objId);
+ ref = allObjects.get(refId);
+ if(ref != null) {
+ ref.link(obj);
+ } else if(refId == 0) {
+ obj.linkToZero();
}
- links.add(refId);
- } else if(type.equals("OP")) {
+ break;
+ case "OP":
objId = (Long) e.properties.get(1);
refId = (Long) e.properties.get(2);
String propName = (String) e.properties.get(3);
- List props = propMap.get(objId);
- if(props == null) {
- props = new ArrayList();
- propMap.put(objId, props);
- }
- props.add(new PropertyLink(refId, propName));
- }
- }
- }
- }
-
- private Geometry createGeomerty(MeshData data) {
- Mesh mesh = new Mesh();
- mesh.setMode(Mode.Triangles);
- // Since each vertex should contain unique texcoord and normal we should unroll vertex indexing
- // So we don't use VertexBuffer.Type.Index for elements drawing
- // Moreover quads should be triangulated (this increases number of vertices)
- boolean isQuads = false;
- if(data.indices != null) {
- data.iCount = data.indices.length;
- data.srcVertexCount = data.vertices.length / 3;
- // Indices contains negative numbers to define polygon last index
- // Check indices strides to be sure we have triangles or quads
- boolean allTriangles = true;
- boolean allQads = true;
- for(int i = 0; i < data.indices.length; ++i) {
- if(i % 3 == 2) { // Triangle stride
- if(data.indices[i] >= 0)
- allTriangles = false;
- } else {
- if(data.indices[i] < 0)
- allTriangles = false;
- }
- if(i % 4 == 3) { // Quad stride
- if(data.indices[i] >= 0)
- allQads = false;
- } else {
- if(data.indices[i] < 0)
- allQads = false;
- }
- }
- if(allTriangles) {
- isQuads = false;
- data.vCount = data.iCount;
- } else if(allQads) {
- isQuads = true;
- data.vCount = 6 * (data.iCount / 4); // Each quad will be splited into two triangles
- } else
- throw new AssetLoadException("Unsupported PolygonVertexIndex stride");
- data.vertexMap = new int[data.vCount];
- data.indexMap = new int[data.vCount];
- // Unroll index array into vertex mapping
- int n = 0;
- for(int i = 0; i < data.iCount; ++i) {
- int index = data.indices[i];
- if(index < 0) {
- int lastIndex = -(index + 1);
- if(isQuads) {
- data.vertexMap[n + 0] = data.indices[i - 3];
- data.vertexMap[n + 1] = data.indices[i - 2];
- data.vertexMap[n + 2] = data.indices[i - 1];
- data.vertexMap[n + 3] = data.indices[i - 3];
- data.vertexMap[n + 4] = data.indices[i - 1];
- data.vertexMap[n + 5] = lastIndex;
- data.indexMap[n + 0] = (i - 3);
- data.indexMap[n + 1] = (i - 2);
- data.indexMap[n + 2] = (i - 1);
- data.indexMap[n + 3] = (i - 3);
- data.indexMap[n + 4] = (i - 1);
- data.indexMap[n + 5] = (i - 0);
- n += 6;
- } else {
- data.vertexMap[n + 0] = data.indices[i - 2];
- data.vertexMap[n + 1] = data.indices[i - 1];
- data.vertexMap[n + 2] = lastIndex;
- data.indexMap[n + 0] = (i - 2);
- data.indexMap[n + 1] = (i - 1);
- data.indexMap[n + 2] = (i - 0);
- n += 3;
- }
- }
- }
- // Build reverse vertex mapping
- data.reverseVertexMap = new ArrayList>(data.srcVertexCount);
- for(int i = 0; i < data.srcVertexCount; ++i)
- data.reverseVertexMap.add(new ArrayList());
- for(int i = 0; i < data.vCount; ++i) {
- int index = data.vertexMap[i];
- data.reverseVertexMap.get(index).add(i);
- }
- } else {
- // Stub for no vertex indexing (direct mapping)
- data.iCount = data.vCount = data.srcVertexCount;
- data.vertexMap = new int[data.vCount];
- data.indexMap = new int[data.vCount];
- data.reverseVertexMap = new ArrayList>(data.vCount);
- for(int i = 0; i < data.vCount; ++i) {
- data.vertexMap[i] = i;
- data.indexMap[i] = i;
- List reverseIndices = new ArrayList(1);
- reverseIndices.add(i);
- data.reverseVertexMap.add(reverseIndices);
- }
- }
- if(data.vertices != null) {
- // Unroll vertices data array
- FloatBuffer posBuf = BufferUtils.createFloatBuffer(data.vCount * 3);
- mesh.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
- int srcCount = data.vertices.length / 3;
- for(int i = 0; i < data.vCount; ++i) {
- int index = data.vertexMap[i];
- if(index > srcCount)
- throw new AssetLoadException("Invalid vertex mapping. Unexpected lookup vertex " + index + " from " + srcCount);
- float x = (float) data.vertices[3 * index + 0] / unitSize;
- float y = (float) data.vertices[3 * index + 1] / unitSize;
- float z = (float) data.vertices[3 * index + 2] / unitSize;
- posBuf.put(x).put(y).put(z);
- }
- }
- if(data.normals != null) {
- // Unroll normals data array
- FloatBuffer normBuf = BufferUtils.createFloatBuffer(data.vCount * 3);
- mesh.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
- int[] mapping = null;
- if(data.normalsMapping.equals("ByVertice"))
- mapping = data.vertexMap;
- else if(data.normalsMapping.equals("ByPolygonVertex"))
- mapping = data.indexMap;
- int srcCount = data.normals.length / 3;
- for(int i = 0; i < data.vCount; ++i) {
- int index = mapping[i];
- if(index > srcCount)
- throw new AssetLoadException("Invalid normal mapping. Unexpected lookup normal " + index + " from " + srcCount);
- float x = (float) data.normals[3 * index + 0];
- float y = (float) data.normals[3 * index + 1];
- float z = (float) data.normals[3 * index + 2];
- normBuf.put(x).put(y).put(z);
- }
- }
- if(data.tangents != null) {
- // Unroll normals data array
- FloatBuffer tanBuf = BufferUtils.createFloatBuffer(data.vCount * 4);
- mesh.setBuffer(VertexBuffer.Type.Tangent, 4, tanBuf);
- int[] mapping = null;
- if(data.tangentsMapping.equals("ByVertice"))
- mapping = data.vertexMap;
- else if(data.tangentsMapping.equals("ByPolygonVertex"))
- mapping = data.indexMap;
- int srcCount = data.tangents.length / 3;
- for(int i = 0; i < data.vCount; ++i) {
- int index = mapping[i];
- if(index > srcCount)
- throw new AssetLoadException("Invalid tangent mapping. Unexpected lookup tangent " + index + " from " + srcCount);
- float x = (float) data.tangents[3 * index + 0];
- float y = (float) data.tangents[3 * index + 1];
- float z = (float) data.tangents[3 * index + 2];
- tanBuf.put(x).put(y).put(z).put(-1.0f);
- }
- }
- if(data.binormals != null) {
- // Unroll normals data array
- FloatBuffer binormBuf = BufferUtils.createFloatBuffer(data.vCount * 3);
- mesh.setBuffer(VertexBuffer.Type.Binormal, 3, binormBuf);
- int[] mapping = null;
- if(data.binormalsMapping.equals("ByVertice"))
- mapping = data.vertexMap;
- else if(data.binormalsMapping.equals("ByPolygonVertex"))
- mapping = data.indexMap;
- int srcCount = data.binormals.length / 3;
- for(int i = 0; i < data.vCount; ++i) {
- int index = mapping[i];
- if(index > srcCount)
- throw new AssetLoadException("Invalid binormal mapping. Unexpected lookup binormal " + index + " from " + srcCount);
- float x = (float) data.binormals[3 * index + 0];
- float y = (float) data.binormals[3 * index + 1];
- float z = (float) data.binormals[3 * index + 2];
- binormBuf.put(x).put(y).put(z);
- }
- }
- if(data.uv != null) {
- int[] unIndexMap = data.vertexMap;
- if(data.uvIndex != null) {
- int uvIndexSrcCount = data.uvIndex.length;
- if(uvIndexSrcCount != data.iCount)
- throw new AssetLoadException("Invalid number of texcoord index data " + uvIndexSrcCount + " expected " + data.iCount);
- // Unroll UV index array
- unIndexMap = new int[data.vCount];
- int n = 0;
- for(int i = 0; i < data.iCount; ++i) {
- int index = data.uvIndex[i];
- if(isQuads && (i % 4) == 3) {
- unIndexMap[n + 0] = data.uvIndex[i - 3];
- unIndexMap[n + 1] = data.uvIndex[i - 1];
- unIndexMap[n + 2] = index;
- n += 3;
- } else {
- unIndexMap[i] = index;
- }
- }
- }
- // Unroll UV data array
- FloatBuffer tcBuf = BufferUtils.createFloatBuffer(data.vCount * 2);
- mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf);
- int srcCount = data.uv.length / 2;
- for(int i = 0; i < data.vCount; ++i) {
- int index = unIndexMap[i];
- if(index > srcCount)
- throw new AssetLoadException("Invalid texcoord mapping. Unexpected lookup texcoord " + index + " from " + srcCount);
- float u = index >= 0 ? (float) data.uv[2 * index + 0] : 0;
- float v = index >= 0 ? (float) data.uv[2 * index + 1] : 0;
- tcBuf.put(u).put(v);
- }
- }
- mesh.setStatic();
- mesh.updateBound();
- mesh.updateCounts();
- Geometry geom = new Geometry();
- geom.setMesh(mesh);
- return geom;
- }
-
- private Material createMaterial(MaterialData data) {
- Material m = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
- m.setName(data.name);
- data.ambientColor.multLocal(data.ambientFactor);
- data.diffuseColor.multLocal(data.diffuseFactor);
- data.specularColor.multLocal(data.specularFactor);
- m.setColor("Ambient", new ColorRGBA(data.ambientColor.x, data.ambientColor.y, data.ambientColor.z, 1));
- m.setColor("Diffuse", new ColorRGBA(data.diffuseColor.x, data.diffuseColor.y, data.diffuseColor.z, 1));
- m.setColor("Specular", new ColorRGBA(data.specularColor.x, data.specularColor.y, data.specularColor.z, 1));
- m.setFloat("Shininess", data.shininessExponent);
- m.setBoolean("UseMaterialColors", true);
- return m;
- }
-
- private Node createNode(ModelData data) {
- Node model = new Node(data.name);
- model.setLocalTranslation(data.localTranslation);
- model.setLocalRotation(data.localRotation);
- model.setLocalScale(data.localScale);
- return model;
- }
-
- private Limb createLimb(ModelData data) {
- Limb limb = new Limb();
- limb.name = data.name;
- Quaternion rotation = data.preRotation.mult(data.localRotation);
- limb.bindTransform = new Transform(data.localTranslation, rotation, data.localScale);
- return limb;
- }
-
- private BindPose createPose(BindPoseData data) {
- BindPose pose = new BindPose();
- pose.name = data.name;
- for(NodeTransformData item : data.list) {
- Transform t = buildTransform(item.transform);
- //t.getTranslation().divideLocal(unitSize);
- t.getScale().multLocal(unitSize);
- pose.nodeTransforms.put(item.nodeId, t);
- }
- return pose;
- }
-
- private Texture createTexture(TextureData data) {
- Texture tex = new Texture2D();
- tex.setName(data.name);
- return tex;
- }
-
- private Image createImage(ImageData data) {
- Image image = null;
- if(data.filename != null) {
- // Try load by absolute path
- File file = new File(data.filename);
- if(file.exists() && file.isFile()) {
- File dir = new File(file.getParent());
- String locatorPath = dir.getAbsolutePath();
- Texture tex = null;
- try {
- assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
- tex = assetManager.loadTexture(file.getName());
- } catch(Exception e) {
- } finally {
- assetManager.unregisterLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
- }
- if(tex != null)
- image = tex.getImage();
- }
- }
- if(image == null && data.relativeFilename != null) {
- // Try load by relative path
- File dir = new File(sceneFolderName);
- String locatorPath = dir.getAbsolutePath();
- Texture tex = null;
- try {
- assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
- tex = assetManager.loadTexture(data.relativeFilename);
- } catch(Exception e) {
- } finally {
- assetManager.unregisterLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
- }
- if(tex != null)
- image = tex.getImage();
- }
- if(image == null && data.content != null) {
- // Try load from content
- String filename = null;
- if(data.filename != null)
- filename = new File(data.filename).getName();
- if(filename != null && data.relativeFilename != null)
- filename = data.relativeFilename;
- // Filename is required to aquire asset loader by extension
- if(filename != null) {
- String locatorPath = sceneFilename;
- filename = sceneFilename + File.separatorChar + filename; // Unique path
- Texture tex = null;
- try {
- assetManager.registerLocator(locatorPath, com.jme3.scene.plugins.fbx.ContentTextureLocator.class);
- tex = assetManager.loadTexture(new ContentTextureKey(filename, data.content));
- } catch(Exception e) {
- } finally {
- assetManager.unregisterLocator(locatorPath, com.jme3.scene.plugins.fbx.ContentTextureLocator.class);
+ obj = allObjects.get(objId);
+ ref = allObjects.get(refId);
+ if(ref != null)
+ ref.link(obj, propName);
+ break;
}
- if(tex != null)
- image = tex.getImage();
}
}
- if(image == null)
- throw new AssetLoadException("Content not loaded for image " + data.name);
- return image;
- }
-
- private Transform buildTransform(double[] transform) {
- float[] m = new float[transform.length];
- for(int i = 0; i < transform.length; ++i)
- m[i] = (float) transform[i];
- Matrix4f matrix = new Matrix4f(m);
- Vector3f pos = matrix.toTranslationVector();
- Quaternion rot = matrix.toRotationQuat();
- Vector3f scale = matrix.toScaleVector();
- return new Transform(pos, rot, scale);
- }
-
- private Quaternion quatFromBoneAngles(float xAngle, float yAngle, float zAngle) {
- float angle;
- float sinY, sinZ, sinX, cosY, cosZ, cosX;
- angle = zAngle * 0.5f;
- sinZ = FastMath.sin(angle);
- cosZ = FastMath.cos(angle);
- angle = yAngle * 0.5f;
- sinY = FastMath.sin(angle);
- cosY = FastMath.cos(angle);
- angle = xAngle * 0.5f;
- sinX = FastMath.sin(angle);
- cosX = FastMath.cos(angle);
- float cosYXcosZ = cosY * cosZ;
- float sinYXsinZ = sinY * sinZ;
- float cosYXsinZ = cosY * sinZ;
- float sinYXcosZ = sinY * cosZ;
- // For some reason bone space is differ, this is modified formulas
- float w = (cosYXcosZ * cosX + sinYXsinZ * sinX);
- float x = (cosYXcosZ * sinX - sinYXsinZ * cosX);
- float y = (sinYXcosZ * cosX + cosYXsinZ * sinX);
- float z = (cosYXsinZ * cosX - sinYXcosZ * sinX);
- return new Quaternion(x, y, z, w).normalizeLocal();
}
- private Node linkScene() {
+ private void linkScene() {
logger.log(Level.FINE, "Linking scene objects");
long startTime = System.currentTimeMillis();
- Node sceneNode = linkSceneNodes();
- linkMaterials();
- linkMeshes(sceneNode);
- linkSkins(sceneNode);
- linkAnimations();
+ applySkinning();
+ buildAnimations();
+ for(FbxMesh mesh : geomMap.values())
+ mesh.clearMaterials();
+ // Remove bones from node structures : JME creates attach node by itself
+ for(FbxNode limb : limbMap.values())
+ limb.node.removeFromParent();
long estimatedTime = System.currentTimeMillis() - startTime;
logger.log(Level.FINE, "Linking done in {0} ms", estimatedTime);
- return sceneNode;
}
- private Node linkSceneNodes() {
- Node sceneNode = new Node(sceneName + "-scene");
- // Build mesh nodes
- for(long nodeId : modelDataMap.keySet()) {
- ModelData data = modelDataMap.get(nodeId);
- Node node = createNode(data);
- modelMap.put(nodeId, node);
- }
- // Link model nodes into scene
- for(long modelId : modelMap.keySet()) {
- List refs = refMap.get(modelId);
- if(refs == null)
- continue;
- Node model = modelMap.get(modelId);
- for(long refId : refs) {
- Node rootNode = (refId != 0) ? modelMap.get(refId) : sceneNode;
- if(rootNode != null)
- rootNode.attachChild(model);
- }
- }
- // Build bind poses
- for(long poseId : poseDataMap.keySet()) {
- BindPoseData data = poseDataMap.get(poseId);
- BindPose pose = createPose(data);
- if(pose != null)
- bindMap.put(poseId, pose);
- }
- // Apply bind poses
- for(BindPose pose : bindMap.values()) {
- for(long nodeId : pose.nodeTransforms.keySet()) {
- Node model = modelMap.get(nodeId);
- if(model != null) {
- Transform t = pose.nodeTransforms.get(nodeId);
- model.setLocalTransform(t);
- }
- }
- }
- return sceneNode;
- }
-
- private void linkMaterials() {
- // Build materials
- for(long matId : matDataMap.keySet()) {
- MaterialData data = matDataMap.get(matId);
- Material material = createMaterial(data);
- if(material != null)
- matMap.put(matId, material);
- }
- // Build textures
- for(long texId : texDataMap.keySet()) {
- TextureData data = texDataMap.get(texId);
- Texture tex = createTexture(data);
- if(tex != null)
- texMap.put(texId, tex);
- }
- // Build Images
- for(long imgId : imgDataMap.keySet()) {
- ImageData data = imgDataMap.get(imgId);
- Image img = createImage(data);
- if(img != null)
- imgMap.put(imgId, img);
- }
- // Link images to textures
- for(long imgId : imgMap.keySet()) {
- List refs = refMap.get(imgId);
- if(refs == null)
- continue;
- Image img = imgMap.get(imgId);
- for(long refId : refs) {
- Texture tex = texMap.get(refId);
- if(tex != null)
- tex.setImage(img);
- }
- }
- // Link textures to material maps
- for(long texId : texMap.keySet()) {
- List props = propMap.get(texId);
- if(props == null)
- continue;
- Texture tex = texMap.get(texId);
- for(PropertyLink prop : props) {
- Material mat = matMap.get(prop.ref);
- if(mat != null) {
- if(prop.propName.equals("DiffuseColor")) {
- mat.setTexture("DiffuseMap", tex);
- mat.setColor("Diffuse", ColorRGBA.White);
- } else if(prop.propName.equals("SpecularColor")) {
- mat.setTexture("SpecularMap", tex);
- mat.setColor("Specular", ColorRGBA.White);
- } else if(prop.propName.equals("NormalMap"))
- mat.setTexture("NormalMap", tex);
- }
- }
- }
- }
-
- private void linkMeshes(Node sceneNode) {
- // Build meshes
- for(long meshId : meshDataMap.keySet()) {
- MeshData data = meshDataMap.get(meshId);
- Geometry geom = createGeomerty(data);
- if(geom != null)
- geomMap.put(meshId, geom);
- }
- // Link meshes to models
- for(long geomId : geomMap.keySet()) {
- List refs = refMap.get(geomId);
- if(refs == null)
- continue;
- Geometry geom = geomMap.get(geomId);
- for(long refId : refs) {
- Node rootNode = (refId != 0) ? modelMap.get(refId) : sceneNode;
- if(rootNode != null) {
- geom.setName(rootNode.getName() + "-mesh");
- geom.updateModelBound();
- rootNode.attachChild(geom);
- break;
- }
- }
- }
- // Link materials to meshes
- for(long matId : matMap.keySet()) {
- List refs = refMap.get(matId);
- if(refs == null)
- continue;
- Material mat = matMap.get(matId);
- for(long refId : refs) {
- Node rootNode = modelMap.get(refId);
- if(rootNode != null) {
- for(Spatial child : rootNode.getChildren())
- child.setMaterial(mat);
- }
- }
- }
- }
-
- private void linkSkins(Node sceneNode) {
- // Build skeleton limbs
- for(long nodeId : modelDataMap.keySet()) {
- ModelData data = modelDataMap.get(nodeId);
- if(data.type.equals("LimbNode")) {
- Limb limb = createLimb(data);
- limbMap.put(nodeId, limb);
- }
- }
- if(limbMap.size() == 0)
+ private void applySkinning() {
+ for(FbxBindPose pose : bindMap.values())
+ pose.fillBindTransforms();
+ if(limbMap == null)
return;
- // Build skeleton bones
- Map bones = new HashMap();
- for(long limbId : limbMap.keySet()) {
- Limb limb = limbMap.get(limbId);
- Bone bone = new Bone(limb.name);
- Transform t = limb.bindTransform;
- bone.setBindTransforms(t.getTranslation(), t.getRotation(), t.getScale());
- bones.put(limbId, bone);
- }
- // Attach bones to roots
- for(long limbId : limbMap.keySet()) {
- List refs = refMap.get(limbId);
- if(refs == null)
- continue;
- // Find root limb
- long rootLimbId = 0L;
- for(long refId : refs) {
- if(limbMap.containsKey(refId)) {
- rootLimbId = refId;
- break;
- }
- }
- if(rootLimbId != 0L) {
- Bone bone = bones.get(limbId);
- Bone root = bones.get(rootLimbId);
- root.addChild(bone);
+ List bones = new ArrayList<>();
+ for(FbxNode limb : limbMap.values()) {
+ if(limb.bone != null) {
+ bones.add(limb.bone);
+ limb.buildBindPoseBoneTransform();
}
}
- // Link bone clusters to skin
- for(long clusterId : clusterMap.keySet()) {
- List refs = refMap.get(clusterId);
- if(refs == null)
- continue;
- for(long skinId : refs) {
- if(skinMap.containsKey(skinId)) {
- ClusterData data = clusterMap.get(clusterId);
- data.skinId = skinId;
- break;
- }
- }
- }
- // Build the skeleton
- this.skeleton = new Skeleton(bones.values().toArray(new Bone[0]));
+ skeleton = new Skeleton(bones.toArray(new Bone[bones.size()]));
skeleton.setBindingPose();
- for(long limbId : bones.keySet()) {
- Bone bone = bones.get(limbId);
- Limb limb = limbMap.get(limbId);
- limb.boneIndex = skeleton.getBoneIndex(bone);
- }
- // Assign bones skinning to meshes
- for(long skinId : skinMap.keySet()) {
- // Find mesh by skin
- Mesh mesh = null;
- MeshData meshData = null;
- for(long meshId : refMap.get(skinId)) {
- Geometry g = geomMap.get(meshId);
- if(g != null) {
- meshData = meshDataMap.get(meshId);
- mesh = g.getMesh();
- break;
- }
- }
- // Bind skinning indexes and weight
- if(mesh != null && meshData != null) {
- // Create bone buffers
- FloatBuffer boneWeightData = BufferUtils.createFloatBuffer(meshData.vCount * 4);
- ByteBuffer boneIndicesData = BufferUtils.createByteBuffer(meshData.vCount * 4);
- mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, boneWeightData);
- mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, boneIndicesData);
- mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(Usage.CpuOnly);
- mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(Usage.CpuOnly);
- VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
- VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
- indicesHW.setUsage(Usage.CpuOnly); // Setting usage to CpuOnly so that the buffer is not send empty to the GPU
- weightsHW.setUsage(Usage.CpuOnly);
- mesh.setBuffer(weightsHW);
- mesh.setBuffer(indicesHW);
- // Accumulate skin bones influence into mesh buffers
- boolean bonesLimitExceeded = false;
- for(long limbId : bones.keySet()) {
- // Search bone cluster for the given limb and skin
- ClusterData cluster = null;
- for(long clusterId : refMap.get(limbId)) {
- ClusterData data = clusterMap.get(clusterId);
- if(data != null && data.skinId == skinId) {
- cluster = data;
- break;
- }
- }
- if(cluster == null || cluster.indexes == null || cluster.weights == null || cluster.indexes.length != cluster.weights.length)
- continue;
- Limb limb = limbMap.get(limbId);
- if(limb.boneIndex > 255)
- throw new AssetLoadException("Bone index can't be packed into byte");
- for(int i = 0; i < cluster.indexes.length; ++i) {
- int vertexIndex = cluster.indexes[i];
- if(vertexIndex >= meshData.reverseVertexMap.size())
- throw new AssetLoadException("Invalid skinning vertex index. Unexpected index lookup " + vertexIndex + " from " + meshData.reverseVertexMap.size());
- List dstVertices = meshData.reverseVertexMap.get(vertexIndex);
- for(int v : dstVertices) {
- // Append bone index and weight to vertex
- int offset;
- float w = 0;
- for(offset = v * 4; offset < v * 4 + 4; ++offset) {
- w = boneWeightData.get(offset);
- if(w == 0)
- break;
- }
- if(w == 0) {
- boneWeightData.put(offset, (float) cluster.weights[i]);
- boneIndicesData.put(offset, (byte) limb.boneIndex);
- } else {
- // TODO It would be nice to discard small weight to accumulate more heavy influence
- bonesLimitExceeded = true;
- }
- }
- }
- }
- if(bonesLimitExceeded)
- logger.log(Level.WARNING, "Skinning support max 4 bone per vertex. Exceeding data will be discarded.");
- // Postprocess bones weights
- int maxWeightsPerVert = 0;
- boneWeightData.rewind();
- for(int v = 0; v < meshData.vCount; v++) {
- float w0 = boneWeightData.get();
- float w1 = boneWeightData.get();
- float w2 = boneWeightData.get();
- float w3 = boneWeightData.get();
- if(w3 != 0) {
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
- } else if(w2 != 0) {
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
- } else if(w1 != 0) {
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
- } else if(w0 != 0) {
- maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
- }
- float sum = w0 + w1 + w2 + w3;
- if(sum != 1f) {
- // normalize weights
- float mult = (sum != 0) ? (1f / sum) : 0;
- boneWeightData.position(v * 4);
- boneWeightData.put(w0 * mult);
- boneWeightData.put(w1 * mult);
- boneWeightData.put(w2 * mult);
- boneWeightData.put(w3 * mult);
- }
- }
- mesh.setMaxNumWeights(maxWeightsPerVert);
- mesh.generateBindPose(true);
- }
- }
+ for(FbxNode limb : limbMap.values())
+ limb.setSkeleton(skeleton);
+ for(FbxSkin skin : skinMap.values())
+ skin.generateSkinning();
// Attach controls
animControl = new AnimControl(skeleton);
sceneNode.addControl(animControl);
@@ -1258,45 +300,20 @@ public class SceneLoader implements AssetLoader {
sceneNode.addControl(control);
}
- private void linkAnimations() {
+ private void buildAnimations() {
if(skeleton == null)
return;
- if(animList == null || animList.list.size() == 0)
- return;
- // Link curves to nodes
- for(long curveId : acurveMap.keySet()) {
- List props = propMap.get(curveId);
- if(props == null)
- continue;
- AnimCurveData acurve = acurveMap.get(curveId);
- for(PropertyLink prop : props) {
- AnimNode anode = anodeMap.get(prop.ref);
- if(anode != null) {
- if(prop.propName.equals("d|X"))
- anode.xCurve = acurve;
- else if(prop.propName.equals("d|Y"))
- anode.yCurve = acurve;
- else if(prop.propName.equals("d|Z"))
- anode.zCurve = acurve;
- }
- }
- }
- // Link nodes to layers
- for(long nodeId : anodeMap.keySet()) {
- List refs = refMap.get(nodeId);
- if(refs == null)
- continue;
- for(long layerId : refs) {
- if(alayerMap.containsKey(layerId)) {
- AnimNode anode = anodeMap.get(nodeId);
- anode.layerId = layerId;
- break;
- }
+ if(animList == null || animList.list.size() == 0) {
+ animList = new AnimationList();
+ for(long layerId : alayerMap.keySet()) {
+ FbxObject layer = alayerMap.get(layerId);
+ animList.add(layer.name, layer.name, 0, -1);
}
}
// Extract aminations
HashMap anims = new HashMap();
for(AnimInverval animInfo : animList.list) {
+ float realLength = 0;
float length = (animInfo.lastFrame - animInfo.firstFrame) / this.animFrameRate;
float animStart = animInfo.firstFrame / this.animFrameRate;
float animStop = animInfo.lastFrame / this.animFrameRate;
@@ -1304,65 +321,50 @@ public class SceneLoader implements AssetLoader {
// Search source layer for animation nodes
long sourceLayerId = 0L;
for(long layerId : alayerMap.keySet()) {
- AnimLayer layer = alayerMap.get(layerId);
+ FbxObject layer = alayerMap.get(layerId);
if(layer.name.equals(animInfo.layerName)) {
sourceLayerId = layerId;
break;
}
}
- // Assign animation nodes to limbs
- for(Limb limb : limbMap.values()) {
- limb.animTranslation = null;
- limb.animRotation = null;
- limb.animScale = null;
- }
- for(long nodeId : anodeMap.keySet()) {
- List props = propMap.get(nodeId);
- if(props == null)
- continue;
- AnimNode anode = anodeMap.get(nodeId);
- if(sourceLayerId != 0L && anode.layerId != sourceLayerId)
- continue; // filter node
- for(PropertyLink prop : props) {
- Limb limb = limbMap.get(prop.ref);
- if(limb != null) {
- if(prop.propName.equals("Lcl Translation"))
- limb.animTranslation = anode;
- else if(prop.propName.equals("Lcl Rotation"))
- limb.animRotation = anode;
- else if(prop.propName.equals("Lcl Scaling"))
- limb.animScale = anode;
- }
- }
- }
// Build bone tracks
- for(Limb limb : limbMap.values()) {
- long[] keyTimes = null;
- boolean haveTranslation = (limb.animTranslation != null && limb.animTranslation.xCurve != null && limb.animTranslation.yCurve != null && limb.animTranslation.zCurve != null);
- boolean haveRotation = (limb.animRotation != null && limb.animRotation.xCurve != null && limb.animRotation.yCurve != null && limb.animRotation.zCurve != null);
- boolean haveScale = (limb.animScale != null && limb.animScale.xCurve != null && limb.animScale.yCurve != null && limb.animScale.zCurve != null);
- // Search key time array
+ for(FbxNode limb : limbMap.values()) {
+ // Animation channels may have different keyframes (non-baked animation).
+ // So we have to restore intermediate values for all channels cause of JME requires
+ // a bone track as a single channel with collective transformation for each keyframe
+ Set stamps = new TreeSet(); // Sorted unique timestamps
+ FbxAnimNode animTranslation = limb.animTranslation(sourceLayerId);
+ FbxAnimNode animRotation = limb.animRotation(sourceLayerId);
+ FbxAnimNode animScale = limb.animScale(sourceLayerId);
+ boolean haveTranslation = haveAnyChannel(animTranslation);
+ boolean haveRotation = haveAnyChannel(animRotation);
+ boolean haveScale = haveAnyChannel(animScale);
+ // Collect keyframes stamps
if(haveTranslation)
- keyTimes = limb.animTranslation.xCurve.keyTimes;
- else if(haveRotation)
- keyTimes = limb.animRotation.xCurve.keyTimes;
- else if(haveScale)
- keyTimes = limb.animScale.xCurve.keyTimes;
- if(keyTimes == null)
+ animTranslation.exportTimes(stamps);
+ if(haveRotation)
+ animRotation.exportTimes(stamps);
+ if(haveScale)
+ animScale.exportTimes(stamps);
+ if(stamps.isEmpty())
continue;
+ long[] keyTimes = new long[stamps.size()];
+ int cnt = 0;
+ for(long t : stamps)
+ keyTimes[cnt++] = t;
// Calculate keys interval by animation time interval
- int firstKey = 0;
- int lastKey = keyTimes.length - 1;
+ int firstKeyIndex = 0;
+ int lastKeyIndex = keyTimes.length - 1;
for(int i = 0; i < keyTimes.length; ++i) {
float time = (float) (((double) keyTimes[i]) * secondsPerUnit); // Translate into seconds
if(time <= animStart)
- firstKey = i;
- if(time >= animStop) {
- lastKey = i;
+ firstKeyIndex = i;
+ if(time >= animStop && animStop >= 0) {
+ lastKeyIndex = i;
break;
}
}
- int keysCount = lastKey - firstKey + 1;
+ int keysCount = lastKeyIndex - firstKeyIndex + 1;
if(keysCount <= 0)
continue;
float[] times = new float[keysCount];
@@ -1371,43 +373,42 @@ public class SceneLoader implements AssetLoader {
Vector3f[] scales = null;
// Calculate keyframes times
for(int i = 0; i < keysCount; ++i) {
- int keyIndex = firstKey + i;
+ int keyIndex = firstKeyIndex + i;
float time = (float) (((double) keyTimes[keyIndex]) * secondsPerUnit); // Translate into seconds
times[i] = time - animStart;
+ realLength = Math.max(realLength, times[i]);
}
// Load keyframes from animation curves
if(haveTranslation) {
for(int i = 0; i < keysCount; ++i) {
- int keyIndex = firstKey + i;
- float x = limb.animTranslation.xCurve.keyValues[keyIndex] - limb.animTranslation.value.x;
- float y = limb.animTranslation.yCurve.keyValues[keyIndex] - limb.animTranslation.value.y;
- float z = limb.animTranslation.zCurve.keyValues[keyIndex] - limb.animTranslation.value.z;
- translations[i] = new Vector3f(x, y, z).divideLocal(unitSize);
+ int keyIndex = firstKeyIndex + i;
+ FbxAnimNode n = animTranslation;
+ Vector3f tvec = n.getValue(keyTimes[keyIndex], n.value).subtractLocal(n.value); // Why do it here but not in other places? FBX magic?
+ translations[i] = tvec.divideLocal(unitSize);
}
} else {
for(int i = 0; i < keysCount; ++i)
- translations[i] = new Vector3f();
+ translations[i] = Vector3f.ZERO;
}
+ RotationOrder ro = RotationOrder.EULER_XYZ;
if(haveRotation) {
for(int i = 0; i < keysCount; ++i) {
- int keyIndex = firstKey + i;
- float x = limb.animRotation.xCurve.keyValues[keyIndex];
- float y = limb.animRotation.yCurve.keyValues[keyIndex];
- float z = limb.animRotation.zCurve.keyValues[keyIndex];
- rotations[i] = new Quaternion().fromAngles(FastMath.DEG_TO_RAD * x, FastMath.DEG_TO_RAD * y, FastMath.DEG_TO_RAD * z);
+ int keyIndex = firstKeyIndex + i;
+ FbxAnimNode n = animRotation;
+ Vector3f tvec = n.getValue(keyTimes[keyIndex], n.value);
+ rotations[i] = ro.rotate(tvec);
}
} else {
for(int i = 0; i < keysCount; ++i)
- rotations[i] = new Quaternion();
+ rotations[i] = Quaternion.IDENTITY;
}
if(haveScale) {
scales = new Vector3f[keysCount];
for(int i = 0; i < keysCount; ++i) {
- int keyIndex = firstKey + i;
- float x = limb.animScale.xCurve.keyValues[keyIndex];
- float y = limb.animScale.yCurve.keyValues[keyIndex];
- float z = limb.animScale.zCurve.keyValues[keyIndex];
- scales[i] = new Vector3f(x, y, z);
+ int keyIndex = firstKeyIndex + i;
+ FbxAnimNode n = animScale;
+ Vector3f tvec = n.getValue(keyTimes[keyIndex], n.value);
+ scales[i] = tvec;
}
}
BoneTrack track = null;
@@ -1417,166 +418,48 @@ public class SceneLoader implements AssetLoader {
track = new BoneTrack(limb.boneIndex, times, translations, rotations);
anim.addTrack(track);
}
+ if(realLength != length && animInfo.lastFrame == -1) {
+ Track[] tracks = anim.getTracks();
+ if(tracks == null || tracks.length == 0)
+ continue;
+ anim = new Animation(animInfo.name, realLength);
+ for(Track track : tracks)
+ anim.addTrack(track);
+ }
anims.put(anim.getName(), anim);
}
animControl.setAnimations(anims);
}
+ private static boolean haveAnyChannel(FbxAnimNode anims) {
+ return anims != null && anims.haveAnyChannel();
+ }
+
private void releaseObjects() {
- meshDataMap.clear();
- matDataMap.clear();
- texDataMap.clear();
- imgDataMap.clear();
- modelDataMap.clear();
- poseDataMap.clear();
+ // Reset settings
+ unitSize = 1;
+ animFrameRate = 30;
+ xAxis = 1;
+ yAxis = 1;
+ zAxis = 1;
+ // Clear cache
+ warnings.clear();
+ animList = null;
+ sceneName = null;
+ sceneFilename = null;
+ sceneFolderName = null;
+ assetManager = null;
+ currentAssetInfo = null;
+ // Clear objects
+ allObjects.clear();
skinMap.clear();
- clusterMap.clear();
- acurveMap.clear();
- anodeMap.clear();
alayerMap.clear();
- refMap.clear();
- propMap.clear();
modelMap.clear();
limbMap.clear();
bindMap.clear();
geomMap.clear();
- matMap.clear();
- texMap.clear();
- imgMap.clear();
skeleton = null;
animControl = null;
- animList = null;
- }
-
- private class MeshData {
- double[] vertices;
- int[] indices;
- int[] edges;
- String normalsMapping;
- String normalsReference;
- double[] normals;
- String tangentsMapping;
- String tangentsReference;
- double[] tangents;
- String binormalsMapping;
- String binormalsReference;
- double[] binormals;
- String uvMapping;
- String uvReference;
- double[] uv;
- int[] uvIndex;
- String smoothingMapping;
- String smoothingReference;
- int[] smoothing;
- String materialsMapping;
- String materialsReference;
- int[] materials;
- // Build helping data
- int iCount;
- int vCount;
- int srcVertexCount;
- int[] vertexMap; // Target vertex -> source vertex
- List> reverseVertexMap; // source vertex -> list of target vertices
- int[] indexMap; // Target vertex -> source index
+ sceneNode = null;
}
-
- private class ModelData {
- String name;
- String type;
- Vector3f localTranslation = new Vector3f();
- Quaternion localRotation = new Quaternion();
- Vector3f localScale = new Vector3f(Vector3f.UNIT_XYZ);
- Quaternion preRotation = new Quaternion();
- }
-
- private class NodeTransformData {
- long nodeId;
- double[] transform;
- }
-
- private class BindPoseData {
- String name;
- List list = new LinkedList();
- }
-
- private class BindPose {
- String name;
- Map nodeTransforms = new HashMap();
- }
-
- private class MaterialData {
- String name;
- String shadingModel = "phong";
- Vector3f ambientColor = new Vector3f(0.2f, 0.2f, 0.2f);
- float ambientFactor = 1.0f;
- Vector3f diffuseColor = new Vector3f(0.8f, 0.8f, 0.8f);
- float diffuseFactor = 1.0f;
- Vector3f specularColor = new Vector3f(0.2f, 0.2f, 0.2f);
- float specularFactor = 1.0f;
- float shininessExponent = 20.0f;
- }
-
- private class TextureData {
- String name;
- String bindType;
- String filename;
- }
-
- private class ImageData {
- String name;
- String type;
- String filename;
- String relativeFilename;
- byte[] content;
- }
-
- private class Limb {
- String name;
- Transform bindTransform;
- int boneIndex;
- AnimNode animTranslation;
- AnimNode animRotation;
- AnimNode animScale;
- }
-
- private class ClusterData {
- int[] indexes;
- double[] weights;
- double[] transform;
- double[] transformLink;
- long skinId;
- }
-
- private class SkinData {
- String type;
- }
-
- private class AnimCurveData {
- long[] keyTimes;
- float[] keyValues;
- }
-
- private class AnimLayer {
- String name;
- }
-
- private class AnimNode {
- String name;
- Vector3f value;
- AnimCurveData xCurve;
- AnimCurveData yCurve;
- AnimCurveData zCurve;
- long layerId;
- }
-
- private class PropertyLink {
- long ref;
- String propName;
-
- public PropertyLink(long id, String prop) {
- this.ref = id;
- this.propName = prop;
- }
- }
-
}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimCurve.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimCurve.java
new file mode 100644
index 000000000..395216156
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimCurve.java
@@ -0,0 +1,49 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxAnimCurve extends FbxObject {
+
+ public long[] keyTimes;
+ public float[] keyValues;
+ public float defaultValue = 0.0f;
+
+ public FbxAnimCurve(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "KeyTime":
+ keyTimes = (long[]) e.properties.get(0);
+ break;
+ case "KeyValueFloat":
+ keyValues = (float[]) e.properties.get(0);
+ break;
+ case "Default":
+ defaultValue = ((Number) e.properties.get(0)).floatValue();
+ break;
+ }
+ }
+ }
+
+ public float getValue(long time) {
+ // Search animation interval
+ for(int i = 0; i < keyTimes.length; ++i) {
+ if(keyTimes[i] == time) { // hit the keyframe
+ return keyValues[i];
+ } else if(keyTimes[i] > time) {
+ if(i == 0) { // left from the whole range
+ return defaultValue;//keyValues[0];
+ } else {
+ // Interpolate between two keyframes
+ float dt = (float) (keyTimes[i] - keyTimes[i - 1]);
+ float dtInt = (float) (time - keyTimes[i - 1]);
+ float dv = keyValues[i] - keyValues[i - 1];
+ return keyValues[i - 1] + dv * (dtInt / dt);
+ }
+ }
+ }
+ // right from the whole range
+ return defaultValue;//keyValues[keyValues.length - 1];
+ }
+}
\ No newline at end of file
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimNode.java
new file mode 100644
index 000000000..7febc92d1
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxAnimNode.java
@@ -0,0 +1,86 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.util.Collection;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxAnimNode extends FbxObject {
+
+ public Vector3f value;
+ public FbxAnimCurve xCurve;
+ public FbxAnimCurve yCurve;
+ public FbxAnimCurve zCurve;
+ public long layerId;
+
+ public FbxAnimNode(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ if(type.equals("")) {
+ Double x = null, y = null, z = null;
+ for(FbxElement e2 : element.getFbxProperties()) {
+ String propName = (String) e2.properties.get(0);
+ switch(propName) {
+ case "d|X":
+ x = (Double) e2.properties.get(4);
+ break;
+ case "d|Y":
+ y = (Double) e2.properties.get(4);
+ break;
+ case "d|Z":
+ z = (Double) e2.properties.get(4);
+ break;
+ }
+ }
+ // Load only T R S curve nodes
+ if(x != null && y != null && z != null)
+ value = new Vector3f(x.floatValue(), y.floatValue(), z.floatValue());
+ }
+ }
+
+ @Override
+ public void link(FbxObject otherObject, String propertyName) {
+ if(otherObject instanceof FbxAnimCurve) {
+ FbxAnimCurve curve = (FbxAnimCurve) otherObject;
+ switch(propertyName) {
+ case "d|X":
+ xCurve = curve;
+ break;
+ case "d|Y":
+ yCurve = curve;
+ break;
+ case "d|Z":
+ zCurve = curve;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void link(FbxObject otherObject) {
+ layerId = otherObject.id;
+ }
+
+ public boolean haveAnyChannel() {
+ return xCurve != null || yCurve != null || zCurve != null;
+ }
+
+ public void exportTimes(Collection stamps) {
+ if(xCurve != null)
+ for(long t : xCurve.keyTimes)
+ stamps.add(t);
+ if(yCurve != null)
+ for(long t : yCurve.keyTimes)
+ stamps.add(t);
+ if(zCurve != null)
+ for(long t : zCurve.keyTimes)
+ stamps.add(t);
+ }
+
+ public Vector3f getValue(long time, Vector3f defaultValue) {
+ float xValue = (xCurve != null) ? xCurve.getValue(time) : defaultValue.x;
+ float yValue = (yCurve != null) ? yCurve.getValue(time) : defaultValue.y;
+ float zValue = (zCurve != null) ? zCurve.getValue(time) : defaultValue.z;
+ return new Vector3f(xValue, yValue, zValue);
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxBindPose.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxBindPose.java
new file mode 100644
index 000000000..1170f9718
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxBindPose.java
@@ -0,0 +1,55 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxBindPose extends FbxObject {
+
+ public Map nodeTransforms = new HashMap<>();
+
+ public FbxBindPose(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ if(type.equals("BindPose")) {
+ for(FbxElement e : element.children) {
+ if(e.id.equals("PoseNode")) {
+ long nodeId = 0;
+ double[] transform = null;
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "Node":
+ nodeId = (Long) e2.properties.get(0);
+ break;
+ case "Matrix":
+ transform = (double[]) e2.properties.get(0);
+ break;
+ }
+ }
+ Matrix4f t = buildTransform(transform);
+ t.scale(new Vector3f(scene.unitSize, scene.unitSize, scene.unitSize));
+ nodeTransforms.put(nodeId, t);
+ }
+ }
+ }
+ }
+
+ public void fillBindTransforms() {
+ for(long nodeId : nodeTransforms.keySet()) {
+ FbxNode node = scene.modelMap.get(nodeId);
+ node.bindTransform = nodeTransforms.get(nodeId).clone();
+ }
+ }
+
+ private static Matrix4f buildTransform(double[] transform) {
+ float[] m = new float[transform.length];
+ for(int i = 0; i < transform.length; ++i)
+ m[i] = (float) transform[i];
+ Matrix4f matrix = new Matrix4f();
+ matrix.set(m, false);
+ return matrix;
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxCluster.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxCluster.java
new file mode 100644
index 000000000..354354567
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxCluster.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxCluster extends FbxObject {
+
+ public int[] indexes;
+ public double[] weights;
+ public double[] transform;
+ public double[] transformLink;
+ public FbxSkin skin;
+
+ public FbxCluster(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "Indexes":
+ indexes = (int[]) e.properties.get(0);
+ break;
+ case "Weights":
+ weights = (double[]) e.properties.get(0);
+ break;
+ case "Transform":
+ transform = (double[]) e.properties.get(0);
+ break;
+ case "TransformLink":
+ transformLink = (double[]) e.properties.get(0);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void link(FbxObject child) {
+ if(child instanceof FbxNode) {
+ FbxNode limb = (FbxNode) child;
+ limb.skinToCluster.put(skin.id, this);
+ skin.bones.add(limb);
+ }
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java
new file mode 100644
index 000000000..0b50aa163
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java
@@ -0,0 +1,123 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.io.File;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.image.ColorSpace;
+import com.jme3.util.BufferUtils;
+import com.jme3.scene.plugins.fbx.ContentTextureKey;
+import com.jme3.scene.plugins.fbx.ContentTextureLocator;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxImage extends FbxObject {
+
+ String filename;
+ String relativeFilename;
+ byte[] content;
+ String imageType;
+
+ public Image image;
+
+ public FbxImage(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ if(type.equals("Clip")) {
+ for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "Type":
+ imageType = (String) e.properties.get(0);
+ break;
+ case "Filename":
+ case "FileName":
+ filename = (String) e.properties.get(0);
+ break;
+ case "RelativeFilename":
+ relativeFilename = (String) e.properties.get(0);
+ break;
+ case "Content":
+ if(e.properties.size() > 0)
+ content = (byte[]) e.properties.get(0);
+ break;
+ }
+ }
+ image = createImage();
+ }
+ }
+
+
+ private Image createImage() {
+ AssetManager assetManager = scene.assetManager;
+ Image image = null;
+ if(filename != null) {
+ // Try load by absolute path
+ File file = new File(filename);
+ if(file.exists() && file.isFile()) {
+ File dir = new File(file.getParent());
+ String locatorPath = dir.getAbsolutePath();
+ Texture tex = null;
+ try {
+ assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
+ tex = assetManager.loadTexture(file.getName());
+ } catch(Exception e) {} finally {
+ assetManager.unregisterLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
+ }
+ if(tex != null)
+ image = tex.getImage();
+ }
+ }
+ if(image == null && relativeFilename != null) {
+ // Try load by relative path
+ File dir = new File(scene.sceneFolderName);
+ String locatorPath = dir.getAbsolutePath();
+ Texture tex = null;
+ try {
+ assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
+ tex = assetManager.loadTexture(relativeFilename);
+ } catch(Exception e) {} finally {
+ assetManager.unregisterLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class);
+ }
+ if(tex != null)
+ image = tex.getImage();
+ }
+ if(image == null && content != null) {
+ // Try load from content
+ String filename = null;
+ if(this.filename != null)
+ filename = new File(this.filename).getName();
+ if(filename != null && this.relativeFilename != null)
+ filename = this.relativeFilename;
+ // Filename is required to aquire asset loader by extension
+ if(filename != null) {
+ String locatorPath = scene.sceneFilename;
+ filename = scene.sceneFilename + File.separatorChar + filename; // Unique path
+ Texture tex = null;
+ try {
+ assetManager.registerLocator(locatorPath, ContentTextureLocator.class);
+ tex = assetManager.loadTexture(new ContentTextureKey(filename, content));
+ } catch(Exception e) {} finally {
+ assetManager.unregisterLocator(locatorPath, ContentTextureLocator.class);
+ }
+ if(tex != null)
+ image = tex.getImage();
+ }
+ }
+ if(image == null) {
+ // Try to load from files near
+ if(relativeFilename != null) {
+ String[] split = relativeFilename.split("[\\\\/]");
+ String filename = split[split.length - 1];
+ Texture tex = null;
+ try {
+ tex = assetManager.loadTexture(new ContentTextureKey(scene.currentAssetInfo.getKey().getFolder() + filename, content));
+ } catch(Exception e) {}
+ if(tex != null)
+ image = tex.getImage();
+ }
+ }
+ if(image == null)
+ return new Image(Image.Format.RGB8, 1, 1, BufferUtils.createByteBuffer((int) ((long) 1 * (long) 1 * (long) Image.Format.RGB8.getBitsPerPixel() / 8L)), ColorSpace.Linear);
+ return image;
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMaterial.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMaterial.java
new file mode 100644
index 000000000..c16d253ad
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMaterial.java
@@ -0,0 +1,113 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxMaterial extends FbxObject {
+
+ public String shadingModel = "phong";
+ public Vector3f ambientColor = new Vector3f(0.2f, 0.2f, 0.2f);
+ public float ambientFactor = 1.0f;
+ public Vector3f diffuseColor = new Vector3f(0.8f, 0.8f, 0.8f);
+ public float diffuseFactor = 1.0f;
+ public Vector3f specularColor = new Vector3f(0.2f, 0.2f, 0.2f);
+ public float specularFactor = 1.0f;
+ public float shininessExponent = 1.0f;
+
+ public Material material;
+
+ public FbxMaterial(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ if(type.equals("")) {
+ for(FbxElement e : element.children) {
+ if(e.id.equals("ShadingModel")) {
+ shadingModel = (String) e.properties.get(0);
+ } else if(e.id.equals("Properties70")) {
+ for(FbxElement e2 : e.children) {
+ if(e2.id.equals("P")) {
+ double x, y, z;
+ String propName = (String) e2.properties.get(0);
+ switch(propName) {
+ case "AmbientColor":
+ x = (Double) e2.properties.get(4);
+ y = (Double) e2.properties.get(5);
+ z = (Double) e2.properties.get(6);
+ ambientColor.set((float) x, (float) y, (float) z);
+ break;
+ case "AmbientFactor":
+ x = (Double) e2.properties.get(4);
+ ambientFactor = (float) x;
+ break;
+ case "DiffuseColor":
+ x = (Double) e2.properties.get(4);
+ y = (Double) e2.properties.get(5);
+ z = (Double) e2.properties.get(6);
+ diffuseColor.set((float) x, (float) y, (float) z);
+ break;
+ case "DiffuseFactor":
+ x = (Double) e2.properties.get(4);
+ diffuseFactor = (float) x;
+ break;
+ case "SpecularColor":
+ x = (Double) e2.properties.get(4);
+ y = (Double) e2.properties.get(5);
+ z = (Double) e2.properties.get(6);
+ specularColor.set((float) x, (float) y, (float) z);
+ break;
+ case "Shininess":
+ case "ShininessExponent":
+ x = (Double) e2.properties.get(4);
+ shininessExponent = (float) x;
+ break;
+ }
+ }
+ }
+ }
+ }
+ material = createMaterial();
+ }
+ }
+
+ @Override
+ public void link(FbxObject otherObject, String propertyName) {
+ if(otherObject instanceof FbxTexture) {
+ FbxTexture tex = (FbxTexture) otherObject;
+ if(tex.texture == null || material == null)
+ return;
+ switch(propertyName) {
+ case "DiffuseColor":
+ material.setTexture("DiffuseMap", tex.texture);
+ material.setColor("Diffuse", ColorRGBA.White);
+ break;
+ case "SpecularColor":
+ material.setTexture("SpecularMap", tex.texture);
+ material.setColor("Specular", ColorRGBA.White);
+ break;
+ case "NormalMap":
+ material.setTexture("NormalMap", tex.texture);
+ break;
+ }
+ }
+ }
+
+ private Material createMaterial() {
+ Material m = new Material(scene.assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ m.setName(name);
+ ambientColor.multLocal(ambientFactor);
+ diffuseColor.multLocal(diffuseFactor);
+ specularColor.multLocal(specularFactor);
+ m.setColor("Ambient", new ColorRGBA(ambientColor.x, ambientColor.y, ambientColor.z, 1));
+ m.setColor("Diffuse", new ColorRGBA(diffuseColor.x, diffuseColor.y, diffuseColor.z, 1));
+ m.setColor("Specular", new ColorRGBA(specularColor.x, specularColor.y, specularColor.z, 1));
+ m.setFloat("Shininess", shininessExponent);
+ m.setBoolean("UseMaterialColors", true);
+ m.setFloat("AlphaDiscardThreshold", 0.5f); // TODO replace with right way in JME to set "Aplha Test"
+ m.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
+ return m;
+ }
+
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
new file mode 100644
index 000000000..b73524b8b
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
@@ -0,0 +1,537 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.jme3.asset.AssetLoadException;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.Node;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap;
+import com.jme3.util.IntMap.Entry;
+
+public class FbxMesh extends FbxObject {
+
+ public double[] vertices;
+ public int[] indices;
+ public int[] edges;
+ public String normalsMapping;
+ public String normalsReference;
+ public double[] normals;
+ public String tangentsMapping;
+ public String tangentsReference;
+ public double[] tangents;
+ public String binormalsMapping;
+ public String binormalsReference;
+ public double[] binormals;
+ public String uvMapping;
+ public String uvReference;
+ public double[] uv;
+ public int[] uvIndex;
+ public List uvIndexes = new ArrayList<>();
+ public List uvs = new ArrayList<>();
+ public String smoothingMapping;
+ public String smoothingReference;
+ public int[] smoothing;
+ public String materialsMapping;
+ public String materialsReference;
+ public int[] materials;
+ // Build helping data
+ public int iCount;
+ public int vCount;
+ public int srcVertexCount;
+ public List vertexMap; // Target vertex -> source vertex
+ public List> reverseVertexMap; // source vertex -> list of target vertices
+ public List indexMap; // Target vertex -> source index
+
+ public List geometries; // One mesh can be split in two geometries in case of by-polygon material mapping
+ public FbxNode parent;
+ public int lastMaterialId = 0;
+
+ public FbxMesh(SceneLoader scene, FbxElement element) throws IOException {
+ super(scene, element);
+ if(type.equals("Mesh")) {
+ data: for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "Vertices":
+ vertices = (double[]) e.properties.get(0);
+ break;
+ case "PolygonVertexIndex":
+ indices = (int[]) e.properties.get(0);
+ break;
+ // TODO edges are not used now
+ /*case "Edges":
+ edges = (int[]) e.properties.get(0);
+ break;*/
+ case "LayerElementNormal":
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ normalsMapping = (String) e2.properties.get(0);
+ if(!normalsMapping.equals("ByVertice") && !normalsMapping.equals("ByPolygonVertex")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementNormal.MappingInformationType attribute (" + normalsReference + ")");
+ continue data;
+ }
+ break;
+ case "ReferenceInformationType":
+ normalsReference = (String) e2.properties.get(0);
+ if(!normalsReference.equals("Direct")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementNormal.ReferenceInformationType attribute (" + normalsReference + ")");
+ continue data;
+ }
+ break;
+ case "Normals":
+ normals = (double[]) e2.properties.get(0);
+ break;
+ }
+ }
+ break;
+ case "LayerElementTangent":
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ tangentsMapping = (String) e2.properties.get(0);
+ if(!tangentsMapping.equals("ByVertice") && !tangentsMapping.equals("ByPolygonVertex")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementTangent.MappingInformationType attribute (" + tangentsMapping + ")");
+ continue data;
+ }
+ break;
+ case "ReferenceInformationType":
+ tangentsReference = (String) e2.properties.get(0);
+ if(!tangentsReference.equals("Direct")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementTangent.ReferenceInformationType attribute (" + tangentsReference + ")");
+ continue data;
+ }
+ break;
+ case "Tangents":
+ tangents = (double[]) e2.properties.get(0);
+ break;
+ }
+ }
+ break;
+ case "LayerElementBinormal":
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ binormalsMapping = (String) e2.properties.get(0);
+ if(!binormalsMapping.equals("ByVertice") && !binormalsMapping.equals("ByPolygonVertex")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementBinormal.MappingInformationType attribute (" + binormalsMapping + ")");
+ continue data;
+ }
+ break;
+ case "ReferenceInformationType":
+ binormalsReference = (String) e2.properties.get(0);
+ if(!binormalsReference.equals("Direct")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementBinormal.ReferenceInformationType attribute (" + binormalsReference + ")");
+ continue data;
+ }
+ break;
+ case "Tangents":
+ binormals = (double[]) e2.properties.get(0);
+ break;
+ }
+ }
+ break;
+ case "LayerElementUV":
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ uvMapping = (String) e2.properties.get(0);
+ if(!uvMapping.equals("ByPolygonVertex")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementUV.MappingInformationType attribute (" + uvMapping + ")");
+ continue data;
+ }
+ break;
+ case "ReferenceInformationType":
+ uvReference = (String) e2.properties.get(0);
+ if(!uvReference.equals("IndexToDirect")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementUV.ReferenceInformationType attribute (" + uvReference + ")");
+ continue data;
+ }
+ break;
+ case "UV":
+ uv = (double[]) e2.properties.get(0);
+ uvs.add(uv);
+ break;
+ case "UVIndex":
+ uvIndex = (int[]) e2.properties.get(0);
+ uvIndexes.add(uvIndex);
+ break;
+ }
+ }
+ break;
+ // TODO smoothing is not used now
+ /*case "LayerElementSmoothing":
+ for(FBXElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ smoothingMapping = (String) e2.properties.get(0);
+ if(!smoothingMapping.equals("ByEdge"))
+ throw new AssetLoadException("Not supported LayerElementSmoothing.MappingInformationType = " + smoothingMapping);
+ break;
+ case "ReferenceInformationType":
+ smoothingReference = (String) e2.properties.get(0);
+ if(!smoothingReference.equals("Direct"))
+ throw new AssetLoadException("Not supported LayerElementSmoothing.ReferenceInformationType = " + smoothingReference);
+ break;
+ case "Smoothing":
+ smoothing = (int[]) e2.properties.get(0);
+ break;
+ }
+ }
+ break;*/
+ case "LayerElementMaterial":
+ for(FbxElement e2 : e.children) {
+ switch(e2.id) {
+ case "MappingInformationType":
+ materialsMapping = (String) e2.properties.get(0);
+ if(!materialsMapping.equals("AllSame") && !materialsMapping.equals("ByPolygon")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementMaterial.MappingInformationType attribute (" + materialsMapping + ")");
+ continue data;
+ }
+ break;
+ case "ReferenceInformationType":
+ materialsReference = (String) e2.properties.get(0);
+ if(!materialsReference.equals("IndexToDirect")) {
+ if(SceneLoader.WARN_IGNORED_ATTRIBUTES)
+ scene.warning("Ignored LayerElementMaterial.ReferenceInformationType attribute (" + materialsReference + ")");
+ continue data;
+ }
+ break;
+ case "Materials":
+ materials = (int[]) e2.properties.get(0);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ geometries = createGeometries();
+ }
+ }
+
+ public void setParent(Node node) {
+ for(int i = 0; i < geometries.size(); ++i) {
+ Geometry geom = geometries.get(i);
+ geom.setName(node.getName() + (i > 0 ? "-" + i : ""));
+ geom.updateModelBound();
+ node.attachChild(geom);
+ }
+ }
+
+ @Override
+ public void linkToZero() {
+ setParent(scene.sceneNode);
+ }
+
+ public void clearMaterials() {
+ for(Geometry g : geometries) {
+ if(g.getUserData("FBXMaterial") != null)
+ g.setUserData("FBXMaterial", null);
+ }
+ }
+
+ @Override
+ public void link(FbxObject otherObject) {
+ if(otherObject instanceof FbxSkin) {
+ FbxSkin skin = (FbxSkin) otherObject;
+ skin.toSkin.add(this);
+ }
+ }
+
+ private List createGeometries() throws IOException {
+ Mesh mesh = new Mesh();
+ mesh.setMode(Mode.Triangles);
+ // Since each vertex should contain unique texcoord and normal we should unroll vertex indexing
+ // So we don't use VertexBuffer.Type.Index for elements drawing
+ // Moreover quads should be triangulated (this increases number of vertices)
+ if(indices != null) {
+ iCount = indices.length;
+ srcVertexCount = vertices.length / 3;
+ // Indices contains negative numbers to define polygon last index
+ // Check indices strides to be sure we have triangles or quads
+ vCount = 0;
+ // Count number of vertices to be produced
+ int polyVertCount = 0;
+ for(int i = 0; i < iCount; ++i) {
+ int index = indices[i];
+ polyVertCount++;
+ if(index < 0) {
+ if(polyVertCount == 3) {
+ vCount += 3; // A triangle
+ } else if(polyVertCount == 4) {
+ vCount += 6; // A quad produce two triangles
+ } else {
+ throw new AssetLoadException("Unsupported PolygonVertexIndex stride");
+ }
+ polyVertCount = 0;
+ }
+ }
+ // Unroll index array into vertex mapping
+ vertexMap = new ArrayList<>(vCount);
+ indexMap = new ArrayList<>(vCount);
+ polyVertCount = 0;
+ for(int i = 0; i < iCount; ++i) {
+ int index = indices[i];
+ polyVertCount++;
+ if(index < 0) {
+ int lastIndex = -(index + 1);
+ if(polyVertCount == 3) {
+ vertexMap.add(indices[i - 2]);
+ vertexMap.add(indices[i - 1]);
+ vertexMap.add(lastIndex);
+ indexMap.add(i - 2);
+ indexMap.add(i - 1);
+ indexMap.add(i - 0);
+ } else if(polyVertCount == 4) {
+ vertexMap.add(indices[i - 3]);
+ vertexMap.add(indices[i - 2]);
+ vertexMap.add(indices[i - 1]);
+ vertexMap.add(indices[i - 3]);
+ vertexMap.add(indices[i - 1]);
+ vertexMap.add(lastIndex);
+ indexMap.add(i - 3);
+ indexMap.add(i - 2);
+ indexMap.add(i - 1);
+ indexMap.add(i - 3);
+ indexMap.add(i - 1);
+ indexMap.add(i - 0);
+ }
+ polyVertCount = 0;
+ }
+ }
+ // Build reverse vertex mapping
+ reverseVertexMap = new ArrayList<>(srcVertexCount);
+ for(int i = 0; i < srcVertexCount; ++i)
+ reverseVertexMap.add(new ArrayList<>());
+ for(int i = 0; i < vCount; ++i) {
+ int index = vertexMap.get(i);
+ reverseVertexMap.get(index).add(i);
+ }
+ } else {
+ // Stub for no vertex indexing (direct mapping)
+ iCount = vCount = srcVertexCount;
+ vertexMap = new ArrayList<>(vCount);
+ indexMap = new ArrayList<>(vCount);
+ reverseVertexMap = new ArrayList<>(vCount);
+ for(int i = 0; i < vCount; ++i) {
+ vertexMap.set(i, i);
+ indexMap.set(i, i);
+ List l = new ArrayList(1);
+ l.add(i);
+ reverseVertexMap.add(l);
+ }
+ }
+ if(vertices != null) {
+ // Unroll vertices data array
+ FloatBuffer posBuf = BufferUtils.createFloatBuffer(vCount * 3);
+ mesh.setBuffer(VertexBuffer.Type.Position, 3, posBuf);
+ int srcCount = vertices.length / 3;
+ for(int i = 0; i < vCount; ++i) {
+ int index = vertexMap.get(i);
+ if(index > srcCount)
+ throw new AssetLoadException("Invalid vertex mapping. Unexpected lookup vertex " + index + " from " + srcCount);
+ float x = (float) vertices[3 * index + 0] / scene.unitSize * scene.xAxis; // XXX Why we should scale by unit size?
+ float y = (float) vertices[3 * index + 1] / scene.unitSize * scene.yAxis;
+ float z = (float) vertices[3 * index + 2] / scene.unitSize * scene.zAxis;
+ posBuf.put(x).put(y).put(z);
+ }
+ }
+ if(normals != null) {
+ // Unroll normals data array
+ FloatBuffer normBuf = BufferUtils.createFloatBuffer(vCount * 3);
+ mesh.setBuffer(VertexBuffer.Type.Normal, 3, normBuf);
+ List mapping = null;
+ if(normalsMapping.equals("ByVertice"))
+ mapping = vertexMap;
+ else if(normalsMapping.equals("ByPolygonVertex"))
+ mapping = indexMap;
+ else
+ throw new IOException("Unknown normals mapping type: " + normalsMapping);
+ int srcCount = normals.length / 3;
+ for(int i = 0; i < vCount; ++i) {
+ int index = mapping.get(i);
+ if(index > srcCount)
+ throw new AssetLoadException("Invalid normal mapping. Unexpected lookup normal " + index + " from " + srcCount);
+ float x = (float) normals[3 * index + 0] * scene.xAxis;
+ float y = (float) normals[3 * index + 1] * scene.yAxis;
+ float z = (float) normals[3 * index + 2] * scene.zAxis;
+ normBuf.put(x).put(y).put(z);
+ }
+ }
+ if(tangents != null) {
+ // Unroll normals data array
+ FloatBuffer tanBuf = BufferUtils.createFloatBuffer(vCount * 4);
+ mesh.setBuffer(VertexBuffer.Type.Tangent, 4, tanBuf);
+ List mapping = null;
+ if(tangentsMapping.equals("ByVertice"))
+ mapping = vertexMap;
+ else if(tangentsMapping.equals("ByPolygonVertex"))
+ mapping = indexMap;
+ else
+ throw new IOException("Unknown tangents mapping type: " + tangentsMapping);
+ int srcCount = tangents.length / 3;
+ for(int i = 0; i < vCount; ++i) {
+ int index = mapping.get(i);
+ if(index > srcCount)
+ throw new AssetLoadException("Invalid tangent mapping. Unexpected lookup tangent " + index + " from " + srcCount);
+ float x = (float) tangents[3 * index + 0] * scene.xAxis;
+ float y = (float) tangents[3 * index + 1] * scene.yAxis;
+ float z = (float) tangents[3 * index + 2] * scene.zAxis;
+ tanBuf.put(x).put(y).put(z).put(-1.0f);
+ }
+ }
+ if(binormals != null) {
+ // Unroll normals data array
+ FloatBuffer binormBuf = BufferUtils.createFloatBuffer(vCount * 3);
+ mesh.setBuffer(VertexBuffer.Type.Binormal, 3, binormBuf);
+ List mapping = null;
+ if(binormalsMapping.equals("ByVertice"))
+ mapping = vertexMap;
+ else if(binormalsMapping.equals("ByPolygonVertex"))
+ mapping = indexMap;
+ else
+ throw new IOException("Unknown binormals mapping type: " + binormalsMapping);
+ int srcCount = binormals.length / 3;
+ for(int i = 0; i < vCount; ++i) {
+ int index = mapping.get(i);
+ if(index > srcCount)
+ throw new AssetLoadException("Invalid binormal mapping. Unexpected lookup binormal " + index + " from " + srcCount);
+ float x = (float) binormals[3 * index + 0] * scene.xAxis;
+ float y = (float) binormals[3 * index + 1] * scene.yAxis;
+ float z = (float) binormals[3 * index + 2] * scene.zAxis;
+ binormBuf.put(x).put(y).put(z);
+ }
+ }
+ for(int uvLayer = 0; uvLayer < uvs.size(); ++uvLayer) {
+ double[] uv = uvs.get(uvLayer);
+ int[] uvIndex = uvIndexes.size() > uvLayer ? uvIndexes.get(uvLayer) : null;
+ List unIndexMap = vertexMap;
+ if(uvIndex != null) {
+ int uvIndexSrcCount = uvIndex.length;
+ if(uvIndexSrcCount != iCount)
+ throw new AssetLoadException("Invalid number of texcoord index data " + uvIndexSrcCount + " expected " + iCount);
+ // Unroll UV index array
+ unIndexMap = new ArrayList<>(vCount);
+ int polyVertCount = 0;
+ for(int i = 0; i < iCount; ++i) {
+ int index = indices[i];
+ polyVertCount++;
+ if(index < 0) {
+ if(polyVertCount == 3) {
+ unIndexMap.add(uvIndex[i - 2]);
+ unIndexMap.add(uvIndex[i - 1]);
+ unIndexMap.add(uvIndex[i - 0]);
+ } else if(polyVertCount == 4) {
+ unIndexMap.add(uvIndex[i - 3]);
+ unIndexMap.add(uvIndex[i - 2]);
+ unIndexMap.add(uvIndex[i - 1]);
+ unIndexMap.add(uvIndex[i - 3]);
+ unIndexMap.add(uvIndex[i - 1]);
+ unIndexMap.add(uvIndex[i - 0]);
+ }
+ polyVertCount = 0;
+ }
+ }
+ }
+ // Unroll UV data array
+ FloatBuffer tcBuf = BufferUtils.createFloatBuffer(vCount * 2);
+ VertexBuffer.Type type = VertexBuffer.Type.TexCoord;
+ switch(uvLayer) {
+ case 1:
+ type = VertexBuffer.Type.TexCoord2;
+ break;
+ case 2:
+ type = VertexBuffer.Type.TexCoord3;
+ break;
+ case 3:
+ type = VertexBuffer.Type.TexCoord4;
+ break;
+ case 4:
+ type = VertexBuffer.Type.TexCoord5;
+ break;
+ case 5:
+ type = VertexBuffer.Type.TexCoord6;
+ break;
+ case 6:
+ type = VertexBuffer.Type.TexCoord7;
+ break;
+ case 7:
+ type = VertexBuffer.Type.TexCoord8;
+ break;
+ }
+ mesh.setBuffer(type, 2, tcBuf);
+ int srcCount = uv.length / 2;
+ for(int i = 0; i < vCount; ++i) {
+ int index = unIndexMap.get(i);
+ if(index > srcCount)
+ throw new AssetLoadException("Invalid texcoord mapping. Unexpected lookup texcoord " + index + " from " + srcCount);
+ float u = (index >= 0) ? (float) uv[2 * index + 0] : 0;
+ float v = (index >= 0) ? (float) uv[2 * index + 1] : 0;
+ tcBuf.put(u).put(v);
+ }
+ }
+ List geometries = new ArrayList();
+ if(materialsReference.equals("IndexToDirect") && materialsMapping.equals("ByPolygon")) {
+ IntMap> indexBuffers = new IntMap<>();
+ for(int polygon = 0; polygon < materials.length; ++polygon) {
+ int material = materials[polygon];
+ List list = indexBuffers.get(material);
+ if(list == null) {
+ list = new ArrayList<>();
+ indexBuffers.put(material, list);
+ }
+ list.add(polygon * 3 + 0);
+ list.add(polygon * 3 + 1);
+ list.add(polygon * 3 + 2);
+ }
+ Iterator>> iterator = indexBuffers.iterator();
+ while(iterator.hasNext()) {
+ Entry> e = iterator.next();
+ int materialId = e.getKey();
+ List indexes = e.getValue();
+ Mesh newMesh = mesh.clone();
+ newMesh.setBuffer(VertexBuffer.Type.Index, 3, toArray(indexes.toArray(new Integer[indexes.size()])));
+ newMesh.setStatic();
+ newMesh.updateBound();
+ newMesh.updateCounts();
+ Geometry geom = new Geometry();
+ geom.setMesh(newMesh);
+ geometries.add(geom);
+ geom.setUserData("FBXMaterial", materialId);
+ }
+ } else {
+ mesh.setStatic();
+ mesh.updateBound();
+ mesh.updateCounts();
+ Geometry geom = new Geometry();
+ geom.setMesh(mesh);
+ geometries.add(geom);
+ }
+ return geometries;
+ }
+
+ private static int[] toArray(Integer[] arr) {
+ int[] ret = new int[arr.length];
+ for(int i = 0; i < arr.length; ++i)
+ ret[i] = arr[i].intValue();
+ return ret;
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
new file mode 100644
index 000000000..b91d3d20e
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
@@ -0,0 +1,274 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.plugins.fbx.RotationOrder;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxNode extends FbxObject {
+
+ public Map userData = new HashMap<>();
+ public FaceCullMode cullMode = FaceCullMode.Back;
+ public Transform localTransform;
+ public Node node;
+ public FbxNode parentFbxNode;
+
+ public boolean rotationActive = false;
+ public RotationOrder rotationOrder = RotationOrder.EULER_XYZ;
+
+
+ // For bones and animation, in world space
+ public Matrix4f bindTransform = null;
+ public int boneIndex;
+ public Map animTranslations = new HashMap<>();
+ public Map animRotations = new HashMap<>();
+ public Map animScales = new HashMap<>();
+ public Bone bone;
+ private FbxAnimNode lastAnimTranslation;
+ private FbxAnimNode lastAnimRotation;
+ private FbxAnimNode lastAnimScale;
+ private FbxMesh mesh;
+ public Map skinToCluster = new HashMap<>();
+
+ public FbxNode(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ Vector3f translationLocalRaw = new Vector3f();
+ Vector3f rotationOffsetRaw = new Vector3f();
+ Vector3f rotationPivotRaw = new Vector3f();
+ Vector3f rotationPreRaw = new Vector3f();
+ Vector3f rotationLocalRaw = new Vector3f();
+ Vector3f rotationPostRaw = new Vector3f();
+ Vector3f scaleOffsetRaw = new Vector3f();
+ Vector3f scalePivotRaw = new Vector3f();
+ Vector3f scaleLocalRaw = new Vector3f(1, 1, 1);
+ for(FbxElement prop : element.getFbxProperties()) {
+ double x, y, z;
+ String propName = (String) prop.properties.get(0);
+ switch(propName) {
+ case "RotationOrder":
+ rotationOrder = RotationOrder.values[(Integer) prop.properties.get(4)];
+ break;
+ case "Lcl Translation":
+ readVectorFromProp(translationLocalRaw, prop);
+ break;
+ case "Lcl Rotation":
+ readVectorFromProp(rotationLocalRaw, prop);
+ break;
+ case "Lcl Scaling":
+ readVectorFromProp(scaleLocalRaw, prop);
+ break;
+ case "PreRotation":
+ readVectorFromProp(rotationPreRaw, prop);
+ break;
+ case "RotationActive":
+ rotationActive = ((Number) prop.properties.get(4)).intValue() == 1;
+ break;
+ case "RotationPivot":
+ readVectorFromProp(rotationPivotRaw, prop);
+ break;
+ case "PostRotation":
+ readVectorFromProp(rotationPostRaw, prop);
+ break;
+ case "ScaleOffset":
+ readVectorFromProp(scaleOffsetRaw, prop);
+ break;
+ case "ScalePivot":
+ readVectorFromProp(scalePivotRaw, prop);
+ break;
+ case "U":
+ String userDataKey = (String) prop.properties.get(0);
+ String userDataType = (String) prop.properties.get(1);
+ Object userDataValue;
+ if(userDataType.equals("KString")) {
+ userDataValue = (String) prop.properties.get(4);
+ } else if(userDataType.equals("int")) {
+ userDataValue = (Integer) prop.properties.get(4);
+ } else if(userDataType.equals("double")) {
+ // NOTE: jME3 does not support doubles in UserData.
+ // Need to convert to float.
+ userDataValue = ((Double) prop.properties.get(4)).floatValue();
+ } else if(userDataType.equals("Vector")) {
+ x = (Double) prop.properties.get(4);
+ y = (Double) prop.properties.get(5);
+ z = (Double) prop.properties.get(6);
+ userDataValue = new Vector3f((float) x, (float) y, (float) z);
+ } else {
+ scene.warning("Unsupported user data type: " + userDataType + ". Ignoring.");
+ continue;
+ }
+ userData.put(userDataKey, userDataValue);
+ break;
+ }
+ }
+
+ FbxElement cullingElement = element.getChildById("Culling");
+ if(cullingElement != null && cullingElement.properties.get(0).equals("CullingOff"))
+ cullMode = FaceCullMode.Off; // TODO Add other variants
+
+ /*From http://area.autodesk.com/forum/autodesk-fbx/fbx-sdk/the-makeup-of-the-local-matrix-of-an-kfbxnode/
+
+ Local Matrix = LclTranslation * RotationOffset * RotationPivot *
+ PreRotation * LclRotation * PostRotation * RotationPivotInverse *
+ ScalingOffset * ScalingPivot * LclScaling * ScalingPivotInverse
+
+ LocalTranslation : translate (xform -query -translation)
+ RotationOffset: translation compensates for the change in the rotate pivot point (xform -q -rotateTranslation)
+ RotationPivot: current rotate pivot position (xform -q -rotatePivot)
+ PreRotation : joint orientation(pre rotation)
+ LocalRotation: rotate transform (xform -q -rotation & xform -q -rotateOrder)
+ PostRotation : rotate axis (xform -q -rotateAxis)
+ RotationPivotInverse: inverse of RotationPivot
+ ScalingOffset: translation compensates for the change in the scale pivot point (xform -q -scaleTranslation)
+ ScalingPivot: current scale pivot position (xform -q -scalePivot)
+ LocalScaling: scale transform (xform -q -scale)
+ ScalingPivotInverse: inverse of ScalingPivot
+ */
+
+ RotationOrder rotOrder = rotationActive ? rotationOrder : RotationOrder.EULER_XYZ;
+
+ Matrix4f transformMatrix = new Matrix4f();
+ transformMatrix.setTranslation(translationLocalRaw.x + rotationOffsetRaw.x + rotationPivotRaw.x, translationLocalRaw.y + rotationOffsetRaw.y + rotationPivotRaw.y, translationLocalRaw.z + rotationOffsetRaw.z + rotationPivotRaw.z);
+
+ if(rotationActive) {
+ Quaternion postRotation = rotOrder.rotate(rotationPostRaw.x, rotationPostRaw.y, rotationPostRaw.z);
+ Quaternion localRotation = rotOrder.rotate(rotationLocalRaw.x, rotationLocalRaw.y, rotationLocalRaw.z);
+ Quaternion preRotation = rotOrder.rotate(rotationPreRaw.x, rotationPreRaw.y, rotationPreRaw.z);
+ //preRotation.multLocal(localRotation).multLocal(postRotation);
+ postRotation.multLocal(localRotation).multLocal(preRotation);
+ transformMatrix.multLocal(postRotation);
+ } else {
+ transformMatrix.multLocal(rotOrder.rotate(rotationLocalRaw.x, rotationLocalRaw.y, rotationLocalRaw.z));
+ }
+
+ Matrix4f mat = new Matrix4f();
+ mat.setTranslation(scaleOffsetRaw.x + scalePivotRaw.x - rotationPivotRaw.x, scaleOffsetRaw.y + scalePivotRaw.y - rotationPivotRaw.y, scaleOffsetRaw.z + scalePivotRaw.z - rotationPivotRaw.z);
+ transformMatrix.multLocal(mat);
+
+ transformMatrix.scale(scaleLocalRaw);
+ transformMatrix.scale(new Vector3f(scene.unitSize, scene.unitSize, scene.unitSize));
+
+ mat.setTranslation(scalePivotRaw.negate());
+ transformMatrix.multLocal(mat);
+
+ localTransform = new Transform(transformMatrix.toTranslationVector(), transformMatrix.toRotationQuat(), transformMatrix.toScaleVector());
+
+ node = new Node(name);
+ userData.forEach((s, o) -> node.setUserData(s, o));
+ node.setLocalTransform(localTransform);
+ }
+
+ @Override
+ public void linkToZero() {
+ scene.sceneNode.attachChild(node);
+ }
+
+ public void setSkeleton(Skeleton skeleton) {
+ if(bone != null)
+ boneIndex = skeleton.getBoneIndex(bone);
+ }
+
+ public void buildBindPoseBoneTransform() {
+ if(bone != null) {
+ Matrix4f t = bindTransform;
+ if(t != null) {
+ Matrix4f parentMatrix = parentFbxNode != null ? parentFbxNode.bindTransform : Matrix4f.IDENTITY;
+ if(parentMatrix == null)
+ parentMatrix = node.getLocalToWorldMatrix(null);
+ t = parentMatrix.invert().multLocal(t);
+ bone.setBindTransforms(t.toTranslationVector(), t.toRotationQuat(), t.toScaleVector());
+ } else {
+ bone.setBindTransforms(node.getLocalTranslation(), node.getLocalRotation(), node.getLocalScale());
+ }
+ }
+ }
+
+ @Override
+ public void link(FbxObject child, String propertyName) {
+ if(child instanceof FbxAnimNode) {
+ FbxAnimNode anim = (FbxAnimNode) child;
+ switch(propertyName) {
+ case "Lcl Translation":
+ animTranslations.put(anim.layerId, anim);
+ lastAnimTranslation = anim;
+ break;
+ case "Lcl Rotation":
+ animRotations.put(anim.layerId, anim);
+ lastAnimRotation = anim;
+ break;
+ case "Lcl Scaling":
+ animScales.put(anim.layerId, anim);
+ lastAnimScale = anim;
+ break;
+ }
+ }
+ }
+
+ public FbxAnimNode animTranslation(long layerId) {
+ if(layerId == 0)
+ return lastAnimTranslation;
+ return animTranslations.get(layerId);
+ }
+
+ public FbxAnimNode animRotation(long layerId) {
+ if(layerId == 0)
+ return lastAnimRotation;
+ return animRotations.get(layerId);
+ }
+
+ public FbxAnimNode animScale(long layerId) {
+ if(layerId == 0)
+ return lastAnimScale;
+ return animScales.get(layerId);
+ }
+
+ @Override
+ public void link(FbxObject otherObject) {
+ if(otherObject instanceof FbxMaterial) {
+ FbxMaterial m = (FbxMaterial) otherObject;
+ Material mat = m.material;
+ if(cullMode != FaceCullMode.Back)
+ mat.getAdditionalRenderState().setFaceCullMode(cullMode);
+ for(Geometry g : mesh.geometries) {
+ if(g.getUserData("FBXMaterial") != null) {
+ if((Integer) g.getUserData("FBXMaterial") == mesh.lastMaterialId)
+ g.setMaterial(mat);
+ } else {
+ g.setMaterial(mat);
+ }
+ }
+ mesh.lastMaterialId++;
+ } else if(otherObject instanceof FbxNode) {
+ FbxNode n = (FbxNode) otherObject;
+ node.attachChild(n.node);
+ n.parentFbxNode = this;
+ if(isLimb() && n.isLimb()) {
+ if(bone == null)
+ bone = new Bone(name);
+ if(n.bone == null)
+ n.bone = new Bone(n.name);
+ bone.addChild(n.bone);
+ }
+ } else if(otherObject instanceof FbxMesh) {
+ FbxMesh m = (FbxMesh) otherObject;
+ m.setParent(node);
+ m.parent = this;
+ mesh = m;
+ }
+ }
+
+ public boolean isLimb() {
+ return type.equals("LimbNode");
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxObject.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxObject.java
new file mode 100644
index 000000000..57611f072
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxObject.java
@@ -0,0 +1,40 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxObject {
+
+ protected final SceneLoader scene;
+ public final FbxElement element;
+ public final long id;
+ public final String name;
+ public final String type;
+
+ public FbxObject(SceneLoader scene, FbxElement element) {
+ this.scene = scene;
+ this.element = element;
+ this.id = (Long) element.properties.get(0);
+ String name = (String) element.properties.get(1);
+ this.name = name.substring(0, name.indexOf(0));
+ this.type = (String) element.properties.get(2);
+ }
+
+ public void link(FbxObject child) {
+ }
+
+ public void link(FbxObject child, String propertyName) {
+ }
+
+ // Parent is 0 id
+ public void linkToZero() {
+ }
+
+ protected static void readVectorFromProp(Vector3f store, FbxElement propElement) {
+ float x = ((Double) propElement.properties.get(4)).floatValue();
+ float y = ((Double) propElement.properties.get(5)).floatValue();
+ float z = ((Double) propElement.properties.get(6)).floatValue();
+ store.set(x, y, z);
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java
new file mode 100644
index 000000000..896860077
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java
@@ -0,0 +1,150 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.jme3.asset.AssetLoadException;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.util.BufferUtils;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxSkin extends FbxObject {
+
+ public String skinningType;
+ public List toSkin = new ArrayList<>();
+ public List bones = new ArrayList<>();
+
+ public FbxSkin(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "SkinningType":
+ skinningType = (String) e.properties.get(0);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void link(FbxObject otherObject) {
+ if(otherObject instanceof FbxCluster) {
+ FbxCluster cluster = ((FbxCluster) otherObject);
+ cluster.skin = this;
+ }
+ }
+
+ public void generateSkinning() {
+ for(FbxMesh fbxMesh : toSkin) {
+ if(fbxMesh.geometries == null)
+ continue;
+ Mesh firstMesh = fbxMesh.geometries.get(0).getMesh();
+ int maxWeightsPerVert = generateBoneData(firstMesh, fbxMesh);
+ for(int i = 0; i < fbxMesh.geometries.size(); ++i) {
+ Mesh mesh = fbxMesh.geometries.get(i).getMesh();
+ if(mesh != firstMesh) {
+ mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.BoneWeight));
+ mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.BoneIndex));
+ mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.HWBoneWeight));
+ mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.HWBoneIndex));
+ }
+ mesh.setMaxNumWeights(maxWeightsPerVert);
+ mesh.generateBindPose(true);
+ }
+ }
+ }
+
+ private int generateBoneData(Mesh mesh, FbxMesh fbxMesh) {
+ // Create bone buffers
+ FloatBuffer boneWeightData = BufferUtils.createFloatBuffer(fbxMesh.vCount * 4);
+ ByteBuffer boneIndicesData = BufferUtils.createByteBuffer(fbxMesh.vCount * 4);
+ mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, boneWeightData);
+ mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, boneIndicesData);
+ mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(Usage.CpuOnly);
+ mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(Usage.CpuOnly);
+ VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
+ VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
+ indicesHW.setUsage(Usage.CpuOnly); // Setting usage to CpuOnly so that the buffer is not send empty to the GPU
+ weightsHW.setUsage(Usage.CpuOnly);
+ mesh.setBuffer(weightsHW);
+ mesh.setBuffer(indicesHW);
+ int bonesLimitExceeded = 0;
+ // Accumulate skin bones influence into mesh buffers
+ for(FbxNode limb : bones) {
+ FbxCluster cluster = limb.skinToCluster.get(id);
+ if(cluster == null || cluster.indexes == null || cluster.weights == null || cluster.indexes.length != cluster.weights.length)
+ continue;
+ if(limb.boneIndex > 255)
+ throw new AssetLoadException("Bone index can't be packed into byte");
+ for(int i = 0; i < cluster.indexes.length; ++i) {
+ int vertexIndex = cluster.indexes[i];
+ if(vertexIndex >= fbxMesh.reverseVertexMap.size())
+ throw new AssetLoadException("Invalid skinning vertex index. Unexpected index lookup " + vertexIndex + " from " + fbxMesh.reverseVertexMap.size());
+ List dstVertices = fbxMesh.reverseVertexMap.get(vertexIndex);
+ for(int j = 0; j < dstVertices.size(); ++j) {
+ int v = dstVertices.get(j);
+ // Append bone index and weight to vertex
+ int offset;
+ int smalestOffset = 0;
+ float w = 0;
+ float smalestW = Float.MAX_VALUE;
+ for(offset = v * 4; offset < v * 4 + 4; ++offset) {
+ w = boneWeightData.get(offset);
+ if(w == 0)
+ break;
+ if(w < smalestW) {
+ smalestW = w;
+ smalestOffset = offset;
+ }
+ }
+ if(w == 0) {
+ boneWeightData.put(offset, (float) cluster.weights[i]);
+ boneIndicesData.put(offset, (byte) limb.boneIndex);
+ } else {
+ if((float) cluster.weights[i] > smalestW) { // If current weight more than smallest, discard smallest
+ boneWeightData.put(smalestOffset, (float) cluster.weights[i]);
+ boneIndicesData.put(smalestOffset, (byte) limb.boneIndex);
+ }
+ bonesLimitExceeded++;
+ }
+ }
+ }
+ }
+ if(bonesLimitExceeded > 0)
+ scene.warning("Skinning support max 4 bone per vertex. Exceeding data of " + bonesLimitExceeded + " weights in mesh bones will be discarded");
+ // Postprocess bones weights
+ int maxWeightsPerVert = 0;
+ boneWeightData.rewind();
+ for(int v = 0; v < fbxMesh.vCount; v++) {
+ float w0 = boneWeightData.get();
+ float w1 = boneWeightData.get();
+ float w2 = boneWeightData.get();
+ float w3 = boneWeightData.get();
+ if(w3 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
+ } else if(w2 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
+ } else if(w1 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
+ } else if(w0 != 0) {
+ maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
+ }
+ float sum = w0 + w1 + w2 + w3;
+ if(sum != 1f) {
+ // normalize weights
+ float mult = (sum != 0) ? (1f / sum) : 0;
+ boneWeightData.position(v * 4);
+ boneWeightData.put(w0 * mult);
+ boneWeightData.put(w1 * mult);
+ boneWeightData.put(w2 * mult);
+ boneWeightData.put(w3 * mult);
+ }
+ }
+ return maxWeightsPerVert;
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java
new file mode 100644
index 000000000..6093d7228
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java
@@ -0,0 +1,42 @@
+package com.jme3.scene.plugins.fbx.objects;
+
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.texture.Texture.WrapMode;
+import com.jme3.scene.plugins.fbx.SceneLoader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+
+public class FbxTexture extends FbxObject {
+
+ String bindType;
+ String filename;
+
+ public Texture texture;
+
+ public FbxTexture(SceneLoader scene, FbxElement element) {
+ super(scene, element);
+ for(FbxElement e : element.children) {
+ switch(e.id) {
+ case "Type":
+ bindType = (String) e.properties.get(0);
+ break;
+ case "FileName":
+ filename = (String) e.properties.get(0);
+ break;
+ }
+ }
+ texture = new Texture2D();
+ texture.setName(name);
+ texture.setWrap(WrapMode.Repeat); // Default FBX wrapping. TODO: Investigate where this is stored (probably, in material)
+ }
+
+ @Override
+ public void link(FbxObject otherObject) {
+ if(otherObject instanceof FbxImage) {
+ FbxImage img = (FbxImage) otherObject;
+ if(img.image == null)
+ return;
+ texture.setImage(img.image);
+ }
+ }
+}
From 4b6be2a78988207ff0c046d5e7c356cb0dea9ecb Mon Sep 17 00:00:00 2001
From: Eirenliel Allier
Date: Tue, 26 Apr 2016 10:16:03 +0300
Subject: [PATCH 2/4] FBX Importer cleanup
---
.../jme3/scene/plugins/fbx/RotationOrder.java | 56 +------------------
.../jme3/scene/plugins/fbx/SceneLoader.java | 5 +-
2 files changed, 5 insertions(+), 56 deletions(-)
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
index 94940609f..8a1e05b36 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/RotationOrder.java
@@ -4,11 +4,11 @@ import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
-// TODO This class has some potential in it... Should investigate
public enum RotationOrder {
EULER_XYZ, EULER_XZY, EULER_YZX, EULER_YXZ, EULER_ZXY, EULER_ZYX, SPHERIC_XYZ;
+ // Static values field for fast access by an oridinal without Enum.values() overhead
public static final RotationOrder[] values = values();
private RotationOrder() {
@@ -37,63 +37,13 @@ public enum RotationOrder {
case EULER_XZY:
return toQuat(x, Vector3f.UNIT_X, z, Vector3f.UNIT_Z, y, Vector3f.UNIT_Y);
case SPHERIC_XYZ:
+ default:
throw new IllegalArgumentException("Spheric rotation is unsupported in this importer");
}
- /*float c1 = FastMath.cos( x / 2 );
- float c2 = FastMath.cos( y / 2 );
- float c3 = FastMath.cos( z / 2 );
- float s1 = FastMath.sin( x / 2 );
- float s2 = FastMath.sin( y / 2 );
- float s3 = FastMath.sin( z / 2 );
-
- float _x;
- float _y;
- float _z;
- float _w;
-
- switch(order) {
- case EULER_XYZ:
- _x = s1 * c2 * c3 + c1 * s2 * s3;
- _y = c1 * s2 * c3 - s1 * c2 * s3;
- _z = c1 * c2 * s3 + s1 * s2 * c3;
- _w = c1 * c2 * c3 - s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- case EULER_YXZ:
- _x = s1 * c2 * c3 + c1 * s2 * s3;
- _y = c1 * s2 * c3 - s1 * c2 * s3;
- _z = c1 * c2 * s3 - s1 * s2 * c3;
- _w = c1 * c2 * c3 + s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- case EULER_ZXY:
- _x = s1 * c2 * c3 - c1 * s2 * s3;
- _y = c1 * s2 * c3 + s1 * c2 * s3;
- _z = c1 * c2 * s3 + s1 * s2 * c3;
- _w = c1 * c2 * c3 - s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- case EULER_ZYX:
- _x = s1 * c2 * c3 - c1 * s2 * s3;
- _y = c1 * s2 * c3 + s1 * c2 * s3;
- _z = c1 * c2 * s3 - s1 * s2 * c3;
- _w = c1 * c2 * c3 + s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- case EULER_YZX:
- _x = s1 * c2 * c3 + c1 * s2 * s3;
- _y = c1 * s2 * c3 + s1 * c2 * s3;
- _z = c1 * c2 * s3 - s1 * s2 * c3;
- _w = c1 * c2 * c3 - s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- case EULER_XZY:
- _x = s1 * c2 * c3 - c1 * s2 * s3;
- _y = c1 * s2 * c3 - s1 * c2 * s3;
- _z = c1 * c2 * s3 + s1 * s2 * c3;
- _w = c1 * c2 * c3 + s1 * s2 * s3;
- return new Quaternion(_x, _y, _z, _w);
- }
- */
- throw new AssertionError("Impossible");
}
private static Quaternion toQuat(float ax1v, Vector3f ax1, float ax2v, Vector3f ax2, float ax3v, Vector3f ax3) {
+ // TODO It has some potential in optimization
Quaternion q1 = new Quaternion().fromAngleNormalAxis(ax1v, ax1);
Quaternion q2 = new Quaternion().fromAngleNormalAxis(ax2v, ax2);
Quaternion q3 = new Quaternion().fromAngleNormalAxis(ax3v, ax3);
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
index cf4af7682..7b9a5a7f5 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
@@ -44,9 +44,8 @@ import com.jme3.scene.plugins.fbx.objects.FbxTexture;
/**
* FBX file format loader
* Loads scene meshes, materials, textures, skeleton and skeletal animation.
- * Multiple animations can be defined with {@link AnimationList} passing into {@link SceneKey}.
- *
- * @author Aleksandra Menshchikova
+ * Multiple animations can be defined with {@link AnimationList} passing into {@link SceneKey}
+ * or loaded from different animation layer.
*/
public class SceneLoader implements AssetLoader {
From 794932104beddd133e93053f80c6d00cdf6b75eb Mon Sep 17 00:00:00 2001
From: Eirenliel Allier
Date: Tue, 26 Apr 2016 11:12:37 +0300
Subject: [PATCH 3/4] Fix compatibility with Java 7
---
.../java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java | 2 +-
.../java/com/jme3/scene/plugins/fbx/objects/FbxNode.java | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
index b73524b8b..930dd830d 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java
@@ -320,7 +320,7 @@ public class FbxMesh extends FbxObject {
// Build reverse vertex mapping
reverseVertexMap = new ArrayList<>(srcVertexCount);
for(int i = 0; i < srcVertexCount; ++i)
- reverseVertexMap.add(new ArrayList<>());
+ reverseVertexMap.add(new ArrayList());
for(int i = 0; i < vCount; ++i) {
int index = vertexMap.get(i);
reverseVertexMap.get(index).add(i);
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
index b91d3d20e..ebb209492 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java
@@ -19,7 +19,6 @@ import com.jme3.scene.plugins.fbx.file.FbxElement;
public class FbxNode extends FbxObject {
- public Map userData = new HashMap<>();
public FaceCullMode cullMode = FaceCullMode.Back;
public Transform localTransform;
public Node node;
@@ -44,6 +43,7 @@ public class FbxNode extends FbxObject {
public FbxNode(SceneLoader scene, FbxElement element) {
super(scene, element);
+ node = new Node(name);
Vector3f translationLocalRaw = new Vector3f();
Vector3f rotationOffsetRaw = new Vector3f();
Vector3f rotationPivotRaw = new Vector3f();
@@ -108,7 +108,7 @@ public class FbxNode extends FbxObject {
scene.warning("Unsupported user data type: " + userDataType + ". Ignoring.");
continue;
}
- userData.put(userDataKey, userDataValue);
+ node.setUserData(userDataKey, userDataValue);
break;
}
}
@@ -164,8 +164,6 @@ public class FbxNode extends FbxObject {
localTransform = new Transform(transformMatrix.toTranslationVector(), transformMatrix.toRotationQuat(), transformMatrix.toScaleVector());
- node = new Node(name);
- userData.forEach((s, o) -> node.setUserData(s, o));
node.setLocalTransform(localTransform);
}
From 681b2101dce29061f6c8588d23d125218f943a32 Mon Sep 17 00:00:00 2001
From: Eirenliel Allier
Date: Tue, 26 Apr 2016 11:21:34 +0300
Subject: [PATCH 4/4] Fix building with JRE 1.7
---
.../jme3/scene/plugins/fbx/SceneLoader.java | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
index 7b9a5a7f5..accbdbbf4 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
@@ -104,7 +104,7 @@ public class SceneLoader implements AssetLoader {
loadScene(stream);
linkScene();
if(warnings.size() > 0)
- logger.log(Level.WARNING, "Model load finished with warnings:\n" + String.join("\n", warnings));
+ logger.log(Level.WARNING, "Model load finished with warnings:\n" + join(warnings, "\n"));
} finally {
releaseObjects();
if(stream != null)
@@ -430,10 +430,6 @@ public class SceneLoader implements AssetLoader {
animControl.setAnimations(anims);
}
- private static boolean haveAnyChannel(FbxAnimNode anims) {
- return anims != null && anims.haveAnyChannel();
- }
-
private void releaseObjects() {
// Reset settings
unitSize = 1;
@@ -461,4 +457,19 @@ public class SceneLoader implements AssetLoader {
animControl = null;
sceneNode = null;
}
+
+
+ private static boolean haveAnyChannel(FbxAnimNode anims) {
+ return anims != null && anims.haveAnyChannel();
+ }
+
+ private static String join(List list, String glue) {
+ StringBuilder sb = new StringBuilder();
+ for(int i = 0; i < list.size(); ++i) {
+ if(sb.length() != 0)
+ sb.append(glue);
+ sb.append(list.get(i));
+ }
+ return sb.toString();
+ }
}