1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.tests.g3d; 18 19 import com.badlogic.gdx.Gdx; 20 import com.badlogic.gdx.Input.Keys; 21 import com.badlogic.gdx.graphics.GL20; 22 import com.badlogic.gdx.graphics.Texture; 23 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 24 import com.badlogic.gdx.graphics.g3d.Environment; 25 import com.badlogic.gdx.graphics.g3d.Material; 26 import com.badlogic.gdx.graphics.g3d.Model; 27 import com.badlogic.gdx.graphics.g3d.ModelBatch; 28 import com.badlogic.gdx.graphics.g3d.ModelInstance; 29 import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute; 30 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 31 import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; 32 import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; 33 import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight; 34 import com.badlogic.gdx.graphics.g3d.model.Animation; 35 import com.badlogic.gdx.graphics.g3d.model.Node; 36 import com.badlogic.gdx.graphics.g3d.utils.AnimationController; 37 import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider; 38 import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder; 39 import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder; 40 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; 41 import com.badlogic.gdx.math.Matrix4; 42 import com.badlogic.gdx.math.Vector3; 43 import com.badlogic.gdx.math.collision.BoundingBox; 44 import com.badlogic.gdx.utils.Array; 45 import com.badlogic.gdx.utils.StringBuilder; 46 47 public class Animation3DTest extends BaseG3dHudTest { 48 ModelInstance skydome; 49 Model floorModel; 50 ModelInstance character; 51 Node ship; 52 ModelInstance tree; 53 AnimationController animation; 54 DirectionalShadowLight shadowLight; 55 ModelBatch shadowBatch; 56 57 Environment lights; 58 59 @Override create()60 public void create () { 61 super.create(); 62 lights = new Environment(); 63 lights.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.f)); 64 lights.add((shadowLight = new DirectionalShadowLight(1024, 1024, 30f, 30f, 1f, 100f)) 65 .set(0.8f, 0.8f, 0.8f, -.4f, -.4f, -.4f)); 66 lights.shadowMap = shadowLight; 67 inputController.rotateLeftKey = inputController.rotateRightKey = inputController.forwardKey = inputController.backwardKey = 0; 68 cam.position.set(25, 25, 25); 69 cam.lookAt(0, 0, 0); 70 cam.update(); 71 modelsWindow.setVisible(false); 72 assets.load("data/g3d/skydome.g3db", Model.class); 73 assets.load("data/g3d/concrete.png", Texture.class); 74 assets.load("data/tree.png", Texture.class); 75 assets.load("data/g3d/ship.obj", Model.class); 76 loading = true; 77 trForward.translation.set(0, 0, 8f); 78 trBackward.translation.set(0, 0, -8f); 79 trLeft.rotation.setFromAxis(Vector3.Y, 90); 80 trRight.rotation.setFromAxis(Vector3.Y, -90); 81 82 ModelBuilder builder = new ModelBuilder(); 83 builder.begin(); 84 builder.node().id = "floor"; 85 MeshPartBuilder part = builder.part("floor", GL20.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal, 86 new Material("concrete")); 87 ((MeshBuilder)part).ensureRectangles(1600); 88 for (float x = -200f; x < 200f; x += 10f) { 89 for (float z = -200f; z < 200f; z += 10f) { 90 part.rect(x, 0, z + 10f, x + 10f, 0, z + 10f, x + 10f, 0, z, x, 0, z, 0, 1, 0); 91 } 92 } 93 builder.node().id = "tree"; 94 part = builder.part("tree", GL20.GL_TRIANGLES, Usage.Position | Usage.TextureCoordinates | Usage.Normal, 95 new Material("tree")); 96 part.rect( 0f, 0f, -10f, 10f, 0f, -10f, 10f, 10f, -10f, 0f, 10f, -10f, 0, 0, 1f); 97 part.setUVRange(1, 0, 0, 1); 98 part.rect(10f, 0f, -10f, 0f, 0f, -10f, 0f, 10f, -10f, 10f, 10f, -10f, 0, 0, -1f); 99 floorModel = builder.end(); 100 101 shadowBatch = new ModelBatch(new DepthShaderProvider()); 102 } 103 104 final AnimationController.Transform trTmp = new AnimationController.Transform(); 105 final AnimationController.Transform trForward = new AnimationController.Transform(); 106 final AnimationController.Transform trBackward = new AnimationController.Transform(); 107 final AnimationController.Transform trRight = new AnimationController.Transform(); 108 final AnimationController.Transform trLeft = new AnimationController.Transform(); 109 final Matrix4 tmpMatrix = new Matrix4(); 110 final Vector3 tmpVector = new Vector3(); 111 int status = 0; 112 final static int idle = 1; 113 final static int walk = 2; 114 final static int back = 3; 115 final static int attack = 4; 116 float angle = 0f; 117 118 @Override render()119 public void render () { 120 if (character != null) { 121 animation.update(Gdx.graphics.getDeltaTime()); 122 if (Gdx.input.isKeyPressed(Keys.UP)) { 123 if (!animation.inAction) { 124 trTmp.idt().lerp(trForward, Gdx.graphics.getDeltaTime() / animation.current.animation.duration); 125 character.transform.mul(trTmp.toMatrix4(tmpMatrix)); 126 } 127 if (status != walk) { 128 animation.animate("Walk", -1, 1f, null, 0.2f); 129 status = walk; 130 } 131 } else if (Gdx.input.isKeyPressed(Keys.DOWN)) { 132 if (!animation.inAction) { 133 trTmp.idt().lerp(trBackward, Gdx.graphics.getDeltaTime() / animation.current.animation.duration); 134 character.transform.mul(trTmp.toMatrix4(tmpMatrix)); 135 } 136 if (status != back) { 137 animation.animate("Walk", -1, -1f, null, 0.2f); 138 status = back; 139 } 140 } else if (status != idle) { 141 animation.animate("Idle", -1, 1f, null, 0.2f); 142 status = idle; 143 } 144 if (Gdx.input.isKeyPressed(Keys.RIGHT) && (status == walk || status == back) && !animation.inAction) { 145 trTmp.idt().lerp(trRight, Gdx.graphics.getDeltaTime() / animation.current.animation.duration); 146 character.transform.mul(trTmp.toMatrix4(tmpMatrix)); 147 } else if (Gdx.input.isKeyPressed(Keys.LEFT) && (status == walk || status == back) && !animation.inAction) { 148 trTmp.idt().lerp(trLeft, Gdx.graphics.getDeltaTime() / animation.current.animation.duration); 149 character.transform.mul(trTmp.toMatrix4(tmpMatrix)); 150 } 151 if (Gdx.input.isKeyPressed(Keys.SPACE) && !animation.inAction) { 152 animation.action("Attack", 1, 1f, null, 0.2f); 153 } 154 if (Gdx.input.isKeyJustPressed(Keys.Z)) 155 ship.parts.get(0).enabled = !ship.parts.get(0).enabled; 156 } 157 158 if (character != null) { 159 shadowLight.begin(character.transform.getTranslation(tmpVector), cam.direction); 160 shadowBatch.begin(shadowLight.getCamera()); 161 if (character != null) shadowBatch.render(character); 162 if (tree != null) shadowBatch.render(tree); 163 shadowBatch.end(); 164 shadowLight.end(); 165 } 166 super.render(); 167 } 168 169 @Override render(ModelBatch batch, Array<ModelInstance> instances)170 protected void render (ModelBatch batch, Array<ModelInstance> instances) { 171 batch.render(instances, lights); 172 if (skydome != null) batch.render(skydome); 173 } 174 175 @Override getStatus(StringBuilder stringBuilder)176 protected void getStatus (StringBuilder stringBuilder) { 177 super.getStatus(stringBuilder); 178 stringBuilder.append(" use arrow keys to walk around, space to attack, Z to toggle attached node."); 179 } 180 181 @Override onModelClicked(final String name)182 protected void onModelClicked (final String name) { 183 } 184 185 @Override onLoaded()186 protected void onLoaded () { 187 if (skydome == null) { 188 skydome = new ModelInstance(assets.get("data/g3d/skydome.g3db", Model.class)); 189 floorModel.getMaterial("concrete").set(TextureAttribute.createDiffuse(assets.get("data/g3d/concrete.png", Texture.class))); 190 floorModel.getMaterial("tree").set( 191 TextureAttribute.createDiffuse(assets.get("data/tree.png", Texture.class)), 192 new BlendingAttribute() 193 ); 194 instances.add(new ModelInstance(floorModel, "floor")); 195 instances.add(tree = new ModelInstance(floorModel, "tree")); 196 assets.load("data/g3d/knight.g3db", Model.class); 197 loading = true; 198 } else if (character == null) { 199 character = new ModelInstance(assets.get("data/g3d/knight.g3db", Model.class)); 200 BoundingBox bbox = new BoundingBox(); 201 character.calculateBoundingBox(bbox); 202 character.transform.setToRotation(Vector3.Y, 180).trn(0, -bbox.min.y, 0); 203 instances.add(character); 204 animation = new AnimationController(character); 205 animation.animate("Idle", -1, 1f, null, 0.2f); 206 status = idle; 207 for (Animation anim : character.animations) 208 Gdx.app.log("Test", anim.id); 209 // Now attach the node of another model at the tip of this knights sword: 210 ship = assets.get("data/g3d/ship.obj", Model.class).nodes.get(0).copy(); 211 ship.detach(); 212 ship.translation.x = 10f; // offset from the sword node to the tip of the sword, in rest pose 213 ship.rotation.set(Vector3.Z, 90f); 214 ship.scale.scl(5f); 215 ship.parts.get(0).enabled = false; 216 character.getNode("sword").addChild(ship); 217 } 218 } 219 220 @Override dispose()221 public void dispose () { 222 super.dispose(); 223 floorModel.dispose(); 224 shadowLight.dispose(); 225 } 226 } 227