FreeGlut MacOS Support

This commit is contained in:
Colton Staiduhar
2026-02-17 21:22:16 -05:00
parent a3bac40258
commit ad2175b9a6
2 changed files with 342 additions and 271 deletions

View File

@@ -10,6 +10,9 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# Declare Project where "GSM_TEMPLATE" is the project name # Declare Project where "GSM_TEMPLATE" is the project name
project(GSM_TEMPLATE LANGUAGES C CXX) 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 # Allow cmake to fetch web repositories
include(FetchContent) include(FetchContent)
@@ -41,13 +44,28 @@ FetchContent_MakeAvailable(stb_image)
# If using the Windows compiler # 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 # Download freeglut
FetchContent_Declare( FetchContent_Declare(
freeglut freeglut
GIT_REPOSITORY https://github.com/freeglut/freeglut.git GIT_REPOSITORY https://github.com/freeglut/freeglut.git
GIT_TAG "master" GIT_TAG "v3.8.0"
GIT_SHALLOW TRUE GIT_SHALLOW TRUE
GIT_PROGRESS ON GIT_PROGRESS ON
) )
@@ -56,6 +74,11 @@ if (MSVC)
set(FREEGLUT_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(FREEGLUT_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(FREEGLUT_BUILD_STATIC_LIBS ON 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) FetchContent_MakeAvailable(freeglut)
@@ -76,8 +99,8 @@ endif()
# Tell Cmake to find the locations of libraries # Tell Cmake to find the locations of libraries
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
# Only require glut if on macos # Only require glut if on macos and not using freeglut
if (APPLE) if (APPLE AND NOT USE_FREEGLUT)
find_package(GLUT REQUIRED) find_package(GLUT REQUIRED)
endif() endif()
# Define MY_SOURCES to be a list of all the source files for my game # 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 on mac
if (APPLE) 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 # Add our include files
target_include_directories("${CMAKE_PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/") target_include_directories("${CMAKE_PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/")
@@ -138,6 +192,7 @@ target_link_libraries("${CMAKE_PROJECT_NAME}"
stb_image stb_image
miniaudio miniaudio
) )
endif()
# If on windows # If on windows

View File

@@ -1,68 +1,66 @@
//############################################################################# // #############################################################################
//# main.cpp // # main.cpp
//############################################################################# // #############################################################################
//# Written by Colton Staiduhar // # Written by Colton Staiduhar
//# Date Created: 02/03/2025 // # Date Created: 02/03/2025
//# Last Modification: 02/05/2025 // # Last Modification: 02/05/2025
//############################################################################# // #############################################################################
//# Main Entry point for Cmake project, declare by the int main() function // # 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 // # This is a simple demonstration of GLUT functioning using STB_Image.H for
//# loading images and MiniAudio.h for playing audio. The repositories for // # loading images and MiniAudio.h for playing audio. The repositories for
//# these libraries can be found below. // # these libraries can be found below.
//# // #
//# Miniaudio // # Miniaudio
//# https://github.com/mackron/miniaudio // # https://github.com/mackron/miniaudio
//# // #
//# STB_Image.h // # STB_Image.h
//# https://github.com/arizotaz/cmake_stb_image // # https://github.com/arizotaz/cmake_stb_image
//# // #
//############################################################################# // #############################################################################
//# // #
//# For GLUT/OpenGL, the operating system should have GLUT in some form already // # For GLUT/OpenGL, the operating system should have GLUT in some form already
//# installed. However, freeglut exists as the modern implementation of GLUT. // # installed. However, freeglut exists as the modern implementation of GLUT.
//# // #
//# GLUT // # GLUT
//# You're on your own for this, but here are some helpful implementation docs // # 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 // # 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 // # Windows & Linux: https://www.opengl.org/resources/libraries/glut/glut_downloads.php
//# // #
//# FreeGLUT: // # FreeGLUT:
//# https://freeglut.sourceforge.net/docs/api.php // # https://freeglut.sourceforge.net/docs/api.php
//# // #
//############################################################################# // #############################################################################
//# // #
//# This file will setup much of GLUT. Getting some output to the screen // # This file will setup much of GLUT. Getting some output to the screen
//# The following is executed: // # The following is executed:
//# Create a windows with GLUT's API // # Create a windows with GLUT's API
//# Create a display update timer // # Create a display update timer
//# Create Keyboard Interrupts // # Create Keyboard Interrupts
//# Load textures the included Texture.H file // # Load textures the included Texture.H file
//# Setup the audio engine from MiniAudio // # Setup the audio engine from MiniAudio
//# Declare the Main Loop funcitons // # Declare the Main Loop funcitons
//# Draw a sprite to the screen // # Draw a sprite to the screen
//# Texture said sprite with an image // # Texture said sprite with an image
//# Rotate, move, and scale the sprite // # Rotate, move, and scale the sprite
//# Apply translations via user keyboard input // # Apply translations via user keyboard input
//# // #
//############################################################################# // #############################################################################
// Libraries // Libraries
#if defined(use_freeglut) #if defined(use_freeglut)
# include <glad/glad.h> #include <glad/glad.h>
# define FREEGLUT_STATIC #define FREEGLUT_STATIC
# include <gl/freeglut.h> #include <gl/freeglut.h>
#else #else
# include <GLUT/glut.h> #include <GLUT/glut.h>
#endif #endif
#include <miniaudio.h> #include <miniaudio.h>
// STD // STD
#include <math.h>
#include <iostream> #include <iostream>
#include <math.h>
#include <stdio.h> #include <stdio.h>
// Custom includes // Custom includes
@@ -76,14 +74,12 @@
void ProcessKeys(unsigned char, int, int); void ProcessKeys(unsigned char, int, int);
void ProcessSpecialKeys(int, int, int); void ProcessSpecialKeys(int, int, int);
void MainLoop(); void MainLoop();
void GlutLoopTimer(int);
// Extended Functionality // Extended Functionality
void UpdateViewPort(); void UpdateViewPort();
void DrawSprite(); void DrawSprite();
void LoadUserTextures(); void LoadUserTextures();
// Miniaudio Engine Declaration // Miniaudio Engine Declaration
void CreateAudioEngine(); void CreateAudioEngine();
ma_engine engine; // Miniaudio engine ma_engine engine; // Miniaudio engine
@@ -97,68 +93,10 @@ unsigned int texID;
ma_sound musicSound; // Miniaudio sound object ma_sound musicSound; // Miniaudio sound object
int lastWinW = 0, lastWinH = 0; int lastWinW = 0, lastWinH = 0;
/** bool running = true;
* Main entry point of the application void CloseCallBack()
*/
int main(int argc, char **argv)
{ {
std::cout << "Starting"; running = false;
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;
} }
/** /**
@@ -167,11 +105,96 @@ int main(int argc, char **argv)
*/ */
void GlutLoopTimer(int v) void GlutLoopTimer(int v)
{ {
// Runs the function specified in glutDisplayFunc // Runs the function specified in glutDisplayFunc
glutPostRedisplay(); glutPostRedisplay();
// Call the timer again in 16.7 seconds ~ 60 times per second // 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 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() void UpdateViewPort()
{ {
// Gets the window size // Gets the window size
int winW = glutGet(GLUT_WINDOW_WIDTH); int winW = glutGet(GLUT_WINDOW_WIDTH);
int winH = glutGet(GLUT_WINDOW_HEIGHT); int winH = glutGet(GLUT_WINDOW_HEIGHT);
// Sets up OpenGL Matrix Mode // Sets up OpenGL Matrix Mode
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
// Create projection type (Orthographic with size of window) // 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 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 // Set last size to the new size
lastWinW = winW; lastWinW = winW;
lastWinH = winH; lastWinH = winH;
} }
/** /**
@@ -202,29 +225,25 @@ void UpdateViewPort()
*/ */
void ProcessSpecialKeys(int key, int x, int y) void ProcessSpecialKeys(int key, int x, int y)
{ {
if (key == GLUT_KEY_LEFT) if (key == GLUT_KEY_LEFT) {
{ moveX = -10;
moveX = -10; movePressed = true;
movePressed = true; }
}
if (key == GLUT_KEY_RIGHT) if (key == GLUT_KEY_RIGHT) {
{ moveX = 10;
moveX = 10; movePressed = true;
movePressed = true; }
}
if (key == GLUT_KEY_UP) if (key == GLUT_KEY_UP) {
{ moveY = 10;
moveY = 10; movePressed = true;
movePressed = true; }
}
if (key == GLUT_KEY_DOWN) if (key == GLUT_KEY_DOWN) {
{ moveY = -10;
moveY = -10; movePressed = true;
movePressed = true; }
}
} }
/** /**
@@ -233,38 +252,37 @@ void ProcessSpecialKeys(int key, int x, int y)
*/ */
void ProcessKeys(unsigned char key, int x, int y) void ProcessKeys(unsigned char key, int x, int y)
{ {
switch (key) switch (key) {
{ case 'q':
case 'q': rotX += 1;
rotX += 1; break;
break; case 'w':
case 'w': rotY += 1;
rotY += 1; break;
break; case 'e':
case 'e': rotZ += 1;
rotZ += 1; break;
break; case 'a':
case 'a': rotX -= 1;
rotX -= 1; break;
break; case 's':
case 's': rotY -= 1;
rotY -= 1; break;
break; case 'd':
case 'd': rotZ -= 1;
rotZ -= 1; break;
break; case 'p':
case 'p': scale += .1;
scale += .1; break;
break; case 'l':
case 'l': scale -= .1;
scale -= .1; break;
break; case 27: // escape
case 27: // escape exit(0);
exit(0); }
}
rotPressed = true; rotPressed = true;
scalePressed = true; scalePressed = true;
} }
/** /**
@@ -273,22 +291,20 @@ void ProcessKeys(unsigned char key, int x, int y)
*/ */
void CreateAudioEngine() void CreateAudioEngine()
{ {
ma_result result = ma_engine_init(NULL, &engine); ma_result result = ma_engine_init(NULL, &engine);
if (result != MA_SUCCESS) if (result != MA_SUCCESS) {
{ std::cout << "Failed to setup the miniaudio engine\n";
std::cout << "Failed to setup the miniaudio engine\n"; fflush(stdout);
fflush(stdout); }
}
// Play Test Sound // Play Test Sound
ma_sound_init_from_file( ma_sound_init_from_file(
&engine, &engine,
RESOURCES_PATH "audio/CrazyLaPaint.mp3", RESOURCES_PATH "audio/CrazyLaPaint.mp3",
0, 0,
NULL, NULL,
NULL, NULL,
&musicSound &musicSound);
);
} }
/** /**
@@ -297,53 +313,54 @@ void CreateAudioEngine()
void MainLoop() void MainLoop()
{ {
// Get the window size // Get the window size
int winW = glutGet(GLUT_WINDOW_WIDTH); int winW = glutGet(GLUT_WINDOW_WIDTH);
int winH = glutGet(GLUT_WINDOW_HEIGHT); int winH = glutGet(GLUT_WINDOW_HEIGHT);
// If Changed, update the view port // If Changed, update the view port
if (winW != lastWinW || winH != lastWinH) if (winW != lastWinW || winH != lastWinH) {
{ UpdateViewPort();
UpdateViewPort(); }
}
// Clear the screen // Clear the screen
glClear(GL_COLOR_BUFFER_BIT); 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 scale oneshot is set, scale the image
if (scalePressed) if (scalePressed) {
{ glScalef(scale, scale, scale);
glScalef(scale, scale, scale); scalePressed = false;
scalePressed = false; scale = 1;
scale = 1; }
}
// If rotation oneshot is set, rotate the image // If rotation oneshot is set, rotate the image
if (rotPressed) if (rotPressed) {
{ glRotatef((GLfloat)rotX, 1.0, 0.0, 0.0);
glRotatef((GLfloat)rotX, 1.0, 0.0, 0.0); glRotatef((GLfloat)rotY, 0.0, 1.0, 0.0);
glRotatef((GLfloat)rotY, 0.0, 1.0, 0.0); glRotatef((GLfloat)rotZ, 0.0, 0.0, 1.0);
glRotatef((GLfloat)rotZ, 0.0, 0.0, 1.0); rotPressed = false;
rotPressed = false; rotX = rotY = rotZ = 0;
rotX = rotY = rotZ = 0; }
} // If move oneshot is set, move the image and set move back to 0
// If move oneshot is set, move the image and set move back to 0 if (movePressed) {
if (movePressed) glTranslatef(moveX, moveY, 0);
{ movePressed = false;
glTranslatef(moveX, moveY, 0); moveX = moveY = 0;
movePressed = false; }
moveX = moveY = 0;
}
// Draws the image // Draws the image
DrawSprite(); DrawSprite();
glFlush(); glFlush();
// Swap buffers to display the new frame // Swap buffers to display the new frame
glutSwapBuffers(); 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() void DrawSprite()
{ {
// Set texture enviorment // Set texture enviorment
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Enable textures so OpenGL will apply the textures to the created object // Enable textures so OpenGL will apply the textures to the created object
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
// Bind our texture // Bind our texture
glBindTexture(GL_TEXTURE_2D, texID); // Which texture glBindTexture(GL_TEXTURE_2D, texID); // Which texture
// Create a polygon and apply textures to it // Create a polygon and apply textures to it
glBegin(GL_POLYGON); 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); // Disable textures so you dont continue adding textures to other objects
glVertex3f(-50, -50, 0); glDisable(GL_TEXTURE_2D); // Turn texturing off
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
} }
/** /**
@@ -385,10 +401,10 @@ void DrawSprite()
*/ */
void LoadUserTextures() void LoadUserTextures()
{ {
/* /*
We add RESOURCES_PATH to the front of the filename We add RESOURCES_PATH to the front of the filename
RESOURCES_PATH is the absolute path of the resources folder on the computer 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 when the application is build for production it turns into a relative path
*/ */
texID = LoadTexture(RESOURCES_PATH "container.jpg"); texID = LoadTexture(RESOURCES_PATH "container.jpg");
} }