Skip to content

Commit

Permalink
Merge pull request #5606 from Web-eWorks/model-tags
Browse files Browse the repository at this point in the history
Model Tag improvements, fix GLTF import
  • Loading branch information
Webster Sheets authored Aug 25, 2023
2 parents 1599de9 + 6728438 commit 1543657
Show file tree
Hide file tree
Showing 31 changed files with 489 additions and 275 deletions.
137 changes: 135 additions & 2 deletions src/Camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ void Camera::Update()
continue;
}

Body *parentBody = f->GetBody();
if (parentBody && parentBody->GetType() == ObjectType::PLANET) {
auto *planet = static_cast<Planet *>(parentBody);

double atmo_rad_sqr = planet->GetAtmosphereRadius() * planet->GetAtmosphereRadius();
if (b->IsType(ObjectType::MODELBODY) && b->GetPosition().LengthSqr() <= atmo_rad_sqr)
attrs.calcAtmosphereLighting = true;
}

m_sortedBodies.push_back(attrs);
}

Expand Down Expand Up @@ -287,6 +296,13 @@ void Camera::Draw(const Body *excludeBody)
m_renderer->SetLights(rendererLights.size(), &rendererLights[0]);
}

std::vector<float> oldIntensities;
std::vector<float> lightIntensities;
for (size_t i = 0; i < m_lightSources.size(); i++) {
lightIntensities.push_back(1.0);
oldIntensities.push_back(m_renderer->GetLight(i).GetIntensity());
}

Graphics::VertexArray billboards(Graphics::ATTRIB_POSITION | Graphics::ATTRIB_NORMAL);

for (std::list<BodyAttrs>::iterator i = m_sortedBodies.begin(); i != m_sortedBodies.end(); ++i) {
Expand All @@ -299,10 +315,28 @@ void Camera::Draw(const Body *excludeBody)
// draw something!
if (attrs->billboard) {
billboards.Add(attrs->billboardPos, vector3f(0.f, 0.f, attrs->billboardSize));
} else
attrs->body->Render(m_renderer, this, attrs->viewCoords, attrs->viewTransform);
continue;
}

if (attrs->calcAtmosphereLighting) {
double ambient, direct;
CalcLighting(attrs->body, ambient, direct);

for (size_t i = 0; i < m_lightSources.size(); i++)
lightIntensities[i] = direct * ShadowedIntensity(i, attrs->body);

// Setup dynamic lighting parameters
m_renderer->SetAmbientColor(Color(ambient * 255, ambient * 255, ambient * 255));
m_renderer->SetLightIntensity(m_lightSources.size(), lightIntensities.data());
}

attrs->body->Render(m_renderer, this, attrs->viewCoords, attrs->viewTransform);
}

// Restore default ambient color and direct light intensities
m_renderer->SetAmbientColor(Color(255, 255, 255));
m_renderer->SetLightIntensity(m_lightSources.size(), oldIntensities.data());

if (!billboards.IsEmpty()) {
Graphics::Renderer::MatrixTicket mt(m_renderer, matrix4x4f::Identity());
m_renderer->DrawBuffer(&billboards, m_billboardMaterial.get());
Expand All @@ -311,6 +345,105 @@ void Camera::Draw(const Body *excludeBody)
SfxManager::RenderAll(m_renderer, rootFrameId, camFrameId);
}

// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location
// 1. Calculates the amount of direct illumination available taking into account
// * multiple suns
// * sun positions relative to up direction i.e. light is dimmed as suns set
// * Thickness of the atmosphere overhead i.e. as atmospheres get thicker light starts dimming earlier as sun sets, without atmosphere the light switches off at point of sunset
// 2. Calculates the split between ambient and directly lit portions taking into account
// * Atmosphere density (optical thickness) of the sky dome overhead
// as optical thickness increases the fraction of ambient light increases
// this takes altitude into account automatically
// * As suns set the split is biased towards ambient
void Camera::CalcLighting(const Body *b, double &ambient, double &direct) const
{
const double minAmbient = 0.05;
ambient = minAmbient;
direct = 1.0;

Body *astro = Frame::GetFrame(b->GetFrame())->GetBody();
Planet *planet = static_cast<Planet *>(astro);
FrameId rotFrame = planet->GetFrame();

// position relative to the rotating frame of the planet
vector3d upDir = b->GetInterpPositionRelTo(rotFrame);
const double planetRadius = planet->GetSystemBody()->GetRadius();
const double dist = std::max(planetRadius, upDir.Length());
upDir = upDir.Normalized();

double pressure, density;
planet->GetAtmosphericState(dist, &pressure, &density);
double surfaceDensity;
Color cl;
planet->GetSystemBody()->GetAtmosphereFlavor(&cl, &surfaceDensity);

// approximate optical thickness fraction as fraction of density remaining relative to earths
double opticalThicknessFraction = density / EARTH_ATMOSPHERE_SURFACE_DENSITY;

// tweak optical thickness curve - lower exponent ==> higher altitude before ambient level drops
// Commenting this out, since it leads to a sharp transition at
// atmosphereRadius, where density is suddenly 0
//opticalThicknessFraction = pow(std::max(0.00001,opticalThicknessFraction),0.15); //max needed to avoid 0^power

if (opticalThicknessFraction < 0.0001)
return;

//step through all the lights and calculate contributions taking into account sun position
double light = 0.0;
double light_clamped = 0.0;

const std::vector<Camera::LightSource> &lightSources = m_lightSources;
for (const LightSource &source : m_lightSources) {
double sunAngle;
// calculate the extent the sun is towards zenith
const Body *lightBody = source.GetBody();
if (lightBody) {
// relative to the rotating frame of the planet
const vector3d lightDir = (lightBody->GetInterpPositionRelTo(rotFrame).Normalized());
sunAngle = lightDir.Dot(upDir);
} else {
// light is the default light for systems without lights
sunAngle = 1.0;
}

const double critAngle = -sqrt(dist * dist - planetRadius * planetRadius) / dist;

//0 to 1 as sunangle goes from critAngle to 1.0
double sunAngle2 = (Clamp(sunAngle, critAngle, 1.0) - critAngle) / (1.0 - critAngle);

// angle at which light begins to fade on Earth
const double surfaceStartAngle = 0.3;
// angle at which sun set completes, which should be after sun has dipped below the horizon on Earth
const double surfaceEndAngle = -0.18;

const double start = std::min((surfaceStartAngle * opticalThicknessFraction), 1.0);
const double end = std::max((surfaceEndAngle * opticalThicknessFraction), -0.2);

sunAngle = (Clamp(sunAngle - critAngle, end, start) - end) / (start - end);

light += sunAngle;
light_clamped += sunAngle2;
}

light_clamped /= lightSources.size();
light /= lightSources.size();

// brightness depends on optical depth and intensity of light from all the stars
direct = 1.0 - Clamp((1.0 - light), 0.0, 1.0) * Clamp(opticalThicknessFraction, 0.0, 1.0);

// ambient light fraction
// alter ratio between directly and ambiently lit portions towards ambiently lit as sun sets
const double fraction = (0.2 + 0.8 * (1.0 - light_clamped)) * Clamp(opticalThicknessFraction, 0.0, 1.0);

// fraction of light left over to be lit directly
direct = (1.0 - fraction) * direct;

// scale ambient by amount of light
ambient = fraction * (Clamp((light), 0.0, 1.0)) * 0.25;

ambient = std::max(minAmbient, ambient);
}

void Camera::CalcShadows(const int lightNum, const Body *b, std::vector<Shadow> &shadowsOut) const
{
// Set up data for eclipses. All bodies are assumed to be spheres.
Expand Down
4 changes: 4 additions & 0 deletions src/Camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class Camera {
bool operator<(const Shadow &other) const { return srad / lrad < other.srad / other.lrad; }
};

void CalcLighting(const Body *b, double &ambient, double &direct) const;
void CalcShadows(const int lightNum, const Body *b, std::vector<Shadow> &shadowsOut) const;
float ShadowedIntensity(const int lightNum, const Body *b) const;
void PrincipalShadows(const Body *b, const int n, std::vector<Shadow> &shadowsOut) const;
Expand Down Expand Up @@ -137,6 +138,9 @@ class Camera {
// body flags. DRAW_LAST is the interesting one
Uint32 bodyFlags;

// if true, calculate atmosphere-attenuated light intensity for the body
bool calcAtmosphereLighting;

// if true, draw object as billboard of billboardSize at billboardPos
bool billboard;
vector3f billboardPos;
Expand Down
14 changes: 7 additions & 7 deletions src/FixedGuns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "Quaternion.h"
#include "StringF.h"
#include "libs.h"
#include "scenegraph/MatrixTransform.h"
#include "scenegraph/Tag.h"
#include "vector3.h"

REGISTER_COMPONENT_TYPE(FixedGuns) {
Expand Down Expand Up @@ -106,21 +106,21 @@ void FixedGuns::InitGuns(SceneGraph::Model *m)
// 32 is a crazy number...
for (int gun = 0; gun < 32; gun++) {
const std::string tag = stringf("tag_gunmount_%0{d}_multi_%1{d}", num, gun); //"gunmount_0_multi_0";
const SceneGraph::MatrixTransform *mt = m->FindTagByName(tag);
if (mt) {
const SceneGraph::Tag *tagNode = m->FindTagByName(tag);
if (tagNode) {
++found;
const matrix4x4f &trans = mt->GetTransform();
const matrix4x4f &trans = tagNode->GetGlobalTransform();
GunData::GunLoc loc;
loc.pos = vector3d(trans.GetTranslate());
loc.dir = vector3d(trans.GetOrient().VectorZ());
m_gun[num].locs.push_back(loc);
} else if (found == 0) {
// look for legacy "tag_gunmount_0" or "tag_gunmount_1" tags
const std::string tag = stringf("tag_gunmount_%0{d}", num); //"gunmount_0";
const SceneGraph::MatrixTransform *mt = m->FindTagByName(tag);
if (mt) {
const SceneGraph::Tag *tagNode = m->FindTagByName(tag);
if (tagNode) {
++found;
const matrix4x4f &trans = mt->GetTransform();
const matrix4x4f &trans = tagNode->GetGlobalTransform();
GunData::GunLoc loc;
loc.pos = vector3d(trans.GetTranslate());
loc.dir = vector3d(trans.GetOrient().VectorZ());
Expand Down
151 changes: 2 additions & 149 deletions src/ModelBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,159 +288,12 @@ void ModelBody::MoveGeoms(const matrix4x4d &m, const vector3d &p)
}
}

// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location
// 1. Calculates the amount of direct illumination available taking into account
// * multiple suns
// * sun positions relative to up direction i.e. light is dimmed as suns set
// * Thickness of the atmosphere overhead i.e. as atmospheres get thicker light starts dimming earlier as sun sets, without atmosphere the light switches off at point of sunset
// 2. Calculates the split between ambient and directly lit portions taking into account
// * Atmosphere density (optical thickness) of the sky dome overhead
// as optical thickness increases the fraction of ambient light increases
// this takes altitude into account automatically
// * As suns set the split is biased towards ambient
void ModelBody::CalcLighting(double &ambient, double &direct, const Camera *camera)
void ModelBody::RenderModel(Graphics::Renderer *r, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform)
{
const double minAmbient = 0.05;
ambient = minAmbient;
direct = 1.0;
Body *astro = Frame::GetFrame(GetFrame())->GetBody();
if (!(astro && astro->IsType(ObjectType::PLANET)))
return;

Planet *planet = static_cast<Planet *>(astro);

// position relative to the rotating frame of the planet
vector3d upDir = GetInterpPositionRelTo(planet->GetFrame());
const double planetRadius = planet->GetSystemBody()->GetRadius();
const double dist = std::max(planetRadius, upDir.Length());
upDir = upDir.Normalized();

double pressure, density;
planet->GetAtmosphericState(dist, &pressure, &density);
double surfaceDensity;
Color cl;
planet->GetSystemBody()->GetAtmosphereFlavor(&cl, &surfaceDensity);

// approximate optical thickness fraction as fraction of density remaining relative to earths
double opticalThicknessFraction = density / EARTH_ATMOSPHERE_SURFACE_DENSITY;

// tweak optical thickness curve - lower exponent ==> higher altitude before ambient level drops
// Commenting this out, since it leads to a sharp transition at
// atmosphereRadius, where density is suddenly 0
//opticalThicknessFraction = pow(std::max(0.00001,opticalThicknessFraction),0.15); //max needed to avoid 0^power

if (opticalThicknessFraction < 0.0001)
return;

//step through all the lights and calculate contributions taking into account sun position
double light = 0.0;
double light_clamped = 0.0;

const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources();
for (std::vector<Camera::LightSource>::const_iterator l = lightSources.begin();
l != lightSources.end(); ++l) {
double sunAngle;
// calculate the extent the sun is towards zenith
if (l->GetBody()) {
// relative to the rotating frame of the planet
const vector3d lightDir = (l->GetBody()->GetInterpPositionRelTo(planet->GetFrame()).Normalized());
sunAngle = lightDir.Dot(upDir);
} else {
// light is the default light for systems without lights
sunAngle = 1.0;
}

const double critAngle = -sqrt(dist * dist - planetRadius * planetRadius) / dist;

//0 to 1 as sunangle goes from critAngle to 1.0
double sunAngle2 = (Clamp(sunAngle, critAngle, 1.0) - critAngle) / (1.0 - critAngle);

// angle at which light begins to fade on Earth
const double surfaceStartAngle = 0.3;
// angle at which sun set completes, which should be after sun has dipped below the horizon on Earth
const double surfaceEndAngle = -0.18;

const double start = std::min((surfaceStartAngle * opticalThicknessFraction), 1.0);
const double end = std::max((surfaceEndAngle * opticalThicknessFraction), -0.2);

sunAngle = (Clamp(sunAngle - critAngle, end, start) - end) / (start - end);

light += sunAngle;
light_clamped += sunAngle2;
}

light_clamped /= lightSources.size();
light /= lightSources.size();

// brightness depends on optical depth and intensity of light from all the stars
direct = 1.0 - Clamp((1.0 - light), 0.0, 1.0) * Clamp(opticalThicknessFraction, 0.0, 1.0);

// ambient light fraction
// alter ratio between directly and ambiently lit portions towards ambiently lit as sun sets
const double fraction = (0.2 + 0.8 * (1.0 - light_clamped)) * Clamp(opticalThicknessFraction, 0.0, 1.0);

// fraction of light left over to be lit directly
direct = (1.0 - fraction) * direct;

// scale ambient by amount of light
ambient = fraction * (Clamp((light), 0.0, 1.0)) * 0.25;

ambient = std::max(minAmbient, ambient);
}

// setLighting: set renderer lights according to current position and sun
// positions. Original lighting is passed back in oldLights, oldAmbient, and
// should be reset after rendering with ModelBody::ResetLighting.
void ModelBody::SetLighting(Graphics::Renderer *r, const Camera *camera, std::vector<float> &oldIntensity, Color &oldAmbient)
{
double ambient, direct;
CalcLighting(ambient, direct, camera);

const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources();
oldIntensity.resize(lightSources.size(), 0.0f);
std::vector<float> lightIntensity(lightSources.size(), 0.0f);
for (size_t i = 0; i < lightSources.size(); i++) {
oldIntensity[i] = r->GetLight(i).GetIntensity();
lightIntensity[i] = direct * camera->ShadowedIntensity(i, this);
}

oldAmbient = r->GetAmbientColor();
r->SetAmbientColor(Color(ambient * 255, ambient * 255, ambient * 255));
r->SetLightIntensity(lightSources.size(), lightIntensity.data());
}

void ModelBody::ResetLighting(Graphics::Renderer *r, const std::vector<float> &oldIntensity, const Color &oldAmbient)
{
// restore old lights
if (!oldIntensity.empty())
r->SetLightIntensity(oldIntensity.size(), oldIntensity.data());
r->SetAmbientColor(oldAmbient);
}

void ModelBody::RenderModel(Graphics::Renderer *r, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform, const bool setLighting)
{
std::vector<float> oldIntensity;
Color oldAmbient;
if (setLighting)
SetLighting(r, camera, oldIntensity, oldAmbient);

matrix4x4d m2 = GetInterpOrient();
m2.SetTranslate(GetInterpPosition());
matrix4x4d t = viewTransform * m2;

//double to float matrix
matrix4x4f trans;
for (int i = 0; i < 12; i++)
trans[i] = float(t[i]);
trans[12] = viewCoords.x;
trans[13] = viewCoords.y;
trans[14] = viewCoords.z;
trans[15] = 1.0f;

m_model->Render(trans);

if (setLighting)
ResetLighting(r, oldIntensity, oldAmbient);
m_model->Render(matrix4x4f(viewTransform * m2));
}

void ModelBody::TimeStepUpdate(const float timestep)
Expand Down
Loading

0 comments on commit 1543657

Please sign in to comment.