Skip to content
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
44 changes: 8 additions & 36 deletions asset/shader/damaged_helmet.frag
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ layout(std140, binding = 2) uniform MaterialUBO
float _padding[3]; // alignment
} u_material;

// SSBO for all lighting
layout(std430, binding = 0) buffer LightingSSBO
// UBO for all lighting (fixed maximum size)
layout(std140, binding = 3) uniform LightingUBO
{
PunctualLight lights[]; // lights[0] is main directional light
int lightCount;
int _padding[3];
PunctualLight lights[32]; // Maximum 32 lights
} u_lighting;

void main()
Expand All @@ -58,41 +60,11 @@ void main()
vec3 Lo = vec3(0.0);

// Calculate lighting contribution from all lights
// lights[0] is always the main directional light
for (int i = 0; i < u_lighting.lights.length(); ++i)
for (int i = 0; i < u_lighting.lightCount; ++i)
{
PunctualLight light = u_lighting.lights[i];
vec3 L;
vec3 radiance;

if (light.type == LIGHT_TYPE_DIRECTIONAL)
{
// Directional light
L = normalize(-light.direction);
radiance = light.color * light.intensity;
}
else if (light.type == LIGHT_TYPE_POINT)
{
// Point light
L = normalize(light.position - v_position);
float distance = length(light.position - v_position);
float attenuation = calculateAttenuation(distance, light.range);
radiance = light.color * light.intensity * attenuation;
}
else if (light.type == LIGHT_TYPE_SPOT)
{
// Spot light
L = normalize(light.position - v_position);
float distance = length(light.position - v_position);
float attenuation = calculateAttenuation(distance, light.range);
float spotEffect = calculateSpotEffect(L, light.direction, light.innerConeAngle, light.outerConeAngle);
radiance = light.color * light.intensity * attenuation * spotEffect;
}
else
{
continue; // Skip unknown light types
}

vec3 L = calculateLightDirection(light, v_position);
vec3 radiance = calculateRadiance(light, v_position, L);
Lo += calculatePBRLighting(N, V, L, baseColor.rgb, metallic, roughness, radiance);
}

Expand Down
41 changes: 41 additions & 0 deletions asset/shader/punctual.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,44 @@ float calculateSpotEffect(vec3 L, vec3 spotDirection, float innerConeAngle, floa

return spotEffect;
}


// Calculate light direction vector L for a given light
vec3 calculateLightDirection(PunctualLight light, vec3 fragPosition)
{
if (light.type == LIGHT_TYPE_DIRECTIONAL)
{
return normalize(-light.direction);
}
else // LIGHT_TYPE_POINT or LIGHT_TYPE_SPOT
{
return normalize(light.position - fragPosition);
}
}

// Calculate incoming radiance for a given light
vec3 calculateRadiance(PunctualLight light, vec3 fragPosition, vec3 L)
{
vec3 radiance = light.color * light.intensity;

if (light.type == LIGHT_TYPE_DIRECTIONAL)
{
// No attenuation for directional lights
return radiance;
}
else if (light.type == LIGHT_TYPE_POINT)
{
float distance = length(light.position - fragPosition);
float attenuation = calculateAttenuation(distance, light.range);
return radiance * attenuation;
}
else if (light.type == LIGHT_TYPE_SPOT)
{
float distance = length(light.position - fragPosition);
float attenuation = calculateAttenuation(distance, light.range);
float spotEffect = calculateSpotEffect(L, light.direction, light.innerConeAngle, light.outerConeAngle);
return radiance * attenuation * spotEffect;
}

return vec3(0.0); // Unknown light type
}
165 changes: 73 additions & 92 deletions source/paimon/app/panel/scene_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,99 +371,66 @@ void ScenePanel::drawComponents(ecs::Entity entity) {
}
}

// Light component
if (entity.hasComponent<ecs::PunctualLight>()) {
if (ImGui::CollapsingHeader("Light", ImGuiTreeNodeFlags_DefaultOpen)) {
auto &lightComp = entity.getComponent<ecs::PunctualLight>();
// Directional Light component
if (entity.hasComponent<ecs::DirectionalLight>()) {
if (ImGui::CollapsingHeader("Directional Light", ImGuiTreeNodeFlags_DefaultOpen)) {
auto &light = entity.getComponent<ecs::DirectionalLight>();

if (lightComp.light) {
auto lightType = lightComp.light->getType();

// Light type dropdown
const char* types[] = { "Directional", "Point", "Spot" };
int currentType = static_cast<int>(lightType);

if (ImGui::Combo("Type", &currentType, types, 3)) {
// Switch light type
switch (currentType) {
case 0: // Directional
if (lightType != sg::PunctualLight::Type::Directional) {
lightComp.light = std::make_shared<sg::DirectionalLight>();
}
break;
case 1: // Point
if (lightType != sg::PunctualLight::Type::Point) {
lightComp.light = std::make_shared<sg::PointLight>();
}
break;
case 2: // Spot
if (lightType != sg::PunctualLight::Type::Spot) {
lightComp.light = std::make_shared<sg::SpotLight>();
}
break;
}
}

ImGui::Separator();

// Common light properties
ImGui::ColorEdit3("Color", glm::value_ptr(lightComp.light->color));
ImGui::DragFloat("Intensity", &lightComp.light->intensity, 0.1f, 0.0f, 100.0f);
ImGui::DragFloat("Range", &lightComp.light->range, 0.1f, 0.0f, 1000.0f);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("0 = infinite range");
}

ImGui::Separator();

// Type-specific properties
if (lightType == sg::PunctualLight::Type::Directional) {
// ImGui::Text("Direction: (%.2f, %.2f, %.2f)",
// lightComp.direction.x, lightComp.direction.y, lightComp.direction.z);
// ImGui::TextWrapped("Tip: Direction is controlled by Transform rotation");
}
else if (lightType == sg::PunctualLight::Type::Point) {
// ImGui::Text("Position: (%.2f, %.2f, %.2f)",
// lightComp.position.x, lightComp.position.y, lightComp.position.z);
// ImGui::TextWrapped("Tip: Position is controlled by Transform translation");
ImGui::ColorEdit3("Color", glm::value_ptr(light.color));
ImGui::DragFloat("Intensity", &light.intensity, 0.1f, 0.0f, 100.0f);
ImGui::TextWrapped("Tip: Direction is controlled by Transform rotation");
}
}

// Point Light component
if (entity.hasComponent<ecs::PointLight>()) {
if (ImGui::CollapsingHeader("Point Light", ImGuiTreeNodeFlags_DefaultOpen)) {
auto &light = entity.getComponent<ecs::PointLight>();

ImGui::ColorEdit3("Color", glm::value_ptr(light.color));
ImGui::DragFloat("Intensity", &light.intensity, 0.1f, 0.0f, 100.0f);
ImGui::DragFloat("Range", &light.range, 0.1f, 0.0f, 1000.0f);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("0 = infinite range");
}
ImGui::TextWrapped("Tip: Position is controlled by Transform translation");
}
}

// Spot Light component
if (entity.hasComponent<ecs::SpotLight>()) {
if (ImGui::CollapsingHeader("Spot Light", ImGuiTreeNodeFlags_DefaultOpen)) {
auto &light = entity.getComponent<ecs::SpotLight>();

ImGui::ColorEdit3("Color", glm::value_ptr(light.color));
ImGui::DragFloat("Intensity", &light.intensity, 0.1f, 0.0f, 100.0f);
ImGui::DragFloat("Range", &light.range, 0.1f, 0.0f, 1000.0f);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("0 = infinite range");
}

ImGui::Separator();

float innerDegrees = glm::degrees(light.innerConeAngle);
float outerDegrees = glm::degrees(light.outerConeAngle);

if (ImGui::SliderFloat("Inner Cone Angle", &innerDegrees, 0.0f, 90.0f, "%.1f°")) {
light.innerConeAngle = glm::radians(innerDegrees);
// Ensure inner <= outer
if (light.innerConeAngle > light.outerConeAngle) {
light.outerConeAngle = light.innerConeAngle;
}
else if (lightType == sg::PunctualLight::Type::Spot) {
auto* spotLight = dynamic_cast<sg::SpotLight*>(lightComp.light.get());
if (spotLight) {
// ImGui::Text("Position: (%.2f, %.2f, %.2f)",
// lightComp.position.x, lightComp.position.y, lightComp.position.z);
// ImGui::Text("Direction: (%.2f, %.2f, %.2f)",
// lightComp.direction.x, lightComp.direction.y, lightComp.direction.z);

ImGui::Separator();

// Convert cone angles to degrees for display
float innerDegrees = glm::degrees(spotLight->innerConeAngle);
float outerDegrees = glm::degrees(spotLight->outerConeAngle);

if (ImGui::SliderFloat("Inner Cone Angle", &innerDegrees, 0.0f, 90.0f, "%.1f°")) {
spotLight->innerConeAngle = glm::radians(innerDegrees);
// Ensure inner <= outer
if (spotLight->innerConeAngle > spotLight->outerConeAngle) {
spotLight->outerConeAngle = spotLight->innerConeAngle;
}
}

if (ImGui::SliderFloat("Outer Cone Angle", &outerDegrees, 0.0f, 90.0f, "%.1f°")) {
spotLight->outerConeAngle = glm::radians(outerDegrees);
// Ensure outer >= inner
if (spotLight->outerConeAngle < spotLight->innerConeAngle) {
spotLight->innerConeAngle = spotLight->outerConeAngle;
}
}

ImGui::TextWrapped("Tip: Position and direction are controlled by Transform");
}
}

if (ImGui::SliderFloat("Outer Cone Angle", &outerDegrees, 0.0f, 90.0f, "%.1f°")) {
light.outerConeAngle = glm::radians(outerDegrees);
// Ensure outer >= inner
if (light.outerConeAngle < light.innerConeAngle) {
light.innerConeAngle = light.outerConeAngle;
}

} else {
ImGui::Text("No light object assigned");
}

ImGui::TextWrapped("Tip: Position and direction are controlled by Transform");
}
}
}
Expand Down Expand Up @@ -500,10 +467,24 @@ void ScenePanel::drawAddComponentButton(ecs::Entity entity) {
}
}

if (!entity.hasComponent<ecs::PunctualLight>()) {
if (ImGui::MenuItem("Light")) {
entity.addComponent<ecs::PunctualLight>();
// Light submenu
if (ImGui::BeginMenu("Light")) {
if (!entity.hasComponent<ecs::DirectionalLight>()) {
if (ImGui::MenuItem("Directional Light")) {
entity.addComponent<ecs::DirectionalLight>();
}
}
if (!entity.hasComponent<ecs::PointLight>()) {
if (ImGui::MenuItem("Point Light")) {
entity.addComponent<ecs::PointLight>();
}
}
if (!entity.hasComponent<ecs::SpotLight>()) {
if (ImGui::MenuItem("Spot Light")) {
entity.addComponent<ecs::SpotLight>();
}
}
ImGui::EndMenu();
}

ImGui::EndPopup();
Expand Down
23 changes: 20 additions & 3 deletions source/paimon/core/ecs/components.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
#include <memory>

#include "paimon/core/ecs/entity.h"
#include "paimon/core/ecs/scene.h"
#include "paimon/core/sg/camera.h"
#include "paimon/core/sg/light.h"
#include "paimon/core/sg/material.h"
#include "paimon/core/sg/mesh.h"

Expand Down Expand Up @@ -70,8 +70,25 @@ struct Camera {
};

/// Directional light component
struct PunctualLight {
std::shared_ptr<sg::PunctualLight> light;
struct DirectionalLight {
glm::vec3 color = glm::vec3(1.0f);
float intensity = 1.0f;
};

/// Point light component
struct PointLight {
glm::vec3 color = glm::vec3(1.0f);
float intensity = 1.0f;
float range = 0.0f; // 0.0 = infinite
};

/// Spot light component
struct SpotLight {
glm::vec3 color = glm::vec3(1.0f);
float intensity = 1.0f;
float range = 0.0f; // 0.0 = infinite
float innerConeAngle = 0.0f;
float outerConeAngle = glm::radians(45.0f);
};

/// Renderable component - marks entities that should be rendered
Expand Down
2 changes: 1 addition & 1 deletion source/paimon/core/ecs/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ std::unique_ptr<Scene> Scene::create() {
{
// Initialize directional light entity
auto directionalLight = scene->createEntity("DirectionalLight");
directionalLight.addComponent<PunctualLight>(std::make_shared<sg::DirectionalLight>());
directionalLight.addComponent<DirectionalLight>();

auto &transform = directionalLight.getComponent<ecs::Transform>();
transform.translation = glm::vec3(0.0f, 0.0f, 3.0f);
Expand Down
Loading
Loading