From ed3fa2eb81a045d26dc43953142ece4884f38356 Mon Sep 17 00:00:00 2001 From: assertivist Date: Fri, 21 Jun 2024 02:16:06 -0700 Subject: [PATCH 01/25] legacy single buffer renderer for speed freaks --- Avara.xcodeproj/project.pbxproj | 9 + src/game/CAvaraApp.cpp | 9 +- src/gui/Preferences.h | 2 + src/render/LegacyOpenGLRenderer.cpp | 469 ++++++++++++++++++++++++++++ src/render/LegacyOpenGLRenderer.h | 52 +++ src/render/OpenGLVertices.h | 1 + 6 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 src/render/LegacyOpenGLRenderer.cpp create mode 100644 src/render/LegacyOpenGLRenderer.h diff --git a/Avara.xcodeproj/project.pbxproj b/Avara.xcodeproj/project.pbxproj index 31f6ce98..28012eca 100644 --- a/Avara.xcodeproj/project.pbxproj +++ b/Avara.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 6025AE4E2C255E6A00A57EFB /* LegacyOpenGLRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 60C274E22C23EBE9005B67A9 /* LegacyOpenGLRenderer.cpp */; }; 940541ED2B5875B200E32241 /* OpenGLShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940541EC2B5875B200E32241 /* OpenGLShader.cpp */; }; 940541EE2B5875B200E32241 /* OpenGLShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940541EC2B5875B200E32241 /* OpenGLShader.cpp */; }; 9419E8472B49ECB1007C50D0 /* CTeamColorAdjuster.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9419E8462B49ECB1007C50D0 /* CTeamColorAdjuster.cpp */; }; @@ -387,6 +388,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 60C274E22C23EBE9005B67A9 /* LegacyOpenGLRenderer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LegacyOpenGLRenderer.cpp; sourceTree = ""; }; + 60C274E32C23EBE9005B67A9 /* LegacyOpenGLRenderer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = LegacyOpenGLRenderer.h; sourceTree = ""; }; 940541EB2B58744500E32241 /* OpenGLShader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenGLShader.h; sourceTree = ""; }; 940541EC2B5875B200E32241 /* OpenGLShader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = OpenGLShader.cpp; sourceTree = ""; }; 9419E8452B49EC89007C50D0 /* CTeamColorAdjuster.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTeamColorAdjuster.h; sourceTree = ""; }; @@ -1083,6 +1086,8 @@ children = ( 947F5C642B576B9F00F41689 /* ModernOpenGLRenderer.cpp */, 947F5C632B576ACD00F41689 /* ModernOpenGLRenderer.h */, + 60C274E22C23EBE9005B67A9 /* LegacyOpenGLRenderer.cpp */, + 60C274E32C23EBE9005B67A9 /* LegacyOpenGLRenderer.h */, 947F5C672B57A30100F41689 /* NullRenderer.h */, 940541EC2B5875B200E32241 /* OpenGLShader.cpp */, 940541EB2B58744500E32241 /* OpenGLShader.h */, @@ -2390,6 +2395,7 @@ E5890CBF29895118007A875D /* CAbstractActor.cpp in Sources */, E5890CAA29895118007A875D /* CAbstractPipe.cpp in Sources */, E5890F0929895124007A875D /* stb_vorbis.c in Sources */, + 6025AE4E2C255E6A00A57EFB /* LegacyOpenGLRenderer.cpp in Sources */, E5890D0529895118007A875D /* CSoundMixer.cpp in Sources */, E5890CF529895118007A875D /* CRamp.cpp in Sources */, E5890C9F29895118007A875D /* Resource.cpp in Sources */, @@ -2597,6 +2603,7 @@ COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=macosx*]" = ""; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -2621,6 +2628,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.avaraline.Avara; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_EMIT_LOC_STRINGS = YES; }; name = Debug; @@ -2661,6 +2669,7 @@ PRODUCT_BUNDLE_IDENTIFIER = net.avaraline.Avara; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_EMIT_LOC_STRINGS = YES; }; name = Release; diff --git a/src/game/CAvaraApp.cpp b/src/game/CAvaraApp.cpp index 1d27944b..78c52d47 100755 --- a/src/game/CAvaraApp.cpp +++ b/src/game/CAvaraApp.cpp @@ -37,6 +37,7 @@ #include "Tags.h" #include "Debug.h" #include "ModernOpenGLRenderer.h" +#include "LegacyOpenGLRenderer.h" // included while we fake things out #include "CPlayerManager.h" @@ -79,7 +80,13 @@ CAvaraAppImpl::CAvaraAppImpl() : CApplication("Avara") { ui->LoadImages(mNVGContext); } - gRenderer = new ModernOpenGLRenderer(mSDLWindow); + if (Get(kUseLegacyRenderer)) { + gRenderer = new LegacyOpenGLRenderer(mSDLWindow); + } + else { + gRenderer = new ModernOpenGLRenderer(mSDLWindow); + } + gRenderer->UpdateViewRect(mSize.x, mSize.y, mPixelRatio); gRenderer->SetFOV(Number(kFOV)); gRenderer->ResetLights(); diff --git a/src/gui/Preferences.h b/src/gui/Preferences.h index 1a39b15d..4e5ed77e 100755 --- a/src/gui/Preferences.h +++ b/src/gui/Preferences.h @@ -42,6 +42,7 @@ using json = nlohmann::json; #define kFullScreenTag "fullscreen" #define kFOV "fov" #define kFXAA "fxaa" +#define kUseLegacyRenderer "useLegacyRenderer" // Other graphics settings #define kColorBlindMode "colorBlindMode" @@ -196,6 +197,7 @@ static json defaultPrefs = { {kThrottle, 0}, {kGoodGamePhrases, {}}, {kShowElo, false}, + {kUseLegacyRenderer, false} }; diff --git a/src/render/LegacyOpenGLRenderer.cpp b/src/render/LegacyOpenGLRenderer.cpp new file mode 100644 index 00000000..aca92817 --- /dev/null +++ b/src/render/LegacyOpenGLRenderer.cpp @@ -0,0 +1,469 @@ +#include "LegacyOpenGLRenderer.h" + +#include "AssetManager.h" +#include "ColorManager.h" +#include "FastMat.h" +#include "OpenGLVertices.h" + +#include + +#include + +#define SKY_VERT "sky_vert.glsl" +#define SKY_FRAG "sky_frag.glsl" + +#define OBJ_VERT "world_vert.glsl" +#define OBJ_FRAG "world_frag.glsl" + +const float legacySkyboxVertices[] = { + -5.0f, 5.0f, -5.0f, + -5.0f, -5.0f, -5.0f, + 5.0f, -5.0f, -5.0f, + 5.0f, -5.0f, -5.0f, + 5.0f, 5.0f, -5.0f, + -5.0f, 5.0f, -5.0f, + + -5.0f, -5.0f, 5.0f, + -5.0f, -5.0f, -5.0f, + -5.0f, 5.0f, -5.0f, + -5.0f, 5.0f, -5.0f, + -5.0f, 5.0f, 5.0f, + -5.0f, -5.0f, 5.0f, + + 5.0f, -5.0f, -5.0f, + 5.0f, -5.0f, 5.0f, + 5.0f, 5.0f, 5.0f, + 5.0f, 5.0f, 5.0f, + 5.0f, 5.0f, -5.0f, + 5.0f, -5.0f, -5.0f, + + -5.0f, -5.0f, 5.0f, + -5.0f, 5.0f, 5.0f, + 5.0f, 5.0f, 5.0f, + 5.0f, 5.0f, 5.0f, + 5.0f, -5.0f, 5.0f, + -5.0f, -5.0f, 5.0f, + + -5.0f, 5.0f, -5.0f, + 5.0f, 5.0f, -5.0f, + 5.0f, 5.0f, 5.0f, + 5.0f, 5.0f, 5.0f, + -5.0f, 5.0f, 5.0f, + -5.0f, 5.0f, -5.0f, + + -5.0f, -5.0f, -5.0f, + -5.0f, -5.0f, 5.0f, + 5.0f, -5.0f, -5.0f, + 5.0f, -5.0f, -5.0f, + -5.0f, -5.0f, 5.0f, + 5.0f, -5.0f, 5.0f +}; + +const char *__glGetErrorString(GLenum error) +{ + switch (error) { + case GL_NO_ERROR: + return "No Error"; + case GL_INVALID_ENUM: + return "Invalid Enum"; + case GL_INVALID_VALUE: + return "Invalid Value"; + case GL_INVALID_OPERATION: + return "Invalid Operation"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "Invalid Framebuffer Operation"; + case GL_OUT_OF_MEMORY: + return "Out of Memory"; + case GL_STACK_UNDERFLOW: + return "Stack Underflow"; + case GL_STACK_OVERFLOW: + return "Stack Overflow"; + // case GL_CONTEXT_LOST: + // return "Context Lost"; + default: + return "Unknown Error"; + } +} + +void ___glCheckErrors(const char *filename, int line) +{ + GLenum err; + while ((err = glGetError()) != GL_NO_ERROR) + SDL_Log("OpenGL Error: %s (%d) [%u] %s\n", filename, line, err, __glGetErrorString(err)); +} +#define __glCheckErrors() ___glCheckErrors(__FILE__, __LINE__) + +inline glm::mat4 ToFloatMat(const Matrix &m) +{ + glm::mat4 mat(1.0); + for (int i = 0; i < 3; i ++) { + mat[0][i] = ToFloat(m[0][i]); + mat[1][i] = ToFloat(m[1][i]); + mat[2][i] = ToFloat(m[2][i]); + mat[3][i] = ToFloat(m[3][i]); + } + return mat; +} + +LegacyOpenGLRenderer::LegacyOpenGLRenderer(SDL_Window *window) : AbstractRenderer() +{ + this->window = window; + + GLsizei w, h; + SDL_GL_GetDrawableSize(window, &w, &h); + viewParams->viewPixelDimensions.h = w; + viewParams->viewPixelDimensions.v = h; + + dynamicWorld = new CBSPWorldImpl(100); + + // Initialize shaders. + skyShader = LoadShader(SKY_VERT, SKY_FRAG); + worldShader = LoadShader(OBJ_VERT, OBJ_FRAG); + ApplyLights(); + ApplyProjection(); + + // Create a separate VBO and VAO for the skybox, and upload its geometry to the GPU. + glGenVertexArrays(1, &skyVertArray); + glGenBuffers(1, &skyBuffer); + __glCheckErrors(); + glBindVertexArray(skyVertArray); + __glCheckErrors(); + glBindBuffer(GL_ARRAY_BUFFER, skyBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(legacySkyboxVertices), legacySkyboxVertices, GL_STATIC_DRAW); + + __glCheckErrors(); + // Rebind to default VBO/VAO. + glBindVertexArray(0); + __glCheckErrors(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE); + __glCheckErrors(); + +} + +LegacyOpenGLRenderer::~LegacyOpenGLRenderer() { + delete dynamicWorld; + AbstractRenderer::~AbstractRenderer(); +} + +void LegacyOpenGLRenderer::AddPart(CBSPPart *part) +{ + dynamicWorld->AddPart(part); +} + +void LegacyOpenGLRenderer::ApplyLights() +{ + float ambientIntensity = ToFloat(viewParams->ambientLight); + float ambientRGB[3]; + viewParams->ambientLightColor.ExportGLFloats(ambientRGB, 3); + + //hudShader->Use(); + //AdjustAmbient(*hudShader, HUD_AMBIENT); + + worldShader->Use(); + AdjustAmbient(*worldShader, ambientIntensity); + worldShader->SetFloat3("ambientColor", ambientRGB); + + for (int i = 0; i < 4; i++) { + float intensity = ToFloat(viewParams->dirLightSettings[i].intensity); + float elevation = ToFloat(viewParams->dirLightSettings[i].angle1); + float azimuth = ToFloat(viewParams->dirLightSettings[i].angle2); + float rgb[3]; + viewParams->dirLightSettings[i].color.ExportGLFloats(rgb, 3); + + float xyz[3] = { + sin(Deg2Rad(-azimuth)) * intensity, + sin(Deg2Rad(-elevation)) * intensity, + cos(Deg2Rad(azimuth)) * intensity + }; + + switch (i) { + case 0: + worldShader->SetFloat3("light0", xyz); + worldShader->SetFloat3("light0Color", rgb); + break; + case 1: + worldShader->SetFloat3("light1", xyz); + worldShader->SetFloat3("light1Color", rgb); + break; + case 2: + worldShader->SetFloat3("light2", xyz); + worldShader->SetFloat3("light2Color", rgb); + break; + case 3: + worldShader->SetFloat3("light3", xyz); + worldShader->SetFloat3("light3Color", rgb); + break; + } + } +} + +void LegacyOpenGLRenderer::ApplyProjection() +{ + SDL_GL_GetDrawableSize(this->window, &resolution[0], &resolution[1]); + + glm::mat4 proj = glm::scale( + glm::perspective( + glm::radians(fov), + (float)viewParams->viewPixelDimensions.h / (float)viewParams->viewPixelDimensions.v, + 0.099f, + 1000.0f + ), + glm::vec3(-1, 1, -1) + ); + + skyShader->Use(); + skyShader->SetMat4("proj", proj); + __glCheckErrors(); + + worldShader->Use(); + worldShader->SetMat4("proj", proj); + __glCheckErrors(); + + //hudShader->Use(); + //hudShader->SetMat4("proj", proj); + //glCheckErrors(); +} + +void LegacyOpenGLRenderer::LevelReset() +{ + dynamicWorld->DisposeParts(); + AbstractRenderer::LevelReset(); +} + +std::unique_ptr LegacyOpenGLRenderer::NewVertexDataInstance() +{ + return std::make_unique(); +} + +void LegacyOpenGLRenderer::OverheadPoint(Fixed *pt, Fixed *extent) +{ + dynamicWorld->OverheadPoint(pt, extent); +} + +void LegacyOpenGLRenderer::RefreshWindow() +{ + SDL_GL_SwapWindow(window); +} + +void LegacyOpenGLRenderer::RemovePart(CBSPPart *part) +{ + dynamicWorld->RemovePart(part); +} + +void LegacyOpenGLRenderer::RenderFrame() +{ + Clear(); + ApplyView(); + + // RENDER SKYBOX /////////////////////////////////////////////////////////////////////////////// + + Matrix *trans = &(viewParams->viewMatrix); + + // Get rid of the view translation. + glm::mat4 glMatrix = ToFloatMat(*trans); + glMatrix[3][0] = glMatrix[3][1] = glMatrix[3][2] = 0; + + float groundColorRGB[3]; + float lowSkyColorRGB[3]; + float highSkyColorRGB[3]; + skyParams->groundColor.ExportGLFloats(groundColorRGB, 3); + skyParams->lowSkyColor.ExportGLFloats(lowSkyColorRGB, 3); + skyParams->highSkyColor.ExportGLFloats(highSkyColorRGB, 3); + + // Switch to first offscreen FBO. + //glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + glBindVertexArray(skyVertArray); + glBindBuffer(GL_ARRAY_BUFFER, skyBuffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, NULL); + glEnableVertexAttribArray(0); + + skyShader->Use(); + skyShader->SetMat4("view", glMatrix); + skyShader->SetFloat3("groundColor", groundColorRGB); + skyShader->SetFloat3("horizonColor", lowSkyColorRGB); + skyShader->SetFloat3("skyColor", highSkyColorRGB); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(legacySkyboxVertices)); + glDisableVertexAttribArray(0); + + __glCheckErrors(); + // RENDER WORLD //////////////////////////////////////////////////////////////////////// + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + glEnable(GL_DEPTH_TEST); + + worldShader->Use(); + + dynamicWorld->PrepareForRender(); + + __glCheckErrors(); + float defaultAmbient = ToFloat(viewParams->ambientLight); + + auto partList = dynamicWorld->GetVisiblePartListPointer(); + auto partCount = dynamicWorld->GetVisiblePartCount(); + for (uint16_t i = 0; i < partCount; i++) { + Draw(*worldShader, **partList, defaultAmbient); + partList++; + } +} + +void LegacyOpenGLRenderer::AdjustAmbient(OpenGLShader &shader, float intensity) +{ + shader.SetFloat("ambient", intensity); +} + +void LegacyOpenGLRenderer::ApplyView() +{ + glm::mat4 glMatrix = ToFloatMat(viewParams->viewMatrix); + + worldShader->Use(); + worldShader->SetMat4("view", glMatrix); + __glCheckErrors(); +} + +void LegacyOpenGLRenderer::Clear() +{ + glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void LegacyOpenGLRenderer::Draw(OpenGLShader &shader, const CBSPPart &part, float defaultAmbient) +{ + OpenGLVertices *glData = dynamic_cast(part.vData.get()); + + if (glData == nullptr) return; + + glBindVertexArray(glData->opaque.vertexArray); + glBindBuffer(GL_ARRAY_BUFFER, glData->opaque.vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, glData->opaque.glDataSize, glData->opaque.glData.data(), GL_STREAM_DRAW); + + // Position! + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), 0); + glEnableVertexAttribArray(0); + + // RGBAColor! + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + + // Normal! + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)(7 * sizeof(float))); + glEnableVertexAttribArray(2); + + // Custom, per-object lighting and depth testing! + float extraAmbient = ToFloat(part.extraAmbient); + if (part.privateAmbient != -1) { + AdjustAmbient(shader, ToFloat(part.privateAmbient)); + } + if (extraAmbient > 0) { + AdjustAmbient(shader, defaultAmbient + extraAmbient); + } + if (part.ignoreDepthTesting) { + glDisable(GL_DEPTH_TEST); + } + if (part.ignoreDirectionalLights) { + IgnoreDirectionalLights(shader, true); + __glCheckErrors(); + } + + SetTransforms(part); + shader.Use(); + __glCheckErrors(); + + glDrawArrays(GL_TRIANGLES, 0, glData->opaque.pointCount); + + glBindVertexArray(glData->alpha.vertexArray); + glBindBuffer(GL_ARRAY_BUFFER, glData->alpha.vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, glData->alpha.glDataSize, glData->alpha.glData.data(), GL_STREAM_DRAW); + + // Position! + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), 0); + glEnableVertexAttribArray(0); + + // RGBAColor! + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + + // Normal! + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(GLData), (void *)(7 * sizeof(float))); + glEnableVertexAttribArray(2); + glDrawArrays(GL_TRIANGLES, 0, glData->alpha.pointCount); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + __glCheckErrors(); + + // Restore previous lighting and depth testing state. + if (part.privateAmbient != -1 || extraAmbient > 0) { + AdjustAmbient(shader, defaultAmbient); + __glCheckErrors(); + } + if (part.ignoreDepthTesting) { + glEnable(GL_DEPTH_TEST); + } + if (part.ignoreDirectionalLights) { + IgnoreDirectionalLights(shader, false); + __glCheckErrors(); + } + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + __glCheckErrors(); +} + +void LegacyOpenGLRenderer::IgnoreDirectionalLights(OpenGLShader &shader, bool yn) +{ + shader.SetFloat("lightsActive", (yn) ? 0.0f : 1.0f); +} + +std::unique_ptr LegacyOpenGLRenderer::LoadShader(const std::string &vertFile, + const std::string &fragFile) +{ + std::optional vertPath, fragPath; + + vertPath = AssetManager::GetShaderPath(vertFile); + fragPath = AssetManager::GetShaderPath(fragFile); + + if (!vertPath || !fragPath) { + SDL_Log("Failed to find shader (%s, %s)", vertFile.c_str(), fragFile.c_str()); + exit(1); + } + + return std::make_unique(*vertPath, *fragPath); +} + +void LegacyOpenGLRenderer::SetTransforms(const CBSPPart &part) { + glm::mat4 mv = ToFloatMat(part.fullTransform); + if (part.hasScale) { + glm::vec3 sc = glm::vec3( + ToFloat(part.scale[0]), + ToFloat(part.scale[1]), + ToFloat(part.scale[2]) + ); + mv = glm::scale(mv, sc); + } + + glm::mat3 normalMat = glm::mat3(1.0f); + for (int i = 0; i < 3; i ++) { + normalMat[0][i] = ToFloat((part.itsTransform)[0][i]); + normalMat[1][i] = ToFloat((part.itsTransform)[1][i]); + normalMat[2][i] = ToFloat((part.itsTransform)[2][i]); + } + + worldShader->Use(); + worldShader->SetMat4("modelview", mv); + worldShader->SetMat3("normalTransform", normalMat, true); +} diff --git a/src/render/LegacyOpenGLRenderer.h b/src/render/LegacyOpenGLRenderer.h new file mode 100644 index 00000000..e8e4ff06 --- /dev/null +++ b/src/render/LegacyOpenGLRenderer.h @@ -0,0 +1,52 @@ +#pragma once + +#include "AbstractRenderer.h" +#include "CBSPPart.h" +#include "CBSPWorld.h" +#include "OpenGLShader.h" +#include "VertexData.h" + +#include + +#include + +class LegacyOpenGLRenderer final: public AbstractRenderer { +public: + LegacyOpenGLRenderer(SDL_Window *window); + virtual ~LegacyOpenGLRenderer() override; + + virtual void AddHUDPart(CBSPPart *part) override { + part->ignoreDirectionalLights = true; + part->ignoreDepthTesting = true; + AddPart(part); + }; + + virtual void AddPart(CBSPPart *part) override; + virtual void ApplyLights() override; + virtual void ApplyProjection() override; + virtual void LevelReset() override; + virtual std::unique_ptr NewVertexDataInstance() override; + virtual void OverheadPoint(Fixed *pt, Fixed *extent) override; + virtual void RefreshWindow() override; + virtual void RemoveHUDPart(CBSPPart *part) override { RemovePart(part); }; + virtual void RemovePart(CBSPPart *part) override; + virtual void RenderFrame() override; +private: + SDL_Window *window; + + CBSPWorldImpl *dynamicWorld; + std::unique_ptr skyShader; + std::unique_ptr worldShader; + + GLsizei resolution[2]; + GLuint skyBuffer; + GLuint skyVertArray; + + void AdjustAmbient(OpenGLShader &shader, float intensity); + void ApplyView(); + void Clear(); + void Draw(OpenGLShader &shader, const CBSPPart &part, float defaultAmbient); + void IgnoreDirectionalLights(OpenGLShader &shader, bool yn); + std::unique_ptr LoadShader(const std::string &vertFile, const std::string &fragFile); + void SetTransforms(const CBSPPart &part); +}; diff --git a/src/render/OpenGLVertices.h b/src/render/OpenGLVertices.h index 2c82de0a..33e0367a 100644 --- a/src/render/OpenGLVertices.h +++ b/src/render/OpenGLVertices.h @@ -36,6 +36,7 @@ struct GLVertexCollection { class OpenGLVertices: public VertexData { public: friend class ModernOpenGLRenderer; + friend class LegacyOpenGLRenderer; OpenGLVertices(); virtual ~OpenGLVertices(); From b4bd6fc2aecd9b21f971dea6390542f4aa0a6030 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 20 Jun 2024 18:02:05 -0600 Subject: [PATCH 02/25] kill some spectator frags A bunch of spectator frags were being caused by processing the spectator's scout controls and/or not respecting the scout controls of players still in the game. --- src/game/CAbstractPlayer.cpp | 6 ++++-- src/game/CGoody.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/game/CAbstractPlayer.cpp b/src/game/CAbstractPlayer.cpp index d3db6b3b..cd804cf8 100644 --- a/src/game/CAbstractPlayer.cpp +++ b/src/game/CAbstractPlayer.cpp @@ -1384,10 +1384,12 @@ void CAbstractPlayer::KeyboardControl(FunctionTable *ft) { } } - // Disable scout controls while spectating - if (winFrame < 0 && !freeView && itsGame->GetSpectatePlayer() == NULL) { + // Disable local scout controls while spectating (players in the game still control their scouts) + if (winFrame < 0 && !itsManager->IsDeadOrDone()) { Boolean doRelease = false; +// SDL_Log("keys> fn=%d: down=%08x, held=%08x, up=%08x\n", +// itsGame->frameNumber, ft->down, ft->held, ft->up); if (TESTFUNC(kfuScoutView, ft->down)) { if (!scoutView && !scoutIdent) { doRelease = true; diff --git a/src/game/CGoody.cpp b/src/game/CGoody.cpp index 138ec3a6..77cae8ee 100644 --- a/src/game/CGoody.cpp +++ b/src/game/CGoody.cpp @@ -202,6 +202,6 @@ void CGoody::FrameAction() { // the goody heading can make a difference in determing a collision with a Hector // FRandSeed += heading; UpdateFRandSeed((uint32_t)heading); - // SDL_Log("fn = %ld, goody=%ld: heading = %8d, FRandSeed = %10d\n", + // SDL_Log("fn = %d, goody=%ld: heading = %8d, FRandSeed = %10d\n", // itsGame->frameNumber, ident, heading, (Fixed)FRandSeed); } From ee26fdd834c9038e856722932dd07fdc45e2d210 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 20 Jun 2024 20:19:50 -0600 Subject: [PATCH 03/25] output an estimate of CPU usage with `/dbg cpu` --- src/game/CAvaraApp.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/game/CAvaraApp.cpp b/src/game/CAvaraApp.cpp index 78c52d47..3eb8ef51 100755 --- a/src/game/CAvaraApp.cpp +++ b/src/game/CAvaraApp.cpp @@ -159,11 +159,24 @@ void CAvaraAppImpl::Done() { } void CAvaraAppImpl::idle() { + static double avg = 0; + FrameNumber curFrame = itsGame->frameNumber; + uint32_t procTime = SDL_GetTicks(); + CheckSockets(); TrackerUpdate(); if (itsGame->GameTick()) { RenderContents(); } + + // output a coarse estimate of cpu time & percent every second when enabled + if (curFrame > 0 && curFrame != itsGame->frameNumber && Debug::IsEnabled("cpu")) { + procTime = SDL_GetTicks() - procTime; + avg = 0.99*avg + 0.01*procTime; + if (curFrame % (1000/itsGame->frameTime) == 0) { + DBG_Log("cpu", "%.1fms (%.0f%%)\n", avg, 100.0*avg/itsGame->frameTime); + } + } } void CAvaraAppImpl::drawContents() { From f95ca3e07a8ab4136588dcc9ff156bccbb8dd7d2 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Sat, 22 Jun 2024 14:59:05 -0600 Subject: [PATCH 04/25] more frag fixes The crux of abort/frag problems is/was that the notification of the abort was asynchronous. But the other players in the game don't need to get the async message because they will get the abort key press so stop sending that message to active players. Also, there's no good reason to keep updating the frag checksum for players who are out of the game. This was also related to the previous problem where players were adding in the location checksum and stopping that addition at different times. This fix is really extra insurance that an aborted or dead player won't cause frags. --- src/game/CGlowActors.cpp | 13 +++++++++---- src/game/CPlayerManager.cpp | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/game/CGlowActors.cpp b/src/game/CGlowActors.cpp index 9124c3f7..03876505 100644 --- a/src/game/CGlowActors.cpp +++ b/src/game/CGlowActors.cpp @@ -65,9 +65,14 @@ void CGlowActors::FrameAction() { } } - // both the server and client update the FRandSeed here, if they get out of sync + // all clients update their FRandSeed here, if they get out of sync // then this number will be different and will be noticed in CNetManager::AutoLatencyControl - uint32_t locsum = location[0] + location[1] + location[2]; - UpdateFRandSeed(locsum); - // SDL_Log("frameNumber = %u, FRandSeed = %10d, locsum = %8d, Actor = %s", gCurrentGame->frameNumber, (Fixed)FRandSeed, locsum, typeid(*this).name()); + uint32_t locsum = 0; + if (maskBits & kSolidBit) { + // only add "solid" objects to the checksum because the location of + // non-solid objects (invisible/limbo/dead) isn't relevant and could cause frags + uint32_t locsum = location[0] + location[1] + location[2]; + UpdateFRandSeed(locsum); + } +// SDL_Log("fn = %u, FRandSeed = %10d, locsum = %8d, Actor = %s", gCurrentGame->frameNumber, (Fixed)FRandSeed, locsum, typeid(*this).name()); } diff --git a/src/game/CPlayerManager.cpp b/src/game/CPlayerManager.cpp index bff1bdef..7fa5b987 100644 --- a/src/game/CPlayerManager.cpp +++ b/src/game/CPlayerManager.cpp @@ -1134,7 +1134,9 @@ void CPlayerManagerImpl::AbortRequest() { void CPlayerManagerImpl::RemoveFromGame() { theNetManager->activePlayersDistribution &= ~(1 << slot); - theNetManager->itsCommManager->SendUrgentPacket(kdEveryone, kpRemoveMeFromGame, 0, 0, 0, 0, 0); + // let inactive players know (not sure if this is even necessary) + uint16_t dist = kdEveryone & ~theNetManager->activePlayersDistribution; + theNetManager->itsCommManager->SendPacket(dist, kpRemoveMeFromGame, 0, 0, 0, 0, 0); } void CPlayerManagerImpl::DeadOrDone() { From 2534cb7b65ba10583b86c05b3e7f4355551c8ee3 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Mon, 24 Jun 2024 17:09:25 -0600 Subject: [PATCH 05/25] local variable scope bug, oops --- src/game/CGlowActors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/CGlowActors.cpp b/src/game/CGlowActors.cpp index 03876505..32b80c3d 100644 --- a/src/game/CGlowActors.cpp +++ b/src/game/CGlowActors.cpp @@ -71,7 +71,7 @@ void CGlowActors::FrameAction() { if (maskBits & kSolidBit) { // only add "solid" objects to the checksum because the location of // non-solid objects (invisible/limbo/dead) isn't relevant and could cause frags - uint32_t locsum = location[0] + location[1] + location[2]; + locsum = location[0] + location[1] + location[2]; UpdateFRandSeed(locsum); } // SDL_Log("fn = %u, FRandSeed = %10d, locsum = %8d, Actor = %s", gCurrentGame->frameNumber, (Fixed)FRandSeed, locsum, typeid(*this).name()); From 0bf4dbbad4da39a490fcf5f88243a0377245ceb9 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Tue, 25 Jun 2024 12:56:14 -0600 Subject: [PATCH 06/25] default Goody angle to zero In fact, all objects with "angle" would just pull the last value parsed, for any object. This will set it to zero if not set in the current ALF node. --- src/game/CGoody.cpp | 4 ++-- src/level/LevelLoader.cpp | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/game/CGoody.cpp b/src/game/CGoody.cpp index 77cae8ee..c891e265 100644 --- a/src/game/CGoody.cpp +++ b/src/game/CGoody.cpp @@ -202,6 +202,6 @@ void CGoody::FrameAction() { // the goody heading can make a difference in determing a collision with a Hector // FRandSeed += heading; UpdateFRandSeed((uint32_t)heading); - // SDL_Log("fn = %d, goody=%ld: heading = %8d, FRandSeed = %10d\n", - // itsGame->frameNumber, ident, heading, (Fixed)FRandSeed); + // SDL_Log("fn = %d, goody=%ld: heading = %8d, FRandSeed = %10d, grenades=%d, missiles=%d\n", + // itsGame->frameNumber, ident, heading, (Fixed)FRandSeed, grenades, missiles); } diff --git a/src/level/LevelLoader.cpp b/src/level/LevelLoader.cpp index 1c638c89..05223231 100644 --- a/src/level/LevelLoader.cpp +++ b/src/level/LevelLoader.cpp @@ -267,6 +267,9 @@ struct ALFWalker: pugi::xml_tree_walker { lastArcAngle = (900 - arcAngle) % 360; lastDomeAngle = 360 - arcAngle; + } else { + lastArcAngle = 0; + lastDomeAngle = 0; } return true; From ba57a49dc45cc8a0061aebeaea0b7bfe1fe9f44b Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Tue, 25 Jun 2024 14:59:47 -0600 Subject: [PATCH 07/25] /dbg frag output all the checksum info to help debug frags --- src/game/CAbstractPlayer.cpp | 2 +- src/game/CGlowActors.cpp | 4 +++- src/game/CGoody.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/game/CAbstractPlayer.cpp b/src/game/CAbstractPlayer.cpp index cd804cf8..c53bd3d9 100644 --- a/src/game/CAbstractPlayer.cpp +++ b/src/game/CAbstractPlayer.cpp @@ -1574,7 +1574,7 @@ void CAbstractPlayer::FrameAction() { } // if a frag frame is specified with /dbg, force a frag on that frame by messing with FRandSeed - int fragFrame = Debug::GetValue("frag"); + int fragFrame = Debug::GetValue("ffrag"); if (fragFrame > 0 && itsGame->frameNumber == fragFrame) { extern Fixed FRandSeed; // to intentionally cause frags below FRandSeed += 1; diff --git a/src/game/CGlowActors.cpp b/src/game/CGlowActors.cpp index 32b80c3d..60e5eff7 100644 --- a/src/game/CGlowActors.cpp +++ b/src/game/CGlowActors.cpp @@ -10,6 +10,7 @@ #include "CGlowActors.h" #include "CSmartPart.h" +#include "Debug.h" extern Fixed FRandSeed; @@ -74,5 +75,6 @@ void CGlowActors::FrameAction() { locsum = location[0] + location[1] + location[2]; UpdateFRandSeed(locsum); } -// SDL_Log("fn = %u, FRandSeed = %10d, locsum = %8d, Actor = %s", gCurrentGame->frameNumber, (Fixed)FRandSeed, locsum, typeid(*this).name()); + DBG_Log("frag", "fn=%u, FRandSeed=%11d, locsum=%8d, Actor=%s", + gCurrentGame->frameNumber, (Fixed)FRandSeed, locsum, typeid(*this).name()); } diff --git a/src/game/CGoody.cpp b/src/game/CGoody.cpp index c891e265..516f8643 100644 --- a/src/game/CGoody.cpp +++ b/src/game/CGoody.cpp @@ -15,6 +15,7 @@ #include "CWallActor.h" #include "GoodyRecord.h" #include "Preferences.h" +#include "Debug.h" #define kGoodySound 250 @@ -202,6 +203,7 @@ void CGoody::FrameAction() { // the goody heading can make a difference in determing a collision with a Hector // FRandSeed += heading; UpdateFRandSeed((uint32_t)heading); - // SDL_Log("fn = %d, goody=%ld: heading = %8d, FRandSeed = %10d, grenades=%d, missiles=%d\n", - // itsGame->frameNumber, ident, heading, (Fixed)FRandSeed, grenades, missiles); + DBG_Log("frag", "fn=%d, FRandSeed=%11d, heading=%7d, goody=%ld, grenades=%d, missiles=%d\n", + + itsGame->frameNumber, (Fixed)FRandSeed, heading, ident, grenades, missiles); } From 10e900e483b1953b1b0fe62fce02822de713ad09 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Tue, 25 Jun 2024 20:36:22 -0600 Subject: [PATCH 08/25] fixed koth field --- levels/avaraline-quirks-mode/alf/heir-apparent.alf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/levels/avaraline-quirks-mode/alf/heir-apparent.alf b/levels/avaraline-quirks-mode/alf/heir-apparent.alf index 4f4855a5..f7127cc3 100644 --- a/levels/avaraline-quirks-mode/alf/heir-apparent.alf +++ b/levels/avaraline-quirks-mode/alf/heir-apparent.alf @@ -4,11 +4,11 @@ From 3b002eadd9cc12ba64117e3271ff268d4313aaa0 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Tue, 25 Jun 2024 23:32:53 -0600 Subject: [PATCH 09/25] try taking the hill logo out of the equation the solid logo in the middle of the hill seems to be the only difference between this level and the other levels that don't frag. Temporarily elevating them out of the way. Also I used Holograms for some but the Hologram doesn't respect "pitch". If Holograms work, we might consider adding pitch to CHologramActor similar to CSolidActor. --- levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf index 2be3d802..e3b0cc3e 100644 --- a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf +++ b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf @@ -214,10 +214,10 @@ - - - - + + + + From b69e3393925305b7578926d90d243108fd63970e Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Wed, 26 Jun 2024 15:43:27 -0600 Subject: [PATCH 10/25] reverting last change for further build/testing --- levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf index e3b0cc3e..2be3d802 100644 --- a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf +++ b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf @@ -214,10 +214,10 @@ - - - - + + + + From 0cbe0bd3e2cd399f635e8aa147f668d972e0e31c Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 27 Jun 2024 00:44:57 -0600 Subject: [PATCH 11/25] output dbg as BSPs are loaded /dbg bsp --> outputs the bounds for all BSPs as they are loaded /dbg bsp 666 --> outputs bounds plus all points for BSP 666 --- src/bsp/CBSPPart.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bsp/CBSPPart.cpp b/src/bsp/CBSPPart.cpp index b0335dad..9fe3bff5 100644 --- a/src/bsp/CBSPPart.cpp +++ b/src/bsp/CBSPPart.cpp @@ -14,6 +14,7 @@ #include "AvaraDefines.h" #include "CViewParameters.h" #include "Memory.h" +#include "Debug.h" #include #include @@ -38,7 +39,7 @@ CBSPPart *CBSPPart::Create(short resId) { void CBSPPart::IBSPPart(short resId) { - //SDL_Log("Loading BSP: %s\n", bspName); + DBG_Log("bsp", "Loading BSP: %d\n", resId); lightSeed = 0; nextTemp = NULL; // colorReplacements = NULL; // Use default colors. @@ -101,6 +102,10 @@ void CBSPPart::IBSPPart(short resId) { maxBounds.z = ToFixed(mxZ); maxBounds.w = FIX1; + DBG_Log("bsp", " bounds.x = [%d, %d]\n", minBounds.x, maxBounds.x); + DBG_Log("bsp", " bounds.y = [%d, %d]\n", minBounds.y, maxBounds.y); + DBG_Log("bsp", " bounds.z = [%d, %d]\n", minBounds.z, maxBounds.z); + origColorTable = std::make_unique(colorCount); currColorTable = std::make_unique(colorCount); pointTable = std::make_unique(pointCount); @@ -123,12 +128,16 @@ void CBSPPart::IBSPPart(short resId) { CheckForAlpha(); + // if command is "/dbg bsp 666" then show points for resId 666 + bool showPoints = (Debug::GetValue("bsp") == resId); + if (showPoints) { DBG_Log("bsp", " points:\n"); } for (uint32_t i = 0; i < pointCount; i++) { nlohmann::json pt = doc["points"][i]; pointTable[i][0] = ToFixed(pt[0]); pointTable[i][1] = ToFixed(pt[1]); pointTable[i][2] = ToFixed(pt[2]); pointTable[i][3] = FIX1; + if (showPoints) { DBG_Log("bsp", " %s\n", FormatVector(pointTable[i]).c_str()); } } totalPoints = 0; From 9af88b0f977f09627abbb4a2519bdb0c54400715 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 27 Jun 2024 15:20:05 -0600 Subject: [PATCH 12/25] added attribute to Hologram --- levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf | 8 ++++---- src/game/CHologramActor.cpp | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf index 2be3d802..046cb3a8 100644 --- a/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf +++ b/levels/avaraline-quirks-mode/alf/layout/heir-apparent.alf @@ -214,10 +214,10 @@ - - - - + + + + diff --git a/src/game/CHologramActor.cpp b/src/game/CHologramActor.cpp index 5e8e58ae..79ca90da 100644 --- a/src/game/CHologramActor.cpp +++ b/src/game/CHologramActor.cpp @@ -19,6 +19,7 @@ void CHologramActor::BeginScript() { ProgramLongVar(iShape, 600); ProgramVariable(iRoll, 0); + ProgramVariable(iPitch, 0); ProgramLongVar(iIsAmbient, false); } @@ -39,6 +40,7 @@ CAbstractActor *CHologramActor::EndScript() { thePart->userFlags |= CBSPUserFlags::kIsAmbient; thePart->Reset(); thePart->RotateZ(ReadFixedVar(iRoll)); + thePart->RotateX(ReadFixedVar(iPitch)); thePart->RotateOneY(heading); TranslatePart(thePart, location[0], location[1], location[2]); thePart->MoveDone(); From 2be0190771323e89715a986d945bab2fefe2e33f Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Mon, 1 Jul 2024 23:16:55 -0600 Subject: [PATCH 13/25] fixed RayTest calls to use the fps ray distances Also fixed a minor bug with how grenade radius was being added to classic instead of fps ray distance. Not sure this will make any noticeable difference but it should be a faster calculation since it searches a smaller volume on each frame. --- src/game/CGrenade.cpp | 10 ++++++---- src/game/CSmart.cpp | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/game/CGrenade.cpp b/src/game/CGrenade.cpp index f53aea57..abbf1ff8 100644 --- a/src/game/CGrenade.cpp +++ b/src/game/CGrenade.cpp @@ -145,13 +145,15 @@ void CGrenade::FrameAction() { // | / // |G <- approaching wall at 45° needs sqrt(2)*radius to intersect wall... lower angles might pass collision instead static Fixed addRad = sqrt(2)*partList[0]->enclosureRadius; - Fixed classicRayLength = NormalizeVector(3, rayHit.direction) + addRad; - rayHit.distance = classicRayLength; + // use the smaller FPS-scaled rayLength to search a much smaller volume + Fixed rayLength = FpsCoefficient2(NormalizeVector(3, rayHit.direction)) + addRad; + rayHit.distance = rayLength; RayTestWithGround(&rayHit, kSolidBit); // if the grenade path (ray test) intersects with an object during this frame - Fixed classicRayDistance = ClassicCoefficient2(rayHit.distance); - if (classicRayLength > classicRayDistance) { + if (rayLength > rayHit.distance) { + // scale distance up to Classic so it's backed out properly when adding to location + Fixed classicRayDistance = ClassicCoefficient2(rayHit.distance); speed[0] = FMul(rayHit.direction[0], classicRayDistance); speed[1] = FMul(rayHit.direction[1], classicRayDistance); speed[2] = FMul(rayHit.direction[2], classicRayDistance); diff --git a/src/game/CSmart.cpp b/src/game/CSmart.cpp index f125d081..c78d0000 100644 --- a/src/game/CSmart.cpp +++ b/src/game/CSmart.cpp @@ -320,8 +320,8 @@ if (IsClassicInterval()) { // indented like this because hope to remove it in th rayHit.closestHit = NULL; rayHit.distance = NormalizeVector(3, rayHit.direction); - realSpeed = rayHit.distance; + rayHit.distance = FpsCoefficient2(rayHit.distance); RayTestWithGround(&rayHit, kSolidBit); @@ -336,7 +336,7 @@ if (IsClassicInterval()) { // indented like this because hope to remove it in th } if (rayHit.closestHit) { - realSpeed = rayHit.distance; + realSpeed = ClassicCoefficient2(rayHit.distance); speed[0] = FMul(rayHit.direction[0], realSpeed); speed[1] = FMul(rayHit.direction[1], realSpeed); speed[2] = FMul(rayHit.direction[2], realSpeed); From a6a45fc34fec4e0ad64ce6a94e5623bf01a44529 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Wed, 3 Jul 2024 01:03:48 -0600 Subject: [PATCH 14/25] backout changes to missile since smart missile movement is still interpolated, the RayTest needs to stay in the classic units. --- src/game/CSmart.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game/CSmart.cpp b/src/game/CSmart.cpp index c78d0000..27721b0a 100644 --- a/src/game/CSmart.cpp +++ b/src/game/CSmart.cpp @@ -321,7 +321,6 @@ if (IsClassicInterval()) { // indented like this because hope to remove it in th rayHit.distance = NormalizeVector(3, rayHit.direction); realSpeed = rayHit.distance; - rayHit.distance = FpsCoefficient2(rayHit.distance); RayTestWithGround(&rayHit, kSolidBit); @@ -336,7 +335,7 @@ if (IsClassicInterval()) { // indented like this because hope to remove it in th } if (rayHit.closestHit) { - realSpeed = ClassicCoefficient2(rayHit.distance); + realSpeed = rayHit.distance; speed[0] = FMul(rayHit.direction[0], realSpeed); speed[1] = FMul(rayHit.direction[1], realSpeed); speed[2] = FMul(rayHit.direction[2], realSpeed); From 75f1ede9a017bbc123947f7b61069479581d1899 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Wed, 3 Jul 2024 14:45:37 -0600 Subject: [PATCH 15/25] make FramesFromNow() accept a float arg Well, this is embarrassing. Most of the time FrameFromNow() is passed an integer. But there's an important call that passes a float on this check: while (FramesFromNow(latencyTolerance) > topSentFrame) { itsNet->FrameAction(); } Passing latencyTolerance as an integer was truncating the LT. For example, an LT=1.5 would be treated as LT=1 and result in less than the full LT of frames being sent on time. --- src/game/CAvaraGame.cpp | 2 +- src/game/CAvaraGame.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game/CAvaraGame.cpp b/src/game/CAvaraGame.cpp index 04a0f3f7..f1a0436f 100755 --- a/src/game/CAvaraGame.cpp +++ b/src/game/CAvaraGame.cpp @@ -1135,7 +1135,7 @@ void CAvaraGame::IncrementFrame(bool firstFrame) { isClassicFrame = (frameNumber % (CLASSICFRAMETIME / frameTime) == 0); } -FrameNumber CAvaraGame::FramesFromNow(FrameNumber classicFrameCount) { +FrameNumber CAvaraGame::FramesFromNow(double classicFrameCount) { return frameNumber + classicFrameCount / fpsScale; } diff --git a/src/game/CAvaraGame.h b/src/game/CAvaraGame.h index 91eb6058..feeab9cc 100644 --- a/src/game/CAvaraGame.h +++ b/src/game/CAvaraGame.h @@ -269,7 +269,7 @@ class CAvaraGame { virtual FrameNumber NextFrameForPeriod(long period, long referenceFrame = 0); virtual void SetFrameTime(int32_t ft); virtual void IncrementFrame(bool firstFrame = false); - virtual FrameNumber FramesFromNow(FrameNumber classicFrames); + virtual FrameNumber FramesFromNow(double classicFrames); virtual void SetSpawnOrder(SpawnOrder order); void SetKeysFromStdin() { keysFromStdin = true; }; From ceca44edcb723ff62993f564db56b3e9a416cf99 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Wed, 10 Jul 2024 01:41:51 -0600 Subject: [PATCH 16/25] consistent grenade/missile/plasma shooting rates (#417) * fix FramesFromNow usage many frame number checks were of the form if (doSomething < frameNumber) { doSomething = FrameFromNow(3) } For classic mode that means to doSomething at most every 4th frame. But for high-FPS, this code would start doing something on frame 13 instead of on frame 16... faster than classic. Changing code to work across frame rates like this: if (doSomething <= frameNumber) { doSomething = FrameFromNow(3+1) } * fix tests and bugs found by tests The HECTOR.Boost* tests had to be fixed because they were actually boosting on frame 1 since boostEndFrame inits to 0. * added 1 classic-frame limiter to the plasma shot so that all frame rates behave the same on shooting rate --- src/game/CAbstractPlayer.cpp | 22 ++++++++++++---------- src/game/CAbstractPlayer.h | 1 + src/game/CBall.cpp | 8 ++++---- src/tests.cpp | 18 +++++++++--------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/game/CAbstractPlayer.cpp b/src/game/CAbstractPlayer.cpp index c53bd3d9..b433a0f7 100644 --- a/src/game/CAbstractPlayer.cpp +++ b/src/game/CAbstractPlayer.cpp @@ -175,6 +175,7 @@ void CAbstractPlayer::StartSystems() { nextGrenadeLoad = 0; nextMissileLoad = 0; + nextPlasmaShot = 0; // variables in AdaptableSettings need to have "classic" counterparts in case they are changed in CWalkerActor::ReceiveConfig() classicGeneratorPower = FIX3(30); @@ -1144,12 +1145,12 @@ void CAbstractPlayer::ArmSmartMissile() { } if (!didDetach && oldKind != kweSmart && missileCount) { - if (nextMissileLoad < itsGame->frameNumber) { + if (nextMissileLoad <= itsGame->frameNumber) { theWeapon = itsGame->itsDepot->AquireWeapon(kweSmart); weaponIdent = theWeapon->Arm(viewPortPart); if (weaponIdent) { missileCount--; - nextMissileLoad = itsGame->FramesFromNow(3); + nextMissileLoad = itsGame->FramesFromNow(4); } } else fireGun = false; @@ -1177,12 +1178,12 @@ void CAbstractPlayer::ArmGrenade() { } if (!didDetach && oldKind != kweGrenade && grenadeCount) { - if (nextGrenadeLoad < itsGame->frameNumber) { + if (nextGrenadeLoad <= itsGame->frameNumber) { theWeapon = itsGame->itsDepot->AquireWeapon(kweGrenade); weaponIdent = theWeapon->Arm(viewPortPart); if (weaponIdent) { grenadeCount--; - nextGrenadeLoad = itsGame->FramesFromNow(2); + nextGrenadeLoad = itsGame->FramesFromNow(3); } } else fireGun = false; @@ -1294,11 +1295,11 @@ void CAbstractPlayer::KeyboardControl(FunctionTable *ft) { FPS_DEBUG(" motors after keyb = " << FormatVector(motors, 2) << std::endl); - if (TESTFUNC(kfuBoostEnergy, ft->down) && boostsRemaining && (boostEndFrame < itsGame->frameNumber)) { + if (TESTFUNC(kfuBoostEnergy, ft->down) && boostsRemaining && (boostEndFrame <= itsGame->frameNumber)) { CBasicSound *theSound; boostsRemaining--; - boostEndFrame = itsGame->FramesFromNow(BOOSTLENGTH); + boostEndFrame = itsGame->FramesFromNow(BOOSTLENGTH+1); if (!boostControlLink) boostControlLink = gHub->GetSoundLink(); @@ -1715,7 +1716,8 @@ void CAbstractPlayer::GunActions() { if (weapon) { weapon->Fire(); weaponIdent = 0; - } else { + } else if (nextPlasmaShot <= itsGame->frameNumber) { + nextPlasmaShot = itsGame->FramesFromNow(1); i = gunEnergy[0] < gunEnergy[1]; if (gunEnergy[i] >= activeGunEnergy) { Vector missileSpeed; @@ -1986,7 +1988,7 @@ bool CAbstractPlayer::ReincarnateComplete(CIncarnator* newSpot) { LinkPartSpheres(); if (reEnergize) { - boostEndFrame = itsGame->FramesFromNow(MINIBOOSTTIME); + boostEndFrame = itsGame->FramesFromNow(MINIBOOSTTIME+1); reEnergize = false; if (shields < maxShields) shields = maxShields; @@ -2299,10 +2301,10 @@ void CAbstractPlayer::TakeGoody(GoodyRecord *gr) { if (energy > maxEnergy) energy = maxEnergy; - if (gr->boostTime > 0 && (boostEndFrame < itsGame->frameNumber)) { + if (gr->boostTime > 0 && (boostEndFrame <= itsGame->frameNumber)) { CBasicSound *theSound; - boostEndFrame = itsGame->FramesFromNow(gr->boostTime); + boostEndFrame = itsGame->FramesFromNow(gr->boostTime+1); if (!boostControlLink) boostControlLink = gHub->GetSoundLink(); diff --git a/src/game/CAbstractPlayer.h b/src/game/CAbstractPlayer.h index 769dbcfb..1ab4488b 100644 --- a/src/game/CAbstractPlayer.h +++ b/src/game/CAbstractPlayer.h @@ -83,6 +83,7 @@ class CAbstractPlayer : public CRealMovers { short grenadeCount = 0; FrameNumber nextGrenadeLoad = 0; FrameNumber nextMissileLoad = 0; + FrameNumber nextPlasmaShot = 0; short missileLimit = 0; short grenadeLimit = 0; diff --git a/src/game/CBall.cpp b/src/game/CBall.cpp index 73b54d73..509c3072 100644 --- a/src/game/CBall.cpp +++ b/src/game/CBall.cpp @@ -298,7 +298,7 @@ void CBall::MagnetAction() { Vector snapTo; CSmartPart *newHost; - if ((theActor->ident != oldHost || looseFrame < thisFrame) && + if ((theActor->ident != oldHost || looseFrame <= thisFrame) && !(theActor->maskBits & kPlayerBit && holdShieldLimit < shields && theActor->teamColor != teamColor)) { snapCode = theActor->GetBallSnapPoint(group, location, snapTo, localSnap, &newHost); @@ -495,7 +495,7 @@ void CBall::FrameAction() { SecondaryDamage(teamColor, -1, ksiObjectCollision); return; // *** return after dispose! *** case kDoRelease: - looseFrame = itsGame->FramesFromNow(100); + looseFrame = itsGame->FramesFromNow(101); oldHost = hostIdent; case kDoReset: { CSmartPart *savedHost; @@ -532,7 +532,7 @@ long CBall::ReceiveSignal(long theSignal, void *miscData) { case kBallReleaseSignal: { CSmartPart *theBall = partList[0]; - looseFrame = itsGame->FramesFromNow(32); + looseFrame = itsGame->FramesFromNow(33); oldHost = hostIdent; ReleaseAttachment(); speed[0] += FMul(pitchZ, theBall->itsTransform[2][0]) + FMul(pitchY, theBall->itsTransform[1][0]); @@ -563,7 +563,7 @@ void CBall::ReleaseDamage(Fixed hitEnergy) { if (hostPart && playerAttach) { releaseHoldAccumulator += hitEnergy; if (releaseHoldAccumulator > dropDamage) { - looseFrame = itsGame->FramesFromNow(10); + looseFrame = itsGame->FramesFromNow(11); oldHost = hostIdent; ReleaseAttachment(); } diff --git a/src/tests.cpp b/src/tests.cpp index 488ee0e2..533ee878 100644 --- a/src/tests.cpp +++ b/src/tests.cpp @@ -468,11 +468,11 @@ vector HectorEnergyRegen(int steps, bool useBoost, int fra int ticksPerStep = GetTicksPerStep(frameTime); scenario.hector->energy = scenario.hector->maxEnergy * 0.5; - if (useBoost) { - scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); - } for (int i = 0; i < steps; i++) { + if (i == 1 && useBoost) { + scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); + } HectorEnergyReadings current(scenario.hector); energyValues.push_back(current); for (int k = 0; k < ticksPerStep; k++) { @@ -490,11 +490,11 @@ vector HectorPlasmaRegen(int steps, bool useBoost, int fra scenario.hector->gunEnergy[0] = 0; scenario.hector->gunEnergy[1] = 0; - if (useBoost) { - scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); - } for (int i = 0; i < steps; i++) { + if (i == 1 && useBoost) { + scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); + } HectorEnergyReadings current(scenario.hector); energyValues.push_back(current); for (int k = 0; k < ticksPerStep; k++) { @@ -511,11 +511,11 @@ vector HectorShieldRegen(int steps, bool useBoost, int fra int ticksPerStep = GetTicksPerStep(frameTime); scenario.hector->shields = scenario.hector->maxShields * 0.5; - if (useBoost) { - scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); - } for (int i = 0; i < steps; i++) { + if (i == 1 && useBoost) { + scenario.hector->itsManager->GetFunctions()->down = (1 << kfuBoostEnergy); + } HectorEnergyReadings current(scenario.hector); energyValues.push_back(current); for (int k = 0; k < ticksPerStep; k++) { From c54dc288c020fbb8d7cca5b8674fa51124a0e06f Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Fri, 9 Aug 2024 11:45:49 -0600 Subject: [PATCH 17/25] fix level-loading crashes When new levels are loaded the Hull and Level/Set menus are re-created from scratch. This would leave dangling pointers in nanogui::Screen (mDragWidget and mFocusPath). The "best" fix would probably be to replace all Widget pointers with shared_ptr. But this is a quick fix that gets the job done. I also added "/dbg rload X" which will load a new level every X seconds. This was useful for recreating this bug but also might be useful for discovering other random level-load issues in the future so I left it in. --- src/game/CAvaraGame.cpp | 11 +++++++++++ src/game/CAvaraGame.h | 1 + vendor/nanogui/nanogui/screen.h | 2 ++ vendor/nanogui/nanogui/widget.h | 13 +++++++++++-- vendor/nanogui/screen.cpp | 15 +++++++++++++-- vendor/nanogui/widget.cpp | 11 +++++++++++ 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/game/CAvaraGame.cpp b/src/game/CAvaraGame.cpp index f1a0436f..55143d95 100755 --- a/src/game/CAvaraGame.cpp +++ b/src/game/CAvaraGame.cpp @@ -142,6 +142,7 @@ void CAvaraGame::IAvaraGame(CAvaraApp *theApp) { statusRequest = kNoVehicleStatus; nextPingTime = 0; + nextLoadTime = 0; showNewHUD = gApplication ? gApplication->Get(kShowNewHUD) : false; // CalcGameRect(); @@ -843,6 +844,16 @@ bool CAvaraGame::GameTick() { nextPingTime = startTime + pingInterval; } + int randLoadPeriod = Debug::GetValue("rload"); // randomly load a level every `rload` seconds + if (randLoadPeriod > 0) { + if (startTime > nextLoadTime) { + auto p = CPlayerManagerImpl::LocalPlayer(); + auto *tui = itsApp->GetTui(); + tui->ExecuteMatchingCommand("/rand", p); + nextLoadTime = startTime + 1000*randLoadPeriod; + } + } + // Not playing? Nothing to do! if (statusRequest != kPlayingStatus) return false; diff --git a/src/game/CAvaraGame.h b/src/game/CAvaraGame.h index feeab9cc..8d6d3ac3 100644 --- a/src/game/CAvaraGame.h +++ b/src/game/CAvaraGame.h @@ -179,6 +179,7 @@ class CAvaraGame { uint32_t nextScheduledFrame; uint32_t nextPingTime; + uint32_t nextLoadTime; long lastFrameTime; Boolean canPreSend; diff --git a/vendor/nanogui/nanogui/screen.h b/vendor/nanogui/nanogui/screen.h index e7b58dfd..6cee1638 100644 --- a/vendor/nanogui/nanogui/screen.h +++ b/vendor/nanogui/nanogui/screen.h @@ -188,6 +188,8 @@ class NANOGUI_EXPORT Screen : public Widget { void moveWindowToFront(Window *window); void drawWidgets(); + virtual void removeNotifyParent(const Widget *w) override; + protected: SDL_Window *mSDLWindow; uint32_t mWindowID; diff --git a/vendor/nanogui/nanogui/widget.h b/vendor/nanogui/nanogui/widget.h index f36f491a..242d15fd 100755 --- a/vendor/nanogui/nanogui/widget.h +++ b/vendor/nanogui/nanogui/widget.h @@ -63,10 +63,16 @@ class NANOGUI_EXPORT Widget : public Object { /// Set the position relative to the parent widget void setPosition(const Vector2i &pos) { mPos = pos; } + /// return position of parent or zeros if no parent + Vector2i parentPosition() const { + static Vector2i zeroPos = {}; + return mParent ? + (mParent->absolutePosition()) : zeroPos; + } + /// Return the absolute position on screen Vector2i absolutePosition() const { - return mParent ? - (parent()->absolutePosition() + mPos) : mPos; + return parentPosition() + mPos; } /// Return the size of the widget @@ -148,6 +154,9 @@ class NANOGUI_EXPORT Widget : public Object { /// Remove a child widget by value void removeChild(const Widget *widget); + /// notify other widgets/parents + virtual void removeNotifyParent(const Widget *w); + /// Retrieves the child at the specific position const Widget* childAt(int index) const { return mChildren[index]; } diff --git a/vendor/nanogui/screen.cpp b/vendor/nanogui/screen.cpp index b500da0f..b0fa5fe6 100755 --- a/vendor/nanogui/screen.cpp +++ b/vendor/nanogui/screen.cpp @@ -444,7 +444,7 @@ bool Screen::cursorPosCallbackEvent(double x, double y) { } } else { ret = mDragWidget->mouseDragEvent( - p - mDragWidget->parent()->absolutePosition(), p - mMousePos, + p - mDragWidget->parentPosition(), p - mMousePos, mMouseState, mModifiers); } @@ -482,7 +482,7 @@ bool Screen::mouseButtonCallbackEvent(int button, int action, int modifiers) { if (mDragActive && action == SDL_RELEASED && dropWidget != mDragWidget) mDragWidget->mouseButtonEvent( - mMousePos - mDragWidget->parent()->absolutePosition(), button, + mMousePos - mDragWidget->parentPosition(), button, false, mModifiers); if (dropWidget != nullptr && dropWidget->cursor() != mCursor) { @@ -694,4 +694,15 @@ bool Screen::handleSDLEvent(SDL_Event &event) { return false; } + +void Screen::removeNotifyParent(const Widget *w) { + // remove widget from various places before deletion + if (w == mDragWidget) { + mDragWidget = nullptr; + mDragActive = false; + } + mFocusPath.erase(std::remove(mFocusPath.begin(), mFocusPath.end(), w), mFocusPath.end()); + Widget::removeNotifyParent(w); +} + NAMESPACE_END(nanogui) diff --git a/vendor/nanogui/widget.cpp b/vendor/nanogui/widget.cpp index 5553751d..e980afc4 100755 --- a/vendor/nanogui/widget.cpp +++ b/vendor/nanogui/widget.cpp @@ -160,10 +160,21 @@ void Widget::removeChild(const Widget *widget) { void Widget::removeChild(int index) { Widget *widget = mChildren[index]; + removeNotifyParent(widget); mChildren.erase(mChildren.begin() + index); widget->decRef(); } +// climb the tree and let everyone (Screen) know this object is being removed. +// ideally we would change everything to use shared_ptr and maybe some weak_ptr references... +// but, i'm lazy and this code should all get replaced soon (right?) +void Widget::removeNotifyParent(const Widget *w) { + if (mParent) { + mParent->removeNotifyParent(w); + } + return; +} + int Widget::childIndex(Widget *widget) const { auto it = std::find(mChildren.begin(), mChildren.end(), widget); if (it == mChildren.end()) From 1f26642b4bfa8ebfa3582f9b6ce71c54ef7c6b11 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Fri, 9 Aug 2024 12:27:09 -0600 Subject: [PATCH 18/25] fixed compiler warnings introduced by previous fix --- vendor/nanogui/nanogui/screen.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/nanogui/nanogui/screen.h b/vendor/nanogui/nanogui/screen.h index 6cee1638..74bdc3ae 100644 --- a/vendor/nanogui/nanogui/screen.h +++ b/vendor/nanogui/nanogui/screen.h @@ -119,10 +119,10 @@ class NANOGUI_EXPORT Screen : public Widget { virtual bool dropEvent(const std::vector & /* filenames */) { return false; /* To be overridden */ } /// Default keyboard event handler - virtual bool keyboardEvent(int key, int scancode, int action, int modifiers); + virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) override; /// Text input event handler: codepoint is native endian UTF-32 format - virtual bool keyboardCharacterEvent(unsigned int codepoint); + virtual bool keyboardCharacterEvent(unsigned int codepoint) override; /// Window resize event handler virtual bool resizeEvent(const Vector2i& size); From 4193ac2930a6aaf39fc0b772428965e4b35102e7 Mon Sep 17 00:00:00 2001 From: Ymihere03 Date: Mon, 12 Aug 2024 18:40:33 -0600 Subject: [PATCH 19/25] Add function to allow the FrameBuffer to be updated on the fly Update the FrameBuffer during the Window Resize event --- src/game/CAvaraApp.cpp | 1 + src/render/AbstractRenderer.h | 5 +++++ src/render/ModernOpenGLRenderer.cpp | 11 +++++++++++ src/render/ModernOpenGLRenderer.h | 1 + src/render/NullRenderer.h | 1 + 5 files changed, 19 insertions(+) diff --git a/src/game/CAvaraApp.cpp b/src/game/CAvaraApp.cpp index 3eb8ef51..e74feec3 100755 --- a/src/game/CAvaraApp.cpp +++ b/src/game/CAvaraApp.cpp @@ -208,6 +208,7 @@ void CAvaraAppImpl::RenderContents() { void CAvaraAppImpl::WindowResized(int width, int height) { gRenderer->UpdateViewRect(width, height, mPixelRatio); + gRenderer->ApplyFrameBuffer(); //performLayout(); } diff --git a/src/render/AbstractRenderer.h b/src/render/AbstractRenderer.h index 563e35a5..89262b8c 100644 --- a/src/render/AbstractRenderer.h +++ b/src/render/AbstractRenderer.h @@ -39,6 +39,11 @@ class AbstractRenderer { */ virtual void ApplyProjection() = 0; + /** + * Update the frame buffer with the currently configured resolution and FOV. + */ + virtual void ApplyFrameBuffer() = 0; + /** * Reset the renderer's state back to its defaults. */ diff --git a/src/render/ModernOpenGLRenderer.cpp b/src/render/ModernOpenGLRenderer.cpp index 0b11a7f7..e32ce64c 100644 --- a/src/render/ModernOpenGLRenderer.cpp +++ b/src/render/ModernOpenGLRenderer.cpp @@ -252,6 +252,9 @@ void ModernOpenGLRenderer::ApplyProjection() { SDL_GL_GetDrawableSize(this->window, &resolution[0], &resolution[1]); + //viewParams->viewPixelDimensions.h = resolution[0]; + //viewParams->viewPixelDimensions.v = resolution[1]; + glm::mat4 proj = glm::scale( glm::perspective( glm::radians(fov), @@ -275,6 +278,14 @@ void ModernOpenGLRenderer::ApplyProjection() glCheckErrors(); } +void ModernOpenGLRenderer::ApplyFrameBuffer() +{ + GLsizei w, h; + SDL_GL_GetDrawableSize(window, &w, &h); + MakeFramebuffer(0, w, h); + MakeFramebuffer(1, w, h); +} + void ModernOpenGLRenderer::LevelReset() { dynamicWorld->DisposeParts(); diff --git a/src/render/ModernOpenGLRenderer.h b/src/render/ModernOpenGLRenderer.h index 5bc65a6a..ca813916 100644 --- a/src/render/ModernOpenGLRenderer.h +++ b/src/render/ModernOpenGLRenderer.h @@ -19,6 +19,7 @@ class ModernOpenGLRenderer final: public AbstractRenderer { virtual void AddPart(CBSPPart *part) override; virtual void ApplyLights() override; virtual void ApplyProjection() override; + virtual void ApplyFrameBuffer() override; virtual void LevelReset() override; virtual std::unique_ptr NewVertexDataInstance() override; virtual void OverheadPoint(Fixed *pt, Fixed *extent) override; diff --git a/src/render/NullRenderer.h b/src/render/NullRenderer.h index 4d5cb23f..a73e7b81 100644 --- a/src/render/NullRenderer.h +++ b/src/render/NullRenderer.h @@ -14,6 +14,7 @@ class NullRenderer final: public AbstractRenderer { virtual void AddPart(CBSPPart *part) override {}; virtual void ApplyLights() override {}; virtual void ApplyProjection() override {}; + virtual void ApplyFrameBuffer() override {}; virtual std::unique_ptr NewVertexDataInstance() override { return nullptr; }; From d3d5fc5e5ac0da862ecdfaa5acba092c91bf8ec5 Mon Sep 17 00:00:00 2001 From: Ymihere03 Date: Mon, 12 Aug 2024 18:46:32 -0600 Subject: [PATCH 20/25] Remove comment lines --- src/render/ModernOpenGLRenderer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/render/ModernOpenGLRenderer.cpp b/src/render/ModernOpenGLRenderer.cpp index e32ce64c..a4b4a68d 100644 --- a/src/render/ModernOpenGLRenderer.cpp +++ b/src/render/ModernOpenGLRenderer.cpp @@ -252,9 +252,6 @@ void ModernOpenGLRenderer::ApplyProjection() { SDL_GL_GetDrawableSize(this->window, &resolution[0], &resolution[1]); - //viewParams->viewPixelDimensions.h = resolution[0]; - //viewParams->viewPixelDimensions.v = resolution[1]; - glm::mat4 proj = glm::scale( glm::perspective( glm::radians(fov), From 17117420d29cbde0b3ee2ef555e0deee10da3c57 Mon Sep 17 00:00:00 2001 From: Ymihere03 Date: Tue, 13 Aug 2024 00:56:39 -0600 Subject: [PATCH 21/25] Refactored window resize handling to prevent memory leaks when updating the GL FBO Window Resize event is ignored if the resolution didn't actually change The HUD bump magnitude is a lot lower when damage taken is low --- src/game/CAbstractPlayer.cpp | 17 +++++++---------- src/game/CAvaraApp.cpp | 5 +++-- src/render/AbstractRenderer.h | 7 +------ src/render/ModernOpenGLRenderer.cpp | 19 ++++++++++++------- src/render/ModernOpenGLRenderer.h | 4 ++-- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/game/CAbstractPlayer.cpp b/src/game/CAbstractPlayer.cpp index b433a0f7..831a53c5 100644 --- a/src/game/CAbstractPlayer.cpp +++ b/src/game/CAbstractPlayer.cpp @@ -562,7 +562,7 @@ void CAbstractPlayer::LoadDashboardParts() { dashboardSpinSpeed = ToFixed(100); dashboardSpinHeading = 0; - int layout = itsGame->itsApp->Get(kHUDPreset); + layout = itsGame->itsApp->Get(kHUDPreset); //float alpha = itsGame->itsApp->Get(kHUDAlpha); //Fixed hudAlpha = FIX1 * alpha; @@ -718,21 +718,22 @@ void CAbstractPlayer::RenderDashboard() { // Lastly set relativeImpulse based on the impact location of the hit to bump the HUD Fixed hitAngle = FOneArcTan2(dSpeed[2], dSpeed[0]); Fixed angleDiff = hitAngle - viewYaw; + float magnitude = ToFloat(VectorLength(3, dSpeed)); if (angleDiff > 0) { // Hit from the right side - relativeImpulse[0] = FIX(1.0); + relativeImpulse[0] = FIX(1.0*magnitude); } else if (angleDiff < 0) { // Hit from the left side - relativeImpulse[0] = FIX(-1.0); + relativeImpulse[0] = FIX(-1.0*magnitude); } if (dSpeed[1] > FIX(.5)) { // Hit from the top - relativeImpulse[1] = FIX(1.0); + relativeImpulse[1] = FIX(1.0*magnitude); } else if (dSpeed[1] < FIX(-.5)) { // Hit from the bottom - relativeImpulse[1] = FIX(-1.0); + relativeImpulse[1] = FIX(-1.0*magnitude); } pidReset(&pMotionX); @@ -924,11 +925,7 @@ void CAbstractPlayer::DashboardPosition(CScaledBSP *part, bool autoRot, float x, // X/Y Coordinates on the screen are roughly described as a percentage of the screen away from the bottom and the left // (-1.0, -1.0) is the bottom left of the screen // (1.0, 1.0) is the top right of the screen - - - // TODO: Adjust these until it looks good, then go multiply - // all the DashboardPosition parameters by these numbers, and - // then delete these + float scale_x = 11.12; float scale_y = 8.23; Fixed hud_dist = (FIX3(6000) * 25)/8; diff --git a/src/game/CAvaraApp.cpp b/src/game/CAvaraApp.cpp index e74feec3..ac534be1 100755 --- a/src/game/CAvaraApp.cpp +++ b/src/game/CAvaraApp.cpp @@ -207,8 +207,9 @@ void CAvaraAppImpl::RenderContents() { } void CAvaraAppImpl::WindowResized(int width, int height) { - gRenderer->UpdateViewRect(width, height, mPixelRatio); - gRenderer->ApplyFrameBuffer(); + // Only update if the resolution is actually changing + if (gRenderer->viewParams->viewPixelDimensions.h != width || gRenderer->viewParams->viewPixelDimensions.v != height) + gRenderer->UpdateViewRect(width, height, mPixelRatio); //performLayout(); } diff --git a/src/render/AbstractRenderer.h b/src/render/AbstractRenderer.h index 89262b8c..97d9ced1 100644 --- a/src/render/AbstractRenderer.h +++ b/src/render/AbstractRenderer.h @@ -39,11 +39,6 @@ class AbstractRenderer { */ virtual void ApplyProjection() = 0; - /** - * Update the frame buffer with the currently configured resolution and FOV. - */ - virtual void ApplyFrameBuffer() = 0; - /** * Reset the renderer's state back to its defaults. */ @@ -108,7 +103,7 @@ class AbstractRenderer { * @param height The height in pixels. * @param pixelRatio The pixel ratio. */ - void UpdateViewRect(int width, int height, float pixelRatio); + virtual void UpdateViewRect(int width, int height, float pixelRatio) = 0; protected: float fov = 50.0f; }; diff --git a/src/render/ModernOpenGLRenderer.cpp b/src/render/ModernOpenGLRenderer.cpp index a4b4a68d..488eacb1 100644 --- a/src/render/ModernOpenGLRenderer.cpp +++ b/src/render/ModernOpenGLRenderer.cpp @@ -177,9 +177,6 @@ ModernOpenGLRenderer::ModernOpenGLRenderer(SDL_Window *window) : AbstractRendere glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); - MakeFramebuffer(0, w, h); - MakeFramebuffer(1, w, h); - // Configure alpha blending. glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE); @@ -275,12 +272,15 @@ void ModernOpenGLRenderer::ApplyProjection() glCheckErrors(); } -void ModernOpenGLRenderer::ApplyFrameBuffer() +void ModernOpenGLRenderer::UpdateViewRect(int width, int height, float pixelRatio) { + AbstractRenderer::UpdateViewRect(width, height, pixelRatio); + GLsizei w, h; SDL_GL_GetDrawableSize(window, &w, &h); - MakeFramebuffer(0, w, h); - MakeFramebuffer(1, w, h); + + AdjustFramebuffer(0, w, h); + AdjustFramebuffer(1, w, h); } void ModernOpenGLRenderer::LevelReset() @@ -575,8 +575,13 @@ std::unique_ptr ModernOpenGLRenderer::LoadShader(const std::string return std::make_unique(*vertPath, *fragPath); } -void ModernOpenGLRenderer::MakeFramebuffer(short index, GLsizei width, GLsizei height) +void ModernOpenGLRenderer::AdjustFramebuffer(short index, GLsizei width, GLsizei height) { + // Remove previous bound objects + glDeleteTextures(1, &texture[index]); + glDeleteFramebuffers(1, &fbo[index]); + glDeleteRenderbuffers(1, &rbo[index]); + // Create a framebuffer, texture, and renderbuffer for the HUD. glGenFramebuffers(1, &fbo[index]); glBindFramebuffer(GL_FRAMEBUFFER, fbo[index]); diff --git a/src/render/ModernOpenGLRenderer.h b/src/render/ModernOpenGLRenderer.h index ca813916..42470304 100644 --- a/src/render/ModernOpenGLRenderer.h +++ b/src/render/ModernOpenGLRenderer.h @@ -19,7 +19,6 @@ class ModernOpenGLRenderer final: public AbstractRenderer { virtual void AddPart(CBSPPart *part) override; virtual void ApplyLights() override; virtual void ApplyProjection() override; - virtual void ApplyFrameBuffer() override; virtual void LevelReset() override; virtual std::unique_ptr NewVertexDataInstance() override; virtual void OverheadPoint(Fixed *pt, Fixed *extent) override; @@ -27,6 +26,7 @@ class ModernOpenGLRenderer final: public AbstractRenderer { virtual void RemoveHUDPart(CBSPPart *part) override; virtual void RemovePart(CBSPPart *part) override; virtual void RenderFrame() override; + virtual void UpdateViewRect(int width, int height, float pixelRatio) override; private: SDL_Window *window; @@ -55,6 +55,6 @@ class ModernOpenGLRenderer final: public AbstractRenderer { void Draw(OpenGLShader &shader, const CBSPPart &part, float defaultAmbient, bool useAlphaBuffer = false); void IgnoreDirectionalLights(OpenGLShader &shader, bool yn); std::unique_ptr LoadShader(const std::string &vertFile, const std::string &fragFile); - void MakeFramebuffer(short index, GLsizei width, GLsizei height); + void AdjustFramebuffer(short index, GLsizei width, GLsizei height); void SetTransforms(const CBSPPart &part); }; From 6aca44076417ecff54f9bea66ad16252e5fed035 Mon Sep 17 00:00:00 2001 From: Ymihere03 Date: Tue, 13 Aug 2024 01:12:39 -0600 Subject: [PATCH 22/25] Fix test class --- src/render/NullRenderer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/render/NullRenderer.h b/src/render/NullRenderer.h index a73e7b81..4d5cb23f 100644 --- a/src/render/NullRenderer.h +++ b/src/render/NullRenderer.h @@ -14,7 +14,6 @@ class NullRenderer final: public AbstractRenderer { virtual void AddPart(CBSPPart *part) override {}; virtual void ApplyLights() override {}; virtual void ApplyProjection() override {}; - virtual void ApplyFrameBuffer() override {}; virtual std::unique_ptr NewVertexDataInstance() override { return nullptr; }; From 7ce62d7c277b43529010f0194986bcc0e0484494 Mon Sep 17 00:00:00 2001 From: Ymihere03 Date: Tue, 13 Aug 2024 01:18:42 -0600 Subject: [PATCH 23/25] Fix test class --- src/render/NullRenderer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/NullRenderer.h b/src/render/NullRenderer.h index 4d5cb23f..21f5bea5 100644 --- a/src/render/NullRenderer.h +++ b/src/render/NullRenderer.h @@ -32,4 +32,5 @@ class NullRenderer final: public AbstractRenderer { virtual void RemoveHUDPart(CBSPPart *part) override {}; virtual void RemovePart(CBSPPart *part) override {}; virtual void RenderFrame() override {}; + virtual void UpdateViewRect(int width, int height, float pixelRatio) override {}; }; From 1a54b4ab7d96e40c6bb539bb0b071e1d132d233b Mon Sep 17 00:00:00 2001 From: assertivist Date: Tue, 13 Aug 2024 01:32:12 -0700 Subject: [PATCH 24/25] merge with update to render api from fix for window resizing --- src/render/LegacyOpenGLRenderer.cpp | 6 ++++++ src/render/LegacyOpenGLRenderer.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/render/LegacyOpenGLRenderer.cpp b/src/render/LegacyOpenGLRenderer.cpp index aca92817..2876285e 100644 --- a/src/render/LegacyOpenGLRenderer.cpp +++ b/src/render/LegacyOpenGLRenderer.cpp @@ -227,6 +227,12 @@ void LegacyOpenGLRenderer::ApplyProjection() //glCheckErrors(); } +void LegacyOpenGLRenderer::UpdateViewRect(int width, int height, float pixelRatio) +{ + AbstractRenderer::UpdateViewRect(width, height, pixelRatio); + ApplyProjection(); +} + void LegacyOpenGLRenderer::LevelReset() { dynamicWorld->DisposeParts(); diff --git a/src/render/LegacyOpenGLRenderer.h b/src/render/LegacyOpenGLRenderer.h index e8e4ff06..cc83b19b 100644 --- a/src/render/LegacyOpenGLRenderer.h +++ b/src/render/LegacyOpenGLRenderer.h @@ -31,6 +31,7 @@ class LegacyOpenGLRenderer final: public AbstractRenderer { virtual void RemoveHUDPart(CBSPPart *part) override { RemovePart(part); }; virtual void RemovePart(CBSPPart *part) override; virtual void RenderFrame() override; + void UpdateViewRect(int width, int height, float pixelRatio) override; private: SDL_Window *window; From a2f9c7aa691cdb7f75bfbc801c3b7d710c8d0492 Mon Sep 17 00:00:00 2001 From: assertivist Date: Tue, 13 Aug 2024 02:19:31 -0700 Subject: [PATCH 25/25] add new files to msvc project --- Avara.msvc/AvaraCore.vcxproj | 2 ++ Avara.msvc/AvaraCore.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/Avara.msvc/AvaraCore.vcxproj b/Avara.msvc/AvaraCore.vcxproj index 9eb2f754..0885b47e 100644 --- a/Avara.msvc/AvaraCore.vcxproj +++ b/Avara.msvc/AvaraCore.vcxproj @@ -285,6 +285,7 @@ + @@ -476,6 +477,7 @@ + diff --git a/Avara.msvc/AvaraCore.vcxproj.filters b/Avara.msvc/AvaraCore.vcxproj.filters index 4da9feb9..404fa6e1 100644 --- a/Avara.msvc/AvaraCore.vcxproj.filters +++ b/Avara.msvc/AvaraCore.vcxproj.filters @@ -636,6 +636,9 @@ Header Files + + Header Files + @@ -1118,5 +1121,8 @@ Source Files + + Source Files + \ No newline at end of file