/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.tests.g3d; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.VertexAttributes.Usage; import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.Material; 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.attributes.BlendingAttribute; import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight; import com.badlogic.gdx.graphics.g3d.model.Animation; import com.badlogic.gdx.graphics.g3d.model.Node; import com.badlogic.gdx.graphics.g3d.utils.AnimationController; import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider; import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder; 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.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; Node ship; ModelInstance tree; AnimationController animation; DirectionalShadowLight shadowLight; ModelBatch shadowBatch; Environment lights; @Override public void create () { super.create(); lights = new Environment(); lights.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.f)); lights.add((shadowLight = new DirectionalShadowLight(1024, 1024, 30f, 30f, 1f, 100f)) .set(0.8f, 0.8f, 0.8f, -.4f, -.4f, -.4f)); lights.shadowMap = shadowLight; 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); assets.load("data/tree.png", Texture.class); assets.load("data/g3d/ship.obj", Model.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(); builder.node().id = "floor"; MeshPartBuilder part = builder.part("floor", GL20.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal, new Material("concrete")); ((MeshBuilder)part).ensureRectangles(1600); 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); } } builder.node().id = "tree"; part = builder.part("tree", GL20.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal, new Material("tree")); part.rect( 0f, 0f, -10f, 10f, 0f, -10f, 10f, 10f, -10f, 0f, 10f, -10f, 0, 0, 1f); part.setUVRange(1, 0, 0, 1); part.rect(10f, 0f, -10f, 0f, 0f, -10f, 0f, 10f, -10f, 10f, 10f, -10f, 0, 0, -1f); floorModel = builder.end(); shadowBatch = new ModelBatch(new DepthShaderProvider()); } 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(); final Vector3 tmpVector = new Vector3(); 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 public void render () { if (character != null) { animation.update(Gdx.graphics.getDeltaTime()); if (Gdx.input.isKeyPressed(Keys.UP)) { 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 (Gdx.input.isKeyPressed(Keys.DOWN)) { 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 (Gdx.input.isKeyPressed(Keys.RIGHT) && (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 (Gdx.input.isKeyPressed(Keys.LEFT) && (status == walk || status == back) && !animation.inAction) { trTmp.idt().lerp(trLeft, Gdx.graphics.getDeltaTime() / animation.current.animation.duration); character.transform.mul(trTmp.toMatrix4(tmpMatrix)); } if (Gdx.input.isKeyPressed(Keys.SPACE) && !animation.inAction) { animation.action("Attack", 1, 1f, null, 0.2f); } if (Gdx.input.isKeyJustPressed(Keys.Z)) ship.parts.get(0).enabled = !ship.parts.get(0).enabled; } if (character != null) { shadowLight.begin(character.transform.getTranslation(tmpVector), cam.direction); shadowBatch.begin(shadowLight.getCamera()); if (character != null) shadowBatch.render(character); if (tree != null) shadowBatch.render(tree); shadowBatch.end(); shadowLight.end(); } super.render(); } @Override protected void render (ModelBatch batch, Array<ModelInstance> instances) { 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, Z to toggle attached node."); } @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.getMaterial("concrete").set(TextureAttribute.createDiffuse(assets.get("data/g3d/concrete.png", Texture.class))); floorModel.getMaterial("tree").set( TextureAttribute.createDiffuse(assets.get("data/tree.png", Texture.class)), new BlendingAttribute() ); instances.add(new ModelInstance(floorModel, "floor")); instances.add(tree = new ModelInstance(floorModel, "tree")); 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); // Now attach the node of another model at the tip of this knights sword: ship = assets.get("data/g3d/ship.obj", Model.class).nodes.get(0).copy(); ship.detach(); ship.translation.x = 10f; // offset from the sword node to the tip of the sword, in rest pose ship.rotation.set(Vector3.Z, 90f); ship.scale.scl(5f); ship.parts.get(0).enabled = false; character.getNode("sword").addChild(ship); } } @Override public void dispose () { super.dispose(); floorModel.dispose(); shadowLight.dispose(); } }