diff --git a/Hurrican/CMakeLists.txt b/Hurrican/CMakeLists.txt
index fdfbeacd..0f83298e 100644
--- a/Hurrican/CMakeLists.txt
+++ b/Hurrican/CMakeLists.txt
@@ -554,8 +554,19 @@ if (SDL2_FOUND)
include_directories(${SDL2_INCLUDE_DIR}
${SDL2_IMAGE_INCLUDE_DIR}
${SDL2_MIXER_INCLUDE_DIR})
-
- target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARY} ${SDL2_MIXER_LIBRARIES} ${SDL2_IMAGE_LIBRARIES})
+ set(_hur_sdl2 ${SDL2_LIBRARY})
+ set(_hur_mix ${SDL2_MIXER_LIBRARIES})
+ set(_hur_img ${SDL2_IMAGE_LIBRARIES})
+ if (TARGET SDL2::SDL2)
+ set(_hur_sdl2 SDL2::SDL2)
+ endif()
+ if (TARGET SDL2_mixer::SDL2_mixer)
+ set(_hur_mix SDL2_mixer::SDL2_mixer)
+ endif()
+ if (TARGET SDL2_image::SDL2_image)
+ set(_hur_img SDL2_image::SDL2_image)
+ endif()
+ target_link_libraries(${PROJECT_NAME} ${_hur_sdl2} ${_hur_mix} ${_hur_img})
else()
include_directories(${SDL_INCLUDE_DIR}
${SDL_IMAGE_INCLUDE_DIR}
diff --git a/Hurrican/darwin/Hurrican-Info.plist b/Hurrican/darwin/Hurrican-Info.plist
new file mode 100644
index 00000000..2908403e
--- /dev/null
+++ b/Hurrican/darwin/Hurrican-Info.plist
@@ -0,0 +1,44 @@
+
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ hurrican
+ CFBundleIdentifier
+ games.hurrican.Hurrican
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Hurrican
+ CFBundleDisplayName
+ Hurrican
+ CFBundleIconFile
+ Hurrican
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ @HURRICAN_BUNDLE_VERSION@
+ CFBundleVersion
+ @HURRICAN_BUNDLE_VERSION@
+ CFBundleSignature
+ ????
+ LSMinimumSystemVersion
+ 11.0
+
+ NSPrincipalClass
+ NSApplication
+ NSHighResolutionCapable
+
+ NSSupportsAutomaticGraphicsSwitching
+
+ LSApplicationCategoryType
+ public.app-category.games
+ NSHumanReadableCopyright
+ Freeware / OSS Hurrican contributors — see upstream.
+ ITSAppUsesNonExemptEncryption
+
+
+
diff --git a/Hurrican/darwin/Hurrican.icns b/Hurrican/darwin/Hurrican.icns
new file mode 100644
index 00000000..7cd41ef4
Binary files /dev/null and b/Hurrican/darwin/Hurrican.icns differ
diff --git a/Hurrican/darwin/make-icon.sh b/Hurrican/darwin/make-icon.sh
new file mode 100755
index 00000000..9944d05d
--- /dev/null
+++ b/Hurrican/darwin/make-icon.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+set -eu
+root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
+png2icns "$root/darwin/Hurrican.icns" "$root/data/textures/hurrican_rund.png"
diff --git a/Hurrican/data/shaders/320/shader_render.frag b/Hurrican/data/shaders/320/shader_render.frag
index 1efbc821..40763772 100644
--- a/Hurrican/data/shaders/320/shader_render.frag
+++ b/Hurrican/data/shaders/320/shader_render.frag
@@ -84,11 +84,11 @@ void main()
vec4 p;
if (c_color_bleed == 1) {
float pixel_size = 1.0/float(c_WindowWidth)*bleeding_range;
- vec4 current_color = texture2D(u_Texture0, xy) * color;
- vec4 color_left = texture2D(u_Texture0,vec2(xy.x-pixel_size, xy.y)) * color;
+ vec4 current_color = texture(u_Texture0, xy) * color;
+ vec4 color_left = texture(u_Texture0, vec2(xy.x-pixel_size, xy.y)) * color;
p = get_color_bleeding(current_color,color_left);
} else {
- p = texture2D(u_Texture0, xy) * color;
+ p = texture(u_Texture0, xy) * color;
}
/* Add scanlines */
diff --git a/Hurrican/src/DX8Graphics.cpp b/Hurrican/src/DX8Graphics.cpp
index cf2853ae..8dda9dc6 100644
--- a/Hurrican/src/DX8Graphics.cpp
+++ b/Hurrican/src/DX8Graphics.cpp
@@ -15,6 +15,7 @@
// --------------------------------------------------------------------------------------
#include "DX8Graphics.hpp"
+#include
#include
#include
#include
@@ -50,6 +51,7 @@ float DegreetoRad[360]; // Tabelle mit Rotationswerten
// --------------------------------------------------------------------------------------
DirectGraphicsClass::DirectGraphicsClass() {
+ glextensions = nullptr;
SupportedETC1 = false;
SupportedPVRTC = false;
use_shader = shader_t::COLOR;
@@ -57,6 +59,10 @@ DirectGraphicsClass::DirectGraphicsClass() {
#if defined(USE_GL2) || defined(USE_GL3)
ProgramCurrent = PROGRAM_NONE;
#endif
+#if defined(USE_GL3)
+ StreamVAO = 0;
+ StreamVBO = 0;
+#endif
}
// --------------------------------------------------------------------------------------
@@ -150,7 +156,14 @@ bool DirectGraphicsClass::Init(std::uint32_t dwBreite, std::uint32_t dwHoehe, st
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#else
+ /* Desktop GL — USE_GL2 / USE_GL3 (not GLES). */
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+#if defined(__APPLE__)
+ /* macOS: request GL 3.2 Core + forward-compatible (needed for GLSL 1.50 shaders). */
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+#endif
#endif /* defined(USE_GLES1) */
#endif /* SDL_VERSION_ATLEAST(2,0,0) */
#endif // !EGL
@@ -315,6 +328,16 @@ bool DirectGraphicsClass::Init(std::uint32_t dwBreite, std::uint32_t dwHoehe, st
// --------------------------------------------------------------------------------------
bool DirectGraphicsClass::Exit() {
+#if defined(USE_GL3)
+ if (StreamVBO != 0) {
+ glDeleteBuffers(1, &StreamVBO);
+ StreamVBO = 0;
+ }
+ if (StreamVAO != 0) {
+ glDeleteVertexArrays(1, &StreamVAO);
+ StreamVAO = 0;
+ }
+#endif
#if defined(USE_GL2) || defined(USE_GL3)
Shaders[PROGRAM_COLOR].Close();
Shaders[PROGRAM_TEXTURE].Close();
@@ -361,7 +384,10 @@ bool DirectGraphicsClass::SetDeviceInfo() {
Protokoll << "GL_SHADING_LANGUAGE_VERSION: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
#endif
glextensions = reinterpret_cast(glGetString(GL_EXTENSIONS));
- Protokoll << "GL_EXTENSIONS: " << glextensions << std::endl;
+ if (glextensions != nullptr)
+ Protokoll << "GL_EXTENSIONS: " << glextensions << std::endl;
+ else
+ Protokoll << "GL_EXTENSIONS: (unavailable in Core profile; glGetString(GL_EXTENSIONS) is null)" << std::endl;
#if defined(USE_ETC1)
SupportedETC1 = ExtensionSupported("GL_OES_compressed_ETC1_RGB8_texture");
@@ -418,7 +444,7 @@ bool DirectGraphicsClass::SetDeviceInfo() {
Shaders[PROGRAM_RENDER].AddConstant("c_WindowWidth", RenderRect.w);
Shaders[PROGRAM_RENDER].AddConstant("c_WindowHeight", RenderRect.h);
- Shaders[PROGRAM_RENDER].AddConstant("c_curvature", CommandLineParams.ScreenCurvature ? 1 : 0);
+ Shaders[PROGRAM_RENDER].AddConstant("c_curvature", CommandLineParams.ScreenCurvature ? 1 : 0);
Shaders[PROGRAM_RENDER].AddConstant("c_color_bleed", CommandLineParams.ColorBleed ? 1 : 0);
Shaders[PROGRAM_RENDER].AddConstant("c_scanlines", CommandLineParams.Scanlines ? 1 : 0);
Shaders[PROGRAM_RENDER].AddConstant("c_noise", CommandLineParams.ScreenNoise ? 1 : 0);
@@ -578,8 +604,8 @@ void DirectGraphicsClass::RendertoBuffer(GLenum PrimitiveType,
// Check if the program is already in use
if (ProgramCurrent != program_next) {
Shaders[program_next].Use();
- if (CommandLineParams.ScreenNoise && (program_next==PROGRAM_RENDER)) {
- glUniform1i(NameTime, 50*SDL_GetTicks()/1000);
+ if (CommandLineParams.ScreenNoise && (program_next == PROGRAM_RENDER)) {
+ glUniform1i(NameTime, 50 * SDL_GetTicks() / 1000);
}
ProgramCurrent = program_next;
}
@@ -598,6 +624,9 @@ void DirectGraphicsClass::RendertoBuffer(GLenum PrimitiveType,
return;
}
+ if (PrimitiveCount == 0)
+ return;
+
#if defined(USE_GL1)
// Enable the client states for transfer
if (use_shader == shader_t::TEXTURE) {
@@ -611,28 +640,47 @@ void DirectGraphicsClass::RendertoBuffer(GLenum PrimitiveType,
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, STRIDE, reinterpret_cast(pVertexStreamZeroData) + CLR_OFFSET);
#elif defined(USE_GL2) || defined(USE_GL3)
- // Enable attributes and uniforms for transfer
- if (is_texture) {
-#if defined(USE_ETC1)
- if (SupportedETC1) {
- glUniform1i(Shaders[ProgramCurrent].texUnit0, 0);
- glUniform1i(Shaders[ProgramCurrent].texUnit1, 1);
- }
+ void *ptrPos;
+ void *ptrClr;
+ void *ptrTex;
+#if defined(USE_GL3)
+ if (StreamVAO == 0) {
+ glGenVertexArrays(1, &StreamVAO);
+ glGenBuffers(1, &StreamVBO);
+ }
+ glBindVertexArray(StreamVAO);
+ glBindBuffer(GL_ARRAY_BUFFER, StreamVBO);
+ glBufferData(GL_ARRAY_BUFFER,
+ static_cast(PrimitiveCount) * static_cast(STRIDE),
+ pVertexStreamZeroData, GL_STREAM_DRAW);
+ ptrPos = reinterpret_cast(static_cast(0));
+ ptrClr = reinterpret_cast(static_cast(CLR_OFFSET));
+ ptrTex = reinterpret_cast(static_cast(TEX_OFFSET));
+#else
+ uint8_t *const vb = reinterpret_cast(pVertexStreamZeroData);
+ ptrPos = vb;
+ ptrClr = vb + CLR_OFFSET;
+ ptrTex = vb + TEX_OFFSET;
#endif
- glEnableVertexAttribArray(Shaders[ProgramCurrent].NameTex);
- glVertexAttribPointer(Shaders[ProgramCurrent].NameTex, 2, GL_FLOAT, GL_FALSE, STRIDE,
- reinterpret_cast(pVertexStreamZeroData) + TEX_OFFSET);
+ if (is_texture) {
+#if defined(USE_ETC1)
+ if (SupportedETC1) {
+ glUniform1i(Shaders[ProgramCurrent].texUnit0, 0);
+ glUniform1i(Shaders[ProgramCurrent].texUnit1, 1);
}
+#endif
+ glEnableVertexAttribArray(Shaders[ProgramCurrent].NameTex);
+ glVertexAttribPointer(Shaders[ProgramCurrent].NameTex, 2, GL_FLOAT, GL_FALSE, STRIDE, ptrTex);
+ }
- glEnableVertexAttribArray(Shaders[ProgramCurrent].NamePos);
- glVertexAttribPointer(Shaders[ProgramCurrent].NamePos, 2, GL_FLOAT, GL_FALSE, STRIDE, pVertexStreamZeroData);
+ glEnableVertexAttribArray(Shaders[ProgramCurrent].NamePos);
+ glVertexAttribPointer(Shaders[ProgramCurrent].NamePos, 2, GL_FLOAT, GL_FALSE, STRIDE, ptrPos);
- glEnableVertexAttribArray(Shaders[ProgramCurrent].NameClr);
- glVertexAttribPointer(Shaders[ProgramCurrent].NameClr, 4, GL_UNSIGNED_BYTE, GL_TRUE, STRIDE,
- reinterpret_cast(pVertexStreamZeroData) + CLR_OFFSET);
+ glEnableVertexAttribArray(Shaders[ProgramCurrent].NameClr);
+ glVertexAttribPointer(Shaders[ProgramCurrent].NameClr, 4, GL_UNSIGNED_BYTE, GL_TRUE, STRIDE, ptrClr);
- glm::mat4x4 matMVP = matProj * g_matModelView;
- glUniformMatrix4fv(Shaders[ProgramCurrent].NameMvp, 1, GL_FALSE, glm::value_ptr(matMVP));
+ glm::mat4x4 matMVP = matProj * g_matModelView;
+ glUniformMatrix4fv(Shaders[ProgramCurrent].NameMvp, 1, GL_FALSE, glm::value_ptr(matMVP));
#endif
glDrawArrays(PrimitiveType, 0, PrimitiveCount);
@@ -646,13 +694,11 @@ void DirectGraphicsClass::RendertoBuffer(GLenum PrimitiveType,
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
#elif defined(USE_GL2) || defined(USE_GL3)
- // Disable attributes and uniforms
- glDisableVertexAttribArray(Shaders[ProgramCurrent].NamePos);
- glDisableVertexAttribArray(Shaders[ProgramCurrent].NameClr);
-
- if (is_texture) {
- glDisableVertexAttribArray(Shaders[ProgramCurrent].NameTex);
- }
+ glDisableVertexAttribArray(Shaders[ProgramCurrent].NamePos);
+ glDisableVertexAttribArray(Shaders[ProgramCurrent].NameClr);
+ if (is_texture) {
+ glDisableVertexAttribArray(Shaders[ProgramCurrent].NameTex);
+ }
#endif
}
@@ -666,6 +712,11 @@ void DirectGraphicsClass::DisplayBuffer() {
}
bool DirectGraphicsClass::ExtensionSupported(const char *ext) {
+ if (glextensions == nullptr) {
+ Protokoll << ext << " is not supported (no GL_EXTENSIONS string in Core profile)" << std::endl;
+ return false;
+ }
+
if (strstr(glextensions, ext) != nullptr) {
Protokoll << ext << " is supported" << std::endl;
return true;
@@ -832,6 +883,8 @@ void DirectGraphicsClass::SetupFramebuffers() {
{
int tmp_w, tmp_h;
SDL_GetWindowSize(Window, &tmp_w, &tmp_h);
+ WindowView.x = 0;
+ WindowView.y = 0;
WindowView.w = tmp_w;
WindowView.h = tmp_h;
}
@@ -941,22 +994,22 @@ void DirectGraphicsClass::SelectBuffer(bool active) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
void DirectGraphicsClass::FlipSurface(SDL_Surface* surface) {
SDL_LockSurface(surface);
-
+
int pitch = surface->pitch; // row size
char* temp = new char[pitch]; // intermediate buffer
char* pixels = static_cast(surface->pixels);
-
+
for(int i = 0; i < surface->h / 2; ++i) {
// get pointers to the two rows to swap
char* row1 = pixels + i * pitch;
char* row2 = pixels + (surface->h - i - 1) * pitch;
-
+
// swap rows
memcpy(temp, row1, pitch);
memcpy(row1, row2, pitch);
memcpy(row2, temp, pitch);
}
-
+
delete[] temp;
SDL_UnlockSurface(surface);
diff --git a/Hurrican/src/DX8Graphics.hpp b/Hurrican/src/DX8Graphics.hpp
index c67093cc..a9be4c7b 100644
--- a/Hurrican/src/DX8Graphics.hpp
+++ b/Hurrican/src/DX8Graphics.hpp
@@ -101,6 +101,10 @@ class DirectGraphicsClass {
#if (defined(USE_GL2) || defined(USE_GL3)) && defined(USE_FBO)
CFbo RenderBuffer;
#endif
+#if defined(USE_GL3)
+ GLuint StreamVAO; /* GL 3 Core: VBO + VAO (no client-side vertex arrays) */
+ GLuint StreamVBO;
+#endif
public:
void ShowBackBuffer(); // Present aufrufen
diff --git a/Hurrican/src/Main.cpp b/Hurrican/src/Main.cpp
index 1b434b3b..c1153af4 100644
--- a/Hurrican/src/Main.cpp
+++ b/Hurrican/src/Main.cpp
@@ -72,10 +72,35 @@ TexturesystemClass Textures; // DKS - Added Texturesystem class (see DX8
DirectGraphicsClass DirectGraphics; // Grafik-Objekt
DirectInputClass DirectInput; // Input-Objekt
TimerClass Timer; // Timer Klasse für die Framerate
+
+/* Protokoll constructs before main(); keep log path writable */
+#if defined(USE_HOME_DIR)
+static std::string default_game_log_path() {
+ std::string base;
+ if (const char *state = getenv("XDG_STATE_HOME")) {
+ if (state[0] != '\0')
+ base = std::string(state) + "/hurrican";
+ }
+ if (base.empty()) {
+ if (const char *home = getenv("HOME")) {
+ if (home[0] != '\0')
+ base = std::string(home) + "/.local/state/hurrican";
+ }
+ }
+ if (base.empty())
+ return std::string("Game_Log.txt");
+ std::error_code ec;
+ fs::create_directories(base, ec);
+ return base + "/Game_Log.txt";
+}
+#endif
+
#if defined(__AROS__)
Logdatei Protokoll("T:Game_Log.txt"); // Protokoll Datei
+#elif defined(USE_HOME_DIR)
+Logdatei Protokoll(default_game_log_path());
#else
-Logdatei Protokoll("Game_Log.txt"); // Protokoll Datei
+Logdatei Protokoll("Game_Log.txt"); // Protokoll Datei (current directory)
#endif
SoundManagerClass SoundManager; // Sound Manager
DirectGraphicsFont *pDefaultFont = new (DirectGraphicsFont);
diff --git a/Hurrican/src/SDLPort/cfbo.cpp b/Hurrican/src/SDLPort/cfbo.cpp
index 83bf64e0..fd67af11 100644
--- a/Hurrican/src/SDLPort/cfbo.cpp
+++ b/Hurrican/src/SDLPort/cfbo.cpp
@@ -55,7 +55,13 @@ bool CFbo::Open(uint16_t w, uint16_t h) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+#if defined(USE_GLES2) || defined(USE_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, nullptr);
+#else
+ /* Desktop: RGB565 FBO color targets can read back black on Apple GL. */
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+#endif
/* Create the framebuffer reference */
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
@@ -100,6 +106,9 @@ bool CFbo::Open(uint16_t w, uint16_t h) {
void CFbo::BindTexture(bool active) {
if (active) {
+#if defined(USE_GL2) || defined(USE_GL3)
+ glActiveTexture(GL_TEXTURE0);
+#endif
glBindTexture(GL_TEXTURE_2D, texture);
#if defined(USE_GL1)
glEnable(GL_TEXTURE_2D);
diff --git a/Hurrican/src/SDLPort/cshader.cpp b/Hurrican/src/SDLPort/cshader.cpp
index 83ceded6..05b17913 100644
--- a/Hurrican/src/SDLPort/cshader.cpp
+++ b/Hurrican/src/SDLPort/cshader.cpp
@@ -108,7 +108,12 @@ GLuint CShader::CompileShader(GLenum type, const std::string &path) {
const std::string version = "#version 110\n";
source.insert(source.begin(), version.begin(), version.end());
#elif defined(USE_GL3)
+ /* macOS Core GL rejects #version 130; 150 core matches the shader sources. */
+#if defined(__APPLE__)
+ const std::string version = "#version 150 core\n";
+#else
const std::string version = "#version 130\n";
+#endif
source.insert(source.begin(), version.begin(), version.end());
#endif
@@ -246,25 +251,32 @@ GLint CShader::GetUniform(const std::string &uniform) {
}
void CShader::PrintLog(uint8_t type, GLuint shader) {
- GLint loglength;
+ GLint loglength = 0;
if (CHECK_FLAG(type, SHADER)) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &loglength);
-
- char *log = new char[loglength + 1];
- glGetShaderInfoLog(shader, loglength, nullptr, log);
- Protokoll << "Shader: Shader log:\n " << log << std::endl;
- delete[] log;
+ if (loglength > 0) {
+ std::vector log(static_cast(loglength) + 1, '\0');
+ glGetShaderInfoLog(shader, static_cast(log.size()), nullptr, log.data());
+ Protokoll << "Shader: Shader log:\n " << log.data() << std::endl;
+ } else {
+ Protokoll << "Shader: Shader log: " << std::endl;
+ }
}
if (CHECK_FLAG(type, PROGRAM)) {
glGetProgramiv(Program, GL_INFO_LOG_LENGTH, &loglength);
-
- char *log = new char[loglength + 1];
- glGetProgramInfoLog(Program, loglength, nullptr, log);
- Protokoll << "Shader: Program log:\n " << log << std::endl;
- delete[] log;
+ if (loglength > 0) {
+ std::vector log(static_cast(loglength) + 1, '\0');
+ glGetProgramInfoLog(Program, static_cast(log.size()), nullptr, log.data());
+ Protokoll << "Shader: Program log:\n " << log.data() << std::endl;
+ } else {
+ Protokoll << "Shader: Program log: " << std::endl;
+ }
}
+
+ /* Ensure failure diagnostics reach logfile before teardown (GL init can exit soon after). */
+ Protokoll.flush();
}
void CShader::AddConstant(std::string name, GLint value) {