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 com.jme3.bullet.objects;
33
34import com.bulletphysics.collision.dispatch.CollisionFlags;
35import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
36import com.bulletphysics.linearmath.Transform;
37import com.jme3.bullet.collision.PhysicsCollisionObject;
38import com.jme3.bullet.collision.shapes.CollisionShape;
39import com.jme3.bullet.util.Converter;
40import com.jme3.export.InputCapsule;
41import com.jme3.export.JmeExporter;
42import com.jme3.export.JmeImporter;
43import com.jme3.export.OutputCapsule;
44import com.jme3.math.Matrix3f;
45import com.jme3.math.Quaternion;
46import com.jme3.math.Vector3f;
47import com.jme3.scene.Spatial;
48import java.io.IOException;
49import java.util.LinkedList;
50import java.util.List;
51
52/**
53 * <i>From Bullet manual:</i><br>
54 * GhostObject can keep track of all objects that are overlapping.
55 * By default, this overlap is based on the AABB.
56 * This is useful for creating a character controller,
57 * collision sensors/triggers, explosions etc.<br>
58 * @author normenhansen
59 */
60public class PhysicsGhostObject extends PhysicsCollisionObject {
61
62    protected PairCachingGhostObject gObject;
63    protected boolean locationDirty = false;
64    //TEMP VARIABLES
65    protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
66    protected Transform tempTrans = new Transform(Converter.convert(new Matrix3f()));
67    private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform();
68    protected javax.vecmath.Quat4f tempRot = new javax.vecmath.Quat4f();
69    private List<PhysicsCollisionObject> overlappingObjects = new LinkedList<PhysicsCollisionObject>();
70
71    public PhysicsGhostObject() {
72    }
73
74    public PhysicsGhostObject(CollisionShape shape) {
75        collisionShape = shape;
76        buildObject();
77    }
78
79    public PhysicsGhostObject(Spatial child, CollisionShape shape) {
80        collisionShape = shape;
81        buildObject();
82    }
83
84    protected void buildObject() {
85        if (gObject == null) {
86            gObject = new PairCachingGhostObject();
87            gObject.setCollisionFlags(gObject.getCollisionFlags() | CollisionFlags.NO_CONTACT_RESPONSE);
88        }
89        gObject.setCollisionShape(collisionShape.getCShape());
90        gObject.setUserPointer(this);
91    }
92
93    @Override
94    public void setCollisionShape(CollisionShape collisionShape) {
95        super.setCollisionShape(collisionShape);
96        if (gObject == null) {
97            buildObject();
98        }else{
99            gObject.setCollisionShape(collisionShape.getCShape());
100        }
101    }
102
103    /**
104     * Sets the physics object location
105     * @param location the location of the actual physics object
106     */
107    public void setPhysicsLocation(Vector3f location) {
108        gObject.getWorldTransform(tempTrans);
109        Converter.convert(location, tempTrans.origin);
110        gObject.setWorldTransform(tempTrans);
111    }
112
113    /**
114     * Sets the physics object rotation
115     * @param rotation the rotation of the actual physics object
116     */
117    public void setPhysicsRotation(Matrix3f rotation) {
118        gObject.getWorldTransform(tempTrans);
119        Converter.convert(rotation, tempTrans.basis);
120        gObject.setWorldTransform(tempTrans);
121    }
122
123    /**
124     * Sets the physics object rotation
125     * @param rotation the rotation of the actual physics object
126     */
127    public void setPhysicsRotation(Quaternion rotation) {
128        gObject.getWorldTransform(tempTrans);
129        Converter.convert(rotation, tempTrans.basis);
130        gObject.setWorldTransform(tempTrans);
131    }
132
133    /**
134     * @return the physicsLocation
135     */
136    public com.jme3.math.Transform getPhysicsTransform() {
137        return physicsLocation;
138    }
139
140    /**
141     * @return the physicsLocation
142     */
143    public Vector3f getPhysicsLocation(Vector3f trans) {
144        if (trans == null) {
145            trans = new Vector3f();
146        }
147        gObject.getWorldTransform(tempTrans);
148        Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
149        return trans.set(physicsLocation.getTranslation());
150    }
151
152    /**
153     * @return the physicsLocation
154     */
155    public Quaternion getPhysicsRotation(Quaternion rot) {
156        if (rot == null) {
157            rot = new Quaternion();
158        }
159        gObject.getWorldTransform(tempTrans);
160        Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
161        return rot.set(physicsLocation.getRotation());
162    }
163
164    /**
165     * @return the physicsLocation
166     */
167    public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) {
168        if (rot == null) {
169            rot = new Matrix3f();
170        }
171        gObject.getWorldTransform(tempTrans);
172        Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
173        return rot.set(physicsLocation.getRotation());
174    }
175
176    /**
177     * @return the physicsLocation
178     */
179    public Vector3f getPhysicsLocation() {
180        gObject.getWorldTransform(tempTrans);
181        Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
182        return physicsLocation.getTranslation();
183    }
184
185    /**
186     * @return the physicsLocation
187     */
188    public Quaternion getPhysicsRotation() {
189        gObject.getWorldTransform(tempTrans);
190        Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
191        return physicsLocation.getRotation();
192    }
193
194    public Matrix3f getPhysicsRotationMatrix() {
195        gObject.getWorldTransform(tempTrans);
196        Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation());
197        return physicsLocation.getRotation().toRotationMatrix();
198    }
199
200    /**
201     * used internally
202     */
203    public PairCachingGhostObject getObjectId() {
204        return gObject;
205    }
206
207    /**
208     * destroys this PhysicsGhostNode and removes it from memory
209     */
210    public void destroy() {
211    }
212
213    /**
214     * Another Object is overlapping with this GhostNode,
215     * if and if only there CollisionShapes overlaps.
216     * They could be both regular PhysicsRigidBodys or PhysicsGhostObjects.
217     * @return All CollisionObjects overlapping with this GhostNode.
218     */
219    public List<PhysicsCollisionObject> getOverlappingObjects() {
220        overlappingObjects.clear();
221        for (com.bulletphysics.collision.dispatch.CollisionObject collObj : gObject.getOverlappingPairs()) {
222            overlappingObjects.add((PhysicsCollisionObject) collObj.getUserPointer());
223        }
224        return overlappingObjects;
225    }
226
227    /**
228     *
229     * @return With how many other CollisionObjects this GhostNode is currently overlapping.
230     */
231    public int getOverlappingCount() {
232        return gObject.getNumOverlappingObjects();
233    }
234
235    /**
236     *
237     * @param index The index of the overlapping Node to retrieve.
238     * @return The Overlapping CollisionObject at the given index.
239     */
240    public PhysicsCollisionObject getOverlapping(int index) {
241        return overlappingObjects.get(index);
242    }
243
244    public void setCcdSweptSphereRadius(float radius) {
245        gObject.setCcdSweptSphereRadius(radius);
246    }
247
248    public void setCcdMotionThreshold(float threshold) {
249        gObject.setCcdMotionThreshold(threshold);
250    }
251
252    public float getCcdSweptSphereRadius() {
253        return gObject.getCcdSweptSphereRadius();
254    }
255
256    public float getCcdMotionThreshold() {
257        return gObject.getCcdMotionThreshold();
258    }
259
260    public float getCcdSquareMotionThreshold() {
261        return gObject.getCcdSquareMotionThreshold();
262    }
263
264    @Override
265    public void write(JmeExporter e) throws IOException {
266        super.write(e);
267        OutputCapsule capsule = e.getCapsule(this);
268        capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
269        capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f());
270        capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
271        capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
272    }
273
274    @Override
275    public void read(JmeImporter e) throws IOException {
276        super.read(e);
277        InputCapsule capsule = e.getCapsule(this);
278        buildObject();
279        setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
280        setPhysicsRotation(((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f())));
281        setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
282        setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
283    }
284}
285