Files
GTASource/game/vfx/misc/LinearPiecewiseFog.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

356 lines
13 KiB
C++

#include "LinearPiecewiseFog.h"
#if LINEAR_PIECEWISE_FOG
#include "parser/manager.h"
#include "LinearPiecewiseFog_parser.h"
///////////////////////////////////////////////////////////////////////////////
// Global data.
///////////////////////////////////////////////////////////////////////////////
#if __BANK
#include "rmptfx/ptxkeyframe.h"
// RAG key frames etc.
LinearPiecewiseFog::FogKeyframe LinearPiecewiseFog::sm_Keyframes[2];
float LinearPiecewiseFog::sm_Interp = 0.0f;
bool LinearPiecewiseFog::sm_DoLinearPiecewiseFog = false;
bool LinearPiecewiseFog::sm_UseOverrideHeightsAndVisibilites = false;
ptxKeyframe LinearPiecewiseFog::sm_ProfileVisualise;
#endif //__BANK
///////////////////////////////////////////////////////////////////////////////
// LinearPiecewiseFogShape.
///////////////////////////////////////////////////////////////////////////////
#define LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY 5.0f // 10^5 = 100000 mtrs.
#define LINEAR_PIECEWISE_FOG_WIDGET_MIN_VISIBILITY 0.0f // 10^0 = 1mtr.
#define LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS 0
#if LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
// Defined over 10^LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT, normalized into [0, 1] range then scaled over shape range.
#define LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT 5.0f // 10^5 = 100000 mtrs.
#define LINEAR_PIECEWISE_FOG_WIDGET_MIN_HEIGHT 0.0f // 10^0 = 1mtr.
#define LINEAR_PIECEWISE_FOG_MAX_HEIGHT powf(10, LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT)
#else // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
#define LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT 1.0f // Just scaled over shape range.
#define LINEAR_PIECEWISE_FOG_WIDGET_MIN_HEIGHT 0.0f
#define LINEAR_PIECEWISE_FOG_MAX_HEIGHT 10000.0f
#endif // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
LinearPiecewiseFogShape::LinearPiecewiseFogShape()
{
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
float k = (float)i/(float)(LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS - 1);
float t = k*LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT;
// Add some alpha values which the key-frame optimizer cannot reduce.
m_ControlPoints[i].heightValue = t;
m_ControlPoints[i].visibilityScale = expf(-k);
}
}
LinearPiecewiseFogShape::~LinearPiecewiseFogShape()
{
}
float LinearPiecewiseFogShape::GetHeightAtControlPoint(int i, float rangeMin, float rangeMax)
{
#if LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
// Height is on a logarithmic scale of base 10.
return powf(10, m_ControlPoints[i].heightValue - LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT)*(rangeMax - rangeMin) + rangeMin;
#else // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
return m_ControlPoints[i].heightValue*(rangeMax - rangeMin) + rangeMin;
#endif // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
}
float LinearPiecewiseFogShape::GetVisibilityAtControlPoint(int i, float log10MinVisibilty)
{
float v = m_ControlPoints[i].visibilityScale;
v = (log10MinVisibilty - LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY)*v + LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY;
// Make the RAG widget act like a logarithmic scale, base 10.
float visibility = powf(10, v);
return visibility;
}
#if __BANK
void LinearPiecewiseFogShape::AddWidgets(bkBank &rBank, int i)
{
char groupName[256];
sprintf(groupName, "Fog shape %d", i);
rBank.PushGroup(groupName, false);
rBank.AddTitle("------------------------ Fog thickness ------------------------");
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
char str[256];
sprintf(str, "%d", i);
rBank.AddSlider(str, &m_ControlPoints[i].visibilityScale, 0.0f, 1.0f, 0.001f);
}
rBank.AddTitle("------------------------ Heights ------------------------");
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
char str[256];
#if LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
sprintf(str, "%d (log10)", i);
#else // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
sprintf(str, "%d", i);
#endif // LINEAR_PIECEWISE_FOG_USE_LOG10_HEIGHTS
rBank.AddSlider(str, &m_ControlPoints[i].heightValue, 0.0f, LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT, 0.001f);
}
rBank.AddButton("Visualize", datCallback(CFA2(LinearPiecewiseFogShape::OnVisualise), CallbackData((void *)this)));
rBank.PopGroup();
}
void LinearPiecewiseFogShape::OnVisualise(CallbackData cbData)
{
LinearPiecewiseFogShape *pThis = (LinearPiecewiseFogShape *)cbData;
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
Vec4V v = Vec4V(1.0f, 1.0f, 1.0f, pThis->m_ControlPoints[i].visibilityScale);
ScalarV t = ScalarV(pThis->m_ControlPoints[i].heightValue);
if(i==0)
LinearPiecewiseFog::sm_ProfileVisualise.Init(t, v);
else
LinearPiecewiseFog::sm_ProfileVisualise.AddEntry(t, v);
}
LinearPiecewiseFog::sm_ProfileVisualise.UpdateWidget();
}
#endif //__BANK
/*---------------------------------------------------------------------------------------------------------------------*/
///////////////////////////////////////////////////////////////////////////////
// LinearPiecewiseFog
///////////////////////////////////////////////////////////////////////////////
LinearPiecewiseFog::FogKeyframe::FogKeyframe()
{
m_minHeight = 0.0f;
m_maxHeight = LINEAR_PIECEWISE_FOG_MAX_HEIGHT;
m_log10MinVisibility = LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY;
m_shapeWeights = Vec4V(1.0f, 0.0f, 0.0f, 0.0f);
}
// Palette of fog shapes.
LinearPiecewise_FogShapePalette LinearPiecewiseFog::sm_ShapePalette;
void LinearPiecewiseFog::Init()
{
LoadShapes();
}
void LinearPiecewiseFog::CleanUp()
{
}
void LinearPiecewiseFog::ComputeShaderParams(LinearPiecewiseFog_ShaderParams &shaderParams, float cameraHeight, FogKeyframe timeCycleKeyframe)
{
static float s_MaxVisibility = powf(10, LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY);
FogKeyframe keyframeToUse = timeCycleKeyframe;
HeightAndVisibility sortedByHeight[LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS];
#if __BANK
if(sm_UseOverrideHeightsAndVisibilites)
{
// Interpolate between key-frames to simulate time-cycle.
keyframeToUse.m_minHeight = (1.0f - sm_Interp)*sm_Keyframes[0].m_minHeight + sm_Interp*sm_Keyframes[1].m_minHeight;
keyframeToUse.m_maxHeight = (1.0f - sm_Interp)*sm_Keyframes[0].m_maxHeight + sm_Interp*sm_Keyframes[1].m_maxHeight;
keyframeToUse.m_log10MinVisibility = (1.0f - sm_Interp)*sm_Keyframes[0].m_log10MinVisibility + sm_Interp*sm_Keyframes[1].m_log10MinVisibility;
keyframeToUse.m_shapeWeights = ScalarV(1.0f - sm_Interp)*sm_Keyframes[0].m_shapeWeights + ScalarV(sm_Interp)*sm_Keyframes[1].m_shapeWeights;
}
#endif //__BANK
HeightAndVisibility entry;
entry.height = 0.0f;
entry.visibility = 0.0f;
// Reset to a zero profile.
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
sortedByHeight[i] = entry;
// Create the fog shape from the keyframe weights.
LinearPiecewiseFogShape finalShape;
float modWeightsSqr = Dot(keyframeToUse.m_shapeWeights, keyframeToUse.m_shapeWeights).Getf();
// Create a valid weight if neds be.
if(modWeightsSqr < 0.001f)
{
Assertf(1, "LinearPiecewiseFog::ComputeShaderParams()...Shape weight is zeros");
keyframeToUse.m_shapeWeights = Vec4V(V_ZERO_WONE);
}
float weights[4];
weights[0] = keyframeToUse.m_shapeWeights.GetXf();
weights[1] = keyframeToUse.m_shapeWeights.GetYf();
weights[2] = keyframeToUse.m_shapeWeights.GetZf();
weights[3] = keyframeToUse.m_shapeWeights.GetWf();
float denominator = 1.0f/(weights[0] + weights[1] + weights[2] + weights[3]);
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
finalShape.m_ControlPoints[i].heightValue = denominator*(weights[0]*sm_ShapePalette.m_Shapes[0].m_ControlPoints[i].heightValue +
weights[1]*sm_ShapePalette.m_Shapes[1].m_ControlPoints[i].heightValue +
weights[2]*sm_ShapePalette.m_Shapes[2].m_ControlPoints[i].heightValue +
weights[3]*sm_ShapePalette.m_Shapes[3].m_ControlPoints[i].heightValue);
finalShape.m_ControlPoints[i].visibilityScale = denominator*(weights[0]*sm_ShapePalette.m_Shapes[0].m_ControlPoints[i].visibilityScale +
weights[1]*sm_ShapePalette.m_Shapes[1].m_ControlPoints[i].visibilityScale +
weights[2]*sm_ShapePalette.m_Shapes[2].m_ControlPoints[i].visibilityScale +
weights[3]*sm_ShapePalette.m_Shapes[3].m_ControlPoints[i].visibilityScale);
}
// Calculate each control point from the final fog shape.
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS; i++)
{
// Compute the two shapes of the fog stretched over the height range provided by the time cycle.
float height = finalShape.GetHeightAtControlPoint(i, keyframeToUse.m_minHeight, keyframeToUse.m_maxHeight);
float visibility = finalShape.GetVisibilityAtControlPoint(i, keyframeToUse.m_log10MinVisibility);
// Make max. visibility fully transparent.
if(s_MaxVisibility - visibility < 5.0f)
visibility = 0.0f;
// Interpolate between the shapes to form a final height and visibility.
entry.height = height;
entry.visibility = visibility;
sortedByHeight[i] = entry;
}
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_SHADER_VARIABLES; i++)
shaderParams.shaderParams[i] = Vec4V(V_ZERO);
bool doFogging = false;
#if __BANK
doFogging = sm_DoLinearPiecewiseFog;
#endif // __BANK
if(doFogging)
{
int outIdx =0;
float densityAtViewer = 0.0f;
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_CONTOL_POINTS-1; i++)
{
// Does i and i + 1 form a valid range AND has fog defined (visibility = 0 means no fog).
if((sortedByHeight[i].height < sortedByHeight[i+1].height) && ((sortedByHeight[i].visibility != 0.0f) || (sortedByHeight[i+1].visibility != 0.0f)))
{
float d1 = (sortedByHeight[i].visibility != 0.0f) ? (1.0f/sortedByHeight[i].visibility) : 0.0f;
float d2 = (sortedByHeight[i+1].visibility != 0.0f) ? (1.0f/sortedByHeight[i+1].visibility) : 0.0f;
float h1 = sortedByHeight[i].height;
float h2 = sortedByHeight[i+1].height;
// Density is a linear function of height : Ax + B.
float A = (d2 - d1)/(h2 - h1);
float B = (h2*d1 - h1*d2)/(h2 - h1);
// Store heights and A, B.
shaderParams.shaderParams[1 + outIdx++] = Vec4V(sortedByHeight[i].height, sortedByHeight[i+1].height, A, B);
// Is the camera inside this band?
if((cameraHeight >= h1) && (cameraHeight <= h2))
{
// Sum the densities in case we have over-lapping bands.
densityAtViewer += A*cameraHeight + B;
}
}
}
// Fill out num of pieces, density at viewer, camera height and Debug on/off switch.
shaderParams.shaderParams[0] = Vec4V((float)outIdx, densityAtViewer, cameraHeight, 1.0f);
}
}
#if __BANK
void LinearPiecewiseFog::AddWidgets(bkBank &rBank)
{
rBank.PushGroup("Linear Piecewise Fog.");
rBank.AddToggle("On/Off", &sm_DoLinearPiecewiseFog);
rBank.AddToggle("Override time cycle.", &sm_UseOverrideHeightsAndVisibilites);
rBank.AddButton("Save shapes", LinearPiecewiseFog::SaveShapes);
rBank.AddButton("Load shapes", LinearPiecewiseFog::LoadShapes);
rBank.AddTitle("Simulate TC interpolation");
rBank.AddSlider("interpolation", &sm_Interp, 0.0f, 1.0f, 0.0010f);
rBank.AddTitle("Previous TC frame");
rBank.AddSlider("Previous TC min height", &sm_Keyframes[0].m_minHeight, 0.0f, LINEAR_PIECEWISE_FOG_MAX_HEIGHT, 0.5f);
rBank.AddSlider("Previous TC max height", &sm_Keyframes[0].m_maxHeight, 0.0f, LINEAR_PIECEWISE_FOG_MAX_HEIGHT, 0.5f);
rBank.AddSlider("Previous visibility (log10)", &sm_Keyframes[0].m_log10MinVisibility, LINEAR_PIECEWISE_FOG_WIDGET_MIN_VISIBILITY, LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY, 0.01f);
rBank.AddVector("shape weights", &sm_Keyframes[0].m_shapeWeights, 0.0f, 1.0f, 0.001f);
rBank.AddTitle("Next TC frame");
rBank.AddSlider("Next TC min height", &sm_Keyframes[1].m_minHeight, 0.0f, LINEAR_PIECEWISE_FOG_MAX_HEIGHT, 0.5f);
rBank.AddSlider("Next TC max height", &sm_Keyframes[1].m_maxHeight, 0.0f, LINEAR_PIECEWISE_FOG_MAX_HEIGHT, 0.5f);
rBank.AddSlider("Next visibility (log10)", &sm_Keyframes[1].m_log10MinVisibility, LINEAR_PIECEWISE_FOG_WIDGET_MIN_VISIBILITY, LINEAR_PIECEWISE_FOG_WIDGET_MAX_VISIBILITY, 0.01f);
rBank.AddVector("shape weights", &sm_Keyframes[1].m_shapeWeights, 0.0f, 1.0f, 0.001f);
/*----------------------------------------------------------------------------------------------------------*/
static const float sMin = 0.0f;
static const float sMax = LINEAR_PIECEWISE_FOG_WIDGET_MAX_HEIGHT;
static const float sDelta = 1.0f;
Vec3V vMinMaxDeltaX = Vec3V(sMin, sMax, sDelta);
Vec3V vMinMaxDeltaY = Vec3V(sMin, sMax, sDelta);
Vec4V vDefaultValue = Vec4V(V_ZERO);
static ptxKeyframeDefn s_WidgetProfile("Visualise", 0, PTXKEYFRAMETYPE_FLOAT4, vDefaultValue, vMinMaxDeltaX, vMinMaxDeltaY, "Red", "Green", "Blue", "Alpha", true);
sm_ProfileVisualise.SetDefn(&s_WidgetProfile);
ScalarV t = ScalarV(0.0f);
Vec4V v = Vec4V(1.0f, 1.0f, 1.0f, 0.0f);
sm_ProfileVisualise.AddEntry(t, v);
sm_ProfileVisualise.AddWidgets(rBank);
for(int i=0; i<LINEAR_PIECEWISE_FOG_NUM_FOG_SHAPES; i++)
sm_ShapePalette.m_Shapes[i].AddWidgets(rBank, i);
rBank.PopGroup();
}
void LinearPiecewiseFog::SaveShapes()
{
PARSER.SaveObject("common:/data/FogShapes", "xml", &LinearPiecewiseFog::sm_ShapePalette, parManager::XML);
}
#endif //__BANK
void LinearPiecewiseFog::LoadShapes()
{
PARSER.LoadObject("common:/data/FogShapes", "xml", LinearPiecewiseFog::sm_ShapePalette);
}
#endif // LINEAR_PIECEWISE_FOG