/*
* Copyright (c) 2009-2012 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.bullet.objects;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Basic Bullet Character
* @author normenhansen
*/
public class PhysicsCharacter extends PhysicsCollisionObject {
protected long characterId = 0;
protected float stepHeight;
protected Vector3f walkDirection = new Vector3f();
protected float fallSpeed = 55.0f;
protected float jumpSpeed = 10.0f;
protected int upAxis = 1;
protected boolean locationDirty = false;
//TEMP VARIABLES
protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
public PhysicsCharacter() {
}
/**
* @param shape The CollisionShape (no Mesh or CompoundCollisionShapes)
* @param stepHeight The quantization size for vertical movement
*/
public PhysicsCharacter(CollisionShape shape, float stepHeight) {
this.collisionShape = shape;
// if (shape instanceof MeshCollisionShape || shape instanceof CompoundCollisionShape) {
// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh or compound collision shapes"));
// }
this.stepHeight = stepHeight;
buildObject();
}
protected void buildObject() {
if (objectId == 0) {
objectId = createGhostObject();
Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Creating GhostObject {0}", Long.toHexString(objectId));
initUserPointer();
}
setCharacterFlags(objectId);
attachCollisionShape(objectId, collisionShape.getObjectId());
if (characterId != 0) {
Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Clearing Character {0}", Long.toHexString(objectId));
finalizeNativeCharacter(characterId);
}
characterId = createCharacterObject(objectId, collisionShape.getObjectId(), stepHeight);
Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Creating Character {0}", Long.toHexString(characterId));
}
private native long createGhostObject();
private native void setCharacterFlags(long objectId);
private native long createCharacterObject(long objectId, long shapeId, float stepHeight);
/**
* Sets the location of this physics character
* @param location
*/
public void warp(Vector3f location) {
warp(characterId, location);
}
private native void warp(long characterId, Vector3f location);
/**
* Set the walk direction, works continuously.
* This should probably be called setPositionIncrementPerSimulatorStep.
* This is neither a direction nor a velocity, but the amount to
* increment the position each physics tick. So vector length = accuracy*speed in m/s
* @param vec the walk direction to set
*/
public void setWalkDirection(Vector3f vec) {
walkDirection.set(vec);
setWalkDirection(characterId, vec);
}
private native void setWalkDirection(long characterId, Vector3f vec);
/**
* @return the currently set walkDirection
*/
public Vector3f getWalkDirection() {
return walkDirection;
}
public void setUpAxis(int axis) {
upAxis = axis;
setUpAxis(characterId, axis);
}
private native void setUpAxis(long characterId, int axis);
public int getUpAxis() {
return upAxis;
}
public void setFallSpeed(float fallSpeed) {
this.fallSpeed = fallSpeed;
setFallSpeed(characterId, fallSpeed);
}
private native void setFallSpeed(long characterId, float fallSpeed);
public float getFallSpeed() {
return fallSpeed;
}
public void setJumpSpeed(float jumpSpeed) {
this.jumpSpeed = jumpSpeed;
setJumpSpeed(characterId, jumpSpeed);
}
private native void setJumpSpeed(long characterId, float jumpSpeed);
public float getJumpSpeed() {
return jumpSpeed;
}
public void setGravity(float value) {
setGravity(characterId, value);
}
private native void setGravity(long characterId, float gravity);
public float getGravity() {
return getGravity(characterId);
}
private native float getGravity(long characterId);
public void setMaxSlope(float slopeRadians) {
setMaxSlope(characterId, slopeRadians);
}
private native void setMaxSlope(long characterId, float slopeRadians);
public float getMaxSlope() {
return getMaxSlope(characterId);
}
private native float getMaxSlope(long characterId);
public boolean onGround() {
return onGround(characterId);
}
private native boolean onGround(long characterId);
public void jump() {
jump(characterId);
}
private native void jump(long characterId);
@Override
public void setCollisionShape(CollisionShape collisionShape) {
// if (!(collisionShape.getObjectId() instanceof ConvexShape)) {
// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
// }
super.setCollisionShape(collisionShape);
if (objectId == 0) {
buildObject();
} else {
attachCollisionShape(objectId, collisionShape.getObjectId());
}
}
/**
* Set the physics location (same as warp())
* @param location the location of the actual physics object
*/
public void setPhysicsLocation(Vector3f location) {
warp(location);
}
/**
* @return the physicsLocation
*/
public Vector3f getPhysicsLocation(Vector3f trans) {
if (trans == null) {
trans = new Vector3f();
}
getPhysicsLocation(objectId, trans);
return trans;
}
private native void getPhysicsLocation(long objectId, Vector3f vec);
/**
* @return the physicsLocation
*/
public Vector3f getPhysicsLocation() {
return getPhysicsLocation(null);
}
public void setCcdSweptSphereRadius(float radius) {
setCcdSweptSphereRadius(objectId, radius);
}
private native void setCcdSweptSphereRadius(long objectId, float radius);
public void setCcdMotionThreshold(float threshold) {
setCcdMotionThreshold(objectId, threshold);
}
private native void setCcdMotionThreshold(long objectId, float threshold);
public float getCcdSweptSphereRadius() {
return getCcdSweptSphereRadius(objectId);
}
private native float getCcdSweptSphereRadius(long objectId);
public float getCcdMotionThreshold() {
return getCcdMotionThreshold(objectId);
}
private native float getCcdMotionThreshold(long objectId);
public float getCcdSquareMotionThreshold() {
return getCcdSquareMotionThreshold(objectId);
}
private native float getCcdSquareMotionThreshold(long objectId);
/**
* used internally
*/
public long getControllerId() {
return characterId;
}
public void destroy() {
}
@Override
public void write(JmeExporter e) throws IOException {
super.write(e);
OutputCapsule capsule = e.getCapsule(this);
capsule.write(stepHeight, "stepHeight", 1.0f);
capsule.write(getGravity(), "gravity", 9.8f * 3);
capsule.write(getMaxSlope(), "maxSlope", 1.0f);
capsule.write(fallSpeed, "fallSpeed", 55.0f);
capsule.write(jumpSpeed, "jumpSpeed", 10.0f);
capsule.write(upAxis, "upAxis", 1);
capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
}
@Override
public void read(JmeImporter e) throws IOException {
super.read(e);
InputCapsule capsule = e.getCapsule(this);
stepHeight = capsule.readFloat("stepHeight", 1.0f);
buildObject();
setGravity(capsule.readFloat("gravity", 9.8f * 3));
setMaxSlope(capsule.readFloat("maxSlope", 1.0f));
setFallSpeed(capsule.readFloat("fallSpeed", 55.0f));
setJumpSpeed(capsule.readFloat("jumpSpeed", 10.0f));
setUpAxis(capsule.readInt("upAxis", 1));
setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
}
@Override
protected void finalize() throws Throwable {
super.finalize();
finalizeNativeCharacter(characterId);
}
private native void finalizeNativeCharacter(long characterId);
}