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.collision;
33
34import com.jme3.asset.AssetManager;
35import com.jme3.bullet.collision.shapes.CollisionShape;
36import com.jme3.bullet.util.DebugShapeFactory;
37import com.jme3.export.*;
38import com.jme3.material.Material;
39import com.jme3.math.ColorRGBA;
40import com.jme3.math.Vector3f;
41import com.jme3.scene.Geometry;
42import com.jme3.scene.Node;
43import com.jme3.scene.Spatial;
44import com.jme3.scene.debug.Arrow;
45import java.io.IOException;
46import java.util.Iterator;
47import java.util.List;
48import java.util.logging.Level;
49import java.util.logging.Logger;
50
51/**
52 * Base class for collision objects (PhysicsRigidBody, PhysicsGhostObject)
53 * @author normenhansen
54 */
55public abstract class PhysicsCollisionObject implements Savable {
56
57    protected long objectId = 0;
58    protected Spatial debugShape;
59    protected Arrow debugArrow;
60    protected Geometry debugArrowGeom;
61    protected Material debugMaterialBlue;
62    protected Material debugMaterialRed;
63    protected Material debugMaterialGreen;
64    protected Material debugMaterialYellow;
65    protected CollisionShape collisionShape;
66    public static final int COLLISION_GROUP_NONE = 0x00000000;
67    public static final int COLLISION_GROUP_01 = 0x00000001;
68    public static final int COLLISION_GROUP_02 = 0x00000002;
69    public static final int COLLISION_GROUP_03 = 0x00000004;
70    public static final int COLLISION_GROUP_04 = 0x00000008;
71    public static final int COLLISION_GROUP_05 = 0x00000010;
72    public static final int COLLISION_GROUP_06 = 0x00000020;
73    public static final int COLLISION_GROUP_07 = 0x00000040;
74    public static final int COLLISION_GROUP_08 = 0x00000080;
75    public static final int COLLISION_GROUP_09 = 0x00000100;
76    public static final int COLLISION_GROUP_10 = 0x00000200;
77    public static final int COLLISION_GROUP_11 = 0x00000400;
78    public static final int COLLISION_GROUP_12 = 0x00000800;
79    public static final int COLLISION_GROUP_13 = 0x00001000;
80    public static final int COLLISION_GROUP_14 = 0x00002000;
81    public static final int COLLISION_GROUP_15 = 0x00004000;
82    public static final int COLLISION_GROUP_16 = 0x00008000;
83    protected int collisionGroup = 0x00000001;
84    protected int collisionGroupsMask = 0x00000001;
85    private Object userObject;
86
87    /**
88     * Sets a CollisionShape to this physics object, note that the object should
89     * not be in the physics space when adding a new collision shape as it is rebuilt
90     * on the physics side.
91     * @param collisionShape the CollisionShape to set
92     */
93    public void setCollisionShape(CollisionShape collisionShape) {
94        this.collisionShape = collisionShape;
95        updateDebugShape();
96    }
97
98    /**
99     * @return the CollisionShape of this PhysicsNode, to be able to reuse it with
100     * other physics nodes (increases performance)
101     */
102    public CollisionShape getCollisionShape() {
103        return collisionShape;
104    }
105
106    /**
107     * Returns the collision group for this collision shape
108     * @return
109     */
110    public int getCollisionGroup() {
111        return collisionGroup;
112    }
113
114    /**
115     * Sets the collision group number for this physics object. <br>
116     * The groups are integer bit masks and some pre-made variables are available in CollisionObject.
117     * All physics objects are by default in COLLISION_GROUP_01.<br>
118     * Two object will collide when <b>one</b> of the partys has the
119     * collisionGroup of the other in its collideWithGroups set.
120     * @param collisionGroup the collisionGroup to set
121     */
122    public void setCollisionGroup(int collisionGroup) {
123        this.collisionGroup = collisionGroup;
124        if (objectId != 0) {
125            setCollisionGroup(objectId, collisionGroup);
126        }
127    }
128
129    /**
130     * Add a group that this object will collide with.<br>
131     * Two object will collide when <b>one</b> of the partys has the
132     * collisionGroup of the other in its collideWithGroups set.<br>
133     * @param collisionGroup
134     */
135    public void addCollideWithGroup(int collisionGroup) {
136        this.collisionGroupsMask = this.collisionGroupsMask | collisionGroup;
137        if (objectId != 0) {
138            setCollideWithGroups(objectId, this.collisionGroupsMask);
139        }
140    }
141
142    /**
143     * Remove a group from the list this object collides with.
144     * @param collisionGroup
145     */
146    public void removeCollideWithGroup(int collisionGroup) {
147        this.collisionGroupsMask = this.collisionGroupsMask & ~collisionGroup;
148        if (objectId != 0) {
149            setCollideWithGroups(this.collisionGroupsMask);
150        }
151    }
152
153    /**
154     * Directly set the bitmask for collision groups that this object collides with.
155     * @param collisionGroup
156     */
157    public void setCollideWithGroups(int collisionGroups) {
158        this.collisionGroupsMask = collisionGroups;
159        if (objectId != 0) {
160            setCollideWithGroups(objectId, this.collisionGroupsMask);
161        }
162    }
163
164    /**
165     * Gets the bitmask of collision groups that this object collides with.
166     * @return
167     */
168    public int getCollideWithGroups() {
169        return collisionGroupsMask;
170    }
171
172    protected void initUserPointer() {
173        Logger.getLogger(this.getClass().getName()).log(Level.INFO, "initUserPointer() objectId = {0}", Long.toHexString(objectId));
174        initUserPointer(objectId, collisionGroup, collisionGroupsMask);
175    }
176    native void initUserPointer(long objectId, int group, int groups);
177    /**
178     * Creates a visual debug shape of the current collision shape of this physics object<br/>
179     * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
180     * @param manager AssetManager to load the default wireframe material for the debug shape
181     */
182    protected Spatial attachDebugShape(AssetManager manager) {
183        debugMaterialBlue = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
184        debugMaterialBlue.getAdditionalRenderState().setWireframe(true);
185        debugMaterialBlue.setColor("Color", ColorRGBA.Blue);
186        debugMaterialGreen = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
187        debugMaterialGreen.getAdditionalRenderState().setWireframe(true);
188        debugMaterialGreen.setColor("Color", ColorRGBA.Green);
189        debugMaterialRed = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
190        debugMaterialRed.getAdditionalRenderState().setWireframe(true);
191        debugMaterialRed.setColor("Color", ColorRGBA.Red);
192        debugMaterialYellow = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
193        debugMaterialYellow.getAdditionalRenderState().setWireframe(true);
194        debugMaterialYellow.setColor("Color", ColorRGBA.Yellow);
195        debugArrow = new Arrow(Vector3f.UNIT_XYZ);
196        debugArrowGeom = new Geometry("DebugArrow", debugArrow);
197        debugArrowGeom.setMaterial(debugMaterialGreen);
198        return attachDebugShape();
199    }
200
201    /**
202     * creates a debug shape for this CollisionObject
203     * @param manager
204     * @return
205     */
206    public Spatial createDebugShape(AssetManager manager){
207        return attachDebugShape(manager);
208    }
209
210    protected Spatial attachDebugShape(Material material) {
211        debugMaterialBlue = material;
212        debugMaterialGreen = material;
213        debugMaterialRed = material;
214        debugMaterialYellow = material;
215        debugArrow = new Arrow(Vector3f.UNIT_XYZ);
216        debugArrowGeom = new Geometry("DebugArrow", debugArrow);
217        debugArrowGeom.setMaterial(debugMaterialGreen);
218        return attachDebugShape();
219    }
220
221    public Spatial debugShape() {
222        return debugShape;
223    }
224
225    /**
226     * Creates a visual debug shape of the current collision shape of this physics object<br/>
227     * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b>
228     * @param material Material to use for the debug shape
229     */
230    protected Spatial attachDebugShape() {
231        if (debugShape != null) {
232            detachDebugShape();
233        }
234        Spatial spatial = getDebugShape();
235        this.debugShape = spatial;
236        return debugShape;
237    }
238
239    protected void updateDebugShape() {
240        if (debugShape != null) {
241            detachDebugShape();
242            attachDebugShape();
243        }
244    }
245
246    protected Spatial getDebugShape() {
247        Spatial spatial = DebugShapeFactory.getDebugShape(collisionShape);
248        if (spatial == null) {
249            return new Node("nullnode");
250        }
251        if (spatial instanceof Node) {
252            List<Spatial> children = ((Node) spatial).getChildren();
253            for (Iterator<Spatial> it1 = children.iterator(); it1.hasNext();) {
254                Spatial spatial1 = it1.next();
255                Geometry geom = ((Geometry) spatial1);
256                geom.setMaterial(debugMaterialBlue);
257                geom.setCullHint(Spatial.CullHint.Never);
258            }
259        } else {
260            Geometry geom = ((Geometry) spatial);
261            geom.setMaterial(debugMaterialBlue);
262            geom.setCullHint(Spatial.CullHint.Never);
263        }
264        spatial.setCullHint(Spatial.CullHint.Never);
265        return spatial;
266    }
267
268    /**
269     * Removes the debug shape
270     */
271    public void detachDebugShape() {
272        debugShape = null;
273    }
274
275    /**
276     * @return the userObject
277     */
278    public Object getUserObject() {
279        return userObject;
280    }
281
282    /**
283     * @param userObject the userObject to set
284     */
285    public void setUserObject(Object userObject) {
286        this.userObject = userObject;
287    }
288
289    public long getObjectId(){
290        return objectId;
291    }
292
293    protected native void attachCollisionShape(long objectId, long collisionShapeId);
294    native void setCollisionGroup(long objectId, int collisionGroup);
295    native void setCollideWithGroups(long objectId, int collisionGroups);
296
297    @Override
298    public void write(JmeExporter e) throws IOException {
299        OutputCapsule capsule = e.getCapsule(this);
300        capsule.write(collisionGroup, "collisionGroup", 0x00000001);
301        capsule.write(collisionGroupsMask, "collisionGroupsMask", 0x00000001);
302        capsule.write(debugShape, "debugShape", null);
303        capsule.write(collisionShape, "collisionShape", null);
304    }
305
306    @Override
307    public void read(JmeImporter e) throws IOException {
308        InputCapsule capsule = e.getCapsule(this);
309        collisionGroup = capsule.readInt("collisionGroup", 0x00000001);
310        collisionGroupsMask = capsule.readInt("collisionGroupsMask", 0x00000001);
311        debugShape = (Spatial) capsule.readSavable("debugShape", null);
312        CollisionShape shape = (CollisionShape) capsule.readSavable("collisionShape", null);
313        collisionShape = shape;
314    }
315
316    @Override
317    protected void finalize() throws Throwable {
318        super.finalize();
319        Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing CollisionObject {0}", Long.toHexString(objectId));
320        finalizeNative(objectId);
321    }
322
323    protected native void finalizeNative(long objectId);
324}
325