GLTF: armature loading.
This commit is contained in:
parent
b129d2954f
commit
6b3093aa3e
@ -331,6 +331,15 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
this.modeStart = cloner.clone(modeStart);
|
this.modeStart = cloner.clone(modeStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param forSoftwareAnim
|
||||||
|
* @deprecated use generateBindPose();
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void generateBindPose(boolean forSoftwareAnim) {
|
||||||
|
generateBindPose();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
|
* Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal},
|
||||||
* and {@link Type#BindPoseTangent}
|
* and {@link Type#BindPoseTangent}
|
||||||
@ -338,11 +347,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
* buffers already set on the mesh.
|
* buffers already set on the mesh.
|
||||||
* This method does nothing if the mesh has no bone weight or index
|
* This method does nothing if the mesh has no bone weight or index
|
||||||
* buffers.
|
* buffers.
|
||||||
*
|
|
||||||
* @param forSoftwareAnim Should be true if the bind pose is to be generated.
|
|
||||||
*/
|
*/
|
||||||
public void generateBindPose(boolean forSoftwareAnim){
|
public void generateBindPose() {
|
||||||
if (forSoftwareAnim){
|
|
||||||
VertexBuffer pos = getBuffer(Type.Position);
|
VertexBuffer pos = getBuffer(Type.Position);
|
||||||
if (pos == null || getBuffer(Type.BoneIndex) == null) {
|
if (pos == null || getBuffer(Type.BoneIndex) == null) {
|
||||||
// ignore, this mesh doesn't have positional data
|
// ignore, this mesh doesn't have positional data
|
||||||
@ -382,7 +388,7 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
setBuffer(bindTangents);
|
setBuffer(bindTangents);
|
||||||
tangents.setUsage(Usage.Stream);
|
tangents.setUsage(Usage.Stream);
|
||||||
}// else hardware setup does nothing, mesh already in bind pose
|
}// else hardware setup does nothing, mesh already in bind pose
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -429,13 +435,24 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
|
|||||||
//if HWBoneIndex and HWBoneWeight are empty, we setup them as direct
|
//if HWBoneIndex and HWBoneWeight are empty, we setup them as direct
|
||||||
//buffers with software anim buffers data
|
//buffers with software anim buffers data
|
||||||
VertexBuffer indicesHW = getBuffer(Type.HWBoneIndex);
|
VertexBuffer indicesHW = getBuffer(Type.HWBoneIndex);
|
||||||
|
Buffer result;
|
||||||
if (indicesHW.getData() == null) {
|
if (indicesHW.getData() == null) {
|
||||||
VertexBuffer indices = getBuffer(Type.BoneIndex);
|
VertexBuffer indices = getBuffer(Type.BoneIndex);
|
||||||
|
if (indices.getFormat() == Format.UnsignedByte) {
|
||||||
ByteBuffer originalIndex = (ByteBuffer) indices.getData();
|
ByteBuffer originalIndex = (ByteBuffer) indices.getData();
|
||||||
ByteBuffer directIndex = BufferUtils.createByteBuffer(originalIndex.capacity());
|
ByteBuffer directIndex = BufferUtils.createByteBuffer(originalIndex.capacity());
|
||||||
originalIndex.clear();
|
originalIndex.clear();
|
||||||
directIndex.put(originalIndex);
|
directIndex.put(originalIndex);
|
||||||
indicesHW.setupData(Usage.Static, indices.getNumComponents(), indices.getFormat(), directIndex);
|
result = directIndex;
|
||||||
|
} else {
|
||||||
|
//bone indices can be stored in an UnsignedShort buffer
|
||||||
|
ShortBuffer originalIndex = (ShortBuffer) indices.getData();
|
||||||
|
ShortBuffer directIndex = BufferUtils.createShortBuffer(originalIndex.capacity());
|
||||||
|
originalIndex.clear();
|
||||||
|
directIndex.put(originalIndex);
|
||||||
|
result = directIndex;
|
||||||
|
}
|
||||||
|
indicesHW.setupData(Usage.Static, indices.getNumComponents(), indices.getFormat(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexBuffer weightsHW = getBuffer(Type.HWBoneWeight);
|
VertexBuffer weightsHW = getBuffer(Type.HWBoneWeight);
|
||||||
|
@ -2,9 +2,7 @@ package com.jme3.scene.plugins.gltf;
|
|||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.jme3.animation.AnimControl;
|
import com.jme3.animation.*;
|
||||||
import com.jme3.animation.Animation;
|
|
||||||
import com.jme3.animation.SpatialTrack;
|
|
||||||
import com.jme3.asset.*;
|
import com.jme3.asset.*;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.material.RenderState;
|
import com.jme3.material.RenderState;
|
||||||
@ -17,7 +15,9 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -45,6 +45,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
private JsonArray images;
|
private JsonArray images;
|
||||||
private JsonArray samplers;
|
private JsonArray samplers;
|
||||||
private JsonArray animations;
|
private JsonArray animations;
|
||||||
|
private JsonArray skins;
|
||||||
|
|
||||||
private Material defaultMat;
|
private Material defaultMat;
|
||||||
private AssetInfo info;
|
private AssetInfo info;
|
||||||
@ -52,9 +53,12 @@ public class GltfLoader implements AssetLoader {
|
|||||||
private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator();
|
private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator();
|
||||||
private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
|
private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
|
||||||
private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
|
private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
|
||||||
|
private Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator();
|
||||||
private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
|
private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
|
||||||
private boolean useNormalsFlag = false;
|
private boolean useNormalsFlag = false;
|
||||||
|
|
||||||
|
Map<Skeleton, List<Spatial>> skinnedSpatials = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMaterialAdapter());
|
defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMaterialAdapter());
|
||||||
}
|
}
|
||||||
@ -94,10 +98,16 @@ public class GltfLoader implements AssetLoader {
|
|||||||
images = root.getAsJsonArray("images");
|
images = root.getAsJsonArray("images");
|
||||||
samplers = root.getAsJsonArray("samplers");
|
samplers = root.getAsJsonArray("samplers");
|
||||||
animations = root.getAsJsonArray("animations");
|
animations = root.getAsJsonArray("animations");
|
||||||
|
skins = root.getAsJsonArray("skins");
|
||||||
|
|
||||||
|
readSkins();
|
||||||
|
|
||||||
JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene");
|
JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene");
|
||||||
|
|
||||||
Node n = loadScenes(defaultScene);
|
Node n = loadScenes(defaultScene);
|
||||||
|
|
||||||
|
setupControls();
|
||||||
|
|
||||||
//only one scene let's not return the root.
|
//only one scene let's not return the root.
|
||||||
if (n.getChildren().size() == 1) {
|
if (n.getChildren().size() == 1) {
|
||||||
n = (Node) n.getChild(0);
|
n = (Node) n.getChild(0);
|
||||||
@ -131,7 +141,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
sceneNode.setName(getAsString(scene.getAsJsonObject(), "name"));
|
sceneNode.setName(getAsString(scene.getAsJsonObject(), "name"));
|
||||||
JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes");
|
JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes");
|
||||||
for (JsonElement node : sceneNodes) {
|
for (JsonElement node : sceneNodes) {
|
||||||
sceneNode.attachChild(loadNode(node.getAsInt()));
|
loadChild(sceneNode, node);
|
||||||
}
|
}
|
||||||
root.attachChild(sceneNode);
|
root.attachChild(sceneNode);
|
||||||
}
|
}
|
||||||
@ -152,13 +162,19 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Spatial loadNode(int nodeIndex) throws IOException {
|
private Object loadNode(int nodeIndex) throws IOException {
|
||||||
Spatial spatial = fetchFromCache("nodes", nodeIndex, Spatial.class);
|
Object obj = fetchFromCache("nodes", nodeIndex, Object.class);
|
||||||
if (spatial != null) {
|
if (obj != null) {
|
||||||
|
if (obj instanceof Bone) {
|
||||||
|
//the node can be a previously loaded bone let's return it
|
||||||
|
return obj;
|
||||||
|
} else {
|
||||||
//If a spatial is referenced several times, it may be attached to different parents,
|
//If a spatial is referenced several times, it may be attached to different parents,
|
||||||
// and it's not possible in JME, so we have to clone it.
|
// and it's not possible in JME, so we have to clone it.
|
||||||
return spatial.clone();
|
return ((Spatial) obj).clone();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Spatial spatial;
|
||||||
JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
|
JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
|
||||||
JsonArray children = nodeData.getAsJsonArray("children");
|
JsonArray children = nodeData.getAsJsonArray("children");
|
||||||
Integer meshIndex = getAsInteger(nodeData, "mesh");
|
Integer meshIndex = getAsInteger(nodeData, "mesh");
|
||||||
@ -189,9 +205,16 @@ public class GltfLoader implements AssetLoader {
|
|||||||
spatial = node;
|
spatial = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Integer skinIndex = getAsInteger(nodeData, "skin");
|
||||||
|
if (skinIndex != null) {
|
||||||
|
Skeleton skeleton = fetchFromCache("skins", skinIndex, Skeleton.class);
|
||||||
|
List<Spatial> spatials = skinnedSpatials.get(skeleton);
|
||||||
|
spatials.add(spatial);
|
||||||
|
}
|
||||||
|
|
||||||
if (children != null) {
|
if (children != null) {
|
||||||
for (JsonElement child : children) {
|
for (JsonElement child : children) {
|
||||||
((Node) spatial).attachChild(loadNode(child.getAsInt()));
|
loadChild(spatial, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +227,19 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return spatial;
|
return spatial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadChild(Spatial parent, JsonElement child) throws IOException {
|
||||||
|
int index = child.getAsInt();
|
||||||
|
Object loaded = loadNode(child.getAsInt());
|
||||||
|
if (loaded instanceof Spatial) {
|
||||||
|
((Node) parent).attachChild((Spatial) loaded);
|
||||||
|
} else if (loaded instanceof Bone) {
|
||||||
|
//fetch the skeleton and add a skeletonControl to the node.
|
||||||
|
// Skeleton skeleton = fetchFromCache("skeletons", index, Skeleton.class);
|
||||||
|
// SkeletonControl control = new SkeletonControl(skeleton);
|
||||||
|
// parent.addControl(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Transform loadTransforms(JsonObject nodeData) {
|
private Transform loadTransforms(JsonObject nodeData) {
|
||||||
Transform transform = new Transform();
|
Transform transform = new Transform();
|
||||||
JsonArray matrix = nodeData.getAsJsonArray("matrix");
|
JsonArray matrix = nodeData.getAsJsonArray("matrix");
|
||||||
@ -276,6 +312,21 @@ public class GltfLoader implements AssetLoader {
|
|||||||
for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) {
|
for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) {
|
||||||
mesh.setBuffer(loadAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(entry.getKey()))));
|
mesh.setBuffer(loadAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(entry.getKey()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mesh.getBuffer(VertexBuffer.Type.BoneIndex) != null) {
|
||||||
|
//the mesh has some skinning let's create needed buffers for HW skinning
|
||||||
|
//creating empty buffers for HW skinning
|
||||||
|
//the buffers will be setup if ever used.
|
||||||
|
VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
|
||||||
|
VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
|
||||||
|
//setting usage to cpuOnly so that the buffer is not send empty to the GPU
|
||||||
|
indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
|
weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
|
||||||
|
mesh.setBuffer(weightsHW);
|
||||||
|
mesh.setBuffer(indicesHW);
|
||||||
|
mesh.generateBindPose();
|
||||||
|
}
|
||||||
|
|
||||||
Geometry geom = new Geometry(null, mesh);
|
Geometry geom = new Geometry(null, mesh);
|
||||||
|
|
||||||
Integer materialIndex = getAsInteger(meshObject, "material");
|
Integer materialIndex = getAsInteger(meshObject, "material");
|
||||||
@ -302,6 +353,7 @@ public class GltfLoader implements AssetLoader {
|
|||||||
geomArray[index] = geom;
|
geomArray[index] = geom;
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
//TODO skins
|
||||||
//TODO targets(morph anim...)
|
//TODO targets(morph anim...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,12 +370,14 @@ public class GltfLoader implements AssetLoader {
|
|||||||
int byteOffset = getAsInteger(accessor, "byteOffset", 0);
|
int byteOffset = getAsInteger(accessor, "byteOffset", 0);
|
||||||
Integer componentType = getAsInteger(accessor, "componentType");
|
Integer componentType = getAsInteger(accessor, "componentType");
|
||||||
assertNotNull(componentType, "No component type defined for accessor " + accessorIndex);
|
assertNotNull(componentType, "No component type defined for accessor " + accessorIndex);
|
||||||
boolean normalized = getAsBoolean(accessor, "normalized", false);
|
|
||||||
Integer count = getAsInteger(accessor, "count");
|
Integer count = getAsInteger(accessor, "count");
|
||||||
assertNotNull(count, "No count attribute defined for accessor " + accessorIndex);
|
assertNotNull(count, "No count attribute defined for accessor " + accessorIndex);
|
||||||
String type = getAsString(accessor, "type");
|
String type = getAsString(accessor, "type");
|
||||||
assertNotNull(type, "No type attribute defined for accessor " + accessorIndex);
|
assertNotNull(type, "No type attribute defined for accessor " + accessorIndex);
|
||||||
|
|
||||||
|
boolean normalized = getAsBoolean(accessor, "normalized", false);
|
||||||
|
//Some float data can be packed into short buffers, "normalized" means they have to be unpacked.
|
||||||
|
//TODO support packed data
|
||||||
//TODO min / max
|
//TODO min / max
|
||||||
//TODO sparse
|
//TODO sparse
|
||||||
//TODO extensions?
|
//TODO extensions?
|
||||||
@ -578,8 +632,10 @@ public class GltfLoader implements AssetLoader {
|
|||||||
control.addAnim(anim);
|
control.addAnim(anim);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//At some pont we'll have bone animation
|
//At some point we'll have bone animation
|
||||||
//TODO support for bone animation.
|
//TODO support for bone animation.
|
||||||
|
System.err.println("animated");
|
||||||
|
System.err.println(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,6 +664,100 @@ public class GltfLoader implements AssetLoader {
|
|||||||
texture.setWrap(Texture.WrapAxis.T, wrapT);
|
texture.setWrap(Texture.WrapAxis.T, wrapT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void readSkins() throws IOException {
|
||||||
|
if (skins == null) {
|
||||||
|
//no skins, no bone animation.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int index = 0; index < skins.size(); index++) {
|
||||||
|
JsonObject skin = skins.get(index).getAsJsonObject();
|
||||||
|
|
||||||
|
//each skin is a skeleton.
|
||||||
|
Integer rootIndex = getAsInteger(skin, "skeleton");
|
||||||
|
JsonArray joints = skin.getAsJsonArray("joints");
|
||||||
|
assertNotNull(joints, "No joints defined for skin");
|
||||||
|
Integer matricesIndex = getAsInteger(skin, "inverseBindMatrices");
|
||||||
|
Matrix4f[] inverseBindMatrices = null;
|
||||||
|
if (matricesIndex != null) {
|
||||||
|
inverseBindMatrices = loadAccessorData(matricesIndex, matrix4fArrayPopulator);
|
||||||
|
} else {
|
||||||
|
inverseBindMatrices = new Matrix4f[joints.size()];
|
||||||
|
for (int i = 0; i < inverseBindMatrices.length; i++) {
|
||||||
|
inverseBindMatrices[i] = new Matrix4f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println(inverseBindMatrices);
|
||||||
|
|
||||||
|
rootIndex = joints.get(0).getAsInt();
|
||||||
|
|
||||||
|
Bone[] bones = new Bone[joints.size()];
|
||||||
|
for (int i = 0; i < joints.size(); i++) {
|
||||||
|
bones[i] = loadNodeAsBone(joints.get(i).getAsInt(), inverseBindMatrices[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < joints.size(); i++) {
|
||||||
|
findChildren(joints.get(i).getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
Skeleton skeleton = new Skeleton(bones);
|
||||||
|
addToCache("skins", index, skeleton, nodes.size());
|
||||||
|
skinnedSpatials.put(skeleton, new ArrayList<Spatial>());
|
||||||
|
|
||||||
|
System.err.println(skeleton);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bone loadNodeAsBone(int nodeIndex, Matrix4f inverseBindMatrix) throws IOException {
|
||||||
|
|
||||||
|
Bone bone = fetchFromCache("nodes", nodeIndex, Bone.class);
|
||||||
|
if (bone != null) {
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
|
||||||
|
JsonArray children = nodeData.getAsJsonArray("children");
|
||||||
|
String name = getAsString(nodeData, "name");
|
||||||
|
if (name == null) {
|
||||||
|
name = "Bone_" + nodeIndex;
|
||||||
|
}
|
||||||
|
bone = new Bone(name);
|
||||||
|
Transform boneTransforms = loadTransforms(nodeData);
|
||||||
|
Transform inverseBind = new Transform();
|
||||||
|
inverseBind.fromTransformMatrix(inverseBindMatrix);
|
||||||
|
// boneTransforms.combineWithParent(inverseBind);
|
||||||
|
bone.setBindTransforms(boneTransforms.getTranslation(), boneTransforms.getRotation(), boneTransforms.getScale());
|
||||||
|
|
||||||
|
|
||||||
|
addToCache("nodes", nodeIndex, bone, nodes.size());
|
||||||
|
return bone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findChildren(int nodeIndex) {
|
||||||
|
Bone bone = fetchFromCache("nodes", nodeIndex, Bone.class);
|
||||||
|
JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
|
||||||
|
JsonArray children = nodeData.getAsJsonArray("children");
|
||||||
|
if (children != null) {
|
||||||
|
for (JsonElement child : children) {
|
||||||
|
bone.addChild(fetchFromCache("nodes", child.getAsInt(), Bone.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupControls() {
|
||||||
|
for (Skeleton skeleton : skinnedSpatials.keySet()) {
|
||||||
|
List<Spatial> spatials = skinnedSpatials.get(skeleton);
|
||||||
|
Spatial spatial = null;
|
||||||
|
if (spatials.size() >= 1) {
|
||||||
|
spatial = findCommonAncestor(spatials);
|
||||||
|
} else {
|
||||||
|
spatial = spatials.get(0);
|
||||||
|
}
|
||||||
|
SkeletonControl control = new SkeletonControl(skeleton);
|
||||||
|
spatial.addControl(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String loadMeshName(int meshIndex) {
|
private String loadMeshName(int meshIndex) {
|
||||||
JsonObject meshData = meshes.get(meshIndex).getAsJsonObject();
|
JsonObject meshData = meshes.get(meshIndex).getAsJsonObject();
|
||||||
return getAsString(meshData, "name");
|
return getAsString(meshData, "name");
|
||||||
@ -737,5 +887,25 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class Matrix4fArrayPopulator implements Populator<Matrix4f[]> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException {
|
||||||
|
|
||||||
|
int numComponents = getNumberOfComponents(type);
|
||||||
|
int dataSize = numComponents * count;
|
||||||
|
Matrix4f[] data = new Matrix4f[count];
|
||||||
|
|
||||||
|
if (bufferViewIndex == null) {
|
||||||
|
//no referenced buffer, specs says to pad the data with zeros.
|
||||||
|
padBuffer(data, dataSize);
|
||||||
|
} else {
|
||||||
|
readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,15 +3,21 @@ package com.jme3.scene.plugins.gltf;
|
|||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.jme3.asset.AssetLoadException;
|
import com.jme3.asset.AssetLoadException;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.VertexBuffer;
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
import com.jme3.util.LittleEndien;
|
import com.jme3.util.LittleEndien;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Nehon on 07/08/2017.
|
* Created by Nehon on 07/08/2017.
|
||||||
@ -210,6 +216,11 @@ public class GltfUtils {
|
|||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
array[i] = new Quaternion();
|
array[i] = new Quaternion();
|
||||||
}
|
}
|
||||||
|
} else if (store instanceof Matrix4f[]) {
|
||||||
|
Matrix4f[] array = (Matrix4f[]) store;
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
array[i] = new Matrix4f();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,6 +251,8 @@ public class GltfUtils {
|
|||||||
populateVector3fArray((Vector3f[]) store, stream, length, byteOffset, byteStride, numComponents);
|
populateVector3fArray((Vector3f[]) store, stream, length, byteOffset, byteStride, numComponents);
|
||||||
} else if (store instanceof Quaternion[]) {
|
} else if (store instanceof Quaternion[]) {
|
||||||
populateQuaternionArray((Quaternion[]) store, stream, length, byteOffset, byteStride, numComponents);
|
populateQuaternionArray((Quaternion[]) store, stream, length, byteOffset, byteStride, numComponents);
|
||||||
|
} else if (store instanceof Matrix4f[]) {
|
||||||
|
populateMatrix4fArray((Matrix4f[]) store, stream, length, byteOffset, byteStride, numComponents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +361,38 @@ public class GltfUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void populateMatrix4fArray(Matrix4f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException {
|
||||||
|
int index = byteOffset;
|
||||||
|
int componentSize = 4;
|
||||||
|
int end = length * componentSize + byteOffset;
|
||||||
|
stream.skipBytes(byteOffset);
|
||||||
|
int arrayIndex = 0;
|
||||||
|
while (index < end) {
|
||||||
|
array[arrayIndex] = new Matrix4f(
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat(),
|
||||||
|
stream.readFloat()
|
||||||
|
);
|
||||||
|
|
||||||
|
arrayIndex++;
|
||||||
|
|
||||||
|
index += Math.max(componentSize * numComponents, byteStride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static LittleEndien getStream(byte[] buffer) {
|
private static LittleEndien getStream(byte[] buffer) {
|
||||||
return new LittleEndien(new DataInputStream(new ByteArrayInputStream(buffer)));
|
return new LittleEndien(new DataInputStream(new ByteArrayInputStream(buffer)));
|
||||||
}
|
}
|
||||||
@ -408,6 +453,45 @@ public class GltfUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Spatial findCommonAncestor(List<Spatial> spatials) {
|
||||||
|
Map<Spatial, List<Spatial>> flatParents = new HashMap<>();
|
||||||
|
|
||||||
|
for (Spatial spatial : spatials) {
|
||||||
|
List<Spatial> parents = new ArrayList<>();
|
||||||
|
Spatial parent = spatial.getParent();
|
||||||
|
while (parent != null) {
|
||||||
|
parents.add(0, parent);
|
||||||
|
parent = parent.getParent();
|
||||||
|
}
|
||||||
|
flatParents.put(spatial, parents);
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
Spatial lastCommonParent = null;
|
||||||
|
Spatial parent = null;
|
||||||
|
while (true) {
|
||||||
|
for (Spatial spatial : flatParents.keySet()) {
|
||||||
|
List<Spatial> parents = flatParents.get(spatial);
|
||||||
|
if (index == parents.size()) {
|
||||||
|
//we reached the end of a spatial hierarchy let's return;
|
||||||
|
return lastCommonParent;
|
||||||
|
}
|
||||||
|
Spatial p = parents.get(index);
|
||||||
|
if (parent == null) {
|
||||||
|
parent = p;
|
||||||
|
} else if (p != parent) {
|
||||||
|
return lastCommonParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastCommonParent = parent;
|
||||||
|
parent = null;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void dumpMesh(Mesh m) {
|
public static void dumpMesh(Mesh m) {
|
||||||
for (VertexBuffer vertexBuffer : m.getBufferList().getArray()) {
|
for (VertexBuffer vertexBuffer : m.getBufferList().getArray()) {
|
||||||
System.err.println(vertexBuffer.getBufferType());
|
System.err.println(vertexBuffer.getBufferType());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user