Skip to content

Commit a59b9e6

Browse files
committed
glTF: Skip tracks that belong to a different skin
1 parent 5dbbaf0 commit a59b9e6

File tree

3 files changed

+330
-25
lines changed

3 files changed

+330
-25
lines changed
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
/*
2+
* Copyright (c) 2009-2012 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package jme3test.model;
33+
34+
import com.jme3.animation.*;
35+
import com.jme3.app.ChaseCameraAppState;
36+
import com.jme3.app.SimpleApplication;
37+
import com.jme3.input.KeyInput;
38+
import com.jme3.input.controls.ActionListener;
39+
import com.jme3.input.controls.KeyTrigger;
40+
import com.jme3.math.*;
41+
import com.jme3.renderer.Limits;
42+
import com.jme3.scene.Node;
43+
import com.jme3.scene.Spatial;
44+
import com.jme3.scene.control.Control;
45+
import com.jme3.scene.debug.custom.SkeletonDebugAppState;
46+
import com.jme3.scene.plugins.gltf.GltfModelKey;
47+
48+
import java.util.*;
49+
50+
public class TestGltfLoading2 extends SimpleApplication {
51+
52+
Node autoRotate = new Node("autoRotate");
53+
List<Spatial> assets = new ArrayList<>();
54+
Node probeNode;
55+
float time = 0;
56+
int assetIndex = 0;
57+
boolean useAutoRotate = false;
58+
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
59+
int duration = 2;
60+
boolean playAnim = true;
61+
62+
public static void main(String[] args) {
63+
TestGltfLoading2 app = new TestGltfLoading2();
64+
app.start();
65+
}
66+
67+
/*
68+
WARNING this test case can't wok without the assets, and considering their size, they are not pushed into the repo
69+
you can find them here :
70+
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0
71+
https://sketchfab.com/features/gltf
72+
You have to copy them in Model/gltf folder in the test-data project.
73+
*/
74+
public void simpleInitApp() {
75+
76+
SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
77+
getStateManager().attach(skeletonDebugAppState);
78+
79+
// cam.setLocation(new Vector3f(4.0339394f, 2.645184f, 6.4627485f));
80+
// cam.setRotation(new Quaternion(-0.013950467f, 0.98604023f, -0.119502485f, -0.11510504f));
81+
cam.setFrustumPerspective(45f, (float) cam.getWidth() / cam.getHeight(), 0.1f, 100f);
82+
renderer.setDefaultAnisotropicFilter(Math.min(renderer.getLimits().get(Limits.TextureAnisotropy), 8));
83+
setPauseOnLostFocus(false);
84+
85+
flyCam.setMoveSpeed(5);
86+
flyCam.setDragToRotate(true);
87+
flyCam.setEnabled(false);
88+
viewPort.setBackgroundColor(new ColorRGBA().setAsSrgb(0.2f, 0.2f, 0.2f, 1.0f));
89+
rootNode.attachChild(autoRotate);
90+
probeNode = (Node) assetManager.loadModel("Scenes/defaultProbe.j3o");
91+
autoRotate.attachChild(probeNode);
92+
93+
// DirectionalLight dl = new DirectionalLight();
94+
// dl.setDirection(new Vector3f(-1f, -1.0f, -1f).normalizeLocal());
95+
// dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
96+
// rootNode.addLight(dl);
97+
98+
// DirectionalLight dl2 = new DirectionalLight();
99+
// dl2.setDirection(new Vector3f(1f, 1.0f, 1f).normalizeLocal());
100+
// dl2.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
101+
// rootNode.addLight(dl2);
102+
103+
// PointLight pl = new PointLight(new Vector3f(5.0f, 5.0f, 5.0f), ColorRGBA.White, 30);
104+
// rootNode.addLight(pl);
105+
// PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
106+
// rootNode.addLight(pl1);
107+
//loadModel("Models/gltf/buffalo/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
108+
//loadModel("Models/gltf/war/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
109+
loadModel("Models/gltf/ganjaarl/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
110+
//loadModel("Models/gltf/hero/scene.gltf", new Vector3f(0, -1, 0), 0.1f);
111+
//loadModel("Models/gltf/mercy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
112+
//loadModel("Models/gltf/crab/scene.gltf", Vector3f.ZERO, 1);
113+
//loadModel("Models/gltf/manta/scene.gltf", Vector3f.ZERO, 0.2f);
114+
// loadModel("Models/gltf/bone/scene.gltf", Vector3f.ZERO, 0.1f);
115+
// loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
116+
// loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
117+
// loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
118+
// loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
119+
//// loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
120+
//loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
121+
// loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
122+
//
123+
// //loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
124+
//
125+
//loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
126+
//loadModel("Models/gltf/RiggedFigure/RiggedFigure.gltf", new Vector3f(0, -1f, 0), 1f);
127+
//loadModel("Models/gltf/CesiumMan/CesiumMan.gltf", new Vector3f(0, -1, 0), 1f);
128+
//loadModel("Models/gltf/BrainStem/BrainStem.gltf", new Vector3f(0, -1, 0), 1f);
129+
//loadModel("Models/gltf/Jaime/Jaime.gltf", new Vector3f(0, -1, 0), 1f);
130+
//loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
131+
//loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
132+
//loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
133+
134+
// loadModel("Models/gltf/corset/Corset.gltf", new Vector3f(0, -1, 0), 20f);
135+
// loadModel("Models/gltf/boxInter/BoxInterleaved.gltf", new Vector3f(0, 0, 0), 1f);
136+
137+
138+
probeNode.attachChild(assets.get(0));
139+
140+
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
141+
chaseCam.setTarget(probeNode);
142+
getStateManager().attach(chaseCam);
143+
chaseCam.setInvertHorizontalAxis(true);
144+
chaseCam.setInvertVerticalAxis(true);
145+
chaseCam.setZoomSpeed(0.5f);
146+
chaseCam.setMinVerticalRotation(-FastMath.HALF_PI);
147+
chaseCam.setRotationSpeed(3);
148+
chaseCam.setDefaultDistance(3);
149+
chaseCam.setDefaultVerticalRotation(0.3f);
150+
151+
inputManager.addMapping("autorotate", new KeyTrigger(KeyInput.KEY_SPACE));
152+
inputManager.addListener(new ActionListener() {
153+
@Override
154+
public void onAction(String name, boolean isPressed, float tpf) {
155+
if (isPressed) {
156+
useAutoRotate = !useAutoRotate;
157+
}
158+
}
159+
}, "autorotate");
160+
161+
inputManager.addMapping("toggleAnim", new KeyTrigger(KeyInput.KEY_RETURN));
162+
163+
inputManager.addListener(new ActionListener() {
164+
@Override
165+
public void onAction(String name, boolean isPressed, float tpf) {
166+
if (isPressed) {
167+
playAnim = !playAnim;
168+
if (playAnim) {
169+
playFirstAnim(rootNode);
170+
} else {
171+
stopAnim(rootNode);
172+
}
173+
}
174+
}
175+
}, "toggleAnim");
176+
inputManager.addMapping("nextAnim", new KeyTrigger(KeyInput.KEY_RIGHT));
177+
inputManager.addListener(new ActionListener() {
178+
@Override
179+
public void onAction(String name, boolean isPressed, float tpf) {
180+
if (isPressed && animControl != null) {
181+
AnimChannel c = animControl.getChannel(0);
182+
if (c == null) {
183+
c = animControl.createChannel();
184+
}
185+
String anim = anims.poll();
186+
anims.add(anim);
187+
c.setAnim(anim);
188+
}
189+
}
190+
}, "nextAnim");
191+
192+
dumpScene(rootNode, 0);
193+
}
194+
195+
private <T extends Control> T findControl(Spatial s, Class<T> controlClass) {
196+
T ctrl = s.getControl(controlClass);
197+
if (ctrl != null) {
198+
return ctrl;
199+
}
200+
if (s instanceof Node) {
201+
Node n = (Node) s;
202+
for (Spatial spatial : n.getChildren()) {
203+
ctrl = findControl(spatial, controlClass);
204+
if (ctrl != null) {
205+
return ctrl;
206+
}
207+
}
208+
}
209+
return null;
210+
}
211+
212+
private void loadModel(String path, Vector3f offset, float scale) {
213+
GltfModelKey k = new GltfModelKey(path);
214+
//k.setKeepSkeletonPose(true);
215+
Spatial s = assetManager.loadModel(k);
216+
s.scale(scale);
217+
s.move(offset);
218+
assets.add(s);
219+
if (playAnim) {
220+
playFirstAnim(s);
221+
}
222+
223+
SkeletonControl ctrl = findControl(s, SkeletonControl.class);
224+
225+
// ctrl.getSpatial().removeControl(ctrl);
226+
if (ctrl == null) {
227+
return;
228+
}
229+
//System.err.println(ctrl.getSkeleton().toString());
230+
//ctrl.setHardwareSkinningPreferred(false);
231+
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
232+
// AnimControl aCtrl = findControl(s, AnimControl.class);
233+
// //ctrl.getSpatial().removeControl(ctrl);
234+
// if (aCtrl == null) {
235+
// return;
236+
// }
237+
// if (aCtrl.getSkeleton() != null) {
238+
// getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getSkeleton(), aCtrl.getSpatial(), true);
239+
// }
240+
241+
}
242+
243+
Queue<String> anims = new LinkedList<>();
244+
AnimControl animControl;
245+
246+
private void playFirstAnim(Spatial s) {
247+
248+
AnimControl control = s.getControl(AnimControl.class);
249+
if (control != null) {
250+
anims.clear();
251+
for (String name : control.getAnimationNames()) {
252+
anims.add(name);
253+
}
254+
if (anims.isEmpty()) {
255+
return;
256+
}
257+
String anim = anims.poll();
258+
anims.add(anim);
259+
control.createChannel().setAnim(anim);
260+
animControl = control;
261+
}
262+
if (s instanceof Node) {
263+
Node n = (Node) s;
264+
for (Spatial spatial : n.getChildren()) {
265+
playFirstAnim(spatial);
266+
}
267+
}
268+
}
269+
270+
private void stopAnim(Spatial s) {
271+
272+
AnimControl control = s.getControl(AnimControl.class);
273+
if (control != null) {
274+
for (int i = 0; i < control.getNumChannels(); i++) {
275+
AnimChannel ch = control.getChannel(i);
276+
ch.reset(true);
277+
}
278+
control.clearChannels();
279+
}
280+
if (s instanceof Node) {
281+
Node n = (Node) s;
282+
for (Spatial spatial : n.getChildren()) {
283+
stopAnim(spatial);
284+
}
285+
}
286+
}
287+
288+
@Override
289+
public void simpleUpdate(float tpf) {
290+
291+
if (!useAutoRotate) {
292+
return;
293+
}
294+
time += tpf;
295+
autoRotate.rotate(0, tpf * 0.5f, 0);
296+
if (time > duration) {
297+
assets.get(assetIndex).removeFromParent();
298+
assetIndex = (assetIndex + 1) % assets.size();
299+
if (assetIndex == 0) {
300+
duration = 10;
301+
}
302+
probeNode.attachChild(assets.get(assetIndex));
303+
time = 0;
304+
}
305+
}
306+
307+
private void dumpScene(Spatial s, int indent) {
308+
System.err.println(indentString.substring(0, indent) + s.getName() + " (" + s.getClass().getSimpleName() + ") / " +
309+
s.getLocalTransform().getTranslation().toString() + ", " +
310+
s.getLocalTransform().getRotation().toString() + ", " +
311+
s.getLocalTransform().getScale().toString());
312+
if (s instanceof Node) {
313+
Node n = (Node) s;
314+
for (Spatial spatial : n.getChildren()) {
315+
dumpScene(spatial, indent + 1);
316+
}
317+
}
318+
}
319+
}

jme3-examples/src/main/java/jme3test/model/shape/TestGltfLoading2.java

Lines changed: 0 additions & 7 deletions
This file was deleted.

jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -734,16 +734,6 @@ public void readAnimation(int animationIndex) throws IOException {
734734
continue;
735735
}
736736

737-
//if targetNode is a bone, check if it's in a used skin.
738-
BoneWrapper bw = fetchFromCache("nodes", targetNode, BoneWrapper.class);
739-
if (bw != null) {
740-
SkinData skin = fetchFromCache("skins", bw.skinIndex, SkinData.class);
741-
if (skin == null || !skin.used) {
742-
//this skin is not referenced by any mesh, let's not load animation for it.
743-
continue;
744-
}
745-
}
746-
747737
TrackData trackData = tracks[targetNode];
748738
if (trackData == null) {
749739
trackData = new TrackData();
@@ -824,17 +814,20 @@ public void readAnimation(int animationIndex) throws IOException {
824814
//apply the inverseBindMatrix to animation data.
825815
b.update(trackData);
826816
usedBones.add(b.bone);
827-
BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
828-
anim.addTrack(track);
817+
829818
if (skinIndex == -1) {
830819
skinIndex = b.skinIndex;
831820
} else {
832-
//Check if all bones affected by this animation are from the same skin, otherwise raise an error.
821+
//Check if all bones affected by this animation are from the same skin, the track will be skipped.
833822
if (skinIndex != b.skinIndex) {
834-
throw new AssetLoadException("Animation " + animationIndex + " (" + name + ") applies to bones that are not from the same skin: skin " + skinIndex + ", bone " + b.bone.getName() + " from skin " + b.skinIndex);
823+
logger.log(Level.WARNING, "Animation " + animationIndex + " (" + name + ") applies to bones that are not from the same skin: skin " + skinIndex + ", bone " + b.bone.getName() + " from skin " + b.skinIndex);
824+
continue;
835825
}
836-
//else everything is fine.
837826
}
827+
828+
BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
829+
anim.addTrack(track);
830+
838831
}
839832
}
840833

@@ -1232,13 +1225,13 @@ public void update(TrackData data) {
12321225

12331226
reverseBlendAnimTransforms(t, bindTransforms);
12341227

1235-
if(data.translations != null) {
1228+
if (data.translations != null) {
12361229
data.translations[i] = t.getTranslation();
12371230
}
1238-
if(data.rotations != null) {
1231+
if (data.rotations != null) {
12391232
data.rotations[i] = t.getRotation();
12401233
}
1241-
if(data.scales != null) {
1234+
if (data.scales != null) {
12421235
data.scales[i] = t.getScale();
12431236
}
12441237
}

0 commit comments

Comments
 (0)