use of com.jme3.animation.Skeleton in project jmonkeyengine by jMonkeyEngine.
the class SceneLoader method buildAnimations.
private void buildAnimations() {
if (skeleton == null)
return;
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<String, Animation> anims = new HashMap<String, Animation>();
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;
Animation anim = new Animation(animInfo.name, length);
// Search source layer for animation nodes
long sourceLayerId = 0L;
for (long layerId : alayerMap.keySet()) {
FbxObject layer = alayerMap.get(layerId);
if (layer.name.equals(animInfo.layerName)) {
sourceLayerId = layerId;
break;
}
}
// Build bone tracks
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
// Sorted unique timestamps
Set<Long> stamps = new TreeSet<Long>();
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)
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 firstKeyIndex = 0;
int lastKeyIndex = keyTimes.length - 1;
for (int i = 0; i < keyTimes.length; ++i) {
// Translate into seconds
float time = (float) (((double) keyTimes[i]) * secondsPerUnit);
if (time <= animStart)
firstKeyIndex = i;
if (time >= animStop && animStop >= 0) {
lastKeyIndex = i;
break;
}
}
int keysCount = lastKeyIndex - firstKeyIndex + 1;
if (keysCount <= 0)
continue;
float[] times = new float[keysCount];
Vector3f[] translations = new Vector3f[keysCount];
Quaternion[] rotations = new Quaternion[keysCount];
Vector3f[] scales = null;
// Calculate keyframes times
for (int i = 0; i < keysCount; ++i) {
int keyIndex = firstKeyIndex + i;
// Translate into seconds
float time = (float) (((double) keyTimes[keyIndex]) * secondsPerUnit);
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 = firstKeyIndex + i;
FbxAnimNode n = animTranslation;
// Why do it here but not in other places? FBX magic?
Vector3f tvec = n.getValue(keyTimes[keyIndex], n.value).subtractLocal(n.value);
translations[i] = tvec.divideLocal(unitSize);
}
} else {
for (int i = 0; i < keysCount; ++i) translations[i] = Vector3f.ZERO;
}
RotationOrder ro = RotationOrder.EULER_XYZ;
if (haveRotation) {
for (int i = 0; i < keysCount; ++i) {
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] = Quaternion.IDENTITY;
}
if (haveScale) {
scales = new Vector3f[keysCount];
for (int i = 0; i < keysCount; ++i) {
int keyIndex = firstKeyIndex + i;
FbxAnimNode n = animScale;
Vector3f tvec = n.getValue(keyTimes[keyIndex], n.value);
scales[i] = tvec;
}
}
BoneTrack track = null;
if (haveScale)
track = new BoneTrack(limb.boneIndex, times, translations, rotations, scales);
else
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);
}
use of com.jme3.animation.Skeleton in project jmonkeyengine by jMonkeyEngine.
the class SceneLoader method applySkinning.
private void applySkinning() {
for (FbxBindPose pose : bindMap.values()) pose.fillBindTransforms();
if (limbMap == null)
return;
List<Bone> bones = new ArrayList<>();
for (FbxNode limb : limbMap.values()) {
if (limb.bone != null) {
bones.add(limb.bone);
limb.buildBindPoseBoneTransform();
}
}
skeleton = new Skeleton(bones.toArray(new Bone[bones.size()]));
skeleton.setBindingPose();
for (FbxNode limb : limbMap.values()) limb.setSkeleton(skeleton);
for (FbxSkin skin : skinMap.values()) skin.generateSkinning();
// Attach controls
animControl = new AnimControl(skeleton);
sceneNode.addControl(animControl);
SkeletonControl control = new SkeletonControl(skeleton);
sceneNode.addControl(control);
}
use of com.jme3.animation.Skeleton in project jmonkeyengine by jMonkeyEngine.
the class FbxMesh method applyCluster.
public void applyCluster(FbxCluster cluster) {
if (boneIndices == null) {
boneIndices = new ArrayList[positions.length];
boneWeights = new ArrayList[positions.length];
}
FbxLimbNode limb = cluster.getLimb();
Bone bone = limb.getJmeBone();
Skeleton skeleton = limb.getSkeletonHolder().getJmeSkeleton();
int boneIndex = skeleton.getBoneIndex(bone);
int[] positionIndices = cluster.getVertexIndices();
double[] weights = cluster.getWeights();
for (int i = 0; i < positionIndices.length; i++) {
int positionIndex = positionIndices[i];
float boneWeight = (float) weights[i];
ArrayList<Integer> boneIndicesForVertex = boneIndices[positionIndex];
ArrayList<Float> boneWeightsForVertex = boneWeights[positionIndex];
if (boneIndicesForVertex == null) {
boneIndicesForVertex = new ArrayList<Integer>();
boneWeightsForVertex = new ArrayList<Float>();
boneIndices[positionIndex] = boneIndicesForVertex;
boneWeights[positionIndex] = boneWeightsForVertex;
}
boneIndicesForVertex.add(boneIndex);
boneWeightsForVertex.add(boneWeight);
}
}
use of com.jme3.animation.Skeleton in project jmonkeyengine by jMonkeyEngine.
the class FbxNode method getPreferredParent.
/**
* If this geometry node is deformed by a skeleton, this
* returns the node containing the skeleton.
*
* In jME3, a mesh can be deformed by a skeleton only if it is
* a child of the node containing the skeleton. However, this
* is not a requirement in FBX, so we have to modify the scene graph
* of the loaded model to adjust for this.
* This happens automatically in
* {@link #createScene(com.jme3.scene.plugins.fbx.node.FbxNode)}.
*
* @return The model this node would like to be a child of, or null
* if no preferred parent.
*/
public FbxNode getPreferredParent() {
if (!(nodeAttribute instanceof FbxMesh)) {
return null;
}
FbxMesh fbxMesh = (FbxMesh) nodeAttribute;
FbxSkinDeformer deformer = fbxMesh.getSkinDeformer();
FbxNode preferredParent = null;
if (deformer != null) {
for (FbxCluster cluster : deformer.getJmeObject()) {
FbxLimbNode limb = cluster.getLimb();
if (preferredParent == null) {
preferredParent = limb.getSkeletonHolder();
} else if (preferredParent != limb.getSkeletonHolder()) {
logger.log(Level.WARNING, "A mesh is being deformed by multiple skeletons. " + "Only one skeleton will work, ignoring other skeletons.");
}
}
}
return preferredParent;
}
use of com.jme3.animation.Skeleton in project jmonkeyengine by jMonkeyEngine.
the class SkeletonLoader method load.
public Object load(InputStream in) throws IOException {
try {
// Added by larynx 25.06.2011
// Android needs the namespace aware flag set to true
// Kirill 30.06.2011
// Now, hack is applied for both desktop and android to avoid
// checking with JmeSystem.
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
XMLReader xr = factory.newSAXParser().getXMLReader();
xr.setContentHandler(this);
xr.setErrorHandler(this);
InputStreamReader r = new InputStreamReader(in);
xr.parse(new InputSource(r));
if (animations == null) {
animations = new ArrayList<Animation>();
}
AnimData data = new AnimData(skeleton, animations);
skeleton = null;
animations = null;
return data;
} catch (SAXException ex) {
IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
ioEx.initCause(ex);
fullReset();
throw ioEx;
} catch (ParserConfigurationException ex) {
IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
ioEx.initCause(ex);
fullReset();
throw ioEx;
}
}
Aggregations