diff options
24 files changed, 734 insertions, 114 deletions
| @@ -0,0 +1,3 @@ | |||
| 1 | CompileFlags: | ||
| 2 | Add: | ||
| 3 | - -I./vendor/raylib-6.0_linux_amd64/include | ||
| @@ -1,31 +1,34 @@ | |||
| 1 | OS := $(shell uname) | 1 | OS := $(shell uname) |
| 2 | ifeq ($(OS), Linux) | 2 | ifeq ($(OS), Linux) |
| 3 | SYSTEM = linux_amd64 | 3 | SYSTEM = linux_amd64 |
| 4 | else ifeq ($(OS), Darwin) | 4 | else ifeq ($(OS), Darwin) |
| 5 | SYSTEM = macos | 5 | SYSTEM = macos |
| 6 | else ifeq ($(OS), WindowsNT) | 6 | else ifeq ($(OS), WindowsNT) |
| 7 | SYSTEM = windows | 7 | SYSTEM = windows |
| 8 | else | 8 | else |
| 9 | SYSTEM = unknown | 9 | SYSTEM = unknown |
| 10 | endif | 10 | endif |
| 11 | 11 | ||
| 12 | CC := clang | 12 | CC := clang |
| 13 | RAYLIB_VER := raylib-6.0_$(SYSTEM) | 13 | RAYLIB_VER := raylib-6.0_$(SYSTEM) |
| 14 | CFLAGS := -std=c99 -v -g -I./vendor/$(RAYLIB_VER)/include | 14 | CFLAGS := -std=c99 -v -g -I./vendor/$(RAYLIB_VER)/include |
| 15 | LDFLAGS := -L./vendor/$(RAYLIB_VER)/lib -Wl,-Bstatic -lraylib -Wl,-Bdynamic -lm -lpthread -ldl -lrt -lX11 | 15 | LDFLAGS := ./vendor/$(RAYLIB_VER)/lib/libraylib.a -lm |
| 16 | GAME := bin/stalag | 16 | GAME := bin/stalag |
| 17 | HEXDUMP := bin/hexdump | 17 | HEXDUMP := bin/hexdump |
| 18 | SOURCES := main.c | 18 | SOURCES := main.c map.c game.c |
| 19 | |||
| 20 | ifeq ($(SYSTEM), linux_amd64) | ||
| 21 | LDFLAGS += -lX11 | ||
| 22 | endif | ||
| 19 | 23 | ||
| 20 | # Check if macOS and then append proper CFLAGS. | ||
| 21 | ifeq ($(SYSTEM), macos) | 24 | ifeq ($(SYSTEM), macos) |
| 22 | CFLAGS += -framework CoreVideo -framework IOKit -framework Cocoa -framework GLUT -framework OpenGL | 25 | LDFLAGS += -framework CoreVideo -framework IOKit -framework Cocoa -framework GLUT -framework OpenGL |
| 23 | endif | 26 | endif |
| 24 | 27 | ||
| 25 | all: info mkdirs $(HEXDUMP) $(GAME) | 28 | all: info mkdirs $(HEXDUMP) $(GAME) |
| 26 | 29 | ||
| 27 | .PHONY: info mkdirs clean | 30 | .PHONY: info mkdirs clean |
| 28 | 31 | ||
| 29 | info: # Print out information about the build | 32 | info: # Print out information about the build |
| 30 | $(info CC : $(CC)) | 33 | $(info CC : $(CC)) |
| 31 | $(info SYSTEM : $(SYSTEM)) | 34 | $(info SYSTEM : $(SYSTEM)) |
| @@ -43,4 +46,4 @@ mkdirs: | |||
| 43 | mkdir -p bin | 46 | mkdir -p bin |
| 44 | 47 | ||
| 45 | clean: | 48 | clean: |
| 46 | -rm $(GAME) $(HEXDUMP) \ No newline at end of file | 49 | -rm $(GAME) $(HEXDUMP) |
| @@ -0,0 +1,117 @@ | |||
| 1 | #ifndef ALL_H | ||
| 2 | #define ALL_H | ||
| 3 | |||
| 4 | #include "raylib.h" | ||
| 5 | #include "raymath.h" | ||
| 6 | #include "rlgl.h" | ||
| 7 | #include "config.h" | ||
| 8 | |||
| 9 | // Resolve conflicts between Raylib and nonstd.h | ||
| 10 | #define Color NS_Color | ||
| 11 | #include "libraries/nonstd.h" | ||
| 12 | #undef Color | ||
| 13 | |||
| 14 | #include "libraries/vfs.h" | ||
| 15 | #include <stdbool.h> | ||
| 16 | #include <stddef.h> | ||
| 17 | |||
| 18 | // --- Map Structures --- | ||
| 19 | |||
| 20 | typedef struct { | ||
| 21 | Vector3 p[3]; | ||
| 22 | char texture[64]; | ||
| 23 | float shift[2]; | ||
| 24 | float rotate; | ||
| 25 | float scale[2]; | ||
| 26 | } MapPlane; | ||
| 27 | |||
| 28 | typedef struct { | ||
| 29 | MapPlane *planes; | ||
| 30 | int plane_count; | ||
| 31 | } MapBrush; | ||
| 32 | |||
| 33 | typedef struct { | ||
| 34 | char key[64]; | ||
| 35 | char value[256]; | ||
| 36 | } MapProperty; | ||
| 37 | |||
| 38 | typedef struct { | ||
| 39 | MapProperty *properties; | ||
| 40 | int property_count; | ||
| 41 | MapBrush *brushes; | ||
| 42 | int brush_count; | ||
| 43 | } MapEntity; | ||
| 44 | |||
| 45 | typedef struct { | ||
| 46 | MapEntity *entities; | ||
| 47 | int entity_count; | ||
| 48 | } Map; | ||
| 49 | |||
| 50 | typedef struct { | ||
| 51 | const char *data; | ||
| 52 | size_t length; | ||
| 53 | size_t pos; | ||
| 54 | } MapParser; | ||
| 55 | |||
| 56 | typedef struct { | ||
| 57 | Vector3 normal; | ||
| 58 | float dist; | ||
| 59 | } Plane; | ||
| 60 | |||
| 61 | typedef struct { | ||
| 62 | Vector3 *verts; | ||
| 63 | int count; | ||
| 64 | } Polygon; | ||
| 65 | |||
| 66 | typedef struct { | ||
| 67 | char texture[64]; | ||
| 68 | array(float) vertices; | ||
| 69 | array(float) texcoords; | ||
| 70 | array(float) normals; | ||
| 71 | } TextureGroup; | ||
| 72 | |||
| 73 | typedef struct { | ||
| 74 | char name[64]; | ||
| 75 | Texture2D tex; | ||
| 76 | } CachedTexture; | ||
| 77 | |||
| 78 | // --- Game State --- | ||
| 79 | |||
| 80 | typedef struct { | ||
| 81 | Camera camera; | ||
| 82 | Model *world_models; | ||
| 83 | int world_model_count; | ||
| 84 | bool cursor_captured; | ||
| 85 | bool vsync; | ||
| 86 | } GameState; | ||
| 87 | |||
| 88 | extern GameState game; | ||
| 89 | |||
| 90 | // --- Prototypes --- | ||
| 91 | |||
| 92 | // Map | ||
| 93 | char map_peek(MapParser *p); | ||
| 94 | char map_get(MapParser *p); | ||
| 95 | void map_skip_whitespace(MapParser *p); | ||
| 96 | bool map_expect(MapParser *p, char expected); | ||
| 97 | void map_parse_token(MapParser *p, char *buffer, int size); | ||
| 98 | Vector3 map_parse_vector(MapParser *p); | ||
| 99 | Map ParseMap(const char *filename); | ||
| 100 | void FreeMap(Map map); | ||
| 101 | |||
| 102 | // Geometry | ||
| 103 | Plane PlaneFromPoints(Vector3 p1, Vector3 p2, Vector3 p3); | ||
| 104 | void PolyFree(Polygon p); | ||
| 105 | Polygon PolyClip(Polygon poly, Plane plane); | ||
| 106 | Polygon CreateLargeQuad(Plane plane); | ||
| 107 | Vector2 GetUV(Vector3 p, MapPlane *mp, Plane plane); | ||
| 108 | |||
| 109 | // Game | ||
| 110 | Texture2D GetTexture(const char *name); | ||
| 111 | void InitGame(void); | ||
| 112 | void UpdateGame(void); | ||
| 113 | void DrawGame(void); | ||
| 114 | bool LoadMap(const char *filename); | ||
| 115 | void UnloadMap(void); | ||
| 116 | |||
| 117 | #endif | ||
diff --git a/config.h b/config.h new file mode 100644 index 0000000..28d4604 --- /dev/null +++ b/config.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef CONFIG_H | ||
| 2 | #define CONFIG_H | ||
| 3 | |||
| 4 | #define PLAYER_MOVE_SPEED 400.0f | ||
| 5 | #define PLAYER_ROTATION_SPEED 0.05f | ||
| 6 | #define PLAYER_MOUSE_SENSITIVITY 0.003f | ||
| 7 | |||
| 8 | #endif | ||
| @@ -0,0 +1,306 @@ | |||
| 1 | #include "all.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <ctype.h> | ||
| 5 | |||
| 6 | GameState game; | ||
| 7 | static array(CachedTexture) texture_cache; | ||
| 8 | |||
| 9 | Texture2D GetTexture(const char *name) { | ||
| 10 | for (int i = 0; i < texture_cache.length; i++) { | ||
| 11 | if (strcmp(texture_cache.data[i].name, name) == 0) return texture_cache.data[i].tex; | ||
| 12 | } | ||
| 13 | |||
| 14 | char lname[256]; | ||
| 15 | strncpy(lname, name, sizeof(lname)); | ||
| 16 | for (int i = 0; lname[i]; i++) lname[i] = tolower(lname[i]); | ||
| 17 | |||
| 18 | char path[256]; | ||
| 19 | void *data = NULL; | ||
| 20 | size_t size = 0; | ||
| 21 | const char *ext = ".jpg"; | ||
| 22 | |||
| 23 | snprintf(path, sizeof(path), "textures/%s.png", lname); | ||
| 24 | data = vfs_read(path, &size); | ||
| 25 | if (data) { | ||
| 26 | ext = ".png"; | ||
| 27 | } else { | ||
| 28 | snprintf(path, sizeof(path), "textures/%s.jpg", lname); | ||
| 29 | data = vfs_read(path, &size); | ||
| 30 | ext = ".jpg"; | ||
| 31 | } | ||
| 32 | |||
| 33 | Texture2D tex = { 0 }; | ||
| 34 | if (data) { | ||
| 35 | Image img = LoadImageFromMemory(ext, data, (int)size); | ||
| 36 | if (img.data) { | ||
| 37 | tex = LoadTextureFromImage(img); | ||
| 38 | UnloadImage(img); | ||
| 39 | } | ||
| 40 | vfs_free(data); | ||
| 41 | } | ||
| 42 | |||
| 43 | if (tex.id == 0) { | ||
| 44 | TraceLog(LOG_WARNING, "Failed to load texture: '%s' (tried path: '%s')", name, path); | ||
| 45 | Image img = GenImageChecked(64, 64, 8, 8, (Color){128, 128, 128, 255}, (Color){200, 200, 200, 255}); | ||
| 46 | tex = LoadTextureFromImage(img); | ||
| 47 | UnloadImage(img); | ||
| 48 | } | ||
| 49 | |||
| 50 | if (tex.id != 0) { | ||
| 51 | GenTextureMipmaps(&tex); | ||
| 52 | SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR); | ||
| 53 | SetTextureWrap(tex, TEXTURE_WRAP_REPEAT); | ||
| 54 | |||
| 55 | CachedTexture cached; | ||
| 56 | strncpy(cached.name, name, sizeof(cached.name)); | ||
| 57 | cached.tex = tex; | ||
| 58 | array_push(texture_cache, cached); | ||
| 59 | } | ||
| 60 | |||
| 61 | return tex; | ||
| 62 | } | ||
| 63 | |||
| 64 | void UnloadMap(void) { | ||
| 65 | if (game.world_models) { | ||
| 66 | for (int i = 0; i < game.world_model_count; i++) { | ||
| 67 | UnloadModel(game.world_models[i]); | ||
| 68 | } | ||
| 69 | free(game.world_models); | ||
| 70 | game.world_models = NULL; | ||
| 71 | game.world_model_count = 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | for (int i = 0; i < texture_cache.length; i++) { | ||
| 75 | UnloadTexture(texture_cache.data[i].tex); | ||
| 76 | } | ||
| 77 | array_clear(texture_cache); | ||
| 78 | } | ||
| 79 | |||
| 80 | bool LoadMap(const char *filename) { | ||
| 81 | TraceLog(LOG_INFO, "Loading map: %s", filename); | ||
| 82 | |||
| 83 | Map map = ParseMap(filename); | ||
| 84 | if (map.entity_count == 0) { | ||
| 85 | TraceLog(LOG_ERROR, "Failed to load map or map is empty: %s", filename); | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | UnloadMap(); | ||
| 90 | |||
| 91 | array(TextureGroup) groups; | ||
| 92 | array_init(groups); | ||
| 93 | |||
| 94 | // Default camera if no start found | ||
| 95 | game.camera.position = (Vector3){ 0, 10, 0 }; | ||
| 96 | game.camera.target = (Vector3){ 1, 10, 0 }; | ||
| 97 | game.camera.up = (Vector3){ 0, 1, 0 }; | ||
| 98 | game.camera.fovy = 75.0f; | ||
| 99 | game.camera.projection = CAMERA_PERSPECTIVE; | ||
| 100 | |||
| 101 | int total_brushes = 0; | ||
| 102 | for (int i = 0; i < map.entity_count; i++) { | ||
| 103 | MapEntity *e = &map.entities[i]; | ||
| 104 | bool is_world = false; | ||
| 105 | const char *classname = ""; | ||
| 106 | for (int j = 0; j < e->property_count; j++) { | ||
| 107 | if (strcmp(e->properties[j].key, "classname") == 0) { | ||
| 108 | classname = e->properties[j].value; | ||
| 109 | if (strcmp(classname, "worldspawn") == 0) is_world = true; | ||
| 110 | } | ||
| 111 | if (strcmp(e->properties[j].key, "origin") == 0) { | ||
| 112 | float x, y, z; | ||
| 113 | sscanf(e->properties[j].value, "%f %f %f", &x, &y, &z); | ||
| 114 | if (strcmp(classname, "info_player_start") == 0) { | ||
| 115 | game.camera.position = (Vector3){ x, z, -y }; | ||
| 116 | game.camera.target = Vector3Add(game.camera.position, (Vector3){0, 0, 1}); | ||
| 117 | TraceLog(LOG_INFO, "Player spawn set to: %f, %f, %f", game.camera.position.x, game.camera.position.y, game.camera.position.z); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | if (is_world) { | ||
| 123 | total_brushes += e->brush_count; | ||
| 124 | for (int j = 0; j < e->brush_count; j++) { | ||
| 125 | MapBrush *brush = &e->brushes[j]; | ||
| 126 | for (int p_idx = 0; p_idx < brush->plane_count; p_idx++) { | ||
| 127 | MapPlane *mp = &brush->planes[p_idx]; | ||
| 128 | Plane plane = PlaneFromPoints(mp->p[0], mp->p[1], mp->p[2]); | ||
| 129 | |||
| 130 | if (Vector3Length(plane.normal) < 0.5f) continue; | ||
| 131 | |||
| 132 | Polygon poly = CreateLargeQuad(plane); | ||
| 133 | for (int k = 0; k < brush->plane_count; k++) { | ||
| 134 | if (p_idx == k) continue; | ||
| 135 | Plane clipPlane = PlaneFromPoints(brush->planes[k].p[0], brush->planes[k].p[1], brush->planes[k].p[2]); | ||
| 136 | if (Vector3Length(clipPlane.normal) < 0.5f) continue; | ||
| 137 | |||
| 138 | Polygon next = PolyClip(poly, clipPlane); | ||
| 139 | PolyFree(poly); | ||
| 140 | poly = next; | ||
| 141 | } | ||
| 142 | if (poly.count >= 3) { | ||
| 143 | TextureGroup *group = NULL; | ||
| 144 | for (int g = 0; g < groups.length; g++) { | ||
| 145 | if (strcmp(groups.data[g].texture, mp->texture) == 0) { | ||
| 146 | group = &groups.data[g]; | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | if (!group) { | ||
| 151 | TextureGroup new_group = { 0 }; | ||
| 152 | strncpy(new_group.texture, mp->texture, sizeof(new_group.texture)); | ||
| 153 | array_init(new_group.vertices); | ||
| 154 | array_init(new_group.texcoords); | ||
| 155 | array_init(new_group.normals); | ||
| 156 | array_push(groups, new_group); | ||
| 157 | group = &groups.data[groups.length - 1]; | ||
| 158 | } | ||
| 159 | for (int v = 1; v < poly.count - 1; v++) { | ||
| 160 | Vector3 v0 = poly.verts[0]; | ||
| 161 | Vector3 v1 = poly.verts[v+1]; | ||
| 162 | Vector3 v2 = poly.verts[v]; | ||
| 163 | array_push(group->vertices, v0.x); array_push(group->vertices, v0.y); array_push(group->vertices, v0.z); | ||
| 164 | array_push(group->vertices, v1.x); array_push(group->vertices, v1.y); array_push(group->vertices, v1.z); | ||
| 165 | array_push(group->vertices, v2.x); array_push(group->vertices, v2.y); array_push(group->vertices, v2.z); | ||
| 166 | Vector2 uv0 = GetUV(v0, mp, plane); | ||
| 167 | Vector2 uv1 = GetUV(v1, mp, plane); | ||
| 168 | Vector2 uv2 = GetUV(v2, mp, plane); | ||
| 169 | array_push(group->texcoords, uv0.x); array_push(group->texcoords, uv0.y); | ||
| 170 | array_push(group->texcoords, uv1.x); array_push(group->texcoords, uv1.y); | ||
| 171 | array_push(group->texcoords, uv2.x); array_push(group->texcoords, uv2.y); | ||
| 172 | Vector3 out_normal = Vector3Scale(plane.normal, -1.0f); | ||
| 173 | array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z); | ||
| 174 | array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z); | ||
| 175 | array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | PolyFree(poly); | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | array(Model) final_models; | ||
| 185 | array_init(final_models); | ||
| 186 | |||
| 187 | for (int i = 0; i < groups.length; i++) { | ||
| 188 | TextureGroup *g = &groups.data[i]; | ||
| 189 | if (g->vertices.length == 0) continue; | ||
| 190 | Mesh mesh = { 0 }; | ||
| 191 | mesh.vertexCount = (int)g->vertices.length / 3; | ||
| 192 | mesh.triangleCount = mesh.vertexCount / 3; | ||
| 193 | mesh.vertices = (float *)malloc(g->vertices.length * sizeof(float)); | ||
| 194 | memcpy(mesh.vertices, g->vertices.data, g->vertices.length * sizeof(float)); | ||
| 195 | mesh.texcoords = (float *)malloc(g->texcoords.length * sizeof(float)); | ||
| 196 | memcpy(mesh.texcoords, g->texcoords.data, g->texcoords.length * sizeof(float)); | ||
| 197 | mesh.normals = (float *)malloc(g->normals.length * sizeof(float)); | ||
| 198 | memcpy(mesh.normals, g->normals.data, g->normals.length * sizeof(float)); | ||
| 199 | UploadMesh(&mesh, false); | ||
| 200 | Model model = LoadModelFromMesh(mesh); | ||
| 201 | model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = GetTexture(g->texture); | ||
| 202 | array_push(final_models, model); | ||
| 203 | array_free(g->vertices); | ||
| 204 | array_free(g->texcoords); | ||
| 205 | array_free(g->normals); | ||
| 206 | } | ||
| 207 | |||
| 208 | game.world_models = final_models.data; | ||
| 209 | game.world_model_count = (int)final_models.length; | ||
| 210 | array_free(groups); | ||
| 211 | |||
| 212 | FreeMap(map); | ||
| 213 | TraceLog(LOG_INFO, "Processed %d brushes into %d models", total_brushes, game.world_model_count); | ||
| 214 | return true; | ||
| 215 | } | ||
| 216 | |||
| 217 | void InitGame(void) { | ||
| 218 | array_init(texture_cache); | ||
| 219 | game.world_models = NULL; | ||
| 220 | game.world_model_count = 0; | ||
| 221 | |||
| 222 | LoadMap("maps/demo1.map"); | ||
| 223 | |||
| 224 | game.cursor_captured = true; | ||
| 225 | DisableCursor(); | ||
| 226 | } | ||
| 227 | |||
| 228 | void UpdateGame(void) { | ||
| 229 | float dt = GetFrameTime(); | ||
| 230 | |||
| 231 | if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) { | ||
| 232 | game.cursor_captured = !game.cursor_captured; | ||
| 233 | if (game.cursor_captured) DisableCursor(); | ||
| 234 | else EnableCursor(); | ||
| 235 | } | ||
| 236 | |||
| 237 | if (IsKeyPressed(KEY_ONE)) LoadMap("maps/demo1.map"); | ||
| 238 | if (IsKeyPressed(KEY_TWO)) LoadMap("maps/demo2.map"); | ||
| 239 | |||
| 240 | if (IsKeyPressed(KEY_V)) { | ||
| 241 | game.vsync = !game.vsync; | ||
| 242 | if (game.vsync) { | ||
| 243 | SetWindowState(FLAG_VSYNC_HINT); | ||
| 244 | SetTargetFPS(GetMonitorRefreshRate(GetCurrentMonitor())); | ||
| 245 | } else { | ||
| 246 | ClearWindowState(FLAG_VSYNC_HINT); | ||
| 247 | SetTargetFPS(0); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | // Manual first-person movement | ||
| 252 | Vector3 forward = Vector3Normalize(Vector3Subtract(game.camera.target, game.camera.position)); | ||
| 253 | Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, game.camera.up)); | ||
| 254 | |||
| 255 | Vector3 move = { 0 }; | ||
| 256 | if (IsKeyDown(KEY_W)) move = Vector3Add(move, forward); | ||
| 257 | if (IsKeyDown(KEY_S)) move = Vector3Subtract(move, forward); | ||
| 258 | if (IsKeyDown(KEY_D)) move = Vector3Add(move, right); | ||
| 259 | if (IsKeyDown(KEY_A)) move = Vector3Subtract(move, right); | ||
| 260 | |||
| 261 | if (Vector3Length(move) > 0) { | ||
| 262 | move = Vector3Scale(Vector3Normalize(move), PLAYER_MOVE_SPEED * dt); | ||
| 263 | game.camera.position = Vector3Add(game.camera.position, move); | ||
| 264 | game.camera.target = Vector3Add(game.camera.target, move); | ||
| 265 | } | ||
| 266 | |||
| 267 | if (game.cursor_captured) { | ||
| 268 | // Manual rotation | ||
| 269 | Vector2 mouseDelta = GetMouseDelta(); | ||
| 270 | float yaw = -mouseDelta.x * PLAYER_MOUSE_SENSITIVITY; | ||
| 271 | float pitch = -mouseDelta.y * PLAYER_MOUSE_SENSITIVITY; | ||
| 272 | |||
| 273 | Vector3 view = Vector3Subtract(game.camera.target, game.camera.position); | ||
| 274 | |||
| 275 | // Yaw | ||
| 276 | view = Vector3RotateByAxisAngle(view, game.camera.up, yaw); | ||
| 277 | |||
| 278 | // Pitch | ||
| 279 | Vector3 axis = Vector3Normalize(Vector3CrossProduct(view, game.camera.up)); | ||
| 280 | view = Vector3RotateByAxisAngle(view, axis, pitch); | ||
| 281 | |||
| 282 | game.camera.target = Vector3Add(game.camera.position, view); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | void DrawGame(void) { | ||
| 287 | BeginDrawing(); | ||
| 288 | ClearBackground(DARKGRAY); | ||
| 289 | BeginMode3D(game.camera); | ||
| 290 | |||
| 291 | // Enable backface culling to hide interior faces of brushes | ||
| 292 | rlEnableBackfaceCulling(); | ||
| 293 | for (int i = 0; i < game.world_model_count; i++) { | ||
| 294 | DrawModel(game.world_models[i], (Vector3){ 0, 0, 0 }, 1.0f, WHITE); | ||
| 295 | } | ||
| 296 | |||
| 297 | EndMode3D(); | ||
| 298 | |||
| 299 | int screenWidth = GetScreenWidth(); | ||
| 300 | int screenHeight = GetScreenHeight(); | ||
| 301 | DrawLine(screenWidth / 2 - 10, screenHeight / 2, screenWidth / 2 + 10, screenHeight / 2, GREEN); | ||
| 302 | DrawLine(screenWidth / 2, screenHeight / 2 - 10, screenWidth / 2, screenHeight / 2 + 10, GREEN); | ||
| 303 | DrawFPS(10, 10); | ||
| 304 | DrawText(TextFormat("VSync: %s", game.vsync ? "ON" : "OFF"), 10, 30, 20, GREEN); | ||
| 305 | EndDrawing(); | ||
| 306 | } | ||
diff --git a/libraries/nonstd.h b/libraries/nonstd.h index 531834d..f1848d3 100644 --- a/libraries/nonstd.h +++ b/libraries/nonstd.h | |||
| @@ -77,12 +77,12 @@ NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count); | |||
| 77 | #define UNUSED(value) (void)(value) | 77 | #define UNUSED(value) (void)(value) |
| 78 | #define TODO(message) \ | 78 | #define TODO(message) \ |
| 79 | do { \ | 79 | do { \ |
| 80 | fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); \ | 80 | TraceLog(LOG_ERROR, "%s:%d: TODO: %s", __FILE__, __LINE__, message); \ |
| 81 | abort(); \ | 81 | abort(); \ |
| 82 | } while (0) | 82 | } while (0) |
| 83 | #define UNREACHABLE(message) \ | 83 | #define UNREACHABLE(message) \ |
| 84 | do { \ | 84 | do { \ |
| 85 | fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); \ | 85 | TraceLog(LOG_ERROR, "%s:%d: UNREACHABLE: %s", __FILE__, __LINE__, message); \ |
| 86 | abort(); \ | 86 | abort(); \ |
| 87 | } while (0) | 87 | } while (0) |
| 88 | 88 | ||
| @@ -315,29 +315,6 @@ NONSTD_DEF stringb read_entire_file_sb(const char *filepath); | |||
| 315 | NONSTD_DEF int write_file_sv(const char *filepath, stringv sv); | 315 | NONSTD_DEF int write_file_sv(const char *filepath, stringv sv); |
| 316 | NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb); | 316 | NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb); |
| 317 | 317 | ||
| 318 | // Logging | ||
| 319 | typedef enum { | ||
| 320 | LOG_ERROR, | ||
| 321 | LOG_WARN, | ||
| 322 | LOG_INFO, | ||
| 323 | LOG_DEBUG, | ||
| 324 | } LogLevel; | ||
| 325 | |||
| 326 | NONSTD_DEF void set_log_level(LogLevel level); | ||
| 327 | NONSTD_DEF LogLevel get_log_level_from_env(void); | ||
| 328 | NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...); | ||
| 329 | |||
| 330 | #define LOG_INFO_MSG(...) log_message(stdout, LOG_INFO, __VA_ARGS__) | ||
| 331 | #define LOG_DEBUG_MSG(...) log_message(stdout, LOG_DEBUG, __VA_ARGS__) | ||
| 332 | #define LOG_WARN_MSG(...) log_message(stderr, LOG_WARN, __VA_ARGS__) | ||
| 333 | #define LOG_ERROR_MSG(...) log_message(stderr, LOG_ERROR, __VA_ARGS__) | ||
| 334 | |||
| 335 | #define COLOR_RESET "\033[0m" | ||
| 336 | #define COLOR_INFO "\033[32m" | ||
| 337 | #define COLOR_DEBUG "\033[36m" | ||
| 338 | #define COLOR_WARNING "\033[33m" | ||
| 339 | #define COLOR_ERROR "\033[31m" | ||
| 340 | |||
| 341 | #ifdef NONSTD_IMPLEMENTATION | 318 | #ifdef NONSTD_IMPLEMENTATION |
| 342 | 319 | ||
| 343 | NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) { | 320 | NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) { |
| @@ -587,66 +564,6 @@ NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) { | |||
| 587 | return write_entire_file(filepath, sb->data, sb->length); | 564 | return write_entire_file(filepath, sb->data, sb->length); |
| 588 | } | 565 | } |
| 589 | 566 | ||
| 590 | // Logging Implementation | ||
| 591 | |||
| 592 | static LogLevel max_level = LOG_INFO; | ||
| 593 | |||
| 594 | static const char *level_strings[] = { | ||
| 595 | "ERROR", | ||
| 596 | "WARN", | ||
| 597 | "INFO", | ||
| 598 | "DEBUG", | ||
| 599 | }; | ||
| 600 | |||
| 601 | static const char *level_colors[] = { | ||
| 602 | COLOR_ERROR, | ||
| 603 | COLOR_WARNING, | ||
| 604 | COLOR_INFO, | ||
| 605 | COLOR_DEBUG, | ||
| 606 | }; | ||
| 607 | |||
| 608 | NONSTD_DEF void set_log_level(LogLevel level) { | ||
| 609 | max_level = level; | ||
| 610 | } | ||
| 611 | |||
| 612 | NONSTD_DEF LogLevel get_log_level_from_env(void) { | ||
| 613 | const char *env = getenv("LOG_LEVEL"); | ||
| 614 | if (env) { | ||
| 615 | int level = atoi(env); | ||
| 616 | if (level >= 0 && level <= 3) { | ||
| 617 | return (LogLevel)level; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | |||
| 621 | return max_level; | ||
| 622 | } | ||
| 623 | |||
| 624 | NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...) { | ||
| 625 | if (max_level < level) | ||
| 626 | return; | ||
| 627 | |||
| 628 | struct timeval tv; | ||
| 629 | gettimeofday(&tv, NULL); | ||
| 630 | struct tm *tm_info = localtime(&tv.tv_sec); | ||
| 631 | |||
| 632 | char time_str[24]; | ||
| 633 | strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); | ||
| 634 | |||
| 635 | const char *color = isatty(fileno(stream)) ? level_colors[level] : ""; | ||
| 636 | const char *reset = isatty(fileno(stream)) ? COLOR_RESET : ""; | ||
| 637 | |||
| 638 | const char *log_format = "%s[%s.%03d] [%-5s] "; | ||
| 639 | fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000), level_strings[level]); | ||
| 640 | |||
| 641 | va_list args; | ||
| 642 | va_start(args, format); | ||
| 643 | vfprintf(stream, format, args); | ||
| 644 | va_end(args); | ||
| 645 | |||
| 646 | fprintf(stream, "%s\n", reset); | ||
| 647 | fflush(stream); | ||
| 648 | } | ||
| 649 | |||
| 650 | // PPM Image Implementation | 567 | // PPM Image Implementation |
| 651 | 568 | ||
| 652 | NONSTD_DEF Canvas ppm_init(u32 width, u32 height) { | 569 | NONSTD_DEF Canvas ppm_init(u32 width, u32 height) { |
diff --git a/libraries/vfs.h b/libraries/vfs.h index 6a243cc..6017407 100644 --- a/libraries/vfs.h +++ b/libraries/vfs.h | |||
| @@ -57,28 +57,28 @@ void vfs_init(const char* pak_path) { | |||
| 57 | const char* debug_env = getenv("DEBUG"); | 57 | const char* debug_env = getenv("DEBUG"); |
| 58 | if (debug_env && (strcmp(debug_env, "1") == 0 || strcmp(debug_env, "true") == 0)) { | 58 | if (debug_env && (strcmp(debug_env, "1") == 0 || strcmp(debug_env, "true") == 0)) { |
| 59 | g_disk_mode = 1; | 59 | g_disk_mode = 1; |
| 60 | log_message(stdout, LOG_INFO, "VFS: Operating in Disk Mode (DEBUG enabled)"); | 60 | TraceLog(LOG_INFO, "VFS: Operating in Disk Mode (DEBUG enabled)"); |
| 61 | return; | 61 | return; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | FILE* f = fopen(pak_path, "rb"); | 64 | FILE* f = fopen(pak_path, "rb"); |
| 65 | if (!f) { | 65 | if (!f) { |
| 66 | log_message(stderr, LOG_ERROR, "VFS: Failed to open pak file: %s (Error: %s)", pak_path, strerror(errno)); | 66 | TraceLog(LOG_ERROR, "VFS: Failed to open pak file: %s (Error: %s)", pak_path, strerror(errno)); |
| 67 | log_message(stderr, LOG_WARN, "VFS: Falling back to Disk Mode"); | 67 | TraceLog(LOG_WARNING, "VFS: Falling back to Disk Mode"); |
| 68 | g_disk_mode = 1; | 68 | g_disk_mode = 1; |
| 69 | return; | 69 | return; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | VfsHeader header; | 72 | VfsHeader header; |
| 73 | if (fread(&header, sizeof(VfsHeader), 1, f) != 1) { | 73 | if (fread(&header, sizeof(VfsHeader), 1, f) != 1) { |
| 74 | log_message(stderr, LOG_ERROR, "VFS: Failed to read header from %s", pak_path); | 74 | TraceLog(LOG_ERROR, "VFS: Failed to read header from %s", pak_path); |
| 75 | fclose(f); | 75 | fclose(f); |
| 76 | g_disk_mode = 1; | 76 | g_disk_mode = 1; |
| 77 | return; | 77 | return; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | if (memcmp(header.magic, "DRP1", 4) != 0) { | 80 | if (memcmp(header.magic, "DRP1", 4) != 0) { |
| 81 | log_message(stderr, LOG_ERROR, "VFS: Invalid magic in %s", pak_path); | 81 | TraceLog(LOG_ERROR, "VFS: Invalid magic in %s", pak_path); |
| 82 | fclose(f); | 82 | fclose(f); |
| 83 | g_disk_mode = 1; | 83 | g_disk_mode = 1; |
| 84 | return; | 84 | return; |
| @@ -87,7 +87,7 @@ void vfs_init(const char* pak_path) { | |||
| 87 | g_num_entries = header.num_files; | 87 | g_num_entries = header.num_files; |
| 88 | g_entries = (VfsEntry*)malloc(sizeof(VfsEntry) * g_num_entries); | 88 | g_entries = (VfsEntry*)malloc(sizeof(VfsEntry) * g_num_entries); |
| 89 | if (fread(g_entries, sizeof(VfsEntry), g_num_entries, f) != g_num_entries) { | 89 | if (fread(g_entries, sizeof(VfsEntry), g_num_entries, f) != g_num_entries) { |
| 90 | log_message(stderr, LOG_ERROR, "VFS: Failed to read index table from %s", pak_path); | 90 | TraceLog(LOG_ERROR, "VFS: Failed to read index table from %s", pak_path); |
| 91 | free(g_entries); | 91 | free(g_entries); |
| 92 | g_entries = NULL; | 92 | g_entries = NULL; |
| 93 | fclose(f); | 93 | fclose(f); |
| @@ -97,7 +97,7 @@ void vfs_init(const char* pak_path) { | |||
| 97 | 97 | ||
| 98 | fclose(f); | 98 | fclose(f); |
| 99 | strncpy(g_pak_path, pak_path, sizeof(g_pak_path) - 1); | 99 | strncpy(g_pak_path, pak_path, sizeof(g_pak_path) - 1); |
| 100 | log_message(stdout, LOG_INFO, "VFS: Loaded %u files from %s", g_num_entries, pak_path); | 100 | TraceLog(LOG_INFO, "VFS: Loaded %u files from %s", g_num_entries, pak_path); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| 103 | void vfs_shutdown(void) { | 103 | void vfs_shutdown(void) { |
| @@ -137,7 +137,7 @@ VfsFile* vfs_open(const char* path) { | |||
| 137 | } | 137 | } |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | log_message(stderr, LOG_WARN, "VFS: File not found: %s", path); | 140 | TraceLog(LOG_WARNING, "VFS: File not found: %s", path); |
| 141 | return NULL; | 141 | return NULL; |
| 142 | } | 142 | } |
| 143 | 143 | ||
| @@ -1,18 +1,24 @@ | |||
| 1 | #include "raylib.h" | 1 | #define _POSIX_C_SOURCE 200809L |
| 2 | #define NONSTD_IMPLEMENTATION | ||
| 3 | #define VFS_IMPLEMENTATION | ||
| 4 | #include "all.h" | ||
| 2 | 5 | ||
| 3 | int main(void) | 6 | int main(void) { |
| 4 | { | 7 | SetConfigFlags(FLAG_VSYNC_HINT); |
| 5 | InitWindow(800, 450, "raylib example - basic window"); | 8 | InitWindow(1920, 1080, "Stalag"); |
| 9 | SetTargetFPS(GetMonitorRefreshRate(GetCurrentMonitor())); | ||
| 6 | 10 | ||
| 7 | while (!WindowShouldClose()) | 11 | vfs_init("data.pak"); |
| 8 | { | 12 | InitGame(); |
| 9 | BeginDrawing(); | 13 | game.vsync = true; |
| 10 | ClearBackground(RAYWHITE); | 14 | |
| 11 | DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); | 15 | while (!WindowShouldClose()) { |
| 12 | EndDrawing(); | 16 | UpdateGame(); |
| 17 | DrawGame(); | ||
| 13 | } | 18 | } |
| 14 | 19 | ||
| 20 | vfs_shutdown(); | ||
| 15 | CloseWindow(); | 21 | CloseWindow(); |
| 16 | 22 | ||
| 17 | return 0; | 23 | return 0; |
| 18 | } \ No newline at end of file | 24 | } |
| @@ -0,0 +1,246 @@ | |||
| 1 | #include "all.h" | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | char map_peek(MapParser *p) { | ||
| 7 | if (p->pos >= p->length) return 0; | ||
| 8 | return p->data[p->pos]; | ||
| 9 | } | ||
| 10 | |||
| 11 | char map_get(MapParser *p) { | ||
| 12 | if (p->pos >= p->length) return 0; | ||
| 13 | return p->data[p->pos++]; | ||
| 14 | } | ||
| 15 | |||
| 16 | void map_skip_whitespace(MapParser *p) { | ||
| 17 | while (true) { | ||
| 18 | char c = map_peek(p); | ||
| 19 | if (isspace(c)) { | ||
| 20 | map_get(p); | ||
| 21 | } else if (c == '/' && p->data[p->pos + 1] == '/') { | ||
| 22 | while (map_peek(p) != '\n' && map_peek(p) != 0) map_get(p); | ||
| 23 | } else { | ||
| 24 | break; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | bool map_expect(MapParser *p, char expected) { | ||
| 30 | map_skip_whitespace(p); | ||
| 31 | if (map_get(p) == expected) return true; | ||
| 32 | return false; | ||
| 33 | } | ||
| 34 | |||
| 35 | void map_parse_token(MapParser *p, char *buffer, int size) { | ||
| 36 | map_skip_whitespace(p); | ||
| 37 | int i = 0; | ||
| 38 | bool quoted = false; | ||
| 39 | if (map_peek(p) == '"') { | ||
| 40 | quoted = true; | ||
| 41 | map_get(p); | ||
| 42 | } | ||
| 43 | |||
| 44 | while (true) { | ||
| 45 | char c = map_peek(p); | ||
| 46 | if (c == 0) break; | ||
| 47 | if (quoted) { | ||
| 48 | if (c == '"') { | ||
| 49 | map_get(p); | ||
| 50 | break; | ||
| 51 | } | ||
| 52 | } else { | ||
| 53 | if (isspace(c) || c == '(' || c == ')' || c == '{' || c == '}') break; | ||
| 54 | } | ||
| 55 | if (i < size - 1) buffer[i++] = map_get(p); | ||
| 56 | else map_get(p); | ||
| 57 | } | ||
| 58 | buffer[i] = 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | Vector3 map_parse_vector(MapParser *p) { | ||
| 62 | map_expect(p, '('); | ||
| 63 | char buf[64]; | ||
| 64 | map_parse_token(p, buf, sizeof(buf)); | ||
| 65 | float x = (float)atof(buf); | ||
| 66 | map_parse_token(p, buf, sizeof(buf)); | ||
| 67 | float y = (float)atof(buf); | ||
| 68 | map_parse_token(p, buf, sizeof(buf)); | ||
| 69 | float z = (float)atof(buf); | ||
| 70 | map_expect(p, ')'); | ||
| 71 | return (Vector3){ x, z, -y }; | ||
| 72 | } | ||
| 73 | |||
| 74 | Map ParseMap(const char *filename) { | ||
| 75 | size_t size; | ||
| 76 | char *data = read_entire_file(filename, &size); | ||
| 77 | Map map = { 0 }; | ||
| 78 | if (!data) return map; | ||
| 79 | |||
| 80 | MapParser p = { data, size, 0 }; | ||
| 81 | array(MapEntity) entities; | ||
| 82 | array_init(entities); | ||
| 83 | |||
| 84 | while (true) { | ||
| 85 | map_skip_whitespace(&p); | ||
| 86 | if (map_peek(&p) == 0) break; | ||
| 87 | |||
| 88 | if (map_expect(&p, '{')) { | ||
| 89 | MapEntity entity = { 0 }; | ||
| 90 | array(MapProperty) props; | ||
| 91 | array(MapBrush) brushes; | ||
| 92 | array_init(props); | ||
| 93 | array_init(brushes); | ||
| 94 | |||
| 95 | while (true) { | ||
| 96 | map_skip_whitespace(&p); | ||
| 97 | char c = map_peek(&p); | ||
| 98 | if (c == '}') { | ||
| 99 | map_get(&p); | ||
| 100 | break; | ||
| 101 | } else if (c == '"') { | ||
| 102 | MapProperty prop; | ||
| 103 | map_parse_token(&p, prop.key, sizeof(prop.key)); | ||
| 104 | map_parse_token(&p, prop.value, sizeof(prop.value)); | ||
| 105 | array_push(props, prop); | ||
| 106 | } else if (c == '{') { | ||
| 107 | map_get(&p); | ||
| 108 | MapBrush brush = { 0 }; | ||
| 109 | array(MapPlane) planes; | ||
| 110 | array_init(planes); | ||
| 111 | while (true) { | ||
| 112 | map_skip_whitespace(&p); | ||
| 113 | if (map_peek(&p) == '}') { | ||
| 114 | map_get(&p); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | MapPlane plane; | ||
| 118 | plane.p[0] = map_parse_vector(&p); | ||
| 119 | plane.p[1] = map_parse_vector(&p); | ||
| 120 | plane.p[2] = map_parse_vector(&p); | ||
| 121 | map_parse_token(&p, plane.texture, sizeof(plane.texture)); | ||
| 122 | |||
| 123 | char buf[64]; | ||
| 124 | map_parse_token(&p, buf, sizeof(buf)); plane.shift[0] = (float)atof(buf); | ||
| 125 | map_parse_token(&p, buf, sizeof(buf)); plane.shift[1] = (float)atof(buf); | ||
| 126 | map_parse_token(&p, buf, sizeof(buf)); plane.rotate = (float)atof(buf); | ||
| 127 | map_parse_token(&p, buf, sizeof(buf)); plane.scale[0] = (float)atof(buf); | ||
| 128 | map_parse_token(&p, buf, sizeof(buf)); plane.scale[1] = (float)atof(buf); | ||
| 129 | |||
| 130 | array_push(planes, plane); | ||
| 131 | } | ||
| 132 | brush.planes = planes.data; | ||
| 133 | brush.plane_count = (int)planes.length; | ||
| 134 | array_push(brushes, brush); | ||
| 135 | } else { | ||
| 136 | map_get(&p); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | entity.properties = props.data; | ||
| 140 | entity.property_count = (int)props.length; | ||
| 141 | entity.brushes = brushes.data; | ||
| 142 | entity.brush_count = (int)brushes.length; | ||
| 143 | array_push(entities, entity); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | map.entities = entities.data; | ||
| 148 | map.entity_count = (int)entities.length; | ||
| 149 | free(data); | ||
| 150 | return map; | ||
| 151 | } | ||
| 152 | |||
| 153 | void FreeMap(Map map) { | ||
| 154 | for (int i = 0; i < map.entity_count; i++) { | ||
| 155 | MapEntity *e = &map.entities[i]; | ||
| 156 | if (e->properties) free(e->properties); | ||
| 157 | for (int j = 0; j < e->brush_count; j++) { | ||
| 158 | if (e->brushes[j].planes) free(e->brushes[j].planes); | ||
| 159 | } | ||
| 160 | if (e->brushes) free(e->brushes); | ||
| 161 | } | ||
| 162 | if (map.entities) free(map.entities); | ||
| 163 | } | ||
| 164 | |||
| 165 | Plane PlaneFromPoints(Vector3 p1, Vector3 p2, Vector3 p3) { | ||
| 166 | Vector3 v1 = Vector3Subtract(p2, p1); | ||
| 167 | Vector3 v2 = Vector3Subtract(p3, p1); | ||
| 168 | Vector3 normal = Vector3Normalize(Vector3CrossProduct(v1, v2)); | ||
| 169 | return (Plane){ normal, Vector3DotProduct(normal, p1) }; | ||
| 170 | } | ||
| 171 | |||
| 172 | void PolyFree(Polygon p) { | ||
| 173 | if (p.verts) free(p.verts); | ||
| 174 | } | ||
| 175 | |||
| 176 | Polygon PolyClip(Polygon poly, Plane plane) { | ||
| 177 | if (poly.count == 0) return poly; | ||
| 178 | |||
| 179 | array(Vector3) out_verts; | ||
| 180 | array_init(out_verts); | ||
| 181 | |||
| 182 | for (int i = 0; i < poly.count; i++) { | ||
| 183 | Vector3 p1 = poly.verts[i]; | ||
| 184 | Vector3 p2 = poly.verts[(i + 1) % poly.count]; | ||
| 185 | |||
| 186 | float d1 = Vector3DotProduct(plane.normal, p1) - plane.dist; | ||
| 187 | float d2 = Vector3DotProduct(plane.normal, p2) - plane.dist; | ||
| 188 | |||
| 189 | // Using a smaller epsilon and more robust logic | ||
| 190 | const float eps = 0.001f; | ||
| 191 | |||
| 192 | if (d1 >= -eps) { | ||
| 193 | if (d2 >= -eps) { | ||
| 194 | array_push(out_verts, p2); | ||
| 195 | } else { | ||
| 196 | float t = d1 / (d1 - d2); | ||
| 197 | Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t)); | ||
| 198 | array_push(out_verts, intersect); | ||
| 199 | } | ||
| 200 | } else { | ||
| 201 | if (d2 >= -eps) { | ||
| 202 | float t = d1 / (d1 - d2); | ||
| 203 | Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t)); | ||
| 204 | array_push(out_verts, intersect); | ||
| 205 | array_push(out_verts, p2); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | Polygon res; | ||
| 211 | res.verts = out_verts.data; | ||
| 212 | res.count = (int)out_verts.length; | ||
| 213 | return res; | ||
| 214 | } | ||
| 215 | |||
| 216 | Polygon CreateLargeQuad(Plane plane) { | ||
| 217 | Vector3 n = plane.normal; | ||
| 218 | Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 }; | ||
| 219 | Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n)); | ||
| 220 | up = Vector3CrossProduct(n, right); | ||
| 221 | |||
| 222 | Vector3 center = Vector3Scale(n, plane.dist); | ||
| 223 | float size = 10000.0f; | ||
| 224 | |||
| 225 | Polygon poly; | ||
| 226 | poly.verts = ALLOC(Vector3, 4); | ||
| 227 | poly.count = 4; | ||
| 228 | poly.verts[0] = Vector3Add(center, Vector3Add(Vector3Scale(right, size), Vector3Scale(up, size))); | ||
| 229 | poly.verts[1] = Vector3Add(center, Vector3Add(Vector3Scale(right, -size), Vector3Scale(up, size))); | ||
| 230 | poly.verts[2] = Vector3Add(center, Vector3Add(Vector3Scale(right, -size), Vector3Scale(up, -size))); | ||
| 231 | poly.verts[3] = Vector3Add(center, Vector3Add(Vector3Scale(right, size), Vector3Scale(up, -size))); | ||
| 232 | |||
| 233 | return poly; | ||
| 234 | } | ||
| 235 | |||
| 236 | Vector2 GetUV(Vector3 p, MapPlane *mp, Plane plane) { | ||
| 237 | Vector3 n = plane.normal; | ||
| 238 | Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 }; | ||
| 239 | Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n)); | ||
| 240 | up = Vector3CrossProduct(n, right); | ||
| 241 | |||
| 242 | float u = Vector3DotProduct(p, right) * (1.0f / (mp->scale[0] ? mp->scale[0] : 1.0f)) + mp->shift[0]; | ||
| 243 | float v = Vector3DotProduct(p, up) * (1.0f / (mp->scale[1] ? mp->scale[1] : 1.0f)) + mp->shift[1]; | ||
| 244 | |||
| 245 | return (Vector2){ u / 64.0f, v / 64.0f }; | ||
| 246 | } | ||
diff --git a/maps/demo1.map b/maps/demo1.map index 687c93c..e2e286f 100644 --- a/maps/demo1.map +++ b/maps/demo1.map | |||
| @@ -30,4 +30,18 @@ | |||
| 30 | ( -16 336 16 ) ( -15 336 16 ) ( -16 336 17 ) environment/planks_012 0 0 0 1 1 | 30 | ( -16 336 16 ) ( -15 336 16 ) ( -16 336 17 ) environment/planks_012 0 0 0 1 1 |
| 31 | ( -16 112 16 ) ( -16 112 17 ) ( -16 113 16 ) environment/planks_012 16 0 0 1 1 | 31 | ( -16 112 16 ) ( -16 112 17 ) ( -16 113 16 ) environment/planks_012 16 0 0 1 1 |
| 32 | } | 32 | } |
| 33 | // brush 3 | ||
| 34 | { | ||
| 35 | ( -16 272 0 ) ( -16 273 0 ) ( -16 272 1 ) environment/planks_012 0 0 0 1 1 | ||
| 36 | ( -16 272 0 ) ( -16 272 1 ) ( -15 272 0 ) environment/planks_012 0 0 0 1 1 | ||
| 37 | ( -16 272 0 ) ( -15 272 0 ) ( -16 273 0 ) environment/planks_012 0 0 0 1 1 | ||
| 38 | ( 80 336 64 ) ( 80 337 64 ) ( 81 336 64 ) environment/planks_012 0 0 0 1 1 | ||
| 39 | ( 80 336 16 ) ( 81 336 16 ) ( 80 336 17 ) environment/planks_012 0 0 0 1 1 | ||
| 40 | ( 80 336 16 ) ( 80 336 17 ) ( 80 337 16 ) environment/planks_012 0 0 0 1 1 | ||
| 41 | } | ||
| 42 | } | ||
| 43 | // entity 1 | ||
| 44 | { | ||
| 45 | "classname" "info_player_start" | ||
| 46 | "origin" "-112 -16 40" | ||
| 33 | } | 47 | } |
diff --git a/textures/brushes/bricks_076c.jpg b/textures/brushes/bricks_076c.jpg deleted file mode 100644 index b4be330..0000000 --- a/textures/brushes/bricks_076c.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/brushes/bricks_076c.png b/textures/brushes/bricks_076c.png new file mode 100644 index 0000000..d9b69dc --- /dev/null +++ b/textures/brushes/bricks_076c.png | |||
| Binary files differ | |||
diff --git a/textures/brushes/ground_068.jpg b/textures/brushes/ground_068.jpg deleted file mode 100644 index beb7d2e..0000000 --- a/textures/brushes/ground_068.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/brushes/ground_068.png b/textures/brushes/ground_068.png new file mode 100644 index 0000000..cfaed17 --- /dev/null +++ b/textures/brushes/ground_068.png | |||
| Binary files differ | |||
diff --git a/textures/brushes/rock_016.jpg b/textures/brushes/rock_016.jpg deleted file mode 100644 index c6986aa..0000000 --- a/textures/brushes/rock_016.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/brushes/rock_016.png b/textures/brushes/rock_016.png new file mode 100644 index 0000000..9c6ade8 --- /dev/null +++ b/textures/brushes/rock_016.png | |||
| Binary files differ | |||
diff --git a/textures/brushes/rock_030.jpg b/textures/brushes/rock_030.jpg deleted file mode 100644 index 6604eb9..0000000 --- a/textures/brushes/rock_030.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/brushes/rock_030.png b/textures/brushes/rock_030.png new file mode 100644 index 0000000..5ed08cb --- /dev/null +++ b/textures/brushes/rock_030.png | |||
| Binary files differ | |||
diff --git a/textures/environment/paintedwood_008a.jpg b/textures/environment/paintedwood_008a.jpg deleted file mode 100644 index 2ab38f0..0000000 --- a/textures/environment/paintedwood_008a.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/environment/paintedwood_008a.png b/textures/environment/paintedwood_008a.png new file mode 100644 index 0000000..bc1d468 --- /dev/null +++ b/textures/environment/paintedwood_008a.png | |||
| Binary files differ | |||
diff --git a/textures/environment/planks_012.jpg b/textures/environment/planks_012.jpg deleted file mode 100644 index 30c21a2..0000000 --- a/textures/environment/planks_012.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/environment/planks_012.png b/textures/environment/planks_012.png new file mode 100644 index 0000000..ddbbe89 --- /dev/null +++ b/textures/environment/planks_012.png | |||
| Binary files differ | |||
diff --git a/textures/environment/roofingtiles_012b.jpg b/textures/environment/roofingtiles_012b.jpg deleted file mode 100644 index 47689fb..0000000 --- a/textures/environment/roofingtiles_012b.jpg +++ /dev/null | |||
| Binary files differ | |||
diff --git a/textures/environment/roofingtiles_012b.png b/textures/environment/roofingtiles_012b.png new file mode 100644 index 0000000..2040b86 --- /dev/null +++ b/textures/environment/roofingtiles_012b.png | |||
| Binary files differ | |||
