Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model Tag improvements, fix GLTF import #5606

Merged
merged 7 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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