1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-22 10:22:52 +01:00

Initial, unstable, water dynamics

This commit is contained in:
Daniel Evans 2014-06-13 21:54:17 +01:00
parent 77aa71df7e
commit 2c16134abd
11 changed files with 191 additions and 13 deletions

View File

@ -260,6 +260,8 @@ public:
* The "real" water heights
*/
uint8_t realWater[128*128];
int getWaterIndexAt(const glm::vec3& ws) const;
};
#endif

View File

@ -33,8 +33,11 @@ struct GameObject
*/
float mHealth;
bool _inWater;
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)
: position(pos), rotation(rot), model(model), engine(engine), animator(nullptr), mHealth(0.f),
_inWater(false)
{}
virtual ~GameObject() {}
@ -96,6 +99,8 @@ struct GameObject
virtual bool isFrameVisible(ModelFrame* frame) const { return true; }
virtual bool isAnimationFixed() const { return true; }
virtual bool isInWater() const { return _inWater; }
};
#endif // __GAMEOBJECTS_HPP__

View File

@ -7,6 +7,12 @@
#include <string>
#include <glm/glm.hpp>
#define NO_WATER_INDEX 48
#define WATER_LQ_DATA_SIZE 64
#define WATER_HQ_DATA_SIZE 128
#define WATER_WORLD_SIZE 4096.f
#define WATER_HQ_DISTANCE 128.f
class Animation;
typedef std::map<std::string, Animation*> AnimationSet;

View File

@ -36,6 +36,9 @@ private:
float brake;
bool handbrake;
unsigned int damageFlags;
// Used to determine in water status
float _lastHeight;
public:
VehicleDataHandle vehicle;
@ -103,6 +106,8 @@ public:
void setPartDamaged(unsigned int flag, bool damaged);
virtual bool isFrameVisible(ModelFrame *frame) const;
void applyWaterFloat(const glm::vec3& relPt, float force, float waterOffset);
};
/**

View File

@ -91,6 +91,8 @@ glm::vec3 PlayerController::getTargetPosition()
void PlayerController::jump()
{
character->jump();
if(! character->isInWater() ) {
character->jump();
}
}

View File

@ -581,3 +581,15 @@ TextureAtlas* GameData::getAtlas(size_t i)
}
return nullptr;
}
int GameData::getWaterIndexAt(const glm::vec3 &ws) const
{
auto wX = (int) ((ws.x + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE));
auto wY = (int) ((ws.y + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE));
if( wX >= 0 && wX < WATER_HQ_DATA_SIZE && wY >= 0 && wY < WATER_HQ_DATA_SIZE ) {
int i = (wX*WATER_HQ_DATA_SIZE) + wY;
return engine->gameData.realWater[i];
}
return 0;
}

View File

@ -225,9 +225,26 @@ void CharacterObject::updateCharacter(float dt)
}
}
// Handle above waist height water.
auto wi = engine->gameData.getWaterIndexAt(getPosition());
if( wi != NO_WATER_INDEX ) {
float wh = engine->gameData.waterHeights[wi];
auto ws = getPosition();
if( ws.z < wh ) {
ws.z = wh;
setPosition(ws);
physCharacter->setGravity(0.f);
_inWater = true;
}
else {
physCharacter->setGravity(9.81f);
_inWater = false;
}
}
if(currentActivity == CharacterObject::Jump)
{
if(physCharacter->onGround())
if(physCharacter->onGround() || isInWater())
{
enterAction(CharacterObject::Idle);
}

View File

@ -9,7 +9,8 @@
VehicleObject::VehicleObject(GameWorld* engine, const glm::vec3& pos, const glm::quat& rot, ModelHandle* model, VehicleDataHandle data, VehicleInfoHandle info, const glm::vec3& prim, const glm::vec3& sec)
: GameObject(engine, pos, rot, model),
steerAngle(0.f), throttle(0.f), brake(0.f), handbrake(false),
damageFlags(0), vehicle(data), info(info), colourPrimary(prim),
damageFlags(0), _lastHeight(0.f), vehicle(data),
info(info), colourPrimary(prim),
colourSecondary(sec), physBody(nullptr), physVehicle(nullptr)
{
mHealth = 100.f;
@ -181,6 +182,63 @@ void VehicleObject::tick(float dt)
//physVehicle->setSteeringValue(std::min(3.141f/2.f, std::abs(steerAngle)) * sign, w);
}
}
auto ws = getPosition();
auto wX = (int) ((ws.x + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE));
auto wY = (int) ((ws.y + WATER_WORLD_SIZE/2.f) / (WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE));
float vH = ws.z - info->handling.dimensions.z;
float wH = 0.f;
if( wX >= 0 && wX < WATER_HQ_DATA_SIZE && wY >= 0 && wY < WATER_HQ_DATA_SIZE ) {
int i = (wX*WATER_HQ_DATA_SIZE) + wY;
int hI = engine->gameData.realWater[i];
if( hI < NO_WATER_INDEX ) {
wH = engine->gameData.waterHeights[hI];
// If the vehicle is currently underwater
if( vH <= wH ) {
// and was not underwater here in the last tick
if( _lastHeight >= wH ) {
// we are for real, underwater
_inWater = true;
}
else if( _inWater == false ) {
// It's just a tunnel or something, we good.
_inWater = false;
}
}
else {
// The water is beneath us
_inWater = false;
}
}
else {
_inWater = false;
}
}
if( _inWater ) {
float oZ = 0.f;
if( vehicle->type != VehicleData::BOAT ) {
oZ = -((-info->handling.dimensions.z/2.f) + info->handling.dimensions.z * (info->handling.percentSubmerged/100.f));
}
auto vFwd = getRotation() * glm::vec3(info->handling.dimensions.y, 0.f, 0.f);
auto vRt = getRotation() * glm::vec3(0.f, info->handling.dimensions.x, 0.f);
float buoyancyForce = info->handling.mass * 9.81f * 0.75f;
// Damper motion
physBody->setDamping(0.9f, 0.9f);
applyWaterFloat( vFwd, buoyancyForce, oZ);
applyWaterFloat(-vFwd, buoyancyForce, oZ);
applyWaterFloat( vRt, buoyancyForce, oZ);
applyWaterFloat(-vRt, buoyancyForce, oZ);
}
else {
physBody->setDamping(0.0f, 0.0f);
}
_lastHeight = vH;
}
}
@ -318,6 +376,19 @@ bool VehicleObject::isFrameVisible(ModelFrame *frame) const
return true;
}
void VehicleObject::applyWaterFloat(const glm::vec3 &relPt, float force, float waterOffset)
{
auto ws = getPosition() + relPt;
auto wi = engine->gameData.getWaterIndexAt(ws);
if(wi != NO_WATER_INDEX) {
float h = engine->gameData.waterHeights[wi] + waterOffset;
if ( ws.z <= h ) {
physBody->applyForce(btVector3(0.f, 0.f, (1.f + h - ws.z) * force),
btVector3(relPt.x, relPt.y, relPt.z));
}
}
}
// Dammnit Bullet
class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback

View File

@ -436,11 +436,6 @@ void GameRenderer::renderWorld(float alpha)
glUseProgram( waterProgram );
// TODO: Add some kind of draw distance
#define NO_WATER_INDEX 48
#define WATER_LQ_DATA_SIZE 64
#define WATER_HQ_DATA_SIZE 128
#define WATER_WORLD_SIZE 4096.f
#define WATER_HQ_DISTANCE 128.f
float blockLQSize = WATER_WORLD_SIZE/WATER_LQ_DATA_SIZE;
float blockHQSize = WATER_WORLD_SIZE/WATER_HQ_DATA_SIZE;

View File

@ -9,15 +9,17 @@
IngameState::IngameState()
: _player(nullptr), _playerCharacter(nullptr)
{
_playerCharacter = getWorld()->createPedestrian(1, {-1240.f, -875.f, 13.f});
_playerCharacter = getWorld()->createPedestrian(1, {-1000.f, -990.f, 13.f});
_player = new PlayerController(_playerCharacter);
setPlayerCharacter( _playerCharacter );
float j = 0;
auto spawnPos = glm::vec3( -1200.f, -885.f, 14.f );
auto spawnPos = glm::vec3( -1000.f, -1000.f, 14.f );
for( auto& vi : getWorld()->vehicleTypes ) {
auto v = getWorld()->createVehicle(vi.first, spawnPos, glm::quat());
auto sp = spawnPos;
if(vi.first == 120) sp = { -1000.f, -1050.f, 5.f };
auto v = getWorld()->createVehicle(vi.first, sp, glm::quat());
spawnPos -= glm::vec3( 2.f + v->info->handling.dimensions.x, 0.f, 0.f);
if( ++j > 33 ) break;
}

61
tests/test_buoyancy.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <boost/test/unit_test.hpp>
#include "test_globals.hpp"
#include <objects/VehicleObject.hpp>
BOOST_AUTO_TEST_SUITE(BuoyancyTests)
BOOST_AUTO_TEST_CASE(test_vehicle_buoyancy)
{
glm::vec2 tpos(-WATER_WORLD_SIZE/2.f + 10.f);
{
VehicleObject* vehicle = Global::get().e->createVehicle(90u, glm::vec3(tpos, 100.f), glm::quat());
BOOST_REQUIRE(vehicle != nullptr);
BOOST_REQUIRE(vehicle->info != nullptr);
BOOST_REQUIRE(vehicle->vehicle != nullptr);
// Relies on tile 0,0 being watered...
BOOST_CHECK( ! vehicle->isInWater() );
// Move it under the water
vehicle->setPosition(glm::vec3(tpos, -5.f));
// Allow the object to update
vehicle->tick(0.0016f);
BOOST_CHECK( vehicle->isInWater() );
// Ensure that the in water state sticks
vehicle->tick(0.0016f);
BOOST_CHECK( vehicle->isInWater() );
vehicle->setPosition(glm::vec3(tpos, 5.f));
vehicle->tick(0.0016f);
BOOST_CHECK( ! vehicle->isInWater() );
// TODO: fix magic numbers
auto orgval = Global::get().e->gameData.realWater[0];
Global::get().e->gameData.realWater[0] = NO_WATER_INDEX;
vehicle->tick(0.0016f);
BOOST_CHECK( ! vehicle->isInWater() );
vehicle->setPosition(glm::vec3(tpos, -5.f));
vehicle->tick(0.0016f);
BOOST_CHECK( ! vehicle->isInWater() );
Global::get().e->gameData.realWater[0] = orgval;
vehicle->tick(0.0016f);
BOOST_CHECK( ! vehicle->isInWater() );
Global::get().e->destroyObject(vehicle);
}
}
BOOST_AUTO_TEST_SUITE_END()