void android_main(android_app *app) { app_dummy(); asset_manager = app->activity->assetManager; user_data p = {}; p.texture_buffer = (uint8_t *)malloc(4 * 960 * 540); strcpy(p.app_name, "org.nxsy.ndk_handmade"); app->userData = &p; app->onAppCmd = on_app_cmd; app->onInputEvent = on_input_event; uint64_t counter; uint start_row = 0; uint start_col = 0; game_memory m = {}; m.PermanentStorageSize = 64 * 1024 * 1024; m.TransientStorageSize = 64 * 1024 * 1024; p.total_size = m.PermanentStorageSize + m.TransientStorageSize; p.game_memory_block = calloc(p.total_size, sizeof(uint8)); m.PermanentStorage = (uint8 *)p.game_memory_block; m.TransientStorage = (uint8_t *)m.PermanentStorage + m.TransientStorageSize; #ifdef HANDMADE_INTERNAL m.DEBUGPlatformReadEntireFile = debug_read_entire_file; #endif thread_context t = {}; game_input input[2] = {}; p.new_input = &input[0]; p.old_input = &input[1]; int monitor_refresh_hz = 60; real32 game_update_hz = (monitor_refresh_hz / 2.0f); // Should almost always be an int... long target_nanoseconds_per_frame = (1000 * 1000 * 1000) / game_update_hz; while (++counter) { timespec start_time = {}; clock_gettime(CLOCK_MONOTONIC_RAW, &start_time); game_controller_input *old_keyboard_controller = GetController(p.old_input, 0); game_controller_input *new_keyboard_controller = GetController(p.new_input, 0); *new_keyboard_controller = {}; new_keyboard_controller->IsConnected = true; for ( uint button_index = 0; button_index < ArrayCount(new_keyboard_controller->Buttons); ++button_index) { new_keyboard_controller->Buttons[button_index].EndedDown = old_keyboard_controller->Buttons[button_index].EndedDown; } int poll_result, events; android_poll_source *source; while((poll_result = ALooper_pollAll(0, 0, &events, (void**)&source)) >= 0) { source->process(app, source); } switch (poll_result) { case ALOOPER_POLL_WAKE: { __android_log_print(ANDROID_LOG_INFO, p.app_name, "poll_result was ALOOPER_POLL_WAKE"); break; } case ALOOPER_POLL_CALLBACK: { __android_log_print(ANDROID_LOG_INFO, p.app_name, "poll_result was ALOOPER_POLL_CALLBACK"); break; } case ALOOPER_POLL_TIMEOUT: { //__android_log_print(ANDROID_LOG_INFO, p.app_name, "poll_result was ALOOPER_POLL_TIMEOUT"); break; } case ALOOPER_POLL_ERROR: { __android_log_print(ANDROID_LOG_INFO, p.app_name, "poll_result was ALOOPER_POLL_ERROR"); break; } default: { __android_log_print(ANDROID_LOG_INFO, p.app_name, "poll_result was %d", poll_result); break; } } p.new_input->dtForFrame = target_nanoseconds_per_frame / (1024.0 * 1024 * 1024); hh_process_events(app, p.new_input, p.old_input); game_offscreen_buffer game_buffer = {}; game_buffer.Memory = p.texture_buffer; game_buffer.Width = 960; game_buffer.Height = 540; game_buffer.Pitch = 960 * 4; game_buffer.BytesPerPixel = 4; GameUpdateAndRender(&t, &m, p.new_input, &game_buffer); draw(app); timespec end_time = {}; clock_gettime(CLOCK_MONOTONIC_RAW, &end_time); end_time.tv_sec -= start_time.tv_sec; start_time.tv_sec -= start_time.tv_sec; int64_t time_taken = ((end_time.tv_sec * 1000000000 + end_time.tv_nsec) - (start_time.tv_sec * 1000000000 + start_time.tv_nsec)); int64_t time_to_sleep = 33 * 1000000; if (time_taken <= time_to_sleep) { timespec sleep_time = {}; sleep_time.tv_nsec = time_to_sleep - time_taken; timespec remainder = {}; nanosleep(&sleep_time, &remainder); } else { if (counter % 10 == 0) { __android_log_print(ANDROID_LOG_INFO, p.app_name, "Skipped frame! Took %" PRId64 " ns total", time_taken); } } game_input *temp_input = p.new_input; p.new_input = p.old_input; p.old_input = temp_input; } }
int main(int argc, char *args[]) { #if _WIN32 // NOTE(nathan): Set the windows schedular granularity to 1ms // so that our SDL_Delay() and be more granular // SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); // NOTE(nathan): This seems to give a more consistant result than above timeBeginPeriod(1); #if _DEBUG HWND console = GetConsoleWindow(); if(console != 0) MoveWindow(console, -900, 100, 700, 800, TRUE); #endif #endif SDL_Init(SDL_INIT_EVERYTHING); // NOTE(nathan): This also initializes the window_with and window_height WindowData window_data = { }; window_data.target_width = 1920; window_data.target_height = 1080; FixViewport(1280, 720, &window_data); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4); SDL_Window *window = SDL_CreateWindow( "Banana", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_data.width, window_data.height, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); // ToggleFullScreen(window); SDL_ShowCursor(SDL_DISABLE); // OutputSystemInfo(); SDL_GLContext glContext = SDL_GL_CreateContext(window); glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) Error("glewInit"); // Goes stuttery if we turn on vsync SDL_GL_SetSwapInterval((int)window_data.vsync); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); bool running = true; SDL_Event e; uint32 gameUpdateHz = 60; float32 targetSecondsPerFrame = 1.0f / (float32)gameUpdateHz; perfCountFreq = SDL_GetPerformanceFrequency(); uint64 lastCounter = GetWallClock(); GameMemory game_memory = { 0 }; game_memory.size = Megabytes(64); game_memory.memory = SDL_malloc(game_memory.size); EditorMemory editor_memory = { 0 }; editor_memory.size = Megabytes(64); editor_memory.memory = SDL_malloc(editor_memory.size); RenderContext render_context = {}; render_context.diffuse = CreateShader("assets/shaders/diffuse.vert", "assets/shaders/diffuse.frag"); InitializeContext(&render_context); VoxelRenderContext voxel_render_context = {}; voxel_render_context.diffuse = CreateShader("assets/shaders/voxels.vert", "assets/shaders/voxels.frag"); InitializeVoxelContext(&voxel_render_context); InputData input = { }; input.window = window; UIContext ui_context = { 0 }; ui_context.input = &input; ui_context.render_context = &render_context; Mode mode = MODE_GAME; bool game_paused = false; while (running) { while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) running = false; if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_RESIZED) { FixViewport((int)e.window.data1, (int)e.window.data2, &window_data); } } if (e.type == SDL_KEYDOWN) { SDL_Scancode key = e.key.keysym.scancode; input.keyboard_state[key] = true; if (key == SDL_SCANCODE_ESCAPE) running = false; if (key == SDL_SCANCODE_F12) window_data.vsync = !window_data.vsync; if (key == SDL_SCANCODE_F11) ToggleFullScreen(window, &window_data); if (key == SDL_SCANCODE_F10) { if (mode == MODE_GAME) { game_paused = true; mode = MODE_EDITOR; } else { mode = MODE_GAME; game_paused = false; } } } if (e.type == SDL_KEYUP) { SDL_Scancode key = e.key.keysym.scancode; input.keyboard_state[key] = false; } if (e.type == SDL_MOUSEMOTION) { input.mouse_pos.x = (((float)window_data.target_width / (float)window_data.vp_width) * ((float)e.motion.x - window_data.vp_x)); input.mouse_pos.y = (((float)window_data.target_height / (float)window_data.vp_height) * ((float)e.motion.y - window_data.vp_y)); } if (e.type == SDL_MOUSEBUTTONDOWN) { if (e.button.button == SDL_BUTTON_LEFT) input.mb_left = true; if (e.button.button == SDL_BUTTON_RIGHT) input.mb_right = true; if (e.button.button == SDL_BUTTON_MIDDLE) input.mb_middle = true; } if (e.type == SDL_MOUSEBUTTONUP) { if (e.button.button == SDL_BUTTON_LEFT) input.mb_left = false; if (e.button.button == SDL_BUTTON_RIGHT) input.mb_right = false; if (e.button.button == SDL_BUTTON_MIDDLE) input.mb_middle = false; } } float32 delta = 1.0f / (float32)gameUpdateHz; GameUpdateAndRender(&game_memory, &input, &render_context, &voxel_render_context, &window_data, game_paused, delta); uint64 workCounter = GetWallClock(); float32 workSecondsElapsed = GetSecondsElapsed(lastCounter, workCounter); // Dont include this in frametime if (mode == MODE_EDITOR) EditorUpdateAndRender(&editor_memory, &input, &render_context, &ui_context, delta); float32 secondsElapsedForFrame = workSecondsElapsed; if (secondsElapsedForFrame < targetSecondsPerFrame) { uint32 sleepMS = (uint32)((1000.0f * (targetSecondsPerFrame - secondsElapsedForFrame)) - 1); if (sleepMS > 0) SDL_Delay(sleepMS); while (secondsElapsedForFrame < targetSecondsPerFrame) { secondsElapsedForFrame = GetSecondsElapsed(lastCounter, GetWallClock()); } } else { // Error("Missed Frame Rate"); } uint64 endCounter = GetWallClock(); float64 msPerFrame = 1000.0f * GetSecondsElapsed(lastCounter, endCounter); lastCounter = endCounter; // Debug Render BeginRenderer(&render_context); std::string s = "FRAMETIME " + std::to_string(msPerFrame) + " VSYNC " + std::to_string(window_data.vsync); RenderString(&render_context, 40.0f, 40.0f, s.c_str(), 0.0f); EndRenderer(); SDL_GL_SwapWindow(window); input.prev_keyboard_state = input.keyboard_state; input.mb_left_prev = input.mb_left; input.mb_right_prev = input.mb_right; input.mb_middle_prev = input.mb_middle; } UnloadContext(&render_context); UnloadVoxelContext(&voxel_render_context); SDL_free(game_memory.memory); SDL_GL_DeleteContext(glContext); SDL_DestroyWindow(window); SDL_Quit(); return(0); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { GameState gameState = {}; gameState.RenderState = 0; gameState.State = 1; gameState.PlayerX = 200.0; gameState.PlayerY = 450.0; glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); GLFWwindow* window = glfwCreateWindow(gameState.ScreenWidth, gameState.ScreenHeight, "Neighbors", NULL, NULL); glfwMakeContextCurrent(window); glewExperimental = GL_TRUE; glewInit(); glGetError(); glfwSetKeyCallback(window, processWindowsCallback); // OpenGL configuration glViewport(0, 0, gameState.ScreenWidth, gameState.ScreenHeight); // Where in the window to render. glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Load shaders ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite"); // Configure shaders glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(gameState.ScreenWidth), static_cast<GLfloat>(gameState.ScreenHeight), 0.0f, -1.0f, 1.0f); ResourceManager::GetShader("sprite").Use().SetInteger("image", 0); ResourceManager::GetShader("sprite").SetMatrix4("projection", projection); // Set render-specific controls Renderer = new SpriteRenderer(ResourceManager::GetShader("sprite")); AnimateRenderer = new AnimatedSpriteRenderer(ResourceManager::GetShader("sprite")); // Load textures ResourceManager::LoadTexture("textures/download.png", GL_TRUE, "face"); ResourceManager::LoadTexture("textures/download.jpg", GL_TRUE, "other"); ResourceManager::LoadTexture("textures/grass.png", GL_TRUE, "grass"); ResourceManager::LoadTexture("textures/floor.png", GL_TRUE, "floor"); ResourceManager::LoadTexture("textures/back_wall.png", GL_TRUE, "backwall"); ResourceManager::LoadSpriteSheet("textures/sonic.png", GL_TRUE, "sonic"); RenderList List; /* Loop until the user closes the window */ while (!glfwWindowShouldClose(window)) { // Clear IsDowns to false for incoming input and update WasDown's. for (int ButtonIndex = 0; ButtonIndex < ArrayCount(GlobalInput.Controller.Buttons); ButtonIndex++) { GlobalInput.Controller.Buttons[ButtonIndex].WasDown = GlobalInput.Controller.Buttons[ButtonIndex].IsDown; GlobalInput.Controller.Buttons[ButtonIndex].IsDown = false; } // Controller input handeling. if (glfwJoystickPresent(GLFW_JOYSTICK_1)) { int countButtons; const unsigned char *button = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &countButtons); for (int i = 0; i < countButtons; i++) { if (button[i] == GLFW_PRESS) { GlobalInput.Controller.Buttons[i].IsDown = true; } } int countAxes; const float *axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &countAxes); for (int i = 0; i < countAxes; i++) { GlobalInput.Controller.Axes[i] = axes[i]; } } /* Poll for and process events */ glfwPollEvents(); if (gameState.State == 0) glfwSetWindowShouldClose(window, 1); /* Render here */ GameUpdateAndRender(&gameState, &GlobalInput, &List); float cameraPosX = gameState.PlayerX - 500; float cameraPosY = gameState.PlayerY - 400; glm::mat4 projection = glm::ortho(cameraPosX, cameraPosX + static_cast<GLfloat>(gameState.ScreenWidth), cameraPosY + static_cast<GLfloat>(gameState.ScreenHeight), cameraPosY, -1.0f, 1.0f); ResourceManager::GetShader("sprite").Use().SetInteger("image", 0); ResourceManager::GetShader("sprite").SetMatrix4("projection", projection); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); while (!List.isEmpty) { RenderObject * obj = List.Pop(); Renderer->DrawSprite(ResourceManager::GetTexture(obj->name), obj->position, obj->scale, 0.0f, obj->color); free(obj); } AnimateRenderer->DrawSprite(ResourceManager::GetSpriteSheet("sonic"), glm::vec2(cameraPosX, cameraPosY), glm::vec2(64, 72), 0.0f, glm::vec3(1.0f, 1.0f, 1.0f), gameState.RenderState); /* Swap front and back buffers */ glfwSwapBuffers(window); } glfwTerminate(); return 0; }