Skip to content

Commit

Permalink
port bulb changes
Browse files Browse the repository at this point in the history
  • Loading branch information
toxieainc committed May 11, 2024
1 parent be4bb15 commit ddcc89d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 80 deletions.
2 changes: 1 addition & 1 deletion LiveUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2047,7 +2047,7 @@ void LiveUI::UpdateMainUI()
view.Invert();
const vec3 up = view.GetOrthoNormalUp(), dir = view.GetOrthoNormalDir(), pos = view.GetOrthoNormalPos();
const vec3 camTarget = pos - dir * m_camDistance;
m_camDistance *= (float) pow(1.1, -ImGui::GetIO().MouseWheel);
m_camDistance *= powf(1.1f, -ImGui::GetIO().MouseWheel);
const vec3 newEye = camTarget + dir * m_camDistance;
m_camView.SetLookAtRH(newEye, camTarget, up);
}
Expand Down
112 changes: 54 additions & 58 deletions bulb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@
#include <cmath>
#include "bulb.h"


/*-------------------
/ Bulb characteristics and precomputed LUTs
/-------------------*/
typedef struct {
double surface; /* filament surface in m² */
double mass; /* filament mass in kg */
double r0; /* resistance at 293K */
double cool_down[3000]; /* precomputed cool down factor */
double heat_factor[3000]; /* precomputed heat factor = 1.0 / (R * Mass * Specific Heat) */
double r0; /* resistance at 293K, but premultiplied by (1/293)^1.215 */
double cool_down[BULB_T_MAX + 1]; /* precomputed cool down factor */
double heat_factor[BULB_T_MAX + 1]; /* precomputed heat factor = 1.0 / (R * Mass * Specific Heat) */
} bulb_tLampCharacteristics;

/*-------------------
/ local variables
/-------------------*/
static struct {
int initialized = FALSE;
double t_to_p[1500];
double t_to_p[BULB_T_MAX + 1 - 1500];
double p_to_t[512];
double specific_heat[3000];
double specific_heat[BULB_T_MAX + 1];
} locals;

// Bulb characteristics, estimated by fitting ratings (U,I,P) at a supposed steady state temperature of 2700K, then validating against high FPS video
static bulb_tLampCharacteristics bulbs[BULB_MAX] = {
{ 0.000001549619403110030, 0.000000203895434417560, 1.70020865326503000 }, // #44 Bulb characteristics (6.3V, 250mA, 1.26W, 5lm)
{ 0.000000929872857822516, 0.000000087040748856477, 2.83368108877505000 }, // #47 Bulb characteristics (6.3V, 150mA, 0.95W, 6.3lm)
{ 0.000001239604749734440, 0.000000140555683560514, 2.12526081658129000 }, // #86 Bulb characteristics (6.3V, 200mA, 1.58W, 11.3lm)
{ 0.000007413493071564570, 0.000001709095572478890, 1.51222718202281000 }, // #89 Bulb characteristics (13V, 580mA, 7.54W, 75.4lm)
{ 0.000001549619403110030, 0.000000203895434417560, 1.70020865326503000 * 0.00100636573473889440796764003454 }, // #44 Bulb characteristics (6.3V, 250mA, 1.26W, 5lm)
{ 0.000000929872857822516, 0.000000087040748856477, 2.83368108877505000 * 0.00100636573473889440796764003454 }, // #47 Bulb characteristics (6.3V, 150mA, 0.95W, 6.3lm)
{ 0.000001239604749734440, 0.000000140555683560514, 2.12526081658129000 * 0.00100636573473889440796764003454 }, // #86 Bulb characteristics (6.3V, 200mA, 1.58W, 11.3lm)
{ 0.000007413493071564570, 0.000001709095572478890, 1.51222718202281000 * 0.00100636573473889440796764003454 }, // #89 Bulb characteristics (13V, 580mA, 7.54W, 75.4lm)
};

// Linear RGB tint of a blackbody for temperatures ranging from 1500 to 3000K. The values are normalized for a relative luminance of 1.
// These values were evaluated using Blender's blackbody implementation, then normalized using the standard relative luminance formula (see https://en.wikipedia.org/wiki/Relative_luminance)
// We use a minimum channel value at 0.000001 to avoid divide by 0 in client application.
static float temperatureToTint[3 * 16] = {
static constexpr float temperatureToTint[3 * 16] = {
3.253114f, 0.431191f, 0.000001f,
3.074210f, 0.484372f, 0.000001f,
2.914679f, 0.531794f, 0.000001f,
Expand All @@ -60,18 +60,19 @@ static float temperatureToTint[3 * 16] = {
/-------------------------------*/
void bulb_init()
{
if (locals.initialized)
return;
locals.initialized = TRUE;
if (locals.initialized)
return;
locals.initialized = TRUE;

// Compute filament temperature to visible emission power LUT, normalized by visible emission power at T=2700K, according
// to the formula from "Luminous radiation from a black body and the mechanical equivalentt of light" by W.W.Coblentz and W.B.Emerson
for (int i=0; i<1500; i++)
for (int i=0; i <= BULB_T_MAX - 1500; i++)
{
double T = 1500.0 + i;
locals.t_to_p[i] = 1.247/pow(1.0+129.05/T, 204.0) + 0.0678/pow(1.0+78.85/T, 404.0) + 0.0489/pow(1.0+23.52/T, 1004.0) + 0.0406/pow(1.0+13.67/T, 2004.0);
}
double P2700 = locals.t_to_p[2700 - 1500];
for (int i=0; i<1500; i++)
for (int i=0; i <= BULB_T_MAX - 1500; i++)
{
locals.t_to_p[i] /= P2700;
}
Expand All @@ -85,7 +86,7 @@ void bulb_init()
t_pos++;
locals.p_to_t[i] = 1500 + t_pos;
}
for (int i=0; i<3000; i++)
for (int i=0; i <= BULB_T_MAX; i++)
{
double T = i;
// Compute Tungsten specific heat (energy to temperature transfer, depending on temperature) according to formula from "Heating-times of tungsten filament incandescent lamps" by Dulli Chandra Agrawal
Expand All @@ -96,37 +97,37 @@ void bulb_init()
// pow(T, 5.0796) is pow(T, 4) from Stefan/Boltzmann multiplied by tungsten overall (all wavelengths) emissivity which is 0.0000664*pow(T,1.0796)
double delta_energy = -0.00000005670374419 * bulbs[j].surface * 0.0000664 * pow(T, 5.0796);
bulbs[j].cool_down[i] = delta_energy / (locals.specific_heat[i] * bulbs[j].mass);
bulbs[j].heat_factor[i] = 1.0 / (bulbs[j].r0 * pow(T / 293.0, 1.215) * locals.specific_heat[i] * bulbs[j].mass);
bulbs[j].heat_factor[i] = 1.0 / (bulbs[j].r0 * pow(T, 1.215) * locals.specific_heat[i] * bulbs[j].mass);
}
}
}

/*-------------------------------
/ Returns relative visible emission power for a given filament temperature. Result is relative to the emission power at 2700K
/-------------------------------*/
double bulb_filament_temperature_to_emission(const double T)
float bulb_filament_temperature_to_emission(const float T)
{
if (T < 1500.0) return 0;
if (T >= 2999.0) return locals.t_to_p[1499];
return locals.t_to_p[(int)T - 1500];
if (T < 1500.0f) return 0.f;
if (T >= (float)BULB_T_MAX) return (float)locals.t_to_p[BULB_T_MAX - 1500];
return (float)locals.t_to_p[(int)T - 1500];
// Linear interpolation is not worth its cost
// int lower_T = (int) T, upper_T = (int) (T + 0.5);
// double alpha = T - lower_T;
// return (1.0 - alpha) * locals.t_to_p[lower_T - 1500] + alpha * locals.t_to_p[upper_T - 1500];
// int lower_T = (int) T, upper_T = lower_T+1;
// float alpha = T - (float)lower_T;
// return (1.0f - alpha) * (float)locals.t_to_p[lower_T - 1500] + alpha * (float)locals.t_to_p[upper_T - 1500];
}

/*-------------------------------
// Returns linear RGB tint for a given filament temperature. The values are normalized for a perceived luminance of 1.
/-------------------------------*/
void bulb_filament_temperature_to_tint(const double T, float* linear_RGB)
void bulb_filament_temperature_to_tint(const float T, float* linear_RGB)
{
if (T < 1500.0)
if (T < 1500.0f)
{
linear_RGB[0] = temperatureToTint[0];
linear_RGB[1] = temperatureToTint[1];
linear_RGB[2] = temperatureToTint[2];
}
else if (T >= 2999.0)
else if (T >= 2999.0f)
{
linear_RGB[0] = temperatureToTint[15 * 3 + 0];
linear_RGB[1] = temperatureToTint[15 * 3 + 1];
Expand All @@ -135,8 +136,8 @@ void bulb_filament_temperature_to_tint(const double T, float* linear_RGB)
else
{
// Linear interpolation between the precomputed values
float t_ref = (float) ((T - 1500.0f) / 100.0f);
int lower_T = (int)t_ref, upper_T = (int)(t_ref + 0.5);
float t_ref = (T - 1500.0f) * (float)(1./100.);
int lower_T = (int)t_ref, upper_T = lower_T+1;
float alpha = t_ref - (float)lower_T;
linear_RGB[0] = (1.0f - alpha) * temperatureToTint[lower_T * 3 + 0] + alpha * temperatureToTint[upper_T * 3 + 0];
linear_RGB[1] = (1.0f - alpha) * temperatureToTint[lower_T * 3 + 1] + alpha * temperatureToTint[upper_T * 3 + 1];
Expand All @@ -146,11 +147,11 @@ void bulb_filament_temperature_to_tint(const double T, float* linear_RGB)


/*-------------------------------
/ Returns filament temperature for a given visible emission power normalized for a an emission power of 1.0 at 2700K
/ Returns filament temperature for a given visible emission power normalized for an emission power of 1.0 at 2700K
/-------------------------------*/
double bulb_emission_to_filament_temperature(const double p)
{
int v = (int)(p * 255);
int v = (int)(p * 255.);
return v >= 512 ? locals.p_to_t[511] : locals.p_to_t[v];
}

Expand All @@ -165,11 +166,11 @@ double bulb_cool_down_factor(const int bulb, const double T)
/*-------------------------------
/ Compute cool down factor of a filament over a given period
/-------------------------------*/
double bulb_cool_down(const int bulb, double T, double duration)
double bulb_cool_down(const int bulb, double T, float duration)
{
while (duration > 0.0)
while (duration > 0.0f)
{
double dt = duration > 0.001 ? 0.001 : duration;
float dt = duration > 0.001f ? 0.001f : duration;
T += dt * bulbs[bulb].cool_down[(int) T];
if (T <= 294.0)
{
Expand All @@ -183,54 +184,49 @@ double bulb_cool_down(const int bulb, double T, double duration)
/*-------------------------------
/ Compute heat up factor of a filament under a given voltage (sum of heating and cooldown)
/-------------------------------*/
double bulb_heat_up_factor(const int bulb, const double T, const double U, const double serial_R)
float bulb_heat_up_factor(const int bulb, const float T, const float U, const float serial_R)
{
if (serial_R != 0.)
{
double R = bulbs[bulb].r0 * pow(T / 293.0, 1.215);
double U1 = U * R / (R + serial_R);
return U1 * U1 * bulbs[bulb].heat_factor[(int) T] + bulbs[bulb].cool_down[(int) T];
}
else
double U1 = U;
if (serial_R != 0.f)
{
return U * U * bulbs[bulb].heat_factor[(int) T] + bulbs[bulb].cool_down[(int) T];
double R = bulbs[bulb].r0 * powf(T, 1.215f);
U1 *= R / (R + serial_R);
}

return (float)(U1 * U1 * bulbs[bulb].heat_factor[(int)T] + bulbs[bulb].cool_down[(int)T]);
}

/*-------------------------------
/ Compute temperature of a filament under a given voltage over a given period (sum of heating and cooldown)
/-------------------------------*/
double bulb_heat_up(const int bulb, double T, double duration, const double U, const double serial_R)
double bulb_heat_up(const int bulb, double T, float duration, const float U, const float serial_R)
{
while (duration > 0.0)
while (duration > 0.0f)
{
T = T < 293.0 ? 293.0 : T > 2999.0 ? 2999.0 : T; // Keeps T within the range of the LUT (between room temperature and melt down point)
T = T < 293.0 ? 293.0 : T > BULB_T_MAX ? BULB_T_MAX : T; // Keeps T within the range of the LUT (between room temperature and melt down point)
double energy;
if (serial_R != 0.)
{
const double R = bulbs[bulb].r0 * pow(T / 293.0, 1.215);
const double U1 = U * R / (R + serial_R);
energy = bulbs[bulb].cool_down[(int) T] + U1 * U1 * bulbs[bulb].heat_factor[(int) T];
}
else
double U1 = U;
if (serial_R != 0.f)
{
energy = bulbs[bulb].cool_down[(int) T] + U * U * bulbs[bulb].heat_factor[(int) T];
const double R = bulbs[bulb].r0 * pow(T, 1.215);
U1 *= R / (R + serial_R);
}
energy = U1 * U1 * bulbs[bulb].heat_factor[(int)T] + bulbs[bulb].cool_down[(int)T];
if (-10 < energy && energy < 10)
{
// Stable state reached since electric heat (roughly) equals radiation cool down
return T;
}
double dt;
float dt;
if (energy > 1000e3)
{
// Initial current surge, 0.5ms integration period in order to account for the fast resistor rise that will quickly lower the current
dt = duration > 0.0005 ? 0.0005 : duration;
dt = duration > 0.0005f ? 0.0005f : duration;
}
else
{
// Ramping up, 1ms integration period
dt = duration > 0.001 ? 0.001 : duration;
dt = duration > 0.001f ? 0.001f : duration;
}
T += dt * energy;
duration -= dt;
Expand Down
12 changes: 7 additions & 5 deletions bulb.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
#define BULB_89 3
#define BULB_MAX 4

#define BULB_T_MAX 3400

void bulb_init();
double bulb_filament_temperature_to_emission(const double T);
void bulb_filament_temperature_to_tint(const double T, float* linear_RGB);
float bulb_filament_temperature_to_emission(const float T);
void bulb_filament_temperature_to_tint(const float T, float* linear_RGB);
double bulb_emission_to_filament_temperature(const double p);
double bulb_cool_down_factor(const int bulb, const double T);
double bulb_cool_down(const int bulb, double T, double duration);
double bulb_heat_up_factor(const int bulb, const double T, const double U, const double serial_R);
double bulb_heat_up(const int bulb, double T, const double duration, const double U, const double serial_R);
double bulb_cool_down(const int bulb, double T, float duration);
float bulb_heat_up_factor(const int bulb, const float T, const float U, const float serial_R);
double bulb_heat_up(const int bulb, double T, float duration, const float U, const float serial_R);

#endif /* INC_BULB */
30 changes: 14 additions & 16 deletions src/parts/light.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,18 +365,19 @@ void Light::UpdateAnimation(const float diff_time_msec)
break;
case FADER_INCANDESCENT:
{
const double fadeSpeed = m_d.m_intensity / (m_currentIntensity < targetIntensity ? m_d.m_fadeSpeedUp : m_d.m_fadeSpeedDown); // Fade speed in ms
const double remaining_time = diff_time_msec * (0.001 * 40.0 / fadeSpeed); // Apply a speed factor (a bulb with this characteristics reach full power between 30 and 40ms so we modulate around this)
assert(m_d.m_intensity * m_d.m_intensity_scale > FLT_MIN);
const float inv_fadeSpeed = (m_currentIntensity < targetIntensity ? m_d.m_fadeSpeedUp : m_d.m_fadeSpeedDown) / (m_d.m_intensity * m_d.m_intensity_scale); // 1.0 / (Fade speed in ms)
const float remaining_time = diff_time_msec * (float)(0.001 * 40.0) * inv_fadeSpeed; // Apply a speed factor (a bulb with this characteristics reaches full power between 30 and 40ms so we modulate around this)
if (lightState != 0.f)
{
const double U = 6.3 * pow(lightState, 0.25); // Modulating by Emission^0.25 is not fully correct (ignoring visible/non visible wavelengths) but an acceptable approximation
m_currentFilamentTemperature = bulb_heat_up(BULB_44, m_currentFilamentTemperature, remaining_time, U, 0.0);
const float U = 6.3f * sqrtf(sqrtf(lightState)); //=powf(lightState, 0.25f); // Modulating by Emission^0.25 is not fully correct (ignoring visible/non visible wavelengths) but an acceptable approximation
m_currentFilamentTemperature = bulb_heat_up(BULB_44, m_currentFilamentTemperature, remaining_time, U, 0.0f);
}
else
{
m_currentFilamentTemperature = bulb_cool_down(BULB_44, m_currentFilamentTemperature, remaining_time);
}
m_currentIntensity = (float)(bulb_filament_temperature_to_emission(m_currentFilamentTemperature) * m_d.m_intensity * m_d.m_intensity_scale);
m_currentIntensity = bulb_filament_temperature_to_emission((float)m_currentFilamentTemperature) * (m_d.m_intensity * m_d.m_intensity_scale);
}
break;
}
Expand Down Expand Up @@ -651,8 +652,8 @@ void Light::Render(const unsigned int renderMask)
if (m_d.m_fader == FADER_INCANDESCENT)
{
float tint2700[3], tint[3];
bulb_filament_temperature_to_tint(2700, tint2700);
bulb_filament_temperature_to_tint(m_currentFilamentTemperature, tint);
bulb_filament_temperature_to_tint(2700.f, tint2700);
bulb_filament_temperature_to_tint((float)m_currentFilamentTemperature, tint);
lightColor_intensity.x *= tint[0] / tint2700[0];
lightColor_intensity.y *= tint[1] / tint2700[1];
lightColor_intensity.z *= tint[2] / tint2700[2];
Expand All @@ -665,16 +666,14 @@ void Light::Render(const unsigned int renderMask)
{
m_rd->SetRenderStateDepthBias(0.0f);
m_rd->SetRenderState(RenderState::ZWRITEENABLE, RenderState::RS_TRUE);
m_rd->SetRenderState(RenderState::CULLMODE, RenderState::CULL_NONE);
}
else
{
m_rd->SetRenderStateDepthBias(-1.0f);
m_rd->SetRenderState(RenderState::ZWRITEENABLE, RenderState::RS_FALSE);
}

if (m_backglass)
m_rd->SetRenderState(RenderState::CULLMODE, RenderState::CULL_NONE);

Vertex2D centerHUD(m_d.m_vCenter.x, m_d.m_vCenter.y);
if (m_backglass)
{
Expand All @@ -695,7 +694,7 @@ void Light::Render(const unsigned int renderMask)
m_rd->lightShader->SetTechnique(SHADER_TECHNIQUE_bulb_light);

m_rd->EnableAlphaBlend(false, false, false);
//m_rd->SetRenderState(RenderState::SRCBLEND, RenderState::SRC_ALPHA); // add the lightcontribution
//m_rd->SetRenderState(RenderState::SRCBLEND, RenderState::SRC_ALPHA); // add the light contribution
m_rd->SetRenderState(RenderState::DESTBLEND, RenderState::INVSRC_COLOR); // but also modulate the light first with the underlying elements by (1+lightcontribution, e.g. a very crude approximation of real lighting)
m_rd->SetRenderState(RenderState::BLENDOP, RenderState::BLENDOP_REVSUBTRACT);

Expand Down Expand Up @@ -778,7 +777,7 @@ void Light::Render(const unsigned int renderMask)
m_lightmapMeshBuffer->m_vb->unlock();
}

Shader *shader = m_d.m_BulbLight ? m_rd->lightShader : m_rd->basicShader;
Shader *const shader = m_d.m_BulbLight ? m_rd->lightShader : m_rd->basicShader;
shader->SetLightData(center_range);
shader->SetLightColor2FalloffPower(lightColor2_falloff_power);
lightColor_intensity.w = m_currentIntensity;
Expand Down Expand Up @@ -1127,11 +1126,10 @@ STDMETHODIMP Light::InterfaceSupportsErrorInfo(REFIID riid)
&IID_ILight,
};

for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
for (size_t i = 0; i < std::size(arr); i++)
if (InlineIsEqualGUID(*arr[i], riid))
return S_OK;
}

return S_FALSE;
}

Expand Down Expand Up @@ -1667,7 +1665,7 @@ STDMETHODIMP Light::GetInPlayIntensity(float *pVal)

STDMETHODIMP Light::get_FilamentTemperature(float *pVal)
{
double T = bulb_emission_to_filament_temperature(m_currentIntensity / m_d.m_intensity * m_d.m_intensity_scale);
double T = bulb_emission_to_filament_temperature(m_currentIntensity / (m_d.m_intensity * m_d.m_intensity_scale));
*pVal = (float)T;

return S_OK;
Expand Down

0 comments on commit ddcc89d

Please sign in to comment.