1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 03:42:48 +01:00

add ProjectileObject with support for grenades

This commit is contained in:
Daniel Evans 2014-07-14 01:29:05 +01:00
parent a8c2312b87
commit f8f17db68f
9 changed files with 319 additions and 45 deletions

View File

@ -6,6 +6,7 @@
#include <loaders/LoaderIDE.hpp>
#include <loaders/LoaderIPL.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <memory>
class CharacterController;
@ -20,8 +21,12 @@ class GameWorld;
* Contains handle to the world, and other useful properties like water level
* tracking used to make tunnels work.
*/
struct GameObject
class GameObject
{
glm::vec3 _lastPosition;
glm::quat _lastRotation;
public:
glm::vec3 position;
glm::quat rotation;
@ -44,7 +49,7 @@ struct GameObject
float _lastHeight;
GameObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model)
: position(pos), rotation(rot), model(model), engine(engine), animator(nullptr), mHealth(0.f),
: _lastPosition(pos), _lastRotation(rot), position(pos), rotation(rot), model(model), engine(engine), animator(nullptr), mHealth(0.f),
_inWater(false), _lastHeight(std::numeric_limits<float>::max())
{}
@ -69,8 +74,9 @@ struct GameObject
virtual Type type() { return Unknown; }
virtual void setPosition(const glm::vec3& pos);
virtual glm::vec3 getPosition() const;
virtual glm::vec3 getPosition() const { return position; }
const glm::vec3& getLastPosition() const { return _lastPosition; }
virtual glm::quat getRotation() const;
@ -117,6 +123,24 @@ struct GameObject
virtual bool isInWater() const { return _inWater; }
virtual void tick(float dt) = 0;
/**
* @brief Function used to modify the last transform
* @param newPos
*/
void _updateLastTransform()
{
_lastPosition = getPosition();
_lastRotation = getRotation();
}
glm::mat4 getTimeAdjustedTransform(float alpha) const {
glm::mat4 t;
t = glm::translate(t, glm::mix(_lastPosition, getPosition(), alpha));
t = t * glm::mat4_cast(glm::slerp(_lastRotation, getRotation(), alpha));
return t;
}
};
#endif // __GAMEOBJECTS_HPP__

View File

@ -0,0 +1,52 @@
#pragma once
#ifndef _PROJECTILEOBJECT_HPP_
#define _PROJECTILEOBJECT_HPP_
#include <engine/GameObject.hpp>
#include <bullet/btBulletDynamicsCommon.h>
/**
* @brief Implements weapon projectile (e.g. molotovs, RPGs etc.)
*/
class ProjectileObject : public GameObject
{
public:
enum ProjectileType {
Grenade,
Molotov,
RPG,
};
struct ProjectileInfo {
ProjectileType type;
glm::vec3 direction;
float velocity;
/** Time to dentonation or removal */
float time;
};
private:
ProjectileInfo _info;
btSphereShape* _shape;
union {
btRigidBody* _body;
};
void explode();
public:
/**
* @brief ProjectileObject constructor
*/
ProjectileObject(GameWorld* world, const glm::vec3& position, const ProjectileInfo& info);
~ProjectileObject();
void tick(float dt);
};
#endif

View File

@ -23,8 +23,14 @@ void Animator::reset()
if( it == getAnimation()->bones.end() ) continue;
auto A = getKeyframeAt(f, 0.f);
_frameInstances.insert( { f, { it->second, A, A } } );
auto kfit = _frameInstances.find( f );
if( kfit == _frameInstances.end() ) {
_frameInstances.insert( { f, { it->second, A, A } } );
}
else {
kfit->second.first = kfit->second.second;
kfit->second.second = A;
}
}
}
}
@ -39,7 +45,7 @@ void Animator::setAnimation(Animation *animation, bool repeat)
queueAnimation(animation);
this->repeat = repeat;
_frameInstances.clear();
//_frameInstances.clear();
reset();
}
@ -218,8 +224,8 @@ glm::mat4 Animator::getFrameMatrix(ModelFrame *frame, float alpha, bool ignoreRo
{
auto it = _frameInstances.find( frame );
if( it != _frameInstances.end() && it->second.bone ) {
const AnimationKeyframe& F = it->second.first;
const AnimationKeyframe& S = it->second.second;
const AnimationKeyframe& S = it->second.first;
const AnimationKeyframe& F = it->second.second;
AnimationKeyframe kf {
glm::slerp(F.rotation, S.rotation, alpha),

View File

@ -4,12 +4,7 @@
void GameObject::setPosition(const glm::vec3& pos)
{
position = pos;
}
glm::vec3 GameObject::getPosition() const
{
return position;
_lastPosition = position = pos;
}
glm::quat GameObject::getRotation() const

View File

@ -298,7 +298,7 @@ void CharacterObject::setPosition(const glm::vec3& pos)
{
btTransform& tf = physCharacter->getGhostObject()->getWorldTransform();
tf.setOrigin(btVector3(pos.x, pos.y, pos.z));
position = pos;
GameObject::setPosition(pos);
}
glm::vec3 CharacterObject::getPosition() const

View File

@ -0,0 +1,80 @@
#include <objects/ProjectileObject.hpp>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
#include <engine/GameWorld.hpp>
#include <data/WeaponData.hpp>
void ProjectileObject::explode()
{
/// @todo accelerate this with bullet instead of doing this stupid loop.
for(auto& o : engine->objects) {
if( o == this ) continue;
switch( o->type() ) {
case GameObject::Instance:
case GameObject::Vehicle:
case GameObject::Character:
break;
default:
continue;
}
float d = glm::distance(getPosition(), o->getPosition());
if( d > 10.f ) continue;
o->takeDamage({
getPosition(),
getPosition(),
10.f / glm::max(d, 1.f),
DamageInfo::Explosion,
0.f
});
}
}
ProjectileObject::ProjectileObject(GameWorld *world, const glm::vec3 &position, const ProjectileObject::ProjectileInfo &info)
: GameObject(world, position, glm::quat(), nullptr), _info(info),
_body(nullptr)
{
_shape = new btSphereShape(0.25f);
btVector3 inertia(0.f, 0.f, 0.f);
_shape->calculateLocalInertia(1.f, inertia);
btRigidBody::btRigidBodyConstructionInfo riginfo(1.f, nullptr, _shape, inertia);
riginfo.m_startWorldTransform = btTransform(btQuaternion(), btVector3(position.x, position.y, position.z));
riginfo.m_mass = 1.f;
_body = new btRigidBody(riginfo);
_body->setUserPointer(this);
_body->setLinearVelocity(btVector3(_info.direction.x, _info.direction.y, _info.direction.z) * _info.velocity);
engine->dynamicsWorld->addRigidBody(_body);
if( _info.type == RPG ) {
// RPGs aren't affected by gravity
_body->setGravity( { 0.f, 0.f, 0.f } );
}
}
ProjectileObject::~ProjectileObject()
{
if( _body ) {
engine->dynamicsWorld->removeCollisionObject(_body);
delete _body;
}
if( _shape ) {
delete _shape;
}
}
void ProjectileObject::tick(float dt)
{
auto& bttr = _body->getWorldTransform();
position = { bttr.getOrigin().x(), bttr.getOrigin().y(), bttr.getOrigin().z() };
_info.time -= dt;
/// @todo implement contact callbacks or GhostObject for detecting overlaps for molotovs and RPGs.
if( _info.time <= 0.f ) {
explode();
/// @todo request the world delete us
}
}

View File

@ -414,10 +414,7 @@ void GameRenderer::renderWorld(float alpha)
void GameRenderer::renderPedestrian(CharacterObject *pedestrian)
{
glm::mat4 matrixModel;
matrixModel = glm::translate(matrixModel, pedestrian->getPosition());
matrixModel = matrixModel * glm::mat4_cast(pedestrian->getRotation());
glm::mat4 matrixModel = pedestrian->getTimeAdjustedTransform( _renderAlpha );
if(!pedestrian->model->model) return;
@ -428,7 +425,7 @@ void GameRenderer::renderPedestrian(CharacterObject *pedestrian)
glm::mat4 localMatrix;
if( handFrame ) {
while( handFrame->getParent() ) {
localMatrix = pedestrian->animator->getFrameMatrix(handFrame) * localMatrix;
localMatrix = pedestrian->animator->getFrameMatrix(handFrame, _renderAlpha) * localMatrix;
handFrame = handFrame->getParent();
}
}
@ -443,9 +440,7 @@ void GameRenderer::renderVehicle(VehicleObject *vehicle)
std::cout << "model " << vehicle->vehicle->modelName << " not loaded (" << engine->gameData.models.size() << " models loaded)" << std::endl;
}
glm::mat4 matrixModel;
matrixModel = glm::translate(matrixModel, vehicle->getPosition());
matrixModel = matrixModel * glm::mat4_cast(vehicle->getRotation());
glm::mat4 matrixModel = vehicle->getTimeAdjustedTransform( _renderAlpha );
renderModel(vehicle->model->model, matrixModel, vehicle);
@ -454,16 +449,40 @@ void GameRenderer::renderVehicle(VehicleObject *vehicle)
auto woi = engine->objectTypes.find(vehicle->vehicle->wheelModelID);
if(woi != engine->objectTypes.end()) {
Model* wheelModel = engine->gameData.models["wheels"]->model;
auto& wi = vehicle->physVehicle->getWheelInfo(w);
if( wheelModel ) {
// Tell bullet to update the matrix for this wheel.
// Construct our own matrix so we can use the local transform
vehicle->physVehicle->updateWheelTransform(w, false);
glm::mat4 wheel_tf;
vehicle->physVehicle->getWheelTransformWS(w).getOpenGLMatrix(glm::value_ptr(wheel_tf));
wheel_tf = glm::scale(wheel_tf, glm::vec3(vehicle->vehicle->wheelScale));
if(vehicle->physVehicle->getWheelInfo(w).m_chassisConnectionPointCS.x() < 0.f) {
wheel_tf = glm::scale(wheel_tf, glm::vec3(-1.f, 1.f, 1.f));
/// @todo migrate this into Vehicle physics tick so we can interpolate old -> new
glm::mat4 wheelM ( matrixModel );
auto up = -wi.m_wheelDirectionCS;
auto right = wi.m_wheelAxleCS;
auto fwd = up.cross(right);
btQuaternion steerQ(up, wi.m_steering);
btQuaternion rollQ(right, -wi.m_rotation);
btMatrix3x3 basis(
right[0], fwd[0], up[0],
right[1], fwd[1], up[1],
right[2], fwd[2], up[2]
);
btTransform t;
t.setBasis(btMatrix3x3(steerQ) * btMatrix3x3(rollQ) * basis);
t.setOrigin(wi.m_chassisConnectionPointCS + wi.m_wheelDirectionCS * wi.m_raycastInfo.m_suspensionLength);
t.getOpenGLMatrix(glm::value_ptr(wheelM));
wheelM = matrixModel * wheelM;
wheelM = glm::scale(wheelM, glm::vec3(vehicle->vehicle->wheelScale));
if(wi.m_chassisConnectionPointCS.x() < 0.f) {
wheelM = glm::scale(wheelM, glm::vec3(-1.f, 1.f, 1.f));
}
renderWheel(wheelModel, wheel_tf, woi->second->modelName);
renderWheel(wheelModel, wheelM, woi->second->modelName);
}
else {
std::cout << "Wheel model " << woi->second->modelName << " not loaded" << std::endl;

View File

@ -40,11 +40,13 @@ int debugMode = 0;
sf::Font font;
glm::vec3 viewPosition { -260.f, -151.5f, 9.f };
glm::vec2 viewAngles { -0.3f, 0.05f };
glm::vec3 viewPosition { -260.f, -151.5f, 9.f }, lastViewPosition;
glm::vec2 viewAngles { -0.3f, 0.05f }, lastViewAngles;
void setViewParameters(const glm::vec3 &center, const glm::vec2 &angles)
{
lastViewPosition = viewPosition;
lastViewAngles = viewAngles;
viewPosition = center;
viewAngles = angles;
}
@ -205,6 +207,8 @@ void init(std::string gtapath, bool loadWorld)
debugDrawer->setDebugMode(btIDebugDraw::DBG_DrawWireframe);
gta->dynamicsWorld->setDebugDrawer(debugDrawer);
setViewParameters( { -260.f, -151.5f, 9.f }, { -0.3f, 0.05f } );
std::cout << "Loaded "
<< gta->gameData.models.size() << " models, "
<< gta->gameData.textures.size() << " textures" << std::endl;
@ -213,20 +217,10 @@ void init(std::string gtapath, bool loadWorld)
void update(float dt)
{
if (inFocus) {
float qpi = glm::half_pi<float>();
glm::mat4 view;
view = glm::translate(view, viewPosition);
view = glm::rotate(view, viewAngles.x, glm::vec3(0, 0, 1));
view = glm::rotate(view, viewAngles.y - qpi, glm::vec3(1, 0, 0));
view = glm::inverse(view);
gta->gameTime += dt;
gta->renderer.camera.worldPos = viewPosition;
gta->renderer.camera.frustum.view = view;
for( GameObject* object : gta->objects ) {
object->_updateLastTransform();
object->tick(dt);
}
@ -238,6 +232,18 @@ void render(float alpha)
{
gta->_work->update();
float qpi = glm::half_pi<float>();
glm::mat4 view;
view = glm::translate(view, glm::mix(lastViewPosition, viewPosition, alpha));
auto va = glm::mix(lastViewAngles, viewAngles, alpha);
view = glm::rotate(view, va.x, glm::vec3(0, 0, 1));
view = glm::rotate(view, va.y - qpi, glm::vec3(1, 0, 0));
view = glm::inverse(view);
gta->renderer.camera.worldPos = viewPosition;
gta->renderer.camera.frustum.view = view;
// Update aspect ratio..
gta->renderer.camera.frustum.aspectRatio = window.getSize().x / (float) window.getSize().y;
@ -376,6 +382,7 @@ int main(int argc, char *argv[])
float accum = 0.f;
float ts = 1.f / 60.f;
float timescale = 1.f;
// Loop until the window is closed or we run out of state.
while (window.isOpen() && StateManager::get().states.size()) {
@ -388,7 +395,7 @@ int main(int argc, char *argv[])
StateManager::get().states.back()->handleEvent(event);
}
accum += clock.restart().asSeconds();
accum += clock.restart().asSeconds() * timescale;
while ( accum >= ts ) {

View File

@ -2,6 +2,7 @@
#include "test_globals.hpp"
#include <objects/CharacterObject.hpp>
#include <data/WeaponData.hpp>
#include <objects/ProjectileObject.hpp>
BOOST_AUTO_TEST_SUITE(WeaponTests)
@ -24,4 +25,94 @@ BOOST_AUTO_TEST_CASE(TestWeaponScan)
}
}
BOOST_AUTO_TEST_CASE(TestProjectile)
{
{
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE( character != nullptr );
auto projectile = new ProjectileObject(Global::get().e, {25.f, 0.f, 10.f},
{
ProjectileObject::Grenade,
{0.f, 0.f, -1.f},
2.0f,
0.5f
});
Global::get().e->objects.insert( projectile );
BOOST_CHECK( character->mHealth == 100.f );
for(float t = 0.f; t <= 1.f; t += 0.016f) {
Global::get().e->dynamicsWorld->stepSimulation(0.016f, 0, 0);
projectile->tick(0.016f);
}
BOOST_CHECK( projectile->getPosition().z < 10.f );
// Grenade should have dentonated by this point
BOOST_CHECK( character->mHealth < 100.f );
Global::get().e->destroyObject(projectile);
Global::get().e->destroyObject(character);
}
{
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE( character != nullptr );
auto projectile = new ProjectileObject(Global::get().e, {25.f, 0.f, 10.f},
{
ProjectileObject::Molotov,
{0.f, 0.f, -1.f},
2.0f,
1.5f
});
Global::get().e->objects.insert( projectile );
BOOST_CHECK( character->mHealth == 100.f );
for(float t = 0.f; t <= 1.f; t += 0.016f) {
Global::get().e->dynamicsWorld->stepSimulation(0.016f, 0, 0);
projectile->tick(0.016f);
}
BOOST_CHECK( projectile->getPosition().z < 10.f );
BOOST_CHECK( character->mHealth < 100.f );
Global::get().e->destroyObject(projectile);
Global::get().e->destroyObject(character);
}
{
auto character = Global::get().e->createPedestrian(1, {25.f, 0.f, 0.f});
BOOST_REQUIRE( character != nullptr );
auto projectile = new ProjectileObject(Global::get().e, {25.f, 0.f, 10.f},
{
ProjectileObject::RPG,
{0.f, 0.f, -1.f},
2.0f,
1.5f
});
Global::get().e->objects.insert( projectile );
BOOST_CHECK( character->mHealth == 100.f );
for(float t = 0.f; t <= 1.f; t += 0.016f) {
Global::get().e->dynamicsWorld->stepSimulation(0.016f, 0, 0);
projectile->tick(0.016f);
}
BOOST_CHECK( projectile->getPosition().z < 10.f );
BOOST_CHECK( character->mHealth < 100.f );
Global::get().e->destroyObject(projectile);
Global::get().e->destroyObject(character);
}
}
BOOST_AUTO_TEST_SUITE_END()