Explorar o código

initial support for skyboxes, #25

Ivan Avdeev %!s(int64=6) %!d(string=hai) anos
pai
achega
2a47e6beb1
Modificáronse 8 ficheiros con 193 adicións e 123 borrados
  1. 1 0
      Makefile
  2. 19 63
      src/OpenSource.c
  3. 3 0
      src/bsp.c
  4. 45 0
      src/camera.c
  5. 17 0
      src/camera.h
  6. 87 57
      src/render.c
  7. 18 2
      src/render.h
  8. 3 1
      src/texture.c

+ 1 - 0
Makefile

@@ -73,6 +73,7 @@ SOURCES += \
 	src/bsp.c \
 	src/atlas.c \
 	src/filemap.c \
+	src/camera.c \
 	src/collection.c \
 	src/vmfparser.c \
 	src/material.c \

+ 19 - 63
src/OpenSource.c

@@ -5,6 +5,7 @@
 #include "common.h"
 #include "texture.h"
 #include "profiler.h"
+#include "camera.h"
 
 #include "atto/app.h"
 #include "atto/math.h"
@@ -24,55 +25,6 @@ static struct Stack stack_persistent = {
 	.cursor = 0
 };
 
-struct SimpleCamera {
-	struct AVec3f pos, dir, up;
-	struct AMat3f axes;
-	struct AMat4f projection;
-	struct AMat4f view_projection;
-};
-
-static void simplecamRecalc(struct SimpleCamera *cam) {
-	cam->dir = aVec3fNormalize(cam->dir);
-	const struct AVec3f
-			z = aVec3fNeg(cam->dir),
-			x = aVec3fNormalize(aVec3fCross(cam->up, z)),
-			y = aVec3fCross(z, x);
-	cam->axes = aMat3fv(x, y, z);
-	const struct AMat3f axes_inv = aMat3fTranspose(cam->axes);
-	cam->view_projection = aMat4fMul(cam->projection,
-		aMat4f3(axes_inv, aVec3fMulMat(axes_inv, aVec3fNeg(cam->pos))));
-}
-
-static void simplecamProjection(struct SimpleCamera *cam, float znear, float zfar, float horizfov, float aspect) {
-	const float w = 2.f * znear * tanf(horizfov / 2.f), h = w / aspect;
-	//aAppDebugPrintf("%f %f %f %f -> %f %f", near, far, horizfov, aspect, w, h);
-	cam->projection = aMat4fPerspective(znear, zfar, w, h);
-}
-
-static void simplecamLookAt(struct SimpleCamera *cam, struct AVec3f pos, struct AVec3f at, struct AVec3f up) {
-	cam->pos = pos;
-	cam->dir = aVec3fNormalize(aVec3fSub(at, pos));
-	cam->up = up;
-	simplecamRecalc(cam);
-}
-
-static void simplecamMove(struct SimpleCamera *cam, struct AVec3f v) {
-	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.X, v.x));
-	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.Y, v.y));
-	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.Z, v.z));
-}
-
-static void simplecamRotateYaw(struct SimpleCamera *cam, float yaw) {
-	const struct AMat3f rot = aMat3fRotateAxis(cam->up, yaw);
-	cam->dir = aVec3fMulMat(rot, cam->dir);
-}
-
-static void simplecamRotatePitch(struct SimpleCamera *cam, float pitch) {
-	/* TODO limit pitch */
-	const struct AMat3f rot = aMat3fRotateAxis(cam->axes.X, pitch);
-	cam->dir = aVec3fMulMat(rot, cam->dir);
-}
-
 struct Map {
 	char *name;
 	int loaded;
@@ -82,7 +34,7 @@ struct Map {
 };
 
 static struct {
-	struct SimpleCamera camera;
+	struct Camera camera;
 	int forward, right, run;
 	struct AVec3f center;
 	float R;
@@ -204,15 +156,13 @@ static void opensrcInit(const char *map, int max_maps) {
 	if (BSPLoadResult_Success != loadMap(g.maps_begin, g.collection_chain))
 		aAppTerminate(-2);
 
-	PRINTF("Maps loaded: %d", g.maps_count);
-
 	g.center = aVec3fMulf(aVec3fAdd(g.maps_begin->model.aabb.min, g.maps_begin->model.aabb.max), .5f);
 	g.R = aVec3fLength(aVec3fSub(g.maps_begin->model.aabb.max, g.maps_begin->model.aabb.min)) * .5f;
 
 	aAppDebugPrintf("Center %f, %f, %f, R~=%f", g.center.x, g.center.y, g.center.z, g.R);
 
 	const float t = 0;
-	simplecamLookAt(&g.camera,
+	cameraLookAt(&g.camera,
 			aVec3fAdd(g.center, aVec3fMulf(aVec3f(cosf(t*.5f), sinf(t*.5f), .25f), g.R*.5f)),
 			g.center, aVec3f(0.f, 0.f, 1.f));
 }
@@ -221,18 +171,18 @@ static void opensrcResize(ATimeUs timestamp, unsigned int old_w, unsigned int ol
 	(void)(timestamp); (void)(old_w); (void)(old_h);
 	renderResize(a_app_state->width, a_app_state->height);
 
-	simplecamProjection(&g.camera, 1.f, g.R * 10.f, 3.1415926f/2.f, (float)a_app_state->width / (float)a_app_state->height);
-	simplecamRecalc(&g.camera);
+	cameraProjection(&g.camera, 1.f, g.R * 10.f, 3.1415926f/2.f, (float)a_app_state->width / (float)a_app_state->height);
+	cameraRecompute(&g.camera);
 }
 
 static void opensrcPaint(ATimeUs timestamp, float dt) {
 	(void)(timestamp); (void)(dt);
 
 	float move = dt * (g.run?3000.f:300.f);
-	simplecamMove(&g.camera, aVec3f(g.right * move, 0.f, -g.forward * move));
-	simplecamRecalc(&g.camera);
+	cameraMove(&g.camera, aVec3f(g.right * move, 0.f, -g.forward * move));
+	cameraRecompute(&g.camera);
 
-	renderClear();
+	renderBegin();
 
 	int triangles = 0;
 	int can_load_map = 1;
@@ -250,14 +200,20 @@ static void opensrcPaint(ATimeUs timestamp, float dt) {
 			continue;
 		}
 
-		const struct AMat4f mvp = aMat4fMul(g.camera.view_projection, aMat4fTranslation(map->offset));
+		const RDrawParams params = {
+			.camera = &g.camera,
+			.translation = map->offset,
+			.lmn = g.lmn
+		};
 
-		renderModelDraw(&mvp, aVec3fSub(g.camera.pos, map->offset), g.lmn, &map->model);
+		renderModelDraw(&params, &map->model);
 
 		for (int i = 0; i < map->model.detailed.draws_count; ++i)
 			triangles += map->model.detailed.draws[i].count / 3;
 	}
 
+	renderEnd(&g.camera);
+
 	if (profilerFrame(&stack_temp)) {
 		PRINTF("Total triangles: %d", triangles);
 	}
@@ -288,9 +244,9 @@ static void opensrcPointer(ATimeUs timestamp, int dx, int dy, unsigned int btndi
 	(void)(timestamp); (void)(dx); (void)(dy); (void)(btndiff);
 	//printf("PTR %u %d %d %x\n", timestamp, dx, dy, btndiff);
 	if (a_app_state->grabbed) {
-		simplecamRotatePitch(&g.camera, dy * -4e-3f);
-		simplecamRotateYaw(&g.camera, dx * -4e-3f);
-		simplecamRecalc(&g.camera);
+		cameraRotatePitch(&g.camera, dy * -4e-3f);
+		cameraRotateYaw(&g.camera, dx * -4e-3f);
+		cameraRecompute(&g.camera);
 	} else if (btndiff)
 		aAppGrabInput(1);
 }

+ 3 - 0
src/bsp.c

@@ -359,6 +359,7 @@ static enum BSPLoadResult bspLoadModelLightmaps(struct LoadModelContext *ctx) {
 	upload.pixels = pixels;
 	upload.generate_mipmaps = 0;
 	upload.type = RTexType_2D;
+	upload.wrap = RTexWrap_Clamp;
 	renderTextureInit(&ctx->lightmap.texture);
 	renderTextureUpload(&ctx->lightmap.texture, upload);
 	//ctx->lightmap.texture.min_filter = RTmF_Nearest;
@@ -748,6 +749,8 @@ static const struct {
 	{"dn", BSPSkyboxDir_DN}};
 
 static void bspLoadSkybox(StringView name, ICollection *coll, Stack *tmp, struct BSPModel *model) {
+	PRINTF("Loading skybox %.*s", name.length, name.str);
+
 	char *zname = alloca(name.length + 3 + 7);
 	memset(zname, 0, name.length + 3 + 7);
 	memcpy(zname, "skybox/", 7);

+ 45 - 0
src/camera.c

@@ -0,0 +1,45 @@
+#include "camera.h"
+
+void cameraRecompute(struct Camera *cam) {
+	cam->dir = aVec3fNormalize(cam->dir);
+	const struct AVec3f
+			z = aVec3fNeg(cam->dir),
+			x = aVec3fNormalize(aVec3fCross(cam->up, z)),
+			y = aVec3fCross(z, x);
+	cam->axes = aMat3fv(x, y, z);
+	cam->orientation = aMat3fTranspose(cam->axes);
+	cam->view_projection = aMat4fMul(cam->projection,
+		aMat4f3(cam->orientation, aVec3fMulMat(cam->orientation, aVec3fNeg(cam->pos))));
+}
+
+void cameraProjection(struct Camera *cam, float znear, float zfar, float horizfov, float aspect) {
+	const float w = 2.f * znear * tanf(horizfov / 2.f), h = w / aspect;
+	cam->z_far = zfar;
+	cam->z_near = znear;
+	//aAppDebugPrintf("%f %f %f %f -> %f %f", near, far, horizfov, aspect, w, h);
+	cam->projection = aMat4fPerspective(znear, zfar, w, h);
+}
+
+void cameraLookAt(struct Camera *cam, struct AVec3f pos, struct AVec3f at, struct AVec3f up) {
+	cam->pos = pos;
+	cam->dir = aVec3fNormalize(aVec3fSub(at, pos));
+	cam->up = up;
+	cameraRecompute(cam);
+}
+
+void cameraMove(struct Camera *cam, struct AVec3f v) {
+	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.X, v.x));
+	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.Y, v.y));
+	cam->pos = aVec3fAdd(cam->pos, aVec3fMulf(cam->axes.Z, v.z));
+}
+
+void cameraRotateYaw(struct Camera *cam, float yaw) {
+	const struct AMat3f rot = aMat3fRotateAxis(cam->up, yaw);
+	cam->dir = aVec3fMulMat(rot, cam->dir);
+}
+
+void cameraRotatePitch(struct Camera *cam, float pitch) {
+	/* TODO limit pitch */
+	const struct AMat3f rot = aMat3fRotateAxis(cam->axes.X, pitch);
+	cam->dir = aVec3fMulMat(rot, cam->dir);
+}

+ 17 - 0
src/camera.h

@@ -0,0 +1,17 @@
+#pragma once
+#include "atto/math.h"
+
+struct Camera {
+	struct AMat4f projection;
+	struct AMat4f view_projection;
+	struct AMat3f axes, orientation;
+	struct AVec3f pos, dir, up;
+	float z_near, z_far;
+};
+
+void cameraRecompute(struct Camera *cam);
+void cameraProjection(struct Camera *cam, float znear, float zfar, float horizfov, float aspect);
+void cameraLookAt(struct Camera *cam, struct AVec3f pos, struct AVec3f at, struct AVec3f up);
+void cameraMove(struct Camera *cam, struct AVec3f v);
+void cameraRotateYaw(struct Camera *cam, float yaw);
+void cameraRotatePitch(struct Camera *cam, float pitch);

+ 87 - 57
src/render.c

@@ -4,6 +4,8 @@
 #include "cache.h"
 #include "common.h"
 #include "profiler.h"
+#include "camera.h"
+
 #include "atto/app.h"
 #include "atto/platform.h"
 
@@ -180,8 +182,8 @@ void renderTextureUpload(RTexture *texture, RTextureUploadParams params) {
 	}
 
 	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;
-
+	const GLint wrap = (params.type == RTexType_2D && params.wrap == RTexWrap_Repeat)
+		? GL_REPEAT : GL_CLAMP;
 
 	GL_CALL(glBindTexture(binding, texture->gl_name));
 
@@ -264,6 +266,7 @@ RENDER_LIST_ATTRIBS
 #define RENDER_LIST_UNIFORMS \
 	RENDER_DECLARE_UNIFORM(mvp) \
 	RENDER_DECLARE_UNIFORM(lmn) \
+	RENDER_DECLARE_UNIFORM(far) \
 	RENDER_DECLARE_UNIFORM(lightmap) \
 	RENDER_DECLARE_UNIFORM(tex0) \
 	RENDER_DECLARE_UNIFORM(tex1) \
@@ -273,7 +276,6 @@ RENDER_LIST_ATTRIBS
 	RENDER_DECLARE_UNIFORM(tex3) \
 	RENDER_DECLARE_UNIFORM(tex4) \
 	RENDER_DECLARE_UNIFORM(tex5) \
-	RENDER_DECLARE_UNIFORM(cam_pos) \
 
 static const RUniform uniforms[] = {
 #define RENDER_DECLARE_UNIFORM(n) {"u_" # n},
@@ -361,14 +363,14 @@ static RProgram programs[Program_COUNT] = {
 		"varying float texid;\n",
 		/* vertex */
 		"attribute vec3 a_vertex;\n"
-		"uniform vec3 u_cam_pos;\n"
 		"attribute vec2 a_tex_uv;\n"
 		"attribute vec2 a_lightmap_uv;\n"
 		"uniform mat4 u_mvp;\n"
+		"uniform float u_far;\n"
 		"void main() {\n"
 			"v_uv = a_tex_uv;\n"
 			"texid = a_lightmap_uv.x;\n"
-			"gl_Position = u_mvp * vec4(u_cam_pos + 100000. * a_vertex, 1.);\n"
+			"gl_Position = u_mvp * vec4(u_far * .5 * a_vertex, 1.);\n"
 		"}\n",
 		/* fragment */
 		"uniform sampler2D u_tex0, u_tex1, u_tex2, u_tex3, u_tex4, u_tex5;\n"
@@ -384,47 +386,47 @@ static RProgram programs[Program_COUNT] = {
 };
 
 static struct BSPModelVertex box[] = {
-	{{ 1.f, -1.f, -1.f}, {0.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{ 1.f, -1.f, -1.f}, {0.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
 	{{ 1.f,  1.f, -1.f}, {0.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
-	{{ 1.f,  1.f,  1.f}, {0.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{ 1.f,  1.f,  1.f}, {0.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{ 1.f,  1.f,  1.f}, {0.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{ 1.f,  1.f,  1.f}, {0.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
 	{{ 1.f, -1.f,  1.f}, {0.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
-	{{ 1.f, -1.f, -1.f}, {0.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{ 1.f, -1.f, -1.f}, {0.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+
+	{{ 1.f,  1.f,  1.f}, {2.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{ 1.f,  1.f, -1.f}, {2.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{-1.f,  1.f, -1.f}, {2.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{-1.f,  1.f, -1.f}, {2.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{-1.f,  1.f,  1.f}, {2.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{ 1.f,  1.f,  1.f}, {2.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+
+	{{ 1.f, -1.f, -1.f}, {3.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{ 1.f, -1.f,  1.f}, {3.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f,  1.f}, {3.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f,  1.f}, {3.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f, -1.f}, {3.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{ 1.f, -1.f, -1.f}, {3.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
 
 	{{-1.f, -1.f,  1.f}, {1.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-	{{-1.f,  1.f,  1.f}, {1.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{-1.f,  1.f,  1.f}, {1.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
 	{{-1.f,  1.f, -1.f}, {1.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
 	{{-1.f,  1.f, -1.f}, {1.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f, -1.f, -1.f}, {1.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f, -1.f}, {1.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
 	{{-1.f, -1.f,  1.f}, {1.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
 
-	{{ 1.f, -1.f,  1.f}, {2.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-	{{ 1.f,  1.f,  1.f}, {2.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
-	{{-1.f,  1.f,  1.f}, {2.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f,  1.f,  1.f}, {2.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f, -1.f,  1.f}, {2.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
-	{{ 1.f, -1.f,  1.f}, {2.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-
-	{{-1.f, -1.f, -1.f}, {3.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-	{{-1.f,  1.f, -1.f}, {3.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
-	{{ 1.f,  1.f, -1.f}, {3.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{ 1.f,  1.f, -1.f}, {3.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{ 1.f, -1.f, -1.f}, {3.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
-	{{-1.f, -1.f, -1.f}, {3.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-
-	{{ 1.f,  1.f,  1.f}, {4.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-	{{ 1.f,  1.f, -1.f}, {4.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
-	{{-1.f,  1.f, -1.f}, {4.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f,  1.f, -1.f}, {4.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f,  1.f,  1.f}, {4.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
-	{{ 1.f,  1.f,  1.f}, {4.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-
-	{{ 1.f, -1.f, -1.f}, {5.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
-	{{ 1.f, -1.f,  1.f}, {5.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
-	{{-1.f, -1.f,  1.f}, {5.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f, -1.f,  1.f}, {5.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
-	{{-1.f, -1.f, -1.f}, {5.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
-	{{ 1.f, -1.f, -1.f}, {5.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{ 1.f, -1.f,  1.f}, {4.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{ 1.f,  1.f,  1.f}, {4.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{-1.f,  1.f,  1.f}, {4.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{-1.f,  1.f,  1.f}, {4.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f,  1.f}, {4.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{ 1.f, -1.f,  1.f}, {4.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+
+	{{-1.f, -1.f, -1.f}, {5.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
+	{{-1.f,  1.f, -1.f}, {5.f, 0.f}, {1.f, 1.f}, {0, 0, 0}},
+	{{ 1.f,  1.f, -1.f}, {5.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{ 1.f,  1.f, -1.f}, {5.f, 0.f}, {0.f, 1.f}, {0, 0, 0}},
+	{{ 1.f, -1.f, -1.f}, {5.f, 0.f}, {0.f, 0.f}, {0, 0, 0}},
+	{{-1.f, -1.f, -1.f}, {5.f, 0.f}, {1.f, 0.f}, {0, 0, 0}},
 };
 
 static RBuffer box_buffer;
@@ -436,8 +438,13 @@ static struct {
 	struct {
 		const float *mvp;
 		float lmn;
-		struct AVec3f campos;
+		float far;
 	} uniforms;
+
+	struct {
+		float distance;
+		const struct BSPModel *model;
+	} closest_map;
 } r;
 
 static void renderApplyAttribs(const RAttrib *attribs, const RBuffer *buffer, unsigned int vbo_offset) {
@@ -474,7 +481,7 @@ static int render_ProgramUse(RProgram *prog) {
 
 	GL_CALL(glUniformMatrix4fv(prog->uniform_locations[RUniformKind_mvp], 1, GL_FALSE, r.uniforms.mvp));
 	GL_CALL(glUniform1f(prog->uniform_locations[RUniformKind_lmn], r.uniforms.lmn));
-	GL_CALL(glUniform3f(prog->uniform_locations[RUniformKind_cam_pos], r.uniforms.campos.x, r.uniforms.campos.y, r.uniforms.campos.z));
+	GL_CALL(glUniform1f(prog->uniform_locations[RUniformKind_far], r.uniforms.far));
 
 	r.current_program = prog;
 	r.current_tex0 = NULL;
@@ -568,6 +575,7 @@ int renderInit() {
 	params.height = 2;
 	params.pixels = (uint16_t[]){0xffffu, 0, 0, 0xffffu};
 	params.generate_mipmaps = 0;
+	params.wrap = RTexWrap_Clamp;
 	renderTextureInit(&default_texture.texture);
 	renderTextureUpload(&default_texture.texture, params);
 	cachePutTexture("opensource/placeholder", &default_texture);
@@ -637,15 +645,24 @@ static void renderDrawSet(const struct BSPModel *model, const struct BSPDrawSet
 	}
 }
 
-static void renderBindTexture(const RTexture *texture, int slot) {
+static void renderBindTexture(const RTexture *texture, int slot, int norepeat) {
 	GL_CALL(glActiveTexture(GL_TEXTURE0 + slot));
 	GL_CALL(glBindTexture(GL_TEXTURE_2D, texture->gl_name));
+	if (norepeat) {
+		const GLuint wrap = GL_CLAMP_TO_EDGE;
+		GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap));
+		GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap));
+	}
 }
 
-static void renderSkybox(const struct BSPModel *model) {
+static void renderSkybox(const struct Camera *camera, const struct BSPModel *model) {
+	const struct AMat4f op = aMat4fMul(camera->projection, aMat4f3(camera->orientation, aVec3ff(0)));
+	r.uniforms.mvp = &op.X.x;
+
 	render_ProgramUse(programs + Program_Skybox);
-	for (int i = 0; i < 6; ++i)
-		renderBindTexture(&model->skybox[i]->texture, 1+i);
+	for (int i = 0; i < BSPSkyboxDir_COUNT; ++i)
+		renderBindTexture(&model->skybox[i]->texture, 1+i, 1);
+
 	renderApplyAttribs(attribs, &box_buffer, 0);
 	GL_CALL(glDisable(GL_CULL_FACE));
 	GL_CALL(glDrawArrays(GL_TRIANGLES, 0, COUNTOF(box)));
@@ -655,42 +672,55 @@ static void renderSkybox(const struct BSPModel *model) {
 static float aMaxf(float a, float b) { return a > b ? a : b; }
 //static float aMinf(float a, float b) { return a < b ? a : b; }
 
-void renderModelDraw(const struct AMat4f *mvp, struct AVec3f camera_position, float lmn, const struct BSPModel *model) {
+void renderModelDraw(const RDrawParams *params, const struct BSPModel *model) {
 	if (!model->detailed.draws_count) return;
 
+	const struct AMat4f mvp = aMat4fMul(params->camera->view_projection,
+			aMat4fTranslation(params->translation));
+
 	GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model->ibo.gl_name));
-	renderBindTexture(&model->lightmap, 0);
+	renderBindTexture(&model->lightmap, 0, 0);
 	GL_CALL(glActiveTexture(GL_TEXTURE0 + 1));
 
+	const struct AVec3f rel_pos = aVec3fSub(params->camera->pos, params->translation);
+
 	r.current_program = NULL;
-	r.uniforms.mvp = &mvp->X.x;
-	r.uniforms.lmn = lmn;
-	r.uniforms.campos = camera_position;
+	r.uniforms.mvp = &mvp.X.x;
+	r.uniforms.lmn = params->lmn;
+	r.uniforms.far = params->camera->z_far;
 
 	const float distance =
 		aMaxf(aMaxf(
-			aMaxf(camera_position.x - model->aabb.max.x, model->aabb.min.x - camera_position.x),
-			aMaxf(camera_position.y - model->aabb.max.y, model->aabb.min.y - camera_position.y)),
-			aMaxf(camera_position.z - model->aabb.max.z, model->aabb.min.z - camera_position.z));
+			aMaxf(rel_pos.x - model->aabb.max.x, model->aabb.min.x - rel_pos.x),
+			aMaxf(rel_pos.y - model->aabb.max.y, model->aabb.min.y - rel_pos.y)),
+			aMaxf(rel_pos.z - model->aabb.max.z, model->aabb.min.z - rel_pos.z));
 
 	/*
 	PRINTF("%f %f %f -> %f",
-			camera_position.x, camera_position.y, camera_position.z, distance);
+			rel_pos.x, rel_pos.y, rel_pos.z, distance);
 	*/
 
+	if (distance < r.closest_map.distance) {
+		r.closest_map.distance = distance;
+		r.closest_map.model = model;
+	}
+
 	if (distance < 5000.f)
 		renderDrawSet(model, &model->detailed);
 	else
 		renderDrawSet(model, &model->coarse);
-
-	renderSkybox(model);
 }
 
 void renderResize(int w, int h) {
 	glViewport(0, 0, w, h);
 }
 
-void renderClear() {
-	glClearColor(.5f,.4f,.2f,0);
+void renderBegin() {
+	glClearColor(0.f,1.f,0.f,0);
 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	r.closest_map.distance = 1e9f;
+}
+
+void renderEnd(const struct Camera *camera) {
+	renderSkybox(camera, r.closest_map.model);
 }

+ 18 - 2
src/render.h

@@ -15,6 +15,11 @@ typedef enum {
 	RTexType_CubeNZ = (1 << 6),
 } RTexType;
 
+typedef enum {
+	RTexWrap_Repeat,
+	RTexWrap_Clamp
+} RTexWrap;
+
 typedef struct {
 	int width, height;
 	RTexFormat format;
@@ -28,6 +33,7 @@ typedef struct {
 	RTexFormat format;
 	const void *pixels;
 	int generate_mipmaps;
+	RTexWrap wrap;
 } RTextureUploadParams;
 
 #define renderTextureInit(texture_ptr) do { (texture_ptr)->gl_name = -1; } while (0)
@@ -49,6 +55,16 @@ void renderResize(int w, int h);
 void renderBufferCreate(RBuffer *buffer, RBufferType type, int size, const void *data);
 
 struct BSPModel;
+struct Camera;
+
+void renderBegin();
+
+typedef struct {
+	const struct Camera *camera;
+	struct AVec3f translation;
+	float lmn;
+} RDrawParams;
+
+void renderModelDraw(const RDrawParams *params, const struct BSPModel *model);
 
-void renderClear();
-void renderModelDraw(const struct AMat4f *mvp, struct AVec3f camera_position, float lmn, const struct BSPModel *model);
+void renderEnd(const struct Camera *camera);

+ 3 - 1
src/texture.c

@@ -178,7 +178,8 @@ static int textureUploadMipmapType(struct Stack *tmp, struct IFile *file, size_t
 		.height = hdr->height,
 		.format = RTexFormat_RGB565,
 		.pixels = dst_texture,
-		.generate_mipmaps = 1
+		.generate_mipmaps = 1,
+		.wrap =  RTexWrap_Repeat
 	};
 
 	renderTextureUpload(tex, params);
@@ -221,6 +222,7 @@ static int textureLoad(struct IFile *file, Texture *tex, struct Stack *tmp, RTex
 	void *pre_alloc_cursor = stackGetCursor(tmp);
 	if (hdr.lores_format != VTFImage_DXT1 && hdr.lores_format != VTFImage_DXT5) {
 		PRINTF("Not implemented lores texture format: %s", vtfFormatStr(hdr.lores_format));
+		tex->avg_color = aVec3ff(1.f);
 	} else {
 		uint16_t *pixels = textureUnpackToTemp(tmp, file, cursor, hdr.lores_width, hdr.lores_height, hdr.lores_format);