Browse Source

add collections as a precursor for asset loading

Ivan Avdeev 7 years ago
parent
commit
aad29b1b37
11 changed files with 379 additions and 81 deletions
  1. 52 7
      OpenSource.c
  2. 30 0
      TODO
  3. 42 26
      bsp.c
  4. 7 47
      bsp.h
  5. 90 0
      collection.c
  6. 54 0
      collection.h
  7. 1 1
      compile.sh
  8. 35 0
      filemap.c
  9. 21 0
      filemap.h
  10. 39 0
      mempools.h
  11. 8 0
      utils.h

+ 52 - 7
OpenSource.c

@@ -1,4 +1,6 @@
 #include "bsp.h"
+#include "collection.h"
+#include "mempools.h"
 
 #include "atto/app.h"
 #define ATTO_GL_DEBUG
@@ -6,10 +8,11 @@
 #include "atto/gl.h"
 #include "atto/math.h"
 
+#include <string.h>
 #include <stdlib.h> /* malloc */
 #include <stddef.h> /* offsetof */
 
-static char temp_data[32*1024*1024];
+static char temp_data[64*1024*1024];
 
 static struct TemporaryPool temp = {
 	.storage = temp_data,
@@ -313,7 +316,7 @@ static struct {
 	struct AMat4f model;
 } g;
 
-static void opensrcInit(const char *filename) {
+static void opensrcInit(struct ICollection *collection, const char *map) {
 	g.source.program = aGLProgramCreateSimple(vertex_src, fragment_src);
 	if (g.source.program < 1) {
 		aAppDebugPrintf("Shader compilation error: %s", a_gl_error);
@@ -322,18 +325,18 @@ static void opensrcInit(const char *filename) {
 	fsquadInit();
 
 	struct BSPLoadModelContext loadctx = {
-		.filename = filename,
+		.collection = collection,
 		.pool = &stdpool,
 		.tmp = &temp,
 		.model = &g.worldspawn
 	};
-	const enum BSPLoadResult result = bspLoadWorldspawn(loadctx);
+	const enum BSPLoadResult result = bspLoadWorldspawn(loadctx, map);
 	if (result != BSPLoadResult_Success) {
-		aAppDebugPrintf("Cannot load file \"%s\": %d", filename, result);
+		aAppDebugPrintf("Cannot load map \"%s\": %d", map, result);
 		aAppTerminate(-2);
 	}
 
-	aAppDebugPrintf("Loaded %s to %u draw calls", filename, g.worldspawn.draws_count);
+	aAppDebugPrintf("Loaded %s to %u draw calls", map, g.worldspawn.draws_count);
 	aAppDebugPrintf("AABB (%f, %f, %f) - (%f, %f, %f)",
 			g.worldspawn.aabb.min.x,
 			g.worldspawn.aabb.min.y,
@@ -518,10 +521,52 @@ static void opensrcPointer(ATimeUs timestamp, int dx, int dy, unsigned int btndi
 
 void attoAppInit(struct AAppProctable *proctable) {
 	aGLInit();
-	opensrcInit(a_app_state->argv[1]);
+	const int max_collections = 16;
+	struct FilesystemCollection collections[max_collections];
+	int free_collection = 0;
+	const char *map = 0;
+
+	for (int i = 1; i < a_app_state->argc; ++i) {
+		const char *argv = a_app_state->argv[i];
+		if (strcmp(argv, "-d") == 0) {
+			if (i == a_app_state->argc - 1) {
+				aAppDebugPrintf("-d requires an argument");
+				goto print_usage_and_exit;
+			}
+			const char *value = a_app_state->argv[++i];
+
+			if (free_collection >= max_collections) {
+				aAppDebugPrintf("Too many fs collections specified: %s", value); 
+				goto print_usage_and_exit;
+			}
+
+			filesystemCollectionCreate(collections + (free_collection++), value);
+		} else {
+			if (map) {
+				aAppDebugPrintf("Only one map can be specified");
+				goto print_usage_and_exit;
+			}
+			map = argv;
+		}
+	}
+
+	if (!map || !free_collection) {
+		aAppDebugPrintf("At least one map and one collection required");
+		goto print_usage_and_exit;
+	}
+
+	for (int i = 0; i < free_collection - 1; ++i)
+		collections[i].head.next = &collections[i+1].head;
+
+	opensrcInit(&collections[0].head, map);
 
 	proctable->resize = opensrcResize;
 	proctable->paint = opensrcPaint;
 	proctable->key = opensrcKeyPress;
 	proctable->pointer = opensrcPointer;
+	return;
+
+print_usage_and_exit:
+	aAppDebugPrintf("usage: %s <-d path> ... mapname", a_app_state->argv[0]);
+	aAppTerminate(1);
 }

+ 30 - 0
TODO

@@ -0,0 +1,30 @@
++ grab input
++ hide cursor
+- simpler camera with limited pitch
++ evdev
+	+ kbd
+	+ mouse
+	+ joy
+	+ scan
+- move atlas to atto
+- move filemap to atto
++ rpi build
+- entities
+- informative log
++ displacements
+- vpk reading || vpk_fuse
+- textures
+- materials
+- bsp models
+- models
+- console
+- running stats
+- ver 20 lightmaps
+- visibility clusters
+- multiple levels
+- bump lightmaps
+- valve lighting
+
+KNOWN ISSUES:
+1. displacement lightmap coordinates are broken. There are visible artifacts.
+And core loading them detects wrong dimensions that are larger than the lightmap size

+ 42 - 26
bsp.c

@@ -1,7 +1,8 @@
-#include "vbsp.h"
 #include "bsp.h"
 #include "atlas.h"
-#include "filemap.h"
+#include "vbsp.h"
+#include "collection.h"
+#include "mempools.h"
 #include <stdint.h>
 #include <string.h> /* memset */
 #include <stdio.h> /* printf */
@@ -688,51 +689,66 @@ static enum BSPLoadResult bspLoadModel(struct BSPModel *model, struct MemoryPool
 	return BSPLoadResult_Success;
 }
 
-static int initLump(const char *name, const struct VBSPLumpHeader *header, const struct AFileMap *map,
-		struct AnyLump *ptr, uint32_t item_size) {
-	if (map->size <= header->file_offset || map->size - header->file_offset < header->size)
-		return 0;
+static int lumpRead(const char *name, const struct VBSPLumpHeader *header,
+		struct IFile *file, struct TemporaryPool *pool,
+		struct AnyLump *out_ptr, uint32_t item_size) {
+	out_ptr->p = tmpAdvance(pool, header->size);
+	if (!out_ptr->p) {
+		PRINT("Not enough temp memory to allocate storage for lump %s", name);
+		return -1;
+	}
+
+	const size_t bytes = file->read(file, header->file_offset, header->size, (void*)out_ptr->p);
+	if (bytes != header->size) {
+		PRINT("Cannot read full lump %s, read only %zu bytes out of %u", name, bytes, header->size);
+		return -1;
+	}
 
-	PRINT("Lump %s, offset %u, size %u bytes / %u item = %u elements",
+	PRINT("Read lump %s, offset %u, size %u bytes / %u item = %u elements",
 			name, header->file_offset, header->size, item_size, header->size / item_size);
 
-	ptr->p = (const char*)map->map + header->file_offset;
-	ptr->n = header->size / item_size;
+	out_ptr->n = header->size / item_size;
 	return 1;
 }
 
-enum BSPLoadResult bspLoadWorldspawn(struct BSPLoadModelContext context) {
+enum BSPLoadResult bspLoadWorldspawn(struct BSPLoadModelContext context, const char *mapname) {
 	enum BSPLoadResult result = BSPLoadResult_Success;
+	struct IFile *file = 0;
+	if (CollectionOpen_Success !=
+			collectionChainOpen(context.collection, mapname, File_Map, &file)) {
+		return BSPLoadResult_ErrorFileOpen;
+	}
+
+	void *tmp_cursor = tmpGetCursor(context.tmp);
 
-	struct AFileMap file = aFileMapOpen(context.filename);
-	if (!file.map) return BSPLoadResult_ErrorFileOpen;
-	if (file.size <= sizeof(struct VBSPHeader)) {
-		PRINT("Size is too small: %zu <= %zu", file.size, sizeof(struct VBSPHeader));
+	struct VBSPHeader vbsp_header;
+	size_t bytes = file->read(file, 0, sizeof vbsp_header, &vbsp_header);
+	if (bytes < sizeof(vbsp_header)) {
+		PRINT("Size is too small: %zu <= %zu", bytes, sizeof(struct VBSPHeader));
 		result = BSPLoadResult_ErrorFileFormat;
 		goto exit;
 	}
 
-	const struct VBSPHeader *hdr = file.map;
-	if (hdr->ident[0] != 'V' || hdr->ident[1] != 'B' ||
-			hdr->ident[2] != 'S' || hdr->ident[3] != 'P') {
+	if (vbsp_header.ident[0] != 'V' || vbsp_header.ident[1] != 'B' ||
+			vbsp_header.ident[2] != 'S' || vbsp_header.ident[3] != 'P') {
 		PRINT("Error: invalid ident => %c%c%c%c != VBSP",
-				hdr->ident[0], hdr->ident[1], hdr->ident[2], hdr->ident[3]);
+				vbsp_header.ident[0], vbsp_header.ident[1], vbsp_header.ident[2], vbsp_header.ident[3]);
 		result = BSPLoadResult_ErrorFileFormat;
 		goto exit;
 	}
 
-	if (hdr->version != 19 && hdr->version != 20) {
-		PRINT("Error: invalid version: %d != 19 or 20", hdr->version);
+	if (vbsp_header.version != 19 && vbsp_header.version != 20) {
+		PRINT("Error: invalid version: %d != 19 or 20", vbsp_header.version);
 		result = BSPLoadResult_ErrorFileFormat;
 		goto exit;
 	}
 
-	PRINT("VBSP version %u opened", hdr->version);
+	PRINT("VBSP version %u opened", vbsp_header.version);
 
 	struct Lumps lumps;
-	lumps.version = hdr->version;
+	lumps.version = vbsp_header.version;
 #define BSPLUMP(name, type, field) \
-	if (!initLump(#name, hdr->lump_headers + VBSP_Lump_##name, &file, \
+	if (1 != lumpRead(#name, vbsp_header.lump_headers + VBSP_Lump_##name, file, context.tmp, \
 			(struct AnyLump*)&lumps.field, sizeof(type))) { \
 		result = BSPLoadResult_ErrorFileFormat; \
 		goto exit; \
@@ -741,11 +757,11 @@ enum BSPLoadResult bspLoadWorldspawn(struct BSPLoadModelContext context) {
 #undef BSPLUMP
 
 	result = bspLoadModel(context.model, context.pool, context.tmp, &lumps, 0);
-	if (result != BSPLoadResult_Success) {
+	if (result != BSPLoadResult_Success)
 		PRINT("Error: bspLoadModel() => %s", R2S(result));
-	}
 
 exit:
-	aFileMapClose(&file);
+	tmpReturnToPosition(context.tmp, tmp_cursor);
+	if (file) file->close(file);
 	return result;
 }

+ 7 - 47
bsp.h

@@ -1,50 +1,6 @@
 #pragma once
-#include "atto/math.h"
 #include "atto/gl.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#define STR1_(m) #m
-#define STR_(m) STR1_(m)
-#define PRINT_(fmt, ...) fprintf(stderr, __FILE__ ":" STR_(__LINE__) ": " fmt "\n", __VA_ARGS__)
-#define ASSERT(cond) if (!(cond)){PRINT_("%s failed", #cond); abort();}
-
-struct MemoryPool {
-	void *(*alloc)(struct MemoryPool *pool, size_t size);
-	void (*free)(struct MemoryPool *pool, void *ptr);
-};
-#define POOL_ALLOC(p, sz) ((p)->alloc(p, sz))
-#define POOL_FREE(p, ptr) ((p)->free(p, ptr))
-
-struct TemporaryPool {
-	char *storage;
-	size_t size, cursor;
-};
-
-static inline void *tmpGetCursor(const struct TemporaryPool *tmp) {
-	return tmp->storage + tmp->cursor;
-}
-static inline size_t tmpGetLeft(const struct TemporaryPool *tmp) {
-	return tmp->size - tmp->cursor;
-}
-static inline void *tmpAdvance(struct TemporaryPool *tmp, size_t size) {
-	if (tmp->size - tmp->cursor < size)
-		return 0;
-
-	void *ret = tmp->storage + tmp->cursor;
-	tmp->cursor += size;
-	return ret;
-}
-static inline void tmpReturn(struct TemporaryPool *tmp, size_t size) {
-	ASSERT(size <= tmp->cursor);
-	tmp->cursor -= size;
-}
-static inline void tmpReturnToPosition(struct TemporaryPool *tmp, void *marker) {
-	ASSERT((char*)marker >= tmp->storage);
-	const size_t to_release = tmp->cursor - ((char*)marker - tmp->storage);
-	tmpReturn(tmp, to_release);
-}
+#include "atto/math.h"
 
 struct AABB { struct AVec3f min, max; };
 struct Plane { struct AVec3f n; float d; };
@@ -82,8 +38,12 @@ struct BSPModel {
 	struct BSPDraw *draws;
 };
 
+struct ICollection;
+struct MemoryPool;
+struct TemporaryPool;
+
 struct BSPLoadModelContext {
-	const char *filename;
+	struct ICollection *collection;
 	struct MemoryPool *pool;
 	struct TemporaryPool *tmp;
 
@@ -100,4 +60,4 @@ enum BSPLoadResult {
 	BSPLoadResult_ErrorCapabilities
 };
 
-enum BSPLoadResult bspLoadWorldspawn(struct BSPLoadModelContext context);
+enum BSPLoadResult bspLoadWorldspawn(struct BSPLoadModelContext context, const char *mapname);

+ 90 - 0
collection.c

@@ -0,0 +1,90 @@
+#include "collection.h"
+#include <string.h>
+#include <stdio.h>
+
+enum CollectionOpenResult collectionChainOpen(struct ICollection *collection,
+		const char *name, enum FileType type, struct IFile **out_file) {
+	while (collection) {
+		enum CollectionOpenResult result = collection->open(collection, name, type, out_file);
+		if (result == CollectionOpen_Success) return result;
+		if (result == CollectionOpen_NotFound) {
+			collection = collection->next;
+		} else {
+			/* TODO print error */
+			return result;
+		}
+	}
+
+	return CollectionOpen_NotFound;
+}
+
+static enum AFileResult filesystemCollectionFile_Open(struct FilesystemCollectionFile_ *f, const char *filename) {
+	const enum AFileResult result = aFileOpen(&f->file, filename);
+	f->opened = result == AFile_Success;
+	f->head.size = f->file.size;
+	return result;
+}
+
+static size_t filesystemCollectionFile_Read(struct IFile *file, size_t offset, size_t size, void *buffer) {
+	struct FilesystemCollectionFile_ *f = (void*)file;
+	const size_t result = aFileReadAtOffset(&f->file, offset, size, buffer);
+	return result != AFileError ? result : 0;
+}
+
+static void filesystemCollectionFile_Close(struct IFile *file) {
+	struct FilesystemCollectionFile_ *f = (void*)file;
+	aFileClose(&f->file);
+	f->head.size = 0;
+	f->opened = 0;
+}
+
+static void filesystemCollectionClose(struct ICollection *collection) {
+	(void)(collection);
+	/* TODO ensure all files are closed */
+}
+
+static enum CollectionOpenResult filesystemCollectionOpen(struct ICollection *collection,
+			const char *name, enum FileType type, struct IFile **out_file) {
+	struct FilesystemCollection *fsc = (struct FilesystemCollection*)collection;
+	char buffer[512];
+	const char *subdir = "/";
+	const char *suffix = "";
+
+	*out_file = 0;
+
+	int fileslot = 0;
+	for(; fileslot < FilesystemCollectionFileSlots; ++fileslot)
+		if (fsc->files[fileslot].head.read) break;
+	if (fileslot >= FilesystemCollectionFileSlots)
+		return CollectionOpen_TooManyFiles;
+	struct FilesystemCollectionFile_ *f = fsc->files + fileslot;
+
+	switch (type) {
+		case File_Map: subdir = "/maps/"; suffix = ".bsp"; break;
+		case File_Material: subdir = "/materials/"; suffix = ".vmt"; break;
+		case File_Texture: subdir = "/textures/"; suffix = ".vtf"; break;
+		case File_Model: subdir = "/models/"; suffix = ".mdl"; break;
+	}
+	snprintf(buffer, sizeof(buffer) - 1, "%s%s%s%s", fsc->path, subdir, name, suffix);
+	/* FIXME case insensitivity */
+
+	if (AFile_Success != filesystemCollectionFile_Open(f, buffer))
+		return CollectionOpen_NotFound;
+	*out_file = &f->head;
+	return CollectionOpen_Success;
+}
+
+void filesystemCollectionCreate(struct FilesystemCollection *collection, const char *dir) {
+	memset(collection, 0, sizeof *collection);
+	strncpy(collection->path, dir, sizeof(collection->path) - 1);
+
+	for (int i = 0; i < FilesystemCollectionFileSlots; ++i) {
+		struct FilesystemCollectionFile_ *f = collection->files + i;
+		aFileReset(&f->file);
+		f->head.read = filesystemCollectionFile_Read;
+		f->head.close = filesystemCollectionFile_Close;
+	}
+
+	collection->head.open = filesystemCollectionOpen;
+	collection->head.close = filesystemCollectionClose;
+}

+ 54 - 0
collection.h

@@ -0,0 +1,54 @@
+#pragma once
+#include "filemap.h"
+#include <stddef.h>
+
+struct IFile {
+	size_t size;
+	/* read size bytes into buffer
+	 * returns bytes read, or < 0 on error. error codes aren't specified */
+	size_t (*read)(struct IFile *file, size_t offset, size_t size, void *buffer);
+	/* free any internal resources.
+	 * will not free memory associated with this structure itself */
+	void (*close)();
+};
+
+enum CollectionOpenResult {
+	CollectionOpen_Success,
+	CollectionOpen_NotFound, /* such item was not found in collection */
+	CollectionOpen_TooManyFiles, /* collection limit on open files exceeded (regardless of was the item found or not) */
+	CollectionOpen_Internal /* collection data is inconsistent internally, e.g. corrupted archive */
+};
+
+enum FileType {
+	File_Map,
+	File_Material,
+	File_Texture,
+	File_Model
+};
+
+struct ICollection {
+	/* free any internal resources, but don't deallocate this structure itself */
+	void (*close)();
+	enum CollectionOpenResult (*open)(struct ICollection *collection,
+			const char *name, enum FileType type, struct IFile **out_file);
+	struct ICollection *next;
+};
+
+enum CollectionOpenResult collectionChainOpen(struct ICollection *collection,
+		const char *name, enum FileType type, struct IFile **out_file);
+
+#define FilesystemCollectionFileSlots 16
+
+struct FilesystemCollectionFile_ {
+		struct IFile head;
+		struct AFile file;
+		int opened;
+};
+
+struct FilesystemCollection {
+	struct ICollection head;
+	char path[256];
+	struct FilesystemCollectionFile_ files[FilesystemCollectionFileSlots];
+};
+
+void filesystemCollectionCreate(struct FilesystemCollection *collection, const char *dir);

+ 1 - 1
compile.sh

@@ -10,7 +10,7 @@ DEBUG=
 RPI=
 RPI_ROOT=${RPI_ROOT:-'/opt/raspberry-pi'}
 
-SOURCES='OpenSource.c bsp.c atlas.c filemap.c'
+SOURCES='OpenSource.c bsp.c atlas.c filemap.c collection.c'
 WERROR='-Werror'
 CFLAGS="-D_POSIX_C_SOURCE=200809L $CFLAGS"
 

+ 35 - 0
filemap.c

@@ -36,3 +36,38 @@ void aFileMapClose(struct AFileMap *file) {
 	}
 }
 
+void aFileReset(struct AFile *file) {
+	file->size = 0;
+	file->impl_.fd = -1;
+}
+
+enum AFileResult aFileOpen(struct AFile *file, const char *filename) {
+	file->impl_.fd = open(filename, O_RDONLY);
+	if (file->impl_.fd < 0)
+		return AFile_Fail;
+
+	struct stat stat;
+	fstat(file->impl_.fd, &stat);
+	file->size = stat.st_size;
+
+	return AFile_Success;
+}
+
+size_t aFileRead(struct AFile *file, size_t size, void *buffer) {
+	ssize_t rd = read(file->impl_.fd, buffer, size);
+	if (rd < 0) perror("read(fd)");
+	return rd;
+}
+
+size_t aFileReadAtOffset(struct AFile *file, size_t off, size_t size, void *buffer) {
+	ssize_t rd = pread(file->impl_.fd, buffer, size, off);
+	if (rd < 0) perror("pread(fd)");
+	return rd;
+}
+
+void aFileClose(struct AFile *file) {
+	if (file->impl_.fd > 0) {
+		close(file->impl_.fd);
+		aFileReset(file);
+	}
+}

+ 21 - 0
filemap.h

@@ -14,4 +14,25 @@ struct AFileMap {
 struct AFileMap aFileMapOpen(const char *filename);
 void aFileMapClose(struct AFileMap *file);
 
+struct AFile {
+	size_t size;
+	struct {
+		int fd;
+	} impl_;
+};
+
+#define AFileError ((size_t)-1)
+
+enum AFileResult {
+	AFile_Success,
+	AFile_Fail
+};
+
+/* reset file to default state, useful for initialization */
+void aFileReset(struct AFile *file);
+enum AFileResult aFileOpen(struct AFile *file, const char *filename);
+size_t aFileRead(struct AFile *file, size_t size, void *buffer);
+size_t aFileReadAtOffset(struct AFile *file, size_t off, size_t size, void *buffer);
+void aFileClose(struct AFile *file);
+
 #endif /* ifndef FILEMAP_H__INCLUDED */

+ 39 - 0
mempools.h

@@ -0,0 +1,39 @@
+#pragma once
+#include "utils.h"
+#include <stddef.h>
+
+struct MemoryPool {
+	void *(*alloc)(struct MemoryPool *pool, size_t size);
+	void (*free)(struct MemoryPool *pool, void *ptr);
+};
+#define POOL_ALLOC(p, sz) ((p)->alloc(p, sz))
+#define POOL_FREE(p, ptr) ((p)->free(p, ptr))
+
+struct TemporaryPool {
+	char *storage;
+	size_t size, cursor;
+};
+
+static inline void *tmpGetCursor(const struct TemporaryPool *tmp) {
+	return tmp->storage + tmp->cursor;
+}
+static inline size_t tmpGetLeft(const struct TemporaryPool *tmp) {
+	return tmp->size - tmp->cursor;
+}
+static inline void *tmpAdvance(struct TemporaryPool *tmp, size_t size) {
+	if (tmp->size - tmp->cursor < size)
+		return 0;
+
+	void *ret = tmp->storage + tmp->cursor;
+	tmp->cursor += size;
+	return ret;
+}
+static inline void tmpReturn(struct TemporaryPool *tmp, size_t size) {
+	ASSERT(size <= tmp->cursor);
+	tmp->cursor -= size;
+}
+static inline void tmpReturnToPosition(struct TemporaryPool *tmp, void *marker) {
+	ASSERT((char*)marker >= tmp->storage);
+	const size_t to_release = tmp->cursor - ((char*)marker - tmp->storage);
+	tmpReturn(tmp, to_release);
+}

+ 8 - 0
utils.h

@@ -0,0 +1,8 @@
+#pragma once
+#include <stdlib.h>
+#include <stdio.h>
+
+#define STR1_(m) #m
+#define STR_(m) STR1_(m)
+#define PRINT_(fmt, ...) fprintf(stderr, __FILE__ ":" STR_(__LINE__) ": " fmt "\n", __VA_ARGS__)
+#define ASSERT(cond) if (!(cond)){PRINT_("%s failed", #cond); abort();}