Skip to content

Commit

Permalink
Added spotlight component.
Browse files Browse the repository at this point in the history
Changed the attenuation function for point and spotlights to a nicer one.
Fixed a crash when running without editor.
  • Loading branch information
fLindahl committed Nov 10, 2024
1 parent 89fba0c commit 6a308eb
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 53 deletions.
52 changes: 44 additions & 8 deletions code/addons/graphicsfeature/components/graphicsfeature.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,45 @@
"type": "uint",
"default": -1
},
"color": "colour",
"intensity": "float",
"range": "float",
"color": {
"type": "colour",
"default": [ 1.0, 1.0, 1.0, 1.0 ]
},
"intensity": {
"type": "float",
"default": 50.0 // candela
},
"range": {
"type": "float",
"default": 5.0
},
"castShadows": "bool"
},
"SpotLight": {
"graphicsEntityId": {
"type": "uint",
"default": -1
},
"color": {
"type": "colour",
"default": [ 1.0, 1.0, 1.0, 1.0 ]
},
"intensity": {
"type": "float",
"default": 50.0 // candela
},
"innerConeAngle": {
"type": "float",
"default": 15.0 // degrees
},
"outerConeAngle": {
"type": "float",
"default": 45.0 // degrees
},
"range": {
"type": "float",
"default": 8.0
},
"castShadows": "bool"
},
"Model": {
Expand All @@ -28,12 +64,12 @@
},
"raytracing": "bool",
"anim": {
"type": "resource",
"default": ""
"type": "resource",
"default": ""
},
"skeleton": {
"type": "resource",
"default": ""
"skeleton": {
"type": "resource",
"default": ""
}
},
"Camera": {
Expand Down
1 change: 1 addition & 0 deletions code/addons/graphicsfeature/graphicsfeatureunit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ void
GraphicsFeatureUnit::OnAttach()
{
Game::RegisterType<PointLight>({.decay = true, .OnInit = &GraphicsManager::InitPointLight });
Game::RegisterType<SpotLight>({.decay = true, .OnInit = &GraphicsManager::InitSpotLight });
Game::RegisterType<Model>({.decay = true, .OnInit = &GraphicsManager::InitModel });
Game::RegisterType<Camera>();
Scripting::RegisterDearguiModule();
Expand Down
2 changes: 1 addition & 1 deletion code/addons/graphicsfeature/managers/cameramanager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ CameraManager::RegisterView(Ptr<Graphics::View> const& view)

Graphics::CameraContext::RegisterEntity(data.gid);
Graphics::CameraContext::SetLODCamera(data.gid);
Graphics::CameraContext::SetupProjectionFov(data.gid, displayMode.GetAspectRatio(), Math::deg2rad(60.f), 0.1f, 10000.0f);
Graphics::CameraContext::SetupProjectionFov(data.gid, displayMode.GetAspectRatio(), Math::deg2rad(60.f), 0.01f, 1000.0f);
Visibility::ObserverContext::RegisterEntity(data.gid);
Visibility::ObserverContext::Setup(data.gid, Visibility::VisibilityEntityType::Camera);
view->SetCamera(data.gid);
Expand Down
126 changes: 101 additions & 25 deletions code/addons/graphicsfeature/managers/graphicsmanager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ DeregisterModelEntity(Model const* model)
Graphics::DestroyEntity(model->graphicsEntityId);
}

//------------------------------------------------------------------------------
/**
*/
void
DeregisterLight(Graphics::GraphicsEntityId gfxId)
{
if (gfxId == Graphics::InvalidGraphicsEntityId)
return;

if (Lighting::LightContext::IsEntityRegistered(gfxId))
{
Lighting::LightContext::DeregisterEntity(gfxId);
}
Graphics::DestroyEntity(gfxId);
}

//------------------------------------------------------------------------------
/**
*/
Expand All @@ -122,26 +138,28 @@ GraphicsManager::OnDecay()
{
Game::World* world = Game::GetWorld(WORLD_DEFAULT);

Game::ComponentDecayBuffer const decayBuffer = world->GetDecayBuffer(Game::GetComponentId<Model>());
Model* data = (Model*)decayBuffer.buffer;
for (int i = 0; i < decayBuffer.size; i++)
// Decay models
Game::ComponentDecayBuffer const modelDecayBuffer = world->GetDecayBuffer(Game::GetComponentId<Model>());
Model* data = (Model*)modelDecayBuffer.buffer;
for (int i = 0; i < modelDecayBuffer.size; i++)
{
DeregisterModelEntity(data + i);
}
Game::ComponentDecayBuffer const lightDecayBuffer = world->GetDecayBuffer(Game::GetComponentId<PointLight>());
PointLight* lightData = (PointLight*)lightDecayBuffer.buffer;
for (int i = 0; i < lightDecayBuffer.size; i++)

Game::ComponentDecayBuffer const pointLightDecayBuffer = world->GetDecayBuffer(Game::GetComponentId<PointLight>());
PointLight* pointLightData = (PointLight*)pointLightDecayBuffer.buffer;
for (int i = 0; i < pointLightDecayBuffer.size; i++)
{
PointLight* light = lightData + i;
if ((Graphics::GraphicsEntityId)light->graphicsEntityId == Graphics::InvalidGraphicsEntityId)
{
continue;
}
if (Lighting::LightContext::IsEntityRegistered(light->graphicsEntityId))
{
Lighting::LightContext::DeregisterEntity(light->graphicsEntityId);
}
Graphics::DestroyEntity(light->graphicsEntityId);
PointLight* light = pointLightData + i;
DeregisterLight(light->graphicsEntityId);
}

Game::ComponentDecayBuffer const spotLightDecayBuffer = world->GetDecayBuffer(Game::GetComponentId<SpotLight>());
SpotLight* spotLightData = (SpotLight*)spotLightDecayBuffer.buffer;
for (int i = 0; i < spotLightDecayBuffer.size; i++)
{
SpotLight* light = spotLightData + i;
DeregisterLight(light->graphicsEntityId);
}
}

Expand Down Expand Up @@ -207,10 +225,32 @@ GraphicsManager::InitPointLight(Game::World* world, Game::Entity entity, PointLi
// TODO: This is not finished, and needs revisiting
Game::Position pos = world->GetComponent<Game::Position>(entity);
Lighting::LightContext::RegisterEntity(light->graphicsEntityId);
// TODO: Cookie projection support
Lighting::LightContext::SetupPointLight(light->graphicsEntityId, light->color.vec, light->intensity, light->range, light->castShadows);
Lighting::LightContext::SetPosition(light->graphicsEntityId, pos);
}

//------------------------------------------------------------------------------
/**
*/
void
GraphicsManager::InitSpotLight(Game::World* world, Game::Entity entity, SpotLight* light)
{
light->graphicsEntityId = Graphics::CreateEntity().id;

// TODO: This is not finished, and needs revisiting
Game::Position pos = world->GetComponent<Game::Position>(entity);
Game::Orientation rot = world->GetComponent<Game::Orientation>(entity);

Lighting::LightContext::RegisterEntity(light->graphicsEntityId);
// TODO: Cookie projection support
Lighting::LightContext::SetupSpotLight(
light->graphicsEntityId, light->color.vec, light->intensity, Math::deg2rad(light->innerConeAngle), Math::deg2rad(light->outerConeAngle), light->range, light->castShadows
);
Lighting::LightContext::SetPosition(light->graphicsEntityId, pos);
Lighting::LightContext::SetRotation(light->graphicsEntityId, rot);
}

//------------------------------------------------------------------------------
/**
*/
Expand All @@ -235,21 +275,57 @@ GraphicsManager::OnCleanup(Game::World* world)
{
n_assert(GraphicsManager::HasInstance());

Game::Filter filter = Game::FilterBuilder().Including<Model>().Build();
Game::Dataset data = world->Query(filter);
{ // Model cleanup
Game::Filter filter = Game::FilterBuilder().Including<Model>().Build();
Game::Dataset data = world->Query(filter);

for (int v = 0; v < data.numViews; v++)
{
Game::Dataset::View const& view = data.views[v];
Model const* const modelData = (Model*)view.buffers[0];
for (int v = 0; v < data.numViews; v++)
{
Game::Dataset::View const& view = data.views[v];
Model const* const componentData = (Model*)view.buffers[0];

for (IndexT i = 0; i < view.numInstances; ++i)
{
DeregisterModelEntity(componentData + i);
}
}

for (IndexT i = 0; i < view.numInstances; ++i)
Game::DestroyFilter(filter);
}
{ // Pointlight cleanup
Game::Filter filter = Game::FilterBuilder().Including<PointLight>().Build();
Game::Dataset data = world->Query(filter);

for (int v = 0; v < data.numViews; v++)
{
DeregisterModelEntity(modelData + i);
Game::Dataset::View const& view = data.views[v];
PointLight const* const componentData = (PointLight*)view.buffers[0];

for (IndexT i = 0; i < view.numInstances; ++i)
{
DeregisterLight((componentData + i)->graphicsEntityId);
}
}

Game::DestroyFilter(filter);
}
{ // Spotlight cleanup
Game::Filter filter = Game::FilterBuilder().Including<SpotLight>().Build();
Game::Dataset data = world->Query(filter);

Game::DestroyFilter(filter);
for (int v = 0; v < data.numViews; v++)
{
Game::Dataset::View const& view = data.views[v];
SpotLight const* const componentData = (SpotLight*)view.buffers[0];

for (IndexT i = 0; i < view.numInstances; ++i)
{
DeregisterLight((componentData + i)->graphicsEntityId);
}
}

Game::DestroyFilter(filter);
}
}

} // namespace GraphicsFeature
Expand Down
7 changes: 3 additions & 4 deletions code/addons/graphicsfeature/managers/graphicsmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,19 @@ class GraphicsManager

/// called automatically when a model needs to be initialized
static void InitModel(Game::World*, Game::Entity, Model*);
/// called automatically when a model needs to be initialized
/// called automatically when a point light needs to be initialized
static void InitPointLight(Game::World* world, Game::Entity entity, PointLight* light);
/// called automatically when a spot light needs to be initialized
static void InitSpotLight(Game::World* world, Game::Entity entity, SpotLight* light);

private:
/// constructor
GraphicsManager();
/// destructor
~GraphicsManager();


void InitCreatePointLightProcessor();
void InitUpdateModelTransformProcessor();
static void OnDecay();

static void OnCleanup(Game::World* world);
};

Expand Down
18 changes: 12 additions & 6 deletions code/application/game/frameevent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,12 @@ FrameEvent::Batch::ExecuteAsync(World* world)
Processor* processor = this->processors[i];

#ifdef WITH_NEBULA_EDITOR
Game::EditorState* editor = Game::EditorState::Instance();
if (editor && editor->isRunning && !editor->isPlaying && !processor->runInEditor)
continue;
if (Game::EditorState::HasInstance())
{
Game::EditorState* editor = Game::EditorState::Instance();
if (editor->isRunning && !editor->isPlaying && !processor->runInEditor)
continue;
}
#endif
datasets[i] = world->Query(processor->filter, processor->cache);
numJobs += datasets[i].numViews;
Expand Down Expand Up @@ -319,9 +322,12 @@ FrameEvent::Batch::ExecuteSequential(World* world)
Processor* processor = this->processors[i];

#ifdef WITH_NEBULA_EDITOR
Game::EditorState* editor = Game::EditorState::Instance();
if (editor && editor->isRunning && !editor->isPlaying && !processor->runInEditor)
continue;
if (Game::EditorState::HasInstance())
{
Game::EditorState* editor = Game::EditorState::Instance();
if (editor->isRunning && !editor->isPlaying && !processor->runInEditor)
continue;
}
#endif

Dataset data = world->Query(processor->filter, processor->cache);
Expand Down
13 changes: 6 additions & 7 deletions syswork/shaders/vk/lib/lighting_functions.fxh
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ const uint AREA_LIGHT_TWOSIDED = 0x20;

//------------------------------------------------------------------------------
/**
GLTF recommended inverse square falloff
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual/README.md#range-property
*/
float
InvSquareFalloff(float radius, vec3 lightDir)
InvSquareFalloff(float maxRange, float currentDistance, vec3 lightDir)
{
float dist2 = dot(lightDir, lightDir);
float factor = dist2 / sqr(radius);
float falloff = saturate(1.0f - sqr(factor));
return sqr(falloff) / max(dist2, 0.0001f);
return max(min(1.0f - pow(currentDistance / maxRange, 4.0f), 1.0f), 0.0f) / pow(currentDistance, 2);
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -186,7 +185,7 @@ CalculateSpotLight(
vec3 lightDir = (light.position - pos);
float lightDirLen = length(lightDir);

float att = InvSquareFalloff(light.range, lightDir);
float att = InvSquareFalloff(light.range, lightDirLen, lightDir);

float oneDivLightDirLen = 1.0f / lightDirLen;
lightDir = lightDir * oneDivLightDirLen;
Expand Down Expand Up @@ -602,7 +601,7 @@ CalculateSpotLightAmbientTransmission(
{
vec3 lightDir = (light.position.xyz - viewPos);
float lightDirLen = length(lightDir);
float att = InvSquareFalloff(light.range, lightDir);
float att = InvSquareFalloff(light.range, lightDirLen, lightDir);
lightDir = lightDir / lightDirLen;

float theta = dot(light.forward.xyz, lightDir);
Expand Down
5 changes: 3 additions & 2 deletions syswork/shaders/vk/volumefog.fx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ LocalLightsFog(
PointLight li = PointLights[lidx];

vec3 lightDir = (li.position.xyz - viewPos);
float att = InvSquareFalloff(li.range, lightDir);
float lightDirLength = length(lightDir);
float att = InvSquareFalloff(li.range, lightDirLength, lightDir);
light += li.color * att;
}
}
Expand All @@ -165,7 +166,7 @@ LocalLightsFog(
// calculate attentuation and angle falloff, and just multiply by color
vec3 lightDir = (li.position.xyz - viewPos);
float lightDirLen = length(lightDir);
float att = InvSquareFalloff(li.range, lightDir);
float att = InvSquareFalloff(li.range, lightDirLen, lightDir);
lightDir = lightDir * (1 / lightDirLen);

float theta = dot(li.forward.xyz, lightDir);
Expand Down
19 changes: 19 additions & 0 deletions toolkit/editor/editorfeature/editorfeatureunit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@ EditorFeatureUnit::OnActivate()
)
.Build();

Game::ProcessorBuilder(world, "EditorGameManager.UpdateSpotLightTransform"_atm)
.On("OnEndFrame")
.OnlyModified()
.RunInEditor()
.Func(
[](Game::World* world,
Game::Position const& pos,
Game::Orientation const& rot,
GraphicsFeature::SpotLight const& light)
{
if (Lighting::LightContext::IsEntityRegistered(light.graphicsEntityId))
{
Lighting::LightContext::SetPosition(light.graphicsEntityId, pos);
Lighting::LightContext::SetRotation(light.graphicsEntityId, rot);
}
}
)
.Build();

//if (!Editor::ConnectToBackend(...))
// Editor::SpawnLocalBackend();
}
Expand Down

0 comments on commit 6a308eb

Please sign in to comment.