From cd6d2340a6fcbd2376aad291081ae5d667bd3317 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 31 Jan 2026 16:12:21 -0800 Subject: Add a ground. Add support for multiple objects and materials in MDL --- src/main.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 118 insertions(+), 31 deletions(-) (limited to 'src/main.c') diff --git a/src/main.c b/src/main.c index dcd0335..225af5f 100644 --- a/src/main.c +++ b/src/main.c @@ -31,9 +31,13 @@ static constexpr R Fovy = (R)(90 * TO_RAD); static constexpr R Near = 0.1f; static constexpr R Far = 100.0f; -#define DEBUG_EVENT_LOOP 1 +static constexpr size_t MaxTextures = 256; -#ifdef DEBUG_EVENT_LOOP +static const char* StateFile = "game.bin"; + +#define DEBUG_EVENT_LOOP 0 + +#if DEBUG_EVENT_LOOP #define EVENT_LOOP_PRINT printf #else #define EVENT_LOOP_PRINT(...) @@ -68,13 +72,55 @@ typedef struct State { SDL_Window* window; void* gfx_mem; swgfx* gfx; - Model* model; - sgImage texture; Camera camera; CameraController camera_controller; Uint64 last_tick; + Model* model; + size_t numTextures; + sgImage textures[MaxTextures]; } State; +static bool StateWrite(const State* state, FILE* file) { + assert(state); + assert(file); + return fwrite(&state->camera, sizeof(state->camera), 1, file) == 1; +} + +static bool StateRead(FILE* file, State* state) { + assert(file); + assert(state); + Camera camera; + if (fread(&camera, sizeof(camera), 1, file) != 1) { + return false; + } + state->camera = camera; + return true; +} + +static bool StateSave(const State* state, const char* path) { + assert(state); + assert(path); + FILE* file = fopen(path, "wb"); + if (!file) { + return false; + } + const bool result = StateWrite(state, file); + fclose(file); + return result; +} + +static bool StateLoad(const char* path, State* state) { + assert(path); + assert(state); + FILE* file = fopen(path, "rb"); + if (!file) { + return false; + } + const bool result = StateRead(file, state); + fclose(file); + return result; +} + static sgVec3 SgVec3FromMathVec3(vec3 v) { return (sgVec3){v.x, v.y, v.z}; } @@ -82,8 +128,9 @@ static sgVec3 SgVec3FromMathVec3(vec3 v) { static CameraCommand CameraCommandFromInput( const bool* keyboard_state, const SDL_MouseButtonFlags mouse_flags) { assert(keyboard_state); - if (keyboard_state[SDL_SCANCODE_W]) { - printf("W: %d\n", keyboard_state[SDL_SCANCODE_W]); + // Control is used for save/load/quit, etc. + if (keyboard_state[SDL_SCANCODE_LCTRL]) { + return (CameraCommand){}; } return (CameraCommand){ .CameraMoveLeft = keyboard_state[SDL_SCANCODE_A], @@ -146,22 +193,34 @@ static bool Update(State* state, R dt) { return true; } -static void RenderIndexedModel(swgfx* gfx, const IndexedModel* model) { +static void RenderIndexedModel(swgfx* gfx, const IndexedModel* model, const ModelObject* object) { assert(gfx); assert(model); - const sgTriIdx* tris = (const sgTriIdx*)(model->data + model->offsetTris); + assert(object); + assert((object->offset + object->count) <= model->numTris); + const sgTriIdx* tris = (const sgTriIdx*)(model->data + model->offsetTris) + object->offset; const sgVec3* positions = (const sgVec3*) (model->data + model->offsetPositions); - const sgVec2* texcoords = (const sgVec2*)(model->data + model->offsetTexcoords); - sgTrianglesIndexedNonUniform(gfx, model->numTris, tris, positions, texcoords); + const sgVec2* texcoords = (const sgVec2*) (model->data + model->offsetTexcoords); + sgTrianglesIndexedNonUniform(gfx, object->count, tris, positions, texcoords); } -static void RenderModel(swgfx* gfx, const Model* model) { +static void RenderModel(swgfx* gfx, const sgImage* textures, const Model* model) { assert(gfx); + assert(textures); assert(model); - switch (model->type) { - case ModelTypeIndexed: RenderIndexedModel(gfx, &model->indexed); break; - case ModelTypeFlat: /* TODO: Render flat models. */ break; - default: assert(false); break; + const ModelObject* objects = modelObjects(model); + for (size_t i = 0; i < model->numObjects; ++i) { + const ModelObject* object = &objects[i]; + // TODO: This indexing into the textures array assumes that we have loaded a + // single model. Generalize later. + assert((size_t)object->material < MaxTextures); + const sgImage* texture = &textures[object->material]; + sgTexture(gfx, texture); + switch (model->type) { + case ModelTypeIndexed: RenderIndexedModel(gfx, &model->indexed, object); break; + case ModelTypeFlat: /* TODO: Render flat models. */ break; + default: assert(false); break; + } } } @@ -225,15 +284,19 @@ static bool Render(State* state) { sgModelId(state->gfx); sgView(state->gfx, SgVec3FromMathVec3(cam->spatial.p), SgVec3FromMathVec3(cam->spatial.f)); sgPerspective(state->gfx, cam->fovy, cam->aspect, cam->near, cam->far); - sgTexture(state->gfx, &state->texture); - RenderModel(state->gfx, state->model); - /*sgIdx indices[3] = {0, 1, 2}; - sgVec3 positions[3] = { + RenderModel(state->gfx, state->textures, state->model); + /*const sgIdx indices[3] = {0, 1, 2}; + const sgVec3 positions[3] = { (sgVec3){0, 0, 0}, (sgVec3){5, 2, 0}, (sgVec3){8, 8, 0}, }; - sgTrianglesIndexed(state->gfx, 3, indices, positions);*/ + const sgVec2 texcoords[3] = { + (sgVec2){0, 0}, + (sgVec2){0.5, 0.5}, + (sgVec2){1.0, 1.0}, + }; + sgTrianglesIndexed(state->gfx, 3, indices, positions, texcoords);*/ sgPresent(state->gfx, WindowDims, window_surface->pixels); if (!SDL_UpdateWindowSurface(state->window)) { @@ -284,23 +347,28 @@ static bool Initialize(State* state) { return false; } - const char* model_path = "/home/jeanne/blender/box_textured.mdl"; + const char* model_path = "/home/jeanne/blender/boxout.mdl"; if (!(state->model = read_file(model_path))) { fprintf(stderr, "Failed to load model: [%s]\n", model_path); return false; } - if (state->model->material.diffuseTexture[0] != 0) { + if ((size_t)state->model->numMaterials > MaxTextures) { + fprintf(stderr, "Model material count is larger than max textures, increase limit\n"); + return false; + } + const ModelMaterial* materials = modelMaterials(state->model); + for (size_t i = 0; i < state->model->numMaterials; ++i) { + const ModelMaterial* material = &materials[i]; // TODO: When doing lighting, need to gamma-correct here. - sgImage texture = {0}; + sgImage* texture = &state->textures[state->numTextures++]; int channels = 0; constexpr int desired_channels = 4; - texture.pixels = (sgPixel*)stbi_load(state->model->material.diffuseTexture, &texture.width, &texture.height, &channels, desired_channels); - if (!texture.pixels) { - fprintf(stderr, "Failed to read texture: [%s]\n", state->model->material.diffuseTexture); + texture->pixels = (sgPixel*)stbi_load(material->diffuseTexture, &texture->width, &texture->height, &channels, desired_channels); + if (!texture->pixels) { + fprintf(stderr, "Failed to read texture: [%s]\n", material->diffuseTexture); return false; } assert(channels == desired_channels); - state->texture = texture; } Camera* camera = &state->camera; @@ -324,16 +392,19 @@ static bool Initialize(State* state) { static void Shutdown(State* state) { assert(state); - if (state->texture.pixels) { - free(state->texture.pixels); - state->texture = (sgImage){0}; + for (size_t i = 0; i < state->numTextures; ++i) { + sgImage* texture = &state->textures[i]; + if (texture->pixels) { + free(texture->pixels); + *texture = (sgImage){0}; + } } if (state->model) { free(state->model); state->model = nullptr; } - + if (state->gfx) { sgDel(&state->gfx); } @@ -412,6 +483,22 @@ int main() { case SDLK_D: running = false; break; + // Save state. + case SDLK_S: + if (StateSave(&state, StateFile)) { + fprintf(stderr, "State saved\n"); + } else { + fprintf(stderr, "Failed to save state\n"); + } + break; + // Load state. + case SDLK_L: + if (StateLoad(StateFile, &state)) { + fprintf(stderr, "State loaded\n"); + } else { + fprintf(stderr, "Failed to load state\n"); + } + break; default: break; } -- cgit v1.2.3