#include"k4.h" #ifdef _WIN32 #include #endif #define ENET_IMPLEMENTATION #define GLAD_GL_IMPLEMENTATION #include"gl.h" #pragma GCC push_options #pragma GCC optimize("Ofast") #define STB_IMAGE_IMPLEMENTATION #include"stb_image.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION #include"stb_image_resize.h" #pragma GCC pop_options #include #include #include #include"k3.h" #include"k3water.h" #include"k3mix.h" #include"k3font.h" #include"k3menu.h" #include"k3bloom.h" #include"resman.h" #include"game.h" #include"luaapi.h" #include"net_server.h" #include"net_client.h" #include GLFWwindow *GameWnd; static int TextureResolutionReduction = 0; static int IrregularShadows = 0; static float CamPitch, CamYaw; static vec3 CamPos; static int ThirdPerson = 1; struct k3MScreen *UiActive; static double CurrentTime; static double LastTime; #include"loaders.inc" void set_ui_mode(int yes) { glfwSetInputMode(GameWnd, GLFW_CURSOR, yes ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED); } static int is_ui_mode() { return glfwGetInputMode(GameWnd, GLFW_CURSOR) == GLFW_CURSOR_NORMAL; } static double xposold, yposold; static void buttoncallback(GLFWwindow *GameWnd, int button, int action, int mods) { if(button == GLFW_MOUSE_BUTTON_LEFT) { if(UiActive) { int ww, wh; glfwGetWindowSize(GameWnd, &ww, &wh); struct k3MEvent ev = { .original = (void*) UiActive, .target = (void*) UiActive, .code = action == GLFW_PRESS ? k3M_EVENT_MOUSE_PRESS : k3M_EVENT_MOUSE_RELEASE, .mouse = { .button = k3M_MOUSE_BUTTON_0, .x = xposold * UiActive->w / ww, .y = (wh - yposold - 1) * UiActive->h / wh, }, }; k3MEventSend(&ev); } } if(!UiActive) { luaapi_ctrlev(button, action == GLFW_PRESS ? 0 : (action == GLFW_RELEASE ? 2 : 1)); } } static void motioncallback(GLFWwindow *GameWnd, double xpos, double ypos) { static int first = 1; if(!first && !is_ui_mode()) { CamPitch -= (ypos - yposold) / 500.f; CamYaw -= (xpos - xposold) / 500.f; if(CamPitch > 1.5393) { CamPitch = 1.5393; } else if(CamPitch < -1.5393) { CamPitch = -1.5393; } } xposold = xpos; yposold = ypos; first = 0; if(UiActive) { int ww, wh; glfwGetWindowSize(GameWnd, &ww, &wh); uint16_t uix = xposold * UiActive->w / ww; uint16_t uiy = (wh - yposold - 1) * UiActive->h / wh; struct k3MEvent ev = { .original = (void*) UiActive, .target = (void*) UiActive, .code = k3M_EVENT_MOUSE_MOTION, .mouse = { .x = uix, .y = uiy, }, }; k3MEventSend(&ev); } } static void keycallback(GLFWwindow *GameWnd, int key, int scancode, int action, int mods) { if(action == GLFW_RELEASE && key == GLFW_KEY_ESCAPE) { luaapi_escape(); set_ui_mode(!!UiActive); } else { if(UiActive) { struct k3MEvent ev = { .original = (void*) UiActive, .target = (void*) UiActive, .code = action == GLFW_PRESS ? k3M_EVENT_KEY_PRESS : k3M_EVENT_KEY_RELEASE, .key = { .num = key, }, }; k3MEventSend(&ev); } } luaapi_ctrlev(key, action == GLFW_PRESS ? 0 : (action == GLFW_RELEASE ? 2 : 1)); } static void charcallback(GLFWwindow *window, unsigned int codepoint) { if(UiActive) { struct k3MEvent ev = { .original = (void*) UiActive, .target = (void*) UiActive, .code = k3M_EVENT_INPUT, .input = { .code = codepoint }, }; k3MEventSend(&ev); } } static void resizecallback(GLFWwindow *window, int width, int height) { k3Resize(width, height); } static int argc; static char **argv; const char *k4_get_arg(const char *name) { for(int i = 1; i < argc; i++) { if(strstr(argv[i], name) == argv[i] && strlen(argv[i]) > strlen(name) && argv[i][strlen(name)] == '=') { return argv[i] + strlen(name) + 1; } } return NULL; } /*static void netwrap_step() { if(NetWrap.stage && CurrentTime >= NetWrap.timeout) { if(NetWrap.stage == 1) { if(stoon_req(&NetWrap.stoon)) { if(stoon_listen(&NetWrap.stoon)) { uint8_t conndata[STOON_CONN_INFO_SIZE] = {}; stoon_serialize(&NetWrap.stoon, conndata); char str[STOON_CONN_INFO_SIZE * 2 + 1] = {}; for(int i = 0; i < sizeof(conndata); i++) { snprintf(str + i * 2, 3, "%02x", conndata[i]); } luaapi_peercode_found(str); NetWrap.stage = 2; } else { k3Log(k3_INFO, "Stoon listen timeout."); NetWrap.timeout = CurrentTime + 0.5; } } else { k3Log(k3_INFO, "Stoon request failed."); NetWrap.timeout = CurrentTime + 0.5; } } else if(NetWrap.stage == 2) { stoon_keepalive(&NetWrap.stoon); NetWrap.timeout = CurrentTime + 1; } else if(NetWrap.stage == 3) { if(NetWrap.isHost) { stoon_kill(&NetWrap.stoon); net_server_init(); NetWrap.stage = 4; } } else if(NetWrap.stage == 4) { if(NetWrap.isHost) { ENetBuffer buf = {.data = "Punch!", .dataLength = 6}; ENetHost *h = net_server_get_enethost(); for(size_t i = 0; i < NETWRAP_BACKLOG; i++) { if(NetWrap.otherpeersActive[i]) { ENetAddress v4 = {}, v6 = {}; stoonpeer_to_enets(NetWrap.otherpeers[i], &v4, &v6); enet_socket_send(h->socket, &v4, &buf, 1); enet_socket_send(h->socket, &v6, &buf, 1); } } } else { stoon_keepalive(&NetWrap.stoon); } NetWrap.timeout = CurrentTime + 1; } else if(NetWrap.stage == 5) { if(!NetWrap.isHost) { char ip[64]; int port; if(*(uint16_t*) (NetWrap.otherpeer + 22) == 0) { inet_ntop(AF_INET, NetWrap.otherpeer, ip, sizeof(ip)); port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 4)); } else { inet_ntop(AF_INET6, NetWrap.otherpeer + 6, ip, sizeof(ip)); port = ntohs(*(uint16_t*) (NetWrap.otherpeer + 22)); } printf("Trying [%s]:%u\n", ip, port); net_client_init(); if(net_client_connect(ip, port)) { NetWrap.stage = 5; } } NetWrap.timeout = CurrentTime + 0.5; } else { NetWrap.timeout = CurrentTime + 1; } } }*/ #include void k4k3LogCallback(enum k3LogLevel lvl, const char *str, size_t len) { static const char *prefixes[] = { [k3_DEBUG] = "[\x1B[95mDEBUG\x1B[0m]", [k3_INFO] = "[\x1B[97mINFO\x1B[0m] ", [k3_WARN] = "[\x1B[93mWARN\x1B[0m] ", [k3_ERR] = "[\x1B[91mERROR\x1B[0m]", }; //if(lvl == k3_ERR) raise(SIGINT); fprintf(stderr, "%s : %s\n", prefixes[lvl], str); } struct k4Control { const char *name; int btn; } Controls[K4_MAX_CONTROLS]; static int eng_init() { glfwInit(); glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE); glfwWindowHint(GLFW_DOUBLEBUFFER, GL_TRUE); glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); #define START_WIDTH 960 #define START_HEIGHT 540 #define START_TITLE "k4" // Prefer core if(!k4_get_arg("core") || strcmp(k4_get_arg("core"), "0")) { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); GameWnd = glfwCreateWindow(START_WIDTH, START_HEIGHT, START_TITLE, NULL, NULL); } if(!GameWnd) { glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); GameWnd = glfwCreateWindow(START_WIDTH, START_HEIGHT, START_TITLE, NULL, NULL); } k3MixInit(44100, 2); k3MixPortAudio(); if(glfwRawMouseMotionSupported()) { glfwSetInputMode(GameWnd, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); } glfwSetMouseButtonCallback(GameWnd, buttoncallback); glfwSetCursorPosCallback(GameWnd, motioncallback); glfwSetKeyCallback(GameWnd, keycallback); glfwSetCharCallback(GameWnd, charcallback); glfwSetFramebufferSizeCallback(GameWnd, resizecallback); glfwMakeContextCurrent(GameWnd); gladLoadGL(glfwGetProcAddress); if(k4_get_arg("shading") && strtol(k4_get_arg("shading"), NULL, 0) == 0) { GLAD_GL_ARB_shading_language_100 = 0; GLAD_GL_ARB_vertex_program = 0; GLAD_GL_ARB_fragment_program = 0; GLAD_GL_ARB_shader_objects = 0; } if(k4_get_arg("srgb") && strtol(k4_get_arg("srgb"), NULL, 0) == 0) { // Just forget sRGB if the driver doesn't bother with GL_FRAMEBUFFER_SRGB GLAD_GL_EXT_framebuffer_sRGB = 0; GLAD_GL_EXT_texture_sRGB = 0; } if(k4_get_arg("fbo") && strtol(k4_get_arg("fbo"), NULL, 0) == 0) { GLAD_GL_ARB_framebuffer_sRGB = 0; GLAD_GL_EXT_framebuffer_object = 0; } glfwSwapInterval(0); printf("GL version: %s\n", glGetString(GL_VERSION)); printf("GL renderer: %s\n", glGetString(GL_RENDERER)); printf("GL vendor: %s\n", glGetString(GL_VENDOR)); k3SetLogCallback(k4k3LogCallback); k3Init(); k3BatchInit(); resizecallback(GameWnd, START_WIDTH, START_HEIGHT); enet_initialize(); init_res_handlers(); luaapi_init(); } static int gr_lowres(int w, int h); static int ResolPercentage = 100; static void fix_resol() { int w, h; glfwGetWindowSize(GameWnd, &w, &h); gr_lowres(roundf(w * ResolPercentage / 100.f / 4) * 4, roundf(h * ResolPercentage / 100.f / 4) * 4); } static void eng_ui_init() { set_ui_mode(1); } static void fps_counter_step(double newDT) { static size_t lastDTidx = 0; static double lastDTs[30] = {}; lastDTs[lastDTidx] = newDT; lastDTidx = (lastDTidx + 1) % (sizeof(lastDTs) / sizeof(*lastDTs)); if(fmodf(CurrentTime, 1) < fmodf(LastTime, 1)) { double dtSum = 0; for(int i = 0; i < sizeof(lastDTs) / sizeof(*lastDTs); i++) { dtSum += lastDTs[i]; } k3Log(k3_DEBUG, "fps=%05.03f", 1 / (dtSum / (sizeof(lastDTs) / sizeof(*lastDTs)))); } } static struct k3Tex *shadowmap; static struct k3Offscreen *shadowmapOffscreen; static struct k3ARBFP *shadowmapDebugThing; static int gr_shadowmap_init() { shadowmap = k3TexCreate(k3_DEPTH); k3TexUpdate(shadowmap, k3_DEPTH, 0, k3TexSzMax() / 2, k3TexSzMax() / 2, NULL); shadowmapOffscreen = k3OffscreenCreate(NULL, shadowmap); if(!shadowmapOffscreen) { return 0; } shadowmapDebugThing = k3ProgramARBFP( "!!ARBfp1.0\n" "TEMP col;\n" "TEX col, fragment.texcoord, texture[0], 2D;\n" "MAD col.xyz, -col, 29.9, 30.1;\n" "RCP col.x, col.x;\n" "RCP col.y, col.y;\n" "RCP col.z, col.z;\n" "MUL col.xyz, col, 0.2;\n" "MOV result.color, col;\n" "END\n" ); return 1; } static struct k3Tex *bloomTex; static struct k3Offscreen *bloomOffscr; static int gr_bloom_init() { if(!k3BloomInit()) { return 0; } bloomTex = k3TexCreate(k3_RAWCOLOR); k3TexUpdate(bloomTex, k3_RAWCOLOR, 0, 1024, 512, NULL); bloomOffscr = k3OffscreenCreate(bloomTex, NULL); return !!bloomOffscr; } static struct k3Tex *lowres, *lowresDepth; static struct k3Offscreen *lowresOffscreen; static int gr_lowres(int newW, int newH) { if(lowresOffscreen) { k3OffscreenDestroy(lowresOffscreen); lowresOffscreen = NULL; } if(newW && newH) { if(!lowres) { lowres = k3TexCreate(k3_RAWCOLOR); } k3TexUpdate(lowres, k3_RAWCOLOR, 0, newW, newH, NULL); if(!lowresDepth) { lowresDepth = k3TexCreate(k3_DEPTH); } k3TexUpdate(lowresDepth, k3_DEPTH, 0, newW, newH, NULL); lowresOffscreen = k3OffscreenCreate(lowres, lowresDepth); } return !!lowresOffscreen; } #define MAX_RAYS 64 static struct LocalRay RaysToCast[MAX_RAYS]; static size_t RaysToCastCount = 0; struct LocalRay *request_ray(struct LocalRay *lr) { if(RaysToCastCount == MAX_RAYS) { return NULL; } memcpy(RaysToCast + RaysToCastCount, lr, sizeof(*lr)); return &RaysToCast[RaysToCastCount++]; } int main(int argc_, char **argv_) { argc = argc_; argv = argv_; eng_init(); eng_ui_init(); gr_shadowmap_init(); gr_bloom_init(); if(k4_get_arg("res%")) { ResolPercentage = strtod(k4_get_arg("res%"), NULL); fix_resol(); } IrregularShadows = 0; if(k4_get_arg("irsh") && strcmp(k4_get_arg("irsh"), "0")) { IrregularShadows = 1; } // INIT LEVEL game_init(); luaapi_load(k4_get_arg("script") ? k4_get_arg("script") : "lvl1"); // LuaapiStartTime = glfwGetTime(); LastTime = glfwGetTime(); double accumulator = 0; while(!glfwWindowShouldClose(GameWnd)) { CurrentTime = glfwGetTime(); double dt = CurrentTime - LastTime; accumulator += dt; fps_counter_step(dt); LastTime = CurrentTime; glfwPollEvents(); net_hi_update(CurrentTime); float alpha = fmodf(accumulator * GAME_TPS, 1.f); vec3 cameraForwardDir = {0, 0, -1}; glm_vec3_rotate(cameraForwardDir, CamPitch, (vec3) {1, 0, 0}); glm_vec3_rotate(cameraForwardDir, CamYaw, (vec3) {0, 1, 0}); /* Control translation from keys to game logic */ struct CPlayerCtrl *cc = Game.controlled == ENT_ID_INVALID ? NULL : game_ensurecomponent(Game.controlled, playerctrl); if(cc && !is_ui_mode()) { cc->yaw = CamYaw; cc->pitch = CamPitch; cc->keys = 0; for(int i = 0; i < K4_MAX_CONTROLS; i++) { struct k4Control *k = &Controls[i]; if(k->name) { if((k->btn <= GLFW_MOUSE_BUTTON_LAST ? glfwGetMouseButton(GameWnd, k->btn) : glfwGetKey(GameWnd, k->btn)) == GLFW_PRESS) { cc->keys |= 1 << i; } } } // This is necessary so the host player would send its own pointing to clients game_ensurecomponent(Game.controlled, movement)->pointing = cc->yaw; } if(Game.isMultiplayer) { if(Game.isAuthority) { net_server_receive(); } else { net_client_receive(); } } game_raycast(RaysToCast, RaysToCastCount); RaysToCastCount = 0; while(accumulator >= 1. / GAME_TPS) { for(size_t i = 0; i < Game.entities.renderCount; i++) { glm_vec4_copy(Game.entities.render[i].pos, Game.entities.render[i].posLast); glm_quat_copy(Game.entities.render[i].rot, Game.entities.render[i].rotLast); } if(Game.isMultiplayer && !Game.isAuthority) { net_client_dejitter(); net_client_update(); } accumulator -= 1. / GAME_TPS; game_update(); luaapi_update(); if(Game.isMultiplayer && Game.isAuthority) { net_server_update(); } } int width, height; glfwGetFramebufferSize(GameWnd, &width, &height); mat4 proj; glm_perspective(glm_rad(LuaapiFov), (float) width / height, 0.01f, 100.f, proj); /* Third-person camera movement */ struct LocalRay camray; if(Game.spectated != ENT_ID_INVALID) { struct CRender *c = game_getcomponent(Game.spectated, render); if(c) { if(!LuaapiFirstPerson) { glm_vec3_lerp(c->posLast, c->pos, alpha, camray.pos); camray.pos[1] += 1.5; glm_vec3_negate_to(cameraForwardDir, camray.dir); camray.ignore = Game.spectated; camray.maxlen = 3; camray.depth = 0; game_raycast(&camray, 1); } } } mat4 cam; if(LuaapiFirstPerson) { struct CRender *c = game_getcomponent(Game.spectated, render); if(c) { vec3 p; glm_vec3_lerp(c->posLast, c->pos, alpha, p); mat4 view; glm_look(p, cameraForwardDir, (vec3) {0, 1, 0}, view); glm_mat4_inv(view, cam); } } else if(camray.depth || LuaapiCamFocus) { glm_mat4_identity(cam); vec3 almostThere; if(LuaapiCamFocus) { glm_vec3_copy(LuaapiCamFocusPos, almostThere); glm_vec3_lerp(CamPos, almostThere, 0.1, CamPos); glm_look(CamPos, LuaapiCamFocusDir, (vec3) {0, 1, 0}, cam); } else { vec3 dirneg; glm_vec3_negate_to(camray.dir, dirneg); glm_vec3_lerp(camray.pos, camray.out, 0.9, almostThere); glm_vec3_lerp(CamPos, almostThere, 0.2, CamPos); glm_look(CamPos, dirneg, (vec3) {0, 1, 0}, cam); } glm_mat4_inv(cam, cam); } glm_mat4_copy(cam, LuaapiCamMatrix); k3SetTime(glfwGetTime()); size_t lightCount; struct k3Light *lights = luaapi_getlights(&lightCount); k3SetLights(lightCount, lights); luaapi_render(dt, alpha); for(size_t i = 0; i < Game.entities.renderCount; i++) { struct CRender *c = &Game.entities.render[i]; versor interrot; glm_quat_nlerp(c->rotLast, c->rot, alpha, interrot); vec4 interpos; glm_vec4_lerp(c->posLast, c->pos, alpha, interpos); mat4 transform = {}; glm_quat_mat4(interrot, transform); glm_vec4_copy(interpos, (float*) transform[3]); if(c->cache) { struct CBoned *b = game_getcomponent(c->entity, boned); k3Batch(c->cache, transform, b ? b->bones : NULL); } } if(!IrregularShadows) { k3PassShadowmap(proj, cam, shadowmapOffscreen); } if(lowresOffscreen) { k3BeginOffscreen(lowresOffscreen); } k3Clear(); if(LuaapiSkybox) { mat4 rotation; glm_quat_mat4(LuaapiSkyboxRotation, rotation); mat4 rotated; glm_mat4_mul(rotation, cam, rotated); k3CubemapTraditional(LuaapiSkybox, proj, rotated); } k3PassDepthOnly(proj, cam, true, true);// TRY COMMENTING THIS LINE if(IrregularShadows) { k3PassIrregular(lowresOffscreen, proj, cam); glEnable(GL_SCISSOR_TEST); //glScissor(256, 0, 10000, 10000); } k3PassForward(proj, cam); glDisable(GL_SCISSOR_TEST); k3BatchClear(); if(lowresOffscreen) { k3EndOffscreen(lowresOffscreen); k3BlitToScreenEffect(lowresOffscreen, false, k3_GLSL, k3ToneMapper(), NULL); if(bloomOffscr) { k3Bloom(lowresOffscreen, bloomOffscr); } } glClear(GL_DEPTH_BUFFER_BIT); if(!k3IsCore) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 3600, 0, 2025, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } luaapi_render2d(); if(UiActive) { struct k3MEvent ev = { .original = UiActive, .target = UiActive, .code = k3M_EVENT_DRAW, }; k3MEventSend(&ev); } k3BatchFlush(); // Shadowmap debugging if(glfwGetKey(GameWnd, GLFW_KEY_F5) == GLFW_PRESS) { k3BlitToScreen(shadowmapOffscreen, 0); } glfwSwapBuffers(GameWnd); } } int k4_control_set(const char *name, int btn) { for(int i = 0; i < K4_MAX_CONTROLS; i++) { if(!Controls[i].name) { Controls[i].name = strdup(name); Controls[i].btn = btn; return i; } } return -1; } int k4_control_get_id(int btn) { for(int i = 0; i < K4_MAX_CONTROLS; i++) { if(Controls[i].name && btn == Controls[i].btn) { return i; } } return -1; } int k4_control_get_by_name(const char *name) { for(int i = 0; i < K4_MAX_CONTROLS; i++) { if(Controls[i].name && !strcmp(Controls[i].name, name)) { return Controls[i].btn; } } return -1; } int k4_control_get_id_by_name(const char *name) { for(int i = 0; i < K4_MAX_CONTROLS; i++) { if(Controls[i].name && !strcmp(Controls[i].name, name)) { return i; } } return -1; } const char *k4_control_get_name_by_ctrl(int btn) { for(int i = 0; i < K4_MAX_CONTROLS; i++) { if(Controls[i].name && Controls[i].btn == btn) { return Controls[i].name; } } return NULL; } void k4_set_reduction(float f) { ResolPercentage = f * 100; fix_resol(); } void k4_set_texture_reduction(int i) { TextureResolutionReduction = i; refresh_textures(); } void k4_set_clipboard_text(const char *str) { glfwSetClipboardString(GameWnd, str); } const char *k4_get_clipboard_text() { return glfwGetClipboardString(GameWnd); }