From ae69aaf5ce8eca137bd5da85e26c63a5027e049d Mon Sep 17 00:00:00 2001 From: aap Date: Tue, 23 Jul 2019 22:55:23 +0200 Subject: [PATCH] implemented cBuoyancy --- src/entities/Entity.h | 2 +- src/render/WaterLevel.cpp | 2 +- src/render/WaterLevel.h | 1 + src/vehicles/Floater.cpp | 195 ++++++++++++++++++++++++++++++++++++++ src/vehicles/Floater.h | 45 +++++++++ 5 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 src/vehicles/Floater.cpp create mode 100644 src/vehicles/Floater.h diff --git a/src/entities/Entity.h b/src/entities/Entity.h index e975fb13..cdc9a173 100644 --- a/src/entities/Entity.h +++ b/src/entities/Entity.h @@ -73,7 +73,7 @@ public: uint32 bRemoveFromWorld : 1; uint32 bHasHitWall : 1; uint32 bImBeingRendered : 1; - uint32 m_flagD8 : 1; + uint32 m_flagD8 : 1; // used by cBuoyancy::ProcessBuoyancy uint32 bIsSubway : 1; // set when subway, but maybe different meaning? uint32 bDrawLast : 1; uint32 m_flagD40 : 1; diff --git a/src/render/WaterLevel.cpp b/src/render/WaterLevel.cpp index 30be88e1..4f315cd0 100644 --- a/src/render/WaterLevel.cpp +++ b/src/render/WaterLevel.cpp @@ -1240,7 +1240,7 @@ STARTPATCHES InjectHook(0x554FE0, &CWaterLevel::Shutdown, PATCH_JUMP); InjectHook(0x555010, &CWaterLevel::CreateWavyAtomic, PATCH_JUMP); InjectHook(0x5552A0, &CWaterLevel::DestroyWavyAtomic, PATCH_JUMP); - InjectHook(0x5552C0, &CWaterLevel::GetWaterLevel, PATCH_JUMP); + InjectHook(0x5552C0, (bool (*)(float,float,float,float*,bool))&CWaterLevel::GetWaterLevel, PATCH_JUMP); InjectHook(0x555440, &CWaterLevel::GetWaterLevelNoWaves, PATCH_JUMP); InjectHook(0x5554E0, &CWaterLevel::RenderWater, PATCH_JUMP); InjectHook(0x556C30, &CWaterLevel::RenderOneFlatSmallWaterPoly, PATCH_JUMP); diff --git a/src/render/WaterLevel.h b/src/render/WaterLevel.h index b8ec7a4d..afc6eac3 100644 --- a/src/render/WaterLevel.h +++ b/src/render/WaterLevel.h @@ -82,6 +82,7 @@ public: static void CreateWavyAtomic(); static void DestroyWavyAtomic(); static bool GetWaterLevel(float fX, float fY, float fZ, float *pfOutLevel, bool bDontCheckZ); + static bool GetWaterLevel(CVector coors, float *pfOutLevel, bool bDontCheckZ) { return GetWaterLevel(coors.x, coors.y, coors.z, pfOutLevel, bDontCheckZ); } static bool GetWaterLevelNoWaves(float fX, float fY, float fZ, float *pfOutLevel); static void RenderWater(); static void RenderOneFlatSmallWaterPoly (float fX, float fY, float fZ, RwRGBA const &color); diff --git a/src/vehicles/Floater.cpp b/src/vehicles/Floater.cpp new file mode 100644 index 00000000..cabe00c3 --- /dev/null +++ b/src/vehicles/Floater.cpp @@ -0,0 +1,195 @@ +#include "common.h" +#include "patcher.h" +#include "Timer.h" +#include "WaterLevel.h" +#include "ModelIndices.h" +#include "Physical.h" +#include "Vehicle.h" +#include "Floater.h" + +cBuoyancy &mod_Buoyancy = *(cBuoyancy*)0x8F2674; + +//static float fVolMultiplier = 1.0f; +static float &fVolMultiplier = *(float*)0x601394; +// amount of boat volume in bounding box +// 1.0-volume is the empty space in the bbox +static float fBoatVolumeDistribution[9] = { + // rear + 0.75f, 0.9f, 0.75f, + 0.95f, 1.0f, 0.95f, + 0.3f, 0.7f, 0.3f + // bow +}; + +bool +cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point) +{ + m_numSteps = 2.0f; + + if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->m_flagD8)) + return false; + m_matrix = phys->GetMatrix(); + + PreCalcSetup(phys, buoyancy); + SimpleCalcBuoyancy(); + float f = CalcBuoyancyForce(phys, impulse, point); + if(m_isBoat) + return true; + return f != 0.0f; +} + +void +cBuoyancy::PreCalcSetup(CPhysical *phys, float buoyancy) +{ + CColModel *colModel; + + m_isBoat = phys->IsVehicle() && ((CVehicle*)phys)->IsBoat(); + colModel = phys->GetColModel(); + m_dimMin = colModel->boundingBox.min; + m_dimMax = colModel->boundingBox.max; + + if(m_isBoat){ + if(phys->GetModelIndex() == MI_PREDATOR){ + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + }else if(phys->GetModelIndex() == MI_SPEEDER){ + m_dimMax.y *= 1.1f; + m_dimMin.y *= 0.9f; + }else if(phys->GetModelIndex() == MI_REEFER){ + m_dimMin.y *= 0.9f; + }else{ + m_dimMax.y *= 0.9f; + m_dimMin.y *= 0.9f; + } + } + + m_step = (m_dimMax - m_dimMin)/m_numSteps; + + if(m_step.z > m_step.x && m_step.z > m_step.y){ + m_stepRatio.x = m_step.x/m_step.z; + m_stepRatio.y = m_step.y/m_step.z; + m_stepRatio.z = 1.0f; + }else if(m_step.y > m_step.x && m_step.y > m_step.z){ + m_stepRatio.x = m_step.x/m_step.y; + m_stepRatio.y = 1.0f; + m_stepRatio.z = m_step.z/m_step.y; + }else{ + m_stepRatio.x = 1.0f; + m_stepRatio.y = m_step.y/m_step.x; + m_stepRatio.z = m_step.z/m_step.x; + } + + m_haveVolume = false; + m_numPartialVolumes = 1.0f; + m_volumeUnderWater = 0.0f; + m_impulse = CVector(0.0f, 0.0f, 0.0f); + m_position = phys->GetPosition(); + m_positionZ = CVector(0.0f, 0.0f, m_position.z); + m_buoyancy = buoyancy; + m_waterlevel += m_waterLevelInc; +} + +void +cBuoyancy::SimpleCalcBuoyancy(void) +{ + float x, y; + int ix, i; + tWaterLevel waterPosition; + + // Floater is divided into 3x3 parts. Process and sum each of them + ix = 0; + for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){ + i = ix; + for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){ + CVector waterLevel(x, y, 0.0f); + FindWaterLevel(m_positionZ, &waterLevel, &waterPosition); + fVolMultiplier = m_isBoat ? fBoatVolumeDistribution[i] : 1.0f; + if(waterPosition != FLOATER_ABOVE_WATER) + SimpleSumBuoyancyData(waterLevel, waterPosition); + i += 3; + } + ix++; + } + + m_volumeUnderWater /= (m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f); +} + +float +cBuoyancy::SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition) +{ + static float fThisVolume; + static CVector AverageOfWaterLevel; + static float fFraction; + static float fRemainingSlice; + + float submerged = Abs(waterLevel.z - m_dimMin.z); + // subtract empty space from submerged volume + fThisVolume = submerged - (1.0f - fVolMultiplier); + if(fThisVolume < 0.0f) + return 0.0f; + + if(m_isBoat){ + fThisVolume *= fVolMultiplier; + if(fThisVolume < 0.5f) + fThisVolume = 2.0f*sq(fThisVolume); + if(fThisVolume < 1.0f) + fThisVolume = sq(fThisVolume); + fThisVolume = sq(fThisVolume); + } + + m_volumeUnderWater += fThisVolume; + + AverageOfWaterLevel.x = waterLevel.x * m_stepRatio.x; + AverageOfWaterLevel.y = waterLevel.y * m_stepRatio.y; + AverageOfWaterLevel.z = (waterLevel.z+m_dimMin.z)/2.0f * m_stepRatio.z; + + if(m_flipAverage) + AverageOfWaterLevel = -AverageOfWaterLevel; + + fFraction = 1.0f/m_numPartialVolumes; + fRemainingSlice = 1.0f - fFraction; + m_impulse = m_impulse*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction; + m_numPartialVolumes += 1.0f; + m_haveVolume = true; + return fThisVolume; +} + +void +cBuoyancy::FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition) +{ + *waterPosition = FLOATER_IN_WATER; + // waterLevel is a local x,y point + // m_position is the global position of our floater + // zpos is the global z coordinate of our floater + CVector xWaterLevel = Multiply3x3(m_matrix, *waterLevel); + CWaterLevel::GetWaterLevel(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y, m_position.z, + &waterLevel->z, true); + waterLevel->z -= xWaterLevel.z + zpos.z; // make local + if(waterLevel->z > m_dimMax.z){ + waterLevel->z = m_dimMax.z; + *waterPosition = FLOATER_UNDER_WATER; + }else if(waterLevel->z < m_dimMin.z){ + waterLevel->z = m_dimMin.z; + *waterPosition = FLOATER_ABOVE_WATER; + } +} + +bool +cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point) +{ + if(!m_haveVolume) + return false; + + *impulse = Multiply3x3(m_matrix, m_impulse); + *point = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep()); + return true; +} + +STARTPATCHES + InjectHook(0x546270, &cBuoyancy::ProcessBuoyancy, PATCH_JUMP); + InjectHook(0x546360, &cBuoyancy::PreCalcSetup, PATCH_JUMP); + InjectHook(0x5466F0, &cBuoyancy::SimpleCalcBuoyancy, PATCH_JUMP); + InjectHook(0x546820, &cBuoyancy::SimpleSumBuoyancyData, PATCH_JUMP); + InjectHook(0x546620, &cBuoyancy::FindWaterLevel, PATCH_JUMP); + InjectHook(0x5465A0, &cBuoyancy::CalcBuoyancyForce, PATCH_JUMP); +ENDPATCHES diff --git a/src/vehicles/Floater.h b/src/vehicles/Floater.h new file mode 100644 index 00000000..ede2b9d0 --- /dev/null +++ b/src/vehicles/Floater.h @@ -0,0 +1,45 @@ +#pragma once + +class Physical; + +enum tWaterLevel +{ + FLOATER_ABOVE_WATER, + FLOATER_IN_WATER, + FLOATER_UNDER_WATER, +}; + +class cBuoyancy +{ +public: + CVector m_position; + CMatrix m_matrix; + int m_field_54; + CVector m_positionZ; + float m_waterlevel; + float m_waterLevelInc; + float m_buoyancy; + CVector m_dimMax; + CVector m_dimMin; + float m_numPartialVolumes; + int m_field_8C; + int m_field_90; + int m_field_94; + bool m_haveVolume; + CVector m_step; + CVector m_stepRatio; + float m_numSteps; + bool m_flipAverage; + char m_field_B9; + bool m_isBoat; + float m_volumeUnderWater; + CVector m_impulse; + + bool ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *impulse, CVector *point); + void PreCalcSetup(CPhysical *phys, float buoyancy); + void SimpleCalcBuoyancy(void); + float SimpleSumBuoyancyData(CVector &waterLevel, tWaterLevel waterPosition); + void FindWaterLevel(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition); + bool CalcBuoyancyForce(CPhysical *phys, CVector *impulse, CVector *point); +}; +extern cBuoyancy &mod_Buoyancy;