/* =========================================================================== Copyright (C) 2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Challenge Quake 3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Challenge Quake 3. If not, see . =========================================================================== */ // Cinematic Rendering Pipeline - sunlight editor #include "crp_local.h" #include "../client/cl_imgui.h" #include "compshaders/crp/fullscreen.h" #include "compshaders/crp/sun_overlay.h" #pragma pack(push, 4) struct SunOverlayRC { vec3_t direction; float angle; vec3_t color; float padding; float textureWidth; float textureHeight; }; #pragma pack(pop) static bool LoadSunFile(const char* filePath) { bool success = false; void* data = NULL; if(ri.FS_ReadFile(filePath, &data) == sizeof(crp.sunlightData) && data != NULL) { memcpy(&crp.sunlightData, data, sizeof(crp.sunlightData)); success = true; } if(data != NULL) { ri.FS_FreeFile(data); } return success; } static void SaveSunFile(const char* filePath) { FS_EnableCNQ3FolderWrites(qtrue); ri.FS_WriteFile(filePath, &crp.sunlightData, sizeof(crp.sunlightData)); FS_EnableCNQ3FolderWrites(qfalse); } static void LoadSunFromShader(const shader_t* skyShader) { vec2_t angles; angles[0] = DEG2RAD(skyShader->sunAzimuth); angles[1] = DEG2RAD(skyShader->sunInclination); VectorCopy(skyShader->sunColor, crp.sunlightData.color); crp.sunlightData.intensity = 1.0f; AzimuthInclinationToDirection(crp.sunlightData.direction, angles); } void DirectionToAzimuthInclination(float* sc, const float* dir) { // 0=azimuth/phi, 1=inclination/theta sc[0] = atan2f(dir[1], dir[0]); sc[1] = atan2f(sqrtf(dir[0] * dir[0] + dir[1] * dir[1]), dir[2]); } void AzimuthInclinationToDirection(float* dir, const float* sc) { // 0=azimuth/phi, 1=inclination/theta dir[0] = sinf(sc[1]) * cosf(sc[0]); dir[1] = sinf(sc[1]) * sinf(sc[0]); dir[2] = cosf(sc[1]); } void SunlightEditor::Init() { { GraphicsPipelineDesc desc("G-Buffer Depth"); MakeFullScreenPipeline(desc, ShaderByteCode(g_sun_overlay_ps)); desc.AddRenderTarget(GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA, crp.renderTargetFormat); pipeline = CreateGraphicsPipeline(desc); } } void SunlightEditor::ProcessWorld(world_t& world) { skyShader = NULL; for(int i = 0; i < tr.numShaders; i++) { if(tr.shaders[i]->isSunDataValid) { skyShader = tr.shaders[i]; break; } } if(!LoadSunFile(va("sun/%s.sun", world.baseName)) && skyShader != NULL) { LoadSunFromShader(skyShader); } } void SunlightEditor::DrawOverlay() { if(!drawOverlay || !IsViewportFullscreen(backEnd.viewParms)) { return; } srp.renderMode = RenderMode::None; SCOPED_RENDER_PASS("Sun Overlay", 1.0f, 1.0f, 1.0f); CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); CmdBeginBarrier(); CmdTextureBarrier(crp.renderTarget, ResourceStates::RenderTargetBit); CmdEndBarrier(); SunOverlayRC rc = {}; rc.angle = 0.0f; VectorCopy(crp.sunlightData.color, rc.color); VectorCopy(crp.sunlightData.direction, rc.direction); rc.textureWidth = glConfig.vidWidth; rc.textureHeight = glConfig.vidHeight; CmdBindRenderTargets(1, &crp.renderTarget, NULL); CmdBindPipeline(pipeline); CmdSetGraphicsRootConstants(0, sizeof(rc), &rc); CmdDraw(3, 0); } void SunlightEditor::DrawGUI() { if(tr.world == NULL) { return; } GUI_AddMainMenuItem(GUI_MainMenu::Tools, "Edit Sunlight", "", &windowActive); if(!windowActive) { return; } if(ImGui::Begin("Sunlight", &windowActive, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Checkbox("Draw Sun", &drawOverlay); float angles[2]; DirectionToAzimuthInclination(angles, crp.sunlightData.direction); ImGui::NewLine(); ImGui::SliderAngle("Azimuth", &angles[0], 0.0f, 360.0f); ImGui::SliderAngle("Inclination", &angles[1], 0.0f, 180.0f); AzimuthInclinationToDirection(crp.sunlightData.direction, angles); ImGui::ColorEdit3("Color", crp.sunlightData.color); ImGui::SliderFloat("Light intensity", &crp.sunlightData.intensity, 0.0f, 10.0f); ImGui::NewLine(); if(ImGui::Button("Save Config...")) { OpenSaveFileDialog("sun", ".sun"); } ImGui::SameLine(); if(ImGui::Button("Open Config...")) { OpenOpenFileDialog("sun", ".sun"); } if(skyShader != NULL) { ImGui::SameLine(); if(ImGui::Button("Import from Sky Shader")) { LoadSunFromShader(skyShader); } } if(SaveFileDialog()) { SaveSunFile(GetSaveFileDialogPath()); } if(OpenFileDialog()) { LoadSunFile(GetOpenFileDialogPath()); } } ImGui::End(); }