OSDN Git Service

Add AnimationController
authorXoppa <contact@xoppa.nl>
Mon, 3 Jun 2013 21:11:59 +0000 (23:11 +0200)
committerXoppa <contact@xoppa.nl>
Mon, 3 Jun 2013 21:11:59 +0000 (23:11 +0200)
gdx/src/com/badlogic/gdx/graphics/g3d/utils/AnimationController.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/graphics/g3d/utils/BaseAnimationController.java [new file with mode: 0644]
tests/gdx-tests-android/assets/data/g3d/concrete.png [new file with mode: 0644]
tests/gdx-tests-android/assets/data/g3d/skydome.g3db [new file with mode: 0644]
tests/gdx-tests-android/assets/data/g3d/skydome.png [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/Animation3DTest.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java

diff --git a/gdx/src/com/badlogic/gdx/graphics/g3d/utils/AnimationController.java b/gdx/src/com/badlogic/gdx/graphics/g3d/utils/AnimationController.java
new file mode 100644 (file)
index 0000000..0b596d8
--- /dev/null
@@ -0,0 +1,234 @@
+package com.badlogic.gdx.graphics.g3d.utils;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.g3d.ModelInstance;
+import com.badlogic.gdx.graphics.g3d.model.Animation;
+import com.badlogic.gdx.graphics.g3d.model.Node;
+import com.badlogic.gdx.graphics.g3d.model.NodeAnimation;
+import com.badlogic.gdx.graphics.g3d.model.NodeKeyframe;
+import com.badlogic.gdx.math.Matrix4;
+import com.badlogic.gdx.math.Quaternion;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.utils.GdxRuntimeException;
+import com.badlogic.gdx.utils.ObjectMap;
+import com.badlogic.gdx.utils.ObjectMap.Entry;
+import com.badlogic.gdx.utils.Pool;
+import com.badlogic.gdx.utils.Pool.Poolable;
+
+public class AnimationController extends BaseAnimationController {
+       public interface AnimationListener {
+               void onEnd(final AnimationDesc animation);
+               void onLoop(final AnimationDesc animation);
+       }
+       public static class AnimationDesc {
+               /** Listener which will be informed when the animation is looped or ended. */
+               public AnimationListener listener;
+               /** The animation to be applied. */
+               public Animation animation;
+               /** The speed at which to play the animation (can be negative), 1.0 for normal speed. */
+               public float speed;
+               /** The current animation time. */
+               public float time;
+               /** The number of remaining loops, negative for continuous, zero if stopped. */
+               public int loopCount;
+               /** @return the remaining time or 0 if still animating. */
+               public float update(float delta) {
+                       if (loopCount != 0 && animation != null) {
+                               final float duration = animation.duration;
+                               final float diff = speed * delta; 
+                               time += diff;
+                               int loops = (int)Math.abs(time / duration);
+                               if (time < 0f) {
+                                       loops++;
+                                       while (time < 0f)
+                                               time += duration;
+                               }
+                               time = Math.abs(time % duration);
+                               for (int i = 0; i < loops; i++) {
+                                       if (loopCount > 0)
+                                               loopCount--;
+                                       if (listener != null)
+                                               listener.onLoop(this);
+                                       if (loopCount == 0) {
+                                               final float result = ((loops - 1) - i) * duration + (diff < 0f ? duration - time : time); 
+                                               time = (diff < 0f) ? duration : 0f;
+                                               if (listener != null)
+                                                       listener.onEnd(this);
+                                               return result;
+                                       }
+                               }
+                               return 0f;
+                       } else
+                               return delta;
+               }
+       }
+       protected final Pool<AnimationDesc> animationPool = new Pool<AnimationDesc>() {
+               @Override
+               protected AnimationDesc newObject() {
+                       return new AnimationDesc();
+               }
+       };
+       
+       public AnimationDesc current;
+       public AnimationDesc queued;
+       public float queuedTransitionTime;
+       public AnimationDesc previous;
+       public float transitionCurrentTime;
+       public float transitionTargetTime;
+       public boolean inAction;
+
+       public AnimationController (ModelInstance target) {
+               super(target);
+       }
+       
+       private AnimationDesc obtain(final Animation anim, int loopCount, float speed, final AnimationListener listener) {
+               final AnimationDesc result = animationPool.obtain();
+               result.animation = anim;
+               result.listener = listener;
+               result.loopCount = loopCount;
+               result.speed = speed;
+               result.time = speed < 0 ? anim.duration : 0.f;
+               return result;
+       }
+       
+       private AnimationDesc obtain(final String id, int loopCount, float speed, final AnimationListener listener) {
+               final Animation anim = target.getAnimation(id);
+               if (anim == null)
+                       throw new GdxRuntimeException("Unknown animation: "+id);
+               return obtain(anim, loopCount, speed, listener);
+       }
+       
+       private AnimationDesc obtain(final AnimationDesc anim) {
+               return obtain(anim.animation, anim.loopCount, anim.speed, anim.listener);
+       }
+       
+       private boolean updating; //FIXME
+       /** @param delta The time elapsed since last update, change this to alter the overall speed (can be negative). */
+       public void update(float delta) {
+               if (current == null || current.loopCount == 0 || current.animation == null)
+                       return;
+               updating = true;
+               final float remain = current.update(delta);
+               if (remain != 0f && queued != null) {
+                       inAction = false;
+                       animate(queued, queuedTransitionTime);
+                       queued = null;
+                       updating = false;
+                       update(remain);
+                       return;
+               }
+               if (previous != null && ((transitionCurrentTime += delta) >= transitionTargetTime)) {
+                       animationPool.free(previous);
+                       previous = null;
+               }
+               if (previous != null)
+                       applyAnimations(previous.animation, previous.time, current.animation, current.time, transitionCurrentTime / transitionTargetTime);
+               else
+                       applyAnimation(current.animation, current.time);
+               updating = false;
+       }
+       
+       /** Set the active animation, replacing any current animation. */
+       public void setAnimation(final String id, int loopCount, float speed, final AnimationListener listener) {
+               setAnimation(obtain(id, loopCount, speed, listener));
+       }
+       
+       /** Set the active animation, replacing any current animation. */
+       protected void setAnimation(final Animation anim, int loopCount, float speed, final AnimationListener listener) {
+               setAnimation(obtain(anim, loopCount, speed, listener));
+       }
+       
+       /** Set the active animation, replacing any current animation. */
+       protected void setAnimation(final AnimationDesc anim) {
+               if (updating) // FIXME Remove this? Just intended for debugging
+                       throw new GdxRuntimeException("Cannot change animation during update");
+               if (current == null)
+                       current = anim;
+               else {
+                       if (current.animation == anim.animation)
+                               anim.time = current.time;
+                       animationPool.free(current);
+                       current = anim;
+               }
+       }
+       
+       /** Changes the current animation by blending the new on top of the old during the transition time. */
+       public void animate(final String id, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               animate(obtain(id, loopCount, speed, listener), transitionTime);
+       }
+       
+       /** Changes the current animation by blending the new on top of the old during the transition time. */
+       protected void animate(final Animation anim, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               animate(obtain(anim, loopCount, speed, listener), transitionTime);
+       }
+       
+       /** Changes the current animation by blending the new on top of the old during the transition time. */ 
+       protected void animate(final AnimationDesc anim, float transitionTime) {
+               if (current == null)
+                       current = anim;
+               else if (inAction)
+                       queue(anim, transitionTime);
+               else if (current.animation == anim.animation) {
+                       anim.time = current.time;
+                       animationPool.free(current);
+                       current = anim;
+               } else {
+                       if (previous != null)
+                               animationPool.free(previous);
+                       previous = current;
+                       current = anim;
+                       transitionCurrentTime = 0f;
+                       transitionTargetTime = transitionTime;
+               }
+       }
+       
+       /** Queue an animation to be applied when the current is finished. If current is continuous it will be synced on next loop. */
+       public void queue(final String id, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               queue(obtain(id, loopCount, speed, listener), transitionTime);  
+       }
+       
+       /** Queue an animation to be applied when the current is finished. If current is continuous it will be synced on next loop. */
+       protected void queue(final Animation anim, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               queue(obtain(anim, loopCount, speed, listener), transitionTime);
+       }
+       
+       /** Queue an animation to be applied when the current is finished. If current is continuous it will be synced on next loop. */
+       protected void queue(final AnimationDesc anim, float transitionTime) {
+               if (current == null || current.loopCount == 0)
+                       animate(anim, transitionTime);
+               else {
+                       if (queued != null)
+                               animationPool.free(queued);
+                       queued = anim;
+                       queuedTransitionTime = transitionTime;
+                       if (current.loopCount < 0)
+                               current.loopCount = 1;
+               }
+       }
+       
+       /** Apply an action animation on top of the current animation. */
+       public void action(final String id, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               action(obtain(id, loopCount, speed, listener), transitionTime); 
+       }
+       
+       /** Apply an action animation on top of the current animation. */
+       protected void action(final Animation anim, int loopCount, float speed, final AnimationListener listener, float transitionTime) {
+               action(obtain(anim, loopCount, speed, listener), transitionTime);
+       }
+       
+       /** Apply an action animation on top of the current animation. */
+       protected void action(final AnimationDesc anim, float transitionTime) {
+               if (anim.loopCount < 0)
+                       throw new GdxRuntimeException("An action cannot be continuous");
+               if (current == null || current.loopCount == 0)
+                       animate(anim, transitionTime);
+               else {
+                       AnimationDesc toQueue = inAction ? null : obtain(current);
+                       inAction = false;
+                       animate(anim, transitionTime);
+                       inAction = true;
+                       if (toQueue != null)
+                               queue(toQueue, transitionTime);
+               }
+       }
+}
diff --git a/gdx/src/com/badlogic/gdx/graphics/g3d/utils/BaseAnimationController.java b/gdx/src/com/badlogic/gdx/graphics/g3d/utils/BaseAnimationController.java
new file mode 100644 (file)
index 0000000..985d781
--- /dev/null
@@ -0,0 +1,168 @@
+package com.badlogic.gdx.graphics.g3d.utils;
+
+import com.badlogic.gdx.graphics.g3d.ModelInstance;
+import com.badlogic.gdx.graphics.g3d.model.Animation;
+import com.badlogic.gdx.graphics.g3d.model.Node;
+import com.badlogic.gdx.graphics.g3d.model.NodeAnimation;
+import com.badlogic.gdx.graphics.g3d.model.NodeKeyframe;
+import com.badlogic.gdx.math.Matrix4;
+import com.badlogic.gdx.math.Quaternion;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.utils.GdxRuntimeException;
+import com.badlogic.gdx.utils.ObjectMap;
+import com.badlogic.gdx.utils.Pool;
+import com.badlogic.gdx.utils.ObjectMap.Entry;
+import com.badlogic.gdx.utils.Pool.Poolable;
+
+public class BaseAnimationController {
+       public final static class Transform implements Poolable {
+               public final Vector3 translation = new Vector3();
+               public final Quaternion rotation = new Quaternion();
+               public final Vector3 scale = new Vector3(1,1,1);
+               public Transform () { }
+               public Transform idt() {
+                       translation.set(0,0,0);
+                       rotation.idt();
+                       scale.set(1,1,1);
+                       return this;
+               }
+               public Transform set(final Vector3 t, final Quaternion r, final Vector3 s) {
+                       translation.set(t);
+                       rotation.set(r);
+                       scale.set(s);
+                       return this;
+               }
+               public Transform set(final Transform other) {
+                       return set(other.translation, other.rotation, other.scale);
+               }
+               public Transform lerp(final Transform target, final float alpha) {
+                       return lerp(target.translation, target.rotation, target.scale, alpha);
+               }
+               public Transform lerp(final Vector3 targetT, final Quaternion targetR, final Vector3 targetS, final float alpha) {
+                       translation.lerp(targetT, alpha);
+                       rotation.slerp(targetR, alpha);
+                       scale.lerp(targetS, alpha);
+                       return this;
+               }
+               public Matrix4 toMatrix4(final Matrix4 out) {
+                       out.idt();
+                       out.translate(translation);
+                       out.rotate(rotation);
+                       out.scl(scale);
+                       return out;
+               }
+               @Override
+               public void reset () {
+                       idt();
+               }
+       }
+       
+       private final Pool<Transform> transformPool = new Pool<Transform>() {
+               @Override
+               protected Transform newObject () {
+                       return new Transform();
+               }
+       };
+       private final static ObjectMap<Node, Transform> transforms = new ObjectMap<Node, Transform>();
+       private boolean applying = false;
+       public final ModelInstance target;
+       
+       public BaseAnimationController(final ModelInstance target) {
+               this.target = target;
+       }
+       
+       /** Begin applying multiple animations to the instance, 
+        * must followed by one or more calls to {{@link #apply(Animation, float, float)} and finally {{@link #end()}. */
+       protected void begin() {
+               if (applying)
+                       throw new GdxRuntimeException("You must call end() after each call to being()");
+               applying = true;
+       }
+       
+       /** Apply an animation, must be called between {{@link #begin()} and {{@link #end()}.
+        * @param weight The blend weight of this animation relative to the previous applied animations. */
+       protected void apply(final Animation animation, final float time, final float weight) {
+               if (!applying)
+                       throw new GdxRuntimeException("You must call begin() before adding an animation");
+               applyAnimation(transforms, transformPool, weight, animation, time);
+       }
+       
+       /** End applying multiple animations to the instance and update it to reflect the changes. */
+       protected void end() {
+               if (!applying)
+                       throw new GdxRuntimeException("You must call begin() first");
+               for (Entry<Node, Transform> entry : transforms.entries()) {
+                       entry.value.toMatrix4(entry.key.localTransform);
+                       transformPool.free(entry.value);
+               }
+               transforms.clear();
+               target.calculateTransforms();
+               applying = false;
+       }
+       
+       /** Apply a single animation to the {@link ModelInstance} and update the it to reflect the changes. */ 
+       protected void applyAnimation(final Animation animation, final float time) {
+               if (applying)
+                       throw new GdxRuntimeException("Call end() first");
+               applyAnimation(null, null, 1.f, animation, time);
+               target.calculateTransforms();
+       }
+       
+       /** Apply two animations, blending the second onto to first using weight. */
+       protected void applyAnimations(final Animation anim1, final float time1, final Animation anim2, final float time2, final float weight) {
+               if (anim2 == null || weight == 0.f)
+                       applyAnimation(anim1, time1);
+               else if (anim1 == null || weight == 1.f)
+                       applyAnimation(anim2, time2);
+               else if (applying)
+                       throw new GdxRuntimeException("Call end() first");
+               else {
+                       begin();
+                       apply(anim1, time1, 1.f);
+                       apply(anim2, time2, weight);
+                       end();
+               }
+       }
+       
+       private final static Transform tmpT = new Transform();
+       /** Helper method to apply one animation to either an objectmap for blending or directly to the bones. */
+       protected static void applyAnimation(final ObjectMap<Node, Transform> out, final Pool<Transform> pool, final float alpha, final Animation animation, final float time) {
+               for (final NodeAnimation nodeAnim : animation.nodeAnimations) {
+                       final Node node = nodeAnim.node;
+                       node.isAnimated = true;
+                       // Find the keyframe(s)
+                       final int n = nodeAnim.keyframes.size - 1;
+                       int first = 0, second = -1;
+                       for (int i = 0; i < n; i++) {
+                               if (time >= nodeAnim.keyframes.get(i).keytime && time <= nodeAnim.keyframes.get(i+1).keytime) {
+                                       first = i;
+                                       second = i+1;
+                                       break;
+                               }
+                       }
+                       // Apply the first keyframe:
+                       final Transform transform = tmpT;
+                       final NodeKeyframe firstKeyframe = nodeAnim.keyframes.get(first);
+                       transform.set(firstKeyframe.translation, firstKeyframe.rotation, firstKeyframe.scale);
+                       // Lerp the second keyframe
+                       if (second > first) {
+                               final NodeKeyframe secondKeyframe = nodeAnim.keyframes.get(second);
+                               final float t = (time - firstKeyframe.keytime) / (secondKeyframe.keytime - firstKeyframe.keytime);
+                               transform.lerp(secondKeyframe.translation, secondKeyframe.rotation, secondKeyframe.scale, t);
+                       }
+                       // Apply the transform, either directly to the bone or to out when blending
+                       if (out == null)
+                               transform.toMatrix4(node.localTransform);
+                       else {
+                               if (out.containsKey(node)) {
+                                       if (alpha == 1.f)
+                                               out.get(node).set(transform);
+                                       else
+                                               out.get(node).lerp(transform, alpha);
+                               } else {
+                                       out.put(node, pool.obtain().set(transform));
+                               }
+                       }
+               }
+       }
+}
diff --git a/tests/gdx-tests-android/assets/data/g3d/concrete.png b/tests/gdx-tests-android/assets/data/g3d/concrete.png
new file mode 100644 (file)
index 0000000..3960efc
Binary files /dev/null and b/tests/gdx-tests-android/assets/data/g3d/concrete.png differ
diff --git a/tests/gdx-tests-android/assets/data/g3d/skydome.g3db b/tests/gdx-tests-android/assets/data/g3d/skydome.g3db
new file mode 100644 (file)
index 0000000..302505c
Binary files /dev/null and b/tests/gdx-tests-android/assets/data/g3d/skydome.g3db differ
diff --git a/tests/gdx-tests-android/assets/data/g3d/skydome.png b/tests/gdx-tests-android/assets/data/g3d/skydome.png
new file mode 100644 (file)
index 0000000..21818f8
Binary files /dev/null and b/tests/gdx-tests-android/assets/data/g3d/skydome.png differ
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/Animation3DTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/Animation3DTest.java
new file mode 100644 (file)
index 0000000..5c9fbf8
--- /dev/null
@@ -0,0 +1,195 @@
+package com.badlogic.gdx.tests.g3d;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input.Keys;
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.GL10;
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.VertexAttributes.Usage;
+import com.badlogic.gdx.graphics.g3d.Model;
+import com.badlogic.gdx.graphics.g3d.ModelBatch;
+import com.badlogic.gdx.graphics.g3d.ModelInstance;
+import com.badlogic.gdx.graphics.g3d.lights.DirectionalLight;
+import com.badlogic.gdx.graphics.g3d.lights.Lights;
+import com.badlogic.gdx.graphics.g3d.materials.ColorAttribute;
+import com.badlogic.gdx.graphics.g3d.materials.Material;
+import com.badlogic.gdx.graphics.g3d.materials.TextureAttribute;
+import com.badlogic.gdx.graphics.g3d.model.Animation;
+import com.badlogic.gdx.graphics.g3d.model.MeshPart;
+import com.badlogic.gdx.graphics.g3d.model.Node;
+import com.badlogic.gdx.graphics.g3d.model.NodeAnimation;
+import com.badlogic.gdx.graphics.g3d.utils.AnimationController;
+import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
+import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
+import com.badlogic.gdx.math.Matrix4;
+import com.badlogic.gdx.math.Quaternion;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.math.collision.BoundingBox;
+import com.badlogic.gdx.utils.Array;
+import com.badlogic.gdx.utils.StringBuilder;
+
+public class Animation3DTest extends BaseG3dHudTest {
+       ModelInstance skydome;
+       Model floorModel;
+       ModelInstance character;
+       AnimationController animation;
+       
+       Lights lights = new Lights(0.4f, 0.4f, 0.4f).add(
+               new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -.8f, -.2f)
+       );
+
+       @Override
+       public void create () {
+               super.create();
+               inputController.rotateLeftKey = inputController.rotateRightKey = inputController.forwardKey = inputController.backwardKey = 0;
+               cam.position.set(25, 25, 25);
+               cam.lookAt(0, 0, 0);
+               cam.update();
+               modelsWindow.setVisible(false);
+               assets.load("data/g3d/skydome.g3db", Model.class);
+               assets.load("data/g3d/concrete.png", Texture.class);
+               loading = true;
+               trForward.translation.set(0,0,8f);
+               trBackward.translation.set(0,0,-8f);
+               trLeft.rotation.setFromAxis(Vector3.Y, 90);
+               trRight.rotation.setFromAxis(Vector3.Y, -90);
+               
+               ModelBuilder builder = new ModelBuilder();
+               builder.begin();
+               MeshPartBuilder part = builder.part("floor", GL10.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal, new Material());
+               for (float x = -200f; x < 200f; x += 10f) {
+                       for (float z = -200f; z < 200f; z += 10f) {
+                               part.rect(x, 0, z+10f, x+10f, 0, z+10f, x+10f, 0, z, x, 0, z, 0, 1, 0);
+                       }
+               }
+               floorModel = builder.end();
+       }
+
+       final AnimationController.Transform trTmp = new AnimationController.Transform();
+       final AnimationController.Transform trForward = new AnimationController.Transform();
+       final AnimationController.Transform trBackward = new AnimationController.Transform();
+       final AnimationController.Transform trRight = new AnimationController.Transform();
+       final AnimationController.Transform trLeft = new AnimationController.Transform();
+       final Matrix4 tmpMatrix = new Matrix4();
+       int status = 0;
+       final static int idle = 1;
+       final static int walk = 2;
+       final static int back = 3;
+       final static int attack = 4;
+       float angle = 0f;
+       @Override
+       protected void render (ModelBatch batch, Array<ModelInstance> instances) {
+               if (character != null) {
+                       animation.update(Gdx.graphics.getDeltaTime());
+                       if (upKey) {
+                               if (!animation.inAction) {
+                                       trTmp.idt().lerp(trForward, Gdx.graphics.getDeltaTime() / animation.current.animation.duration);
+                                       character.transform.mul(trTmp.toMatrix4(tmpMatrix));
+                               }
+                               if (status != walk) {
+                                       animation.animate("Walk", -1, 1f, null, 0.2f);
+                                       status = walk;
+                               }
+                       } else if (downKey) {
+                               if (!animation.inAction) {
+                                       trTmp.idt().lerp(trBackward, Gdx.graphics.getDeltaTime() / animation.current.animation.duration);
+                                       character.transform.mul(trTmp.toMatrix4(tmpMatrix));
+                               }
+                               if (status != back) {
+                                       animation.animate("Walk", -1, -1f, null, 0.2f);
+                                       status = back;
+                               }
+                       } else if (status != idle) {
+                                       animation.animate("Idle", -1, 1f, null, 0.2f);
+                                       status = idle;
+                       }
+                       if (rightKey && (status == walk || status == back) && !animation.inAction) {
+                               trTmp.idt().lerp(trRight, Gdx.graphics.getDeltaTime() / animation.current.animation.duration);
+                               character.transform.mul(trTmp.toMatrix4(tmpMatrix));
+                       } else if (leftKey && (status == walk || status == back) && !animation.inAction) {
+                               trTmp.idt().lerp(trLeft, Gdx.graphics.getDeltaTime() / animation.current.animation.duration);
+                               character.transform.mul(trTmp.toMatrix4(tmpMatrix));
+                       }
+                       if (spaceKey && !animation.inAction) {
+                               animation.action("Attack", 1, 1f, null, 0.2f);
+                       }
+               }
+               batch.render(instances, lights);
+               if (skydome != null)
+                       batch.render(skydome);
+       }
+       
+       @Override
+       protected void getStatus (StringBuilder stringBuilder) {
+               super.getStatus(stringBuilder);
+               stringBuilder.append(" use arrow keys to walk around, space to attack.");
+       }
+       
+       @Override
+       protected void onModelClicked(final String name) {      }
+       
+       @Override
+       protected void onLoaded() {
+               if (skydome == null) {
+                       skydome = new ModelInstance(assets.get("data/g3d/skydome.g3db", Model.class));
+                       floorModel.materials.get(0).set(TextureAttribute.createDiffuse(assets.get("data/g3d/concrete.png", Texture.class)));
+                       instances.add(new ModelInstance(floorModel));
+                       assets.load("data/g3d/knight.g3db", Model.class);
+                       loading = true;
+               }
+               else if (character == null) {
+                       character = new ModelInstance(assets.get("data/g3d/knight.g3db", Model.class));
+                       BoundingBox bbox = new BoundingBox();
+                       character.calculateBoundingBox(bbox);
+                       character.transform.setToRotation(Vector3.Y, 180).trn(0, -bbox.min.y, 0);
+                       instances.add(character);
+                       animation = new AnimationController(character);
+                       animation.animate("Idle", -1, 1f, null, 0.2f);
+                       status = idle;
+                       for (Animation anim : character.animations)
+                               Gdx.app.log("Test", anim.id);
+               }
+       }
+
+       @Override
+       public boolean needsGL20 () {
+               return true;
+       }
+       
+       boolean rightKey, leftKey, upKey, downKey, spaceKey;
+       @Override
+       public boolean keyUp (int keycode) {
+               if (keycode == Keys.LEFT)
+                       leftKey = false;
+               if (keycode == Keys.RIGHT)
+                       rightKey = false;
+               if (keycode == Keys.UP)
+                       upKey = false;
+               if (keycode == Keys.DOWN)
+                       downKey = false;
+               if (keycode == Keys.SPACE)
+                       spaceKey = false;
+               return super.keyUp(keycode);
+       }
+       
+       @Override
+       public boolean keyDown (int keycode) {
+               if (keycode == Keys.LEFT)
+                       leftKey = true;
+               if (keycode == Keys.RIGHT)
+                       rightKey = true;
+               if (keycode == Keys.UP)
+                       upKey = true;
+               if (keycode == Keys.DOWN)
+                       downKey = true;
+               if (keycode == Keys.SPACE)
+                       spaceKey = true;
+               return super.keyDown(keycode);
+       }
+       
+       @Override
+       public void dispose () {
+               super.dispose();
+               floorModel.dispose();
+       }
+}
\ No newline at end of file
index 6e125ab..9d894a3 100644 (file)
@@ -36,6 +36,7 @@ import java.util.List;
 import com.badlogic.gdx.tests.*;\r
 import com.badlogic.gdx.tests.bench.TiledMapBench;\r
 import com.badlogic.gdx.tests.examples.MoveSpriteExample;\r
+import com.badlogic.gdx.tests.g3d.Animation3DTest;\r
 import com.badlogic.gdx.tests.g3d.Basic3DTest;\r
 import com.badlogic.gdx.tests.g3d.MaterialTest;\r
 import com.badlogic.gdx.tests.g3d.ModelTest;\r
@@ -83,7 +84,7 @@ public class GdxTests {
                TextButtonTest.class, TextButtonTestGL2.class, TextureBindTest.class, SortedSpriteTest.class,\r
                ExternalMusicTest.class, SoftKeyboardTest.class, DirtyRenderingTest.class, YDownTest.class,\r
                ScreenCaptureTest.class, BitmapFontTest.class, LabelScaleTest.class, GamepadTest.class, NetAPITest.class, TideMapAssetManagerTest.class, TideMapDirectLoaderTest.class, TiledMapAssetManagerTest.class, TiledMapBench.class,\r
-               RunnablePostTest.class, Vector2dTest.class, SuperKoalio.class, NinePatchTest.class, Basic3DSceneTest.class,\r
+               RunnablePostTest.class, Vector2dTest.class, SuperKoalio.class, NinePatchTest.class, Basic3DSceneTest.class, Animation3DTest.class,\r
                ModelTest.class, Basic3DTest.class, ShaderTest.class, SkeletonTest.class, HexagonalTiledMapTest.class));\r
        \r
        public static List<String> getNames () {\r