diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 149 |
1 files changed, 118 insertions, 31 deletions
| @@ -31,9 +31,13 @@ static constexpr R Fovy = (R)(90 * TO_RAD); | |||
| 31 | static constexpr R Near = 0.1f; | 31 | static constexpr R Near = 0.1f; |
| 32 | static constexpr R Far = 100.0f; | 32 | static constexpr R Far = 100.0f; |
| 33 | 33 | ||
| 34 | #define DEBUG_EVENT_LOOP 1 | 34 | static constexpr size_t MaxTextures = 256; |
| 35 | 35 | ||
| 36 | #ifdef DEBUG_EVENT_LOOP | 36 | static const char* StateFile = "game.bin"; |
| 37 | |||
| 38 | #define DEBUG_EVENT_LOOP 0 | ||
| 39 | |||
| 40 | #if DEBUG_EVENT_LOOP | ||
| 37 | #define EVENT_LOOP_PRINT printf | 41 | #define EVENT_LOOP_PRINT printf |
| 38 | #else | 42 | #else |
| 39 | #define EVENT_LOOP_PRINT(...) | 43 | #define EVENT_LOOP_PRINT(...) |
| @@ -68,13 +72,55 @@ typedef struct State { | |||
| 68 | SDL_Window* window; | 72 | SDL_Window* window; |
| 69 | void* gfx_mem; | 73 | void* gfx_mem; |
| 70 | swgfx* gfx; | 74 | swgfx* gfx; |
| 71 | Model* model; | ||
| 72 | sgImage texture; | ||
| 73 | Camera camera; | 75 | Camera camera; |
| 74 | CameraController camera_controller; | 76 | CameraController camera_controller; |
| 75 | Uint64 last_tick; | 77 | Uint64 last_tick; |
| 78 | Model* model; | ||
| 79 | size_t numTextures; | ||
| 80 | sgImage textures[MaxTextures]; | ||
| 76 | } State; | 81 | } State; |
| 77 | 82 | ||
| 83 | static bool StateWrite(const State* state, FILE* file) { | ||
| 84 | assert(state); | ||
| 85 | assert(file); | ||
| 86 | return fwrite(&state->camera, sizeof(state->camera), 1, file) == 1; | ||
| 87 | } | ||
| 88 | |||
| 89 | static bool StateRead(FILE* file, State* state) { | ||
| 90 | assert(file); | ||
| 91 | assert(state); | ||
| 92 | Camera camera; | ||
| 93 | if (fread(&camera, sizeof(camera), 1, file) != 1) { | ||
| 94 | return false; | ||
| 95 | } | ||
| 96 | state->camera = camera; | ||
| 97 | return true; | ||
| 98 | } | ||
| 99 | |||
| 100 | static bool StateSave(const State* state, const char* path) { | ||
| 101 | assert(state); | ||
| 102 | assert(path); | ||
| 103 | FILE* file = fopen(path, "wb"); | ||
| 104 | if (!file) { | ||
| 105 | return false; | ||
| 106 | } | ||
| 107 | const bool result = StateWrite(state, file); | ||
| 108 | fclose(file); | ||
| 109 | return result; | ||
| 110 | } | ||
| 111 | |||
| 112 | static bool StateLoad(const char* path, State* state) { | ||
| 113 | assert(path); | ||
| 114 | assert(state); | ||
| 115 | FILE* file = fopen(path, "rb"); | ||
| 116 | if (!file) { | ||
| 117 | return false; | ||
| 118 | } | ||
| 119 | const bool result = StateRead(file, state); | ||
| 120 | fclose(file); | ||
| 121 | return result; | ||
| 122 | } | ||
| 123 | |||
| 78 | static sgVec3 SgVec3FromMathVec3(vec3 v) { | 124 | static sgVec3 SgVec3FromMathVec3(vec3 v) { |
| 79 | return (sgVec3){v.x, v.y, v.z}; | 125 | return (sgVec3){v.x, v.y, v.z}; |
| 80 | } | 126 | } |
| @@ -82,8 +128,9 @@ static sgVec3 SgVec3FromMathVec3(vec3 v) { | |||
| 82 | static CameraCommand CameraCommandFromInput( | 128 | static CameraCommand CameraCommandFromInput( |
| 83 | const bool* keyboard_state, const SDL_MouseButtonFlags mouse_flags) { | 129 | const bool* keyboard_state, const SDL_MouseButtonFlags mouse_flags) { |
| 84 | assert(keyboard_state); | 130 | assert(keyboard_state); |
| 85 | if (keyboard_state[SDL_SCANCODE_W]) { | 131 | // Control is used for save/load/quit, etc. |
| 86 | printf("W: %d\n", keyboard_state[SDL_SCANCODE_W]); | 132 | if (keyboard_state[SDL_SCANCODE_LCTRL]) { |
| 133 | return (CameraCommand){}; | ||
| 87 | } | 134 | } |
| 88 | return (CameraCommand){ | 135 | return (CameraCommand){ |
| 89 | .CameraMoveLeft = keyboard_state[SDL_SCANCODE_A], | 136 | .CameraMoveLeft = keyboard_state[SDL_SCANCODE_A], |
| @@ -146,22 +193,34 @@ static bool Update(State* state, R dt) { | |||
| 146 | return true; | 193 | return true; |
| 147 | } | 194 | } |
| 148 | 195 | ||
| 149 | static void RenderIndexedModel(swgfx* gfx, const IndexedModel* model) { | 196 | static void RenderIndexedModel(swgfx* gfx, const IndexedModel* model, const ModelObject* object) { |
| 150 | assert(gfx); | 197 | assert(gfx); |
| 151 | assert(model); | 198 | assert(model); |
| 152 | const sgTriIdx* tris = (const sgTriIdx*)(model->data + model->offsetTris); | 199 | assert(object); |
| 200 | assert((object->offset + object->count) <= model->numTris); | ||
| 201 | const sgTriIdx* tris = (const sgTriIdx*)(model->data + model->offsetTris) + object->offset; | ||
| 153 | const sgVec3* positions = (const sgVec3*) (model->data + model->offsetPositions); | 202 | const sgVec3* positions = (const sgVec3*) (model->data + model->offsetPositions); |
| 154 | const sgVec2* texcoords = (const sgVec2*)(model->data + model->offsetTexcoords); | 203 | const sgVec2* texcoords = (const sgVec2*) (model->data + model->offsetTexcoords); |
| 155 | sgTrianglesIndexedNonUniform(gfx, model->numTris, tris, positions, texcoords); | 204 | sgTrianglesIndexedNonUniform(gfx, object->count, tris, positions, texcoords); |
| 156 | } | 205 | } |
| 157 | 206 | ||
| 158 | static void RenderModel(swgfx* gfx, const Model* model) { | 207 | static void RenderModel(swgfx* gfx, const sgImage* textures, const Model* model) { |
| 159 | assert(gfx); | 208 | assert(gfx); |
| 209 | assert(textures); | ||
| 160 | assert(model); | 210 | assert(model); |
| 161 | switch (model->type) { | 211 | const ModelObject* objects = modelObjects(model); |
| 162 | case ModelTypeIndexed: RenderIndexedModel(gfx, &model->indexed); break; | 212 | for (size_t i = 0; i < model->numObjects; ++i) { |
| 163 | case ModelTypeFlat: /* TODO: Render flat models. */ break; | 213 | const ModelObject* object = &objects[i]; |
| 164 | default: assert(false); break; | 214 | // TODO: This indexing into the textures array assumes that we have loaded a |
| 215 | // single model. Generalize later. | ||
| 216 | assert((size_t)object->material < MaxTextures); | ||
| 217 | const sgImage* texture = &textures[object->material]; | ||
| 218 | sgTexture(gfx, texture); | ||
| 219 | switch (model->type) { | ||
| 220 | case ModelTypeIndexed: RenderIndexedModel(gfx, &model->indexed, object); break; | ||
| 221 | case ModelTypeFlat: /* TODO: Render flat models. */ break; | ||
| 222 | default: assert(false); break; | ||
| 223 | } | ||
| 165 | } | 224 | } |
| 166 | } | 225 | } |
| 167 | 226 | ||
| @@ -225,15 +284,19 @@ static bool Render(State* state) { | |||
| 225 | sgModelId(state->gfx); | 284 | sgModelId(state->gfx); |
| 226 | sgView(state->gfx, SgVec3FromMathVec3(cam->spatial.p), SgVec3FromMathVec3(cam->spatial.f)); | 285 | sgView(state->gfx, SgVec3FromMathVec3(cam->spatial.p), SgVec3FromMathVec3(cam->spatial.f)); |
| 227 | sgPerspective(state->gfx, cam->fovy, cam->aspect, cam->near, cam->far); | 286 | sgPerspective(state->gfx, cam->fovy, cam->aspect, cam->near, cam->far); |
| 228 | sgTexture(state->gfx, &state->texture); | 287 | RenderModel(state->gfx, state->textures, state->model); |
| 229 | RenderModel(state->gfx, state->model); | 288 | /*const sgIdx indices[3] = {0, 1, 2}; |
| 230 | /*sgIdx indices[3] = {0, 1, 2}; | 289 | const sgVec3 positions[3] = { |
| 231 | sgVec3 positions[3] = { | ||
| 232 | (sgVec3){0, 0, 0}, | 290 | (sgVec3){0, 0, 0}, |
| 233 | (sgVec3){5, 2, 0}, | 291 | (sgVec3){5, 2, 0}, |
| 234 | (sgVec3){8, 8, 0}, | 292 | (sgVec3){8, 8, 0}, |
| 235 | }; | 293 | }; |
| 236 | sgTrianglesIndexed(state->gfx, 3, indices, positions);*/ | 294 | const sgVec2 texcoords[3] = { |
| 295 | (sgVec2){0, 0}, | ||
| 296 | (sgVec2){0.5, 0.5}, | ||
| 297 | (sgVec2){1.0, 1.0}, | ||
| 298 | }; | ||
| 299 | sgTrianglesIndexed(state->gfx, 3, indices, positions, texcoords);*/ | ||
| 237 | sgPresent(state->gfx, WindowDims, window_surface->pixels); | 300 | sgPresent(state->gfx, WindowDims, window_surface->pixels); |
| 238 | 301 | ||
| 239 | if (!SDL_UpdateWindowSurface(state->window)) { | 302 | if (!SDL_UpdateWindowSurface(state->window)) { |
| @@ -284,23 +347,28 @@ static bool Initialize(State* state) { | |||
| 284 | return false; | 347 | return false; |
| 285 | } | 348 | } |
| 286 | 349 | ||
| 287 | const char* model_path = "/home/jeanne/blender/box_textured.mdl"; | 350 | const char* model_path = "/home/jeanne/blender/boxout.mdl"; |
| 288 | if (!(state->model = read_file(model_path))) { | 351 | if (!(state->model = read_file(model_path))) { |
| 289 | fprintf(stderr, "Failed to load model: [%s]\n", model_path); | 352 | fprintf(stderr, "Failed to load model: [%s]\n", model_path); |
| 290 | return false; | 353 | return false; |
| 291 | } | 354 | } |
| 292 | if (state->model->material.diffuseTexture[0] != 0) { | 355 | if ((size_t)state->model->numMaterials > MaxTextures) { |
| 356 | fprintf(stderr, "Model material count is larger than max textures, increase limit\n"); | ||
| 357 | return false; | ||
| 358 | } | ||
| 359 | const ModelMaterial* materials = modelMaterials(state->model); | ||
| 360 | for (size_t i = 0; i < state->model->numMaterials; ++i) { | ||
| 361 | const ModelMaterial* material = &materials[i]; | ||
| 293 | // TODO: When doing lighting, need to gamma-correct here. | 362 | // TODO: When doing lighting, need to gamma-correct here. |
| 294 | sgImage texture = {0}; | 363 | sgImage* texture = &state->textures[state->numTextures++]; |
| 295 | int channels = 0; | 364 | int channels = 0; |
| 296 | constexpr int desired_channels = 4; | 365 | constexpr int desired_channels = 4; |
| 297 | texture.pixels = (sgPixel*)stbi_load(state->model->material.diffuseTexture, &texture.width, &texture.height, &channels, desired_channels); | 366 | texture->pixels = (sgPixel*)stbi_load(material->diffuseTexture, &texture->width, &texture->height, &channels, desired_channels); |
| 298 | if (!texture.pixels) { | 367 | if (!texture->pixels) { |
| 299 | fprintf(stderr, "Failed to read texture: [%s]\n", state->model->material.diffuseTexture); | 368 | fprintf(stderr, "Failed to read texture: [%s]\n", material->diffuseTexture); |
| 300 | return false; | 369 | return false; |
| 301 | } | 370 | } |
| 302 | assert(channels == desired_channels); | 371 | assert(channels == desired_channels); |
| 303 | state->texture = texture; | ||
| 304 | } | 372 | } |
| 305 | 373 | ||
| 306 | Camera* camera = &state->camera; | 374 | Camera* camera = &state->camera; |
| @@ -324,16 +392,19 @@ static bool Initialize(State* state) { | |||
| 324 | static void Shutdown(State* state) { | 392 | static void Shutdown(State* state) { |
| 325 | assert(state); | 393 | assert(state); |
| 326 | 394 | ||
| 327 | if (state->texture.pixels) { | 395 | for (size_t i = 0; i < state->numTextures; ++i) { |
| 328 | free(state->texture.pixels); | 396 | sgImage* texture = &state->textures[i]; |
| 329 | state->texture = (sgImage){0}; | 397 | if (texture->pixels) { |
| 398 | free(texture->pixels); | ||
| 399 | *texture = (sgImage){0}; | ||
| 400 | } | ||
| 330 | } | 401 | } |
| 331 | 402 | ||
| 332 | if (state->model) { | 403 | if (state->model) { |
| 333 | free(state->model); | 404 | free(state->model); |
| 334 | state->model = nullptr; | 405 | state->model = nullptr; |
| 335 | } | 406 | } |
| 336 | 407 | ||
| 337 | if (state->gfx) { | 408 | if (state->gfx) { |
| 338 | sgDel(&state->gfx); | 409 | sgDel(&state->gfx); |
| 339 | } | 410 | } |
| @@ -412,6 +483,22 @@ int main() { | |||
| 412 | case SDLK_D: | 483 | case SDLK_D: |
| 413 | running = false; | 484 | running = false; |
| 414 | break; | 485 | break; |
| 486 | // Save state. | ||
| 487 | case SDLK_S: | ||
| 488 | if (StateSave(&state, StateFile)) { | ||
| 489 | fprintf(stderr, "State saved\n"); | ||
| 490 | } else { | ||
| 491 | fprintf(stderr, "Failed to save state\n"); | ||
| 492 | } | ||
| 493 | break; | ||
| 494 | // Load state. | ||
| 495 | case SDLK_L: | ||
| 496 | if (StateLoad(StateFile, &state)) { | ||
| 497 | fprintf(stderr, "State loaded\n"); | ||
| 498 | } else { | ||
| 499 | fprintf(stderr, "Failed to load state\n"); | ||
| 500 | } | ||
| 501 | break; | ||
| 415 | default: | 502 | default: |
| 416 | break; | 503 | break; |
| 417 | } | 504 | } |
