aboutsummaryrefslogtreecommitdiff
path: root/map.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-04-30 19:19:27 +0200
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-04-30 19:44:07 +0200
commitb4d0ad9e95226d225d5361b1182866884aaa6366 (patch)
tree5440b34d97e89a08fc01abd49bb80e5b50dda945 /map.c
parentddde2e8fdf05efac5914ca2b812b7762b78ba9ec (diff)
downloadstalag-b4d0ad9e95226d225d5361b1182866884aaa6366.tar.gz
Structural refactor
Diffstat (limited to 'map.c')
-rw-r--r--map.c597
1 files changed, 394 insertions, 203 deletions
diff --git a/map.c b/map.c
index 12d40f9..4e2b83c 100644
--- a/map.c
+++ b/map.c
@@ -1,246 +1,437 @@
1#include "all.h" 1#include "all.h"
2
2#include <ctype.h> 3#include <ctype.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <stdio.h> 5#include <stdio.h>
6#include <string.h>
7#include <float.h>
5 8
6char map_peek(MapParser *p) { 9char map_peek(MapParser *p) {
7 if (p->pos >= p->length) return 0; 10 if (p->pos >= p->length) return 0;
8 return p->data[p->pos]; 11 return p->data[p->pos];
9} 12}
10 13
11char map_get(MapParser *p) { 14char map_get(MapParser *p) {
12 if (p->pos >= p->length) return 0; 15 if (p->pos >= p->length) return 0;
13 return p->data[p->pos++]; 16 return p->data[p->pos++];
14} 17}
15 18
16void map_skip_whitespace(MapParser *p) { 19void map_skip_whitespace(MapParser *p) {
17 while (true) { 20 while (true) {
18 char c = map_peek(p); 21 char c = map_peek(p);
19 if (isspace(c)) { 22 if (isspace(c)) {
20 map_get(p); 23 map_get(p);
21 } else if (c == '/' && p->data[p->pos + 1] == '/') { 24 } else if (c == '/' && p->data[p->pos + 1] == '/') {
22 while (map_peek(p) != '\n' && map_peek(p) != 0) map_get(p); 25 while (map_peek(p) != '\n' && map_peek(p) != 0) map_get(p);
23 } else { 26 } else {
24 break; 27 break;
25 } 28 }
26 } 29 }
27} 30}
28 31
29bool map_expect(MapParser *p, char expected) { 32bool map_expect(MapParser *p, char expected) {
30 map_skip_whitespace(p); 33 map_skip_whitespace(p);
31 if (map_get(p) == expected) return true; 34 if (map_get(p) == expected) return true;
32 return false; 35 return false;
33} 36}
34 37
35void map_parse_token(MapParser *p, char *buffer, int size) { 38void map_parse_token(MapParser *p, char *buffer, int size) {
36 map_skip_whitespace(p); 39 map_skip_whitespace(p);
37 int i = 0; 40 int i = 0;
38 bool quoted = false; 41 bool quoted = false;
39 if (map_peek(p) == '"') { 42 if (map_peek(p) == '"') {
40 quoted = true; 43 quoted = true;
41 map_get(p); 44 map_get(p);
42 } 45 }
43 46
44 while (true) { 47 while (true) {
45 char c = map_peek(p); 48 char c = map_peek(p);
46 if (c == 0) break; 49 if (c == 0) break;
47 if (quoted) { 50 if (quoted) {
48 if (c == '"') { 51 if (c == '"') {
49 map_get(p); 52 map_get(p);
50 break; 53 break;
51 } 54 }
52 } else { 55 } else {
53 if (isspace(c) || c == '(' || c == ')' || c == '{' || c == '}') break; 56 if (isspace(c) || c == '(' || c == ')' || c == '{' || c == '}') break;
54 } 57 }
55 if (i < size - 1) buffer[i++] = map_get(p); 58 if (i < size - 1) buffer[i++] = map_get(p);
56 else map_get(p); 59 else map_get(p);
57 } 60 }
58 buffer[i] = 0; 61 buffer[i] = 0;
59} 62}
60 63
61Vector3 map_parse_vector(MapParser *p) { 64Vector3 map_parse_vector(MapParser *p) {
62 map_expect(p, '('); 65 map_expect(p, '(');
63 char buf[64]; 66 char buf[64];
64 map_parse_token(p, buf, sizeof(buf)); 67 map_parse_token(p, buf, sizeof(buf));
65 float x = (float)atof(buf); 68 float x = (float)atof(buf);
66 map_parse_token(p, buf, sizeof(buf)); 69 map_parse_token(p, buf, sizeof(buf));
67 float y = (float)atof(buf); 70 float y = (float)atof(buf);
68 map_parse_token(p, buf, sizeof(buf)); 71 map_parse_token(p, buf, sizeof(buf));
69 float z = (float)atof(buf); 72 float z = (float)atof(buf);
70 map_expect(p, ')'); 73 map_expect(p, ')');
71 return (Vector3){ x, z, -y }; 74 return (Vector3){ x, z, -y };
72} 75}
73 76
74Map ParseMap(const char *filename) { 77Map ParseMap(const char *filename) {
75 size_t size; 78 size_t size;
76 char *data = (char *)vfs_read(filename, &size); 79 char *data = (char *)vfs_read(filename, &size);
77 Map map = { 0 }; 80 Map map = { 0 };
78 if (!data) return map; 81 if (!data) return map;
79 82
80 MapParser p = { data, size, 0 }; 83 MapParser p = { data, size, 0 };
81 array(MapEntity) entities; 84 array(MapEntity) entities;
82 array_init(entities); 85 array_init(entities);
83 86
84 while (true) { 87 while (true) {
85 map_skip_whitespace(&p); 88 map_skip_whitespace(&p);
86 if (map_peek(&p) == 0) break; 89 if (map_peek(&p) == 0) break;
87 90
88 if (map_expect(&p, '{')) { 91 if (map_expect(&p, '{')) {
89 MapEntity entity = { 0 }; 92 MapEntity entity = { 0 };
90 array(MapProperty) props; 93 array(MapProperty) props;
91 array(MapBrush) brushes; 94 array(MapBrush) brushes;
92 array_init(props); 95 array_init(props);
93 array_init(brushes); 96 array_init(brushes);
94 97
95 while (true) { 98 while (true) {
96 map_skip_whitespace(&p); 99 map_skip_whitespace(&p);
97 char c = map_peek(&p); 100 char c = map_peek(&p);
98 if (c == '}') { 101 if (c == '}') {
99 map_get(&p); 102 map_get(&p);
100 break; 103 break;
101 } else if (c == '"') { 104 } else if (c == '"') {
102 MapProperty prop; 105 MapProperty prop;
103 map_parse_token(&p, prop.key, sizeof(prop.key)); 106 map_parse_token(&p, prop.key, sizeof(prop.key));
104 map_parse_token(&p, prop.value, sizeof(prop.value)); 107 map_parse_token(&p, prop.value, sizeof(prop.value));
105 array_push(props, prop); 108 array_push(props, prop);
106 } else if (c == '{') { 109 } else if (c == '{') {
107 map_get(&p); 110 map_get(&p);
108 MapBrush brush = { 0 }; 111 MapBrush brush = { 0 };
109 array(MapPlane) planes; 112 array(MapPlane) planes;
110 array_init(planes); 113 array_init(planes);
111 while (true) { 114 while (true) {
112 map_skip_whitespace(&p); 115 map_skip_whitespace(&p);
113 if (map_peek(&p) == '}') { 116 if (map_peek(&p) == '}') {
114 map_get(&p); 117 map_get(&p);
115 break; 118 break;
116 } 119 }
117 MapPlane plane; 120 MapPlane plane;
118 plane.p[0] = map_parse_vector(&p); 121 plane.p[0] = map_parse_vector(&p);
119 plane.p[1] = map_parse_vector(&p); 122 plane.p[1] = map_parse_vector(&p);
120 plane.p[2] = map_parse_vector(&p); 123 plane.p[2] = map_parse_vector(&p);
121 map_parse_token(&p, plane.texture, sizeof(plane.texture)); 124 map_parse_token(&p, plane.texture, sizeof(plane.texture));
122 125
123 char buf[64]; 126 char buf[64];
124 map_parse_token(&p, buf, sizeof(buf)); plane.shift[0] = (float)atof(buf); 127 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); 128 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); 129 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); 130 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); 131 map_parse_token(&p, buf, sizeof(buf)); plane.scale[1] = (float)atof(buf);
129 132
130 array_push(planes, plane); 133 array_push(planes, plane);
131 } 134 }
132 brush.planes = planes.data; 135 brush.planes = planes.data;
133 brush.plane_count = (int)planes.length; 136 brush.plane_count = (int)planes.length;
134 array_push(brushes, brush); 137 array_push(brushes, brush);
135 } else { 138 } else {
136 map_get(&p); 139 map_get(&p);
137 } 140 }
138 } 141 }
139 entity.properties = props.data; 142 entity.properties = props.data;
140 entity.property_count = (int)props.length; 143 entity.property_count = (int)props.length;
141 entity.brushes = brushes.data; 144 entity.brushes = brushes.data;
142 entity.brush_count = (int)brushes.length; 145 entity.brush_count = (int)brushes.length;
143 array_push(entities, entity); 146 array_push(entities, entity);
144 } 147 }
145 } 148 }
146 149
147 map.entities = entities.data; 150 map.entities = entities.data;
148 map.entity_count = (int)entities.length; 151 map.entity_count = (int)entities.length;
149 vfs_free(data); 152 vfs_free(data);
150 return map; 153 return map;
151} 154}
152 155
153void FreeMap(Map map) { 156void FreeMap(Map map) {
154 for (int i = 0; i < map.entity_count; i++) { 157 for (int i = 0; i < map.entity_count; i++) {
155 MapEntity *e = &map.entities[i]; 158 MapEntity *e = &map.entities[i];
156 if (e->properties) free(e->properties); 159 if (e->properties) free(e->properties);
157 for (int j = 0; j < e->brush_count; j++) { 160 for (int j = 0; j < e->brush_count; j++) {
158 if (e->brushes[j].planes) free(e->brushes[j].planes); 161 if (e->brushes[j].planes) free(e->brushes[j].planes);
159 } 162 }
160 if (e->brushes) free(e->brushes); 163 if (e->brushes) free(e->brushes);
161 } 164 }
162 if (map.entities) free(map.entities); 165 if (map.entities) free(map.entities);
163} 166}
164 167
165Plane PlaneFromPoints(Vector3 p1, Vector3 p2, Vector3 p3) { 168Plane PlaneFromPoints(Vector3 p1, Vector3 p2, Vector3 p3) {
166 Vector3 v1 = Vector3Subtract(p2, p1); 169 Vector3 v1 = Vector3Subtract(p2, p1);
167 Vector3 v2 = Vector3Subtract(p3, p1); 170 Vector3 v2 = Vector3Subtract(p3, p1);
168 Vector3 normal = Vector3Normalize(Vector3CrossProduct(v1, v2)); 171 Vector3 normal = Vector3Normalize(Vector3CrossProduct(v1, v2));
169 return (Plane){ normal, Vector3DotProduct(normal, p1) }; 172 return (Plane){ normal, Vector3DotProduct(normal, p1) };
170} 173}
171 174
172void PolyFree(Polygon p) { 175void PolyFree(Polygon p) {
173 if (p.verts) free(p.verts); 176 if (p.verts) free(p.verts);
174} 177}
175 178
176Polygon PolyClip(Polygon poly, Plane plane) { 179Polygon PolyClip(Polygon poly, Plane plane) {
177 if (poly.count == 0) return poly; 180 if (poly.count == 0) return poly;
178 181
179 array(Vector3) out_verts; 182 array(Vector3) out_verts;
180 array_init(out_verts); 183 array_init(out_verts);
181 184
182 for (int i = 0; i < poly.count; i++) { 185 for (int i = 0; i < poly.count; i++) {
183 Vector3 p1 = poly.verts[i]; 186 Vector3 p1 = poly.verts[i];
184 Vector3 p2 = poly.verts[(i + 1) % poly.count]; 187 Vector3 p2 = poly.verts[(i + 1) % poly.count];
185 188
186 float d1 = Vector3DotProduct(plane.normal, p1) - plane.dist; 189 float d1 = Vector3DotProduct(plane.normal, p1) - plane.dist;
187 float d2 = Vector3DotProduct(plane.normal, p2) - plane.dist; 190 float d2 = Vector3DotProduct(plane.normal, p2) - plane.dist;
188 191
189 // Using a smaller epsilon and more robust logic 192 const float eps = 0.001f;
190 const float eps = 0.001f; 193
191 194 if (d1 >= -eps) {
192 if (d1 >= -eps) { 195 if (d2 >= -eps) {
193 if (d2 >= -eps) { 196 array_push(out_verts, p2);
194 array_push(out_verts, p2); 197 } else {
195 } else { 198 float t = d1 / (d1 - d2);
196 float t = d1 / (d1 - d2); 199 Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t));
197 Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t)); 200 array_push(out_verts, intersect);
198 array_push(out_verts, intersect); 201 }
199 } 202 } else {
200 } else { 203 if (d2 >= -eps) {
201 if (d2 >= -eps) { 204 float t = d1 / (d1 - d2);
202 float t = d1 / (d1 - d2); 205 Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t));
203 Vector3 intersect = Vector3Add(p1, Vector3Scale(Vector3Subtract(p2, p1), t)); 206 array_push(out_verts, intersect);
204 array_push(out_verts, intersect); 207 array_push(out_verts, p2);
205 array_push(out_verts, p2); 208 }
206 } 209 }
207 } 210 }
208 } 211
209 212 Polygon res;
210 Polygon res; 213 res.verts = out_verts.data;
211 res.verts = out_verts.data; 214 res.count = (int)out_verts.length;
212 res.count = (int)out_verts.length; 215 return res;
213 return res;
214} 216}
215 217
216Polygon CreateLargeQuad(Plane plane) { 218Polygon CreateLargeQuad(Plane plane) {
217 Vector3 n = plane.normal; 219 Vector3 n = plane.normal;
218 Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 }; 220 Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 };
219 Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n)); 221 Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n));
220 up = Vector3CrossProduct(n, right); 222 up = Vector3CrossProduct(n, right);
221 223
222 Vector3 center = Vector3Scale(n, plane.dist); 224 Vector3 center = Vector3Scale(n, plane.dist);
223 float size = 10000.0f; 225 float size = 10000.0f;
224 226
225 Polygon poly; 227 Polygon poly;
226 poly.verts = ALLOC(Vector3, 4); 228 poly.verts = ALLOC(Vector3, 4);
227 poly.count = 4; 229 poly.count = 4;
228 poly.verts[0] = Vector3Add(center, Vector3Add(Vector3Scale(right, size), Vector3Scale(up, size))); 230 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))); 231 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))); 232 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))); 233 poly.verts[3] = Vector3Add(center, Vector3Add(Vector3Scale(right, size), Vector3Scale(up, -size)));
232 234
233 return poly; 235 return poly;
234} 236}
235 237
236Vector2 GetUV(Vector3 p, MapPlane *mp, Plane plane) { 238Vector2 GetUV(Vector3 p, MapPlane *mp, Plane plane) {
237 Vector3 n = plane.normal; 239 Vector3 n = plane.normal;
238 Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 }; 240 Vector3 up = (fabsf(n.y) < 0.999f) ? (Vector3){ 0, 1, 0 } : (Vector3){ 1, 0, 0 };
239 Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n)); 241 Vector3 right = Vector3Normalize(Vector3CrossProduct(up, n));
240 up = Vector3CrossProduct(n, right); 242 up = Vector3CrossProduct(n, right);
241 243
242 float u = Vector3DotProduct(p, right) * (1.0f / (mp->scale[0] ? mp->scale[0] : 1.0f)) + mp->shift[0]; 244 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]; 245 float v = Vector3DotProduct(p, up) * (1.0f / (mp->scale[1] ? mp->scale[1] : 1.0f)) + mp->shift[1];
244 246
245 return (Vector2){ u / 64.0f, v / 64.0f }; 247 return (Vector2){ u / 64.0f, v / 64.0f };
248}
249
250void UnloadMap(void) {
251 if (game.map.models) {
252 for (int i = 0; i < game.map.count; i++) {
253 UnloadModel(game.map.models[i]);
254 }
255 free(game.map.models);
256 game.map.models = NULL;
257 game.map.count = 0;
258 }
259}
260
261bool LoadMap(const char *filename) {
262 TraceLog(LOG_INFO, "Loading map: %s", filename);
263
264 Map map = ParseMap(filename);
265 if (map.entity_count == 0) {
266 TraceLog(LOG_ERROR, "Failed to load map or map is empty: %s", filename);
267 return false;
268 }
269
270 UnloadMap();
271
272 array(TextureGroup) groups;
273 array_init(groups);
274
275 // Default player state if no start found
276 game.player.pos = (Vector3){ 0, 10, 0 };
277 game.player.yaw = 0;
278 game.player.pitch = 0;
279
280 int total_brushes = 0;
281 for (int i = 0; i < map.entity_count; i++) {
282 MapEntity *e = &map.entities[i];
283 bool is_world = false;
284 const char *classname = "";
285
286 for (int j = 0; j < e->property_count; j++) {
287 if (strcmp(e->properties[j].key, "classname") == 0) {
288 classname = e->properties[j].value;
289 if (strcmp(classname, "worldspawn") == 0) is_world = true;
290 }
291 }
292
293 // Handle entities (like player start)
294 for (int j = 0; j < e->property_count; j++) {
295 if (strcmp(e->properties[j].key, "origin") == 0) {
296 float x, y, z;
297 sscanf(e->properties[j].value, "%f %f %f", &x, &y, &z);
298 if (strcmp(classname, "info_player_start") == 0) {
299 game.player.pos = (Vector3){ x, z, -y };
300 float angle = 0;
301 for (int k = 0; k < e->property_count; k++) {
302 if (strcmp(e->properties[k].key, "angle") == 0) {
303 angle = (float)atof(e->properties[k].value);
304 }
305 }
306 game.player.yaw = (angle + 90.0f) * DEG2RAD;
307 game.player.pitch = 0;
308 TraceLog(LOG_INFO, "Player spawn set to: %f, %f, %f", game.player.pos.x, game.player.pos.y, game.player.pos.z);
309 }
310 }
311 }
312
313 if (is_world) {
314 total_brushes += e->brush_count;
315 for (int j = 0; j < e->brush_count; j++) {
316 MapBrush *brush = &e->brushes[j];
317 for (int p_idx = 0; p_idx < brush->plane_count; p_idx++) {
318 MapPlane *mp = &brush->planes[p_idx];
319 Plane plane = PlaneFromPoints(mp->p[0], mp->p[1], mp->p[2]);
320
321 if (Vector3Length(plane.normal) < 0.5f) continue;
322
323 Polygon poly = CreateLargeQuad(plane);
324 for (int k = 0; k < brush->plane_count; k++) {
325 if (p_idx == k) continue;
326 Plane clipPlane = PlaneFromPoints(brush->planes[k].p[0], brush->planes[k].p[1], brush->planes[k].p[2]);
327 if (Vector3Length(clipPlane.normal) < 0.5f) continue;
328
329 Polygon next = PolyClip(poly, clipPlane);
330 PolyFree(poly);
331 poly = next;
332 }
333
334 if (poly.count >= 3) {
335 TextureGroup *group = NULL;
336 for (int g = 0; g < groups.length; g++) {
337 if (strcmp(groups.data[g].texture, mp->texture) == 0) {
338 group = &groups.data[g];
339 break;
340 }
341 }
342 if (!group) {
343 TextureGroup new_group = { 0 };
344 strncpy(new_group.texture, mp->texture, sizeof(new_group.texture));
345 array_init(new_group.vertices);
346 array_init(new_group.texcoords);
347 array_init(new_group.normals);
348 array_push(groups, new_group);
349 group = &groups.data[groups.length - 1];
350 }
351 for (int v = 1; v < poly.count - 1; v++) {
352 Vector3 v0 = poly.verts[0];
353 Vector3 v1 = poly.verts[v+1];
354 Vector3 v2 = poly.verts[v];
355 array_push(group->vertices, v0.x); array_push(group->vertices, v0.y); array_push(group->vertices, v0.z);
356 array_push(group->vertices, v1.x); array_push(group->vertices, v1.y); array_push(group->vertices, v1.z);
357 array_push(group->vertices, v2.x); array_push(group->vertices, v2.y); array_push(group->vertices, v2.z);
358 Vector2 uv0 = GetUV(v0, mp, plane);
359 Vector2 uv1 = GetUV(v1, mp, plane);
360 Vector2 uv2 = GetUV(v2, mp, plane);
361 array_push(group->texcoords, uv0.x); array_push(group->texcoords, uv0.y);
362 array_push(group->texcoords, uv1.x); array_push(group->texcoords, uv1.y);
363 array_push(group->texcoords, uv2.x); array_push(group->texcoords, uv2.y);
364 Vector3 out_normal = Vector3Scale(plane.normal, -1.0f);
365 array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z);
366 array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z);
367 array_push(group->normals, out_normal.x); array_push(group->normals, out_normal.y); array_push(group->normals, out_normal.z);
368 }
369 }
370 PolyFree(poly);
371 }
372 }
373 }
374 }
375
376 array(Model) final_models;
377 array_init(final_models);
378
379 for (int i = 0; i < groups.length; i++) {
380 TextureGroup *g = &groups.data[i];
381 if (g->vertices.length == 0) continue;
382 Mesh mesh = { 0 };
383 mesh.vertexCount = (int)g->vertices.length / 3;
384 mesh.triangleCount = mesh.vertexCount / 3;
385 mesh.vertices = (float *)malloc(g->vertices.length * sizeof(float));
386 memcpy(mesh.vertices, g->vertices.data, g->vertices.length * sizeof(float));
387 mesh.texcoords = (float *)malloc(g->texcoords.length * sizeof(float));
388 memcpy(mesh.texcoords, g->texcoords.data, g->texcoords.length * sizeof(float));
389 mesh.normals = (float *)malloc(g->normals.length * sizeof(float));
390 memcpy(mesh.normals, g->normals.data, g->normals.length * sizeof(float));
391 UploadMesh(&mesh, false);
392 Model model = LoadModelFromMesh(mesh);
393 model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = GetTexture(g->texture);
394 array_push(final_models, model);
395 array_free(g->vertices);
396 array_free(g->texcoords);
397 array_free(g->normals);
398 }
399
400 game.map.models = final_models.data;
401 game.map.count = (int)final_models.length;
402 array_free(groups);
403
404 FreeMap(map);
405 TraceLog(LOG_INFO, "Processed %d brushes into %d models", total_brushes, game.map.count);
406 return true;
407}
408
409bool CheckMapCollision(Vector3 start, Vector3 end, RayCollision *outCollision) {
410 Vector3 diff = Vector3Subtract(end, start);
411 float maxDist = Vector3Length(diff);
412 if (maxDist < 0.001f) return false;
413
414 Ray ray = { 0 };
415 ray.position = start;
416 ray.direction = Vector3Scale(diff, 1.0f / maxDist);
417
418 RayCollision closest = { 0 };
419 closest.distance = FLT_MAX;
420 closest.hit = false;
421
422 for (int i = 0; i < game.map.count; i++) {
423 Model model = game.map.models[i];
424 for (int m = 0; m < model.meshCount; m++) {
425 RayCollision col = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
426 if (col.hit && col.distance < closest.distance && col.distance <= maxDist) {
427 closest = col;
428 }
429 }
430 }
431
432 if (closest.hit) {
433 if (outCollision) *outCollision = closest;
434 return true;
435 }
436 return false;
246} 437}