diff --git a/Resources/Engine-Root/Modu-Logo.png b/Resources/Engine-Root/Modu-Logo.png new file mode 100644 index 0000000..7fcaa89 Binary files /dev/null and b/Resources/Engine-Root/Modu-Logo.png differ diff --git a/Resources/Shaders/frag.glsl b/Resources/Shaders/frag.glsl index 35f3db5..9c96388 100644 --- a/Resources/Shaders/frag.glsl +++ b/Resources/Shaders/frag.glsl @@ -21,7 +21,7 @@ uniform float specularStrength = 0.5; uniform float shininess = 32.0; const int MAX_LIGHTS = 10; uniform int lightCount = 0; // up to MAX_LIGHTS -// type: 0 dir, 1 point, 2 spot +// type: 0 dir, 1 point, 2 spot, 3 area (rect) uniform int lightTypeArr[MAX_LIGHTS]; uniform vec3 lightDirArr[MAX_LIGHTS]; uniform vec3 lightPosArr[MAX_LIGHTS]; @@ -30,6 +30,7 @@ uniform float lightIntensityArr[MAX_LIGHTS]; uniform float lightRangeArr[MAX_LIGHTS]; uniform float lightInnerCosArr[MAX_LIGHTS]; uniform float lightOuterCosArr[MAX_LIGHTS]; +uniform vec2 lightAreaSizeArr[MAX_LIGHTS]; // Single directional light controlled by hierarchy (fallback if none set) uniform vec3 lightDir = normalize(vec3(0.3, 1.0, 0.5)); @@ -69,12 +70,59 @@ void main() int count = min(lightCount, MAX_LIGHTS); for (int i = 0; i < count; ++i) { int ltype = lightTypeArr[i]; - vec3 ldir = (ltype == 0) ? -normalize(lightDirArr[i]) : lightPosArr[i] - FragPos; - float dist = length(ldir); - vec3 lDirN = normalize(ldir); + float intensity = lightIntensityArr[i]; + if (intensity <= 0.0) continue; + vec3 lDirN; float attenuation = 1.0; - if (ltype != 0) { + + if (ltype == 0) { + lDirN = -normalize(lightDirArr[i]); + } else if (ltype == 3) { // area light approximate + vec3 n = normalize(lightDirArr[i]); + vec3 up = abs(n.y) > 0.9 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0); + vec3 tangent = normalize(cross(up, n)); + vec3 bitangent = cross(n, tangent); + + vec3 center = lightPosArr[i]; + vec3 rel = FragPos - center; + float distPlane = dot(rel, n); + vec3 onPlane = FragPos - distPlane * n; + vec2 halfSize = lightAreaSizeArr[i] * 0.5; + vec2 local; + local.x = dot(onPlane - center, tangent); + local.y = dot(onPlane - center, bitangent); + vec2 clamped = clamp(local, -halfSize, halfSize); + vec3 closest = center + tangent * clamped.x + bitangent * clamped.y; + + vec3 lvec = closest - FragPos; + float dist = length(lvec); + if (dist < 1e-4) continue; + lDirN = normalize(lvec); + + float range = lightRangeArr[i]; + if (range > 0.0 && dist > range) continue; + if (range > 0.0) { + float falloff = clamp(1.0 - (dist / range), 0.0, 1.0); + attenuation = falloff * falloff; + } + + // Lambert against area normal for softer look + float nl = max(dot(norm, lDirN), 0.0); + float facing = max(dot(n, -lDirN), 0.0); + attenuation *= facing; + + vec3 diffuse = nl * lightColorArr[i] * intensity; + vec3 halfwayDir = normalize(lDirN + viewDir); + float spec = pow(max(dot(norm, halfwayDir), 0.0), shininess); + vec3 specular = specularStrength * spec * lightColorArr[i] * intensity; + lighting += attenuation * (diffuse + specular) * baseColor; + continue; + } else { + vec3 ldir = lightPosArr[i] - FragPos; + float dist = length(ldir); + lDirN = normalize(ldir); + float range = lightRangeArr[i]; if (range > 0.0 && dist > range) continue; if (range > 0.0) { @@ -83,9 +131,6 @@ void main() } } - float intensity = lightIntensityArr[i]; - if (intensity <= 0.0) continue; - float diff = max(dot(norm, lDirN), 0.0); vec3 diffuse = diff * lightColorArr[i] * intensity; diff --git a/src/EnginePanels.cpp b/src/EnginePanels.cpp index c98cfdc..1cdac1e 100644 --- a/src/EnginePanels.cpp +++ b/src/EnginePanels.cpp @@ -3101,6 +3101,105 @@ void Engine::renderViewport() { } } + // Light visualization overlays + auto drawLightOverlays = [&](const SceneObject& lightObj) { + if (!lightObj.light.enabled) return; + ImDrawList* dl = ImGui::GetWindowDrawList(); + ImU32 col = ImGui::GetColorU32(ImVec4(1.0f, 0.9f, 0.4f, 0.7f)); + ImU32 faint = ImGui::GetColorU32(ImVec4(1.0f, 0.9f, 0.4f, 0.25f)); + auto forwardFromRotation = [](const SceneObject& obj) { + glm::vec3 f = glm::normalize(glm::vec3( + glm::sin(glm::radians(obj.rotation.y)) * glm::cos(glm::radians(obj.rotation.x)), + glm::sin(glm::radians(obj.rotation.x)), + glm::cos(glm::radians(obj.rotation.y)) * glm::cos(glm::radians(obj.rotation.x)) + )); + if (glm::length(f) < 1e-3f || !std::isfinite(f.x)) f = glm::vec3(0.0f, -1.0f, 0.0f); + return f; + }; + + if (lightObj.type == ObjectType::PointLight) { + auto center = projectToScreen(lightObj.position); + glm::vec3 offset = lightObj.position + glm::vec3(lightObj.light.range, 0.0f, 0.0f); + auto edge = projectToScreen(offset); + if (center && edge) { + float r = std::sqrt((center->x - edge->x)*(center->x - edge->x) + (center->y - edge->y)*(center->y - edge->y)); + dl->AddCircle(*center, r, faint, 48, 2.0f); + } + } else if (lightObj.type == ObjectType::SpotLight) { + glm::vec3 dir = forwardFromRotation(lightObj); + glm::vec3 tip = lightObj.position; + glm::vec3 end = tip + dir * lightObj.light.range; + float innerRad = glm::tan(glm::radians(lightObj.light.innerAngle)) * lightObj.light.range; + float outerRad = glm::tan(glm::radians(lightObj.light.outerAngle)) * lightObj.light.range; + + // Build basis + glm::vec3 up = glm::abs(dir.y) > 0.9f ? glm::vec3(1,0,0) : glm::vec3(0,1,0); + glm::vec3 right = glm::normalize(glm::cross(dir, up)); + up = glm::normalize(glm::cross(right, dir)); + + auto drawConeRing = [&](float radius, ImU32 color) { + const int segments = 24; + ImVec2 prev; + bool first = true; + for (int i = 0; i <= segments; ++i) { + float a = (float)i / segments * 2.0f * PI; + glm::vec3 p = end + right * std::cos(a) * radius + up * std::sin(a) * radius; + auto sp = projectToScreen(p); + if (!sp) continue; + if (first) { prev = *sp; first = false; continue; } + dl->AddLine(prev, *sp, color, 1.5f); + prev = *sp; + } + }; + + auto sTip = projectToScreen(tip); + auto sEnd = projectToScreen(end); + if (sTip && sEnd) { + dl->AddLine(*sTip, *sEnd, col, 2.0f); + drawConeRing(innerRad, col); + drawConeRing(outerRad, faint); + } + } else if (lightObj.type == ObjectType::AreaLight) { + glm::vec3 n = forwardFromRotation(lightObj); + glm::vec3 up = glm::abs(n.y) > 0.9f ? glm::vec3(1,0,0) : glm::vec3(0,1,0); + glm::vec3 tangent = glm::normalize(glm::cross(up, n)); + glm::vec3 bitangent = glm::cross(n, tangent); + glm::vec2 half = lightObj.light.size * 0.5f; + glm::vec3 c = lightObj.position; + glm::vec3 corners[4] = { + c + tangent * half.x + bitangent * half.y, + c - tangent * half.x + bitangent * half.y, + c - tangent * half.x - bitangent * half.y, + c + tangent * half.x - bitangent * half.y + }; + ImVec2 projected[4]; + bool ok = true; + for (int i = 0; i < 4; ++i) { + auto p = projectToScreen(corners[i]); + if (!p) { ok = false; break; } + projected[i] = *p; + } + if (ok) { + for (int i = 0; i < 4; ++i) { + dl->AddLine(projected[i], projected[(i+1)%4], col, 2.0f); + } + // normal indicator + auto cproj = projectToScreen(c); + auto nproj = projectToScreen(c + n * glm::max(lightObj.light.range, 0.5f)); + if (cproj && nproj) { + dl->AddLine(*cproj, *nproj, col, 2.0f); + dl->AddCircleFilled(*nproj, 4.0f, col); + } + } + } + }; + + for (const auto& obj : sceneObjects) { + if (obj.type == ObjectType::PointLight || obj.type == ObjectType::SpotLight || obj.type == ObjectType::AreaLight) { + drawLightOverlays(obj); + } + } + // Toolbar const float toolbarPadding = 6.0f; const float toolbarSpacing = 5.0f; diff --git a/src/Rendering.cpp b/src/Rendering.cpp index 25c068e..b6c0e6d 100644 --- a/src/Rendering.cpp +++ b/src/Rendering.cpp @@ -864,7 +864,7 @@ void Renderer::renderSceneInternal(const Camera& camera, const std::vector 0.0f) ? obj.light.range : glm::max(sizeHint * 2.0f, 1.0f); + l.areaSize = obj.light.size; + lights.push_back(l); + if (lights.size() >= 10) break; + } + } + } for (const auto& obj : sceneObjects) { // Skip light gizmo-only types and camera helpers @@ -958,10 +976,11 @@ void Renderer::renderSceneInternal(const Camera& camera, const std::vectorsetVec3("lightPosArr" + idx, l.pos); shader->setVec3("lightColorArr" + idx, l.color); shader->setFloat("lightIntensityArr" + idx, l.intensity); - shader->setFloat("lightRangeArr" + idx, l.range); - shader->setFloat("lightInnerCosArr" + idx, l.inner); - shader->setFloat("lightOuterCosArr" + idx, l.outer); - } + shader->setFloat("lightRangeArr" + idx, l.range); + shader->setFloat("lightInnerCosArr" + idx, l.inner); + shader->setFloat("lightOuterCosArr" + idx, l.outer); + shader->setVec2("lightAreaSizeArr" + idx, l.areaSize); + } glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, obj.position);