Sfoglia il codice sorgente

try loading skyboxes into cubemaps and fail

they're not square, #25
Ivan Avdeev 6 anni fa
parent
commit
a276d2cd02
10 ha cambiato i file con 195 aggiunte e 46 eliminazioni
  1. 1 1
      Makefile
  2. 27 3
      src/bsp.c
  3. 2 0
      src/bsp.h
  4. 4 4
      src/collection.h
  5. 1 0
      src/libc.h
  6. 2 2
      src/mempools.h
  7. 40 13
      src/render.c
  8. 16 2
      src/render.h
  9. 93 19
      src/texture.c
  10. 9 2
      src/texture.h

+ 1 - 1
Makefile

@@ -3,7 +3,7 @@ MAKEOPTS+=-r
 
 MAP ?= d1_trainstation_01
 VPKDIR ?= ~/.local/share/Steam/steamapps/common/Half-Life\ 2/hl2
-MAX_MAPS ?= 32
+MAX_MAPS ?= 99
 ARGS ?= -p $(VPKDIR)/hl2_textures_dir.vpk -p $(VPKDIR)/hl2_misc_dir.vpk -p $(VPKDIR)/hl2_pak_dir.vpk -d $(VPKDIR) -n $(MAX_MAPS)
 
 CFLAGS += -Wall -Wextra -D_GNU_SOURCE -Isrc/atto -fPIE

+ 27 - 3
src/bsp.c

@@ -358,12 +358,15 @@ static enum BSPLoadResult bspLoadModelLightmaps(struct LoadModelContext *ctx) {
 		} /* for y */
 	} /* fot all visible faces */
 
-	RTextureCreateParams upload;
+	RTextureUploadParams upload;
 	upload.width = atlas_context.width;
 	upload.height = atlas_context.height;
 	upload.format = RTexFormat_RGB565;
 	upload.pixels = pixels;
-	renderTextureCreate(&ctx->lightmap.texture, upload);
+	upload.generate_mipmaps = 1;
+	upload.type = RTexType_2D;
+	renderTextureInit(&ctx->lightmap.texture);
+	renderTextureUpload(&ctx->lightmap.texture, upload);
 	//ctx->lightmap.texture.min_filter = RTmF_Nearest;
 
 	/* pixels buffer is not needed anymore */
@@ -843,6 +846,27 @@ enum BSPLoadResult bspReadEntityTriggerChangelevel(struct BSPLoadModelContext *c
 	return BSPLoadResult_Success;
 }
 
+enum BSPLoadResult bspReadEntityWorldspawn(struct BSPLoadModelContext *ctx, struct TokenContext* tctx) {
+	(void)ctx;
+	struct EntityProp props[] = {
+		{"skyname", NULL, 0},
+	};
+
+	const enum BSPLoadResult result = bspReadEntityProps(tctx, props, COUNTOF(props));
+
+	if (result != BSPLoadResult_Success)
+		return result;
+
+	/* FIXME skyboxes cannot be cube maps ;_;
+	if (props[0].value_length > 0) {
+		const StringView sky = { props[0].value, props[0].value_length };
+		ctx->model->skybox = textureGetSkybox(sky, ctx->collection, ctx->persistent);
+	}
+	*/
+
+	return BSPLoadResult_Success;
+}
+
 enum BSPLoadResult bspReadEntityAndDumpProps(struct BSPLoadModelContext *ctx, struct TokenContext* tctx) {
 	(void)ctx;
 	return bspReadEntityProps(tctx, NULL, 0);
@@ -876,7 +900,7 @@ enum BSPLoadResult bspReadEntities(struct BSPLoadModelContext *ctx, const char *
 			}
 			LOAD_ENTITY("info_landmark", bspReadEntityInfoLandmark);
 			LOAD_ENTITY("trigger_changelevel", bspReadEntityTriggerChangelevel);
-			LOAD_ENTITY("worldspawn", bspReadEntityAndDumpProps);
+			LOAD_ENTITY("worldspawn", bspReadEntityWorldspawn);
 			LOAD_ENTITY("info_player_start", bspReadEntityAndDumpProps);
 			break;
 		case Token_Error:

+ 2 - 0
src/bsp.h

@@ -54,6 +54,8 @@ struct BSPModel {
 	RTexture lightmap;
 	RBuffer vbo, ibo;
 
+	const struct Texture *skybox;
+
 	struct BSPDrawSet detailed;
 	struct BSPDrawSet coarse;
 

+ 4 - 4
src/collection.h

@@ -3,7 +3,7 @@
 #include "mempools.h"
 #include <stddef.h>
 
-struct IFile {
+typedef struct IFile {
 	size_t size;
 	/* read size bytes into buffer
 	 * returns bytes read, or < 0 on error. error codes aren't specified */
@@ -11,7 +11,7 @@ struct IFile {
 	/* free any internal resources.
 	 * will not free memory associated with this structure itself */
 	void (*close)(struct IFile *file);
-};
+} IFile;
 
 enum CollectionOpenResult {
 	CollectionOpen_Success,
@@ -27,13 +27,13 @@ enum FileType {
 	File_Model
 };
 
-struct ICollection {
+typedef struct ICollection {
 	/* free any internal resources, but don't deallocate this structure itself */
 	void (*close)(struct ICollection *collection);
 	enum CollectionOpenResult (*open)(struct ICollection *collection,
 			const char *name, enum FileType type, struct IFile **out_file);
 	struct ICollection *next;
-};
+} ICollection;
 
 enum CollectionOpenResult collectionChainOpen(struct ICollection *collection,
 		const char *name, enum FileType type, struct IFile **out_file);

+ 1 - 0
src/libc.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <alloca.h>
 #include <stdint.h>
 #include <stdio.h> /* printf */
 #include <stdlib.h> /* malloc */

+ 2 - 2
src/mempools.h

@@ -2,10 +2,10 @@
 #include "common.h"
 #include <stddef.h>
 
-struct Stack {
+typedef struct Stack {
 	char *storage;
 	size_t size, cursor;
-};
+} Stack;
 
 static inline void *stackGetCursor(const struct Stack *stack) {
 	return stack->storage + stack->cursor;

+ 40 - 13
src/render.c

@@ -33,7 +33,6 @@
 #define GL_CALL(f) (f)
 #else
 #if 0
-#include <stdlib.h> /* abort() */
 static void a__GlPrintError(const char *message, int error) {
 	const char *errstr = "UNKNOWN";
 	switch (error) {
@@ -57,7 +56,7 @@ static void a__GlPrintError(const char *message, int error) {
 		const int glerror = glGetError(); \
 		if (glerror != GL_NO_ERROR) { \
 			a__GlPrintError(__FILE__ ":" RENDER__GL_STR(__LINE__) ": " #f " returned ", glerror); \
-			abort(); \
+			RENDER_ASSERT(!"GL error"); \
 		}
 #else
 #define RENDER_GL_GETERROR(f)
@@ -138,29 +137,54 @@ static GLint render_ShaderCreate(GLenum type, const char *sources[]) {
 	return shader;
 }
 
-void renderTextureCreate(RTexture *texture, RTextureCreateParams params) {
+void renderTextureUpload(RTexture *texture, RTextureUploadParams params) {
 	GLenum internal, format, type;
-	GL_CALL(glGenTextures(1, &texture->gl_name));
+
+	if (texture->gl_name == (GLuint)-1) {
+		GL_CALL(glGenTextures(1, &texture->gl_name));
+		texture->type_flags = 0;
+	}
 
 	switch (params.format) {
 		case RTexFormat_RGB565:
 			internal = format = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5; break;
+		default:
+			ATTO_ASSERT(!"Impossible texture format");
 	}
-	GL_CALL(glBindTexture(GL_TEXTURE_2D, texture->gl_name));
 
-	GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, internal, params.width, params.height, 0,
+	const GLenum binding = (params.type == RTexType_2D) ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP;
+	const GLint wrap = (params.type == RTexType_2D) ? GL_REPEAT : GL_CLAMP;
+
+
+	GL_CALL(glBindTexture(binding, texture->gl_name));
+
+	GLenum upload_binding = binding;
+	switch (params.type) {
+		case RTexType_2D: upload_binding = GL_TEXTURE_2D; break;
+		case RTexType_CubePX: upload_binding = GL_TEXTURE_CUBE_MAP_POSITIVE_X; break;
+		case RTexType_CubeNX: upload_binding = GL_TEXTURE_CUBE_MAP_NEGATIVE_X; break;
+		case RTexType_CubePY: upload_binding = GL_TEXTURE_CUBE_MAP_POSITIVE_Y; break;
+		case RTexType_CubeNY: upload_binding = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; break;
+		case RTexType_CubePZ: upload_binding = GL_TEXTURE_CUBE_MAP_POSITIVE_Z; break;
+		case RTexType_CubeNZ: upload_binding = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; break;
+	}
+
+	GL_CALL(glTexImage2D(upload_binding, 0, internal, params.width, params.height, 0,
 			format, type, params.pixels));
 
-	GL_CALL(glGenerateMipmap(GL_TEXTURE_2D));
+	if (params.generate_mipmaps)
+		GL_CALL(glGenerateMipmap(binding));
+
+	GL_CALL(glTexParameteri(binding, GL_TEXTURE_MIN_FILTER, params.generate_mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST));
+	GL_CALL(glTexParameteri(binding, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
 
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
-	GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
+	GL_CALL(glTexParameteri(binding, GL_TEXTURE_WRAP_S, wrap));
+	GL_CALL(glTexParameteri(binding, GL_TEXTURE_WRAP_T, wrap));
 
 	texture->width = params.width;
 	texture->height = params.height;
 	texture->format = params.format;
+	texture->type_flags |= params.type;
 }
 
 void renderBufferCreate(RBuffer *buffer, RBufferType type, int size, const void *data) {
@@ -422,12 +446,15 @@ int renderInit() {
 	}
 
 	struct Texture default_texture;
-	RTextureCreateParams params;
+	RTextureUploadParams params;
+	params.type = RTexType_2D;
 	params.format = RTexFormat_RGB565;
 	params.width = 2;
 	params.height = 2;
 	params.pixels = (uint16_t[]){0xffffu, 0, 0, 0xffffu};
-	renderTextureCreate(&default_texture.texture, params);
+	params.generate_mipmaps = 0;
+	renderTextureInit(&default_texture.texture);
+	renderTextureUpload(&default_texture.texture, params);
 	cachePutTexture("opensource/placeholder", &default_texture);
 
 	{

+ 16 - 2
src/render.h

@@ -40,19 +40,33 @@ typedef enum {
 	RTexFormat_RGB565
 } RTexFormat;
 
+typedef enum {
+	RTexType_2D = (1 << 0),
+	RTexType_CubePX = (1 << 1),
+	RTexType_CubeNX = (1 << 2),
+	RTexType_CubePY = (1 << 3),
+	RTexType_CubeNY = (1 << 4),
+	RTexType_CubePZ = (1 << 5),
+	RTexType_CubeNZ = (1 << 6),
+} RTexType;
+
 typedef struct RTexture {
 	int width, height;
 	RTexFormat format;
 	GLuint gl_name;
+	int type_flags;
 } RTexture;
 
 typedef struct {
+	RTexType type;
 	int width, height;
 	RTexFormat format;
 	const void *pixels;
-} RTextureCreateParams;
+	int generate_mipmaps;
+} RTextureUploadParams;
 
-void renderTextureCreate(RTexture *texture, RTextureCreateParams params);
+#define renderTextureInit(texture_ptr) do { (texture_ptr)->gl_name = (GLuint)-1; } while (0)
+void renderTextureUpload(RTexture *texture, RTextureUploadParams params);
 
 typedef struct {
 	GLuint gl_name;

+ 93 - 19
src/texture.c

@@ -90,8 +90,33 @@ static int vtfImageSize(enum VTFImageFormat fmt, int width, int height) {
 	return width * height * pixel_bits / 8;
 }
 
+static void textureUnpackDXTto565(uint8_t *src, uint16_t *dst, int width, int height, enum VTFImageFormat format) {
+	const struct DXTUnpackContext dxt_ctx = {
+		.width = width,
+		.height = height,
+		.packed = src,
+		.output = dst
+	};
+
+	if (format == VTFImage_DXT1)
+		dxt1Unpack(dxt_ctx);
+	else
+		dxt5Unpack(dxt_ctx);
+}
+
+static void textureUnpack888to565(uint8_t *src, uint16_t *dst, int width, int height) {
+	const int pixels = width * height;
+	for (int i = 0; i < pixels; ++i, src+=3) {
+		const int r = (src[0] >> 3) << 11;
+		const int g = (src[1] >> 2) << 5;
+		const int b = (src[2] >> 3);
+		dst[i] = r | g | b;
+	}
+}
+
 static uint16_t *textureUnpackToTemp(struct Stack *tmp, struct IFile *file, size_t cursor,
 		int width, int height, enum VTFImageFormat format) {
+
 	const int src_texture_size = vtfImageSize(format, width, height);
 	const int dst_texture_size = sizeof(uint16_t) * width * height;
 	void *dst_texture = stackAlloc(tmp, dst_texture_size);
@@ -104,28 +129,31 @@ static uint16_t *textureUnpackToTemp(struct Stack *tmp, struct IFile *file, size
 		PRINTF("Cannot allocate %d bytes for texture", src_texture_size);
 		return 0;
 	}
+
 	if (src_texture_size != (int)file->read(file, cursor, src_texture_size, src_texture)) {
 		PRINT("Cannot read texture data");
 		return 0;
 	}
 
-	const struct DXTUnpackContext dxt_ctx = {
-		.width = width,
-		.height = height,
-		.packed = src_texture,
-		.output = dst_texture
-	};
-	if (format == VTFImage_DXT1)
-		dxt1Unpack(dxt_ctx);
-	else
-		dxt5Unpack(dxt_ctx);
+	switch (format) {
+		case VTFImage_DXT1:
+		case VTFImage_DXT5:
+			textureUnpackDXTto565(src_texture, dst_texture, width, height, format);
+			break;
+		case VTFImage_BGR8:
+			textureUnpack888to565(src_texture, dst_texture, width, height);
+			break;
+		default:
+			PRINTF("Unsupported texture format %s", vtfFormatStr(format));
+			return 0;
+	}
 
 	stackFreeUpToPosition(tmp, src_texture);
 	return dst_texture;
 }
 
-static int textureUploadMipmap(struct Stack *tmp, struct IFile *file, size_t cursor,
-		const struct VTFHeader *hdr, int miplevel, RTexture *tex) {
+static int textureUploadMipmapType(struct Stack *tmp, struct IFile *file, size_t cursor,
+		const struct VTFHeader *hdr, int miplevel, RTexture *tex, RTexType tex_type) {
 	for (int mip = hdr->mipmap_count - 1; mip > miplevel; --mip) {
 		const unsigned int mip_width = hdr->width >> mip;
 		const unsigned int mip_height = hdr->height >> mip;
@@ -143,18 +171,21 @@ static int textureUploadMipmap(struct Stack *tmp, struct IFile *file, size_t cur
 		return 0;
 	}
 
-	const RTextureCreateParams params = {
+
+	const RTextureUploadParams params = {
+		.type = tex_type,
 		.width = hdr->width,
 		.height = hdr->height,
 		.format = RTexFormat_RGB565,
-		.pixels = dst_texture
+		.pixels = dst_texture,
+		.generate_mipmaps = 1
 	};
 
-	renderTextureCreate(tex, params);
+	renderTextureUpload(tex, params);
 	return 1;
 }
 
-static int textureLoad(struct IFile *file, Texture *tex, struct Stack *tmp) {
+static int textureLoad(struct IFile *file, Texture *tex, struct Stack *tmp, RTexType type) {
 	struct VTFHeader hdr;
 	size_t cursor = 0;
 	int retval = 0;
@@ -179,7 +210,7 @@ static int textureLoad(struct IFile *file, Texture *tex, struct Stack *tmp) {
 	//PRINTF("Texture: %dx%d, %s",
 	//	hdr.width, hdr.height, vtfFormatStr(hdr.hires_format));
 
-	if (hdr.hires_format != VTFImage_DXT1 && hdr.hires_format != VTFImage_DXT5) {
+	if (hdr.hires_format != VTFImage_DXT1 && hdr.hires_format != VTFImage_DXT5 && hdr.hires_format != VTFImage_BGR8) {
 		PRINTF("Not implemented texture format: %s", vtfFormatStr(hdr.hires_format));
 		return 0;
 	}
@@ -218,7 +249,7 @@ static int textureLoad(struct IFile *file, Texture *tex, struct Stack *tmp) {
 		hdr.lores_width, hdr.lores_height, vtfFormatStr(hdr.lores_format), hdr.mipmap_count, hdr.header_size);
 	*/
 
-	retval = textureUploadMipmap(tmp, file, cursor, &hdr, 0, &tex->texture);
+	retval = textureUploadMipmapType(tmp, file, cursor, &hdr, 0, &tex->texture, type);
 	stackFreeUpToPosition(tmp, pre_alloc_cursor);
 
 	return retval;
@@ -235,7 +266,8 @@ const Texture *textureGet(const char *name, struct ICollection *collection, stru
 	}
 
 	struct Texture localtex;
-	if (textureLoad(texfile, &localtex, tmp) == 0) {
+	renderTextureInit(&localtex.texture);
+	if (textureLoad(texfile, &localtex, tmp, RTexType_2D) == 0) {
 		PRINTF("Texture \"%s\" found, but could not be loaded", name);
 	} else {
 		cachePutTexture(name, &localtex);
@@ -245,3 +277,45 @@ const Texture *textureGet(const char *name, struct ICollection *collection, stru
 	texfile->close(texfile);
 	return tex ? tex : cacheGetTexture("opensource/placeholder");
 }
+
+static const struct {
+	const char *suffix;
+	RTexType type;
+} texture_skybox_faces[6] = {
+	{"rt", RTexType_CubePX},
+	{"bk", RTexType_CubeNY},
+	{"dn", RTexType_CubeNZ},
+	{"ft", RTexType_CubePY},
+	{"lf", RTexType_CubeNX},
+	{"up", RTexType_CubePZ}};
+
+const Texture *textureGetSkybox(StringView name, ICollection *coll, Stack *tmp) {
+	char *zname = alloca(name.length + 3 + 7);
+	memset(zname, 0, name.length + 3 + 7);
+	memcpy(zname, "skybox/", 7);
+	memcpy(zname + 7, name.str, name.length);
+	const Texture *tex = cacheGetTexture(zname); /* FIXME will collide with non-skybox textures with the same name */
+	if (tex) return tex;
+
+	Texture localtex;
+	renderTextureInit(&localtex.texture);
+	for (int i = 0; i < 6; ++i) {
+		memcpy(zname + name.length + 7, texture_skybox_faces[i].suffix, 2);
+		struct IFile *texfile;
+		if (CollectionOpen_Success != collectionChainOpen(coll, zname, File_Texture, &texfile)) {
+			/* FIXME partially loaded skybox will leak here */
+			PRINTF("Texture \"%s\" not found", zname);
+			return cacheGetTexture("opensource/placeholder");
+		}
+
+		if (textureLoad(texfile, &localtex, tmp, texture_skybox_faces[i].type) == 0) {
+			/* FIXME partially loaded skybox will leak here */
+			texfile->close(texfile);
+			PRINTF("Texture \"%s\" found, but could not be loaded", zname);
+			return cacheGetTexture("opensource/placeholder");
+		}
+	}
+
+	cachePutTexture(zname, &localtex);
+	return cacheGetTexture(zname);
+}

+ 9 - 2
src/texture.h

@@ -1,8 +1,8 @@
 #pragma once
 #include "render.h"
 
-struct ICollection;
-struct Stack;
+typedef struct ICollection ICollection;
+typedef struct Stack Stack;
 
 typedef struct Texture {
 	RTexture texture;
@@ -10,3 +10,10 @@ typedef struct Texture {
 } Texture;
 
 const Texture *textureGet(const char *name, struct ICollection *collection, struct Stack *tmp);
+
+typedef struct {
+	const char *str;
+	int length;
+} StringView;
+
+const Texture *textureGetSkybox(StringView name, ICollection *coll, Stack *tmp);