1/*
2 * Copyright (c) 2009-2010 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 */
32package jme3test.bullet;
33
34import com.jme3.animation.*;
35import com.jme3.app.SimpleApplication;
36import com.jme3.asset.TextureKey;
37import com.jme3.bullet.BulletAppState;
38import com.jme3.bullet.PhysicsSpace;
39import com.jme3.bullet.collision.PhysicsCollisionEvent;
40import com.jme3.bullet.collision.PhysicsCollisionObject;
41import com.jme3.bullet.collision.RagdollCollisionListener;
42import com.jme3.bullet.collision.shapes.SphereCollisionShape;
43import com.jme3.bullet.control.KinematicRagdollControl;
44import com.jme3.bullet.control.RigidBodyControl;
45import com.jme3.font.BitmapText;
46import com.jme3.input.KeyInput;
47import com.jme3.input.MouseInput;
48import com.jme3.input.controls.ActionListener;
49import com.jme3.input.controls.KeyTrigger;
50import com.jme3.input.controls.MouseButtonTrigger;
51import com.jme3.light.DirectionalLight;
52import com.jme3.material.Material;
53import com.jme3.math.ColorRGBA;
54import com.jme3.math.FastMath;
55import com.jme3.math.Quaternion;
56import com.jme3.math.Vector3f;
57import com.jme3.scene.Geometry;
58import com.jme3.scene.Node;
59import com.jme3.scene.debug.SkeletonDebugger;
60import com.jme3.scene.shape.Sphere;
61import com.jme3.scene.shape.Sphere.TextureMode;
62import com.jme3.texture.Texture;
63
64/**
65 * PHYSICS RAGDOLLS ARE NOT WORKING PROPERLY YET!
66 * @author normenhansen
67 */
68public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisionListener, AnimEventListener {
69
70    private BulletAppState bulletAppState;
71    Material matBullet;
72    Node model;
73    KinematicRagdollControl ragdoll;
74    float bulletSize = 1f;
75    Material mat;
76    Material mat3;
77    private Sphere bullet;
78    private SphereCollisionShape bulletCollisionShape;
79
80    public static void main(String[] args) {
81        TestBoneRagdoll app = new TestBoneRagdoll();
82        app.start();
83    }
84
85    public void simpleInitApp() {
86        initCrossHairs();
87        initMaterial();
88
89        cam.setLocation(new Vector3f(0.26924422f, 6.646658f, 22.265987f));
90        cam.setRotation(new Quaternion(-2.302544E-4f, 0.99302495f, -0.117888905f, -0.0019395084f));
91
92
93        bulletAppState = new BulletAppState();
94        bulletAppState.setEnabled(true);
95        stateManager.attach(bulletAppState);
96        bullet = new Sphere(32, 32, 1.0f, true, false);
97        bullet.setTextureMode(TextureMode.Projected);
98        bulletCollisionShape = new SphereCollisionShape(1.0f);
99
100//        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
101        PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
102        setupLight();
103
104        model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
105
106        //  model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
107
108        //debug view
109        AnimControl control = model.getControl(AnimControl.class);
110        SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", control.getSkeleton());
111        Material mat2 = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
112        mat2.getAdditionalRenderState().setWireframe(true);
113        mat2.setColor("Color", ColorRGBA.Green);
114        mat2.getAdditionalRenderState().setDepthTest(false);
115        skeletonDebug.setMaterial(mat2);
116        skeletonDebug.setLocalTranslation(model.getLocalTranslation());
117
118        //Note: PhysicsRagdollControl is still TODO, constructor will change
119        ragdoll = new KinematicRagdollControl(0.5f);
120        setupSinbad(ragdoll);
121        ragdoll.addCollisionListener(this);
122        model.addControl(ragdoll);
123
124        float eighth_pi = FastMath.PI * 0.125f;
125        ragdoll.setJointLimit("Waist", eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi, eighth_pi);
126        ragdoll.setJointLimit("Chest", eighth_pi, eighth_pi, 0, 0, eighth_pi, eighth_pi);
127
128
129        //Oto's head is almost rigid
130        //    ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0);
131
132        getPhysicsSpace().add(ragdoll);
133        speed = 1.3f;
134
135        rootNode.attachChild(model);
136        // rootNode.attachChild(skeletonDebug);
137        flyCam.setMoveSpeed(50);
138
139
140        animChannel = control.createChannel();
141        animChannel.setAnim("Dance");
142        control.addListener(this);
143
144        inputManager.addListener(new ActionListener() {
145
146            public void onAction(String name, boolean isPressed, float tpf) {
147                if (name.equals("toggle") && isPressed) {
148
149                    Vector3f v = new Vector3f();
150                    v.set(model.getLocalTranslation());
151                    v.y = 0;
152                    model.setLocalTranslation(v);
153                    Quaternion q = new Quaternion();
154                    float[] angles = new float[3];
155                    model.getLocalRotation().toAngles(angles);
156                    q.fromAngleAxis(angles[1], Vector3f.UNIT_Y);
157                    model.setLocalRotation(q);
158                    if (angles[0] < 0) {
159                        animChannel.setAnim("StandUpBack");
160                        ragdoll.blendToKinematicMode(0.5f);
161                    } else {
162                        animChannel.setAnim("StandUpFront");
163                        ragdoll.blendToKinematicMode(0.5f);
164                    }
165
166                }
167                if (name.equals("bullet+") && isPressed) {
168                    bulletSize += 0.1f;
169
170                }
171                if (name.equals("bullet-") && isPressed) {
172                    bulletSize -= 0.1f;
173
174                }
175
176                if (name.equals("stop") && isPressed) {
177                    ragdoll.setEnabled(!ragdoll.isEnabled());
178                    ragdoll.setRagdollMode();
179                }
180
181                if (name.equals("shoot") && !isPressed) {
182                    Geometry bulletg = new Geometry("bullet", bullet);
183                    bulletg.setMaterial(matBullet);
184                    bulletg.setLocalTranslation(cam.getLocation());
185                    bulletg.setLocalScale(bulletSize);
186                    bulletCollisionShape = new SphereCollisionShape(bulletSize);
187                    RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, bulletSize * 10);
188                    bulletNode.setCcdMotionThreshold(0.001f);
189                    bulletNode.setLinearVelocity(cam.getDirection().mult(80));
190                    bulletg.addControl(bulletNode);
191                    rootNode.attachChild(bulletg);
192                    getPhysicsSpace().add(bulletNode);
193                }
194                if (name.equals("boom") && !isPressed) {
195                    Geometry bulletg = new Geometry("bullet", bullet);
196                    bulletg.setMaterial(matBullet);
197                    bulletg.setLocalTranslation(cam.getLocation());
198                    bulletg.setLocalScale(bulletSize);
199                    bulletCollisionShape = new SphereCollisionShape(bulletSize);
200                    BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
201                    bulletNode.setForceFactor(8);
202                    bulletNode.setExplosionRadius(20);
203                    bulletNode.setCcdMotionThreshold(0.001f);
204                    bulletNode.setLinearVelocity(cam.getDirection().mult(180));
205                    bulletg.addControl(bulletNode);
206                    rootNode.attachChild(bulletg);
207                    getPhysicsSpace().add(bulletNode);
208                }
209            }
210        }, "toggle", "shoot", "stop", "bullet+", "bullet-", "boom");
211        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
212        inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
213        inputManager.addMapping("boom", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
214        inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_H));
215        inputManager.addMapping("bullet-", new KeyTrigger(KeyInput.KEY_COMMA));
216        inputManager.addMapping("bullet+", new KeyTrigger(KeyInput.KEY_PERIOD));
217
218
219    }
220
221    private void setupLight() {
222        // AmbientLight al = new AmbientLight();
223        //  al.setColor(ColorRGBA.White.mult(1));
224        //   rootNode.addLight(al);
225
226        DirectionalLight dl = new DirectionalLight();
227        dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
228        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
229        rootNode.addLight(dl);
230    }
231
232    private PhysicsSpace getPhysicsSpace() {
233        return bulletAppState.getPhysicsSpace();
234    }
235
236    public void initMaterial() {
237
238        matBullet = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
239        TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
240        key2.setGenerateMips(true);
241        Texture tex2 = assetManager.loadTexture(key2);
242        matBullet.setTexture("ColorMap", tex2);
243    }
244
245    protected void initCrossHairs() {
246        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
247        BitmapText ch = new BitmapText(guiFont, false);
248        ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
249        ch.setText("+"); // crosshairs
250        ch.setLocalTranslation( // center
251                settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
252                settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
253        guiNode.attachChild(ch);
254    }
255
256    public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event) {
257
258        if (object.getUserObject() != null && object.getUserObject() instanceof Geometry) {
259            Geometry geom = (Geometry) object.getUserObject();
260            if ("Floor".equals(geom.getName())) {
261                return;
262            }
263        }
264
265        ragdoll.setRagdollMode();
266
267    }
268
269    private void setupSinbad(KinematicRagdollControl ragdoll) {
270        ragdoll.addBoneName("Ulna.L");
271        ragdoll.addBoneName("Ulna.R");
272        ragdoll.addBoneName("Chest");
273        ragdoll.addBoneName("Foot.L");
274        ragdoll.addBoneName("Foot.R");
275        ragdoll.addBoneName("Hand.R");
276        ragdoll.addBoneName("Hand.L");
277        ragdoll.addBoneName("Neck");
278        ragdoll.addBoneName("Root");
279        ragdoll.addBoneName("Stomach");
280        ragdoll.addBoneName("Waist");
281        ragdoll.addBoneName("Humerus.L");
282        ragdoll.addBoneName("Humerus.R");
283        ragdoll.addBoneName("Thigh.L");
284        ragdoll.addBoneName("Thigh.R");
285        ragdoll.addBoneName("Calf.L");
286        ragdoll.addBoneName("Calf.R");
287        ragdoll.addBoneName("Clavicle.L");
288        ragdoll.addBoneName("Clavicle.R");
289
290    }
291    float elTime = 0;
292    boolean forward = true;
293    AnimControl animControl;
294    AnimChannel animChannel;
295    Vector3f direction = new Vector3f(0, 0, 1);
296    Quaternion rotate = new Quaternion().fromAngleAxis(FastMath.PI / 8, Vector3f.UNIT_Y);
297    boolean dance = true;
298
299    @Override
300    public void simpleUpdate(float tpf) {
301        // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
302//        elTime += tpf;
303//        if (elTime > 3) {
304//            elTime = 0;
305//            if (dance) {
306//                rotate.multLocal(direction);
307//            }
308//            if (Math.random() > 0.80) {
309//                dance = true;
310//                animChannel.setAnim("Dance");
311//            } else {
312//                dance = false;
313//                animChannel.setAnim("RunBase");
314//                rotate.fromAngleAxis(FastMath.QUARTER_PI * ((float) Math.random() - 0.5f), Vector3f.UNIT_Y);
315//                rotate.multLocal(direction);
316//            }
317//        }
318//        if (!ragdoll.hasControl() && !dance) {
319//            if (model.getLocalTranslation().getZ() < -10) {
320//                direction.z = 1;
321//                direction.normalizeLocal();
322//            } else if (model.getLocalTranslation().getZ() > 10) {
323//                direction.z = -1;
324//                direction.normalizeLocal();
325//            }
326//            if (model.getLocalTranslation().getX() < -10) {
327//                direction.x = 1;
328//                direction.normalizeLocal();
329//            } else if (model.getLocalTranslation().getX() > 10) {
330//                direction.x = -1;
331//                direction.normalizeLocal();
332//            }
333//            model.move(direction.multLocal(tpf * 8));
334//            direction.normalizeLocal();
335//            model.lookAt(model.getLocalTranslation().add(direction), Vector3f.UNIT_Y);
336//        }
337    }
338
339    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
340//        if(channel.getAnimationName().equals("StandUpFront")){
341//            channel.setAnim("Dance");
342//        }
343
344        if (channel.getAnimationName().equals("StandUpBack") || channel.getAnimationName().equals("StandUpFront")) {
345            channel.setLoopMode(LoopMode.DontLoop);
346            channel.setAnim("IdleTop", 5);
347            channel.setLoopMode(LoopMode.Loop);
348        }
349//        if(channel.getAnimationName().equals("IdleTop")){
350//            channel.setAnim("StandUpFront");
351//        }
352
353    }
354
355    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
356    }
357}
358