From 1c62b35652c52a3fc8f469306c2521bb7502dd7f Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 24 Aug 2003 23:00:26 +0000 Subject: [PATCH] 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 --- curves.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++ curves.h | 9 +++ darkplaces.dsp | 8 +++ gl_backend.c | 1 + gl_backend.h | 1 + gl_rsurf.c | 63 ++++++++++++++++++- makefile | 2 +- model_brush.c | 39 +++++++----- todo | 6 +- 9 files changed, 272 insertions(+), 18 deletions(-) create mode 100644 curves.c create mode 100644 curves.h 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) -- 2.39.2