Created
June 21, 2025 16:31
-
-
Save MurageKibicho/bed97510b8e37315aafa95eff612e456 to your computer and use it in GitHub Desktop.
Rotate with WASD, move and pan with mouse OpenGL, SDL2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef __EMSCRIPTEN__ | |
#include <emscripten.h> | |
#include <SDL.h> | |
#include <SDL_opengles2.h> | |
#else | |
#include <SLD2/SDL.h> | |
#include <SDL2/SDL_opengles2.h> | |
#endif | |
#ifndef M_PI | |
#define M_PI 3.14159265358979323846 | |
#endif | |
#include <stdlib.h> | |
#include <math.h> | |
#include "cglm/cglm.h" | |
#include "cglm/call.h" | |
//Run : emcc HelloTriangle.c -s USE_SDL=2 -s FULL_ES2=1 -s WASM=1 -Icglm/include -DCGLM_HEADER_ONLY -o HelloTriangle.html | |
//Preview : emrun HelloTriangle.html | |
typedef struct event_handler_struct *EventHandler; | |
typedef struct camera_struct *Camera; | |
struct camera_struct | |
{ | |
bool cameraUpdated; | |
bool windowResized; | |
int windowWidth; | |
int windowHeight; | |
GLfloat viewportX; | |
GLfloat viewportY; | |
GLfloat basePanX; | |
GLfloat basePanY; | |
GLfloat regPanX; | |
GLfloat regPanY; | |
GLfloat zoom; | |
GLfloat aspect; | |
GLfloat zoomMin; | |
GLfloat zoomMax; | |
//3D Camera Controls | |
vec3 worldPosition3D; | |
vec3 target; | |
vec3 up; | |
GLfloat rotationX; | |
GLfloat rotationY; | |
GLfloat distanceFromTarget; | |
GLfloat movementSpeed; | |
GLfloat rotationSpeed; | |
}; | |
struct event_handler_struct | |
{ | |
//Window | |
SDL_Window *window; | |
Uint32 windowID; | |
//Mouse input | |
bool mouseButtonDown; | |
float mouseWheelDelta; | |
int mouseButtonDownX; | |
int mouseButtonDownY; | |
int mousePositionX; | |
int mousePositionY; | |
//Finger input | |
bool fingerDown; | |
int fingerDownX; | |
int fingerDownY; | |
int fingerDownID; | |
//Pinch input | |
bool pinchDown; | |
float pinchZoomDelta; | |
float pinchScale; | |
//Global Camera | |
Camera camera; | |
GLint shaderPan; | |
GLint shaderZoom; | |
GLint shaderAspect; | |
}; | |
float clamp(float val, float lo, float hi) { | |
return (val < lo) ? lo : ((val > hi) ? hi : val); | |
} | |
Camera CreateCamera() | |
{ | |
Camera camera = malloc(sizeof(struct camera_struct)); | |
camera->cameraUpdated = false; | |
camera->windowResized = false; | |
camera->windowWidth = 0; | |
camera->windowHeight = 0; | |
camera->viewportX = 0.0f; | |
camera->viewportY = 0.0f; | |
camera->basePanX = 0.0f; | |
camera->basePanY = 0.0f; | |
camera->regPanX = 0.0f; | |
camera->regPanY = 0.0f; | |
camera->zoom = 1.0f; | |
camera->aspect = 1.0f; | |
camera->zoomMin = 0.1f; | |
camera->zoomMax = 10.0f; | |
//3D Camera Parameters | |
glm_vec3_copy((vec3){0.0f, 0.0f, 3.0f}, camera->worldPosition3D); | |
glm_vec3_copy((vec3){0.0f, 0.0f, 0.0f}, camera->target); | |
glm_vec3_copy((vec3){0.0f, 1.0f, 0.0f}, camera->up); // Fix this line | |
camera->rotationX = 0.0f; | |
camera->rotationY = 0.0f; | |
camera->distanceFromTarget = 3.0f; | |
camera->movementSpeed = 0.1f; | |
camera->rotationSpeed = 0.05f; | |
return camera; | |
} | |
void Camera_UpdatePosition(Camera camera) | |
{ | |
// Calculate base camera position without 2D transformations | |
camera->worldPosition3D[0] = camera->target[0] + camera->distanceFromTarget * sinf(camera->rotationX) * cosf(camera->rotationY); | |
camera->worldPosition3D[1] = camera->target[1] + camera->distanceFromTarget * sinf(camera->rotationY); | |
camera->worldPosition3D[2] = camera->target[2] + camera->distanceFromTarget * cosf(camera->rotationX) * cosf(camera->rotationY); | |
camera->cameraUpdated = true; | |
} | |
void Camera_SetWindowSize(Camera camera, int windowWidth, int windowHeight) | |
{ | |
if(camera->windowWidth != windowWidth || camera->windowHeight != windowHeight) | |
{ | |
camera->windowResized = true; | |
camera->cameraUpdated = true; | |
camera->windowWidth = windowWidth; | |
camera->windowHeight = windowHeight; | |
camera->viewportX = (float)windowWidth; | |
camera->viewportY = (float)windowHeight; | |
camera->aspect = (float)windowWidth / (float) windowHeight; | |
} | |
} | |
void Camera_Move(Camera camera, float vertical, float horizontal) | |
{ | |
// For vertical orbiting (W/S) | |
camera->rotationY += vertical * camera->rotationSpeed; | |
// For horizontal orbiting (A/D) | |
camera->rotationX += horizontal * camera->rotationSpeed; | |
// Clamp vertical rotation to prevent flipping | |
camera->rotationY = fmaxf(-M_PI/2 + 0.1f, fminf(camera->rotationY, M_PI/2 - 0.1f)); | |
Camera_UpdatePosition(camera); | |
} | |
void Camera_DeviceToWorldCoords(Camera camera, float deviceX, float deviceY, float *worldX, float *worldY) | |
{ | |
*worldX = deviceX / camera->zoom - camera->regPanX; | |
*worldY = deviceY / camera->aspect / camera->zoom - camera->regPanY; | |
} | |
void Camera_WindowToDeviceCoords(Camera camera, int windowX, int windowY, float *deviceX, float *deviceY) | |
{ | |
float normalizedWindowX = windowX / (float)camera->windowWidth; | |
float normalizedWindowY = windowY / (float)camera->windowHeight; | |
*deviceX = (normalizedWindowX - 0.5f) * 2.0f; | |
*deviceY = (1.0f - normalizedWindowY - 0.5f) * 2.0f; | |
} | |
void Camera_WindowToWorldCoords(Camera camera, int windowX, int windowY, float *worldX, float *worldY) | |
{ | |
float deviceX = 0.0f;float deviceY = 0.0f; | |
Camera_WindowToDeviceCoords(camera, windowX, windowY, &deviceX, &deviceY); | |
Camera_DeviceToWorldCoords(camera, windowX, windowY, &deviceX, &deviceY); | |
} | |
void Camera_SetZoomDelta(Camera camera, GLfloat zoomDelta) | |
{ | |
camera->zoom = clamp(camera->zoom + zoomDelta, camera->zoomMin, camera->zoomMax); | |
camera->cameraUpdated = true; | |
} | |
void Camera_SetPanDelta(Camera camera, GLfloat deltaWorldX, GLfloat deltaWorldY) | |
{ | |
camera->regPanX += deltaWorldX; | |
camera->regPanY += deltaWorldY; | |
camera->cameraUpdated = true; | |
} | |
void Camera_SetBasePan(Camera camera) | |
{ | |
camera->basePanX = camera->regPanX; | |
camera->basePanY = camera->regPanY; | |
} | |
void Camera_SetPan(Camera camera, GLfloat x, GLfloat y) | |
{ | |
camera->regPanX = x; | |
camera->regPanY = y; | |
camera->cameraUpdated = true; | |
} | |
void EventHandler_ResizeWindow(EventHandler eventHandler, int windowWidth, int windowHeight) | |
{ | |
//Set OpenGL viewport | |
glViewport(0, 0, windowWidth, windowHeight); | |
Camera_SetWindowSize(eventHandler->camera, windowWidth, windowHeight); | |
} | |
EventHandler CreateEventHandler(char *windowName, int windowWidth, int windowHeight) | |
{ | |
EventHandler eventHandler = malloc(sizeof(struct event_handler_struct)); | |
//Initialize SDL window | |
eventHandler->window = SDL_CreateWindow(windowName, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,windowWidth, windowHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN); | |
eventHandler->windowID = SDL_GetWindowID(eventHandler->window); | |
//Initialize mouse handlers | |
eventHandler->mouseButtonDown = false; | |
eventHandler->mouseWheelDelta = 0.05f; | |
eventHandler->mouseButtonDownX = 0; | |
eventHandler->mouseButtonDownY = 0; | |
eventHandler->mousePositionX = 0; | |
eventHandler->mousePositionY = 0; | |
//Initialize finger handlers | |
eventHandler->fingerDown = false; | |
eventHandler->fingerDownX = 0.0f; | |
eventHandler->fingerDownY = 0.0f; | |
eventHandler->fingerDownID = 0; | |
//Initialize pinch handlers | |
eventHandler->pinchDown = false; | |
eventHandler->pinchZoomDelta = 0.001f; | |
eventHandler->pinchScale = 8.0f; | |
//Create camera | |
eventHandler->camera = CreateCamera(); | |
eventHandler->shaderPan = 0; | |
eventHandler->shaderZoom = 0; | |
eventHandler->shaderAspect = 0; | |
//Create OpenGLES2 context inside SDL Window | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |
SDL_GL_SetSwapInterval(1); | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |
//Create OpenGL Context | |
SDL_GLContext openGLContext = SDL_GL_CreateContext(eventHandler->window); | |
if(openGLContext == NULL){SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Could not create OpenGL Context: %s\n", SDL_GetError());exit(1);} | |
printf("INFO: GL version: %s\n", glGetString(GL_VERSION)); | |
glEnable(GL_DEPTH_TEST); | |
glDepthFunc(GL_LESS); | |
//OpenGL Set Background Color to white | |
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); | |
//Call GL Clear | |
glClear(GL_COLOR_BUFFER_BIT); | |
//Change windowWidth and windowHeight to OpenGL window's actual size in pixels | |
int newWidth = 0;int newHeight=0; | |
SDL_GL_GetDrawableSize(eventHandler->window, &newWidth, &newHeight); | |
windowWidth = newWidth;windowHeight = newHeight; | |
printf("INFO: GL window size = %dx%d\n", windowWidth, windowHeight); | |
EventHandler_ResizeWindow(eventHandler, windowWidth, windowHeight); | |
return eventHandler; | |
} | |
void EventHandler_PanMouse(EventHandler eventHandler, int x, int y) | |
{ | |
int deltaX = eventHandler->camera->windowWidth / 2 + (x - eventHandler->mouseButtonDownX); | |
int deltaY = eventHandler->camera->windowHeight / 2 + (y - eventHandler->mouseButtonDownY); | |
float deviceX = 0.0f;float deviceY = 0.0f; | |
Camera_WindowToDeviceCoords(eventHandler->camera, deltaX, deltaY, &deviceX, &deviceY); | |
GLfloat basePanX = eventHandler->camera->basePanX; | |
GLfloat basePanY = eventHandler->camera->basePanX; | |
basePanX = basePanX + deviceX / eventHandler->camera->zoom; | |
basePanY = basePanY + deviceY / eventHandler->camera->zoom; | |
Camera_SetPan(eventHandler->camera, basePanX, basePanY); | |
} | |
void EventHandler_ZoomMouse(EventHandler eventHandler, bool mouseWheelDown) | |
{ | |
float beforeZoomX = 0.0f;float beforeZoomY = 0.0f; | |
float afterZoomX = 0.0f;float afterZoomY = 0.0f; | |
Camera_WindowToWorldCoords(eventHandler->camera, eventHandler->mousePositionX, eventHandler->mousePositionY, &beforeZoomX, &beforeZoomY); | |
float zoomDelta = mouseWheelDown ? -eventHandler->mouseWheelDelta : eventHandler->mouseWheelDelta; | |
Camera_SetZoomDelta(eventHandler->camera, zoomDelta); | |
float deltaWorldX = afterZoomX - beforeZoomX; | |
float deltaWorldY = afterZoomY - beforeZoomY; | |
Camera_SetPanDelta(eventHandler->camera, deltaWorldX, deltaWorldY); | |
} | |
const GLchar* vertexSource = | |
"uniform mat4 model; \n" | |
"uniform mat4 view; \n" | |
"uniform mat4 projection; \n" | |
"uniform vec2 pan; \n" // Keep your 2D pan | |
"uniform float zoom; \n" // Keep your 2D zoom | |
"uniform float aspect; \n" // Keep aspect ratio | |
"attribute vec3 position; \n" | |
"varying vec3 color; \n" | |
"void main() \n" | |
"{ \n" | |
" vec4 pos = projection * view * model * vec4(position, 1.0); \n" | |
" // Apply 2D transformations after 3D \n" | |
" pos.xy += pan; \n" | |
" pos.xy *= zoom; \n" | |
" pos.y *= aspect; \n" | |
" gl_Position = pos; \n" | |
" color = gl_Position.xyz + vec3(0.5); \n" | |
"} \n"; | |
// Fragment/pixel shader | |
const GLchar* fragmentSource = | |
"precision mediump float; \n" | |
"varying vec3 color; \n" | |
"void main() \n" | |
"{ \n" | |
" gl_FragColor = vec4 ( color, 1.0 ); \n" | |
"} \n"; | |
void UpdateShader(EventHandler eventHandler) | |
{ | |
// Get the current shader program | |
GLuint shaderProgram; | |
glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&shaderProgram); | |
// Get uniform locations (could cache these for better performance) | |
eventHandler->shaderPan = glGetUniformLocation(shaderProgram, "pan"); | |
eventHandler->shaderZoom = glGetUniformLocation(shaderProgram, "zoom"); | |
eventHandler->shaderAspect = glGetUniformLocation(shaderProgram, "aspect"); | |
GLint modelLoc = glGetUniformLocation(shaderProgram, "model"); | |
GLint viewLoc = glGetUniformLocation(shaderProgram, "view"); | |
GLint projLoc = glGetUniformLocation(shaderProgram, "projection"); | |
// Create a vec2 for pan (since your camera stores pan components separately) | |
GLfloat pan[2] = {eventHandler->camera->regPanX, eventHandler->camera->regPanY}; | |
// Set 2D uniform values | |
glUniform2fv(eventHandler->shaderPan, 1, pan); | |
glUniform1f(eventHandler->shaderZoom , eventHandler->camera->zoom); | |
glUniform1f(eventHandler->shaderAspect, eventHandler->camera->aspect); | |
// Set 3D uniform values | |
mat4 model, view, projection; | |
glm_mat4_identity(model); // No model transformation | |
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, (float*)model); | |
// Identity matrix for model (no transformation) | |
glm_mat4_identity(model); | |
// Create view matrix (camera) | |
glm_lookat(eventHandler->camera->worldPosition3D, eventHandler->camera->target, eventHandler->camera->up, view); | |
// Create projection matrix | |
float aspect = (float)eventHandler->camera->windowWidth / (float)eventHandler->camera->windowHeight; | |
glm_perspective(glm_rad(45.0f), aspect, 0.01f, 100.0f, projection); // Changed near plane to 0.01 | |
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, (float*)model); | |
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, (float*)view); | |
glUniformMatrix4fv(projLoc, 1, GL_FALSE, (float*)projection); | |
} | |
GLuint InitializeShader() | |
{ | |
// Create and compile vertex shader | |
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); | |
glShaderSource(vertexShader, 1, &vertexSource, NULL); | |
glCompileShader(vertexShader); | |
//Check for compilation issues in vertex shader | |
GLint success; | |
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); | |
if(!success) | |
{ | |
char compilationErrorString[512]; | |
glGetShaderInfoLog(vertexShader, 512, NULL, compilationErrorString); | |
printf("Vertex shader compilation failed: %s\n", compilationErrorString); | |
} | |
// Create and compile fragment shader | |
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(fragmentShader, 1, &fragmentSource, NULL); | |
glCompileShader(fragmentShader); | |
//Check for compilation issues in fragmentShader | |
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); | |
if(!success) | |
{ | |
char compilationErrorString[512]; | |
glGetShaderInfoLog(fragmentShader, 512, NULL, compilationErrorString); | |
printf("Fragment shader compilation failed: %s\n", compilationErrorString); | |
} | |
// Link vertex and fragment shader into shader program and use it | |
GLuint shaderProgram = glCreateProgram(); | |
glAttachShader(shaderProgram, vertexShader); | |
glAttachShader(shaderProgram, fragmentShader); | |
glLinkProgram(shaderProgram); | |
glUseProgram(shaderProgram); | |
// Cleanup: Detach and delete shaders (they're now part of the program) | |
glDetachShader(shaderProgram, vertexShader); | |
glDetachShader(shaderProgram, fragmentShader); | |
glDeleteShader(vertexShader); | |
glDeleteShader(fragmentShader); | |
return shaderProgram; | |
} | |
void InitializeTriangleGeometry(GLuint shaderProgram) | |
{ | |
// Create vertex buffer object and copy vertex data into it | |
GLuint vbo; | |
glGenBuffers(1, &vbo); | |
glBindBuffer(GL_ARRAY_BUFFER, vbo); | |
//Triangle Vertices | |
GLfloat vertices[] = | |
{ | |
0.0f, 0.5f, 0.0f, | |
-0.5f, -0.5f, 0.0f, | |
0.5f, -0.5f, 0.0f | |
}; | |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | |
// Specify the layout of the shader vertex data (positions only, 3 floats) | |
GLint posAttrib = glGetAttribLocation(shaderProgram, "position"); | |
glEnableVertexAttribArray(posAttrib); | |
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0); | |
} | |
void Redraw(SDL_Window *sdlWindow) | |
{ | |
// Clear screen | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
// Draw the vertex buffer | |
glDrawArrays(GL_TRIANGLES, 0, 3); | |
// Swap front/back framebuffers | |
SDL_GL_SwapWindow(sdlWindow); | |
} | |
void ProcessEventsWithSDL(EventHandler eventHandler) | |
{ | |
SDL_Event event; | |
//Infinite loop to process Events | |
while(SDL_PollEvent(&event)) | |
{ | |
//Handle different event types using a switch | |
switch(event.type) | |
{ | |
//Handle quit | |
case SDL_QUIT: | |
exit(0); | |
break; | |
//Handle Resizing | |
case SDL_WINDOWEVENT: | |
if(event.window.windowID == eventHandler->windowID && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) | |
{ | |
EventHandler_ResizeWindow(eventHandler, event.window.data1, event.window.data2); | |
} | |
break; | |
//Keyboard Events | |
case SDL_KEYUP: | |
printf("Key pressed: %s (SDL_Keycode: %d)\n", SDL_GetKeyName(event.key.keysym.sym),event.key.keysym.sym); | |
break; | |
case SDL_KEYDOWN: | |
printf("Key released: %s (SDL_Keycode: %d)\n", SDL_GetKeyName(event.key.keysym.sym),event.key.keysym.sym); | |
Camera camera = eventHandler->camera; | |
switch(event.key.keysym.sym) | |
{ | |
case SDLK_w: | |
Camera_Move(camera, 1.0f, 0.0f); // Orbit up | |
break; | |
case SDLK_s: | |
Camera_Move(camera, -1.0f, 0.0f); // Orbit down | |
break; | |
case SDLK_a: | |
Camera_Move(camera, 0.0f, -1.0f); // Orbit left | |
break; | |
case SDLK_d: | |
Camera_Move(camera, 0.0f, 1.0f); // Orbit right | |
break; | |
} | |
break; | |
//Mouse Events | |
case SDL_MOUSEMOTION: | |
printf("Mouse moved to (%d, %d)\n", event.motion.x, event.motion.y); | |
eventHandler->mousePositionX = event.motion.x; | |
eventHandler->mousePositionY = event.motion.y; | |
if(eventHandler->mouseButtonDown && eventHandler->fingerDown == false && eventHandler->pinchDown == false) | |
{ | |
EventHandler_PanMouse(eventHandler, eventHandler->mousePositionX, eventHandler->mousePositionY); | |
} | |
break; | |
case SDL_MOUSEBUTTONUP: | |
if(event.button.button == SDL_BUTTON_LEFT) | |
{ | |
eventHandler->mouseButtonDown = false; | |
printf("Mouse button left released\n"); | |
} | |
break; | |
case SDL_MOUSEBUTTONDOWN: | |
if(event.button.button == SDL_BUTTON_LEFT && eventHandler->fingerDown == false && eventHandler->pinchDown == false) | |
{ | |
eventHandler->mouseButtonDown = true; | |
eventHandler->mouseButtonDownX = event.motion.x; | |
eventHandler->mouseButtonDownY = event.motion.y; | |
Camera_SetBasePan(eventHandler->camera); | |
printf("Mouse button left down\n"); | |
} | |
break; | |
case SDL_MOUSEWHEEL: | |
printf("Mouse wheel scrolled: x=%d, y=%d\n", event.wheel.x, event.wheel.y); | |
printf("Zoom: %f, Pan: (%f, %f)\n", eventHandler->camera->zoom,eventHandler->camera->regPanX,eventHandler->camera->regPanY); | |
bool mouseWheelDown = (event.wheel.y < 0.0); | |
eventHandler->mousePositionX = event.motion.x; | |
eventHandler->mousePositionY = event.motion.y; | |
EventHandler_ZoomMouse(eventHandler, mouseWheelDown); | |
break; | |
//Touch Events | |
case SDL_FINGERDOWN: | |
printf("Finger %lld touched at (%.2f, %.2f)\n", event.tfinger.fingerId, event.tfinger.x, event.tfinger.y); | |
break; | |
case SDL_FINGERUP: | |
printf("Finger %lld lifted\n", event.tfinger.fingerId); | |
eventHandler->fingerDown = false; | |
eventHandler->pinchDown = false; | |
break; | |
case SDL_FINGERMOTION: | |
printf("Finger %lld moved to (%.2f, %.2f)\n", event.tfinger.fingerId, event.tfinger.x, event.tfinger.y); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
void MainLoop(void *mainLoopArgument) | |
{ | |
EventHandler eventHandler = *(EventHandler*)mainLoopArgument; | |
ProcessEventsWithSDL(eventHandler); | |
UpdateShader(eventHandler); | |
Redraw(eventHandler->window); | |
} | |
int main() | |
{ | |
if(SDL_Init(SDL_INIT_VIDEO) != 0){SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Could not Initialize Video: %s\n", SDL_GetError());exit(1);} | |
int windowWidth = 512; | |
int windowHeight= 512; | |
char *windowName = "HelloTriangle"; | |
EventHandler eventHandler = CreateEventHandler(windowName, windowWidth, windowHeight); | |
if(eventHandler == NULL){printf("Error: Could not create eventHandler\n");} | |
eventHandler->camera->rotationX = M_PI/4; // 45 degree angle | |
eventHandler->camera->rotationY = M_PI/6; // 30 degree angle | |
Camera_UpdatePosition(eventHandler->camera); | |
//Initialize shader and geometry | |
GLuint shaderProgram = InitializeShader(); | |
InitializeTriangleGeometry(shaderProgram); | |
//Start Emscripten main loop | |
void *mainLoopArgument = eventHandler; | |
#ifdef __EMSCRIPTEN__ | |
int fps = 0; | |
emscripten_set_main_loop_arg(MainLoop, &mainLoopArgument, fps, true); | |
#else | |
while(true) | |
{ | |
MainLoop(&mainLoopArgument); | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment