From: havoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Sun, 24 Aug 2003 23:00:26 +0000 (+0000)
Subject: the quadratic spline patches in quake3 maps now work (in english: curves!)
X-Git-Tag: xonotic-v0.1.0preview~6417
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=1c62b35652c52a3fc8f469306c2521bb7502dd7f;p=xonotic%2Fdarkplaces.git

the quadratic spline patches in quake3 maps now work (in english: curves!)


git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3414 d7cf8633-e32d-0410-b094-e92efae38249
---

diff --git a/curves.c b/curves.c
new file mode 100644
index 00000000..5b192434
--- /dev/null
+++ b/curves.c
@@ -0,0 +1,161 @@
+
+// this code written by Forest Hale, on 2003-08-23, and placed into public domain
+// this code deals with quadratic splines (minimum of 3 points), the same kind used in Quake3 maps.
+
+// LordHavoc's rant on misuse of the name 'bezier': many people seem to think that bezier is a generic term for splines, but it is not, it is a term for a specific type of spline (minimum of 4 control points, cubic spline).
+
+#include "curves.h"
+
+void QuadraticSplineSubdivideFloat(int inpoints, int components, const float *in, int instride, float *out, int outstride)
+{
+	int s;
+	// the input (control points) is read as a stream of points, and buffered
+	// by the cpprev, cpcurr, and cpnext variables (to allow subdivision in
+	// overlapping memory buffers, even subdivision in-place with pre-spaced
+	// control points in the buffer)
+	// the output (resulting curve) is written as a stream of points
+	// this subdivision is meant to be repeated until the desired flatness
+	// level is reached
+	if (components == 1 && instride == (int)sizeof(float) && outstride == instride)
+	{
+		// simple case, single component and no special stride
+		float cpprev0 = 0, cpcurr0 = 0, cpnext0;
+		cpnext0 = *in++;
+		for (s = 0;s < inpoints - 1;s++)
+		{
+			cpprev0 = cpcurr0;
+			cpcurr0 = cpnext0;
+			if (s < inpoints - 1)
+				cpnext0 = *in++;
+			if (s > 0)
+			{
+				// 50% flattened control point
+				// cp1 = average(cp1, average(cp0, cp2));
+				*out++ = (cpcurr0 + (cpprev0 + cpnext0) * 0.5f) * 0.5f;
+			}
+			else
+			{
+				// copy the control point directly
+				*out++ = cpcurr0;
+			}
+			// midpoint
+			// mid = average(cp0, cp1);
+			*out++ = (cpcurr0 + cpnext0) * 0.5f;
+		}
+		// copy the final control point
+		*out++ = cpnext0;
+	}
+	else
+	{
+		// multiple components or stride is used (complex case)
+		int c;
+		float cpprev[4], cpcurr[4], cpnext[4];
+		// check if there are too many components for the buffers
+		if (components > 1)
+		{
+			// more components can be handled, but slowly, by calling self multiple times...
+			for (c = 0;c < components;c++, in++, out++)
+				QuadraticSplineSubdivideFloat(inpoints, 1, in, instride, out, outstride);
+			return;
+		}
+		for (c = 0;c < components;c++)
+			cpnext[c] = in[c];
+		(unsigned char *)in += instride;
+		for (s = 0;s < inpoints - 1;s++)
+		{
+			for (c = 0;c < components;c++)
+				cpprev[c] = cpcurr[c];
+			for (c = 0;c < components;c++)
+				cpcurr[c] = cpnext[c];
+			for (c = 0;c < components;c++)
+				cpnext[c] = in[c];
+			(unsigned char *)in += instride;
+			// the end points are copied as-is
+			if (s > 0)
+			{
+				// 50% flattened control point
+				// cp1 = average(cp1, average(cp0, cp2));
+				for (c = 0;c < components;c++)
+					out[c] = (cpcurr[c] + (cpprev[c] + cpnext[c]) * 0.5f) * 0.5f;
+			}
+			else
+			{
+				// copy the control point directly
+				for (c = 0;c < components;c++)
+					out[c] = cpcurr[c];
+			}
+			(unsigned char *)out += outstride;
+			// midpoint
+			// mid = average(cp0, cp1);
+			for (c = 0;c < components;c++)
+				out[c] = (cpcurr[c] + cpnext[c]) * 0.5f;
+			(unsigned char *)out += outstride;
+		}
+		// copy the final control point
+		for (c = 0;c < components;c++)
+			out[c] = cpnext[c];
+		//(unsigned char *)out += outstride;
+	}
+}
+
+// note: out must have enough room!
+// (see finalwidth/finalheight calcs below)
+void QuadraticSplinePatchSubdivideFloatBuffer(int cpwidth, int cpheight, int xlevel, int ylevel, int components, const float *in, float *out)
+{
+	int finalwidth, finalheight, xstep, ystep, x, y, c;
+	float *o;
+
+	// error out on various bogus conditions
+	if (xlevel < 0 || ylevel < 0 || xlevel > 16 || ylevel > 16 || cpwidth < 3 || cpheight < 3)
+		return;
+
+	xstep = 1 << xlevel;
+	ystep = 1 << ylevel;
+	finalwidth = (cpwidth - 1) * xstep + 1;
+	finalheight = (cpheight - 1) * ystep + 1;
+
+	for (y = 0;y < finalheight;y++)
+		for (x = 0;x < finalwidth;x++)
+			for (c = 0, o = out + (y * finalwidth + x) * components;c < components;c++)
+				o[c] = 0;
+
+	if (xlevel == 1 && ylevel == 0)
+	{
+		for (y = 0;y < finalheight;y++)
+			QuadraticSplineSubdivideFloat(cpwidth, components, in + y * cpwidth * components, sizeof(float) * components, out + y * finalwidth * components, sizeof(float) * components);
+		return;
+	}
+	if (xlevel == 0 && ylevel == 1)
+	{
+		for (x = 0;x < finalwidth;x++)
+			QuadraticSplineSubdivideFloat(cpheight, components, in + x * components, sizeof(float) * cpwidth * components, out + x * components, sizeof(float) * finalwidth * components);
+		return;
+	}
+
+	// copy control points into correct positions in destination buffer
+	for (y = 0;y < finalheight;y += ystep)
+		for (x = 0;x < finalwidth;x += xstep)
+			for (c = 0, o = out + (y * finalwidth + x) * components;c < components;c++)
+				o[c] = *in++;
+
+	// subdivide in place in the destination buffer
+	while (xstep > 1 || ystep > 1)
+	{
+		if (xstep > 1)
+		{
+			xstep >>= 1;
+			for (y = 0;y < finalheight;y += ystep)
+				QuadraticSplineSubdivideFloat(cpwidth, components, out + y * finalwidth * components, sizeof(float) * xstep * 2 * components, out + y * finalwidth * components, sizeof(float) * xstep * components);
+			cpwidth = (cpwidth - 1) * 2 + 1;
+		}
+		if (ystep > 1)
+		{
+			ystep >>= 1;
+			for (x = 0;x < finalwidth;x += xstep)
+				QuadraticSplineSubdivideFloat(cpheight, components, out + x * components, sizeof(float) * ystep * 2 * finalwidth * components, out + x * components, sizeof(float) * ystep * finalwidth * components);
+			cpheight = (cpheight - 1) * 2 + 1;
+		}
+	}
+}
+
+
diff --git a/curves.h b/curves.h
new file mode 100644
index 00000000..72567dd3
--- /dev/null
+++ b/curves.h
@@ -0,0 +1,9 @@
+
+#ifndef CURVES_H
+#define CURVES_H
+
+void QuadraticSplineSubdivideFloat(int inpoints, int components, const float *in, int instride, float *out, int outstride);
+void QuadraticSplinePatchSubdivideFloatBuffer(int cpwidth, int cpheight, int xlevel, int ylevel, int components, const float *in, float *out);
+
+#endif
+
diff --git a/darkplaces.dsp b/darkplaces.dsp
index 0e57d95d..96834b11 100644
--- a/darkplaces.dsp
+++ b/darkplaces.dsp
@@ -168,6 +168,10 @@ SOURCE=.\crc.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\curves.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\cvar.c
 # End Source File
 # Begin Source File
@@ -476,6 +480,10 @@ SOURCE=.\crc.h
 # End Source File
 # Begin Source File
 
+SOURCE=.\curves.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\cvar.h
 # End Source File
 # Begin Source File
diff --git a/gl_backend.c b/gl_backend.c
index fe474eac..6d2f7b95 100644
--- a/gl_backend.c
+++ b/gl_backend.c
@@ -1122,6 +1122,7 @@ float varray_color4f[65536*4];
 float varray_texcoord2f[4][65536*2];
 float varray_texcoord3f[4][65536*3];
 float varray_normal3f[65536*3];
+int earray_element3i[65536];
 
 //===========================================================================
 // vertex array caching subsystem
diff --git a/gl_backend.h b/gl_backend.h
index e8918dc3..435f48f4 100644
--- a/gl_backend.h
+++ b/gl_backend.h
@@ -108,6 +108,7 @@ extern float varray_color4f[65536*4];
 extern float varray_texcoord2f[4][65536*2];
 extern float varray_texcoord3f[4][65536*3];
 extern float varray_normal3f[65536*3];
+extern int earray_element3i[65536];
 
 #endif
 
diff --git a/gl_rsurf.c b/gl_rsurf.c
index 80f8096b..8ac29f4e 100644
--- a/gl_rsurf.c
+++ b/gl_rsurf.c
@@ -37,6 +37,7 @@ cvar_t r_testvis = {0, "r_testvis", "0"};
 cvar_t r_floatbuildlightmap = {0, "r_floatbuildlightmap", "0"};
 cvar_t r_detailtextures = {CVAR_SAVE, "r_detailtextures", "1"};
 cvar_t r_surfaceworldnode = {0, "r_surfaceworldnode", "1"};
+cvar_t r_curves_subdivide_level = {0, "r_curves_subdivide_level", "0"};
 
 static int dlightdivtable[32768];
 
@@ -1937,8 +1938,67 @@ void R_Q3BSP_DrawFace_Mesh(entity_render_t *ent, q3mface_t *face)
 	R_Mesh_Draw(face->numvertices, face->numtriangles, face->data_element3i);
 }
 
+#include "curves.h"
+
 void R_Q3BSP_DrawFace_Patch(entity_render_t *ent, q3mface_t *face)
 {
+	int *e, row0, row1, finalwidth, finalheight, x, y, xlevel = r_curves_subdivide_level.integer, ylevel = r_curves_subdivide_level.integer;
+	rmeshstate_t m;
+
+	finalwidth = ((face->patchsize[0] - 1) << xlevel) + 1;
+	finalheight = ((face->patchsize[1] - 1) << ylevel) + 1;
+
+	// generate vertex arrays
+	QuadraticSplinePatchSubdivideFloatBuffer(face->patchsize[0], face->patchsize[1], xlevel, ylevel, 3, face->data_vertex3f, varray_vertex3f);
+	QuadraticSplinePatchSubdivideFloatBuffer(face->patchsize[0], face->patchsize[1], xlevel, ylevel, 2, face->data_texcoordtexture2f, varray_texcoord2f[0]);
+	if (face->lightmaptexture)
+		QuadraticSplinePatchSubdivideFloatBuffer(face->patchsize[0], face->patchsize[1], xlevel, ylevel, 2, face->data_texcoordlightmap2f, varray_texcoord2f[1]);
+	else
+		QuadraticSplinePatchSubdivideFloatBuffer(face->patchsize[0], face->patchsize[1], xlevel, ylevel, 4, face->data_color4f, varray_color4f);
+
+	// generate elements
+	e = earray_element3i;
+	for (y = 0;y < finalheight - 1;y++)
+	{
+		row0 = (y + 0) * finalwidth;
+		row1 = (y + 1) * finalwidth;
+		for (x = 0;x < finalwidth - 1;x++)
+		{
+			*e++ = row0;
+			*e++ = row1;
+			*e++ = row0 + 1;
+			*e++ = row1;
+			*e++ = row1 + 1;
+			*e++ = row0 + 1;
+			row0++;
+			row1++;
+		}
+	}
+	for (x = 0;x < (finalwidth-1)*(finalheight-1)*6;x++)
+		if ((unsigned int)earray_element3i[x] >= (unsigned int)(finalwidth*finalheight))
+			Con_Printf("e[%i] = %i (> %i)\n", x, earray_element3i[x], finalwidth*finalheight);
+
+	memset(&m, 0, sizeof(m));
+	GL_BlendFunc(GL_ONE, GL_ZERO);
+	GL_DepthMask(true);
+	GL_DepthTest(true);
+	m.tex[0] = R_GetTexture(face->texture->skin.base);
+	m.pointer_texcoord[0] = varray_texcoord2f[0];
+	if (face->lightmaptexture)
+	{
+		m.tex[1] = R_GetTexture(face->lightmaptexture);
+		m.pointer_texcoord[1] = varray_texcoord2f[1];
+		m.texrgbscale[1] = 2;
+		GL_Color(r_colorscale, r_colorscale, r_colorscale, 1);
+	}
+	else
+	{
+		m.texrgbscale[0] = 2;
+		GL_ColorPointer(varray_color4f);
+	}
+	R_Mesh_State_Texture(&m);
+	GL_VertexPointer(varray_vertex3f);
+	R_Mesh_Draw(finalwidth * finalheight, (finalwidth - 1) * (finalheight - 1) * 2, earray_element3i);
 }
 
 void R_Q3BSP_DrawFace(entity_render_t *ent, q3mface_t *face)
@@ -2006,7 +2066,7 @@ void R_Q3BSP_Draw(entity_render_t *ent)
 	model = ent->model;
 	if (r_drawcollisionbrushes.integer < 2)
 	{
-		if (ent == &cl_entities[0].render)
+		if (ent == &cl_entities[0].render && model->brushq3.num_pvsclusters && !r_novis.integer)
 		{
 			Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg);
 			pvs = model->brush.GetPVS(model, modelorg);
@@ -2075,6 +2135,7 @@ void GL_Surf_Init(void)
 	Cvar_RegisterVariable(&r_floatbuildlightmap);
 	Cvar_RegisterVariable(&r_detailtextures);
 	Cvar_RegisterVariable(&r_surfaceworldnode);
+	Cvar_RegisterVariable(&r_curves_subdivide_level);
 
 	R_RegisterModule("GL_Surf", gl_surf_start, gl_surf_shutdown, gl_surf_newmap);
 }
diff --git a/makefile b/makefile
index bd43e706..ef555bc9 100644
--- a/makefile
+++ b/makefile
@@ -56,7 +56,7 @@ SHAREDOBJECTS=	cmd.o collision.o common.o crc.o cvar.o \
 		filematch.o host.o host_cmd.o image.o mathlib.o matrixlib.o \
 		model_alias.o model_brush.o model_shared.o model_sprite.o \
 		netconn.o lhnet.o palette.o portals.o protocol.o fs.o \
-		sys_shared.o winding.o world.o wad.o zone.o
+		sys_shared.o winding.o world.o wad.o zone.o curves.o
 COMMONOBJECTS= $(CLIENTOBJECTS) $(SERVEROBJECTS) $(SHAREDOBJECTS)
 
 # note that builddate.c is very intentionally not compiled to a .o before
diff --git a/model_brush.c b/model_brush.c
index 77e9560d..33f98bd9 100644
--- a/model_brush.c
+++ b/model_brush.c
@@ -3510,7 +3510,10 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
 	{
 		*out = LittleLong(*in);
 		if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
-			Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
+		{
+			Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
+			*out = 0;
+		}
 	}
 }
 
@@ -3615,19 +3618,19 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 			out->type = 0; // error
 			continue;
 		}
+		out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
+		out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
+		out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
+		out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
+		out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
+		out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
+		out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
+		out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
+		out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
 		switch(out->type)
 		{
 		case Q3FACETYPE_POLYGON:
 		case Q3FACETYPE_MESH:
-			out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
-			out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
-			out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
-			out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
-			out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
-			out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
-			out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
-			out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
-			out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
 			break;
 		case Q3FACETYPE_PATCH:
 			patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
@@ -3638,15 +3641,18 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 				out->type = 0; // error
 				continue;
 			}
+			out->patchsize[0] = patchsize[0];
+			out->patchsize[1] = patchsize[1];
+			out->numelements = out->numtriangles = 0;
 			// FIXME: convert patch to triangles here!
-			Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
-			out->type = 0;
-			continue;
+			//Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
+			//out->type = 0;
+			//continue;
 			break;
 		case Q3FACETYPE_FLARE:
 			Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
-			out->type = 0;
-			continue;
+			//out->type = 0;
+			//continue;
 			break;
 		}
 		for (j = 0, invalidelements = 0;j < out->numelements;j++)
@@ -3900,6 +3906,9 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l)
 	q3dpvs_t *in;
 	int totalchains;
 
+	if (l->filelen == 0)
+		return;
+
 	in = (void *)(mod_base + l->fileofs);
 	if (l->filelen < 9)
 		Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
diff --git a/todo b/todo
index ef397365..1d0499a6 100644
--- a/todo
+++ b/todo
@@ -3,6 +3,10 @@
 -n darkplaces: fix a crash when changing level while using qe1 textures (Todd)
 -n darkplaces: revert noclip movement to match nq for compatibility with mods that trap movement as input (MauveBib)
 -n dpmod: make grapple off-hand (joe hill)
+0 darkplaces: add chase_pitch cvar to control pitch angle of chase camera, and chase_angle cvar to control yaw angle of chase camera, and add back chase_right cvar (Electro)
+0 darkplaces: add a scr_screenshot_jpeg_quality cvar (Electro)
+0 darkplaces: shadows are not working with model tag attachments (Electro)
+0 darkplaces: add automatic binding to whatever address the machine's hostname resolves to (in addition to 0.0.0.0); see original quake code for examples (yummyluv)
 0 darkplaces: ability to disable fopen builtin access to read /, read data/, write data/, or disable fopen builtin entirely
 0 darkplaces: add DP_GFX_QUAKE3MODELTAGS, DP_GFX_SKINFILES, and any other new extensions to the wiki
 0 darkplaces: add DP_LITSUPPORT extension
@@ -154,7 +158,7 @@
 1 darkplaces: display "No servers found" instead of a cursor when there are none (yummyluv)
 1 darkplaces: don't accept connect packets after first one (tell Willis)
 1 darkplaces: figure out what's causing skybox to go textureless occasionally (yummyluv)
-1 darkplaces: finish porting Quake2 keyboard stuff (such as clipboard) (Rick)
+1 darkplaces: finish porting Quake2 keyboard stuff (such as clipboard) (Rick, FrikaC)
 1 darkplaces: fix lots of bugs and then retitle the website to get more publicity: DarkPlaces: Re-live Quake again...
 1 darkplaces: fix stuck buttons during a level change (mercury82, tkimmet@ezworks.net)  (further note: this is from the console becoming active temporarily and catching the key release when the player lets go during the loading stage, make it possible to release a button that was pressed before the console was activated, or make it execute -commands for all pressed binds when level starts)
 1 darkplaces: make Host_Error call error reset functions on renderer subsystems?  (models are already flushed)