963 lines
22 KiB
C
963 lines
22 KiB
C
![]() |
#include"k4.h"
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
#include<winsock2.h>
|
||
|
#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<GLFW/glfw3.h>
|
||
|
#include<cglm/cam.h>
|
||
|
#include<cglm/affine.h>
|
||
|
|
||
|
#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<ctype.h>
|
||
|
|
||
|
struct NetWrap NetWrap;
|
||
|
|
||
|
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);
|
||
|
|
||
|
//glfwSetClipboardString(NULL, str);
|
||
|
|
||
|
//screenMain.lblpeerdata->invisible = 0;
|
||
|
//screenMain.btnconnect->invisible = 0;
|
||
|
|
||
|
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) {
|
||
|
int status = stoon_poonch(&NetWrap.stoon, NetWrap.otherpeer);
|
||
|
|
||
|
//screenMain.lblpeerdata->txt = strdup("Trying to connect...\nMay not work if both are\nbehind symmetric NATs.");
|
||
|
|
||
|
if(status == STOON_POONCH_INCOMPATIBLE_NETFAMS) {
|
||
|
NetWrap.stage = 0;
|
||
|
|
||
|
stoon_kill(&NetWrap.stoon);
|
||
|
|
||
|
//screenMain.lblpeerdata->txt = strdup("Connection cannot be established:\nIncompatible netfams.");
|
||
|
} else if(status == STOON_POONCH_NO_KNOCK && NetWrap.stoon.poonchstage == POONCH_STAGE_ACK) {
|
||
|
if(NetWrap.gotten++ > 5) {
|
||
|
NetWrap.stage = 0;
|
||
|
stoon_kill(&NetWrap.stoon);
|
||
|
|
||
|
if(NetWrap.isHost) {
|
||
|
net_server_init();
|
||
|
} else {
|
||
|
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();
|
||
|
net_client_connect(ip, port);
|
||
|
}
|
||
|
|
||
|
//screenMain.lblpeerdata->txt = strdup("Connection successful.");
|
||
|
}
|
||
|
} else NetWrap.gotten = 0;
|
||
|
|
||
|
NetWrap.timeout = CurrentTime + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#include<signal.h>
|
||
|
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 bool onqualitypress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
k3GraphicalReduction = (k3GraphicalReduction + 1) % 3;
|
||
|
return true;
|
||
|
}
|
||
|
static bool onresolpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
struct k3MTextButton *btn = (void*) ev->target;
|
||
|
|
||
|
ResolPercentage -= 10;
|
||
|
|
||
|
if(ResolPercentage <= 0) {
|
||
|
ResolPercentage = 100;
|
||
|
}
|
||
|
|
||
|
const char *txt = NULL;
|
||
|
|
||
|
switch(ResolPercentage) {
|
||
|
case 10:
|
||
|
txt = "10%";
|
||
|
break;
|
||
|
case 20:
|
||
|
txt = "20%";
|
||
|
break;
|
||
|
case 30:
|
||
|
txt = "30%";
|
||
|
break;
|
||
|
case 40:
|
||
|
txt = "40%";
|
||
|
break;
|
||
|
case 50:
|
||
|
txt = "50%";
|
||
|
break;
|
||
|
case 60:
|
||
|
txt = "60%";
|
||
|
break;
|
||
|
case 70:
|
||
|
txt = "70%";
|
||
|
break;
|
||
|
case 80:
|
||
|
txt = "80%";
|
||
|
break;
|
||
|
case 90:
|
||
|
txt = "90%";
|
||
|
break;
|
||
|
case 100:
|
||
|
txt = "Full Resolution";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
btn->txt = strdup(txt);
|
||
|
|
||
|
fix_resol();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
static bool ontexrespress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
TextureResolutionReduction = (TextureResolutionReduction + 1) % 3;
|
||
|
refresh_textures();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
static bool onresumepress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
UiActive = NULL;
|
||
|
set_ui_mode(0);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
#ifdef LOCALHOST_ONLY
|
||
|
static bool onhostpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
net_server_init();
|
||
|
return true;
|
||
|
}
|
||
|
static bool onjoinpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
net_client_init();
|
||
|
net_client_connect("127.0.0.1", 26656);
|
||
|
return true;
|
||
|
}
|
||
|
static bool onconnectpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
return true;
|
||
|
}
|
||
|
#else
|
||
|
static bool onhostpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
screenMain.btnhost->disabled = 1;
|
||
|
screenMain.btnjoin->disabled = 1;
|
||
|
|
||
|
NetWrap.stage = 1;
|
||
|
NetWrap.timeout = glfwGetTime() + 0;
|
||
|
NetWrap.isHost = 1;
|
||
|
NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
static bool onjoinpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
screenMain.btnhost->disabled = 1;
|
||
|
screenMain.btnjoin->disabled = 1;
|
||
|
|
||
|
NetWrap.stage = 1;
|
||
|
NetWrap.timeout = glfwGetTime() + 0;
|
||
|
NetWrap.isHost = 0;
|
||
|
NetWrap.stoon = stoon_init("stun.easybell.de", 3478, 26656);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
static bool onconnectpress(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
const char *name = glfwGetClipboardString(NULL);
|
||
|
|
||
|
while(isspace(*name)) name++;
|
||
|
|
||
|
if(strlen(name) < STOON_CONN_INFO_SIZE * 2) {
|
||
|
goto bad;
|
||
|
}
|
||
|
|
||
|
for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) {
|
||
|
int b = 0;
|
||
|
|
||
|
if(name[i * 2 + 0] >= '0' && name[i * 2 + 0] <= '9') {
|
||
|
b |= (name[i * 2 + 0] - '0') << 4;
|
||
|
} else if(name[i * 2 + 0] >= 'a' && name[i * 2 + 0] <= 'f') {
|
||
|
b |= (name[i * 2 + 0] - 'a' + 10) << 4;
|
||
|
} else goto bad;
|
||
|
|
||
|
if(name[i * 2 + 1] >= '0' && name[i * 2 + 1] <= '9') {
|
||
|
b |= (name[i * 2 + 1] - '0') << 0;
|
||
|
} else if(name[i * 2 + 1] >= 'a' && name[i * 2 + 1] <= 'f') {
|
||
|
b |= (name[i * 2 + 1] - 'a' + 10) << 0;
|
||
|
} else goto bad;
|
||
|
|
||
|
NetWrap.otherpeer[i] = b;
|
||
|
}
|
||
|
|
||
|
NetWrap.stage = 3;
|
||
|
screenMain.btnconnect->disabled = 1;
|
||
|
|
||
|
return true;
|
||
|
|
||
|
bad:
|
||
|
screenMain.lblpeerdata->txt = strdup("Incorrect peer conndata.");
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
#endif
|
||
|
static bool onconsoleenter(struct k3MEvent *ev, uint8_t *ud) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
|
||
|
netwrap_step();
|
||
|
|
||
|
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);
|
||
|
}
|