added gyro thingy on the top right, attempted to fix Motion blur, (it did not go well 😭)
This commit is contained in:
@@ -20,6 +20,17 @@ uniform bool enableMotionBlur = false;
|
|||||||
uniform bool hasHistory = false;
|
uniform bool hasHistory = false;
|
||||||
uniform float motionBlurStrength = 0.15;
|
uniform float motionBlurStrength = 0.15;
|
||||||
|
|
||||||
|
uniform bool enableVignette = false;
|
||||||
|
uniform float vignetteIntensity = 0.35;
|
||||||
|
uniform float vignetteSmoothness = 0.25;
|
||||||
|
|
||||||
|
uniform bool enableChromatic = false;
|
||||||
|
uniform float chromaticAmount = 0.0025;
|
||||||
|
|
||||||
|
uniform bool enableAO = false;
|
||||||
|
uniform float aoRadius = 0.0035;
|
||||||
|
uniform float aoStrength = 0.6;
|
||||||
|
|
||||||
vec3 applyColorAdjust(vec3 color) {
|
vec3 applyColorAdjust(vec3 color) {
|
||||||
if (enableColorAdjust) {
|
if (enableColorAdjust) {
|
||||||
color *= exp2(exposure);
|
color *= exp2(exposure);
|
||||||
@@ -31,18 +42,94 @@ vec3 applyColorAdjust(vec3 color) {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
vec3 sampleCombined(vec2 uv) {
|
||||||
vec3 color = texture(sceneTex, TexCoord).rgb;
|
vec3 c = texture(sceneTex, uv).rgb;
|
||||||
if (enableBloom) {
|
if (enableBloom) {
|
||||||
vec3 glow = texture(bloomTex, TexCoord).rgb;
|
vec3 glow = texture(bloomTex, uv).rgb * bloomIntensity;
|
||||||
color += glow * bloomIntensity;
|
c += glow;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 sampleAdjusted(vec2 uv) {
|
||||||
|
return applyColorAdjust(sampleCombined(uv));
|
||||||
|
}
|
||||||
|
|
||||||
|
float luminance(vec3 c) {
|
||||||
|
return dot(c, vec3(0.299, 0.587, 0.114));
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeVignette(vec2 uv) {
|
||||||
|
float dist = length(uv - vec2(0.5));
|
||||||
|
float vig = smoothstep(0.8 - vignetteIntensity, 1.0 + vignetteSmoothness, dist);
|
||||||
|
return clamp(1.0 - vig * vignetteIntensity, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 applyChromatic(vec2 uv) {
|
||||||
|
vec3 base = sampleAdjusted(uv);
|
||||||
|
vec2 dir = uv - vec2(0.5);
|
||||||
|
float dist = max(length(dir), 0.0001);
|
||||||
|
vec2 offset = normalize(dir) * chromaticAmount * (1.0 + dist * 2.0);
|
||||||
|
vec3 rSample = sampleAdjusted(uv + offset);
|
||||||
|
vec3 bSample = sampleAdjusted(uv - offset);
|
||||||
|
vec3 ca = vec3(rSample.r, base.g, bSample.b);
|
||||||
|
return mix(base, ca, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeAOFactor(vec2 uv) {
|
||||||
|
vec3 centerColor = sampleAdjusted(uv);
|
||||||
|
float centerLum = luminance(centerColor);
|
||||||
|
float occlusion = 0.0;
|
||||||
|
vec2 directions[4] = vec2[](vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0));
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
vec2 sampleUv = uv + directions[i] * aoRadius;
|
||||||
|
vec3 sampleColor = sampleAdjusted(sampleUv);
|
||||||
|
float sampleLum = luminance(sampleColor);
|
||||||
|
occlusion += max(0.0, centerLum - sampleLum);
|
||||||
|
}
|
||||||
|
occlusion /= 4.0;
|
||||||
|
return clamp(1.0 - occlusion * aoStrength, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 color = sampleAdjusted(TexCoord);
|
||||||
|
|
||||||
|
if (enableChromatic) {
|
||||||
|
color = applyChromatic(TexCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
color = applyColorAdjust(color);
|
if (enableAO) {
|
||||||
|
color *= computeAOFactor(TexCoord);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableVignette) {
|
||||||
|
color *= computeVignette(TexCoord);
|
||||||
|
}
|
||||||
|
|
||||||
if (enableMotionBlur && hasHistory) {
|
if (enableMotionBlur && hasHistory) {
|
||||||
vec3 history = texture(historyTex, TexCoord).rgb;
|
vec2 dir = TexCoord - vec2(0.5);
|
||||||
color = mix(color, history, clamp(motionBlurStrength, 0.0, 0.98));
|
float len = length(dir);
|
||||||
|
dir = (len > 0.0001) ? dir / len : vec2(0.0);
|
||||||
|
float smear = clamp(motionBlurStrength, 0.0, 0.98) * 0.035; // subtle default
|
||||||
|
|
||||||
|
vec3 accum = vec3(0.0);
|
||||||
|
float weightSum = 0.0;
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
float t = (float(i) + 1.0) / 3.0;
|
||||||
|
float w = 1.0 - t * 0.4;
|
||||||
|
vec2 offsetUv = TexCoord - dir * smear * t;
|
||||||
|
offsetUv = clamp(offsetUv, vec2(0.002), vec2(0.998));
|
||||||
|
vec3 sampleCol = texture(historyTex, offsetUv).rgb;
|
||||||
|
accum += sampleCol * w;
|
||||||
|
weightSum += w;
|
||||||
|
}
|
||||||
|
vec3 history = (weightSum > 0.0) ? accum / weightSum : texture(historyTex, TexCoord).rgb;
|
||||||
|
float diff = length(color - history);
|
||||||
|
float motionWeight = smoothstep(0.01, 0.08, diff); // suppress blur when camera still
|
||||||
|
float mixAmt = clamp(motionBlurStrength * 0.85, 0.0, 0.9) * motionWeight;
|
||||||
|
if (mixAmt > 0.0001) {
|
||||||
|
color = mix(color, history, mixAmt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FragColor = vec4(color, 1.0);
|
FragColor = vec4(color, 1.0);
|
||||||
|
|||||||
@@ -1784,6 +1784,7 @@ void Engine::renderInspectorPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SceneObject& obj = *it;
|
SceneObject& obj = *it;
|
||||||
|
bool addComponentButtonShown = false;
|
||||||
|
|
||||||
if (selectedObjectIds.size() > 1) {
|
if (selectedObjectIds.size() > 1) {
|
||||||
ImGui::Text("Multiple objects selected: %zu", selectedObjectIds.size());
|
ImGui::Text("Multiple objects selected: %zu", selectedObjectIds.size());
|
||||||
@@ -1968,10 +1969,50 @@ void Engine::renderInspectorPanel() {
|
|||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextDisabled("Vignette");
|
||||||
|
if (ImGui::Checkbox("Enable Vignette", &obj.postFx.vignetteEnabled)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::BeginDisabled(!obj.postFx.vignetteEnabled);
|
||||||
|
if (ImGui::SliderFloat("Intensity", &obj.postFx.vignetteIntensity, 0.0f, 1.5f, "%.2f")) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("Smoothness", &obj.postFx.vignetteSmoothness, 0.05f, 1.0f, "%.2f")) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextDisabled("Ambient Occlusion");
|
||||||
|
if (ImGui::Checkbox("Enable AO", &obj.postFx.ambientOcclusionEnabled)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::BeginDisabled(!obj.postFx.ambientOcclusionEnabled);
|
||||||
|
if (ImGui::SliderFloat("AO Radius", &obj.postFx.aoRadius, 0.0005f, 0.01f, "%.4f")) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (ImGui::SliderFloat("AO Strength", &obj.postFx.aoStrength, 0.0f, 2.0f, "%.2f")) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextDisabled("Chromatic Aberration");
|
||||||
|
if (ImGui::Checkbox("Enable Chromatic", &obj.postFx.chromaticAberrationEnabled)) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::BeginDisabled(!obj.postFx.chromaticAberrationEnabled);
|
||||||
|
if (ImGui::SliderFloat("Fringe Amount", &obj.postFx.chromaticAmount, 0.0f, 0.01f, "%.4f")) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
projectManager.currentProject.hasUnsavedChanges = true;
|
projectManager.currentProject.hasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
ImGui::TextDisabled("Nodes stack in hierarchy order; latest node overrides previous settings.");
|
ImGui::TextDisabled("Nodes stack in hierarchy order; latest node overrides previous settings.");
|
||||||
|
ImGui::TextDisabled("Wireframe/line mode auto-disables post effects.");
|
||||||
ImGui::Unindent(10.0f);
|
ImGui::Unindent(10.0f);
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
@@ -2095,6 +2136,22 @@ void Engine::renderInspectorPanel() {
|
|||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Material");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImVec4 previewColor(obj.material.color.x, obj.material.color.y, obj.material.color.z, 1.0f);
|
||||||
|
ImVec2 sphereStart = ImGui::GetCursorScreenPos();
|
||||||
|
float sphereRadius = 12.0f;
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
ImU32 shadowCol = ImGui::ColorConvertFloat4ToU32(ImVec4(previewColor.x * 0.3f, previewColor.y * 0.3f, previewColor.z * 0.3f, 1.0f));
|
||||||
|
ImU32 baseCol = ImGui::ColorConvertFloat4ToU32(previewColor);
|
||||||
|
ImU32 highlightCol = ImGui::ColorConvertFloat4ToU32(ImVec4(std::min(1.0f, previewColor.x + 0.25f), std::min(1.0f, previewColor.y + 0.25f), std::min(1.0f, previewColor.z + 0.25f), 1.0f));
|
||||||
|
ImVec2 center = ImVec2(sphereStart.x + sphereRadius, sphereStart.y + sphereRadius);
|
||||||
|
dl->AddCircleFilled(center, sphereRadius, shadowCol);
|
||||||
|
dl->AddCircleFilled(ImVec2(center.x, center.y - 1.5f), sphereRadius - 1.5f, baseCol);
|
||||||
|
dl->AddCircleFilled(ImVec2(center.x - sphereRadius * 0.35f, center.y - sphereRadius * 0.5f), sphereRadius * 0.35f, highlightCol);
|
||||||
|
ImGui::Dummy(ImVec2(sphereRadius * 2.0f, sphereRadius * 2.0f));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextDisabled("%s", obj.materialPath.empty() ? "Unsaved Material" : fs::path(obj.materialPath).filename().string().c_str());
|
||||||
ImGui::Text("Material Asset");
|
ImGui::Text("Material Asset");
|
||||||
|
|
||||||
char matPathBuf[512] = {};
|
char matPathBuf[512] = {};
|
||||||
@@ -2125,6 +2182,63 @@ void Engine::renderInspectorPanel() {
|
|||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextDisabled("Material Slots");
|
||||||
|
for (size_t slot = 0; slot < obj.additionalMaterialPaths.size(); ++slot) {
|
||||||
|
ImGui::PushID(static_cast<int>(slot));
|
||||||
|
char slotBuf[512] = {};
|
||||||
|
std::snprintf(slotBuf, sizeof(slotBuf), "%s", obj.additionalMaterialPaths[slot].c_str());
|
||||||
|
ImGui::SetNextItemWidth(-140);
|
||||||
|
if (ImGui::InputText("##AdditionalMat", slotBuf, sizeof(slotBuf))) {
|
||||||
|
obj.additionalMaterialPaths[slot] = slotBuf;
|
||||||
|
materialChanged = true;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::SmallButton("Use Selection / Blender")) {
|
||||||
|
if (!fileBrowser.selectedFile.empty() && fs::exists(fileBrowser.selectedFile)) {
|
||||||
|
fs::directory_entry entry(fileBrowser.selectedFile);
|
||||||
|
if (fileBrowser.getFileCategory(entry) == FileCategory::Material) {
|
||||||
|
obj.additionalMaterialPaths[slot] = entry.path().string();
|
||||||
|
materialChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::SmallButton("Remove")) {
|
||||||
|
obj.additionalMaterialPaths.erase(obj.additionalMaterialPaths.begin() + static_cast<long>(slot));
|
||||||
|
materialChanged = true;
|
||||||
|
ImGui::PopID();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
if (ImGui::SmallButton("Add Material Slot")) {
|
||||||
|
obj.additionalMaterialPaths.push_back("");
|
||||||
|
materialChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextDisabled("Preview");
|
||||||
|
ImVec4 previewColorBar(obj.material.color.x, obj.material.color.y, obj.material.color.z, 1.0f);
|
||||||
|
ImGui::ColorButton("##MaterialPreview", previewColorBar, ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetContentRegionAvail().x, 32.0f));
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
addComponentButtonShown = true;
|
||||||
|
ImGui::PushID("MaterialAddComponent");
|
||||||
|
if (ImGui::Button("Add Component", ImVec2(-1, 0))) {
|
||||||
|
ImGui::OpenPopup("AddComponentPopup");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("AddComponentPopup")) {
|
||||||
|
if (ImGui::MenuItem("Script")) {
|
||||||
|
obj.scripts.push_back(ScriptComponent{});
|
||||||
|
materialChanged = true;
|
||||||
|
addComponentButtonShown = true;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
if (materialChanged) {
|
if (materialChanged) {
|
||||||
projectManager.currentProject.hasUnsavedChanges = true;
|
projectManager.currentProject.hasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
@@ -2311,29 +2425,72 @@ void Engine::renderInspectorPanel() {
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
if (!addComponentButtonShown) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.4f, 0.35f, 0.55f, 1.0f));
|
ImGui::Spacing();
|
||||||
if (ImGui::CollapsingHeader("Scripts", ImGuiTreeNodeFlags_DefaultOpen)) {
|
ImGui::Separator();
|
||||||
ImGui::Indent(10.0f);
|
ImGui::TextDisabled("Components");
|
||||||
|
addComponentButtonShown = true;
|
||||||
|
ImGui::PushID("FallbackAddComponent");
|
||||||
|
if (ImGui::Button("Add Component", ImVec2(-1, 0))) {
|
||||||
|
ImGui::OpenPopup("AddComponentPopup");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("AddComponentPopup")) {
|
||||||
|
if (ImGui::MenuItem("Script")) {
|
||||||
|
obj.scripts.push_back(ScriptComponent{});
|
||||||
|
projectManager.currentProject.hasUnsavedChanges = true;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
bool changed = false;
|
ImGui::Spacing();
|
||||||
if (ImGui::Button("Add Script", ImVec2(-1, 0))) {
|
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.9f, 1.0f), "Scripts");
|
||||||
obj.scripts.push_back(ScriptComponent{});
|
|
||||||
changed = true;
|
bool scriptsChanged = false;
|
||||||
|
int scriptToRemove = -1;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < obj.scripts.size(); ++i) {
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushID(static_cast<int>(i));
|
||||||
|
ScriptComponent& sc = obj.scripts[i];
|
||||||
|
|
||||||
|
std::string headerLabel = sc.path.empty() ? "Script" : fs::path(sc.path).filename().string();
|
||||||
|
std::string headerId = headerLabel + "##ScriptHeader" + std::to_string(i);
|
||||||
|
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen;
|
||||||
|
bool open = ImGui::CollapsingHeader(headerId.c_str(), flags);
|
||||||
|
|
||||||
|
ImVec2 headerMin = ImGui::GetItemRectMin();
|
||||||
|
ImVec2 headerMax = ImGui::GetItemRectMax();
|
||||||
|
float menuWidth = ImGui::GetFrameHeight();
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(headerMax.x - menuWidth - ImGui::GetStyle().ItemSpacing.x, headerMin.y + (headerMax.y - headerMin.y - menuWidth) * 0.5f));
|
||||||
|
if (ImGui::SmallButton("...")) {
|
||||||
|
ImGui::OpenPopup("ScriptComponentMenu");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("ScriptComponentMenu")) {
|
||||||
|
if (ImGui::MenuItem("Compile", nullptr, false, !sc.path.empty())) {
|
||||||
|
compileScriptFile(sc.path);
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem("Remove")) {
|
||||||
|
scriptToRemove = static_cast<int>(i);
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < obj.scripts.size(); ++i) {
|
if (scriptToRemove == static_cast<int>(i)) {
|
||||||
ImGui::Separator();
|
ImGui::PopID();
|
||||||
ImGui::PushID(static_cast<int>(i));
|
continue;
|
||||||
ScriptComponent& sc = obj.scripts[i];
|
}
|
||||||
|
|
||||||
|
if (open) {
|
||||||
char pathBuf[512] = {};
|
char pathBuf[512] = {};
|
||||||
std::snprintf(pathBuf, sizeof(pathBuf), "%s", sc.path.c_str());
|
std::snprintf(pathBuf, sizeof(pathBuf), "%s", sc.path.c_str());
|
||||||
ImGui::Text("Script %zu", i + 1);
|
ImGui::TextDisabled("Path");
|
||||||
ImGui::SetNextItemWidth(-140);
|
ImGui::SetNextItemWidth(-140);
|
||||||
if (ImGui::InputText("##ScriptPath", pathBuf, sizeof(pathBuf))) {
|
if (ImGui::InputText("##ScriptPath", pathBuf, sizeof(pathBuf))) {
|
||||||
sc.path = pathBuf;
|
sc.path = pathBuf;
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -2342,7 +2499,7 @@ void Engine::renderInspectorPanel() {
|
|||||||
fs::directory_entry entry(fileBrowser.selectedFile);
|
fs::directory_entry entry(fileBrowser.selectedFile);
|
||||||
if (fileBrowser.getFileCategory(entry) == FileCategory::Script) {
|
if (fileBrowser.getFileCategory(entry) == FileCategory::Script) {
|
||||||
sc.path = entry.path().string();
|
sc.path = entry.path().string();
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2354,14 +2511,6 @@ void Engine::renderInspectorPanel() {
|
|||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::SmallButton("Remove")) {
|
|
||||||
obj.scripts.erase(obj.scripts.begin() + static_cast<long>(i));
|
|
||||||
changed = true;
|
|
||||||
ImGui::PopID();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sc.path.empty()) {
|
if (!sc.path.empty()) {
|
||||||
fs::path binary = resolveScriptBinary(sc.path);
|
fs::path binary = resolveScriptBinary(sc.path);
|
||||||
sc.lastBinaryPath = binary.string();
|
sc.lastBinaryPath = binary.string();
|
||||||
@@ -2391,18 +2540,18 @@ void Engine::renderInspectorPanel() {
|
|||||||
ImGui::SetNextItemWidth(140);
|
ImGui::SetNextItemWidth(140);
|
||||||
if (ImGui::InputText("##Key", keyBuf, sizeof(keyBuf))) {
|
if (ImGui::InputText("##Key", keyBuf, sizeof(keyBuf))) {
|
||||||
sc.settings[s].key = keyBuf;
|
sc.settings[s].key = keyBuf;
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(-100);
|
ImGui::SetNextItemWidth(-100);
|
||||||
if (ImGui::InputText("##Value", valBuf, sizeof(valBuf))) {
|
if (ImGui::InputText("##Value", valBuf, sizeof(valBuf))) {
|
||||||
sc.settings[s].value = valBuf;
|
sc.settings[s].value = valBuf;
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::SmallButton("X")) {
|
if (ImGui::SmallButton("X")) {
|
||||||
sc.settings.erase(sc.settings.begin() + static_cast<long>(s));
|
sc.settings.erase(sc.settings.begin() + static_cast<long>(s));
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2411,19 +2560,21 @@ void Engine::renderInspectorPanel() {
|
|||||||
|
|
||||||
if (ImGui::SmallButton("Add Setting")) {
|
if (ImGui::SmallButton("Add Setting")) {
|
||||||
sc.settings.push_back(ScriptSetting{"", ""});
|
sc.settings.push_back(ScriptSetting{"", ""});
|
||||||
changed = true;
|
scriptsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
ImGui::PopID();
|
||||||
projectManager.currentProject.hasUnsavedChanges = true;
|
}
|
||||||
}
|
|
||||||
|
if (scriptToRemove >= 0 && scriptToRemove < static_cast<int>(obj.scripts.size())) {
|
||||||
ImGui::Unindent(10.0f);
|
obj.scripts.erase(obj.scripts.begin() + scriptToRemove);
|
||||||
|
scriptsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scriptsChanged) {
|
||||||
|
projectManager.currentProject.hasUnsavedChanges = true;
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
if (browserHasMaterial) {
|
if (browserHasMaterial) {
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
@@ -2750,6 +2901,99 @@ void Engine::renderViewport() {
|
|||||||
ImVec2 imageMin = ImGui::GetItemRectMin();
|
ImVec2 imageMin = ImGui::GetItemRectMin();
|
||||||
ImVec2 imageMax = ImGui::GetItemRectMax();
|
ImVec2 imageMax = ImGui::GetItemRectMax();
|
||||||
mouseOverViewportImage = ImGui::IsItemHovered();
|
mouseOverViewportImage = ImGui::IsItemHovered();
|
||||||
|
ImDrawList* viewportDrawList = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
|
auto setCameraFacing = [&](const glm::vec3& dir) {
|
||||||
|
glm::vec3 worldUp = glm::vec3(0, 1, 0);
|
||||||
|
glm::vec3 n = glm::normalize(dir);
|
||||||
|
glm::vec3 up = worldUp;
|
||||||
|
if (std::abs(glm::dot(n, worldUp)) > 0.98f) {
|
||||||
|
up = glm::vec3(0, 0, 1);
|
||||||
|
}
|
||||||
|
glm::vec3 right = glm::normalize(glm::cross(up, n));
|
||||||
|
if (glm::length(right) < 1e-4f) {
|
||||||
|
right = glm::vec3(1, 0, 0);
|
||||||
|
}
|
||||||
|
up = glm::normalize(glm::cross(n, right));
|
||||||
|
|
||||||
|
camera.front = n;
|
||||||
|
camera.up = up;
|
||||||
|
camera.pitch = glm::degrees(std::asin(glm::clamp(n.y, -1.0f, 1.0f)));
|
||||||
|
camera.pitch = glm::clamp(camera.pitch, -89.0f, 89.0f);
|
||||||
|
camera.yaw = glm::degrees(std::atan2(n.z, n.x));
|
||||||
|
camera.firstMouse = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw small axis widget in top-right of viewport
|
||||||
|
{
|
||||||
|
const float widgetSize = 94.0f;
|
||||||
|
const float padding = 12.0f;
|
||||||
|
ImVec2 center = ImVec2(
|
||||||
|
imageMax.x - padding - widgetSize * 0.5f,
|
||||||
|
imageMin.y + padding + widgetSize * 0.5f
|
||||||
|
);
|
||||||
|
float radius = widgetSize * 0.46f;
|
||||||
|
ImU32 ringCol = ImGui::GetColorU32(ImVec4(0.07f, 0.07f, 0.1f, 0.9f));
|
||||||
|
ImU32 ringBorder = ImGui::GetColorU32(ImVec4(1, 1, 1, 0.18f));
|
||||||
|
viewportDrawList->AddCircleFilled(center, radius + 10.0f, ringCol, 48);
|
||||||
|
viewportDrawList->AddCircle(center, radius + 10.0f, ringBorder, 48);
|
||||||
|
viewportDrawList->AddCircle(center, radius + 3.0f, ImGui::GetColorU32(ImVec4(1,1,1,0.08f)), 32);
|
||||||
|
viewportDrawList->AddCircleFilled(center, 5.5f, ImGui::GetColorU32(ImVec4(1,1,1,0.6f)), 24);
|
||||||
|
|
||||||
|
glm::mat3 viewRot = glm::mat3(view);
|
||||||
|
ImVec2 widgetMin = ImVec2(center.x - widgetSize * 0.5f, center.y - widgetSize * 0.5f);
|
||||||
|
ImVec2 widgetMax = ImVec2(center.x + widgetSize * 0.5f, center.y + widgetSize * 0.5f);
|
||||||
|
bool widgetHover = ImGui::IsMouseHoveringRect(widgetMin, widgetMax);
|
||||||
|
struct AxisArrow {
|
||||||
|
glm::vec3 dir;
|
||||||
|
ImU32 color;
|
||||||
|
const char* label;
|
||||||
|
};
|
||||||
|
AxisArrow arrows[] = {
|
||||||
|
{ glm::vec3(1, 0, 0), ImGui::GetColorU32(ImVec4(0.9f, 0.2f, 0.2f, 1.0f)), "X" },
|
||||||
|
{ glm::vec3(-1, 0, 0), ImGui::GetColorU32(ImVec4(0.6f, 0.2f, 0.2f, 1.0f)), "-X" },
|
||||||
|
{ glm::vec3(0, 1, 0), ImGui::GetColorU32(ImVec4(0.2f, 0.9f, 0.2f, 1.0f)), "Y" },
|
||||||
|
{ glm::vec3(0,-1, 0), ImGui::GetColorU32(ImVec4(0.2f, 0.6f, 0.2f, 1.0f)), "-Y" },
|
||||||
|
{ glm::vec3(0, 0, 1), ImGui::GetColorU32(ImVec4(0.2f, 0.4f, 0.9f, 1.0f)), "Z" },
|
||||||
|
{ glm::vec3(0, 0,-1), ImGui::GetColorU32(ImVec4(0.2f, 0.3f, 0.6f, 1.0f)), "-Z" },
|
||||||
|
};
|
||||||
|
|
||||||
|
ImVec2 mouse = ImGui::GetIO().MousePos;
|
||||||
|
int clickedIdx = -1;
|
||||||
|
float clickRadius = 12.0f;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
glm::vec3 camSpace = viewRot * arrows[i].dir;
|
||||||
|
glm::vec2 dir2 = glm::normalize(glm::vec2(camSpace.x, -camSpace.y));
|
||||||
|
float depthScale = glm::clamp(0.35f + 0.65f * ((camSpace.z + 1.0f) * 0.5f), 0.25f, 1.0f);
|
||||||
|
float len = radius * depthScale;
|
||||||
|
ImVec2 tip = ImVec2(center.x + dir2.x * len, center.y + dir2.y * len);
|
||||||
|
|
||||||
|
ImVec2 base1 = ImVec2(center.x + dir2.x * (len * 0.55f) + dir2.y * (len * 0.12f),
|
||||||
|
center.y + dir2.y * (len * 0.55f) - dir2.x * (len * 0.12f));
|
||||||
|
ImVec2 base2 = ImVec2(center.x + dir2.x * (len * 0.55f) - dir2.y * (len * 0.12f),
|
||||||
|
center.y + dir2.y * (len * 0.55f) + dir2.x * (len * 0.12f));
|
||||||
|
|
||||||
|
viewportDrawList->AddTriangleFilled(base1, tip, base2, arrows[i].color);
|
||||||
|
viewportDrawList->AddTriangle(base1, tip, base2, ImGui::GetColorU32(ImVec4(0,0,0,0.35f)));
|
||||||
|
|
||||||
|
ImVec2 labelPos = ImVec2(center.x + dir2.x * (len * 0.78f), center.y + dir2.y * (len * 0.78f));
|
||||||
|
viewportDrawList->AddCircleFilled(labelPos, 6.0f, ImGui::GetColorU32(ImVec4(0,0,0,0.5f)), 12);
|
||||||
|
viewportDrawList->AddText(ImVec2(labelPos.x - 4.0f, labelPos.y - 7.0f), ImGui::GetColorU32(ImVec4(1,1,1,0.95f)), arrows[i].label);
|
||||||
|
|
||||||
|
if (widgetHover) {
|
||||||
|
float dx = mouse.x - tip.x;
|
||||||
|
float dy = mouse.y - tip.y;
|
||||||
|
if (std::sqrt(dx*dx + dy*dy) <= clickRadius && ImGui::IsMouseReleased(0)) {
|
||||||
|
clickedIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickedIdx >= 0) {
|
||||||
|
setCameraFacing(arrows[clickedIdx].dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto projectToScreen = [&](const glm::vec3& p) -> std::optional<ImVec2> {
|
auto projectToScreen = [&](const glm::vec3& p) -> std::optional<ImVec2> {
|
||||||
glm::vec4 clip = proj * view * glm::vec4(p, 1.0f);
|
glm::vec4 clip = proj * view * glm::vec4(p, 1.0f);
|
||||||
|
|||||||
@@ -284,6 +284,10 @@ bool SceneSerializer::saveScene(const fs::path& filePath,
|
|||||||
file << "vertexShader=" << obj.vertexShaderPath << "\n";
|
file << "vertexShader=" << obj.vertexShaderPath << "\n";
|
||||||
file << "fragmentShader=" << obj.fragmentShaderPath << "\n";
|
file << "fragmentShader=" << obj.fragmentShaderPath << "\n";
|
||||||
file << "useOverlay=" << (obj.useOverlay ? 1 : 0) << "\n";
|
file << "useOverlay=" << (obj.useOverlay ? 1 : 0) << "\n";
|
||||||
|
file << "additionalMaterialCount=" << obj.additionalMaterialPaths.size() << "\n";
|
||||||
|
for (size_t mi = 0; mi < obj.additionalMaterialPaths.size(); ++mi) {
|
||||||
|
file << "additionalMaterial" << mi << "=" << obj.additionalMaterialPaths[mi] << "\n";
|
||||||
|
}
|
||||||
file << "scripts=" << obj.scripts.size() << "\n";
|
file << "scripts=" << obj.scripts.size() << "\n";
|
||||||
for (size_t si = 0; si < obj.scripts.size(); ++si) {
|
for (size_t si = 0; si < obj.scripts.size(); ++si) {
|
||||||
const auto& sc = obj.scripts[si];
|
const auto& sc = obj.scripts[si];
|
||||||
@@ -317,6 +321,14 @@ bool SceneSerializer::saveScene(const fs::path& filePath,
|
|||||||
file << "postColorFilter=" << obj.postFx.colorFilter.r << "," << obj.postFx.colorFilter.g << "," << obj.postFx.colorFilter.b << "\n";
|
file << "postColorFilter=" << obj.postFx.colorFilter.r << "," << obj.postFx.colorFilter.g << "," << obj.postFx.colorFilter.b << "\n";
|
||||||
file << "postMotionBlurEnabled=" << (obj.postFx.motionBlurEnabled ? 1 : 0) << "\n";
|
file << "postMotionBlurEnabled=" << (obj.postFx.motionBlurEnabled ? 1 : 0) << "\n";
|
||||||
file << "postMotionBlurStrength=" << obj.postFx.motionBlurStrength << "\n";
|
file << "postMotionBlurStrength=" << obj.postFx.motionBlurStrength << "\n";
|
||||||
|
file << "postVignetteEnabled=" << (obj.postFx.vignetteEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postVignetteIntensity=" << obj.postFx.vignetteIntensity << "\n";
|
||||||
|
file << "postVignetteSmoothness=" << obj.postFx.vignetteSmoothness << "\n";
|
||||||
|
file << "postChromaticEnabled=" << (obj.postFx.chromaticAberrationEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postChromaticAmount=" << obj.postFx.chromaticAmount << "\n";
|
||||||
|
file << "postAOEnabled=" << (obj.postFx.ambientOcclusionEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postAORadius=" << obj.postFx.aoRadius << "\n";
|
||||||
|
file << "postAOStrength=" << obj.postFx.aoStrength << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
file << "scriptCount=" << obj.scripts.size() << "\n";
|
file << "scriptCount=" << obj.scripts.size() << "\n";
|
||||||
@@ -448,6 +460,14 @@ bool SceneSerializer::loadScene(const fs::path& filePath,
|
|||||||
currentObj->fragmentShaderPath = value;
|
currentObj->fragmentShaderPath = value;
|
||||||
} else if (key == "useOverlay") {
|
} else if (key == "useOverlay") {
|
||||||
currentObj->useOverlay = (std::stoi(value) != 0);
|
currentObj->useOverlay = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "additionalMaterialCount") {
|
||||||
|
int count = std::stoi(value);
|
||||||
|
currentObj->additionalMaterialPaths.resize(std::max(0, count));
|
||||||
|
} else if (key.rfind("additionalMaterial", 0) == 0) {
|
||||||
|
int idx = std::stoi(key.substr(18)); // length of "additionalMaterial"
|
||||||
|
if (idx >= 0 && idx < (int)currentObj->additionalMaterialPaths.size()) {
|
||||||
|
currentObj->additionalMaterialPaths[idx] = value;
|
||||||
|
}
|
||||||
} else if (key == "scripts") {
|
} else if (key == "scripts") {
|
||||||
int count = std::stoi(value);
|
int count = std::stoi(value);
|
||||||
currentObj->scripts.resize(std::max(0, count));
|
currentObj->scripts.resize(std::max(0, count));
|
||||||
@@ -534,6 +554,22 @@ bool SceneSerializer::loadScene(const fs::path& filePath,
|
|||||||
currentObj->postFx.motionBlurEnabled = (std::stoi(value) != 0);
|
currentObj->postFx.motionBlurEnabled = (std::stoi(value) != 0);
|
||||||
} else if (key == "postMotionBlurStrength") {
|
} else if (key == "postMotionBlurStrength") {
|
||||||
currentObj->postFx.motionBlurStrength = std::stof(value);
|
currentObj->postFx.motionBlurStrength = std::stof(value);
|
||||||
|
} else if (key == "postVignetteEnabled") {
|
||||||
|
currentObj->postFx.vignetteEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postVignetteIntensity") {
|
||||||
|
currentObj->postFx.vignetteIntensity = std::stof(value);
|
||||||
|
} else if (key == "postVignetteSmoothness") {
|
||||||
|
currentObj->postFx.vignetteSmoothness = std::stof(value);
|
||||||
|
} else if (key == "postChromaticEnabled") {
|
||||||
|
currentObj->postFx.chromaticAberrationEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postChromaticAmount") {
|
||||||
|
currentObj->postFx.chromaticAmount = std::stof(value);
|
||||||
|
} else if (key == "postAOEnabled") {
|
||||||
|
currentObj->postFx.ambientOcclusionEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postAORadius") {
|
||||||
|
currentObj->postFx.aoRadius = std::stof(value);
|
||||||
|
} else if (key == "postAOStrength") {
|
||||||
|
currentObj->postFx.aoStrength = std::stof(value);
|
||||||
} else if (key == "scriptCount") {
|
} else if (key == "scriptCount") {
|
||||||
int count = std::stoi(value);
|
int count = std::stoi(value);
|
||||||
currentObj->scripts.resize(std::max(0, count));
|
currentObj->scripts.resize(std::max(0, count));
|
||||||
|
|||||||
@@ -1066,7 +1066,19 @@ PostFXSettings Renderer::gatherPostFX(const std::vector<SceneObject>& sceneObjec
|
|||||||
|
|
||||||
void Renderer::applyPostProcessing(const std::vector<SceneObject>& sceneObjects) {
|
void Renderer::applyPostProcessing(const std::vector<SceneObject>& sceneObjects) {
|
||||||
PostFXSettings settings = gatherPostFX(sceneObjects);
|
PostFXSettings settings = gatherPostFX(sceneObjects);
|
||||||
bool wantsEffects = settings.enabled && (settings.bloomEnabled || settings.colorAdjustEnabled || settings.motionBlurEnabled);
|
GLint polygonMode[2] = { GL_FILL, GL_FILL };
|
||||||
|
#ifdef GL_POLYGON_MODE
|
||||||
|
glGetIntegerv(GL_POLYGON_MODE, polygonMode);
|
||||||
|
#endif
|
||||||
|
bool wireframe = (polygonMode[0] == GL_LINE || polygonMode[1] == GL_LINE);
|
||||||
|
|
||||||
|
bool wantsEffects = settings.enabled &&
|
||||||
|
(settings.bloomEnabled || settings.colorAdjustEnabled || settings.motionBlurEnabled ||
|
||||||
|
settings.vignetteEnabled || settings.chromaticAberrationEnabled || settings.ambientOcclusionEnabled);
|
||||||
|
|
||||||
|
if (wireframe) {
|
||||||
|
wantsEffects = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!wantsEffects || !postShader || currentWidth <= 0 || currentHeight <= 0) {
|
if (!wantsEffects || !postShader || currentWidth <= 0 || currentHeight <= 0) {
|
||||||
displayTexture = viewportTexture;
|
displayTexture = viewportTexture;
|
||||||
@@ -1142,6 +1154,14 @@ void Renderer::applyPostProcessing(const std::vector<SceneObject>& sceneObjects)
|
|||||||
postShader->setBool("enableMotionBlur", settings.motionBlurEnabled);
|
postShader->setBool("enableMotionBlur", settings.motionBlurEnabled);
|
||||||
postShader->setFloat("motionBlurStrength", settings.motionBlurStrength);
|
postShader->setFloat("motionBlurStrength", settings.motionBlurStrength);
|
||||||
postShader->setBool("hasHistory", historyValid);
|
postShader->setBool("hasHistory", historyValid);
|
||||||
|
postShader->setBool("enableVignette", settings.vignetteEnabled);
|
||||||
|
postShader->setFloat("vignetteIntensity", settings.vignetteIntensity);
|
||||||
|
postShader->setFloat("vignetteSmoothness", settings.vignetteSmoothness);
|
||||||
|
postShader->setBool("enableChromatic", settings.chromaticAberrationEnabled);
|
||||||
|
postShader->setFloat("chromaticAmount", settings.chromaticAmount);
|
||||||
|
postShader->setBool("enableAO", settings.ambientOcclusionEnabled);
|
||||||
|
postShader->setFloat("aoRadius", settings.aoRadius);
|
||||||
|
postShader->setFloat("aoStrength", settings.aoStrength);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, viewportTexture);
|
glBindTexture(GL_TEXTURE_2D, viewportTexture);
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ struct PostFXSettings {
|
|||||||
glm::vec3 colorFilter = glm::vec3(1.0f);
|
glm::vec3 colorFilter = glm::vec3(1.0f);
|
||||||
bool motionBlurEnabled = false;
|
bool motionBlurEnabled = false;
|
||||||
float motionBlurStrength = 0.15f; // 0..1 blend with previous frame
|
float motionBlurStrength = 0.15f; // 0..1 blend with previous frame
|
||||||
|
bool vignetteEnabled = false;
|
||||||
|
float vignetteIntensity = 0.35f;
|
||||||
|
float vignetteSmoothness = 0.25f;
|
||||||
|
bool chromaticAberrationEnabled = false;
|
||||||
|
float chromaticAmount = 0.0025f;
|
||||||
|
bool ambientOcclusionEnabled = false;
|
||||||
|
float aoRadius = 0.0035f;
|
||||||
|
float aoStrength = 0.6f;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ConsoleMessageType {
|
enum class ConsoleMessageType {
|
||||||
@@ -115,6 +123,7 @@ public:
|
|||||||
CameraComponent camera; // Only used when type is camera
|
CameraComponent camera; // Only used when type is camera
|
||||||
PostFXSettings postFx; // Only used when type is PostFXNode
|
PostFXSettings postFx; // Only used when type is PostFXNode
|
||||||
std::vector<ScriptComponent> scripts;
|
std::vector<ScriptComponent> scripts;
|
||||||
|
std::vector<std::string> additionalMaterialPaths;
|
||||||
|
|
||||||
SceneObject(const std::string& name, ObjectType type, int id)
|
SceneObject(const std::string& name, ObjectType type, int id)
|
||||||
: name(name), type(type), position(0.0f), rotation(0.0f), scale(1.0f), id(id) {}
|
: name(name), type(type), position(0.0f), rotation(0.0f), scale(1.0f), id(id) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user