/* * Copyright (C) 2018 Tomasz Kramkowski * SPDX-License-Identifier: MIT */ #include #include #include "camera.h" #include "eprintf.h" #include "gl.h" #include "glprog.h" #include "linmath.h" #include "math.h" #include "model.h" #include "nelem.h" enum { WIDTH = 800, HEIGHT = 600, }; #define FOV (PI * 0.5) void viewport(GLuint proj, int width, int height) { mat4x4 pers; gl_viewport(0, 0, width, height); mat4x4_perspective(pers, FOV, (float)width / (float)height, 0.1, 100); gl_uni_setm4fv(proj, 1, GL_FALSE, (GLfloat *)pers); } void loadmodel(struct model *mdl, char *name) { FILE *f; struct fmd fmd; f = fopen(name, "rb"); if (f == NULL) eprintf("Could not open '%s':", name); fmd_load(&fmd, f); fclose(f); model_load(mdl, &fmd); fmd_free(&fmd); } int main(int argc, char **argv) { bool running = true; SDL_Window *win; SDL_GLContext *glc; struct model mdl; struct camera cam = { .pos = { 0.0, 0.0, 3.0 }, .yaw = -PI/2 }; struct { bool w, a, s, d, spc, lct; } key = { 0 }; vec4 lipos; float tlast; setprogname(argc >= 1 && argv[0] != NULL ? argv[0] : "faqe"); if (SDL_Init(SDL_INIT_VIDEO) != 0) eprintf("Failed to initialise SDL: %s", SDL_GetError()); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); win = SDL_CreateWindow("faqe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (win == NULL) eprintf("Failed to create window: %s", SDL_GetError()); SDL_SetRelativeMouseMode(SDL_TRUE); glc = SDL_GL_CreateContext(win); if (glc == NULL) eprintf("Failed to create OpenGL context: %s", SDL_GetError()); gl_load(SDL_GL_GetProcAddress); glprog_init(); loadmodel(&mdl, "assets/utah.fmd"); gl_prog_use(prog.main.prog); viewport(prog.main.uni.proj, WIDTH, HEIGHT); gl_enable(GL_DEPTH_TEST); gl_enable(GL_FRAMEBUFFER_SRGB); gl_enable(GL_CULL_FACE); tlast = SDL_GetTicks() * 0.0001; while (running) { mat4x4 model, view; vec3 dir, right, motion = { 0 }, strafe = { 0 }; SDL_Event ev; float tdiff, tnow; while (SDL_PollEvent(&ev)) { bool down; switch (ev.type) { case SDL_MOUSEMOTION: cam.yaw += ev.motion.xrel * 0.005; cam.pitch += ev.motion.yrel * -0.005; camera_clamp(&cam); break; case SDL_KEYDOWN: case SDL_KEYUP: down = ev.key.state == SDL_PRESSED; switch (ev.key.keysym.sym) { case SDLK_w: key.w = down; break; case SDLK_a: key.a = down; break; case SDLK_s: key.s = down; break; case SDLK_d: key.d = down; break; case SDLK_SPACE: key.spc = down; break; case SDLK_LCTRL: key.lct = down; break; } if (ev.key.state != SDL_RELEASED || ev.key.keysym.sym != SDLK_ESCAPE) break; /* fallthrough */ case SDL_QUIT: running = false; break; case SDL_WINDOWEVENT: switch (ev.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: viewport(prog.main.uni.proj, ev.window.data1, ev.window.data2); } break; } } tnow = SDL_GetTicks() * 0.001; tdiff = tnow - tlast; tlast = tnow; camera_dir(dir, &cam); vec3_mul_cross(right, dir, CAM_UP); vec3_norm(right, right); if (key.w != key.s) vec3_scale(motion, dir, tdiff * (key.w ? 1 : -1)); if (key.a != key.d) vec3_scale(strafe, right, tdiff * (key.d ? 1 : -1)); if (key.spc != key.lct) cam.pos[1] += tdiff * (key.spc ? 1 : -1); vec3_add(cam.pos, cam.pos, strafe); vec3_add(cam.pos, cam.pos, motion); camera_lookat(view, &cam); lipos[0] = 10.0 * cosf(tnow / 10) * cosf(tnow / 20); lipos[1] = 10.0 * sinf(tnow / 10); lipos[2] = 10.0 * cosf(tnow / 10) * sinf(tnow / 20); gl_clearcolor(0.2, 0.3, 0.3, 1.0); gl_clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); mat4x4_identity(model); gl_prog_use(prog.main.prog); gl_uni_setm4fv(prog.main.uni.view, 1, GL_FALSE, (GLfloat *)view); gl_uni_setm4fv(prog.main.uni.model, 1, GL_FALSE, (GLfloat *)model); gl_uni_set3fv(prog.main.uni.light, 1, lipos); model_render(&mdl); gl_va_bind(0); SDL_GL_SwapWindow(win); } SDL_GL_DeleteContext(glc); SDL_DestroyWindow(win); SDL_Quit(); return 0; }