the quadratic spline patches in quake3 maps now work (in english: curves!)
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 24 Aug 2003 23:00:26 +0000 (23:00 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 24 Aug 2003 23:00:26 +0000 (23:00 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3414 d7cf8633-e32d-0410-b094-e92efae38249

curves.c [new file with mode: 0644]
curves.h [new file with mode: 0644]
darkplaces.dsp
gl_backend.c
gl_backend.h
gl_rsurf.c
makefile
model_brush.c
todo

diff --git a/curves.c b/curves.c
new file mode 100644 (file)
index 0000000..5b19243
--- /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 (file)
index 0000000..72567dd
--- /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
+
index 0e57d95..96834b1 100644 (file)
@@ -168,6 +168,10 @@ SOURCE=.\crc.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\curves.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\cvar.c\r
 # End Source File\r
 # Begin Source File\r
@@ -476,6 +480,10 @@ SOURCE=.\crc.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\curves.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\cvar.h\r
 # End Source File\r
 # Begin Source File\r
index fe474ea..6d2f7b9 100644 (file)
@@ -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
index e8918dc..435f48f 100644 (file)
@@ -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
 
index 80f8096..8ac29f4 100644 (file)
@@ -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);
 }
index bd43e70..ef555bc 100644 (file)
--- 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
index 77e9560..33f98bd 100644 (file)
@@ -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 ef39736..1d0499a 100644 (file)
--- 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
 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)