2019-07-23 20:55:23 +00:00
|
|
|
#include "common.h"
|
2020-04-17 13:31:11 +00:00
|
|
|
|
2019-07-23 20:55:23 +00:00
|
|
|
#include "Timer.h"
|
|
|
|
#include "WaterLevel.h"
|
|
|
|
#include "ModelIndices.h"
|
|
|
|
#include "Physical.h"
|
|
|
|
#include "Vehicle.h"
|
|
|
|
#include "Floater.h"
|
|
|
|
|
2020-07-01 16:03:39 +00:00
|
|
|
//--MIAMI: done
|
|
|
|
|
2020-04-17 05:54:14 +00:00
|
|
|
cBuoyancy mod_Buoyancy;
|
2019-07-23 20:55:23 +00:00
|
|
|
|
2020-07-01 16:03:39 +00:00
|
|
|
float fVolMultiplier = 1.0f;
|
2019-07-23 20:55:23 +00:00
|
|
|
// amount of boat volume in bounding box
|
|
|
|
// 1.0-volume is the empty space in the bbox
|
2020-07-01 16:03:39 +00:00
|
|
|
float fBoatVolumeDistribution[9] = {
|
2019-07-23 20:55:23 +00:00
|
|
|
// rear
|
|
|
|
0.75f, 0.9f, 0.75f,
|
|
|
|
0.95f, 1.0f, 0.95f,
|
2020-07-01 16:03:39 +00:00
|
|
|
0.4f, 0.7f, 0.4f
|
2019-07-23 20:55:23 +00:00
|
|
|
// bow
|
|
|
|
};
|
2020-07-01 16:03:39 +00:00
|
|
|
float fBoatVolumeDistributionCat[9] = {
|
|
|
|
0.9f, 0.3f, 0.9f,
|
|
|
|
1.0f, 0.5f, 1.0f,
|
|
|
|
0.95f, 0.4f, 0.95f
|
|
|
|
};
|
|
|
|
float fBoatVolumeDistributionSail[9] = {
|
|
|
|
0.55f, 0.95f, 0.55f,
|
|
|
|
0.75f, 1.1f, 0.75f,
|
|
|
|
0.3f, 0.8f, 0.3f
|
|
|
|
};
|
|
|
|
float fBoatVolumeDistributionDinghy[9] = {
|
|
|
|
0.65f, 0.85f, 0.65f,
|
|
|
|
0.85f, 1.1f, 0.85f,
|
|
|
|
0.65f, 0.95f, 0.65f
|
|
|
|
};
|
|
|
|
float fBoatVolumeDistributionSpeed[9] = {
|
|
|
|
0.7f, 0.9f, 0.7f,
|
|
|
|
0.95f, 1.0f, 0.95f,
|
|
|
|
0.6f, 0.7f, 0.6f
|
|
|
|
};
|
2019-07-23 20:55:23 +00:00
|
|
|
|
|
|
|
bool
|
2019-07-24 21:06:48 +00:00
|
|
|
cBuoyancy::ProcessBuoyancy(CPhysical *phys, float buoyancy, CVector *point, CVector *impulse)
|
2019-07-23 20:55:23 +00:00
|
|
|
{
|
|
|
|
m_numSteps = 2.0f;
|
|
|
|
|
2020-04-10 11:44:08 +00:00
|
|
|
if(!CWaterLevel::GetWaterLevel(phys->GetPosition(), &m_waterlevel, phys->bTouchingWater))
|
2019-07-23 20:55:23 +00:00
|
|
|
return false;
|
|
|
|
m_matrix = phys->GetMatrix();
|
|
|
|
|
|
|
|
PreCalcSetup(phys, buoyancy);
|
|
|
|
SimpleCalcBuoyancy();
|
2019-07-24 21:06:48 +00:00
|
|
|
float f = CalcBuoyancyForce(phys, point, impulse);
|
2019-07-23 20:55:23 +00:00
|
|
|
if(m_isBoat)
|
|
|
|
return true;
|
|
|
|
return f != 0.0f;
|
|
|
|
}
|
|
|
|
|
2020-07-01 16:03:39 +00:00
|
|
|
bool
|
|
|
|
cBuoyancy::ProcessBuoyancyBoat(CVehicle *veh, float buoyancy, CVector *point, CVector *impulse, bool bNoTurnForce)
|
|
|
|
{
|
|
|
|
m_numSteps = 2.0f;
|
|
|
|
|
|
|
|
if(!CWaterLevel::GetWaterLevel(veh->GetPosition(), &m_waterlevel, veh->bTouchingWater))
|
|
|
|
return false;
|
|
|
|
m_matrix = veh->GetMatrix();
|
|
|
|
PreCalcSetup(veh, buoyancy);
|
|
|
|
|
|
|
|
|
|
|
|
float x, y;
|
|
|
|
int ix, i;
|
|
|
|
tWaterLevel waterPosition;
|
|
|
|
CVector waterNormal;
|
|
|
|
|
|
|
|
// Floater is divided into 3x3 parts. Process and sum each of them
|
|
|
|
float volDiv = 1.0f/((m_dimMax.z - m_dimMin.z)*sq(m_numSteps+1.0f));
|
|
|
|
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);
|
|
|
|
FindWaterLevelNorm(m_positionZ, &waterLevel, &waterPosition, &waterNormal);
|
|
|
|
switch(veh->GetModelIndex()){
|
|
|
|
case MI_RIO:
|
|
|
|
fVolMultiplier = fBoatVolumeDistributionCat[i];
|
|
|
|
break;
|
|
|
|
case MI_SQUALO:
|
|
|
|
case MI_SPEEDER:
|
|
|
|
case MI_JETMAX:
|
|
|
|
fVolMultiplier = fBoatVolumeDistributionSpeed[i];
|
|
|
|
break;
|
|
|
|
case MI_COASTG:
|
|
|
|
case MI_DINGHY:
|
|
|
|
fVolMultiplier = fBoatVolumeDistributionDinghy[i];
|
|
|
|
break;
|
|
|
|
case MI_MARQUIS:
|
|
|
|
fVolMultiplier = fBoatVolumeDistributionSail[i];
|
|
|
|
break;
|
|
|
|
case MI_PREDATOR:
|
|
|
|
case MI_SKIMMER:
|
|
|
|
case MI_REEFER:
|
|
|
|
case MI_TROPIC:
|
|
|
|
default:
|
|
|
|
fVolMultiplier = fBoatVolumeDistribution[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(waterPosition != FLOATER_ABOVE_WATER){
|
|
|
|
float volume = SimpleSumBuoyancyData(waterLevel, waterPosition);
|
|
|
|
float upImpulse = volume * volDiv * buoyancy * CTimer::GetTimeStep();
|
|
|
|
CVector speed = veh->GetSpeed(Multiply3x3(veh->GetMatrix(), CVector(x, y, 0.0f)));
|
|
|
|
float damp = 1.0f - DotProduct(speed, waterNormal)*veh->pHandling->fSuspensionDampingLevel;
|
|
|
|
float finalImpulse = upImpulse*Max(damp, 0.0f);
|
|
|
|
impulse->z += finalImpulse;
|
|
|
|
if(!bNoTurnForce)
|
|
|
|
veh->ApplyTurnForce(finalImpulse*waterNormal, Multiply3x3(m_matrix, waterLevel));
|
|
|
|
}
|
|
|
|
i += 3;
|
|
|
|
}
|
|
|
|
ix++;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_volumeUnderWater *= volDiv;
|
|
|
|
|
|
|
|
*point = Multiply3x3(m_matrix, m_impulsePoint);
|
|
|
|
return m_isBoat || m_haveVolume;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-07-23 20:55:23 +00:00
|
|
|
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){
|
2020-07-01 16:03:39 +00:00
|
|
|
switch(phys->GetModelIndex()){
|
|
|
|
case MI_PREDATOR:
|
|
|
|
default:
|
|
|
|
m_dimMax.y *= 1.05f;
|
|
|
|
m_dimMin.y *= 0.9f;
|
|
|
|
break;
|
|
|
|
case MI_SPEEDER:
|
|
|
|
m_dimMax.y *= 1.25f;
|
|
|
|
m_dimMin.y *= 0.83f;
|
|
|
|
break;
|
|
|
|
case MI_REEFER:
|
|
|
|
m_dimMin.y *= 0.9f;
|
|
|
|
break;
|
|
|
|
case MI_RIO:
|
|
|
|
m_dimMax.y *= 0.9f;
|
|
|
|
m_dimMin.y *= 0.9f;
|
|
|
|
m_dimMax.z += 0.25f;
|
|
|
|
m_dimMin.z -= 0.2f;
|
|
|
|
break;
|
|
|
|
case MI_SQUALO:
|
2019-07-23 20:55:23 +00:00
|
|
|
m_dimMax.y *= 0.9f;
|
|
|
|
m_dimMin.y *= 0.9f;
|
2020-07-01 16:03:39 +00:00
|
|
|
break;
|
|
|
|
case MI_TROPIC:
|
|
|
|
m_dimMax.y *= 1.3f;
|
|
|
|
m_dimMin.y *= 0.82f;
|
|
|
|
m_dimMin.z -= 0.2f;
|
|
|
|
break;
|
|
|
|
case MI_SKIMMER:
|
|
|
|
m_dimMin.y = -m_dimMax.y;
|
|
|
|
m_dimMax.y *= 1.2f;
|
|
|
|
break;
|
|
|
|
case MI_COASTG:
|
2019-07-23 20:55:23 +00:00
|
|
|
m_dimMax.y *= 1.1f;
|
|
|
|
m_dimMin.y *= 0.9f;
|
2020-07-01 16:03:39 +00:00
|
|
|
m_dimMin.z -= 0.3f;
|
|
|
|
break;
|
|
|
|
case MI_DINGHY:
|
|
|
|
m_dimMax.y *= 1.3f;
|
2019-07-23 20:55:23 +00:00
|
|
|
m_dimMin.y *= 0.9f;
|
2020-07-01 16:03:39 +00:00
|
|
|
m_dimMin.z -= 0.2f;
|
|
|
|
break;
|
|
|
|
case MI_MARQUIS:
|
|
|
|
m_dimMax.y *= 1.3f;
|
2019-07-23 20:55:23 +00:00
|
|
|
m_dimMin.y *= 0.9f;
|
2020-07-01 16:03:39 +00:00
|
|
|
break;
|
|
|
|
case MI_JETMAX:
|
|
|
|
m_dimMin.y *= 0.9f;
|
|
|
|
break;
|
2019-07-23 20:55:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-07-24 21:06:48 +00:00
|
|
|
m_impulsePoint = CVector(0.0f, 0.0f, 0.0f);
|
2019-07-23 20:55:23 +00:00
|
|
|
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;
|
|
|
|
tWaterLevel waterPosition;
|
|
|
|
|
|
|
|
// Floater is divided into 3x3 parts. Process and sum each of them
|
|
|
|
for(x = m_dimMin.x; x <= m_dimMax.x; x += m_step.x){
|
|
|
|
for(y = m_dimMin.y; y <= m_dimMax.y; y += m_step.y){
|
|
|
|
CVector waterLevel(x, y, 0.0f);
|
|
|
|
FindWaterLevel(m_positionZ, &waterLevel, &waterPosition);
|
2020-07-01 16:03:39 +00:00
|
|
|
fVolMultiplier = 1.0f;
|
2019-07-23 20:55:23 +00:00
|
|
|
if(waterPosition != FLOATER_ABOVE_WATER)
|
|
|
|
SimpleSumBuoyancyData(waterLevel, waterPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
2019-07-24 21:06:48 +00:00
|
|
|
m_impulsePoint = m_impulsePoint*fRemainingSlice + AverageOfWaterLevel*fThisVolume*fFraction;
|
2019-07-23 20:55:23 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 16:03:39 +00:00
|
|
|
// Same as above but also get normal
|
|
|
|
void
|
|
|
|
cBuoyancy::FindWaterLevelNorm(const CVector &zpos, CVector *waterLevel, tWaterLevel *waterPosition, CVector *normal)
|
|
|
|
{
|
|
|
|
*waterPosition = FLOATER_IN_WATER;
|
|
|
|
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_dimMin.z)
|
|
|
|
*normal = CWaterLevel::GetWaterNormal(xWaterLevel.x + m_position.x, xWaterLevel.y + m_position.y);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-23 20:55:23 +00:00
|
|
|
bool
|
2019-07-24 21:06:48 +00:00
|
|
|
cBuoyancy::CalcBuoyancyForce(CPhysical *phys, CVector *point, CVector *impulse)
|
2019-07-23 20:55:23 +00:00
|
|
|
{
|
|
|
|
if(!m_haveVolume)
|
|
|
|
return false;
|
|
|
|
|
2019-07-24 21:06:48 +00:00
|
|
|
*point = Multiply3x3(m_matrix, m_impulsePoint);
|
|
|
|
*impulse = CVector(0.0f, 0.0f, m_volumeUnderWater*m_buoyancy*CTimer::GetTimeStep());
|
2019-07-23 20:55:23 +00:00
|
|
|
return true;
|
|
|
|
}
|