浏览代码

RagdollControl ---WIP----
- Automatic creation of the ragdoll collisions shapes
- Changed TestBoneRagdoll (press space to activate physics, left click to shoot bombs)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7151 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

rem..om 14 年之前
父节点
当前提交
50d55b7e59

+ 13 - 1
engine/src/core/com/jme3/animation/AnimControl.java

@@ -32,6 +32,7 @@
 
 package com.jme3.animation;
 
+import com.jme3.bullet.control.RagdollControl;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.export.InputCapsule;
@@ -372,6 +373,13 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
 
         return a.getLength();
     }
+    
+    private RagdollControl ragdoll=null;
+
+    public void setRagdoll(RagdollControl ragdoll) {
+        this.ragdoll = ragdoll;
+    }
+    
 
     @Override
     protected void controlUpdate(float tpf) {
@@ -383,8 +391,12 @@ public final class AnimControl extends AbstractControl implements Savable, Clone
         }
 
         skeleton.updateWorldVectors();
-        // here update the targets verticles if no hardware skinning supported
+        // here update the targets vertices if no hardware skinning supported
 
+        if(ragdoll!=null){
+            ragdoll.update(tpf);
+        }
+        
         Matrix4f[] offsetMatrices = skeleton.computeSkinningMatrices();
 
         // if hardware skinning is supported, the matrices and weight buffer

+ 15 - 11
engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java

@@ -27,10 +27,15 @@ public class HullCollisionShape extends CollisionShape {
         createShape(this.points);
     }
 
+    public HullCollisionShape(float[] points) {
+        this.points = points;
+        createShape(this.points);
+    }
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         super.write(ex);
-        
+
         OutputCapsule capsule = ex.getCapsule(this);
         capsule.write(points, "points", null);
     }
@@ -41,37 +46,36 @@ public class HullCollisionShape extends CollisionShape {
         InputCapsule capsule = im.getCapsule(this);
 
         // for backwards compatability
-        Mesh mesh = (Mesh)capsule.readSavable("hullMesh", null);
-        if (mesh != null){
+        Mesh mesh = (Mesh) capsule.readSavable("hullMesh", null);
+        if (mesh != null) {
             this.points = getPoints(mesh);
-        }else{
+        } else {
             this.points = capsule.readFloatArray("points", null);
-            
+
         }
         createShape(this.points);
     }
 
-    protected void createShape(float[] points){
+    protected void createShape(float[] points) {
         ObjectArrayList<Vector3f> pointList = new ObjectArrayList<Vector3f>();
         for (int i = 0; i < points.length; i += 3) {
-            pointList.add(new Vector3f(points[i], points[i+1], points[i+2]));
+            pointList.add(new Vector3f(points[i], points[i + 1], points[i + 2]));
         }
         cShape = new ConvexHullShape(pointList);
         cShape.setLocalScaling(Converter.convert(getScale()));
         cShape.setMargin(margin);
     }
 
-    protected float[] getPoints(Mesh mesh){
+    protected float[] getPoints(Mesh mesh) {
         FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
         vertices.rewind();
         int components = mesh.getVertexCount() * 3;
         float[] pointsArray = new float[components];
         for (int i = 0; i < components; i += 3) {
             pointsArray[i] = vertices.get();
-            pointsArray[i+1] = vertices.get();
-            pointsArray[i+2] = vertices.get();
+            pointsArray[i + 1] = vertices.get();
+            pointsArray[i + 2] = vertices.get();
         }
         return pointsArray;
     }
-
 }

+ 160 - 72
engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java

@@ -6,21 +6,28 @@ import com.jme3.animation.Skeleton;
 import com.jme3.asset.AssetManager;
 import com.jme3.bullet.PhysicsSpace;
 import com.jme3.bullet.collision.shapes.BoxCollisionShape;
-import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
+import com.jme3.bullet.collision.shapes.HullCollisionShape;
 import com.jme3.bullet.joints.ConeJoint;
 import com.jme3.bullet.joints.PhysicsJoint;
 import com.jme3.bullet.objects.PhysicsRigidBody;
 import com.jme3.export.JmeExporter;
 import com.jme3.export.JmeImporter;
 import com.jme3.math.FastMath;
+import com.jme3.math.Matrix3f;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.control.Control;
 import com.jme3.util.TempVars;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -38,42 +45,60 @@ public class RagdollControl implements PhysicsControl {
     protected boolean debug = false;
     protected Quaternion tmp_jointRotation = new Quaternion();
     protected PhysicsRigidBody baseRigidBody;
-
+    protected float weightThreshold = 1.0f;
+    protected Spatial targetModel;
+    protected Vector3f initPosition;
+    protected Quaternion initRotation;
+    protected Vector3f initScale;
+
+//Normen: Think you have the system you want, with movement
+//Normen: but the rootBone movement and translation is also accessible like getRootBoneLocation()
+//Normen: and you can disable the applying of it
+//Normen: setApplyRootBoneMovement(false)
+//Normen: when you add a RigidBodyControl..
+//Normen: it does this in setSpatial:
+//Normen: if (spatial.getcontrol(AnimControl.class)){animControl.setApplyRootBoneMovement(false)
+//Normen: and instead reads the current location and sets it to the RigidBody
+//Normen: simply said
+//Normen: update(){setPhysicsLocation(animControl.getRootBoneLocation())
     public RagdollControl() {
     }
 
+    public RagdollControl(float weightThreshold) {
+        this.weightThreshold = weightThreshold;
+    }
+
     public void update(float tpf) {
         if (!enabled) {
             return;
         }
+
         TempVars vars = TempVars.get();
         assert vars.lock();
-
-        skeleton.reset();
+        Quaternion q2 = vars.quat1;
+        //   skeleton.reset();
         for (PhysicsBoneLink link : boneLinks) {
+
+//            if(link.bone==skeleton.getRoots()[0]){
+//                  Vector3f loc=vars.vect1;
+//                  loc.set(baseRigidBody.getMotionState().getWorldLocation());//.subtractLocal(rootBoneInit);
+//                 ((Geometry)targetModel).setLocalTranslation(loc);
+//                
+//            }
             Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
             Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
 
-            q.toAxes(vars.tri);
+            q2.set(q).multLocal(link.initalWorldRotation).normalize();
 
-            Vector3f dir = vars.tri[2];
-            float len = link.length;
+            link.bone.setUserTransformsWorld(p, q2);
 
-            Vector3f parentPos = new Vector3f(p).subtractLocal(dir.mult(len / 2f));
-            Vector3f childPos = new Vector3f(p).addLocal(dir.mult(len / 2f));
+        }
 
-            Quaternion q2 = q.clone();
-            Quaternion rot = new Quaternion();
-            rot.fromAngles(FastMath.HALF_PI, 0, 0);
-            q2.multLocal(rot);
-            q2.normalize();
 
-            link.parentBone.setUserTransformsWorld(parentPos, q2);
-            if (link.childBone.getChildren().size() == 0) {
-                link.childBone.setUserTransformsWorld(childPos, q2.clone());
-            }
-        }
         assert vars.unlock();
+
+        //baseRigidBody.getMotionState().applyTransform(targetModel);
+
     }
 
     public Control cloneForSpatial(Spatial spatial) {
@@ -81,6 +106,7 @@ public class RagdollControl implements PhysicsControl {
     }
 
     public void setSpatial(Spatial model) {
+        targetModel = model;
         removeFromPhysicsSpace();
         clearData();
         // put into bind pose and compute bone transforms in model space
@@ -95,23 +121,30 @@ public class RagdollControl implements PhysicsControl {
     }
 
     private void scanSpatial(Spatial model) {
-        AnimControl animControl = model.getControl(AnimControl.class);
+        AnimControl animControl = model.getControl(AnimControl.class);        
+
+        initPosition = model.getLocalTranslation();
+        initRotation = model.getLocalRotation();
+        initScale = model.getLocalScale();
 
         skeleton = animControl.getSkeleton();
         skeleton.resetAndUpdate();
-        for (int i = 0; i < skeleton.getBoneCount(); i++) {
-            Bone childBone = skeleton.getBone(i);
+        for (int i = 0; i < skeleton.getRoots().length; i++) {
+            Bone childBone = skeleton.getRoots()[i];
             childBone.setUserControl(true);
             if (childBone.getParent() == null) {
-                Vector3f parentPos = childBone.getModelSpacePosition().add(model.getWorldTranslation());
+                Vector3f parentPos = childBone.getModelSpacePosition().add(initPosition);
+              //  Quaternion parentRot= childBone.getModelSpaceRotation().mult(initRotation);
                 logger.log(Level.INFO, "Found root bone in skeleton {0}", skeleton);
                 baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(.1f)), 1);
                 baseRigidBody.setPhysicsLocation(parentPos);
+               // baseRigidBody.setPhysicsRotation(parentRot);
                 boneLinks = boneRecursion(model, childBone, baseRigidBody, boneLinks, 1);
                 return;
             }
 
         }
+
 //        BoneAnimation myAnimation = new BoneAnimation("boneAnimation", 1000000);
 //        myAnimation.setTracks(new BoneTrack[0]);
 //        animControl.addAnim(myAnimation);
@@ -120,52 +153,47 @@ public class RagdollControl implements PhysicsControl {
     }
 
     private List<PhysicsBoneLink> boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, List<PhysicsBoneLink> list, int reccount) {
-        ArrayList<Bone> children = bone.getChildren();
+        //Allow bone's transformation change outside of animation
         bone.setUserControl(true);
-        for (Iterator<Bone> it = children.iterator(); it.hasNext();) {
-            Bone childBone = it.next();
-            Bone parentBone = bone;
-            Vector3f parentPos = parentBone.getModelSpacePosition().add(model.getWorldTranslation());
-            Vector3f childPos = childBone.getModelSpacePosition().add(model.getWorldTranslation());
-            //get location between the two bones (physicscapsule center)
-            Vector3f jointCenter = parentPos.add(childPos).multLocal(0.5f);
-            tmp_jointRotation.lookAt(childPos.subtract(parentPos), Vector3f.UNIT_Y);
-            // length of the joint
-            float height = parentPos.distance(childPos);
-
-            // TODO: joints act funny when bone is too thin??
-            float radius = height > 2f ? 0.4f : height * .2f;
-            CapsuleCollisionShape shape = new CapsuleCollisionShape(radius, height - (radius), 2);
-
-            PhysicsRigidBody shapeNode = new PhysicsRigidBody(shape, 10.0f / (float) reccount);
-            shapeNode.setPhysicsLocation(jointCenter);
-            shapeNode.setPhysicsRotation(tmp_jointRotation.toRotationMatrix());
-
-            PhysicsBoneLink link = new PhysicsBoneLink();
-            link.parentBone = parentBone;
-            link.childBone = childBone;
-            link.rigidBody = shapeNode;
-            link.length = height;
-
-            //TODO: ragdoll mass 1
-            if (parent != null) {
-                //get length of parent
-                float parentHeight = 0.0f;
-                if (bone.getParent() != null) {
-                    parentHeight = bone.getParent().getModelSpacePosition().add(model.getWorldTranslation()).distance(parentPos);
-                }
-                //local position from parent
-                link.pivotA = new Vector3f(0, 0, (parentHeight * .5f));
-                //local position from child
-                link.pivotB = new Vector3f(0, 0, -(height * .5f));
 
-                ConeJoint joint = new ConeJoint(parent, shapeNode, link.pivotA, link.pivotB);
-                joint.setLimit(FastMath.HALF_PI, FastMath.HALF_PI, 0.01f);
-
-                link.joint = joint;
-                joint.setCollisionBetweenLinkedBodys(false);
+        //get world space position of the bone
+        Vector3f pos = bone.getModelSpacePosition().add(model.getLocalTranslation());
+        Quaternion rot= bone.getModelSpaceRotation().mult(initRotation);
+
+        //creating the collision shape from the bone's associated vertices
+        PhysicsRigidBody shapeNode = new PhysicsRigidBody(makeShape(bone, model), 10.0f / (float) reccount);
+        shapeNode.setPhysicsLocation(pos);
+       // shapeNode.setPhysicsRotation(rot);
+
+        PhysicsBoneLink link = new PhysicsBoneLink();
+        link.bone = bone;
+        link.rigidBody = shapeNode;
+        link.initalWorldRotation = bone.getModelSpaceRotation().clone();
+
+        //TODO: ragdoll mass 1
+        if (parent != null) {
+            //get joint position for parent
+            Vector3f posToParent = new Vector3f();
+            if (bone.getParent() != null) {
+                bone.getModelSpacePosition().subtract(bone.getParent().getModelSpacePosition(), posToParent);
             }
-            list.add(link);
+
+            //Joint local position from parent
+            link.pivotA = posToParent;
+            //joint local position from current bone
+            link.pivotB = new Vector3f(0, 0, 0f);
+
+            ConeJoint joint = new ConeJoint(parent, shapeNode, link.pivotA, link.pivotB);
+            //TODO make joints editable by user or find a way to correctly compute/import them
+            joint.setLimit(FastMath.HALF_PI, FastMath.HALF_PI, 0.01f);
+
+            link.joint = joint;
+            joint.setCollisionBetweenLinkedBodys(false);
+        }
+        list.add(link);
+
+        for (Iterator<Bone> it = bone.getChildren().iterator(); it.hasNext();) {
+            Bone childBone = it.next();
             boneRecursion(model, childBone, shapeNode, list, reccount++);
         }
         return list;
@@ -194,6 +222,67 @@ public class RagdollControl implements PhysicsControl {
         }
     }
 
+    private HullCollisionShape makeShape(Bone bone, Spatial model) {
+        int boneIndex = skeleton.getBoneIndex(bone);
+        System.out.println(boneIndex);
+        ArrayList<Float> points = new ArrayList<Float>();
+        if (model instanceof Geometry) {
+            Geometry g = (Geometry) model;
+            points.addAll(getPoints(g.getMesh(), boneIndex, bone.getModelSpacePosition()));
+        } else if (model instanceof Node) {
+            Node node = (Node) model;
+            for (Spatial s : node.getChildren()) {
+                if (s instanceof Geometry) {
+                    Geometry g = (Geometry) s;
+                    points.addAll(getPoints(g.getMesh(), boneIndex, bone.getModelSpacePosition()));
+                }
+            }
+        }
+        float[] p = new float[points.size()];
+        for (int i = 0; i < points.size(); i++) {
+            p[i] = points.get(i);
+        }
+
+        return new HullCollisionShape(p);
+    }
+
+    protected List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f offset) {
+
+        FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+        ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+        vertices.rewind();
+        boneIndices.rewind();
+        boneWeight.rewind();
+
+        ArrayList<Float> results = new ArrayList<Float>();
+
+        int vertexComponents = mesh.getVertexCount() * 3;
+        for (int i = 0; i < vertexComponents; i += 3) {
+            int k;
+            boolean add = false;
+            int start = i / 3 * 4;
+            for (k = start; k < start + 4; k++) {
+                if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) {
+                    add = true;
+                    break;
+                }
+            }
+            if (add) {
+                Vector3f pos = new Vector3f();
+                pos.x = vertices.get(i);
+                pos.y = vertices.get(i + 1);
+                pos.z = vertices.get(i + 2);
+                pos.subtractLocal(offset);
+                results.add(pos.x);
+                results.add(pos.y);
+                results.add(pos.z);
+            }
+        }
+        return results;
+    }
+
     private void removeFromPhysicsSpace() {
         if (baseRigidBody != null) {
             space.remove(baseRigidBody);
@@ -214,9 +303,9 @@ public class RagdollControl implements PhysicsControl {
 
     public void setEnabled(boolean enabled) {
         this.enabled = enabled;
-        if(!enabled&&space!=null){
+        if (!enabled && space != null) {
             removeFromPhysicsSpace();
-        }else if(enabled && space!=null){
+        } else if (enabled && space != null) {
             addToPhysicsSpace();
         }
     }
@@ -284,14 +373,13 @@ public class RagdollControl implements PhysicsControl {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    private static class PhysicsBoneLink {
+    protected static class PhysicsBoneLink {
 
-        Bone childBone;
-        Bone parentBone;
+        Bone bone;
+        Quaternion initalWorldRotation;
         PhysicsJoint joint;
         PhysicsRigidBody rigidBody;
         Vector3f pivotA;
         Vector3f pivotB;
-        float length;
     }
 }

+ 109 - 4
engine/src/test/jme3test/bullet/TestBoneRagdoll.java

@@ -35,14 +35,31 @@ package jme3test.bullet;
 import com.jme3.animation.AnimControl;
 import com.jme3.bullet.BulletAppState;
 import com.jme3.app.SimpleApplication;
+import com.jme3.asset.TextureKey;
 import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
 import com.jme3.bullet.control.RagdollControl;
+import com.jme3.bullet.control.RigidBodyControl;
+import com.jme3.font.BitmapText;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.input.controls.MouseButtonTrigger;
 import com.jme3.light.DirectionalLight;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
 import com.jme3.scene.debug.SkeletonDebugger;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.scene.shape.Sphere.TextureMode;
+import com.jme3.texture.Texture;
 
 /**
  * PHYSICS RAGDOLLS ARE NOT WORKING PROPERLY YET!
@@ -51,6 +68,8 @@ import com.jme3.scene.debug.SkeletonDebugger;
 public class TestBoneRagdoll  extends SimpleApplication {
 
     private BulletAppState bulletAppState;
+    Material matBullet;
+    Node model;
 
     public static void main(String[] args){
         TestBoneRagdoll app = new TestBoneRagdoll();
@@ -58,13 +77,18 @@ public class TestBoneRagdoll  extends SimpleApplication {
     }
 
     public void simpleInitApp() {
+        initCrossHairs();
+        initMaterial();
         bulletAppState = new BulletAppState();
+        bulletAppState.setEnabled(false);
         stateManager.attach(bulletAppState);
         bulletAppState.getPhysicsSpace().enableDebug(assetManager);
         PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
         setupLight();
 
-        Node model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+        model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+//           model.setLocalTranslation(5,5,5);
+//        model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
 
         //debug view
         AnimControl control= model.getControl(AnimControl.class);
@@ -73,18 +97,62 @@ public class TestBoneRagdoll  extends SimpleApplication {
         mat2.setColor("Color", ColorRGBA.Green);
         mat2.getAdditionalRenderState().setDepthTest(false);
         skeletonDebug.setMaterial(mat2);
+        skeletonDebug.setLocalTranslation(model.getLocalTranslation());
+        control.createChannel().setAnim("Dodge");
 
         //Note: PhysicsRagdollControl is still TODO, constructor will change
         RagdollControl ragdoll = new RagdollControl();
+      //  ragdoll.setEnabled(true);
+      //  ragdoll.attachDebugShape(assetManager);
         
-        model.addControl(ragdoll);
-        getPhysicsSpace().add(ragdoll);
-        speed = .2f;
+        ragdoll.setSpatial(model);
+        ragdoll.setPhysicsSpace(getPhysicsSpace());
+        control.setRagdoll(ragdoll);
+        
+//        model.addControl(ragdoll);
+//        getPhysicsSpace().add(ragdoll);
+        speed = 1f;
 
         rootNode.attachChild(model);
         rootNode.attachChild(skeletonDebug);
+        flyCam.setEnabled(false);
+      //  flyCam.setMoveSpeed(10);
+        ChaseCamera chaseCamera=new ChaseCamera(cam, inputManager);
+        model.addControl(chaseCamera);
+        
+        inputManager.addListener(new ActionListener() {
+
+            public void onAction(String name, boolean isPressed, float tpf) {
+               if(name.equals("toggle") && isPressed){
+                   bulletAppState.setEnabled(!bulletAppState.isEnabled());
+               }
+               if (name.equals("shoot") && !isPressed) {
+                Geometry bulletg = new Geometry("bullet", bullet);
+                bulletg.setMaterial(matBullet);      
+                bulletg.setLocalTranslation(cam.getLocation());
+                RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
+              //  RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, 1);
+                bulletNode.setLinearVelocity(cam.getDirection().mult(60));
+                bulletg.addControl(bulletNode);
+                rootNode.attachChild(bulletg);
+                getPhysicsSpace().add(bulletNode);
+                
+               
+              }
+            }
+        }, "toggle", "shoot");
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+        inputManager.addMapping("shoot", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
+        
     }
 
+    @Override
+    public void simpleUpdate(float tpf) {
+  
+    }
+    
+    
+
     private void setupLight() {
         DirectionalLight dl = new DirectionalLight();
         dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
@@ -96,4 +164,41 @@ public class TestBoneRagdoll  extends SimpleApplication {
         return bulletAppState.getPhysicsSpace();
     }
 
+    
+        
+    Material mat;
+ 
+    Material mat3;
+  
+    private static final Sphere bullet;   
+    private static final SphereCollisionShape bulletCollisionShape;
+
+    static {
+        bullet = new Sphere(32, 32, 0.4f, true, false);
+        bullet.setTextureMode(TextureMode.Projected);
+        bulletCollisionShape = new SphereCollisionShape(0.4f);      
+    }
+   
+
+    public void initMaterial() {
+      
+        matBullet = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
+        TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG");
+        key2.setGenerateMips(true);
+        Texture tex2 = assetManager.loadTexture(key2);
+        matBullet.setTexture("ColorMap", tex2);       
+    }
+
+ 
+
+    protected void initCrossHairs() {
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        BitmapText ch = new BitmapText(guiFont, false);
+        ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
+        ch.setText("+"); // crosshairs
+        ch.setLocalTranslation( // center
+                settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
+                settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
+        guiNode.attachChild(ch);
+    }
 }