package de.fau.cs.mad.fly.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.physics.bullet.collision.btCollisionShape;
import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody;
import com.badlogic.gdx.physics.bullet.dynamics.btRigidBody.btRigidBodyConstructionInfo;
import com.badlogic.gdx.utils.Disposable;
import de.fau.cs.mad.fly.game.object.IGameObjectMover;
/**
* Wrapper for ModelInstance.
* <p>
* Extends ModelInstance with features for frustum culling and collision
* detection.
*
* @author Tobias Zangl
*/
public class GameObject extends ModelInstance implements Disposable {
/**
* The mover for the game object. Empty mover is no mover is defined.
*/
private IGameObjectMover mover = null;
/**
* Position of the game object.
*/
private final Vector3 position = new Vector3();
/**
* Center of the bounding box of the game object.
*/
private final Vector3 frustumBBoxCenter = new Vector3();
/**
* Dimensions of the bounding box of the game object.
*/
private final Vector3 frustumBBoxDimensions = new Vector3();
/**
* Bounding box of the game object used for frustum culling.
*/
private final static BoundingBox frustumBBox = new BoundingBox();
/**
* Rigid body of the game object.
*/
protected btRigidBody rigidBody;
/**
* Motion state for the rigid body.
*/
private GameObjectMotionState motionState;
/**
* Model of the game object.
*/
private final GameModel gmodel;
/**
* Data used for collision detection.
*/
private Object userData;
/**
* Determines if the game object is currently visible.
*/
private boolean visible = true;
/**
* Determines if the game object is only a dummy object and the player does
* not lose life if colliding with it.
*/
protected boolean dummy = false;
/**
* Collision group of the game object for filtering.
*/
private short filterGroup = CollisionDetector.OBJECT_FLAG;
/**
* Collision mask of the game object. It can only collide with objects in
* the filter mask.
*/
private short filterMask = CollisionDetector.ALL_FLAG;
/**
* Id of the game object.
*/
private String id;
/**
* Id of the model of this game object.
*/
private String modelId;
/**
* The environment this GameObject is rendered with.
*/
public Environment environment;
/**
* Constructs a new game object without any collision detection.
*
* @param model
* @param id
*/
public GameObject(GameModel model, String id) {
super(model.display);
this.gmodel = model;
this.userData = this;
this.id = id;
initFrustumBoundingBox();
}
/**
* Adds a rigid body with a shape and a rigid body info to the game object
* and adds it to the collision world.
*
* @param shape
* @param rigidBodyInfo
*/
public void createRigidBody(String id, btCollisionShape shape, float mass, short filterGroup, short filterMask) {
btRigidBodyConstructionInfo info = CollisionDetector.getInstance().getRigidBodyInfoManager().createRigidBodyInfo(id, shape, mass);
this.filterGroup = filterGroup;
this.filterMask = filterMask;
this.rigidBody = CollisionDetector.createRigidBody(this, shape, this, info);
}
/**
* Initializes the bounding box for the frustum culling.
* <p>
* Size of the bounding box is doubled to make sure the object is always
* displayed when it should be.
*/
private void initFrustumBoundingBox() {
calculateBoundingBox(frustumBBox);
frustumBBoxCenter.set(frustumBBox.getCenter());
frustumBBoxDimensions.set(frustumBBox.getDimensions().cpy().scl(2.0f));
}
/**
* Updates the scale of the bounding box if the transform matrix was scaled.
*/
public void scaleFrustumBoundingBox() {
Vector3 dummy = new Vector3();
frustumBBoxCenter.scl(transform.getScale(dummy));
frustumBBoxDimensions.scl(transform.getScale(dummy));
}
/**
* Returns if the object is hidden.
*/
public boolean isHidden() {
return !visible;
}
/**
* Returns if the object is visible.
*/
public boolean isVisible() {
return visible;
}
/**
* Makes the object hidden.
*/
public void hide() {
visible = false;
}
/**
* Makes the object visible.
*/
public void show() {
visible = true;
}
/**
* Checks if the object is visible for the given Camera.
*
* @param camera
* the Camera for the frustum culling.
* @return true, if the object is visible, otherwise false.
*/
public boolean isVisibleInFrustum(final Camera camera) {
transform.getTranslation(position);
position.add(frustumBBoxCenter);
return camera.frustum.boundsInFrustum(position, frustumBBoxDimensions);
}
/**
* Setter if the game object is only a dummy object.
*
* @param isDummy
*/
public void setDummy(boolean isDummy) {
dummy = isDummy;
}
/**
* Checks if the game object is only a dummy object.
*
* @return true, if the object is a dummy, otherwise false.
*/
public boolean isDummy() {
return dummy;
}
/**
* Setter for the rigidBody.userData of the GameObject.
*/
public void setCollisionTarget(Object object) {
if (rigidBody == null)
return;
rigidBody.userData = object;
}
/**
* Setter for the rigidBody.userValue of the GameObject.
*/
public void setCollisionType(int userValue) {
if (rigidBody == null)
return;
rigidBody.setUserValue(userValue);
}
/**
* Setter for the rigid body restitution of the GameObject.
*/
public void setRestitution(float rest) {
if (rigidBody == null)
return;
rigidBody.setRestitution(rest);
}
/**
* Adds a motion state to the game object which cares about the updating of
* the transform matrix if the rigid body is updated by the dynamic world.
*/
public void addMotionState() {
motionState = new GameObjectMotionState();
motionState.transform = transform;
rigidBody.setMotionState(motionState);
}
/**
* Getter for the rigid body.
*/
public btRigidBody getRigidBody() {
return rigidBody;
}
/**
* Updates the current transform matrix with the rigid body transform matrix
* after the rigid body simulation.
*/
public void updateRigidBody() {
rigidBody.getWorldTransform(transform);
}
/**
* Moves the game object with the specific mover.
*
* @param delta
* The delta since the last call.
*/
public void move(float delta) {
if (mover != null) {
mover.move(delta);
}
}
/**
* Renders the game object.
*
* @param batch
* The model batch of the screen.
* @param cam
* The camera used to display the world.
*/
public void render(ModelBatch batch, PerspectiveCamera cam) {
render(batch, environment, cam);
}
/**
* Renders the game object with environment.
*
* @param batch
* The model batch of the screen.
* @param environment
* The environment used to display the world.
* @param cam
* The camera used to display the world.
*/
public void render(ModelBatch batch, Environment environment, PerspectiveCamera cam) {
if (visible && isVisibleInFrustum(cam)) {
if (environment == null) {
batch.render(this);
} else {
batch.render(this, environment);
}
}
}
/**
* Getter of the position in 3D space of the object.
*
* @return {@link #position}
*/
public Vector3 getPosition() {
transform.getTranslation(position);
return position;
}
/**
* Setter for the rotation.
*
* @param vel
*/
public void setRotation(Vector3 vel) {
rigidBody.setAngularVelocity(vel);
}
/**
* Setter for the movement.
*
* @param vel
*/
public void setMovement(Vector3 vel) {
rigidBody.setLinearVelocity(vel);
}
/**
* Getter for the game object mover.
*
* @return mover
*/
public IGameObjectMover getMover() {
return mover;
}
/**
* Setter for the game object mover.
*
* @param mover
* The mover to use for this game object.
*/
public void setMover(IGameObjectMover mover) {
this.mover = mover;
}
/**
* Flips the direction around.
*/
public void flipDirection() {
rigidBody.setLinearVelocity(rigidBody.getLinearVelocity().scl(-1.0f));
}
/**
* Removes the rigid body from the collision world and disposes it.
*/
public void removeRigidBody() {
Gdx.app.log("GameObject", "removeRigidBody");
// if (rigidBody != null && CollisionDetector.getInstance() != null) {
// CollisionDetector.getInstance().removeRigidBody(this);
// rigidBody.dispose();
// rigidBody = null;
// }
}
@Override
public void dispose() {
removeRigidBody();
}
public Object getUserData() {
return userData;
}
public short getFilterGroup() {
return filterGroup;
}
public void setFilterGroup(short filterGroup) {
this.filterGroup = filterGroup;
}
public short getFilterMask() {
return filterMask;
}
public void setFilterMask(short filterMask) {
this.filterMask = filterMask;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getModelId() {
return modelId;
}
public void setModelId(String modelId) {
this.modelId = modelId;
}
}