From ad2175b9a64915c8bdc875bcdeae745bf6291bed Mon Sep 17 00:00:00 2001 From: Colton Staiduhar <29374798+arizotaz@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:22:16 -0500 Subject: [PATCH] FreeGlut MacOS Support --- CMakeLists.txt | 63 +++++- src/main.cpp | 550 +++++++++++++++++++++++++------------------------ 2 files changed, 342 insertions(+), 271 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cf4e73..89ae074 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,9 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # Declare Project where "GSM_TEMPLATE" is the project name project(GSM_TEMPLATE LANGUAGES C CXX) +# Use FreeGlut on Macos (Requires X11 to be installed, but doesn't use it) +option(USE_FREEGLUT "Use FreeGLUT on Mac (ignored on Windows)" ON) + # Allow cmake to fetch web repositories include(FetchContent) @@ -41,13 +44,28 @@ FetchContent_MakeAvailable(stb_image) # If using the Windows compiler -if (MSVC) +if (MSVC OR USE_FREEGLUT) + + if (APPLE) + + find_path(XQUARTZ_INCLUDE_DIR X11/Xlib.h + PATHS /opt/X11/include + NO_DEFAULT_PATH + ) + + if(NOT XQUARTZ_INCLUDE_DIR) + message(FATAL_ERROR "To use FreeGLUT on Mac, you need XQuartz installed. Please install it from https://www.xquartz.org/ or set the USE_FREEGLUT cache option to OFF") + endif() + + endif() + + set(CMAKE_POLICY_VERSION_MINIMUM 3.16) # Download freeglut FetchContent_Declare( freeglut GIT_REPOSITORY https://github.com/freeglut/freeglut.git - GIT_TAG "master" + GIT_TAG "v3.8.0" GIT_SHALLOW TRUE GIT_PROGRESS ON ) @@ -56,6 +74,11 @@ if (MSVC) set(FREEGLUT_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(FREEGLUT_BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) + # Force Cocca on Mac + if(APPLE) + set(FREEGLUT_USE_X11 OFF CACHE BOOL "" FORCE) + set(FREEGLUT_COCOA ON CACHE BOOL "" FORCE) + endif() FetchContent_MakeAvailable(freeglut) @@ -76,8 +99,8 @@ endif() # Tell Cmake to find the locations of libraries find_package(OpenGL REQUIRED) -# Only require glut if on macos -if (APPLE) +# Only require glut if on macos and not using freeglut +if (APPLE AND NOT USE_FREEGLUT) find_package(GLUT REQUIRED) endif() # Define MY_SOURCES to be a list of all the source files for my game @@ -117,6 +140,37 @@ target_sources("${CMAKE_PROJECT_NAME}" PRIVATE ${MY_SOURCES}) # If on mac if (APPLE) +# FreeGlut for mac +if (USE_FREEGLUT) + + +target_sources("${CMAKE_PROJECT_NAME}" PRIVATE "${glad_SOURCE_DIR}/src/glad.c") +target_include_directories("${CMAKE_PROJECT_NAME}" PUBLIC "${glad_SOURCE_DIR}/include") + +# Add our include files +target_include_directories("${CMAKE_PROJECT_NAME}" PUBLIC +"${CMAKE_CURRENT_SOURCE_DIR}/include/" +"${freeglut_SOURCE_DIR}/include" +"${glad_SOURCE_DIR}/include" +) + +target_compile_definitions("${CMAKE_PROJECT_NAME}" PUBLIC use_freeglut=TRUE) + +target_link_libraries("${CMAKE_PROJECT_NAME}" + PRIVATE + OpenGL::GL + freeglut_static + "-framework Cocoa" + "-framework IOKit" + "-framework CoreVideo" + "-lpthread" + stb_image + miniaudio +) + +# Native GLUT for Mac +else() + # Add our include files target_include_directories("${CMAKE_PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/") @@ -138,6 +192,7 @@ target_link_libraries("${CMAKE_PROJECT_NAME}" stb_image miniaudio ) +endif() # If on windows diff --git a/src/main.cpp b/src/main.cpp index eff8f41..faec625 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,68 +1,66 @@ -//############################################################################# -//# main.cpp -//############################################################################# -//# Written by Colton Staiduhar -//# Date Created: 02/03/2025 -//# Last Modification: 02/05/2025 -//############################################################################# -//# Main Entry point for Cmake project, declare by the int main() function -//############################################################################# -//# -//# This is a simple demonstration of GLUT functioning using STB_Image.H for -//# loading images and MiniAudio.h for playing audio. The repositories for -//# these libraries can be found below. -//# -//# Miniaudio -//# https://github.com/mackron/miniaudio -//# -//# STB_Image.h -//# https://github.com/arizotaz/cmake_stb_image -//# -//############################################################################# -//# -//# For GLUT/OpenGL, the operating system should have GLUT in some form already -//# installed. However, freeglut exists as the modern implementation of GLUT. -//# -//# GLUT -//# You're on your own for this, but here are some helpful implementation docs -//# MacOS: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_drawing/opengl_drawing.html -//# Windows & Linux: https://www.opengl.org/resources/libraries/glut/glut_downloads.php -//# -//# FreeGLUT: -//# https://freeglut.sourceforge.net/docs/api.php -//# -//############################################################################# -//# -//# This file will setup much of GLUT. Getting some output to the screen -//# The following is executed: -//# Create a windows with GLUT's API -//# Create a display update timer -//# Create Keyboard Interrupts -//# Load textures the included Texture.H file -//# Setup the audio engine from MiniAudio -//# Declare the Main Loop funcitons -//# Draw a sprite to the screen -//# Texture said sprite with an image -//# Rotate, move, and scale the sprite -//# Apply translations via user keyboard input -//# -//############################################################################# - - +// ############################################################################# +// # main.cpp +// ############################################################################# +// # Written by Colton Staiduhar +// # Date Created: 02/03/2025 +// # Last Modification: 02/05/2025 +// ############################################################################# +// # Main Entry point for Cmake project, declare by the int main() function +// ############################################################################# +// # +// # This is a simple demonstration of GLUT functioning using STB_Image.H for +// # loading images and MiniAudio.h for playing audio. The repositories for +// # these libraries can be found below. +// # +// # Miniaudio +// # https://github.com/mackron/miniaudio +// # +// # STB_Image.h +// # https://github.com/arizotaz/cmake_stb_image +// # +// ############################################################################# +// # +// # For GLUT/OpenGL, the operating system should have GLUT in some form already +// # installed. However, freeglut exists as the modern implementation of GLUT. +// # +// # GLUT +// # You're on your own for this, but here are some helpful implementation docs +// # MacOS: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_drawing/opengl_drawing.html +// # Windows & Linux: https://www.opengl.org/resources/libraries/glut/glut_downloads.php +// # +// # FreeGLUT: +// # https://freeglut.sourceforge.net/docs/api.php +// # +// ############################################################################# +// # +// # This file will setup much of GLUT. Getting some output to the screen +// # The following is executed: +// # Create a windows with GLUT's API +// # Create a display update timer +// # Create Keyboard Interrupts +// # Load textures the included Texture.H file +// # Setup the audio engine from MiniAudio +// # Declare the Main Loop funcitons +// # Draw a sprite to the screen +// # Texture said sprite with an image +// # Rotate, move, and scale the sprite +// # Apply translations via user keyboard input +// # +// ############################################################################# // Libraries #if defined(use_freeglut) -# include -# define FREEGLUT_STATIC -# include +#include +#define FREEGLUT_STATIC +#include #else -# include +#include #endif #include // STD -#include #include +#include #include // Custom includes @@ -76,14 +74,12 @@ void ProcessKeys(unsigned char, int, int); void ProcessSpecialKeys(int, int, int); void MainLoop(); -void GlutLoopTimer(int); // Extended Functionality void UpdateViewPort(); void DrawSprite(); void LoadUserTextures(); - // Miniaudio Engine Declaration void CreateAudioEngine(); ma_engine engine; // Miniaudio engine @@ -97,68 +93,10 @@ unsigned int texID; ma_sound musicSound; // Miniaudio sound object int lastWinW = 0, lastWinH = 0; -/** - * Main entry point of the application - */ -int main(int argc, char **argv) +bool running = true; +void CloseCallBack() { - std::cout << "Starting"; - fflush(stdout); - - int winW = 1280; - int winH = 720; - - // Initialize GLUT - glutInit(&argc, argv); - glutInitDisplayMode(GLUT_RGB); // RGB mode - - // Setup then create the window - glutInitWindowSize(winW, winH); // window size - glutInitWindowPosition(0, 0); - glutCreateWindow("Transform Example"); - - // This line only needs to run when glad is in use - #if defined(use_freeglut) - if (!gladLoadGL()) {} - #endif - - // Clear screen - glClearColor(0.0, 0.0, 0.0, 1.0); // clear the window screen - - // Sets up the viewport - UpdateViewPort(); - - // Calls the texture load method - LoadUserTextures(); - - // Setup Audio Engine - CreateAudioEngine(); - - // Play the music - ma_sound_start(&musicSound); - - // Deplare the Main Loop Function - // (Will be executed when glutPostRedisplay() is called in the timer) - glutDisplayFunc(MainLoop); - - // Declare Keyboard Interupts - glutKeyboardFunc(ProcessKeys); - - // Declare Special Keys Keyboard Interrupt - glutSpecialFunc(ProcessSpecialKeys); - - // Setup a timer to run the main loop at an interval - // (This call will call the GlutLoopTimer() function immediately) - glutTimerFunc(0, GlutLoopTimer, 0); - - std::cout << "Started"; - fflush(stdout); - // Start the Glut Loop (Calls the funtions above repeatedly) - glutMainLoop(); - - std::cout << "exited"; - fflush(stdout); - return 0; + running = false; } /** @@ -167,11 +105,96 @@ int main(int argc, char **argv) */ void GlutLoopTimer(int v) { - // Runs the function specified in glutDisplayFunc - glutPostRedisplay(); + // Runs the function specified in glutDisplayFunc + glutPostRedisplay(); - // Call the timer again in 16.7 seconds ~ 60 times per second - glutTimerFunc(16.7, GlutLoopTimer, v); // Creates a frame delay that is counted in miliseconds + // Call the timer again in 16.7 seconds ~ 60 times per second + glutTimerFunc(16.7, GlutLoopTimer, v); // Creates a frame delay that is counted in miliseconds +} + +/** + * Main entry point of the application + */ +int main(int argc, char** argv) +{ + std::cout << "Starting"; + fflush(stdout); + + int winW = 1280; + int winH = 720; + + // Initialize GLUT + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB); // RGB mode + + // Setup then create the window + glutInitWindowSize(winW, winH); // window size + glutInitWindowPosition(0, 0); + glutCreateWindow("Transform Example - Using Native Glut"); + +// This line only needs to run when glad is in use +#if defined(use_freeglut) + glutSetWindowTitle("Transform Example - using FreeGLUT"); + if (!gladLoadGL()) { } +#endif + + // Clear screen + glClearColor(0.0, 0.0, 0.0, 1.0); // clear the window screen + + // Sets up the viewport + UpdateViewPort(); + + // Calls the texture load method + LoadUserTextures(); + + // Setup Audio Engine + CreateAudioEngine(); + + // Play the music + ma_sound_start(&musicSound); + + // Deplare the Main Loop Function + // (Will be executed when glutPostRedisplay() is called in the timer) + glutDisplayFunc(MainLoop); + + // Declare Keyboard Interupts + glutKeyboardFunc(ProcessKeys); + + // Declare Special Keys Keyboard Interrupt + glutSpecialFunc(ProcessSpecialKeys); + +#ifndef use_freeglut + // Setup a timer to run the main loop at an interval + // (This call will call the GlutLoopTimer() function immediately) + glutTimerFunc(0, GlutLoopTimer, 0); +#endif + + std::cout << "Started" << std::endl; + fflush(stdout); + // Start the Glut Loop (Calls the funtions above repeatedly) + +#if defined(use_freeglut) + // FreeGlut allows for manual updating of the main loop + // So we can call the main loop in a while loop and tell the loop when to stop + + // Tell FreeGlut to do nothing on window X press + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); + + // Run callback when window X is pressed + glutCloseFunc(CloseCallBack); + + // Our loop, calls the funtion in glutDisplayFunc repeatedly until running = false + while (running) + glutMainLoopEvent(); + +#else + // Start the Glut Loop (Calls the funtions above repeatedly) + glutMainLoop(); +#endif + + std::cout << "exited" << std::endl; + fflush(stdout); + return 0; } /** @@ -179,20 +202,20 @@ void GlutLoopTimer(int v) */ void UpdateViewPort() { - // Gets the window size - int winW = glutGet(GLUT_WINDOW_WIDTH); - int winH = glutGet(GLUT_WINDOW_HEIGHT); + // Gets the window size + int winW = glutGet(GLUT_WINDOW_WIDTH); + int winH = glutGet(GLUT_WINDOW_HEIGHT); - // Sets up OpenGL Matrix Mode - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + // Sets up OpenGL Matrix Mode + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); - // Create projection type (Orthographic with size of window) - glOrtho(-winW / 2, winW / 2, winH / 2, -winH / 2, -1000.0f, 1000.0f); // Clipping plane is set to 1000 behind camera and 1000 infront, this works because ortho is cool + // Create projection type (Orthographic with size of window) + glOrtho(-winW / 2, winW / 2, winH / 2, -winH / 2, -1000.0f, 1000.0f); // Clipping plane is set to 1000 behind camera and 1000 infront, this works because ortho is cool - // Set last size to the new size - lastWinW = winW; - lastWinH = winH; + // Set last size to the new size + lastWinW = winW; + lastWinH = winH; } /** @@ -202,29 +225,25 @@ void UpdateViewPort() */ void ProcessSpecialKeys(int key, int x, int y) { - if (key == GLUT_KEY_LEFT) - { - moveX = -10; - movePressed = true; - } + if (key == GLUT_KEY_LEFT) { + moveX = -10; + movePressed = true; + } - if (key == GLUT_KEY_RIGHT) - { - moveX = 10; - movePressed = true; - } + if (key == GLUT_KEY_RIGHT) { + moveX = 10; + movePressed = true; + } - if (key == GLUT_KEY_UP) - { - moveY = 10; - movePressed = true; - } + if (key == GLUT_KEY_UP) { + moveY = 10; + movePressed = true; + } - if (key == GLUT_KEY_DOWN) - { - moveY = -10; - movePressed = true; - } + if (key == GLUT_KEY_DOWN) { + moveY = -10; + movePressed = true; + } } /** @@ -233,38 +252,37 @@ void ProcessSpecialKeys(int key, int x, int y) */ void ProcessKeys(unsigned char key, int x, int y) { - switch (key) - { - case 'q': - rotX += 1; - break; - case 'w': - rotY += 1; - break; - case 'e': - rotZ += 1; - break; - case 'a': - rotX -= 1; - break; - case 's': - rotY -= 1; - break; - case 'd': - rotZ -= 1; - break; - case 'p': - scale += .1; - break; - case 'l': - scale -= .1; - break; - case 27: // escape - exit(0); - } + switch (key) { + case 'q': + rotX += 1; + break; + case 'w': + rotY += 1; + break; + case 'e': + rotZ += 1; + break; + case 'a': + rotX -= 1; + break; + case 's': + rotY -= 1; + break; + case 'd': + rotZ -= 1; + break; + case 'p': + scale += .1; + break; + case 'l': + scale -= .1; + break; + case 27: // escape + exit(0); + } - rotPressed = true; - scalePressed = true; + rotPressed = true; + scalePressed = true; } /** @@ -273,22 +291,20 @@ void ProcessKeys(unsigned char key, int x, int y) */ void CreateAudioEngine() { - ma_result result = ma_engine_init(NULL, &engine); - if (result != MA_SUCCESS) - { - std::cout << "Failed to setup the miniaudio engine\n"; - fflush(stdout); - } + ma_result result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + std::cout << "Failed to setup the miniaudio engine\n"; + fflush(stdout); + } - // Play Test Sound - ma_sound_init_from_file( - &engine, - RESOURCES_PATH "audio/CrazyLaPaint.mp3", - 0, - NULL, - NULL, - &musicSound - ); + // Play Test Sound + ma_sound_init_from_file( + &engine, + RESOURCES_PATH "audio/CrazyLaPaint.mp3", + 0, + NULL, + NULL, + &musicSound); } /** @@ -297,53 +313,54 @@ void CreateAudioEngine() void MainLoop() { - // Get the window size - int winW = glutGet(GLUT_WINDOW_WIDTH); - int winH = glutGet(GLUT_WINDOW_HEIGHT); + // Get the window size + int winW = glutGet(GLUT_WINDOW_WIDTH); + int winH = glutGet(GLUT_WINDOW_HEIGHT); - // If Changed, update the view port - if (winW != lastWinW || winH != lastWinH) - { - UpdateViewPort(); - } + // If Changed, update the view port + if (winW != lastWinW || winH != lastWinH) { + UpdateViewPort(); + } - // Clear the screen - glClear(GL_COLOR_BUFFER_BIT); + // Clear the screen + glClear(GL_COLOR_BUFFER_BIT); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - // If scale oneshot is set, scale the image - if (scalePressed) - { - glScalef(scale, scale, scale); - scalePressed = false; - scale = 1; - } + // If scale oneshot is set, scale the image + if (scalePressed) { + glScalef(scale, scale, scale); + scalePressed = false; + scale = 1; + } - // If rotation oneshot is set, rotate the image - if (rotPressed) - { - glRotatef((GLfloat)rotX, 1.0, 0.0, 0.0); - glRotatef((GLfloat)rotY, 0.0, 1.0, 0.0); - glRotatef((GLfloat)rotZ, 0.0, 0.0, 1.0); - rotPressed = false; - rotX = rotY = rotZ = 0; - } - // If move oneshot is set, move the image and set move back to 0 - if (movePressed) - { - glTranslatef(moveX, moveY, 0); - movePressed = false; - moveX = moveY = 0; - } + // If rotation oneshot is set, rotate the image + if (rotPressed) { + glRotatef((GLfloat)rotX, 1.0, 0.0, 0.0); + glRotatef((GLfloat)rotY, 0.0, 1.0, 0.0); + glRotatef((GLfloat)rotZ, 0.0, 0.0, 1.0); + rotPressed = false; + rotX = rotY = rotZ = 0; + } + // If move oneshot is set, move the image and set move back to 0 + if (movePressed) { + glTranslatef(moveX, moveY, 0); + movePressed = false; + moveX = moveY = 0; + } - // Draws the image - DrawSprite(); + // Draws the image + DrawSprite(); - glFlush(); + glFlush(); - // Swap buffers to display the new frame - glutSwapBuffers(); + // Swap buffers to display the new frame + glutSwapBuffers(); + + // With freeglut, we can trigger screen update right after we are done +#if defined(use_freeglut) + glutPostRedisplay(); +#endif } /** @@ -351,33 +368,32 @@ void MainLoop() */ void DrawSprite() { - // Set texture enviorment - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + // Set texture enviorment + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - // Enable textures so OpenGL will apply the textures to the created object - glEnable(GL_TEXTURE_2D); + // Enable textures so OpenGL will apply the textures to the created object + glEnable(GL_TEXTURE_2D); - // Bind our texture - glBindTexture(GL_TEXTURE_2D, texID); // Which texture + // Bind our texture + glBindTexture(GL_TEXTURE_2D, texID); // Which texture - // Create a polygon and apply textures to it - glBegin(GL_POLYGON); - { + // Create a polygon and apply textures to it + glBegin(GL_POLYGON); + { + glTexCoord2f(0.0, 0.0); + glVertex3f(-50, -50, 0); + glTexCoord2f(1.0, 0.0); + glVertex3f(50, -50, 0); + glTexCoord2f(1.0, 1.0); + glVertex3f(50, 50, 0); + glTexCoord2f(0.0, 1.0); + glVertex3f(-50, 50, 0); + } + glEnd(); - glTexCoord2f(0.0, 0.0); - glVertex3f(-50, -50, 0); - glTexCoord2f(1.0, 0.0); - glVertex3f(50, -50, 0); - glTexCoord2f(1.0, 1.0); - glVertex3f(50, 50, 0); - glTexCoord2f(0.0, 1.0); - glVertex3f(-50, 50, 0); - } - glEnd(); - - // Disable textures so you dont continue adding textures to other objects - glDisable(GL_TEXTURE_2D); // Turn texturing off + // Disable textures so you dont continue adding textures to other objects + glDisable(GL_TEXTURE_2D); // Turn texturing off } /** @@ -385,10 +401,10 @@ void DrawSprite() */ void LoadUserTextures() { - /* - We add RESOURCES_PATH to the front of the filename - RESOURCES_PATH is the absolute path of the resources folder on the computer - when the application is build for production it turns into a relative path - */ - texID = LoadTexture(RESOURCES_PATH "container.jpg"); + /* + We add RESOURCES_PATH to the front of the filename + RESOURCES_PATH is the absolute path of the resources folder on the computer + when the application is build for production it turns into a relative path + */ + texID = LoadTexture(RESOURCES_PATH "container.jpg"); } \ No newline at end of file