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