rewrote memory system entirely (hunk, cache, and zone are gone, memory pools replaced...
authorlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 16 Jan 2002 06:16:58 +0000 (06:16 +0000)
committerlordhavoc <lordhavoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 16 Jan 2002 06:16:58 +0000 (06:16 +0000)
models can be reloaded at any time (Mod_CheckLoaded, etc)
entire renderer can be restarted using r_restart command
batch triangle mesh rendering system (gl_backend.c)
most rendering code does not touch GL anymore
gl_textures now supports procedural textures, and fragment textures (small textures combined into larger images)
lightmaps can now be mipmapped (r_miplightmaps)
broke up r_part.c and r_decals.c into cl_particles.c, r_particles.c, cl_decals.c, and r_decals.c to improve renderer/client separation, but explosions are still renderer
moved CL_NextDemo from cl_main.c to cl_demo.c
cleaned up demo stop/disconnect code
removed render modules stuff from all cl_ files
renderer uses separate light array, and recalculates radius for lights based on color, and calculates subtract value to give it a soft edge at the radius perimeter
small surfaces do not use lightmaps (unfortunately they still look slightly different in dynamic lighting)
items can now bob according to cl_itembob* cvars
moved CL_SignonReply from cl_main.c to cl_parse.c
monster movement interpolation is now done clientside instead of serverside, Nehahra movie should look better now
cvars now have a .integer field containing the integer value of their string (nearly all .value accesses have been changed to .integer for speed reasons)
pmodel is now only available in Nehahra mode (pmodel was a bad hack)
r_farclip cvar removed, farclip now dynamically adjusts to level as you explore it
parsing of wad names out of HL maps is now in renderer code
changed isworldmodel stuff in model loading
cleaned up CL_ParseUpdate a bit (related to clientside monster interpolation)
renamed r_glowinglightning to cl_glowinglightning
moved FindNonSolidLocation to bmodel code
lightning beam models are now only looked up once
entity_t now contains an entity_persistent_t which holds data persistent from frame to frame (interpolation mainly), entity_render_t is wiped every frame
marked a lot more things as static
increased command buffer (script execution buffer) from 8k to 32k
COM_LoadMallocFile has been renamed to COM_LoadFile, and all other variants are gone
rounding on MSG_Read/Write stuff has been changed to fix negative rounding (C rounds toward zero, old code assumed it always rounded down)
improved cachepic (menu images) system to load from wad
statusbar now uses cachepic system
cachepics are cleared when renderer is restarted
fixed fog on transparent objects, should always look correct now
moved R_TimeRefresh_f from gl_rmisc.c to gl_rmain.c
moved R_NewMap from gl_rmisc.c to gl_rmain.c
deleted gl_rmisc.c
removed support for glfog
masked sky rendering is now done by first rendering the sky scene, clearing the depthbuffer, rendering invisible depth polys over it, then rendering the scene, this is for more flexibility
C-code based shader system (not scripted)
deleted hcompress.c
due to more thorough memory corruption detection, a (harmless with old zone system) buffer overflow bug in the client name command was fixed
untested (and quite possibly broken) quaternion math stuff added to mathlib.h
removed support for colormod entity effect (massive mess to support it everywhere)
improved QC PR_RunError reports
moved QC opcode execution loop into pr_execprogram.h, included multiple times
fairly major sbar cleanup
rearranged GL extension detection again
added EXT_texture_env_combine support

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

103 files changed:
buildnumber.c
chase.c
cl_decals.c [new file with mode: 0644]
cl_demo.c
cl_light.c
cl_light.h
cl_main.c
cl_parse.c
cl_particles.c [new file with mode: 0644]
cl_tent.c
client.h
cmd.c
cmd.h
common.c
common.h
console.c
cvar.c
cvar.h
draw.h
fractalnoise.c
gl_backend.c [new file with mode: 0644]
gl_backend.h [new file with mode: 0644]
gl_draw.c
gl_models.c
gl_rmain.c
gl_rmisc.c [deleted file]
gl_rsurf.c
gl_screen.c
gl_textures.c
glquake.h
hcompress.c [deleted file]
host.c
host_cmd.c
image.c
image.h
in_svgalib.c
in_win.c
makefile
mathlib.c
mathlib.h
menu.c
model_alias.c
model_alias.h
model_brush.c
model_brush.h
model_shared.c
model_shared.h
model_sprite.c
model_sprite.h
modelgen.h
net_main.c
palette.c
palette.h
portals.c
pr_cmds.c
pr_edict.c
pr_exec.c
pr_execprogram.h [new file with mode: 0644]
progs.h
protocol.c
protocol.h
quakedef.h
quakeio.c
r_clip.c
r_crosshairs.c
r_decals.c
r_explosion.c
r_lerpanim.c
r_light.c
r_light.h
r_modules.c
r_part.c [deleted file]
r_particles.c [new file with mode: 0644]
r_sky.c [new file with mode: 0644]
r_sprites.c
r_textures.h
render.h
sbar.c
snd_dma.c
snd_mem.c
sound.h
spritegn.h
sv_light.c
sv_main.c
sv_phys.c
sv_user.c
sys_linux.c
sys_shared.c
sys_win.c
transform.c
transform.h
ui.c
vid_3dfxsvga.c
vid_glx.c
vid_shared.c
vid_wgl.c
view.c
wad.c
wad.h
world.c
world.h
zone.c
zone.h

index 8ec24a4..2d3037b 100644 (file)
@@ -1,4 +1,4 @@
 
-#define BUILDNUMBER 256
+#define BUILDNUMBER 604
 
 int buildnumber = BUILDNUMBER;
diff --git a/chase.c b/chase.c
index f4ee5b9..1b6e78e 100644 (file)
--- a/chase.c
+++ b/chase.c
@@ -44,6 +44,11 @@ float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int con
 {
        trace_t trace;
 
+// FIXME: broken, fix it
+//     if (impact == NULL && normal == NULL && contents == 0)
+//             return SV_TestLine (cl.worldmodel->hulls, 0, start, end);
+
+       Mod_CheckLoaded(cl.worldmodel);
        memset (&trace, 0, sizeof(trace));
        VectorCopy (end, trace.endpos);
        trace.fraction = 1;
diff --git a/cl_decals.c b/cl_decals.c
new file mode 100644 (file)
index 0000000..0a071c5
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#define MAX_DECALS 2048
+
+typedef struct decal_s
+{
+       entity_render_t *ent;
+       int tex;
+       model_t *model;
+       int surface;
+       float scale;
+       vec3_t org;
+       vec3_t dir;
+       float color[4];
+}
+decal_t;
+
+static decal_t *cl_decals;
+static int cl_currentdecal; // wraps around in decal array, replacing old ones when a new one is needed
+
+static renderdecal_t *cl_renderdecals;
+
+static mempool_t *cl_decal_mempool;
+
+void CL_Decals_Clear(void)
+{
+       memset(cl_decals, 0, MAX_DECALS * sizeof(decal_t));
+       cl_currentdecal = 0;
+}
+
+void CL_Decals_Init(void)
+{
+       cl_decal_mempool = Mem_AllocPool("CL_Decals");
+       cl_decals = (decal_t *) Mem_Alloc(cl_decal_mempool, MAX_DECALS * sizeof(decal_t));
+       memset(cl_decals, 0, MAX_DECALS * sizeof(decal_t));
+       cl_currentdecal = 0;
+
+       // FIXME: r_refdef stuff should be allocated somewhere else?
+       r_refdef.decals = cl_renderdecals = Mem_Alloc(cl_decal_mempool, MAX_DECALS * sizeof(renderdecal_t));
+}
+
+
+// these are static globals only to avoid putting unnecessary things on the stack
+static vec3_t decalorg, decalbestorg;
+static float decalbestdist;
+static msurface_t *decalbestsurf;
+static entity_render_t *decalbestent, *decalent;
+static model_t *decalmodel;
+void CL_RecursiveDecalSurface (mnode_t *node)
+{
+       // these are static because only one occurance of them need exist at once, so avoid putting them on the stack
+       static float ndist, dist;
+       static msurface_t *surf, *endsurf;
+       static vec3_t impact;
+       static int ds, dt;
+
+loc0:
+       if (node->contents < 0)
+               return;
+
+       ndist = PlaneDiff(decalorg, node->plane);
+
+       if (ndist > 16)
+       {
+               node = node->children[0];
+               goto loc0;
+       }
+       if (ndist < -16)
+       {
+               node = node->children[1];
+               goto loc0;
+       }
+
+// mark the polygons
+       surf = decalmodel->surfaces + node->firstsurface;
+       endsurf = surf + node->numsurfaces;
+       for (;surf < endsurf;surf++)
+       {
+               if (!(surf->flags & SURF_LIGHTMAP))
+                       continue;
+
+               dist = PlaneDiff(decalorg, surf->plane);
+               if (surf->flags & SURF_PLANEBACK)
+                       dist = -dist;
+               if (dist < -1)
+                       continue;
+               if (dist >= decalbestdist)
+                       continue;
+
+               impact[0] = decalorg[0] - surf->plane->normal[0] * dist;
+               impact[1] = decalorg[1] - surf->plane->normal[1] * dist;
+               impact[2] = decalorg[2] - surf->plane->normal[2] * dist;
+
+               ds = (int) (DotProduct(impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) - surf->texturemins[0];
+               dt = (int) (DotProduct(impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) - surf->texturemins[1];
+
+               if (ds < 0 || dt < 0 || ds > surf->extents[0] || dt > surf->extents[1])
+                       continue;
+
+               VectorCopy(decalorg, decalbestorg);
+               decalbestent = decalent;
+               decalbestsurf = surf;
+               decalbestdist = dist;
+       }
+
+       if (node->children[0]->contents >= 0)
+       {
+               if (node->children[1]->contents >= 0)
+               {
+                       CL_RecursiveDecalSurface (node->children[0]);
+                       node = node->children[1];
+                       goto loc0;
+               }
+               else
+               {
+                       node = node->children[0];
+                       goto loc0;
+               }
+       }
+       else if (node->children[1]->contents >= 0)
+       {
+               node = node->children[1];
+               goto loc0;
+       }
+}
+
+void CL_Decal(vec3_t origin, int tex, float scale, float red, float green, float blue, float alpha)
+{
+       int i;
+       decal_t *decal;
+
+       if (alpha < (1.0f / 255.0f))
+               return;
+
+       // find the best surface to place the decal on
+       decalbestent = NULL;
+       decalbestsurf = NULL;
+       decalbestdist = 16;
+
+       decalent = NULL;
+       decalmodel = cl.worldmodel;
+       Mod_CheckLoaded(decalmodel);
+       VectorCopy(origin, decalorg);
+       CL_RecursiveDecalSurface (decalmodel->nodes);
+
+       for (i = 1;i < MAX_EDICTS;i++)
+       {
+               decalent = &cl_entities[i].render;
+               decalmodel = decalent->model;
+               if (decalmodel && decalmodel->name[0])
+               {
+                       Mod_CheckLoaded(decalmodel);
+                       if (decalmodel->type == mod_brush)
+                       {
+                               softwaretransformforentity(decalent);
+                               softwareuntransform(origin, decalorg);
+                               CL_RecursiveDecalSurface (decalmodel->nodes);
+                       }
+               }
+       }
+
+       // abort if no suitable surface was found
+       if (decalbestsurf == NULL)
+               return;
+
+       // grab a decal from the array and advance to the next decal to replace, wrapping to replace an old decal if necessary
+       decal = &cl_decals[cl_currentdecal++];
+       if (cl_currentdecal >= MAX_DECALS)
+               cl_currentdecal = 0;
+       memset(decal, 0, sizeof(*decal));
+
+       decal->ent = decalbestent;
+       if (decal->ent)
+               decal->model = decal->ent->model;
+       else
+               decal->model = cl.worldmodel;
+
+       decal->tex = tex + 1; // our texture numbers are +1 to make 0 mean invisible
+       VectorNegate(decalbestsurf->plane->normal, decal->dir);
+       if (decalbestsurf->flags & SURF_PLANEBACK)
+               VectorNegate(decal->dir, decal->dir);
+       // 0.25 to push it off the surface a bit
+       decalbestdist -= 0.25f;
+       decal->org[0] = decalbestorg[0] + decal->dir[0] * decalbestdist;
+       decal->org[1] = decalbestorg[1] + decal->dir[1] * decalbestdist;
+       decal->org[2] = decalbestorg[2] + decal->dir[2] * decalbestdist;
+       decal->scale = scale * 0.5f;
+       // store the color
+       decal->color[0] = red;
+       decal->color[1] = green;
+       decal->color[2] = blue;
+       decal->color[3] = alpha;
+       // store the surface information
+       decal->surface = decalbestsurf - decal->model->surfaces;
+}
+
+void CL_UpdateDecals (void)
+{
+       int i;
+       decal_t *p;
+       renderdecal_t *r;
+
+       for (i = 0, p = cl_decals, r = r_refdef.decals;i < MAX_DECALS;i++, p++)
+       {
+               if (p->tex == 0)
+                       continue;
+
+               if (p->ent && p->ent->visframe == r_framecount && p->ent->model != p->model)
+               {
+                       p->tex = 0;
+                       continue;
+               }
+
+               r->ent = p->ent;
+               r->tex = p->tex - 1; // our texture numbers are +1 to make 0 mean invisible
+               r->surface = p->surface;
+               r->scale = p->scale;
+               VectorCopy(p->org, r->org);
+               VectorCopy(p->dir, r->dir);
+               VectorCopy4(p->color, r->color);
+               r++;
+       }
+       r_refdef.numdecals = r - r_refdef.decals;
+}
+
index ee61e50..780b1f1 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -36,12 +36,45 @@ read from the demo file.
 */
 
 /*
+=====================
+CL_NextDemo
+
+Called to play the next demo in the demo loop
+=====================
+*/
+void CL_NextDemo (void)
+{
+       char    str[1024];
+
+       if (cls.demonum == -1)
+               return;         // don't play demos
+
+//     SCR_BeginLoadingPlaque ();
+
+       if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
+       {
+               cls.demonum = 0;
+               if (!cls.demos[cls.demonum][0])
+               {
+                       Con_Printf ("No demos listed with startdemos\n");
+                       cls.demonum = -1;
+                       return;
+               }
+       }
+
+       sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
+       Cbuf_InsertText (str);
+       cls.demonum++;
+}
+
+/*
 ==============
 CL_StopPlayback
 
 Called when a demo file runs out, or the user starts a game
 ==============
 */
+// LordHavoc: now called only by CL_Disconnect
 void CL_StopPlayback (void)
 {
        if (!cls.demoplayback)
@@ -50,7 +83,6 @@ void CL_StopPlayback (void)
        Qclose (cls.demofile);
        cls.demoplayback = false;
        cls.demofile = NULL;
-       cls.state = ca_disconnected;
 
        if (cls.timedemo)
                CL_FinishTimeDemo ();
@@ -134,7 +166,7 @@ int CL_GetMessage (void)
                r = Qread (cls.demofile, net_message.data, net_message.cursize);
                if (r != net_message.cursize)
                {
-                       CL_StopPlayback ();
+                       CL_Disconnect ();
                        return 0;
                }
        
index e4b9d4b..be8ab86 100644 (file)
@@ -2,24 +2,6 @@
 
 dlight_t cl_dlights[MAX_DLIGHTS];
 
-void cl_light_start(void)
-{
-}
-
-void cl_light_shutdown(void)
-{
-}
-
-void cl_light_newmap(void)
-{
-       memset (cl_dlights, 0, sizeof(cl_dlights));
-}
-
-void CL_Light_Init(void)
-{
-       R_RegisterModule("CL_Light", cl_light_start, cl_light_shutdown, cl_light_newmap);
-}
-
 /*
 ===============
 CL_AllocDlight
@@ -76,7 +58,6 @@ void CL_DecayLights (void)
 
        time = cl.time - cl.oldtime;
 
-       c_dlights = 0;
        dl = cl_dlights;
        for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
        {
@@ -88,8 +69,6 @@ void CL_DecayLights (void)
                        continue;
                }
 
-               c_dlights++; // count every dlight in use
-
                dl->radius -= time*dl->decay;
                if (dl->radius < 0)
                        dl->radius = 0;
index 3843065..f29c25b 100644 (file)
@@ -3,19 +3,25 @@
 #define        MAX_DLIGHTS             256
 typedef struct
 {
+       // location
        vec3_t  origin;
+       // stop lighting after this time
+       float   die;
+       // color of light
+       vec3_t  color;
+       // brightness (not really radius anymore)
        float   radius;
-       float   die;                    // stop lighting after this time
-       float   decay;                  // drop this each second
-       entity_render_t *ent;   // the entity that spawned this light (can be NULL if it will never be replaced)
-       vec3_t  color;                  // LordHavoc: colored lighting
-} dlight_t;
+       // drop this each second
+       float   decay;
+       // the entity that spawned this light (can be NULL if it will never be replaced)
+       entity_render_t *ent;
+}
+dlight_t;
 
 // LordHavoc: this affects the lighting scale of the whole game
-#define LIGHTOFFSET 4096.0f
+#define LIGHTOFFSET 1024.0f
 
-extern dlight_t                cl_dlights[MAX_DLIGHTS];
+extern dlight_t cl_dlights[MAX_DLIGHTS];
 
 extern void CL_AllocDlight (entity_render_t *ent, vec3_t org, float radius, float red, float green, float blue, float decay, float lifetime);
 extern void CL_DecayLights (void);
-
index 26b78cf..73d7adc 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -25,24 +25,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // references them even when on a unix system.
 
 // these two are not intended to be set directly
-cvar_t cl_name = {CVAR_SAVE, "_cl_name", "player"};
-cvar_t cl_color = {CVAR_SAVE, "_cl_color", "0"};
-cvar_t cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
+cvar_t cl_name = {CVAR_SAVE, "_cl_name", "player"};
+cvar_t cl_color = {CVAR_SAVE, "_cl_color", "0"};
+cvar_t cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
 
-cvar_t cl_shownet = {0, "cl_shownet","0"};
-cvar_t cl_nolerp = {0, "cl_nolerp", "0"};
+cvar_t cl_shownet = {0, "cl_shownet","0"};
+cvar_t cl_nolerp = {0, "cl_nolerp", "0"};
 
-cvar_t lookspring = {CVAR_SAVE, "lookspring","0"};
-cvar_t lookstrafe = {CVAR_SAVE, "lookstrafe","0"};
-cvar_t sensitivity = {CVAR_SAVE, "sensitivity","3", 1, 30};
+cvar_t cl_itembobheight = {0, "cl_itembobheight", "8"};
+cvar_t cl_itembobspeed = {0, "cl_itembobspeed", "0.5"};
 
-cvar_t m_pitch = {CVAR_SAVE, "m_pitch","0.022"};
-cvar_t m_yaw = {CVAR_SAVE, "m_yaw","0.022"};
-cvar_t m_forward = {CVAR_SAVE, "m_forward","1"};
-cvar_t m_side = {CVAR_SAVE, "m_side","0.8"};
+cvar_t lookspring = {CVAR_SAVE, "lookspring","0"};
+cvar_t lookstrafe = {CVAR_SAVE, "lookstrafe","0"};
+cvar_t sensitivity = {CVAR_SAVE, "sensitivity","3", 1, 30};
+
+cvar_t m_pitch = {CVAR_SAVE, "m_pitch","0.022"};
+cvar_t m_yaw = {CVAR_SAVE, "m_yaw","0.022"};
+cvar_t m_forward = {CVAR_SAVE, "m_forward","1"};
+cvar_t m_side = {CVAR_SAVE, "m_side","0.8"};
 
 cvar_t freelook = {CVAR_SAVE, "freelook", "1"};
 
+cvar_t cl_draweffects = {0, "cl_draweffects", "1"};
+
+mempool_t *cl_scores_mempool;
+
 client_static_t        cls;
 client_state_t cl;
 // FIXME: put these on hunk?
@@ -53,6 +60,27 @@ lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
 int                            cl_numvisedicts;
 entity_t               *cl_visedicts[MAX_VISEDICTS];
 
+typedef struct effect_s
+{
+       int active;
+       vec3_t origin;
+       float starttime;
+       float framerate;
+       int modelindex;
+       int startframe;
+       int endframe;
+       // these are for interpolation
+       int frame;
+       double frame1time;
+       double frame2time;
+}
+cl_effect_t;
+
+#define MAX_EFFECTS 256
+
+static cl_effect_t cl_effect[MAX_EFFECTS];
+
+
 /*
 =====================
 CL_ClearState
@@ -66,16 +94,22 @@ void CL_ClearState (void)
        if (!sv.active)
                Host_ClearMemory ();
 
+       Mem_EmptyPool(cl_scores_mempool);
+
 // wipe the entire cl structure
        memset (&cl, 0, sizeof(cl));
 
        SZ_Clear (&cls.message);
 
 // clear other arrays
-       memset (cl_entities, 0, sizeof(cl_entities));
-       memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
-       memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
-       memset (cl_beams, 0, sizeof(cl_beams));
+       memset(cl_entities, 0, sizeof(cl_entities));
+       memset(cl_lightstyle, 0, sizeof(cl_lightstyle));
+       memset(cl_temp_entities, 0, sizeof(cl_temp_entities));
+       memset(cl_beams, 0, sizeof(cl_beams));
+       memset(cl_dlights, 0, sizeof(cl_dlights));
+       memset(cl_effect, 0, sizeof(cl_effect));
+       CL_Particles_Clear();
+       CL_Decals_Clear();
        // LordHavoc: have to set up the baseline info for alpha and other stuff
        for (i = 0;i < MAX_EDICTS;i++)
        {
@@ -142,7 +176,8 @@ void CL_Disconnect (void)
        cl.cshifts[2].percent = 0;
        cl.cshifts[3].percent = 0;
 
-// if running a local server, shut it down
+       cl.worldmodel = NULL;
+
        if (cls.demoplayback)
                CL_StopPlayback ();
        else if (cls.state == ca_connected)
@@ -156,11 +191,11 @@ void CL_Disconnect (void)
                NET_SendUnreliableMessage (cls.netcon, &cls.message);
                SZ_Clear (&cls.message);
                NET_Close (cls.netcon);
-
-               cls.state = ca_disconnected;
+               // if running a local server, shut it down
                if (sv.active)
                        Host_ShutdownServer(false);
        }
+       cls.state = ca_disconnected;
 
        cls.demoplayback = cls.timedemo = false;
        cls.signon = 0;
@@ -197,106 +232,23 @@ void CL_EstablishConnection (char *host)
        if (!cls.netcon)
                Host_Error ("CL_Connect: connect failed\n");
        Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
-       
+
        cls.demonum = -1;                       // not in the demo loop now
        cls.state = ca_connected;
        cls.signon = 0;                         // need all the signon messages before playing
 }
 
 /*
-=====================
-CL_SignonReply
-
-An svc_signonnum has been received, perform a client side setup
-=====================
-*/
-void CL_SignonReply (void)
-{
-       char    str[8192];
-
-Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
-
-       switch (cls.signon)
-       {
-       case 1:
-               MSG_WriteByte (&cls.message, clc_stringcmd);
-               MSG_WriteString (&cls.message, "prespawn");
-               break;
-
-       case 2:
-               MSG_WriteByte (&cls.message, clc_stringcmd);
-               MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
-
-               MSG_WriteByte (&cls.message, clc_stringcmd);
-               MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
-       
-               if (cl_pmodel.value)
-               {
-                       MSG_WriteByte (&cls.message, clc_stringcmd);
-                       MSG_WriteString (&cls.message, va("pmodel %f\n", cl_pmodel.value));
-               }
-
-               MSG_WriteByte (&cls.message, clc_stringcmd);
-               sprintf (str, "spawn %s", cls.spawnparms);
-               MSG_WriteString (&cls.message, str);
-               break;
-               
-       case 3: 
-               MSG_WriteByte (&cls.message, clc_stringcmd);
-               MSG_WriteString (&cls.message, "begin");
-               Cache_Report ();                // print remaining memory
-               break;
-               
-       case 4:
-//             SCR_EndLoadingPlaque ();                // allow normal screen updates
-               Con_ClearNotify();
-               break;
-       }
-}
-
-/*
-=====================
-CL_NextDemo
-
-Called to play the next demo in the demo loop
-=====================
-*/
-void CL_NextDemo (void)
-{
-       char    str[1024];
-
-       if (cls.demonum == -1)
-               return;         // don't play demos
-
-//     SCR_BeginLoadingPlaque ();
-
-       if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
-       {
-               cls.demonum = 0;
-               if (!cls.demos[cls.demonum][0])
-               {
-                       Con_Printf ("No demos listed with startdemos\n");
-                       cls.demonum = -1;
-                       return;
-               }
-       }
-
-       sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
-       Cbuf_InsertText (str);
-       cls.demonum++;
-}
-
-/*
 ==============
 CL_PrintEntities_f
 ==============
 */
-void CL_PrintEntities_f (void)
+static void CL_PrintEntities_f (void)
 {
        entity_t        *ent;
        int                     i, j;
        char            name[32];
-       
+
        for (i = 0, ent = cl_entities;i < MAX_EDICTS /*cl.num_entities*/;i++, ent++)
        {
                if (!ent->state_current.active)
@@ -327,19 +279,19 @@ Determines the fraction between the last two messages that the objects
 should be put at.
 ===============
 */
-float  CL_LerpPoint (void)
+static float CL_LerpPoint (void)
 {
        float   f, frac;
 
        f = cl.mtime[0] - cl.mtime[1];
 
        // LordHavoc: lerp in listen games as the server is being capped below the client (usually)
-       if (!f || cl_nolerp.value || cls.timedemo || (sv.active && svs.maxclients == 1))
+       if (!f || cl_nolerp.integer || cls.timedemo || (sv.active && svs.maxclients == 1))
        {
                cl.time = cl.mtime[0];
                return 1;
        }
-               
+
        if (f > 0.1)
        {       // dropped packet, or start of demo
                cl.mtime[1] = cl.mtime[0] - 0.1;
@@ -365,35 +317,18 @@ float     CL_LerpPoint (void)
                }
                frac = 1;
        }
-               
-       return frac;
-}
-
-float CL_EntityLerpPoint (entity_t *ent)
-{
-       float   f;
-
-       if (cl_nolerp.value || cls.timedemo || (sv.active && svs.maxclients == 1))
-               return 1;
-
-       f = ent->state_current.time - ent->state_previous.time;
-//     Con_Printf(" %g-%g=%g", ent->state_current.time, ent->state_previous.time, f);
 
-       if (f <= 0)
-               return 1;
-       if (f >= 0.1)
-               f = 0.1;
-
-//     Con_Printf(" %g-%g/%g=%f", cl.time, ent->state_previous.time, f, (cl.time - ent->state_previous.time) / f);
-       f = (cl.time - ent->state_previous.time) / f;
-       return bound(0, f, 1);
+       return frac;
 }
 
-void CL_RelinkStaticEntities(void)
+static void CL_RelinkStaticEntities(void)
 {
        int i;
        for (i = 0;i < cl.num_statics && cl_numvisedicts < MAX_VISEDICTS;i++)
+       {
+               Mod_CheckLoaded(cl_static_entities[i].render.model);
                cl_visedicts[cl_numvisedicts++] = &cl_static_entities[i];
+       }
 }
 
 /*
@@ -401,16 +336,18 @@ void CL_RelinkStaticEntities(void)
 CL_RelinkEntities
 ===============
 */
-void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent);
-void CL_RelinkNetworkEntities()
+static void CL_RelinkNetworkEntities()
 {
        entity_t        *ent;
-       int                     i, j, glowcolor, effects;
-       float           f, d, bobjrotate/*, bobjoffset*/, dlightradius, glowsize;
+       int                     i, glowcolor, effects;
+       float           f, d, bobjrotate, bobjoffset, dlightradius, glowsize, lerp;
        vec3_t          oldorg, neworg, delta, dlightcolor;
 
        bobjrotate = ANGLEMOD(100*cl.time);
-//     bobjoffset = cos(180 * cl.time * M_PI / 180) * 4.0f + 4.0f;
+       if (cl_itembobheight.value)
+               bobjoffset = (cos(cl.time * cl_itembobspeed.value * (2.0 * M_PI)) + 1.0) * 0.5 * cl_itembobheight.value;
+       else
+               bobjoffset = 0;
 
        CL_RelinkStaticEntities();
 
@@ -426,19 +363,84 @@ void CL_RelinkNetworkEntities()
                if (!ent->state_previous.active)
                {
                        // only one state available
+                       lerp = 1;
+                       VectorCopy (ent->state_current.origin, oldorg); // skip trails
                        VectorCopy (ent->state_current.origin, neworg);
                        VectorCopy (ent->state_current.angles, ent->render.angles);
+
+                       /*
+                       // monster interpolation
+                       ent->persistent.steplerptime = 0;
+                       VectorCopy(ent->state_current.origin, ent->persistent.stepoldorigin);
+                       VectorCopy(ent->state_current.angles, ent->persistent.stepoldangles);
+                       VectorCopy(ent->state_current.origin, ent->persistent.steporigin);
+                       VectorCopy(ent->state_current.angles, ent->persistent.stepangles);
+                       */
+               }
+               /*
+               else if ((ent->state_current.flags & ent->state_previous.flags) & ENTFLAG_STEP)
+               {
+                       if (ent->state_current.origin[0] != ent->persistent.steporigin[0]
+                        || ent->state_current.origin[1] != ent->persistent.steporigin[1]
+                        || ent->state_current.origin[2] != ent->persistent.steporigin[2]
+                        || ent->state_current.angles[0] != ent->persistent.stepangles[0]
+                        || ent->state_current.angles[1] != ent->persistent.stepangles[1]
+                        || ent->state_current.angles[2] != ent->persistent.stepangles[2])
+                       {
+                               // update lerp positions
+                               ent->clientpersistent.steplerptime = sv.time;
+                               VectorCopy(ent->steporigin, ent->stepoldorigin);
+                               VectorCopy(ent->stepangles, ent->stepoldangles);
+                               VectorCopy(ent->v.origin, ent->steporigin);
+                               VectorCopy(ent->v.angles, ent->stepangles);
+                       }
+                       lerp = (cl.time - ent->persistent.steplerptime) * 10.0;
+                       if (lerp < 1)
+                       {
+                               // origin
+                               VectorSubtract(ent->persistent.steporigin, ent->persistent.stepoldorigin, delta);
+                               VectorMA(ent->persistent.stepoldorigin, lerp, delta, neworg);
+
+                               // angles
+                               VectorSubtract(ent->persistent.stepangles, ent->persistent.stepoldangles, delta);
+                               // choose shortest rotate (to avoid 'spin around' situations)
+                               if (delta[0] < -180) delta[0] += 360;else if (delta[0] >= 180) delta[0] -= 360;
+                               if (delta[1] < -180) delta[1] += 360;else if (delta[1] >= 180) delta[1] -= 360;
+                               if (delta[2] < -180) delta[2] += 360;else if (delta[2] >= 180) delta[2] -= 360;
+                               VectorMA(ent->stepoldangles, lerp, delta, ent->render.angles);
+                       }
+                       else
+                       {
+                               VectorCopy(ent->persistent.steporigin, neworg);
+                               VectorCopy(ent->persistent.stepangles, ent->render.angles);
+                       }
                }
+               */
                else
                {
+                       /*
+                       // monster interpolation
+                       ent->persistent.steplerptime = 0;
+                       VectorCopy(ent->state_current.origin, ent->persistent.stepoldorigin);
+                       VectorCopy(ent->state_current.angles, ent->persistent.stepoldangles);
+                       VectorCopy(ent->state_current.origin, ent->persistent.steporigin);
+                       VectorCopy(ent->state_current.angles, ent->persistent.stepangles);
+                       */
+
                        // if the delta is large, assume a teleport and don't lerp
                        VectorSubtract(ent->state_current.origin, ent->state_previous.origin, delta);
                        // LordHavoc: increased tolerance from 100 to 200
-                       if (DotProduct(delta, delta) > 200*200)
-                               f = 1;
+                       if ((sv.active && svs.maxclients == 1 && !(ent->state_current.flags & RENDER_STEP)) || cls.timedemo || DotProduct(delta, delta) > 200*200 || cl_nolerp.integer)
+                               lerp = 1;
                        else
-                               f = CL_EntityLerpPoint(ent);
-                       if (f >= 1)
+                       {
+                               f = ent->state_current.time - ent->state_previous.time;
+                               if (f > 0)
+                                       lerp = (cl.time - ent->state_previous.time) / f;
+                               else
+                                       lerp = 1;
+                       }
+                       if (lerp >= 1)
                        {
                                // no interpolation
                                VectorCopy (ent->state_current.origin, neworg);
@@ -447,29 +449,25 @@ void CL_RelinkNetworkEntities()
                        else
                        {
                                // interpolate the origin and angles
-                               for (j = 0;j < 3;j++)
-                               {
-                                       neworg[j] = ent->state_previous.origin[j] + f*delta[j];
-
-                                       d = ent->state_current.angles[j] - ent->state_previous.angles[j];
-                                       if (d > 180)
-                                               d -= 360;
-                                       else if (d < -180)
-                                               d += 360;
-                                       ent->render.angles[j] = ent->state_previous.angles[j] + f*d;
-                               }
+                               VectorMA(ent->state_previous.origin, lerp, delta, neworg);
+                               VectorSubtract(ent->state_current.angles, ent->state_previous.angles, delta);
+                               if (delta[0] < -180) delta[0] += 360;else if (delta[0] >= 180) delta[0] -= 360;
+                               if (delta[1] < -180) delta[1] += 360;else if (delta[1] >= 180) delta[1] -= 360;
+                               if (delta[2] < -180) delta[2] += 360;else if (delta[2] >= 180) delta[2] -= 360;
+                               VectorMA(ent->state_previous.angles, lerp, delta, ent->render.angles);
                        }
                }
 
                VectorCopy (neworg, ent->persistent.trail_origin);
                // persistent.modelindex will be updated by CL_LerpUpdate
-               if (ent->state_current.modelindex != ent->persistent.modelindex)
+               if (ent->state_current.modelindex != ent->persistent.modelindex || !ent->state_previous.active)
                        VectorCopy(neworg, oldorg);
 
                VectorCopy (neworg, ent->render.origin);
                ent->render.flags = ent->state_current.flags;
                ent->render.effects = effects = ent->state_current.effects;
                ent->render.model = cl.model_precache[ent->state_current.modelindex];
+               Mod_CheckLoaded(ent->render.model);
                ent->render.frame = ent->state_current.frame;
                if (cl.scores == NULL || !ent->state_current.colormap)
                        ent->render.colormap = -1; // no special coloring
@@ -480,9 +478,6 @@ void CL_RelinkNetworkEntities()
                ent->render.scale = ent->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate?
                glowsize = ent->state_current.glowsize * 4.0f; // FIXME: interpolate?
                glowcolor = ent->state_current.glowcolor;
-               ent->render.colormod[0] = (float) ((ent->state_current.colormod >> 5) & 7) * (1.0f / 7.0f);
-               ent->render.colormod[1] = (float) ((ent->state_current.colormod >> 2) & 7) * (1.0f / 7.0f);
-               ent->render.colormod[2] = (float) (ent->state_current.colormod & 3) * (1.0f / 3.0f);
 
                // update interpolation info
                CL_LerpUpdate(ent, ent->state_current.frame, ent->state_current.modelindex);
@@ -497,7 +492,7 @@ void CL_RelinkNetworkEntities()
                if (effects)
                {
                        if (effects & EF_BRIGHTFIELD)
-                               R_EntityParticles (ent);
+                               CL_EntityParticles (ent);
                        if (effects & EF_MUZZLEFLASH)
                        {
                                vec3_t v, v2;
@@ -559,7 +554,7 @@ void CL_RelinkNetworkEntities()
                                        }
                                        // how many flames to make
                                        temp = (int) (cl.time * 300) - (int) (cl.oldtime * 300);
-                                       R_FlameCube(mins, maxs, temp);
+                                       CL_FlameCube(mins, maxs, temp);
                                }
                                d = lhrandom(200, 250);
                                dlightcolor[0] += d * 1.0f;
@@ -574,22 +569,22 @@ void CL_RelinkNetworkEntities()
                        if (ent->render.model->flags & EF_ROTATE)
                        {
                                ent->render.angles[1] = bobjrotate;
-//                             ent->render.origin[2] += bobjoffset;
+                               ent->render.origin[2] += bobjoffset;
                        }
                        // only do trails if present in the previous frame as well
                        if (ent->state_previous.active)
                        {
                                if (ent->render.model->flags & EF_GIB)
-                                       R_RocketTrail (oldorg, neworg, 2, ent);
+                                       CL_RocketTrail (oldorg, neworg, 2, ent);
                                else if (ent->render.model->flags & EF_ZOMGIB)
-                                       R_RocketTrail (oldorg, neworg, 4, ent);
+                                       CL_RocketTrail (oldorg, neworg, 4, ent);
                                else if (ent->render.model->flags & EF_TRACER)
-                                       R_RocketTrail (oldorg, neworg, 3, ent);
+                                       CL_RocketTrail (oldorg, neworg, 3, ent);
                                else if (ent->render.model->flags & EF_TRACER2)
-                                       R_RocketTrail (oldorg, neworg, 5, ent);
+                                       CL_RocketTrail (oldorg, neworg, 5, ent);
                                else if (ent->render.model->flags & EF_ROCKET)
                                {
-                                       R_RocketTrail (oldorg, ent->render.origin, 0, ent);
+                                       CL_RocketTrail (oldorg, ent->render.origin, 0, ent);
                                        dlightcolor[0] += 200.0f;
                                        dlightcolor[1] += 160.0f;
                                        dlightcolor[2] +=  80.0f;
@@ -597,12 +592,12 @@ void CL_RelinkNetworkEntities()
                                else if (ent->render.model->flags & EF_GRENADE)
                                {
                                        if (ent->render.alpha == -1) // LordHavoc: Nehahra dem compatibility
-                                               R_RocketTrail (oldorg, neworg, 7, ent);
+                                               CL_RocketTrail (oldorg, neworg, 7, ent);
                                        else
-                                               R_RocketTrail (oldorg, neworg, 1, ent);
+                                               CL_RocketTrail (oldorg, neworg, 1, ent);
                                }
                                else if (ent->render.model->flags & EF_TRACER3)
-                                       R_RocketTrail (oldorg, neworg, 6, ent);
+                                       CL_RocketTrail (oldorg, neworg, 6, ent);
                        }
                }
                // LordHavoc: customizable glow
@@ -615,7 +610,7 @@ void CL_RelinkNetworkEntities()
                }
                // LordHavoc: customizable trail
                if (ent->render.flags & RENDER_GLOWTRAIL)
-                       R_RocketTrail2 (oldorg, neworg, glowcolor, ent);
+                       CL_RocketTrail2 (oldorg, neworg, glowcolor, ent);
 
                if (dlightcolor[0] || dlightcolor[1] || dlightcolor[2])
                {
@@ -624,12 +619,12 @@ void CL_RelinkNetworkEntities()
                        d = 1.0f / dlightradius;
                        VectorCopy(neworg, vec);
                        // hack to make glowing player light shine on their gun
-                       if (i == cl.viewentity && !chase_active.value)
+                       if (i == cl.viewentity && !chase_active.integer)
                                vec[2] += 30;
                        CL_AllocDlight (&ent->render, vec, dlightradius, dlightcolor[0] * d, dlightcolor[1] * d, dlightcolor[2] * d, 0, 0);
                }
 
-               if (chase_active.value)
+               if (chase_active.integer)
                {
                        if (ent->render.flags & RENDER_VIEWMODEL)
                                continue;
@@ -649,7 +644,7 @@ void CL_RelinkNetworkEntities()
        }
 }
 
-void CL_LerpPlayerVelocity (void)
+static void CL_LerpPlayerVelocity (void)
 {
        int i;
        float frac, d;
@@ -675,12 +670,90 @@ void CL_LerpPlayerVelocity (void)
        }
 }
 
+void CL_Effect(vec3_t org, int modelindex, int startframe, int framecount, float framerate)
+{
+       int i;
+       cl_effect_t *e;
+       if (!modelindex) // sanity check
+               return;
+       for (i = 0, e = cl_effect;i < MAX_EFFECTS;i++, e++)
+       {
+               if (e->active)
+                       continue;
+               e->active = true;
+               VectorCopy(org, e->origin);
+               e->modelindex = modelindex;
+               e->starttime = cl.time;
+               e->startframe = startframe;
+               e->endframe = startframe + framecount;
+               e->framerate = framerate;
+
+               e->frame = 0;
+               e->frame1time = cl.time;
+               e->frame2time = cl.time;
+               break;
+       }
+}
+
+static void CL_RelinkEffects()
+{
+       int i, intframe;
+       cl_effect_t *e;
+       entity_t *vis;
+       float frame;
+
+       for (i = 0, e = cl_effect;i < MAX_EFFECTS;i++, e++)
+       {
+               if (e->active)
+               {
+                       frame = (cl.time - e->starttime) * e->framerate + e->startframe;
+                       intframe = frame;
+                       if (intframe < 0 || intframe >= e->endframe)
+                       {
+                               e->active = false;
+                               memset(e, 0, sizeof(*e));
+                               continue;
+                       }
+
+                       if (intframe != e->frame)
+                       {
+                               e->frame = intframe;
+                               e->frame1time = e->frame2time;
+                               e->frame2time = cl.time;
+                       }
+
+                       if ((vis = CL_NewTempEntity()))
+                       {
+                               // interpolation stuff
+                               vis->render.frame1 = intframe;
+                               vis->render.frame2 = intframe + 1;
+                               if (vis->render.frame2 >= e->endframe)
+                                       vis->render.frame2 = -1; // disappear
+                               vis->render.framelerp = frame - intframe;
+                               vis->render.frame1time = e->frame1time;
+                               vis->render.frame2time = e->frame2time;
+
+                               // normal stuff
+                               VectorCopy(e->origin, vis->render.origin);
+                               vis->render.model = cl.model_precache[e->modelindex];
+                               vis->render.frame = vis->render.frame2;
+                               vis->render.colormap = -1; // no special coloring
+                               vis->render.scale = 1;
+                               vis->render.alpha = 1;
+                       }
+               }
+       }
+}
+
 void CL_RelinkEntities (void)
 {
        cl_numvisedicts = 0;
 
        CL_LerpPlayerVelocity();
        CL_RelinkNetworkEntities();
+       CL_RelinkEffects();
+       CL_MoveParticles();
+       CL_UpdateDecals();
 }
 
 
@@ -697,7 +770,7 @@ int CL_ReadFromServer (void)
 
        cl.oldtime = cl.time;
        cl.time += cl.frametime;
-       
+
        netshown = false;
        do
        {
@@ -706,22 +779,21 @@ int CL_ReadFromServer (void)
                        Host_Error ("CL_ReadFromServer: lost server connection");
                if (!ret)
                        break;
-               
+
                cl.last_received_message = realtime;
 
-               if (cl_shownet.value)
+               if (cl_shownet.integer)
                        netshown = true;
 
                CL_ParseServerMessage ();
        }
        while (ret && cls.state == ca_connected);
-       
+
        if (netshown)
                Con_Printf ("\n");
 
        CL_RelinkEntities ();
        CL_UpdateTEnts ();
-       CL_DoEffects ();
 
 //
 // bring the links up to date
@@ -748,7 +820,7 @@ void CL_SendCmd (void)
 
        // allow mice or other external controllers to add to the move
                IN_Move (&cmd);
-       
+
        // send the unreliable message
                CL_SendMove (&cmd);
        }
@@ -758,11 +830,11 @@ void CL_SendCmd (void)
                SZ_Clear (&cls.message);
                return;
        }
-       
+
 // send the reliable message
        if (!cls.message.cursize)
                return;         // no message at all
-       
+
        if (!NET_CanSendMessage (cls.netcon))
        {
                Con_DPrintf ("CL_WriteToServer: can't send\n");
@@ -776,7 +848,7 @@ void CL_SendCmd (void)
 }
 
 // LordHavoc: pausedemo command
-void CL_PauseDemo_f (void)
+static void CL_PauseDemo_f (void)
 {
        cls.demopaused = !cls.demopaused;
        if (cls.demopaused)
@@ -791,7 +863,7 @@ CL_PModel_f
 LordHavoc: Intended for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
 ======================
 */
-void CL_PModel_f (void)
+static void CL_PModel_f (void)
 {
        int i;
        eval_t *val;
@@ -805,7 +877,7 @@ void CL_PModel_f (void)
 
        if (cmd_source == src_command)
        {
-               if (cl_pmodel.value == i)
+               if (cl_pmodel.integer == i)
                        return;
                Cvar_SetValue ("_cl_pmodel", i);
                if (cls.state == ca_connected)
@@ -823,7 +895,7 @@ void CL_PModel_f (void)
 CL_Fog_f
 ======================
 */
-void CL_Fog_f (void)
+static void CL_Fog_f (void)
 {
        if (Cmd_Argc () == 1)
        {
@@ -842,18 +914,19 @@ CL_Init
 =================
 */
 void CL_Init (void)
-{      
-       SZ_Alloc (&cls.message, 1024);
+{
+       SZ_Alloc (&cls.message, 1024, "cls.message");
 
        CL_InitInput ();
        CL_InitTEnts ();
-       
+
 //
 // register our commands
 //
        Cvar_RegisterVariable (&cl_name);
        Cvar_RegisterVariable (&cl_color);
-       Cvar_RegisterVariable (&cl_pmodel);
+       if (gamemode == GAME_NEHAHRA)
+               Cvar_RegisterVariable (&cl_pmodel);
        Cvar_RegisterVariable (&cl_upspeed);
        Cvar_RegisterVariable (&cl_forwardspeed);
        Cvar_RegisterVariable (&cl_backspeed);
@@ -874,8 +947,9 @@ void CL_Init (void)
        Cvar_RegisterVariable (&m_forward);
        Cvar_RegisterVariable (&m_side);
 
-//     Cvar_RegisterVariable (&cl_autofire);
-       
+       Cvar_RegisterVariable (&cl_itembobspeed);
+       Cvar_RegisterVariable (&cl_itembobheight);
+
        Cmd_AddCommand ("entities", CL_PrintEntities_f);
        Cmd_AddCommand ("bitprofile", CL_BitProfile_f);
        Cmd_AddCommand ("disconnect", CL_Disconnect_f);
@@ -888,8 +962,14 @@ void CL_Init (void)
 
        // LordHavoc: added pausedemo
        Cmd_AddCommand ("pausedemo", CL_PauseDemo_f);
-       // LordHavoc: added pmodel command (like name, etc, only intended for Nehahra)
-       Cmd_AddCommand ("pmodel", CL_PModel_f);
+       if (gamemode == GAME_NEHAHRA)
+               Cmd_AddCommand ("pmodel", CL_PModel_f);
+
+       cl_scores_mempool = Mem_AllocPool("client player info");
+
+       Cvar_RegisterVariable(&cl_draweffects);
 
        CL_Parse_Init();
+       CL_Particles_Init();
+       CL_Decals_Init();
 }
index ec3d3d5..9a06ee4 100644 (file)
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -273,19 +273,17 @@ void CL_ParseEntityLump(char *entdata)
 {
        char *data;
        char key[128], value[4096];
-       char wadname[128];
        char targetnamebuffer[65536];
        char *targetname[8192], *target[MAX_STATICLIGHTS], light_target[256];
        vec3_t targetnameorigin[8192], targetnametemporigin, v;
        int targets, targetnames, targetnamebufferpos, targetnameorigintofillin;
-       int i, j, k, n;
+       int i, j, n;
        float f1, f2, f3, f4;
        float ambientlight, ambientcolor[3], sunlight, sunlightdirection[3], sunlightcolor[3];
        int light_fadetype, light_style, hlight, tyrlite, light_enable;
        float light_origin[3], light_light, light_distancescale, light_lightcolor[3], light_color[3], light_direction[3], light_cone, light_lightradius;
        FOG_clear(); // LordHavoc: no fog until set
        R_SetSkyBox(""); // LordHavoc: no environment mapped sky until set
-       r_farclip.value = 6144; // LordHavoc: default farclip distance
        r_sunlightenabled = false;
        staticlights = 0;
        data = entdata;
@@ -335,12 +333,6 @@ void CL_ParseEntityLump(char *entdata)
                        R_SetSkyBox(value);
                else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK)
                        R_SetSkyBox(value);
-               else if (!strcmp("farclip", key))
-               {
-                       r_farclip.value = atof(value);
-                       if (r_farclip.value < 64)
-                               r_farclip.value = 64;
-               }
                else if (!strcmp("fog", key))
                        scanf(value, "%f %f %f %f", &fog_density, &fog_red, &fog_green, &fog_blue);
                else if (!strcmp("fog_density", key))
@@ -351,36 +343,6 @@ void CL_ParseEntityLump(char *entdata)
                        fog_green = atof(value);
                else if (!strcmp("fog_blue", key))
                        fog_blue = atof(value);
-               else if (!strcmp("wad", key)) // for HalfLife maps
-               {
-                       if (hlbsp)
-                       {
-                               j = 0;
-                               for (i = 0;i < 4096;i++)
-                                       if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
-                                               break;
-                               if (value[i])
-                               {
-                                       for (;i < 4096;i++)
-                                       {
-                                               // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
-                                               if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
-                                                       j = i+1;
-                                               else if (value[i] == ';' || value[i] == 0)
-                                               {
-                                                       k = value[i];
-                                                       value[i] = 0;
-                                                       strcpy(wadname, "textures/");
-                                                       strcat(wadname, &value[j]);
-                                                       W_LoadTextureWadFile (wadname, false);
-                                                       j = i+1;
-                                                       if (!k)
-                                                               break;
-                                               }
-                                       }
-                               }
-                       }
-               }
                else if (!strcmp("light", key))
                        ambientlight = atof(value);
                else if (!strcmp("sunlight", key))
@@ -590,7 +552,7 @@ void CL_ParseEntityLump(char *entdata)
                        staticlights++;
                }
        }
-       if (hlbsp)
+       if (cl.worldmodel->ishlbsp)
                n = LIGHTFADE_LDIVX2;
        else if (tyrlite)
                n = LIGHTFADE_LMINUSX;
@@ -621,6 +583,56 @@ void CL_ParseEntityLump(char *entdata)
 }
 
 /*
+=====================
+CL_SignonReply
+
+An svc_signonnum has been received, perform a client side setup
+=====================
+*/
+static void CL_SignonReply (void)
+{
+       char    str[8192];
+
+Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
+
+       switch (cls.signon)
+       {
+       case 1:
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, "prespawn");
+               break;
+
+       case 2:
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
+
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, va("color %i %i\n", cl_color.integer >> 4, cl_color.integer & 15));
+
+               if (cl_pmodel.integer)
+               {
+                       MSG_WriteByte (&cls.message, clc_stringcmd);
+                       MSG_WriteString (&cls.message, va("pmodel %i\n", cl_pmodel.integer));
+               }
+
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               sprintf (str, "spawn %s", cls.spawnparms);
+               MSG_WriteString (&cls.message, str);
+               break;
+
+       case 3:
+               MSG_WriteByte (&cls.message, clc_stringcmd);
+               MSG_WriteString (&cls.message, "begin");
+               break;
+
+       case 4:
+//             SCR_EndLoadingPlaque ();                // allow normal screen updates
+               Con_ClearNotify();
+               break;
+       }
+}
+
+/*
 ==================
 CL_ParseServerInfo
 ==================
@@ -649,7 +661,7 @@ void CL_ParseServerInfo (void)
        Nehahrademcompatibility = false;
        if (i == 250)
                Nehahrademcompatibility = true;
-       if (cls.demoplayback && demo_nehahra.value)
+       if (cls.demoplayback && demo_nehahra.integer)
                Nehahrademcompatibility = true;
        dpprotocol = i == DPPROTOCOL_VERSION;
 
@@ -660,7 +672,7 @@ void CL_ParseServerInfo (void)
                Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients);
                return;
        }
-       cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores");
+       cl.scores = Mem_Alloc(cl_scores_mempool, cl.maxclients*sizeof(*cl.scores));
 
 // parse gametype
        cl.gametype = MSG_ReadByte ();
@@ -682,7 +694,11 @@ void CL_ParseServerInfo (void)
 // needlessly purge it
 //
 
-       Hunk_Check ();
+       Mem_CheckSentinelsGlobal();
+
+       Mod_ClearUsed();
+
+       Mem_CheckSentinelsGlobal();
 
 // precache models
        memset (cl.model_precache, 0, sizeof(cl.model_precache));
@@ -700,9 +716,6 @@ void CL_ParseServerInfo (void)
                        Host_Error ("Server sent a precache name of %i characters (max %i)", strlen(str), MAX_QPATH - 1);
                strcpy (model_precache[nummodels], str);
                Mod_TouchModel (str);
-
-//             Hunk_Check ();
-
        }
 
 // precache sounds
@@ -723,18 +736,20 @@ void CL_ParseServerInfo (void)
                S_TouchSound (str);
        }
 
+       Mem_CheckSentinelsGlobal();
+
+       Mod_PurgeUnused();
+
 //
 // now we try to load everything else until a cache allocation fails
 //
 
-       Hunk_Check ();
+       Mem_CheckSentinelsGlobal();
 
        for (i=1 ; i<nummodels ; i++)
        {
-               isworldmodel = i == 1; // LordHavoc: first model is the world model
-               cl.model_precache[i] = Mod_ForName (model_precache[i], false);
-
-//             Hunk_Check ();
+               // LordHavoc: i == 1 means the first model is the world model
+               cl.model_precache[i] = Mod_ForName (model_precache[i], false, true, i == 1);
 
                if (cl.model_precache[i] == NULL)
                {
@@ -744,7 +759,7 @@ void CL_ParseServerInfo (void)
                CL_KeepaliveMessage ();
        }
 
-       Hunk_Check ();
+       Mem_CheckSentinelsGlobal();
 
        S_BeginPrecaching ();
        for (i=1 ; i<numsounds ; i++)
@@ -754,15 +769,14 @@ void CL_ParseServerInfo (void)
        }
        S_EndPrecaching ();
 
-
 // local state
        cl_entities[0].render.model = cl.worldmodel = cl.model_precache[1];
-
-//     Hunk_Check ();
+       cl_entities[0].render.scale = 1;
+       cl_entities[0].render.alpha = 1;
 
        R_NewMap ();
 
-       Hunk_Check ();          // make sure nothing is hurt
+       Mem_CheckSentinelsGlobal();
 
        noclip_anglehack = false;               // noclip is turned off at start
 }
@@ -782,14 +796,15 @@ void CL_ValidateState(entity_state_t *s)
                Host_Error ("CL_ValidateState: colormap (%i) > cl.maxclients (%i)", s->colormap, cl.maxclients);
 
        model = cl.model_precache[s->modelindex];
+       Mod_CheckLoaded(model);
        if (model && s->frame >= model->numframes)
        {
-               Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\"\n", s->frame, model->name);
+               Con_Printf("CL_ValidateState: no such frame %i in \"%s\"\n", s->frame, model->name);
                s->frame = 0;
        }
-       if (model && s->skin >= model->numskins)
+       if (model && s->skin > 0 && s->skin >= model->numskins)
        {
-               Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\"\n", s->skin, model->name);
+               Con_Printf("CL_ValidateState: no such skin %i in \"%s\"\n", s->skin, model->name);
                s->skin = 0;
        }
 }
@@ -809,6 +824,7 @@ void CL_ParseUpdate (int bits)
 {
        int i, num, deltadie;
        entity_t *ent;
+       entity_state_t new;
 
        if (cls.signon == SIGNONS - 1)
        {       // first update is the final signon stage
@@ -825,7 +841,7 @@ void CL_ParseUpdate (int bits)
                        bits |= MSG_ReadByte() << 24;
        }
 
-       if (bits & U_LONGENTITY)        
+       if (bits & U_LONGENTITY)
                num = (unsigned) MSG_ReadShort ();
        else
                num = (unsigned) MSG_ReadByte ();
@@ -845,43 +861,48 @@ void CL_ParseUpdate (int bits)
                        bitprofile[i]++;
        bitprofilecount++;
 
-       ent->state_previous = ent->state_current;
        deltadie = false;
        if (bits & U_DELTA)
        {
-               if (!ent->state_current.active)
+               new = ent->state_current;
+               if (!new.active)
                        deltadie = true; // was not present in previous frame, leave hidden until next full update
        }
        else
-               ent->state_current = ent->state_baseline;
-
-       ent->state_current.time = cl.mtime[0];
-
-       ent->state_current.flags = 0;
-       ent->state_current.active = true;
-       if (bits & U_MODEL)             ent->state_current.modelindex = (ent->state_current.modelindex & 0xFF00) | MSG_ReadByte();
-       if (bits & U_FRAME)             ent->state_current.frame = (ent->state_current.frame & 0xFF00) | MSG_ReadByte();
-       if (bits & U_COLORMAP)  ent->state_current.colormap = MSG_ReadByte();
-       if (bits & U_SKIN)              ent->state_current.skin = MSG_ReadByte();
-       if (bits & U_EFFECTS)   ent->state_current.effects = (ent->state_current.effects & 0xFF00) | MSG_ReadByte();
-       if (bits & U_ORIGIN1)   ent->state_current.origin[0] = MSG_ReadCoord();
-       if (bits & U_ANGLE1)    ent->state_current.angles[0] = MSG_ReadAngle();
-       if (bits & U_ORIGIN2)   ent->state_current.origin[1] = MSG_ReadCoord();
-       if (bits & U_ANGLE2)    ent->state_current.angles[1] = MSG_ReadAngle();
-       if (bits & U_ORIGIN3)   ent->state_current.origin[2] = MSG_ReadCoord();
-       if (bits & U_ANGLE3)    ent->state_current.angles[2] = MSG_ReadAngle();
-       if (bits & U_STEP)              ent->state_current.flags |= RENDER_STEP;
-       if (bits & U_ALPHA)             ent->state_current.alpha = MSG_ReadByte();
-       if (bits & U_SCALE)             ent->state_current.scale = MSG_ReadByte();
-       if (bits & U_EFFECTS2)  ent->state_current.effects = (ent->state_current.effects & 0x00FF) | (MSG_ReadByte() << 8);
-       if (bits & U_GLOWSIZE)  ent->state_current.glowsize = MSG_ReadByte();
-       if (bits & U_GLOWCOLOR) ent->state_current.glowcolor = MSG_ReadByte();
-       if (bits & U_GLOWTRAIL) ent->state_current.flags |= RENDER_GLOWTRAIL;
-       if (bits & U_COLORMOD)  ent->state_current.colormod = MSG_ReadByte();
-       if (bits & U_FRAME2)    ent->state_current.frame = (ent->state_current.frame & 0x00FF) | (MSG_ReadByte() << 8);
-       if (bits & U_MODEL2)    ent->state_current.modelindex = (ent->state_current.modelindex & 0x00FF) | (MSG_ReadByte() << 8);
-       if (bits & U_VIEWMODEL) ent->state_current.flags |= RENDER_VIEWMODEL;
-       if (bits & U_EXTERIORMODEL)     ent->state_current.flags |= RENDER_EXTERIORMODEL;
+               new = ent->state_baseline;
+
+       new.time = cl.mtime[0];
+
+       new.flags = 0;
+       new.active = true;
+       if (bits & U_MODEL)             new.modelindex = (new.modelindex & 0xFF00) | MSG_ReadByte();
+       if (bits & U_FRAME)             new.frame = (new.frame & 0xFF00) | MSG_ReadByte();
+       if (bits & U_COLORMAP)  new.colormap = MSG_ReadByte();
+       if (bits & U_SKIN)              new.skin = MSG_ReadByte();
+       if (bits & U_EFFECTS)   new.effects = (new.effects & 0xFF00) | MSG_ReadByte();
+       if (bits & U_ORIGIN1)   new.origin[0] = MSG_ReadCoord();
+       if (bits & U_ANGLE1)    new.angles[0] = MSG_ReadAngle();
+       if (bits & U_ORIGIN2)   new.origin[1] = MSG_ReadCoord();
+       if (bits & U_ANGLE2)    new.angles[1] = MSG_ReadAngle();
+       if (bits & U_ORIGIN3)   new.origin[2] = MSG_ReadCoord();
+       if (bits & U_ANGLE3)    new.angles[2] = MSG_ReadAngle();
+       if (bits & U_STEP)              new.flags |= RENDER_STEP;
+       if (bits & U_ALPHA)             new.alpha = MSG_ReadByte();
+       if (bits & U_SCALE)             new.scale = MSG_ReadByte();
+       if (bits & U_EFFECTS2)  new.effects = (new.effects & 0x00FF) | (MSG_ReadByte() << 8);
+       if (bits & U_GLOWSIZE)  new.glowsize = MSG_ReadByte();
+       if (bits & U_GLOWCOLOR) new.glowcolor = MSG_ReadByte();
+#if 0
+       if (bits & U_COLORMOD)  {int i = MSG_ReadByte();float r = (((int) i >> 5) & 7) * 1.0 / 7, g = (((int) i >> 2) & 7) * 1.0 / 7, b = ((int) i & 3) * 1.0 / 3;Con_Printf("warning: U_COLORMOD %i (%1.2f %1.2f %1.2f) ignored\n", i, r, g, b);}
+#else
+       // apparently the dpcrush demo uses this (unintended, and it uses white anyway)
+       if (bits & U_COLORMOD)  MSG_ReadByte();
+#endif
+       if (bits & U_GLOWTRAIL) new.flags |= RENDER_GLOWTRAIL;
+       if (bits & U_FRAME2)    new.frame = (new.frame & 0x00FF) | (MSG_ReadByte() << 8);
+       if (bits & U_MODEL2)    new.modelindex = (new.modelindex & 0x00FF) | (MSG_ReadByte() << 8);
+       if (bits & U_VIEWMODEL) new.flags |= RENDER_VIEWMODEL;
+       if (bits & U_EXTERIORMODEL)     new.flags |= RENDER_EXTERIORMODEL;
 
        // LordHavoc: to allow playback of the Nehahra movie
        if (Nehahrademcompatibility && (bits & U_EXTEND1))
@@ -892,44 +913,42 @@ void CL_ParseUpdate (int bits)
                if (i == 2)
                {
                        if (MSG_ReadFloat())
-                               ent->state_current.effects |= EF_FULLBRIGHT;
+                               new.effects |= EF_FULLBRIGHT;
                }
                if (j < 0)
-                       ent->state_current.alpha = 0;
+                       new.alpha = 0;
                else if (j == 0 || j >= 255)
-                       ent->state_current.alpha = 255;
+                       new.alpha = 255;
                else
-                       ent->state_current.alpha = j;
+                       new.alpha = j;
        }
 
        if (deltadie)
        {
                // hide the entity
-               ent->state_current.active = false;
+               new.active = false;
        }
        else
-       {
-               CL_ValidateState(&ent->state_current);
+               CL_ValidateState(&new);
 
-               /*
-               if (!ent->state_current.active)
+       if (new.flags & RENDER_STEP) // FIXME: rename this flag?
+       {
+               // make time identical for memcmp
+               new.time = ent->state_current.time;
+               if (memcmp(&new, &ent->state_current, sizeof(entity_state_t)))
                {
-                       if (bits & U_DELTA)
-                       {
-                               if (bits & U_MODEL)
-                                       Con_Printf("CL_ParseUpdate: delta NULL model on %i: %i %i\n", num, ent->state_previous.modelindex, ent->state_current.modelindex);
-                               else
-                                       Con_Printf("CL_ParseUpdate: delta NULL model on %i: %i\n", num, ent->state_previous.modelindex);
-                       }
-                       else
-                       {
-                               if (bits & U_MODEL)
-                                       Con_Printf("CL_ParseUpdate:       NULL model on %i: %i %i\n", num, ent->state_baseline.modelindex, ent->state_current.modelindex);
-                               else
-                                       Con_Printf("CL_ParseUpdate:       NULL model on %i: %i\n", num, ent->state_baseline.modelindex);
-                       }
+                       // state has changed
+                       ent->state_previous = ent->state_current;
+                       ent->state_current = new;
+                       // assume 10fps animation
+                       ent->state_previous.time = cl.mtime[0];
+                       ent->state_current.time = cl.mtime[0] + 0.1; //ent->state_previous.time + 0.1;
                }
-               */
+       }
+       else
+       {
+               ent->state_previous = ent->state_current;
+               ent->state_current = new;
        }
 }
 
@@ -957,7 +976,7 @@ char *bitprofilenames[32] =
        "U_EFFECTS2",
        "U_GLOWSIZE",
        "U_GLOWCOLOR",
-       "U_COLORMOD",
+       "obsolete U_COLORMOD",
        "U_EXTEND2",
        "U_GLOWTRAIL",
        "U_VIEWMODEL",
@@ -1028,7 +1047,6 @@ void CL_ParseBaseline (entity_t *ent, int large)
        ent->state_baseline.scale = 16;
        ent->state_baseline.glowsize = 0;
        ent->state_baseline.glowcolor = 254;
-       ent->state_baseline.colormod = 255;
        ent->state_previous = ent->state_current = ent->state_baseline;
 
        CL_ValidateState(&ent->state_baseline);
@@ -1141,7 +1159,6 @@ void CL_ParseStatic (int large)
        ent->render.alpha = 1;
        ent->render.scale = 1;
        ent->render.alpha = 1;
-       ent->render.colormod[0] = ent->render.colormod[1] = ent->render.colormod[2] = 1;
 
        VectorCopy (ent->state_baseline.origin, ent->render.origin);
        VectorCopy (ent->state_baseline.angles, ent->render.angles);    
@@ -1197,7 +1214,7 @@ void CL_ParseEffect2 (void)
 }
 
 
-#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
+#define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
 
 /*
 =====================
@@ -1215,9 +1232,9 @@ void CL_ParseServerMessage (void)
 //
 // if recording demos, copy the message out
 //
-       if (cl_shownet.value == 1)
+       if (cl_shownet.integer == 1)
                Con_Printf ("%i ",net_message.cursize);
-       else if (cl_shownet.value == 2)
+       else if (cl_shownet.integer == 2)
                Con_Printf ("------------------\n");
        
        cl.onground = false;    // unless the server says otherwise     
@@ -1303,7 +1320,7 @@ void CL_ParseServerMessage (void)
                        cl.mtime[1] = cl.mtime[0];
                        cl.mtime[0] = MSG_ReadFloat ();                 
                        break;
-                       
+
                case svc_clientdata:
                        i = MSG_ReadShort ();
                        CL_ParseClientdata (i);
@@ -1316,7 +1333,7 @@ void CL_ParseServerMessage (void)
                        Nehahrademcompatibility = false;
                        if (i == 250)
                                Nehahrademcompatibility = true;
-                       if (cls.demoplayback && demo_nehahra.value)
+                       if (cls.demoplayback && demo_nehahra.integer)
                                Nehahrademcompatibility = true;
                        dpprotocol = i == DPPROTOCOL_VERSION;
                        break;
@@ -1335,11 +1352,11 @@ void CL_ParseServerMessage (void)
                case svc_stufftext:
                        Cbuf_AddText (MSG_ReadString ());
                        break;
-                       
+
                case svc_damage:
                        V_ParseDamage ();
                        break;
-                       
+
                case svc_serverinfo:
                        CL_ParseServerInfo ();
 //                     vid.recalc_refdef = true;       // leave intermission full screen
@@ -1375,7 +1392,7 @@ void CL_ParseServerMessage (void)
                        i = MSG_ReadShort();
                        S_StopSound(i>>3, i&7);
                        break;
-               
+
                case svc_updatename:
                        i = MSG_ReadByte ();
                        if (i >= cl.maxclients)
@@ -1398,7 +1415,7 @@ void CL_ParseServerMessage (void)
                        break;
                        
                case svc_particle:
-                       R_ParseParticleEffect ();
+                       CL_ParseParticleEffect ();
                        break;
 
                case svc_effect:
@@ -1487,7 +1504,7 @@ void CL_ParseServerMessage (void)
                        cl.intermission = 2;
                        cl.completed_time = cl.time;
 //                     vid.recalc_refdef = true;       // go to full screen
-                       SCR_CenterPrint (MSG_ReadString ());                    
+                       SCR_CenterPrint (MSG_ReadString ());
                        break;
 
                case svc_cutscene:
diff --git a/cl_particles.c b/cl_particles.c
new file mode 100644 (file)
index 0000000..12fe619
--- /dev/null
@@ -0,0 +1,1181 @@
+/*
+Copyright (C) 1996-1997 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#include "quakedef.h"
+
+#define MAX_PARTICLES                  16384   // default max # of particles at one time
+#define ABSOLUTE_MIN_PARTICLES 512             // no fewer than this no matter what's on the command line
+
+typedef enum
+{
+       pt_static, pt_grav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_spark, pt_bubble, pt_fade, pt_steam, pt_splash, pt_splashpuff, pt_flame, pt_blood, pt_oneframe, pt_lavasplash, pt_raindropsplash, pt_underwaterspark, pt_explosionsplash
+}
+ptype_t;
+
+typedef struct particle_s
+{
+       ptype_t         type;
+       vec3_t          org;
+       vec3_t          vel;
+       int                     tex;
+       float           die;
+       float           scale;
+       float           alpha; // 0-255
+       float           time2; // used for various things (snow fluttering, for example)
+       float           bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
+       vec3_t          oldorg;
+       vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
+       float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
+       float           pressure; // if non-zero, apply pressure to other particles
+       int                     dynlight; // if set the particle will be dynamically lit (if cl_dynamicparticles is on), used for smoke and blood
+       byte            color[4];
+}
+particle_t;
+
+static int particlepalette[256] =
+{
+       0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
+       0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
+       0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
+       0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
+       0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
+       0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
+       0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
+       0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
+       0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
+       0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
+       0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
+       0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
+       0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
+       0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
+       0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
+       0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
+       0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
+       0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
+       0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
+       0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
+       0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
+       0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
+       0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
+       0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
+       0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
+       0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
+       0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
+       0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
+       0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
+       0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
+       0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
+       0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
+};
+
+static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
+//static int explounderwatersparkramp[8] = {0x00074b, 0x000f6f, 0x071f93, 0x0f33b7, 0x2b63cf, 0x4f97e3, 0xb5e7ff, 0xffffff};
+
+// these must match r_part.c's textures
+static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+static const int tex_bullethole[8] = {8, 9, 10, 11, 12, 13, 14, 15};
+static const int tex_rainsplash[16] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+static const int tex_particle = 32;
+static const int tex_rain = 33;
+static const int tex_bubble = 34;
+static const int tex_rocketglow = 35;
+
+static int                     cl_maxparticles;
+static int                     cl_numparticles;
+static particle_t      *particles;
+static particle_t      **freeparticles; // list used only in compacting particles array
+static renderparticle_t        *cl_renderparticles;
+
+static cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
+static cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
+static cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
+static cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
+static cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
+static cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
+static cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
+static cvar_t cl_particles_explosions = {CVAR_SAVE, "cl_particles_explosions", "0"};
+
+static mempool_t *cl_part_mempool;
+
+void CL_Particles_Clear(void)
+{
+       cl_numparticles = 0;
+}
+
+/*
+===============
+CL_InitParticles
+===============
+*/
+void CL_ReadPointFile_f (void);
+void CL_Particles_Init (void)
+{
+       int             i;
+
+       i = COM_CheckParm ("-particles");
+
+       if (i)
+       {
+               cl_maxparticles = (int)(atoi(com_argv[i+1]));
+               if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
+                       cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
+       }
+       else
+               cl_maxparticles = MAX_PARTICLES;
+
+       Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
+
+       Cvar_RegisterVariable (&cl_particles);
+       Cvar_RegisterVariable (&cl_particles_size);
+       Cvar_RegisterVariable (&cl_particles_bloodshowers);
+       Cvar_RegisterVariable (&cl_particles_blood);
+       Cvar_RegisterVariable (&cl_particles_smoke);
+       Cvar_RegisterVariable (&cl_particles_sparks);
+       Cvar_RegisterVariable (&cl_particles_bubbles);
+       Cvar_RegisterVariable (&cl_particles_explosions);
+
+       cl_part_mempool = Mem_AllocPool("CL_Part");
+       particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
+       freeparticles = (void *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t *));
+       cl_numparticles = 0;
+
+       // FIXME: r_refdef stuff should be allocated somewhere else?
+       r_refdef.particles = cl_renderparticles = Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(renderparticle_t));
+}
+
+#define particle(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2, pfriction, ppressure)\
+{\
+       particle_t      *part;\
+       int tempcolor;\
+       if (cl_numparticles >= cl_maxparticles)\
+               return;\
+       part = &particles[cl_numparticles++];\
+       part->type = (ptype);\
+       tempcolor = (pcolor);\
+       part->color[0] = ((tempcolor) >> 16) & 0xFF;\
+       part->color[1] = ((tempcolor) >> 8) & 0xFF;\
+       part->color[2] = (tempcolor) & 0xFF;\
+       part->color[3] = 0xFF;\
+       part->tex = (ptex);\
+       part->dynlight = (plight);\
+       part->scale = (pscale);\
+       part->alpha = (palpha);\
+       part->die = cl.time + (ptime);\
+       part->bounce = (pbounce);\
+       part->org[0] = (px);\
+       part->org[1] = (py);\
+       part->org[2] = (pz);\
+       part->vel[0] = (pvx);\
+       part->vel[1] = (pvy);\
+       part->vel[2] = (pvz);\
+       part->time2 = (ptime2);\
+       part->vel2[0] = (pvx2);\
+       part->vel2[1] = (pvy2);\
+       part->vel2[2] = (pvz2);\
+       part->friction = (pfriction);\
+       part->pressure = (ppressure);\
+}
+
+/*
+===============
+CL_EntityParticles
+===============
+*/
+void CL_EntityParticles (entity_t *ent)
+{
+       int                     i;
+       float           angle;
+       float           sp, sy, cp, cy;
+       vec3_t          forward;
+       float           dist;
+       float           beamlength;
+       static vec3_t avelocities[NUMVERTEXNORMALS];
+       if (!cl_particles.integer) return;
+
+       dist = 64;
+       beamlength = 16;
+
+       if (!avelocities[0][0])
+               for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
+                       avelocities[0][i] = (rand()&255) * 0.01;
+
+       for (i=0 ; i<NUMVERTEXNORMALS ; i++)
+       {
+               angle = cl.time * avelocities[i][0];
+               sy = sin(angle);
+               cy = cos(angle);
+               angle = cl.time * avelocities[i][1];
+               sp = sin(angle);
+               cp = cos(angle);
+
+               forward[0] = cp*cy;
+               forward[1] = cp*sy;
+               forward[2] = -sp;
+
+               particle(pt_oneframe, particlepalette[0x6f], tex_particle, false, 2, 255, 9999, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+       }
+}
+
+
+void CL_ReadPointFile_f (void)
+{
+       vec3_t  org;
+       int             r, c;
+       char    *pointfile, *pointfilepos, *t, tchar;
+
+       pointfile = COM_LoadFile(va("maps/%s.pts", sv.name), true);
+       if (!pointfile)
+       {
+               Con_Printf ("couldn't open %s.pts\n", sv.name);
+               return;
+       }
+
+       Con_Printf ("Reading %s.pts...\n", sv.name);
+       c = 0;
+       pointfilepos = pointfile;
+       while (*pointfilepos)
+       {
+               while (*pointfilepos == '\n' || *pointfilepos == '\r')
+                       pointfilepos++;
+               if (!*pointfilepos)
+                       break;
+               t = pointfilepos;
+               while (*t && *t != '\n' && *t != '\r')
+                       t++;
+               tchar = *t;
+               *t = 0;
+               r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
+               *t = tchar;
+               pointfilepos = t;
+               if (r != 3)
+                       break;
+               c++;
+
+               if (cl_numparticles >= cl_maxparticles)
+               {
+                       Con_Printf ("Not enough free particles\n");
+                       break;
+               }
+               particle(pt_static, particlepalette[(-c)&15], tex_particle, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+       }
+
+       Mem_Free(pointfile);
+       Con_Printf ("%i points read\n", c);
+}
+
+/*
+===============
+CL_ParseParticleEffect
+
+Parse an effect out of the server message
+===============
+*/
+void CL_ParseParticleEffect (void)
+{
+       vec3_t          org, dir;
+       int                     i, count, msgcount, color;
+
+       for (i=0 ; i<3 ; i++)
+               org[i] = MSG_ReadCoord ();
+       for (i=0 ; i<3 ; i++)
+               dir[i] = MSG_ReadChar () * (1.0/16);
+       msgcount = MSG_ReadByte ();
+       color = MSG_ReadByte ();
+
+       if (msgcount == 255)
+               count = 1024;
+       else
+               count = msgcount;
+
+       CL_RunParticleEffect (org, dir, color, count);
+}
+
+/*
+===============
+CL_ParticleExplosion
+
+===============
+*/
+void CL_ParticleExplosion (vec3_t org, int smoke)
+{
+       int i, j;
+       float f;
+       vec3_t v, end, ang;
+       byte noise1[32*32], noise2[32*32];
+
+       if (cl_particles.integer && cl_particles_explosions.integer)
+       {
+               i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
+               if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
+               {
+                       for (i = 0;i < 128;i++)
+                               particle(pt_bubble, 0xFFFFFF, tex_bubble, false, lhrandom(1, 2), 255, 9999, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), 0, 0, 0, 0, 0, 0);
+
+                       ang[2] = lhrandom(0, 360);
+                       fractalnoise(noise1, 32, 4);
+                       fractalnoise(noise2, 32, 8);
+                       for (i = 0;i < 32;i++)
+                       {
+                               for (j = 0;j < 32;j++)
+                               {
+                                       VectorRandom(v);
+                                       VectorMA(org, 16, v, v);
+                                       TraceLine(org, v, end, NULL, 0);
+                                       ang[0] = (j + 0.5f) * (360.0f / 32.0f);
+                                       ang[1] = (i + 0.5f) * (360.0f / 32.0f);
+                                       AngleVectors(ang, v, NULL, NULL);
+                                       f = noise1[j*32+i] * 1.5f;
+                                       VectorScale(v, f, v);
+                                       particle(pt_underwaterspark, noise2[j*32+i] * 0x010101, tex_smoke[rand()&7], false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
+                                       VectorScale(v, 0.75, v);
+                                       particle(pt_underwaterspark, explosparkramp[(noise2[j*32+i] >> 5)], tex_particle, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
+                               }
+                       }
+               }
+               else
+               {
+                       ang[2] = lhrandom(0, 360);
+                       fractalnoise(noise1, 32, 4);
+                       fractalnoise(noise2, 32, 8);
+                       for (i = 0;i < 32;i++)
+                       {
+                               for (j = 0;j < 32;j++)
+                               {
+                                       VectorRandom(v);
+                                       VectorMA(org, 16, v, v);
+                                       TraceLine(org, v, end, NULL, 0);
+                                       ang[0] = (j + 0.5f) * (360.0f / 32.0f);
+                                       ang[1] = (i + 0.5f) * (360.0f / 32.0f);
+                                       AngleVectors(ang, v, NULL, NULL);
+                                       f = noise1[j*32+i] * 1.5f;
+                                       VectorScale(v, f, v);
+                                       particle(pt_spark, noise2[j*32+i] * 0x010101, tex_smoke[random()&7], false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
+                                       VectorScale(v, 0.75, v);
+                                       particle(pt_spark, explosparkramp[(noise2[j*32+i] >> 5)], tex_particle, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
+                               //      VectorRandom(v);
+                               //      VectorScale(v, 384, v);
+                               //      particle(pt_spark, explosparkramp[rand()&7], tex_particle, false, 2, lhrandom(16, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
+                               }
+                       }
+               }
+       }
+       else
+       {
+               R_NewExplosion(org);
+
+               for (i = 0;i < 256;i++)
+               {
+                       VectorRandom(v);
+                       particle(pt_spark, explosparkramp[rand()&7], tex_particle, false, 2, lhrandom(16, 255), 9999, 1.5, end[0], end[1], end[2], v[0] * 384.0f, v[1] * 384.0f, v[2] * 384.0f + 160.0f, 512.0f, 0, 0, 0, 2, 0);
+               }
+       }
+}
+
+/*
+===============
+CL_ParticleExplosion2
+
+===============
+*/
+void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
+{
+       int                     i;
+       if (!cl_particles.integer) return;
+
+       for (i = 0;i < 512;i++)
+               particle(pt_fade, particlepalette[colorStart + (i % colorLength)], tex_particle, false, 1.5, 255, 0.3, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 0.1f, 0);
+}
+
+/*
+===============
+CL_BlobExplosion
+
+===============
+*/
+void CL_BlobExplosion (vec3_t org)
+{
+       int                     i;
+       if (!cl_particles.integer) return;
+
+       for (i = 0;i < 256;i++)
+               particle(pt_blob , particlepalette[ 66+(rand()%6)], tex_particle, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
+       for (i = 0;i < 256;i++)
+               particle(pt_blob2, particlepalette[150+(rand()%6)], tex_particle, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
+}
+
+/*
+===============
+CL_RunParticleEffect
+
+===============
+*/
+void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
+{
+       if (!cl_particles.integer) return;
+
+       if (count == 1024)
+       {
+               CL_ParticleExplosion(org, false);
+               return;
+       }
+       while (count--)
+               particle(pt_fade, particlepalette[color + (rand()&7)], tex_particle, false, 1, 128, 9999, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-15, 15), lhrandom(-15, 15), lhrandom(-15, 15), 0, 0, 0, 0, 0, 0);
+}
+
+// LordHavoc: added this for spawning sparks/dust (which have strong gravity)
+/*
+===============
+CL_SparkShower
+===============
+*/
+void CL_SparkShower (vec3_t org, vec3_t dir, int count)
+{
+       if (!cl_particles.integer) return;
+
+       CL_Decal(org, tex_bullethole[rand()&7], 16 * cl_particles_size.value, 0, 0, 0, 1);
+
+       // smoke puff
+       if (cl_particles_smoke.integer)
+               particle(pt_bulletsmoke, 0xA0A0A0, tex_smoke[rand()&7], true, 5, 255, 9999, 0, org[0], org[1], org[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 0, 0, 0, 0, 0, 0);
+
+       if (cl_particles_sparks.integer)
+       {
+               // sparks
+               while(count--)
+                       particle(pt_spark, particlepalette[0x68 + (rand() & 7)], tex_particle, false, 1, lhrandom(0, 255), 9999, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(0, 128), 512.0f, 0, 0, 0, 0.2f, 0);
+       }
+}
+
+void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
+{
+       // bloodcount is used to accumulate counts too small to cause a blood particle
+       static int bloodcount = 0;
+       if (!cl_particles.integer) return;
+       if (!cl_particles_blood.integer) return;
+
+       if (count > 100)
+               count = 100;
+       bloodcount += count;
+       while(bloodcount >= 10)
+       {
+               particle(pt_blood, 0x300000, tex_smoke[rand()&7], true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
+               bloodcount -= 10;
+       }
+}
+
+void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
+{
+       vec3_t diff, center, velscale;
+       if (!cl_particles.integer) return;
+       if (!cl_particles_bloodshowers.integer) return;
+       if (!cl_particles_blood.integer) return;
+
+       VectorSubtract(maxs, mins, diff);
+       center[0] = (mins[0] + maxs[0]) * 0.5;
+       center[1] = (mins[1] + maxs[1]) * 0.5;
+       center[2] = (mins[2] + maxs[2]) * 0.5;
+       // FIXME: change velspeed back to 2.0x after fixing mod
+       velscale[0] = velspeed * 2.0 / diff[0];
+       velscale[1] = velspeed * 2.0 / diff[1];
+       velscale[2] = velspeed * 2.0 / diff[2];
+
+       while (count--)
+       {
+               vec3_t org, vel;
+               org[0] = lhrandom(mins[0], maxs[0]);
+               org[1] = lhrandom(mins[1], maxs[1]);
+               org[2] = lhrandom(mins[2], maxs[2]);
+               vel[0] = (org[0] - center[0]) * velscale[0];
+               vel[1] = (org[1] - center[1]) * velscale[1];
+               vel[2] = (org[2] - center[2]) * velscale[2];
+               particle(pt_blood, 0x300000, tex_smoke[rand()&7], true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1.0f, 0);
+       }
+}
+
+void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
+{
+       float           t;
+       if (!cl_particles.integer) return;
+       if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
+       if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
+       if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
+
+       while (count--)
+               particle(gravity ? pt_grav : pt_static, particlepalette[colorbase + (rand()&3)], tex_particle, false, 2, 255, lhrandom(1, 2), 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0, 0, 0, 0, 0, 0);
+}
+
+void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
+{
+       vec3_t          vel;
+       float           t, z;
+       if (!cl_particles.integer) return;
+       if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
+       if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
+       if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
+       if (dir[2] < 0) // falling
+       {
+               t = (maxs[2] - mins[2]) / -dir[2];
+               z = maxs[2];
+       }
+       else // rising??
+       {
+               t = (maxs[2] - mins[2]) / dir[2];
+               z = mins[2];
+       }
+       if (t < 0 || t > 2) // sanity check
+               t = 2;
+
+       switch(type)
+       {
+       case 0:
+               while(count--)
+               {
+                       vel[0] = dir[0] + lhrandom(-16, 16);
+                       vel[1] = dir[1] + lhrandom(-16, 16);
+                       vel[2] = dir[2] + lhrandom(-32, 32);
+                       particle(pt_rain, particlepalette[colorbase + (rand()&3)], tex_rain, true, 3, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
+               }
+               break;
+       case 1:
+               while(count--)
+               {
+                       vel[0] = dir[0] + lhrandom(-16, 16);
+                       vel[1] = dir[1] + lhrandom(-16, 16);
+                       vel[2] = dir[2] + lhrandom(-32, 32);
+                       particle(pt_snow, particlepalette[colorbase + (rand()&3)], tex_particle, false, 2, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
+               }
+               break;
+       default:
+               Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
+       }
+}
+
+void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
+{
+       float           t;
+       if (!cl_particles.integer) return;
+       if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
+       if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
+       if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
+
+       while (count--)
+               particle(pt_flame, particlepalette[224 + (rand()&15)], tex_particle, false, 8, 255, 9999, 1.1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 64), 0, 0, 0, 0, 0.1f, 0);
+}
+
+void CL_Flames (vec3_t org, vec3_t vel, int count)
+{
+       if (!cl_particles.integer) return;
+
+       while (count--)
+               particle(pt_flame, particlepalette[224 + (rand()&15)], tex_particle, false, 8, 255, 9999, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 0, 0, 0, 0, 0.1f, 0);
+}
+
+
+
+/*
+===============
+CL_LavaSplash
+
+===============
+*/
+void CL_LavaSplash (vec3_t origin)
+{
+       int                     i, j;
+       float           vel;
+       vec3_t          dir, org;
+       if (!cl_particles.integer) return;
+
+       for (i=-128 ; i<128 ; i+=16)
+       {
+               for (j=-128 ; j<128 ; j+=16)
+               {
+                       dir[0] = j + lhrandom(0, 8);
+                       dir[1] = i + lhrandom(0, 8);
+                       dir[2] = 256;
+                       org[0] = origin[0] + dir[0];
+                       org[1] = origin[1] + dir[1];
+                       org[2] = origin[2] + lhrandom(0, 64);
+                       vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
+                       particle(pt_lavasplash, particlepalette[224 + (rand()&7)], tex_particle, false, 7, 255, 9999, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
+               }
+       }
+}
+
+/*
+===============
+CL_TeleportSplash
+
+===============
+*/
+void CL_TeleportSplash (vec3_t org)
+{
+       int                     i, j, k;
+       if (!cl_particles.integer) return;
+
+       for (i=-16 ; i<16 ; i+=8)
+               for (j=-16 ; j<16 ; j+=8)
+                       for (k=-24 ; k<32 ; k+=8)
+                               particle(pt_fade, 0xFFFFFF, tex_particle, false, 1, lhrandom(64, 128), 9999, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), i*2 + lhrandom(-12.5, 12.5), j*2 + lhrandom(-12.5, 12.5), k*2 + lhrandom(27.5, 52.5), 0, 0, 0, 0, 0.1f, -512.0f);
+}
+
+void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
+{
+       vec3_t          vec, dir, vel;
+       float           len, dec = 0, speed;
+       int                     contents, bubbles;
+       double          t;
+       if (!cl_particles.integer) return;
+
+       VectorSubtract(end, start, dir);
+       VectorNormalize(dir);
+
+       if (type == 0 && host_frametime != 0) // rocket glow
+               particle(pt_oneframe, 0xFFFFFF, tex_rocketglow, false, 24, 255, 9999, 0, end[0] - 12 * dir[0], end[1] - 12 * dir[1], end[2] - 12 * dir[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+       t = ent->persistent.trail_time;
+       if (t >= cl.time)
+               return; // no particles to spawn this frame (sparse trail)
+
+       if (t < cl.oldtime)
+               t = cl.oldtime;
+
+       VectorSubtract (end, start, vec);
+       len = VectorNormalizeLength (vec);
+       if (len <= 0.01f)
+       {
+               // advance the trail time
+               ent->persistent.trail_time = cl.time;
+               return;
+       }
+       speed = len / (cl.time - cl.oldtime);
+       VectorScale(vec, speed, vel);
+
+       // advance into this frame to reach the first puff location
+       dec = t - cl.oldtime;
+       dec *= speed;
+       VectorMA(start, dec, vec, start);
+
+       contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
+       if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
+       {
+               // advance the trail time
+               ent->persistent.trail_time = cl.time;
+               return;
+       }
+
+       bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
+
+       while (t < cl.time)
+       {
+               switch (type)
+               {
+                       case 0: // rocket trail
+                               if (!cl_particles_smoke.integer)
+                                       dec = cl.time - t;
+                               else if (bubbles && cl_particles_bubbles.integer)
+                               {
+                                       dec = 0.005f;
+                                       particle(pt_bubble, 0xFFFFFF, tex_bubble, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_bubble, 0xFFFFFF, tex_bubble, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_smoke, 0xFFFFFF, tex_smoke[rand()&7], false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               }
+                               else
+                               {
+                                       dec = 0.005f;
+                                       particle(pt_smoke, 0xC0C0C0, tex_smoke[rand()&7], true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                                       //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], tex_particle, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
+                                       //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], tex_particle, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
+                                       //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], tex_particle, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
+                                       //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], tex_particle, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
+                               }
+                               break;
+
+                       case 1: // grenade trail
+                               // FIXME: make it gradually stop smoking
+                               if (!cl_particles_smoke.integer)
+                                       dec = cl.time - t;
+                               else if (bubbles && cl_particles_bubbles.integer)
+                               {
+                                       dec = 0.02f;
+                                       particle(pt_bubble, 0xFFFFFF, tex_bubble, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_bubble, 0xFFFFFF, tex_bubble, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
+                                       particle(pt_smoke, 0xFFFFFF, tex_smoke[rand()&7], false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               }
+                               else
+                               {
+                                       dec = 0.02f;
+                                       particle(pt_smoke, 0x808080, tex_smoke[rand()&7], true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               }
+                               break;
+
+
+                       case 2: // blood
+                               if (!cl_particles_blood.integer)
+                                       dec = cl.time - t;
+                               else
+                               {
+                                       dec = 0.1f;
+                                       particle(pt_blood, 0x300000, tex_smoke[rand()&7], true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
+                               }
+                               break;
+
+                       case 4: // slight blood
+                               if (!cl_particles_blood.integer)
+                                       dec = cl.time - t;
+                               else
+                               {
+                                       dec = 0.15f;
+                                       particle(pt_blood, 0x300000, tex_smoke[rand()&7], true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
+                               }
+                               break;
+
+                       case 3: // green tracer
+                               dec = 0.02f;
+                               particle(pt_fade, 0x373707, tex_smoke[rand()&7], false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               break;
+
+                       case 5: // flame tracer
+                               dec = 0.02f;
+                               particle(pt_fade, 0xCF632B, tex_smoke[rand()&7], false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               break;
+
+                       case 6: // voor trail
+                               dec = 0.05f; // sparse trail
+                               particle(pt_fade, 0x47232B, tex_smoke[rand()&7], false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               break;
+
+                       case 7: // Nehahra smoke tracer
+                               if (!cl_particles_smoke.integer)
+                                       dec = cl.time - t;
+                               else
+                               {
+                                       dec = 0.14f;
+                                       particle(pt_smoke, 0xC0C0C0, tex_smoke[rand()&7], true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+                               }
+                               break;
+               }
+
+               // advance to next time and position
+               t += dec;
+               dec *= speed;
+               VectorMA (start, dec, vec, start);
+       }
+       ent->persistent.trail_time = t;
+}
+
+void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
+{
+       vec3_t          vec;
+       int                     len;
+       if (!cl_particles.integer) return;
+       if (!cl_particles_smoke.integer) return;
+
+       VectorSubtract (end, start, vec);
+       len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
+       VectorScale(vec, 3, vec);
+       color = particlepalette[color];
+       while (len--)
+       {
+               particle(pt_smoke, color, tex_particle, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
+               VectorAdd (start, vec, start);
+       }
+}
+
+
+/*
+===============
+CL_MoveParticles
+===============
+*/
+void CL_MoveParticles (void)
+{
+       particle_t *p;
+       renderparticle_t *r;
+       int i, activeparticles, maxparticle, j, a, b, pressureused = false;
+       float gravity, dvel, frametime, f, dist, normal[3], v[3], org[3], o[3];
+
+       // LordHavoc: early out condition
+       if (!cl_numparticles)
+               return;
+
+       frametime = cl.time - cl.oldtime;
+       if (!frametime)
+               return; // if absolutely still, don't update particles
+       gravity = frametime * sv_gravity.value;
+       dvel = 1+4*frametime;
+
+       activeparticles = 0;
+       maxparticle = -1;
+       j = 0;
+       for (i = 0, p = particles, r = r_refdef.particles;i < cl_numparticles;i++, p++)
+       {
+               if (p->die < cl.time)
+               {
+                       freeparticles[j++] = p;
+                       continue;
+               }
+
+               VectorCopy(p->org, p->oldorg);
+               VectorMA(p->org, frametime, p->vel, p->org);
+               if (p->friction)
+               {
+                       f = 1.0f - (p->friction * frametime);
+                       VectorScale(p->vel, f, p->vel);
+               }
+               VectorCopy(p->org, org);
+               if (p->bounce)
+               {
+                       if (TraceLine(p->oldorg, p->org, v, normal, 0) < 1)
+                       {
+                               VectorCopy(v, p->org);
+                               if (p->bounce < 0)
+                               {
+                                       CL_Decal(v, p->tex, p->scale * cl_particles_size.value, p->color[0] * (1.0f / 255.0f), p->color[1] * (1.0f / 255.0f), p->color[2] * (1.0f / 255.0f), p->alpha * (1.0f / 255.0f));
+                                       p->die = -1;
+                                       freeparticles[j++] = p;
+                                       continue;
+                               }
+                               else
+                               {
+                                       dist = DotProduct(p->vel, normal) * -p->bounce;
+                                       VectorMA(p->vel, dist, normal, p->vel);
+                                       if (DotProduct(p->vel, p->vel) < 0.03)
+                                               VectorClear(p->vel);
+                               }
+                       }
+               }
+
+               switch (p->type)
+               {
+               case pt_static:
+                       break;
+
+                       // LordHavoc: drop-through because of shared code
+               case pt_blob:
+                       p->vel[2] *= dvel;
+               case pt_blob2:
+                       p->vel[0] *= dvel;
+                       p->vel[1] *= dvel;
+                       p->alpha -= frametime * 256;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+
+               case pt_grav:
+                       p->vel[2] -= gravity;
+                       break;
+               case pt_lavasplash:
+                       p->vel[2] -= gravity * 0.05;
+                       p->alpha -= frametime * 192;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_snow:
+                       if (cl.time > p->time2)
+                       {
+                               p->time2 = cl.time + (rand() & 3) * 0.1;
+                               p->vel[0] = (rand()&63)-32 + p->vel2[0];
+                               p->vel[1] = (rand()&63)-32 + p->vel2[1];
+                               p->vel[2] = (rand()&63)-32 + p->vel2[2];
+                       }
+                       a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
+                       if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
+                       {
+                               vec3_t normal;
+                               if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
+                                       break; // still in solid
+                               p->die = cl.time + 1000;
+                               p->vel[0] = p->vel[1] = p->vel[2] = 0;
+                               switch (a)
+                               {
+                               case CONTENTS_LAVA:
+                               case CONTENTS_SLIME:
+                                       p->tex = tex_smoke[rand()&7];
+                                       p->type = pt_steam;
+                                       p->alpha = 96;
+                                       p->scale = 5;
+                                       p->vel[2] = 96;
+                                       break;
+                               case CONTENTS_WATER:
+                                       p->tex = tex_smoke[rand()&7];
+                                       p->type = pt_splash;
+                                       p->alpha = 96;
+                                       p->scale = 5;
+                                       p->vel[2] = 96;
+                                       break;
+                               default: // CONTENTS_SOLID and any others
+                                       TraceLine(p->oldorg, p->org, v, normal, 0);
+                                       VectorCopy(v, p->org);
+                                       p->tex = tex_smoke[rand()&7];
+                                       p->type = pt_fade;
+                                       VectorClear(p->vel);
+                                       break;
+                               }
+                       }
+                       break;
+               case pt_blood:
+                       p->friction = 1;
+                       a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
+                       if (a != CONTENTS_EMPTY)
+                       {
+                               if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
+                               {
+                                       p->friction = 5;
+                                       p->scale += frametime * 32.0f;
+                                       p->alpha -= frametime * 128.0f;
+                                       p->vel[2] += gravity * 0.125f;
+                                       if (p->alpha < 1)
+                                               p->die = -1;
+                                       break;
+                               }
+                               else
+                               {
+                                       p->die = -1;
+                                       break;
+                               }
+                       }
+                       p->vel[2] -= gravity * 0.5;
+                       break;
+               case pt_spark:
+                       p->alpha -= frametime * p->time2;
+                       p->vel[2] -= gravity;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       else if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
+                               p->type = pt_underwaterspark;
+                       break;
+               case pt_underwaterspark:
+                       if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
+                       {
+                               p->tex = tex_smoke[rand()&7];
+                               p->color[0] = p->color[1] = p->color[2] = 255;
+                               p->scale = 16;
+                               p->type = pt_explosionsplash;
+                       }
+                       else
+                               p->vel[2] += gravity * 0.5f;
+                       p->alpha -= frametime * p->time2;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_explosionsplash:
+                       if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
+                               p->vel[2] -= gravity;
+                       else
+                               p->alpha = 0;
+                       p->scale += frametime * 64.0f;
+                       p->alpha -= frametime * 1024.0f;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_fade:
+                       p->alpha -= frametime * 512;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_bubble:
+                       a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
+                       if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
+                       {
+                               p->tex = tex_smoke[rand()&7];
+                               p->type = pt_splashpuff;
+                               p->scale = 4;
+                               p->vel[0] = p->vel[1] = p->vel[2] = 0;
+                               break;
+                       }
+                       p->vel[2] += gravity * 0.25;
+                       p->vel[0] *= (1 - (frametime * 0.0625));
+                       p->vel[1] *= (1 - (frametime * 0.0625));
+                       p->vel[2] *= (1 - (frametime * 0.0625));
+                       if (cl.time > p->time2)
+                       {
+                               p->time2 = cl.time + lhrandom(0, 0.5);
+                               p->vel[0] += lhrandom(-32,32);
+                               p->vel[1] += lhrandom(-32,32);
+                               p->vel[2] += lhrandom(-32,32);
+                       }
+                       p->alpha -= frametime * 256;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_bulletsmoke:
+                       p->scale += frametime * 16;
+                       p->alpha -= frametime * 1024;
+                       p->vel[2] += gravity * 0.1;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_smoke:
+                       p->scale += frametime * 24;
+                       p->alpha -= frametime * 256;
+                       p->vel[2] += gravity * 0.1;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_steam:
+                       p->scale += frametime * 48;
+                       p->alpha -= frametime * 512;
+                       p->vel[2] += gravity * 0.05;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_splashpuff:
+                       p->alpha -= frametime * 1024;
+                       if (p->alpha < 1)
+                               p->die = -1;
+                       break;
+               case pt_rain:
+                       f = 0;
+                       b = Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents;
+                       VectorCopy(p->oldorg, o);
+                       while (f < 1)
+                       {
+                               a = b;
+                               f = TraceLine(o, p->org, v, normal, a);
+                               b = traceline_endcontents;
+                               if (f < 1 && b != CONTENTS_EMPTY && b != CONTENTS_SKY)
+                               {
+                                       p->die = cl.time + 1000;
+                                       p->vel[0] = p->vel[1] = p->vel[2] = 0;
+                                       VectorCopy(v, p->org);
+                                       switch (b)
+                                       {
+                                       case CONTENTS_LAVA:
+                                       case CONTENTS_SLIME:
+                                               p->tex = tex_smoke[rand()&7];
+                                               p->type = pt_steam;
+                                               p->scale = 3;
+                                               p->vel[2] = 96;
+                                               break;
+                                       default: // water, solid, and anything else
+                                               p->tex = tex_rainsplash[0];
+                                               p->time2 = 0;
+                                               VectorCopy(normal, p->vel2);
+                                       //      VectorAdd(p->org, normal, p->org);
+                                               p->type = pt_raindropsplash;
+                                               p->scale = 8;
+                                               break;
+                                       }
+                               }
+                       }
+                       break;
+               case pt_raindropsplash:
+                       p->time2 += frametime * 64.0f;
+                       if (p->time2 >= 16.0f)
+                       {
+                               p->die = -1;
+                               break;
+                       }
+                       p->tex = tex_rainsplash[(int) p->time2];
+                       break;
+               case pt_flame:
+                       p->alpha -= frametime * 512;
+                       p->vel[2] += gravity;
+                       if (p->alpha < 16)
+                               p->die = -1;
+                       break;
+               case pt_oneframe:
+                       if (p->time2)
+                               p->die = -1;
+                       p->time2 = 1;
+                       break;
+               default:
+                       printf("unknown particle type %i\n", p->type);
+                       p->die = -1;
+                       break;
+               }
+
+               // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
+               if (p->die < cl.time)
+                       freeparticles[j++] = p;
+               else
+               {
+                       maxparticle = i;
+                       activeparticles++;
+                       if (p->pressure)
+                               pressureused = true;
+
+                       // build renderparticle for renderer to use
+                       if (p->type == pt_raindropsplash)
+                       {
+                               r->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
+                               r->dir[0] = p->vel2[0];
+                               r->dir[1] = p->vel2[1];
+                               r->dir[2] = p->vel2[2];
+                       }
+                       else if (p->tex == tex_rain)
+                               r->orientation = PARTICLE_UPRIGHT_FACING;
+                       else
+                               r->orientation = PARTICLE_BILLBOARD;
+                       r->org[0] = p->org[0];
+                       r->org[1] = p->org[1];
+                       r->org[2] = p->org[2];
+                       r->tex = p->tex;
+                       r->scale = p->scale * 0.5f * cl_particles_size.value;
+                       r->dynlight = p->dynlight;
+                       r->color[0] = p->color[0] * (1.0f / 255.0f);
+                       r->color[1] = p->color[1] * (1.0f / 255.0f);
+                       r->color[2] = p->color[2] * (1.0f / 255.0f);
+                       r->color[3] = p->alpha * (1.0f / 255.0f);
+                       r++;
+               }
+       }
+       r_refdef.numparticles = r - r_refdef.particles;
+       // fill in gaps to compact the array
+       i = 0;
+       while (maxparticle >= activeparticles)
+       {
+               *freeparticles[i++] = particles[maxparticle--];
+               while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
+                       maxparticle--;
+       }
+       cl_numparticles = activeparticles;
+
+       if (pressureused)
+       {
+               activeparticles = 0;
+               for (i = 0, p = particles;i < cl_numparticles;i++, p++)
+                       if (p->pressure)
+                               freeparticles[activeparticles++] = p;
+
+               if (activeparticles)
+               {
+                       for (i = 0, p = particles;i < cl_numparticles;i++, p++)
+                       {
+                               for (j = 0;j < activeparticles;j++)
+                               {
+                                       if (freeparticles[j] != p)
+                                       {
+                                               float dist, diff[3];
+                                               VectorSubtract(p->org, freeparticles[j]->org, diff);
+                                               dist = DotProduct(diff, diff);
+                                               if (dist < 4096 && dist >= 1)
+                                               {
+                                                       dist = freeparticles[j]->scale * 4.0f * frametime / sqrt(dist);
+                                                       VectorMA(p->vel, dist, diff, p->vel);
+                                                       //dist = freeparticles[j]->scale * 4.0f * frametime / dist;
+                                                       //VectorMA(p->vel, dist, freeparticles[j]->vel, p->vel);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
index d441a61..d38ee27 100644 (file)
--- a/cl_tent.c
+++ b/cl_tent.c
@@ -21,19 +21,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
-cvar_t r_glowinglightning = {CVAR_SAVE, "r_glowinglightning", "1"};
+cvar_t cl_glowinglightning = {CVAR_SAVE, "cl_glowinglightning", "1"};
 
 int                    num_temp_entities;
 entity_t       cl_temp_entities[MAX_TEMP_ENTITIES];
 beam_t         cl_beams[MAX_BEAMS];
 
-sfx_t                  *cl_sfx_wizhit;
-sfx_t                  *cl_sfx_knighthit;
-sfx_t                  *cl_sfx_tink1;
-sfx_t                  *cl_sfx_ric1;
-sfx_t                  *cl_sfx_ric2;
-sfx_t                  *cl_sfx_ric3;
-sfx_t                  *cl_sfx_r_exp3;
+model_t                *cl_model_bolt = NULL;
+model_t                *cl_model_bolt2 = NULL;
+model_t                *cl_model_bolt3 = NULL;
+model_t                *cl_model_beam = NULL;
+
+sfx_t          *cl_sfx_wizhit;
+sfx_t          *cl_sfx_knighthit;
+sfx_t          *cl_sfx_tink1;
+sfx_t          *cl_sfx_ric1;
+sfx_t          *cl_sfx_ric2;
+sfx_t          *cl_sfx_ric3;
+sfx_t          *cl_sfx_r_exp3;
 
 /*
 =================
@@ -42,7 +47,7 @@ CL_ParseTEnt
 */
 void CL_InitTEnts (void)
 {
-       Cvar_RegisterVariable(&r_glowinglightning);
+       Cvar_RegisterVariable(&cl_glowinglightning);
        cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
        cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
        cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
@@ -63,7 +68,7 @@ void CL_ParseBeam (model_t *m)
        vec3_t  start, end;
        beam_t  *b;
        int             i;
-       
+
        ent = MSG_ReadShort ();
        MSG_ReadVector(start);
        MSG_ReadVector(end);
@@ -96,25 +101,6 @@ void CL_ParseBeam (model_t *m)
        Con_Printf ("beam list overflow!\n");   
 }
 
-//void R_BlastParticles(vec3_t org, vec_t radius, vec_t power);
-void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count);
-void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel);
-void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type);
-
-// attempts to find the nearest non-solid location, used for explosions mainly
-void FindNonSolidLocation(vec3_t pos)
-{
-       if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[0]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[0]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[0]-=1;
-       pos[1]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[1]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[1]-=1;
-       pos[2]-=1;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[2]+=2;if (SV_HullPointContents (cl.worldmodel->hulls, 0, pos) != CONTENTS_SOLID) return;
-       pos[2]-=1;
-}
 
 /*
 =================
@@ -138,21 +124,21 @@ void CL_ParseTEnt (void)
        {
        case TE_WIZSPIKE:                       // spike hitting wall
                MSG_ReadVector(pos);
-               R_RunParticleEffect (pos, vec3_origin, 20, 30);
+               CL_RunParticleEffect (pos, vec3_origin, 20, 30);
                S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
                break;
                
        case TE_KNIGHTSPIKE:                    // spike hitting wall
                MSG_ReadVector(pos);
-               R_RunParticleEffect (pos, vec3_origin, 226, 20);
+               CL_RunParticleEffect (pos, vec3_origin, 226, 20);
                S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
                break;
                
        case TE_SPIKE:                  // spike hitting wall
                MSG_ReadVector(pos);
                // LordHavoc: changed to spark shower
-               R_SparkShower(pos, vec3_origin, 15);
-               //R_RunParticleEffect (pos, vec3_origin, 0, 10);
+               CL_SparkShower(pos, vec3_origin, 15);
+               //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
                if ( rand() % 5 )
                        S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
                else
@@ -169,8 +155,8 @@ void CL_ParseTEnt (void)
        case TE_SPIKEQUAD:                      // quad spike hitting wall
                MSG_ReadVector(pos);
                // LordHavoc: changed to spark shower
-               R_SparkShower(pos, vec3_origin, 15);
-               //R_RunParticleEffect (pos, vec3_origin, 0, 10);
+               CL_SparkShower(pos, vec3_origin, 15);
+               //CL_RunParticleEffect (pos, vec3_origin, 0, 10);
                CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                if ( rand() % 5 )
@@ -189,8 +175,8 @@ void CL_ParseTEnt (void)
        case TE_SUPERSPIKE:                     // super spike hitting wall
                MSG_ReadVector(pos);
                // LordHavoc: changed to dust shower
-               R_SparkShower(pos, vec3_origin, 30);
-               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               CL_SparkShower(pos, vec3_origin, 30);
+               //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
                if ( rand() % 5 )
                        S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
                else
@@ -207,8 +193,8 @@ void CL_ParseTEnt (void)
        case TE_SUPERSPIKEQUAD:                 // quad super spike hitting wall
                MSG_ReadVector(pos);
                // LordHavoc: changed to dust shower
-               R_SparkShower(pos, vec3_origin, 30);
-               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               CL_SparkShower(pos, vec3_origin, 30);
+               //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
                CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
                if ( rand() % 5 )
                        S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
@@ -230,11 +216,11 @@ void CL_ParseTEnt (void)
                dir[1] = MSG_ReadChar ();
                dir[2] = MSG_ReadChar ();
                count = MSG_ReadByte (); // amount of particles
-               R_BloodPuff(pos, dir, count);
+               CL_BloodPuff(pos, dir, count);
                break;
        case TE_BLOOD2: // blood puff
                MSG_ReadVector(pos);
-               R_BloodPuff(pos, vec3_origin, 10);
+               CL_BloodPuff(pos, vec3_origin, 10);
                break;
        case TE_SPARK:  // spark shower
                MSG_ReadVector(pos);
@@ -242,7 +228,7 @@ void CL_ParseTEnt (void)
                dir[1] = MSG_ReadChar ();
                dir[2] = MSG_ReadChar ();
                count = MSG_ReadByte (); // amount of particles
-               R_SparkShower(pos, dir, count);
+               CL_SparkShower(pos, dir, count);
                break;
                // LordHavoc: added for improved gore
        case TE_BLOODSHOWER:    // vaporized body
@@ -250,7 +236,7 @@ void CL_ParseTEnt (void)
                MSG_ReadVector(pos2); // maxs
                velspeed = MSG_ReadCoord (); // speed
                count = MSG_ReadShort (); // number of particles
-               R_BloodShower(pos, pos2, velspeed, count);
+               CL_BloodShower(pos, pos2, velspeed, count);
                break;
        case TE_PARTICLECUBE:   // general purpose particle effect
                MSG_ReadVector(pos); // mins
@@ -260,7 +246,7 @@ void CL_ParseTEnt (void)
                colorStart = MSG_ReadByte (); // color
                colorLength = MSG_ReadByte (); // gravity (1 or 0)
                velspeed = MSG_ReadCoord (); // randomvel
-               R_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
+               CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength, velspeed);
                break;
 
        case TE_PARTICLERAIN:   // general purpose particle effect
@@ -269,7 +255,7 @@ void CL_ParseTEnt (void)
                MSG_ReadVector(dir); // dir
                count = MSG_ReadShort (); // number of particles
                colorStart = MSG_ReadByte (); // color
-               R_ParticleRain(pos, pos2, dir, count, colorStart, 0);
+               CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
                break;
 
        case TE_PARTICLESNOW:   // general purpose particle effect
@@ -278,36 +264,36 @@ void CL_ParseTEnt (void)
                MSG_ReadVector(dir); // dir
                count = MSG_ReadShort (); // number of particles
                colorStart = MSG_ReadByte (); // color
-               R_ParticleRain(pos, pos2, dir, count, colorStart, 1);
+               CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
                break;
 
        case TE_GUNSHOT:                        // bullet hitting wall
                MSG_ReadVector(pos);
                // LordHavoc: changed to dust shower
-               R_SparkShower(pos, vec3_origin, 15);
-               //R_RunParticleEffect (pos, vec3_origin, 0, 20);
+               CL_SparkShower(pos, vec3_origin, 15);
+               //CL_RunParticleEffect (pos, vec3_origin, 0, 20);
                break;
 
        case TE_GUNSHOTQUAD:                    // quad bullet hitting wall
                MSG_ReadVector(pos);
-               R_SparkShower(pos, vec3_origin, 15);
+               CL_SparkShower(pos, vec3_origin, 15);
                CL_AllocDlight (NULL, pos, 200, 0.1f, 0.1f, 1.0f, 1000, 0.2);
                break;
 
        case TE_EXPLOSION:                      // rocket explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_ParticleExplosion (pos, false);
-//             R_BlastParticles (pos, 120, 120);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_ParticleExplosion (pos, false);
+//             CL_BlastParticles (pos, 120, 120);
                CL_AllocDlight (NULL, pos, 350, 1.0f, 0.8f, 0.4f, 700, 0.5);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                break;
 
        case TE_EXPLOSIONQUAD:                  // quad rocket explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_ParticleExplosion (pos, false);
-//             R_BlastParticles (pos, 120, 480);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_ParticleExplosion (pos, false);
+//             CL_BlastParticles (pos, 120, 480);
                CL_AllocDlight (NULL, pos, 600, 0.5f, 0.4f, 1.0f, 1200, 0.5);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                break;
@@ -315,8 +301,8 @@ void CL_ParseTEnt (void)
                /*
        case TE_SMOKEEXPLOSION:                 // rocket explosion with a cloud of smoke
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_ParticleExplosion (pos, true);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_ParticleExplosion (pos, true);
                CL_AllocDlight (NULL, pos, 350, 1.0f, 0.8f, 0.4f, 700, 0.5);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                break;
@@ -324,18 +310,18 @@ void CL_ParseTEnt (void)
 
        case TE_EXPLOSION3:                             // Nehahra movie colored lighting explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_ParticleExplosion (pos, false);
-//             R_BlastParticles (pos, 120, 120);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_ParticleExplosion (pos, false);
+//             CL_BlastParticles (pos, 120, 120);
                CL_AllocDlight (NULL, pos, 350, MSG_ReadCoord(), MSG_ReadCoord(), MSG_ReadCoord(), 700, 0.5);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                break;
 
        case TE_EXPLOSIONRGB:                   // colored lighting explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_ParticleExplosion (pos, false);
-//             R_BlastParticles (pos, 120, 120);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_ParticleExplosion (pos, false);
+//             CL_BlastParticles (pos, 120, 120);
                color[0] = MSG_ReadByte() * (1.0 / 255.0);
                color[1] = MSG_ReadByte() * (1.0 / 255.0);
                color[2] = MSG_ReadByte() * (1.0 / 255.0);
@@ -345,9 +331,9 @@ void CL_ParseTEnt (void)
 
        case TE_TAREXPLOSION:                   // tarbaby explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
-               R_BlobExplosion (pos);
-//             R_BlastParticles (pos, 120, 120);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
+               CL_BlobExplosion (pos);
+//             CL_BlastParticles (pos, 120, 120);
 
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
                CL_AllocDlight (NULL, pos, 600, 0.8f, 0.4f, 1.0f, 1200, 0.5);
@@ -356,13 +342,13 @@ void CL_ParseTEnt (void)
 
        case TE_SMALLFLASH:
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
                CL_AllocDlight (NULL, pos, 200, 1, 1, 1, 1000, 0.2);
                break;
 
        case TE_CUSTOMFLASH:
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
                radius = MSG_ReadByte() * 8;
                velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
                color[0] = MSG_ReadByte() * (1.0 / 255.0);
@@ -375,53 +361,61 @@ void CL_ParseTEnt (void)
                MSG_ReadVector(pos);
                MSG_ReadVector(dir);
                count = MSG_ReadByte();
-               R_Flames(pos, dir, count);
+               CL_Flames(pos, dir, count);
                break;
 
        case TE_LIGHTNING1:                             // lightning bolts
-               CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
+               if (!cl_model_bolt)
+                       cl_model_bolt = Mod_ForName("progs/bolt.mdl", true, true, false);
+               CL_ParseBeam (cl_model_bolt);
                break;
 
        case TE_LIGHTNING2:                             // lightning bolts
-               CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
+               if (!cl_model_bolt2)
+                       cl_model_bolt2 = Mod_ForName("progs/bolt2.mdl", true, true, false);
+               CL_ParseBeam (cl_model_bolt2);
                break;
-       
+
        case TE_LIGHTNING3:                             // lightning bolts
-               CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
+               if (!cl_model_bolt3)
+                       cl_model_bolt3 = Mod_ForName("progs/bolt3.mdl", true, true, false);
+               CL_ParseBeam (cl_model_bolt3);
                break;
 
-// PGM 01/21/97 
+// PGM 01/21/97
        case TE_BEAM:                           // grappling hook beam
-               CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
+               if (!cl_model_beam)
+                       cl_model_beam = Mod_ForName("progs/beam.mdl", true, true, false);
+               CL_ParseBeam (cl_model_beam);
                break;
 // PGM 01/21/97
 
 // LordHavoc: for compatibility with the Nehahra movie...
        case TE_LIGHTNING4NEH:
-               CL_ParseBeam (Mod_ForName(MSG_ReadString(), true));
+               CL_ParseBeam (Mod_ForName(MSG_ReadString(), true, false, false));
                break;
 
        case TE_LAVASPLASH:     
                pos[0] = MSG_ReadCoord ();
                pos[1] = MSG_ReadCoord ();
                pos[2] = MSG_ReadCoord ();
-               R_LavaSplash (pos);
+               CL_LavaSplash (pos);
                break;
        
        case TE_TELEPORT:
                pos[0] = MSG_ReadCoord ();
                pos[1] = MSG_ReadCoord ();
                pos[2] = MSG_ReadCoord ();
-               R_TeleportSplash (pos);
+               CL_TeleportSplash (pos);
                break;
                
        case TE_EXPLOSION2:                             // color mapped explosion
                MSG_ReadVector(pos);
-               FindNonSolidLocation(pos);
+               Mod_FindNonSolidLocation(pos, cl.worldmodel);
                colorStart = MSG_ReadByte ();
                colorLength = MSG_ReadByte ();
-               R_ParticleExplosion2 (pos, colorStart, colorLength);
-//             R_BlastParticles (pos, 80, 80);
+               CL_ParticleExplosion2 (pos, colorStart, colorLength);
+//             CL_BlastParticles (pos, 80, 80);
                tempcolor = (byte *)&d_8to24table[(rand()%colorLength) + colorStart];
                CL_AllocDlight (NULL, pos, 350, tempcolor[0] * (1.0f / 255.0f), tempcolor[1] * (1.0f / 255.0f), tempcolor[2] * (1.0f / 255.0f), 700, 0.5);
                S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
@@ -453,7 +447,6 @@ entity_t *CL_NewTempEntity (void)
        ent->render.colormap = -1; // no special coloring
        ent->render.scale = 1;
        ent->render.alpha = 1;
-       ent->render.colormod[0] = ent->render.colormod[1] = ent->render.colormod[2] = 1;
        return ent;
 }
 
@@ -523,8 +516,8 @@ void CL_UpdateTEnts (void)
                        ent->render.angles[1] = yaw;
                        ent->render.angles[2] = rand()%360;
 
-                       if (r_glowinglightning.value > 0)
-                               CL_AllocDlight(&ent->render, ent->render.origin, lhrandom(100, 120), r_glowinglightning.value * 0.25f, r_glowinglightning.value * 0.25f, r_glowinglightning.value * 0.25f, 0, 0);
+                       if (cl_glowinglightning.value > 0)
+                               CL_AllocDlight(&ent->render, ent->render.origin, lhrandom(200, 240), cl_glowinglightning.value * 0.25f, cl_glowinglightning.value * 0.25f, cl_glowinglightning.value * 0.25f, 0, 0);
 
                        VectorMA(org, 30, dist, org);
                        d -= 30;
index 4b469bb..d9dbbe5 100644 (file)
--- a/client.h
+++ b/client.h
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -19,6 +19,70 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // client.h
 
+typedef struct frameblend_s
+{
+       int frame;
+       float lerp;
+}
+frameblend_t;
+
+// LordHavoc: nothing in this structure is persistant, it may be overwritten by the client every frame, for persistant data use entity_lerp_t.
+typedef struct entity_render_s
+{
+       vec3_t  origin;                 // location
+       vec3_t  angles;                 // orientation
+       float   alpha;                  // opacity (alpha) of the model
+       float   scale;                  // size the model is shown
+
+       model_t *model;                 // NULL = no model
+       int             frame;                  // current uninterpolated animation frame (for things which do not use interpolation)
+       int             colormap;               // entity shirt and pants colors
+       int             effects;                // light, particles, etc
+       int             skinnum;                // for Alias models
+       int             flags;                  // render flags
+
+       // these are copied from the persistent data
+       int             frame1;                 // frame that the model is interpolating from
+       int             frame2;                 // frame that the model is interpolating to
+       double  framelerp;              // interpolation factor, usually computed from frame2time
+       double  frame1time;             // time frame1 began playing (for framegroup animations)
+       double  frame2time;             // time frame2 began playing (for framegroup animations)
+
+       // calculated by the renderer (but not persistent)
+       int             visframe;               // if visframe == r_framecount, it is visible
+       vec3_t  mins, maxs;             // calculated during R_AddModelEntities
+       frameblend_t    frameblend[4]; // 4 frame numbers (-1 if not used) and their blending scalers (0-1), if interpolation is not desired, use frame instead
+}
+entity_render_t;
+
+typedef struct entity_persistent_s
+{
+       // particles
+       vec3_t  trail_origin;   // trail rendering
+       float   trail_time;             // trail rendering
+
+       // interpolated animation
+       int             modelindex;             // lerp resets when model changes
+       int             frame1;                 // frame that the model is interpolating from
+       int             frame2;                 // frame that the model is interpolating to
+       double  framelerp;              // interpolation factor, usually computed from frame2time
+       double  frame1time;             // time frame1 began playing (for framegroup animations)
+       double  frame2time;             // time frame2 began playing (for framegroup animations)
+}
+entity_persistent_t;
+
+typedef struct entity_s
+{
+       entity_state_t state_baseline;  // baseline state (default values)
+       entity_state_t state_previous;  // previous state (interpolating from this)
+       entity_state_t state_current;   // current state (interpolating to this)
+
+       entity_persistent_t persistent; // used for regenerating parts of render
+
+       entity_render_t render; // the only data the renderer should know about
+}
+entity_t;
+
 typedef struct
 {
        vec3_t  viewangles;
@@ -64,8 +128,6 @@ typedef struct
 
 #define        SIGNONS         4                       // signon messages to receive before connected
 
-#include "r_light.h"
-
 #define        MAX_BEAMS       24
 typedef struct
 {
@@ -96,7 +158,7 @@ typedef struct
 {
        cactive_t       state;
 
-// personalization data sent to server 
+// personalization data sent to server
        char            mapstring[MAX_QPATH];
        char            spawnparms[MAX_MAPSTRING];      // to restart a level
 
@@ -134,7 +196,7 @@ typedef struct
 {
        int                     movemessages;   // since connecting to this server
                                                                // throw out the first couple, so the player
-                                                               // doesn't accidentally do something the 
+                                                               // doesn't accidentally do something the
                                                                // first frame
        usercmd_t       cmd;                    // last command sent to the server
 
@@ -154,7 +216,7 @@ typedef struct
        vec3_t          mviewangles[2]; // during demo playback viewangles is lerped
                                                                // between these
        vec3_t          viewangles;
-       
+
        vec3_t          mvelocity[2];   // update by server, used for lean+bob
                                                                // (0 is newest)
        vec3_t          velocity;               // lerped between mvelocity[0] and [1]
@@ -179,7 +241,7 @@ typedef struct
        int                     intermission;   // don't change view angle, full screen, etc
        int                     completed_time; // latched at intermission start
 
-       double          mtime[2];               // the timestamp of last two messages   
+       double          mtime[2];               // the timestamp of last two messages
        double          time;                   // clients view of time, should be between
                                                                // servertime and oldservertime to generate
                                                                // a lerp point for other data
@@ -215,6 +277,7 @@ typedef struct
 }
 client_state_t;
 
+extern mempool_t *cl_scores_mempool;
 
 //
 // cvars
@@ -266,26 +329,22 @@ extern    lightstyle_t    cl_lightstyle[MAX_LIGHTSTYLES];
 extern entity_t                cl_temp_entities[MAX_TEMP_ENTITIES];
 extern beam_t                  cl_beams[MAX_BEAMS];
 
-//=============================================================================
-
 #include "cl_light.h"
 
+//=============================================================================
+
 //
 // cl_main
 //
 
-extern void CL_Init (void);
+void CL_Init (void);
 
-extern void CL_EstablishConnection (char *host);
-extern void CL_Signon1 (void);
-extern void CL_Signon2 (void);
-extern void CL_Signon3 (void);
-extern void CL_Signon4 (void);
+void CL_EstablishConnection (char *host);
 
-extern void CL_Disconnect (void);
-extern void CL_Disconnect_f (void);
-extern void CL_NextDemo (void);
+void CL_Disconnect (void);
+void CL_Disconnect_f (void);
 
+// LordHavoc: fixme: move this to r_refdef?
 // LordHavoc: raised this from 256 to the maximum possible number of entities visible
 #define MAX_VISEDICTS (MAX_EDICTS + MAX_STATIC_ENTITIES + MAX_TEMP_ENTITIES)
 extern int                     cl_numvisedicts;
@@ -305,62 +364,134 @@ extern   kbutton_t       in_mlook, in_klook;
 extern         kbutton_t       in_strafe;
 extern         kbutton_t       in_speed;
 
-extern void CL_InitInput (void);
-extern void CL_SendCmd (void);
-extern void CL_SendMove (usercmd_t *cmd);
+void CL_InitInput (void);
+void CL_SendCmd (void);
+void CL_SendMove (usercmd_t *cmd);
 
-extern void CL_ParseTEnt (void);
-extern void CL_UpdateTEnts (void);
-extern void CL_DoEffects (void);
+void CL_LerpUpdate(entity_t *e, int frame, int modelindex);
+void CL_ParseTEnt (void);
+void CL_UpdateTEnts (void);
 
-extern entity_t *CL_NewTempEntity (void);
+entity_t *CL_NewTempEntity (void);
 
-extern void CL_Effect(vec3_t org, int modelindex, int startframe, int framecount, float framerate);
+void CL_Effect(vec3_t org, int modelindex, int startframe, int framecount, float framerate);
 
-extern void CL_ClearState (void);
+void CL_ClearState (void);
 
 
-extern int  CL_ReadFromServer (void);
-extern void CL_WriteToServer (usercmd_t *cmd);
-extern void CL_BaseMove (usercmd_t *cmd);
+int  CL_ReadFromServer (void);
+void CL_WriteToServer (usercmd_t *cmd);
+void CL_BaseMove (usercmd_t *cmd);
 
 
-extern float CL_KeyState (kbutton_t *key);
-extern char *Key_KeynumToString (int keynum);
+float CL_KeyState (kbutton_t *key);
+char *Key_KeynumToString (int keynum);
 
 //
 // cl_demo.c
 //
-extern void CL_StopPlayback (void);
-extern int CL_GetMessage (void);
+void CL_StopPlayback (void);
+int CL_GetMessage (void);
 
-extern void CL_Stop_f (void);
-extern void CL_Record_f (void);
-extern void CL_PlayDemo_f (void);
-extern void CL_TimeDemo_f (void);
+void CL_NextDemo (void);
+void CL_Stop_f (void);
+void CL_Record_f (void);
+void CL_PlayDemo_f (void);
+void CL_TimeDemo_f (void);
 
 //
 // cl_parse.c
 //
-extern void CL_Parse_Init(void);
-extern void CL_ParseServerMessage(void);
-extern void CL_BitProfile_f(void);
+void CL_Parse_Init(void);
+void CL_ParseServerMessage(void);
+void CL_BitProfile_f(void);
 
 //
 // view
 //
-extern void V_StartPitchDrift (void);
-extern void V_StopPitchDrift (void);
+void V_StartPitchDrift (void);
+void V_StopPitchDrift (void);
 
-extern void V_RenderView (void);
-extern void V_UpdateBlends (void);
-extern void V_Register (void);
-extern void V_ParseDamage (void);
-extern void V_SetContentsColor (int contents);
+void V_RenderView (void);
+void V_UpdateBlends (void);
+void V_Register (void);
+void V_ParseDamage (void);
+void V_SetContentsColor (int contents);
 
 
 //
 // cl_tent
 //
-extern void CL_InitTEnts (void);
-extern void CL_SignonReply (void);
+void CL_InitTEnts (void);
+
+//
+// cl_part
+//
+
+#define PARTICLE_INVALID 0
+#define PARTICLE_BILLBOARD 1
+#define PARTICLE_UPRIGHT_FACING 2
+#define PARTICLE_ORIENTED_DOUBLESIDED 3
+
+typedef struct renderparticle_s
+{
+       int                     tex;
+       int                     orientation;
+       int                     dynlight;
+       float           scale;
+       float           org[3];
+       float           dir[3];
+       float           color[4];
+}
+renderparticle_t;
+
+void CL_Particles_Clear(void);
+void CL_Particles_Init(void);
+
+void CL_ParseParticleEffect (void);
+void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
+void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent);
+void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent);
+void CL_SparkShower (vec3_t org, vec3_t dir, int count);
+void CL_BloodPuff (vec3_t org, vec3_t vel, int count);
+void CL_FlameCube (vec3_t mins, vec3_t maxs, int count);
+void CL_Flames (vec3_t org, vec3_t vel, int count);
+void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count);
+void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel);
+void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type);
+void CL_EntityParticles (entity_t *ent);
+void CL_BlobExplosion (vec3_t org);
+void CL_ParticleExplosion (vec3_t org, int smoke);
+void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength);
+void CL_LavaSplash (vec3_t org);
+void CL_TeleportSplash (vec3_t org);
+void CL_MoveParticles(void);
+void CL_UpdateDecals(void);
+void R_MoveExplosions(void);
+void R_NewExplosion(vec3_t org);
+
+//
+// cl_decal
+//
+
+typedef struct renderdecal_s
+{
+       entity_render_t *ent;
+       int tex;
+       int surface;
+       float scale;
+       vec3_t org;
+       vec3_t dir;
+       float color[4];
+}
+renderdecal_t;
+
+void CL_Decals_Clear(void);
+void CL_Decals_Init(void);
+void CL_Decal(vec3_t origin, int tex, float scale, float red, float green, float blue, float alpha);
+
+// if contents is not zero, it will impact on content changes
+// (leafs matching contents are considered empty, others are solid)
+extern int traceline_endcontents; // set by TraceLine
+float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int contents);
+
diff --git a/cmd.c b/cmd.c
index 71f476b..293155d 100644 (file)
--- a/cmd.c
+++ b/cmd.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -21,8 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "quakedef.h"
 
-void Cmd_ForwardToServer (void);
-
 #define        MAX_ALIAS_NAME  32
 
 typedef struct cmdalias_s
@@ -32,12 +30,11 @@ typedef struct cmdalias_s
        char    *value;
 } cmdalias_t;
 
-cmdalias_t     *cmd_alias;
+static cmdalias_t *cmd_alias;
 
-int trashtest;
-int *trashspot;
+static qboolean cmd_wait;
 
-qboolean       cmd_wait;
+static mempool_t *cmd_mempool;
 
 //=============================================================================
 
@@ -50,7 +47,7 @@ next frame.  This allows commands like:
 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
 ============
 */
-void Cmd_Wait_f (void)
+static void Cmd_Wait_f (void)
 {
        cmd_wait = true;
 }
@@ -63,7 +60,7 @@ void Cmd_Wait_f (void)
 =============================================================================
 */
 
-sizebuf_t      cmd_text;
+static sizebuf_t       cmd_text;
 
 /*
 ============
@@ -72,7 +69,8 @@ Cbuf_Init
 */
 void Cbuf_Init (void)
 {
-       SZ_Alloc (&cmd_text, 8192);             // space for commands and script files
+       // LordHavoc: inreased this from 8192 to 32768
+       SZ_Alloc (&cmd_text, 32768, "command buffer"); // space for commands and script files
 }
 
 
@@ -86,7 +84,7 @@ Adds command text at the end of the buffer
 void Cbuf_AddText (char *text)
 {
        int             l;
-       
+
        l = strlen (text);
 
        if (cmd_text.cursize + l >= cmd_text.maxsize)
@@ -123,10 +121,10 @@ void Cbuf_InsertText (char *text)
        }
        else
                temp = NULL;    // shut up compiler
-               
+
 // add the entire text of the file
        Cbuf_AddText (text);
-       
+
 // add the copied off data
        if (templen)
        {
@@ -146,7 +144,7 @@ void Cbuf_Execute (void)
        char    *text;
        char    line[1024];
        int             quotes;
-       
+
        while (cmd_text.cursize)
        {
 // find a \n or ; line break
@@ -162,11 +160,10 @@ void Cbuf_Execute (void)
                        if (text[i] == '\n')
                                break;
                }
-                       
-                               
+
                memcpy (line, text, i);
                line[i] = 0;
-               
+
 // delete the text from the command buffer and move remaining commands down
 // this is necessary because commands (exec, alias) can insert data at the
 // beginning of the text buffer
@@ -182,7 +179,7 @@ void Cbuf_Execute (void)
 
 // execute the command line
                Cmd_ExecuteString (line, src_command);
-               
+
                if (cmd_wait)
                {       // skip out while text still remains in buffer, leaving it
                        // for next frame
@@ -215,7 +212,7 @@ void Cmd_StuffCmds_f (void)
        int             i, j;
        int             s;
        char    *text, *build, c;
-               
+
        if (Cmd_Argc () != 1)
        {
                Con_Printf ("stuffcmds : execute command line parameters\n");
@@ -232,7 +229,7 @@ void Cmd_StuffCmds_f (void)
        }
        if (!s)
                return;
-               
+
        text = Z_Malloc (s+1);
        text[0] = 0;
        for (i=1 ; i<com_argc ; i++)
@@ -243,11 +240,11 @@ void Cmd_StuffCmds_f (void)
                if (i != com_argc-1)
                        strcat (text, " ");
        }
-       
+
 // pull out the commands
        build = Z_Malloc (s+1);
        build[0] = 0;
-       
+
        for (i=0 ; i<s-1 ; i++)
        {
                if (text[i] == '+')
@@ -259,17 +256,17 @@ void Cmd_StuffCmds_f (void)
 
                        c = text[j];
                        text[j] = 0;
-                       
+
                        strcat (build, text+i);
                        strcat (build, "\n");
                        text[j] = c;
                        i = j-1;
                }
        }
-       
+
        if (build[0])
                Cbuf_InsertText (build);
-       
+
        Z_Free (text);
        Z_Free (build);
 }
@@ -280,7 +277,7 @@ void Cmd_StuffCmds_f (void)
 Cmd_Exec_f
 ===============
 */
-void Cmd_Exec_f (void)
+static void Cmd_Exec_f (void)
 {
        char    *f;
 
@@ -290,16 +287,16 @@ void Cmd_Exec_f (void)
                return;
        }
 
-       f = (char *)COM_LoadMallocFile (Cmd_Argv(1), false);
+       f = (char *)COM_LoadFile (Cmd_Argv(1), false);
        if (!f)
        {
                Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
                return;
        }
        Con_Printf ("execing %s\n",Cmd_Argv(1));
-       
+
        Cbuf_InsertText (f);
-       qfree(f);
+       Mem_Free(f);
 }
 
 
@@ -310,10 +307,10 @@ Cmd_Echo_f
 Just prints the rest of the line to the console
 ===============
 */
-void Cmd_Echo_f (void)
+static void Cmd_Echo_f (void)
 {
        int             i;
-       
+
        for (i=1 ; i<Cmd_Argc() ; i++)
                Con_Printf ("%s ",Cmd_Argv(i));
        Con_Printf ("\n");
@@ -327,16 +324,16 @@ Creates a new command that executes a command string (possibly ; seperated)
 ===============
 */
 
-char *CopyString (char *in)
+static char *CopyString (char *in)
 {
        char    *out;
-       
+
        out = Z_Malloc (strlen(in)+1);
        strcpy (out, in);
        return out;
 }
 
-void Cmd_Alias_f (void)
+static void Cmd_Alias_f (void)
 {
        cmdalias_t      *a;
        char            cmd[1024];
@@ -374,7 +371,7 @@ void Cmd_Alias_f (void)
                a->next = cmd_alias;
                cmd_alias = a;
        }
-       strcpy (a->name, s);    
+       strcpy (a->name, s);
 
 // copy the rest of the command line
        cmd[0] = 0;             // start out with a null string
@@ -386,7 +383,7 @@ void Cmd_Alias_f (void)
                        strcat (cmd, " ");
        }
        strcat (cmd, "\n");
-       
+
        a->value = CopyString (cmd);
 }
 
@@ -408,15 +405,15 @@ typedef struct cmd_function_s
 
 #define        MAX_ARGS                80
 
-static int                     cmd_argc;
-static char            *cmd_argv[MAX_ARGS];
-static char            *cmd_null_string = "";
-static char            *cmd_args = NULL;
+static int cmd_argc;
+static char *cmd_argv[MAX_ARGS];
+static char *cmd_null_string = "";
+static char *cmd_args = NULL;
 
-cmd_source_t   cmd_source;
+cmd_source_t cmd_source;
 
 
-static cmd_function_t  *cmd_functions;         // possible commands to execute
+static cmd_function_t *cmd_functions;          // possible commands to execute
 
 /*
 ========
@@ -427,7 +424,7 @@ Cmd_List
 
 ========
 */
-void Cmd_List_f (void)
+static void Cmd_List_f (void)
 {
        cmd_function_t  *cmd;
        char                    *partial;
@@ -464,6 +461,8 @@ Cmd_Init
 */
 void Cmd_Init (void)
 {
+       cmd_mempool = Mem_AllocPool("commands");
+
 //
 // register our commands
 //
@@ -497,7 +496,7 @@ char        *Cmd_Argv (int arg)
 {
        if (arg >= cmd_argc )
                return cmd_null_string;
-       return cmd_argv[arg];   
+       return cmd_argv[arg];
 }
 
 /*
@@ -518,17 +517,17 @@ Cmd_TokenizeString
 Parses the given string into command line tokens.
 ============
 */
-void Cmd_TokenizeString (char *text)
+static void Cmd_TokenizeString (char *text)
 {
        int             i;
-       
+
 // clear the args from the last string
        for (i=0 ; i<cmd_argc ; i++)
                Z_Free (cmd_argv[i]);
-               
+
        cmd_argc = 0;
        cmd_args = NULL;
-       
+
        while (1)
        {
 // skip whitespace up to a /n
@@ -536,7 +535,7 @@ void Cmd_TokenizeString (char *text)
                {
                        text++;
                }
-               
+
                if (*text == '\n')
                {       // a newline seperates commands in the buffer
                        text++;
@@ -545,10 +544,10 @@ void Cmd_TokenizeString (char *text)
 
                if (!*text)
                        return;
-       
+
                if (cmd_argc == 1)
                         cmd_args = text;
-                       
+
                text = COM_Parse (text);
                if (!text)
                        return;
@@ -560,7 +559,7 @@ void Cmd_TokenizeString (char *text)
                        cmd_argc++;
                }
        }
-       
+
 }
 
 
@@ -572,17 +571,17 @@ Cmd_AddCommand
 void   Cmd_AddCommand (char *cmd_name, xcommand_t function)
 {
        cmd_function_t  *cmd;
-       
-       if (host_initialized)   // because hunk allocation would get stomped
-               Sys_Error ("Cmd_AddCommand after host_initialized");
-               
+
+//     if (host_initialized)   // because hunk allocation would get stomped
+//             Sys_Error ("Cmd_AddCommand after host_initialized");
+
 // fail if the command is a variable name
        if (Cvar_VariableString(cmd_name)[0])
        {
                Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
                return;
        }
-       
+
 // fail if the command already exists
        for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
        {
@@ -593,7 +592,7 @@ void        Cmd_AddCommand (char *cmd_name, xcommand_t function)
                }
        }
 
-       cmd = Hunk_AllocName (sizeof(cmd_function_t), "commands");
+       cmd = Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
        cmd->name = cmd_name;
        cmd->function = function;
        cmd->next = cmd_functions;
@@ -610,10 +609,8 @@ qboolean   Cmd_Exists (char *cmd_name)
        cmd_function_t  *cmd;
 
        for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
-       {
                if (!strcmp (cmd_name,cmd->name))
                        return true;
-       }
 
        return false;
 }
@@ -629,12 +626,12 @@ char *Cmd_CompleteCommand (char *partial)
 {
        cmd_function_t  *cmd;
        int                             len;
-       
+
        len = strlen(partial);
-       
+
        if (!len)
                return NULL;
-               
+
 // check functions
        for (cmd = cmd_functions; cmd; cmd = cmd->next)
                if (!strncmp(partial, cmd->name, len))
@@ -652,19 +649,18 @@ char *Cmd_CompleteCommand (char *partial)
        Thanks to taniwha
 
 */
-int
-Cmd_CompleteCountPossible (char *partial)
+int Cmd_CompleteCountPossible (char *partial)
 {
        cmd_function_t  *cmd;
        int                             len;
        int                             h;
-       
+
        h = 0;
        len = strlen(partial);
-       
+
        if (!len)
                return 0;
-       
+
        // Loop through the command list and count all partial matches
        for (cmd = cmd_functions; cmd; cmd = cmd->next)
                if (!strncasecmp(partial, cmd->name, len))
@@ -682,8 +678,7 @@ Cmd_CompleteCountPossible (char *partial)
        Thanks to taniwha
 
 */
-char   **
-Cmd_CompleteBuildList (char *partial)
+char **Cmd_CompleteBuildList (char *partial)
 {
        cmd_function_t  *cmd;
        int                             len = 0;
@@ -692,7 +687,7 @@ Cmd_CompleteBuildList (char *partial)
        char                    **buf;
 
        len = strlen(partial);
-       buf = qmalloc(sizeofbuf + sizeof (char *));
+       buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
        // Loop through the alias list and print all matches
        for (cmd = cmd_functions; cmd; cmd = cmd->next)
                if (!strncasecmp(partial, cmd->name, len))
@@ -711,8 +706,7 @@ Cmd_CompleteBuildList (char *partial)
        Thanks to taniwha
 
 */
-char
-*Cmd_CompleteAlias (char * partial)
+char *Cmd_CompleteAlias (char * partial)
 {
        cmdalias_t      *alias;
        int                     len;
@@ -739,8 +733,7 @@ char
        Thanks to taniwha
 
 */
-int
-Cmd_CompleteAliasCountPossible (char *partial)
+int Cmd_CompleteAliasCountPossible (char *partial)
 {
        cmdalias_t      *alias;
        int                     len;
@@ -770,8 +763,7 @@ Cmd_CompleteAliasCountPossible (char *partial)
        Thanks to taniwha
 
 */
-char   **
-Cmd_CompleteAliasBuildList (char *partial)
+char **Cmd_CompleteAliasBuildList (char *partial)
 {
        cmdalias_t      *alias;
        int                     len = 0;
@@ -780,7 +772,7 @@ Cmd_CompleteAliasBuildList (char *partial)
        char            **buf;
 
        len = strlen(partial);
-       buf = qmalloc(sizeofbuf + sizeof (char *));
+       buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
        // Loop through the alias list and print all matches
        for (alias = cmd_alias; alias; alias = alias->next)
                if (!strncasecmp(partial, alias->name, len))
@@ -799,13 +791,13 @@ FIXME: lookupnoadd the token to speed search?
 ============
 */
 void   Cmd_ExecuteString (char *text, cmd_source_t src)
-{      
+{
        cmd_function_t  *cmd;
        cmdalias_t              *a;
 
        cmd_source = src;
        Cmd_TokenizeString (text);
-                       
+
 // execute the command line
        if (!Cmd_Argc())
                return;         // no tokens
@@ -829,11 +821,10 @@ void      Cmd_ExecuteString (char *text, cmd_source_t src)
                        return;
                }
        }
-       
+
 // check cvars
        if (!Cvar_Command ())
                Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
-       
 }
 
 
@@ -851,7 +842,7 @@ void Cmd_ForwardToServer (void)
                Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
                return;
        }
-       
+
        if (cls.demoplayback)
                return;         // not really connected
 
@@ -880,14 +871,14 @@ where the given parameter apears, or 0 if not present
 int Cmd_CheckParm (char *parm)
 {
        int i;
-       
+
        if (!parm)
                Sys_Error ("Cmd_CheckParm: NULL");
 
        for (i = 1; i < Cmd_Argc (); i++)
                if (!Q_strcasecmp (parm, Cmd_Argv (i)))
                        return i;
-                       
+
        return 0;
 }
 
diff --git a/cmd.h b/cmd.h
index ba2796e..640afc5 100644 (file)
--- a/cmd.h
+++ b/cmd.h
@@ -116,7 +116,7 @@ int Cmd_CheckParm (char *parm);
 // Returns the position (1 to argc-1) in the command's argument list
 // where the given parameter apears, or 0 if not present
 
-void Cmd_TokenizeString (char *text);
+//void Cmd_TokenizeString (char *text);
 // Takes a null terminated string.  Does not need to be /n terminated.
 // breaks the string up into arg tokens.
 
index 5c73c03..2711267 100644 (file)
--- a/common.c
+++ b/common.c
@@ -31,32 +31,34 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #define NUM_SAFE_ARGVS  7
 
-static char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
-static char     *argvdummy = " ";
+static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
+static char *argvdummy = " ";
 
-static char     *safeargvs[NUM_SAFE_ARGVS] =
+static char *safeargvs[NUM_SAFE_ARGVS] =
        {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-window"};
 
 cvar_t registered = {0, "registered","0"};
 cvar_t cmdline = {0, "cmdline","0"};
 
-qboolean        com_modified;   // set true if using non-id files
+mempool_t *pak_mempool;
 
-//qboolean             proghack;
+qboolean com_modified;   // set true if using non-id files
 
-//int             static_registered = 1;  // only for startup check, then set
+//qboolean proghack;
 
-qboolean               msg_suppress_1 = 0;
+//int static_registered = 1;  // only for startup check, then set
+
+qboolean msg_suppress_1 = 0;
 
 void COM_InitFilesystem (void);
 
-char   com_token[1024];
-int            com_argc;
-char   **com_argv;
+char com_token[1024];
+int com_argc;
+char **com_argv;
 
 // LordHavoc: made commandline 1024 characters instead of 256
 #define CMDLINE_LENGTH 1024
-char   com_cmdline[CMDLINE_LENGTH];
+char com_cmdline[CMDLINE_LENGTH];
 
 int gamemode;
 char *gamename;
@@ -192,7 +194,7 @@ int Q_strcmp (char *s1, char *s2)
                s1++;
                s2++;
        }
-       
+
        return -1;
 }
 
@@ -209,7 +211,7 @@ int Q_strncmp (char *s1, char *s2, int count)
                s1++;
                s2++;
        }
-       
+
        return -1;
 }
 */
@@ -224,7 +226,7 @@ int Q_strncasecmp (char *s1, char *s2, int n)
 
                if (!n--)
                        return 0;               // strings are equal until end point
-               
+
                if (c1 != c2)
                {
                        if (c1 >= 'a' && c1 <= 'z')
@@ -239,7 +241,7 @@ int Q_strncasecmp (char *s1, char *s2, int n)
 //              s1++;
 //              s2++;
        }
-       
+
        return -1;
 }
 
@@ -410,10 +412,12 @@ short   ShortSwap (short l)
        return (b1<<8) + b2;
 }
 
+#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
 short   ShortNoSwap (short l)
 {
        return l;
 }
+#endif
 
 int    LongSwap (int l)
 {
@@ -427,10 +431,12 @@ int    LongSwap (int l)
        return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
 }
 
+#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
 int     LongNoSwap (int l)
 {
        return l;
 }
+#endif
 
 float FloatSwap (float f)
 {
@@ -439,8 +445,8 @@ float FloatSwap (float f)
                float   f;
                byte    b[4];
        } dat1, dat2;
-       
-       
+
+
        dat1.f = f;
        dat2.b[0] = dat1.b[3];
        dat2.b[1] = dat1.b[2];
@@ -449,10 +455,12 @@ float FloatSwap (float f)
        return dat2.f;
 }
 
+#if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
 float FloatNoSwap (float f)
 {
        return f;
 }
+#endif
 
 /*
 ==============================================================================
@@ -496,7 +504,7 @@ void MSG_WriteByte (sizebuf_t *sb, int c)
 void MSG_WriteShort (sizebuf_t *sb, int c)
 {
        byte    *buf;
-       
+
 //#ifdef PARANOID
 //     if (c < ((short)0x8000) || c > (short)0x7fff)
 //             Sys_Error ("MSG_WriteShort: range error");
@@ -510,7 +518,7 @@ void MSG_WriteShort (sizebuf_t *sb, int c)
 void MSG_WriteLong (sizebuf_t *sb, int c)
 {
        byte    *buf;
-       
+
        buf = SZ_GetSpace (sb, 4);
        buf[0] = c&0xff;
        buf[1] = (c>>8)&0xff;
@@ -525,11 +533,11 @@ void MSG_WriteFloat (sizebuf_t *sb, float f)
                float   f;
                int     l;
        } dat;
-       
-       
+
+
        dat.f = f;
        dat.l = LittleLong (dat.l);
-       
+
        SZ_Write (sb, &dat.l, 4);
 }
 
@@ -556,18 +564,29 @@ void MSG_WriteCoord (sizebuf_t *sb, float f)
        if (dpprotocol)
                MSG_WriteFloat(sb, f);
        else
-               MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
+       {
+               if (f >= 0)
+                       MSG_WriteShort (sb, (int)(f*8.0f + 0.5f));
+               else
+                       MSG_WriteShort (sb, (int)(f*8.0f - 0.5f));
+       }
 }
 
 void MSG_WritePreciseAngle (sizebuf_t *sb, float f)
 {
-       MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
+       if (f >= 0)
+               MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535);
+       else
+               MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535);
 }
 
-// LordHavoc: round to nearest value, rather than rounding down, fixes crosshair problem
+// LordHavoc: round to nearest value, rather than rounding toward zero, fixes crosshair problem
 void MSG_WriteAngle (sizebuf_t *sb, float f)
 {
-       MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
+       if (f >= 0)
+               MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255);
+       else
+               MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255);
 }
 
 //
@@ -598,7 +617,7 @@ int MSG_ReadChar (void)
                
        c = (signed char)net_message.data[msg_readcount];
        msg_readcount++;
-       
+
        return c;
 }
 
@@ -613,7 +632,7 @@ int MSG_ReadByte (void)
                msg_badread = true;
                return -1;
        }
-               
+
        c = (unsigned char)net_message.data[msg_readcount];
        msg_readcount++;
        
@@ -624,7 +643,7 @@ int MSG_ReadByte (void)
 int MSG_ReadShort (void)
 {
        int     c;
-       
+
        if (msg_readcount+2 > net_message.cursize)
        {
                msg_badread = true;
@@ -737,11 +756,12 @@ float MSG_ReadPreciseAngle (void)
 
 //===========================================================================
 
-void SZ_Alloc (sizebuf_t *buf, int startsize)
+void SZ_Alloc (sizebuf_t *buf, int startsize, char *name)
 {
        if (startsize < 256)
                startsize = 256;
-       buf->data = Hunk_AllocName (startsize, "sizebuf");
+       buf->mempool = Mem_AllocPool(name);
+       buf->data = Mem_Alloc(buf->mempool, startsize);
        buf->maxsize = startsize;
        buf->cursize = 0;
 }
@@ -749,9 +769,9 @@ void SZ_Alloc (sizebuf_t *buf, int startsize)
 
 void SZ_Free (sizebuf_t *buf)
 {
-//      Z_Free (buf->data);
-//      buf->data = NULL;
-//      buf->maxsize = 0;
+       Mem_FreePool(&buf->mempool);
+       buf->data = NULL;
+       buf->maxsize = 0;
        buf->cursize = 0;
 }
 
@@ -763,18 +783,18 @@ void SZ_Clear (sizebuf_t *buf)
 void *SZ_GetSpace (sizebuf_t *buf, int length)
 {
        void    *data;
-       
+
        if (buf->cursize + length > buf->maxsize)
        {
                if (!buf->allowoverflow)
                        Host_Error ("SZ_GetSpace: overflow without allowoverflow set - use -zone on the commandline for more zone memory, default: 128k (quake original default was 48k)");
-               
+
                if (length > buf->maxsize)
                        Host_Error ("SZ_GetSpace: %i is > full buffer size", length);
-                       
+
                buf->overflowed = true;
                Con_Printf ("SZ_GetSpace: overflow");
-               SZ_Clear (buf); 
+               SZ_Clear (buf);
        }
 
        data = buf->data + buf->cursize;
@@ -813,7 +833,7 @@ COM_SkipPath
 char *COM_SkipPath (char *pathname)
 {
        char    *last;
-       
+
        last = pathname;
        while (*pathname)
        {
@@ -1056,7 +1076,7 @@ void COM_CheckRegistered (void)
 //                     Sys_Error ("You must have the registered version to use modified games");
                return;
        }
-       
+
 //     Cvar_Set ("cmdline", com_cmdline);
        Cvar_Set ("registered", "1");
 //     static_registered = 1;
@@ -1166,42 +1186,6 @@ void COM_InitArgv (int argc, char **argv)
 }
 
 
-unsigned int qmalloctotal_alloc, qmalloctotal_alloccount, qmalloctotal_free, qmalloctotal_freecount;
-
-void *qmalloc(unsigned int size)
-{
-       unsigned int *mem;
-       qmalloctotal_alloc += size;
-       qmalloctotal_alloccount++;
-       mem = malloc(size+sizeof(unsigned int));
-       if (!mem)
-               return mem;
-       *mem = size;
-       return (void *)(mem + 1);
-}
-
-void qfree(void *mem)
-{
-       unsigned int *m;
-       if (!mem)
-               return;
-       m = mem;
-       m--; // back up to size
-       qmalloctotal_free += *m; // size
-       qmalloctotal_freecount++;
-       free(m);
-}
-
-extern void GL_TextureStats_PrintTotal(void);
-extern int hunk_low_used, hunk_high_used, hunk_size;
-void COM_Memstats_f(void)
-{
-       Con_Printf("%i malloc calls totalling %i bytes (%.4gMB)\n%i free calls totalling %i bytes (%.4gMB)\n%i bytes (%.4gMB) currently allocated\n", qmalloctotal_alloccount, qmalloctotal_alloc, qmalloctotal_alloc / 1048576.0, qmalloctotal_freecount, qmalloctotal_free, qmalloctotal_free / 1048576.0, qmalloctotal_alloc - qmalloctotal_free, (qmalloctotal_alloc - qmalloctotal_free) / 1048576.0);
-       GL_TextureStats_PrintTotal();
-       Con_Printf ("%i bytes (%.4gMB) of %.4gMB hunk in use\n", hunk_low_used + hunk_high_used, (hunk_low_used + hunk_high_used) / 1048576.0, hunk_size / 1048576.0);
-}
-
-
 extern void Mathlib_Init(void);
 
 /*
@@ -1209,12 +1193,12 @@ extern void Mathlib_Init(void);
 COM_Init
 ================
 */
-void COM_Init (char *basedir)
+void COM_Init (void)
 {
 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
        byte    swaptest[2] = {1,0};
 
-// set the byte swapping variables in a portable manner 
+// set the byte swapping variables in a portable manner
        if ( *(short *)swaptest == 1)
        {
                BigShort = ShortSwap;
@@ -1235,10 +1219,11 @@ void COM_Init (char *basedir)
        }
 #endif
 
+       pak_mempool = Mem_AllocPool("paks");
+
        Cvar_RegisterVariable (&registered);
        Cvar_RegisterVariable (&cmdline);
        Cmd_AddCommand ("path", COM_Path_f);
-       Cmd_AddCommand ("memstats", COM_Memstats_f);
 
        Mathlib_Init();
 
@@ -1297,16 +1282,18 @@ int     com_filesize;
 
 typedef struct
 {
-       char    name[MAX_QPATH];
-       int             filepos, filelen;
+       char name[MAX_QPATH];
+       int filepos, filelen;
 } packfile_t;
 
 typedef struct pack_s
 {
-       char    filename[MAX_OSPATH];
-       int             handle;
-       int             numfiles;
-       packfile_t      *files;
+       char filename[MAX_OSPATH];
+       int handle;
+       int numfiles;
+       packfile_t *files;
+       mempool_t *mempool;
+       struct pack_s *next;
 } pack_t;
 
 //
@@ -1314,19 +1301,21 @@ typedef struct pack_s
 //
 typedef struct
 {
-       char    name[56];
-       int             filepos, filelen;
+       char name[56];
+       int filepos, filelen;
 } dpackfile_t;
 
 typedef struct
 {
-       char    id[4];
-       int             dirofs;
-       int             dirlen;
+       char id[4];
+       int dirofs;
+       int dirlen;
 } dpackheader_t;
 
-// LordHavoc: was 2048, increased to 16384 and changed info[MAX_PACK_FILES] to a temporary malloc to avoid stack overflows
-#define MAX_FILES_IN_PACK       16384
+// LordHavoc: was 2048, increased to 65536 and changed info[MAX_PACK_FILES] to a temporary alloc
+#define MAX_FILES_IN_PACK       65536
+
+pack_t *packlist = NULL;
 
 #if CACHEENABLE
 char   com_cachedir[MAX_OSPATH];
@@ -1335,8 +1324,8 @@ char      com_gamedir[MAX_OSPATH];
 
 typedef struct searchpath_s
 {
-       char    filename[MAX_OSPATH];
-       pack_t  *pack;          // only one of filename / pack will be used
+       char filename[MAX_OSPATH];
+       pack_t *pack;          // only one of filename / pack will be used
        struct searchpath_s *next;
 } searchpath_t;
 
@@ -1351,7 +1340,7 @@ COM_Path_f
 void COM_Path_f (void)
 {
        searchpath_t    *s;
-       
+
        Con_Printf ("Current search path:\n");
        for (s=com_searchpaths ; s ; s=s->next)
        {
@@ -1431,7 +1420,7 @@ void COM_CopyFile (char *netpath, char *cachepath)
        int             in, out;
        int             remaining, count;
        char    buf[4096];
-       
+
        remaining = Sys_FileOpenRead (netpath, &in);            
        COM_CreatePath (cachepath);     // create directories up to the cache file
        out = Sys_FileOpenWrite (cachepath);
@@ -1631,10 +1620,9 @@ Filename are reletive to the quake directory.
 Always appends a 0 byte.
 ============
 */
-cache_user_t   *loadcache;
 byte                   *loadbuf;
 int                            loadsize;
-byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
+byte *COM_LoadFile (char *path, qboolean quiet)
 {
        QFile             *h;
        byte    *buf;
@@ -1650,64 +1638,22 @@ byte *COM_LoadFile (char *path, int usehunk, qboolean quiet)
                return NULL;
 
        loadsize = len;
-       
+
 // extract the filename base name for hunk tag
        COM_FileBase (path, base);
-       
-       switch (usehunk)
-       {
-       case 1:
-               buf = Hunk_AllocName (len+1, va("%s (file)", path));
-               if (!buf)
-                       Sys_Error ("COM_LoadFile: not enough hunk space for %s (size %i)", path, len);
-               break;
-//     case 0:
-//             buf = Z_Malloc (len+1);
-//             if (!buf)
-//                     Sys_Error ("COM_LoadFile: not enough zone space for %s (size %i)", path, len);
-//             break;
-//     case 3:
-//             buf = Cache_Alloc (loadcache, len+1, base);
-//             if (!buf)
-//                     Sys_Error ("COM_LoadFile: not enough cache space for %s (size %i)", path, len);
-//             break;
-       case 5:
-               buf = qmalloc (len+1);
-               if (!buf)
-                       Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
-               break;
-       default:
-               Sys_Error ("COM_LoadFile: bad usehunk");
-               break;
-       }
+
+       buf = Mem_Alloc(tempmempool, len+1);
+       if (!buf)
+               Sys_Error ("COM_LoadFile: not enough available memory for %s (size %i)", path, len);
 
        ((byte *)buf)[len] = 0;
 
-       Qread (h, buf, len);                     
+       Qread (h, buf, len);
        Qclose (h);
 
        return buf;
 }
 
-byte *COM_LoadHunkFile (char *path, qboolean quiet)
-{
-       return COM_LoadFile (path, 1, quiet);
-}
-
-// LordHavoc: returns malloc'd memory
-byte *COM_LoadMallocFile (char *path, qboolean quiet)
-{
-       return COM_LoadFile (path, 5, quiet);
-}
-
-/*
-void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet)
-{
-       loadcache = cu;
-       COM_LoadFile (path, 3, quiet);
-}
-*/
-
 /*
 =================
 COM_LoadPackFile
@@ -1722,51 +1668,54 @@ pack_t *COM_LoadPackFile (char *packfile)
 {
        dpackheader_t   header;
        int                             i;
-       packfile_t              *newfiles;
        int                             numpackfiles;
        pack_t                  *pack;
        int                             packhandle;
-       // LordHavoc: changed from stack array to temporary malloc, allowing huge pack directories
+       // LordHavoc: changed from stack array to temporary alloc, allowing huge pack directories
        dpackfile_t             *info;
 
        if (Sys_FileOpenRead (packfile, &packhandle) == -1)
        {
-//              Con_Printf ("Couldn't open %s\n", packfile);
+               //Con_Printf ("Couldn't open %s\n", packfile);
                return NULL;
        }
        Sys_FileRead (packhandle, (void *)&header, sizeof(header));
-       if (header.id[0] != 'P' || header.id[1] != 'A'
-       || header.id[2] != 'C' || header.id[3] != 'K')
+       if (memcmp(header.id, "PACK", 4))
                Sys_Error ("%s is not a packfile", packfile);
        header.dirofs = LittleLong (header.dirofs);
        header.dirlen = LittleLong (header.dirlen);
 
+       if (header.dirlen % sizeof(dpackfile_t))
+               Sys_Error ("%s has an invalid directory size", packfile);
+
        numpackfiles = header.dirlen / sizeof(dpackfile_t);
 
        if (numpackfiles > MAX_FILES_IN_PACK)
                Sys_Error ("%s has %i files", packfile, numpackfiles);
 
-       newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "pack file-table");
+       pack = Mem_Alloc(pak_mempool, sizeof (pack_t));
+       strcpy (pack->filename, packfile);
+       pack->handle = packhandle;
+       pack->numfiles = numpackfiles;
+       pack->mempool = Mem_AllocPool(packfile);
+       pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t));
+       pack->next = packlist;
+       packlist = pack;
 
-       info = qmalloc(sizeof(*info)*MAX_FILES_IN_PACK);
+       info = Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles);
        Sys_FileSeek (packhandle, header.dirofs);
        Sys_FileRead (packhandle, (void *)info, header.dirlen);
 
 // parse the directory
-       for (i=0 ; i<numpackfiles ; i++)
+       for (i = 0;i < numpackfiles;i++)
        {
-               strcpy (newfiles[i].name, info[i].name);
-               newfiles[i].filepos = LittleLong(info[i].filepos);
-               newfiles[i].filelen = LittleLong(info[i].filelen);
+               strcpy (pack->files[i].name, info[i].name);
+               pack->files[i].filepos = LittleLong(info[i].filepos);
+               pack->files[i].filelen = LittleLong(info[i].filelen);
        }
-       qfree(info);
 
-       pack = Hunk_AllocName (sizeof (pack_t), packfile);
-       strcpy (pack->filename, packfile);
-       pack->handle = packhandle;
-       pack->numfiles = numpackfiles;
-       pack->files = newfiles;
-       
+       Mem_Free(info);
+
        Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles);
        return pack;
 }
@@ -1777,7 +1726,7 @@ pack_t *COM_LoadPackFile (char *packfile)
 COM_AddGameDirectory
 
 Sets com_gamedir, adds the directory to the head of the path,
-then loads and adds pak1.pak pak2.pak ... 
+then loads and adds pak1.pak pak2.pak ...
 ================
 */
 void COM_AddGameDirectory (char *dir)
@@ -1792,7 +1741,7 @@ void COM_AddGameDirectory (char *dir)
 //
 // add the directory to the search path
 //
-       search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
+       search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
        strcpy (search->filename, dir);
        search->next = com_searchpaths;
        com_searchpaths = search;
@@ -1806,7 +1755,7 @@ void COM_AddGameDirectory (char *dir)
                pak = COM_LoadPackFile (pakfile);
                if (!pak)
                        break;
-               search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
+               search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
                search->pack = pak;
                search->next = com_searchpaths;
                com_searchpaths = search;
@@ -1919,7 +1868,7 @@ void COM_InitFilesystem (void)
                        if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-')
                                break;
 
-                       search = Hunk_AllocName (sizeof(searchpath_t), "pack info");
+                       search = Mem_Alloc(pak_mempool, sizeof(searchpath_t));
                        if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") )
                        {
                                search->pack = COM_LoadPackFile (com_argv[i]);
index c7a0be3..7d084f4 100644 (file)
--- a/common.h
+++ b/common.h
@@ -19,18 +19,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 // comndef.h  -- general definitions
 
-#if !defined BYTE_DEFINED
-typedef unsigned char          byte;
-#define BYTE_DEFINED 1
-#endif
-
-#undef true
-#undef false
-
-typedef enum {false, true}     qboolean;
-
-#include "quakeio.h"
-
 // LordHavoc: MSVC has a different name for snprintf
 #ifdef WIN32
 #define snprintf _snprintf
@@ -38,21 +26,17 @@ typedef enum {false, true}  qboolean;
 
 //============================================================================
 
-extern void *qmalloc(unsigned int size);
-extern void qfree(void *mem);
-
-//============================================================================
-
 typedef struct sizebuf_s
 {
        qboolean        allowoverflow;  // if false, do a Sys_Error
        qboolean        overflowed;             // set to true if the buffer size failed
-       byte    *data;
-       int             maxsize;
-       int             cursize;
+       byte            *data;
+       mempool_t       *mempool;
+       int                     maxsize;
+       int                     cursize;
 } sizebuf_t;
 
-void SZ_Alloc (sizebuf_t *buf, int startsize);
+void SZ_Alloc (sizebuf_t *buf, int startsize, char *name);
 void SZ_Free (sizebuf_t *buf);
 void SZ_Clear (sizebuf_t *buf);
 void *SZ_GetSpace (sizebuf_t *buf, int length);
@@ -60,12 +44,6 @@ void SZ_Write (sizebuf_t *buf, void *data, int length);
 void SZ_Print (sizebuf_t *buf, char *data);    // strcats onto the sizebuf
 
 //============================================================================
-
-#ifndef NULL
-#define NULL ((void *)0)
-#endif
-
-//============================================================================
 #if !defined(ENDIAN_LITTLE) && !defined(ENDIAN_BIG)
 #if  defined(__i386__) || defined(__ia64__) || defined(WIN32) || (defined(__alpha__) || defined(__alpha)) || defined(__arm__) || (defined(__mips__) && defined(__MIPSEL__)) || defined(__LITTLE_ENDIAN__)
 #define ENDIAN_LITTLE
@@ -179,7 +157,7 @@ extern      int             com_argc;
 extern char    **com_argv;
 
 int COM_CheckParm (char *parm);
-void COM_Init (char *path);
+void COM_Init (void);
 void COM_InitArgv (int argc, char **argv);
 
 char *COM_SkipPath (char *pathname);
@@ -194,7 +172,6 @@ char        *va(char *format, ...);
 //============================================================================
 
 extern int com_filesize;
-struct cache_user_s;
 
 extern char    com_gamedir[MAX_OSPATH];
 
@@ -203,11 +180,7 @@ int COM_FOpenFile (char *filename, QFile **file, qboolean quiet, qboolean zip);
 
 // set by COM_LoadFile functions
 extern int loadsize;
-byte *COM_LoadHunkFile (char *path, qboolean quiet);
-byte *COM_LoadMallocFile (char *path, qboolean quiet);
-//void COM_LoadCacheFile (char *path, struct cache_user_s *cu, qboolean quiet);
-
-byte *COM_LoadFile (char *path, int usehunk, qboolean quiet);
+byte *COM_LoadFile (char *path, qboolean quiet);
 
 int COM_FileExists(char *filename);
 
index 69b093d..d8654f3 100644 (file)
--- a/console.c
+++ b/console.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -63,10 +63,12 @@ extern      char    key_lines[32][MAXCMDLINE];
 extern int             edit_line;
 extern int             key_linepos;
 extern int             key_insert;
-               
+
 
 qboolean       con_initialized;
 
+mempool_t      *console_mempool;
+
 int            con_notifylines;                // scan lines to clear for notify lines
 
 extern void M_Menu_Main_f (void);
@@ -230,10 +232,11 @@ void Con_Init (void)
                        sprintf (temp, "%s%s", com_gamedir, t2);
                        unlink (temp);
                }
-               logfile.value = 1;
+               logfile.integer = 1;
        }
 
-       con_text = Hunk_AllocName (CON_TEXTSIZE, "context");
+       console_mempool = Mem_AllocPool("console");
+       con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
        memset (con_text, ' ', CON_TEXTSIZE);
        con_linewidth = -1;
        Con_CheckResize ();
@@ -439,7 +442,7 @@ void Con_DPrintf (char *fmt, ...)
        va_list         argptr;
        char            msg[MAXPRINTMSG];
                
-       if (!developer.value)
+       if (!developer.integer)
                return;                 // don't confuse non-developers with techie stuff...
 
        va_start (argptr,fmt);
@@ -798,6 +801,6 @@ Con_CompleteCommandLine (void)
        }
        for (i = 0; i < 3; i++)
                if (list[i])
-                       qfree (list[i]);
+                       Mem_Free(list[i]);
 }
 
diff --git a/cvar.c b/cvar.c
index d38c5ba..bee89d9 100644 (file)
--- a/cvar.c
+++ b/cvar.c
@@ -144,7 +144,7 @@ Cvar_CompleteBuildList (char *partial)
        char    **buf;
 
        len = strlen(partial);
-       buf = qmalloc(sizeofbuf + sizeof (char *));
+       buf = Mem_Alloc(tempmempool, sizeofbuf + sizeof (char *));
        // Loop through the alias list and print all matches
        for (cvar = cvar_vars; cvar; cvar = cvar->next)
                if (!strncasecmp(partial, cvar->name, len))
@@ -178,6 +178,7 @@ void Cvar_Set (char *var_name, char *value)
        var->string = Z_Malloc (strlen(value)+1);
        strcpy (var->string, value);
        var->value = atof (var->string);
+       var->integer = (int) var->value;
        if ((var->flags & CVAR_NOTIFY) && changed)
        {
                if (sv.active)
@@ -229,6 +230,7 @@ void Cvar_RegisterVariable (cvar_t *variable)
        variable->string = Z_Malloc (strlen(variable->string)+1);
        strcpy (variable->string, oldstr);
        variable->value = atof (variable->string);
+       variable->integer = (int) variable->value;
 
 // link the variable in
        variable->next = cvar_vars;
diff --git a/cvar.h b/cvar.h
index 61871b6..0751a2d 100644 (file)
--- a/cvar.h
+++ b/cvar.h
@@ -97,7 +97,7 @@ typedef struct cvar_s
        char                    *string;
 //     qboolean                archive;                // set to true to cause it to be saved to vars.rc
 //     qboolean                server;         // notifies players when changed
-       int                             intvalue;
+       int                             integer;
        float                   value;
        float                   vector[3];
        menucvar_t              menuinfo;
diff --git a/draw.h b/draw.h
index 80ba2d1..b8b6ea3 100644 (file)
--- a/draw.h
+++ b/draw.h
@@ -21,8 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 // draw.h -- these are the only functions outside the refresh allowed
 // to touch the vid buffer
 
-extern qpic_t          *draw_disc;     // also used on sbar
-
 void Draw_Init (void);
 void Draw_Character (int x, int y, int num);
 void Draw_GenericPic (rtexture_t *tex, float red, float green, float blue, float alpha, int x, int y, int width, int height);
index 3425daf..31b140d 100644 (file)
@@ -18,7 +18,7 @@ void fractalnoise(byte *noise, int size, int startgrid)
        startgrid = bound(0, startgrid, size);
 
        amplitude = 0xFFFF; // this gets halved before use
-       noisebuf = qmalloc(size*size*sizeof(int));
+       noisebuf = Mem_Alloc(tempmempool, size*size*sizeof(int));
        memset(noisebuf, 0, size*size*sizeof(int));
 
        for (g2 = startgrid;g2;g2 >>= 1)
@@ -60,6 +60,6 @@ void fractalnoise(byte *noise, int size, int startgrid)
        for (y = 0;y < size;y++)
                for (x = 0;x < size;x++)
                        *noise++ = (byte) (((n(x,y) - min) * 256) / max);
-       qfree(noisebuf);
+       Mem_Free(noisebuf);
 #undef n
 }
diff --git a/gl_backend.c b/gl_backend.c
new file mode 100644 (file)
index 0000000..adb0fcc
--- /dev/null
@@ -0,0 +1,979 @@
+
+#include "quakedef.h"
+
+static int max_meshs;
+static int max_batch;
+static int max_verts; // always max_meshs * 3
+#define TRANSDEPTHRES 4096
+
+static cvar_t gl_mesh_maxtriangles = {0, "gl_mesh_maxtriangles", "21760"};
+static cvar_t gl_mesh_batchtriangles = {0, "gl_mesh_batchtriangles", "1024"};
+static cvar_t gl_mesh_merge = {0, "gl_mesh_merge", "1"};
+static cvar_t gl_mesh_floatcolors = {0, "gl_mesh_floatcolors", "1"};
+
+typedef struct buf_mesh_s
+{
+       struct buf_mesh_s *next;
+       int depthmask;
+       int blendfunc1, blendfunc2;
+       int textures[MAX_TEXTUREUNITS];
+       float texturergbscale[MAX_TEXTUREUNITS];
+       int firsttriangle;
+       int triangles;
+}
+buf_mesh_t;
+
+typedef struct buf_transtri_s
+{
+       struct buf_transtri_s *next;
+       buf_mesh_t *mesh;
+       int index[3];
+}
+buf_transtri_t;
+
+typedef struct buf_tri_s
+{
+       int index[3];
+}
+buf_tri_t;
+
+typedef struct
+{
+       float v[4];
+}
+buf_vertex_t;
+
+typedef struct
+{
+       float c[4];
+}
+buf_fcolor_t;
+
+typedef struct
+{
+       byte c[4];
+}
+buf_bcolor_t;
+
+typedef struct
+{
+       float t[2];
+}
+buf_texcoord_t;
+
+static float meshfarclip;
+static int currentmesh, currenttriangle, currentvertex, backendunits, backendactive, meshmerge, floatcolors, transranout;
+static buf_mesh_t *buf_mesh;
+static buf_tri_t *buf_tri;
+static buf_vertex_t *buf_vertex;
+static buf_fcolor_t *buf_fcolor;
+static buf_bcolor_t *buf_bcolor;
+static buf_texcoord_t *buf_texcoord[MAX_TEXTUREUNITS];
+
+static int currenttransmesh, currenttransvertex, currenttranstriangle;
+static buf_mesh_t *buf_transmesh;
+static buf_transtri_t *buf_transtri;
+static buf_transtri_t **buf_transtri_list;
+static buf_vertex_t *buf_transvertex;
+static buf_fcolor_t *buf_transfcolor;
+static buf_bcolor_t *buf_transbcolor;
+static buf_texcoord_t *buf_transtexcoord[MAX_TEXTUREUNITS];
+
+static mempool_t *gl_backend_mempool;
+
+static void gl_backend_start(void)
+{
+       int i;
+
+       max_verts = max_meshs * 3;
+
+       gl_backend_mempool = Mem_AllocPool("GL_Backend");
+
+#define BACKENDALLOC(var, count, sizeofstruct)\
+       {\
+               var = Mem_Alloc(gl_backend_mempool, count * sizeof(sizeofstruct));\
+               if (var == NULL)\
+                       Sys_Error("gl_backend_start: unable to allocate memory\n");\
+               memset(var, 0, count * sizeof(sizeofstruct));\
+       }
+
+       BACKENDALLOC(buf_mesh, max_meshs, buf_mesh_t)
+       BACKENDALLOC(buf_tri, max_meshs, buf_tri_t)
+       BACKENDALLOC(buf_vertex, max_verts, buf_vertex_t)
+       BACKENDALLOC(buf_fcolor, max_verts, buf_fcolor_t)
+       BACKENDALLOC(buf_bcolor, max_verts, buf_bcolor_t)
+
+       BACKENDALLOC(buf_transmesh, max_meshs, buf_mesh_t)
+       BACKENDALLOC(buf_transtri, max_meshs, buf_transtri_t)
+       BACKENDALLOC(buf_transtri_list, TRANSDEPTHRES, buf_transtri_t *)
+       BACKENDALLOC(buf_transvertex, max_verts, buf_vertex_t)
+       BACKENDALLOC(buf_transfcolor, max_verts, buf_fcolor_t)
+       BACKENDALLOC(buf_transbcolor, max_verts, buf_bcolor_t)
+
+       for (i = 0;i < MAX_TEXTUREUNITS;i++)
+       {
+               // only allocate as many texcoord arrays as we need
+               if (i < gl_textureunits)
+               {
+                       BACKENDALLOC(buf_texcoord[i], max_verts, buf_texcoord_t)
+                       BACKENDALLOC(buf_transtexcoord[i], max_verts, buf_texcoord_t)
+               }
+               else
+               {
+                       buf_texcoord[i] = NULL;
+                       buf_transtexcoord[i] = NULL;
+               }
+       }
+       backendunits = min(MAX_TEXTUREUNITS, gl_textureunits);
+       backendactive = true;
+}
+
+static void gl_backend_shutdown(void)
+{
+       int i;
+       /*
+#define BACKENDFREE(var)\
+       if (var)\
+       {\
+               Mem_Free(var);\
+               var = NULL;\
+       }
+       */
+#define BACKENDFREE(var) var = NULL;
+
+       BACKENDFREE(buf_mesh)
+       BACKENDFREE(buf_tri)
+       BACKENDFREE(buf_vertex)
+       BACKENDFREE(buf_fcolor)
+       BACKENDFREE(buf_bcolor)
+
+       BACKENDFREE(buf_transmesh)
+       BACKENDFREE(buf_transtri)
+       BACKENDFREE(buf_transtri_list)
+       BACKENDFREE(buf_transvertex)
+       BACKENDFREE(buf_transfcolor)
+       BACKENDFREE(buf_transbcolor)
+
+       for (i = 0;i < MAX_TEXTUREUNITS;i++)
+       {
+               BACKENDFREE(buf_texcoord[i])
+               BACKENDFREE(buf_transtexcoord[i])
+       }
+
+       Mem_FreePool(&gl_backend_mempool);
+
+       backendunits = 0;
+       backendactive = false;
+}
+
+static void gl_backend_bufferchanges(int init)
+{
+       // 21760 is (65536 / 3) rounded off to a multiple of 128
+       if (gl_mesh_maxtriangles.integer < 256)
+               Cvar_SetValue("gl_mesh_maxtriangles", 256);
+       if (gl_mesh_maxtriangles.integer > 21760)
+               Cvar_SetValue("gl_mesh_maxtriangles", 21760);
+
+       if (gl_mesh_batchtriangles.integer < 0)
+               Cvar_SetValue("gl_mesh_batchtriangles", 0);
+       if (gl_mesh_batchtriangles.integer > gl_mesh_maxtriangles.integer)
+               Cvar_SetValue("gl_mesh_batchtriangles", gl_mesh_maxtriangles.integer);
+
+       max_batch = gl_mesh_batchtriangles.integer;
+
+       if (max_meshs != gl_mesh_maxtriangles.integer)
+       {
+               max_meshs = gl_mesh_maxtriangles.integer;
+
+               if (!init)
+               {
+                       gl_backend_shutdown();
+                       gl_backend_start();
+               }
+       }
+}
+
+float r_farclip, r_newfarclip;
+
+static void gl_backend_newmap(void)
+{
+       r_farclip = r_newfarclip = 2048.0f;
+}
+
+int polyindexarray[768];
+
+void gl_backend_init(void)
+{
+       int i;
+       Cvar_RegisterVariable(&gl_mesh_maxtriangles);
+       Cvar_RegisterVariable(&gl_mesh_batchtriangles);
+       Cvar_RegisterVariable(&gl_mesh_merge);
+       Cvar_RegisterVariable(&gl_mesh_floatcolors);
+       R_RegisterModule("GL_Backend", gl_backend_start, gl_backend_shutdown, gl_backend_newmap);
+       gl_backend_bufferchanges(true);
+       for (i = 0;i < 256;i++)
+       {
+               polyindexarray[i*3+0] = 0;
+               polyindexarray[i*3+1] = i + 1;
+               polyindexarray[i*3+2] = i + 2;
+       }
+}
+
+static float viewdist;
+
+int c_meshtris;
+
+// called at beginning of frame
+void R_Mesh_Clear(void)
+{
+       if (!backendactive)
+               Sys_Error("R_Mesh_Clear: called when backend is not active\n");
+
+       gl_backend_bufferchanges(false);
+
+       currentmesh = 0;
+       currenttriangle = 0;
+       currentvertex = 0;
+       currenttransmesh = 0;
+       currenttranstriangle = 0;
+       currenttransvertex = 0;
+       meshfarclip = 0;
+       meshmerge = gl_mesh_merge.integer;
+       floatcolors = gl_mesh_floatcolors.integer;
+       transranout = false;
+       viewdist = DotProduct(r_origin, vpn);
+
+       c_meshtris = 0;
+}
+
+#ifdef DEBUGGL
+void GL_PrintError(int errornumber, char *filename, int linenumber)
+{
+       switch(errornumber)
+       {
+       case GL_INVALID_ENUM:
+               Con_Printf("GL_INVALID_ENUM at %s:%i\n", filename, linenumber);
+               break;
+       case GL_INVALID_VALUE:
+               Con_Printf("GL_INVALID_VALUE at %s:%i\n", filename, linenumber);
+               break;
+       case GL_INVALID_OPERATION:
+               Con_Printf("GL_INVALID_OPERATION at %s:%i\n", filename, linenumber);
+               break;
+       case GL_STACK_OVERFLOW:
+               Con_Printf("GL_STACK_OVERFLOW at %s:%i\n", filename, linenumber);
+               break;
+       case GL_STACK_UNDERFLOW:
+               Con_Printf("GL_STACK_UNDERFLOW at %s:%i\n", filename, linenumber);
+               break;
+       case GL_OUT_OF_MEMORY:
+               Con_Printf("GL_OUT_OF_MEMORY at %s:%i\n", filename, linenumber);
+               break;
+       case GL_TABLE_TOO_LARGE:
+               Con_Printf("GL_TABLE_TOO_LARGE at %s:%i\n", filename, linenumber);
+               break;
+       default:
+               Con_Printf("GL UNKNOWN (%i) at %s:%i\n", errornumber, filename, linenumber);
+               break;
+       }
+}
+
+int errornumber = 0;
+#endif
+
+// renders mesh buffers, called to flush buffers when full
+void R_Mesh_Render(void)
+{
+       int i, k, blendfunc1, blendfunc2, blend, depthmask, unit = 0, clientunit = 0, firsttriangle, triangles, texture[MAX_TEXTUREUNITS];
+       float farclip, texturergbscale[MAX_TEXTUREUNITS];
+       buf_mesh_t *mesh;
+       if (!backendactive)
+               Sys_Error("R_Mesh_Render: called when backend is not active\n");
+       if (!currentmesh)
+               return;
+
+CHECKGLERROR
+
+       farclip = meshfarclip + 256.0f - viewdist; // + 256 just to be safe
+
+       // push out farclip for next frame
+       if (farclip > r_newfarclip)
+               r_newfarclip = ceil((farclip + 255) / 256) * 256 + 256;
+
+       for (i = 0;i < backendunits;i++)
+               texturergbscale[i] = 1;
+
+       glEnable(GL_CULL_FACE);
+CHECKGLERROR
+       glCullFace(GL_FRONT);
+CHECKGLERROR
+       glEnable(GL_DEPTH_TEST);
+CHECKGLERROR
+       blendfunc1 = GL_ONE;
+       blendfunc2 = GL_ZERO;
+       glBlendFunc(blendfunc1, blendfunc2);
+CHECKGLERROR
+       blend = 0;
+       glDisable(GL_BLEND);
+CHECKGLERROR
+       depthmask = 1;
+       glDepthMask(depthmask);
+CHECKGLERROR
+
+CHECKGLERROR
+       glVertexPointer(3, GL_FLOAT, sizeof(buf_vertex_t), buf_vertex);
+CHECKGLERROR
+       glEnableClientState(GL_VERTEX_ARRAY);
+CHECKGLERROR
+       if (floatcolors)
+       {
+               glColorPointer(4, GL_FLOAT, sizeof(buf_fcolor_t), buf_fcolor);
+CHECKGLERROR
+       }
+       else
+       {
+               glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(buf_bcolor_t), buf_bcolor);
+CHECKGLERROR
+       }
+       glEnableClientState(GL_COLOR_ARRAY);
+CHECKGLERROR
+
+       if (backendunits > 1)
+       {
+               for (i = 0;i < backendunits;i++)
+               {
+                       glActiveTextureARB(GL_TEXTURE0_ARB + (unit = i));
+CHECKGLERROR
+                       glBindTexture(GL_TEXTURE_2D, (texture[i] = 0));
+CHECKGLERROR
+                       glDisable(GL_TEXTURE_2D);
+CHECKGLERROR
+                       if (gl_combine.integer)
+                       {
+                               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_CONSTANT_ARB);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS_ARB);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, GL_CONSTANT_ARB);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+CHECKGLERROR
+                               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, GL_SRC_ALPHA);
+CHECKGLERROR
+                               glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1.0f);
+CHECKGLERROR
+                               glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1.0f);
+CHECKGLERROR
+                       }
+                       else
+                       {
+                               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+CHECKGLERROR
+                       }
+
+                       glClientActiveTextureARB(GL_TEXTURE0_ARB + (clientunit = i));
+CHECKGLERROR
+                       glTexCoordPointer(2, GL_FLOAT, sizeof(buf_texcoord_t), buf_texcoord[i]);
+CHECKGLERROR
+                       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+               }
+       }
+       else
+       {
+               glBindTexture(GL_TEXTURE_2D, (texture[0] = 0));
+CHECKGLERROR
+               glDisable(GL_TEXTURE_2D);
+CHECKGLERROR
+               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+CHECKGLERROR
+
+               glTexCoordPointer(2, GL_FLOAT, sizeof(buf_texcoord_t), buf_texcoord[0]);
+CHECKGLERROR
+               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+       }
+
+       // lock as early as possible
+       GL_LockArray(0, currentvertex);
+CHECKGLERROR
+
+       for (k = 0;k < currentmesh;)
+       {
+               mesh = &buf_mesh[k];
+
+               if (backendunits > 1)
+               {
+//                     int topunit = 0;
+                       for (i = 0;i < backendunits;i++)
+                       {
+                               if (texture[i] != mesh->textures[i])
+                               {
+                                       if (unit != i)
+                                       {
+                                               glActiveTextureARB(GL_TEXTURE0_ARB + (unit = i));
+CHECKGLERROR
+                                       }
+                                       if (texture[i] == 0)
+                                       {
+                                               glEnable(GL_TEXTURE_2D);
+CHECKGLERROR
+                                               // have to disable texcoord array on disabled texture
+                                               // units due to NVIDIA driver bug with
+                                               // compiled_vertex_array
+                                               if (clientunit != i)
+                                               {
+                                                       glClientActiveTextureARB(GL_TEXTURE0_ARB + (clientunit = i));
+CHECKGLERROR
+                                               }
+                                               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+                                       }
+                                       glBindTexture(GL_TEXTURE_2D, (texture[i] = mesh->textures[i]));
+CHECKGLERROR
+                                       if (texture[i] == 0)
+                                       {
+                                               glDisable(GL_TEXTURE_2D);
+CHECKGLERROR
+                                               // have to disable texcoord array on disabled texture
+                                               // units due to NVIDIA driver bug with
+                                               // compiled_vertex_array
+                                               if (clientunit != i)
+                                               {
+                                                       glClientActiveTextureARB(GL_TEXTURE0_ARB + (clientunit = i));
+CHECKGLERROR
+                                               }
+                                               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+                                       }
+                               }
+                               if (texturergbscale[i] != mesh->texturergbscale[i])
+                               {
+                                       if (unit != i)
+                                       {
+                                               glActiveTextureARB(GL_TEXTURE0_ARB + (unit = i));
+CHECKGLERROR
+                                       }
+                                       glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, (texturergbscale[i] = mesh->texturergbscale[i]));
+CHECKGLERROR
+                               }
+//                             if (texture[i])
+//                                     topunit = i;
+                       }
+//                     if (unit != topunit)
+//                     {
+//                             glActiveTextureARB(GL_TEXTURE0_ARB + (unit = topunit));
+//CHECKGLERROR
+//                     }
+               }
+               else
+               {
+                       if (texture[0] != mesh->textures[0])
+                       {
+                               if (texture[0] == 0)
+                               {
+                                       glEnable(GL_TEXTURE_2D);
+CHECKGLERROR
+                                       glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+                               }
+                               glBindTexture(GL_TEXTURE_2D, (texture[0] = mesh->textures[0]));
+CHECKGLERROR
+                               if (texture[0] == 0)
+                               {
+                                       glDisable(GL_TEXTURE_2D);
+CHECKGLERROR
+                                       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+                               }
+                       }
+               }
+               if (blendfunc1 != mesh->blendfunc1 || blendfunc2 != mesh->blendfunc2)
+               {
+                       blendfunc1 = mesh->blendfunc1;
+                       blendfunc2 = mesh->blendfunc2;
+                       glBlendFunc(blendfunc1, blendfunc2);
+CHECKGLERROR
+                       if (blendfunc2 == GL_ZERO)
+                       {
+                               if (blendfunc1 == GL_ONE)
+                               {
+                                       if (blend)
+                                       {
+                                               blend = 0;
+                                               glDisable(GL_BLEND);
+CHECKGLERROR
+                                       }
+                               }
+                               else
+                               {
+                                       if (!blend)
+                                       {
+                                               blend = 1;
+                                               glEnable(GL_BLEND);
+CHECKGLERROR
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (!blend)
+                               {
+                                       blend = 1;
+                                       glEnable(GL_BLEND);
+CHECKGLERROR
+                               }
+                       }
+               }
+               if (depthmask != mesh->depthmask)
+               {
+                       depthmask = mesh->depthmask;
+                       glDepthMask(depthmask);
+CHECKGLERROR
+               }
+
+               firsttriangle = mesh->firsttriangle;
+               triangles = mesh->triangles;
+               mesh = &buf_mesh[++k];
+
+               if (meshmerge)
+               {
+                       #if MAX_TEXTUREUNITS != 4
+                       #error update this code
+                       #endif
+                       while (k < currentmesh
+                               && mesh->blendfunc1 == blendfunc1
+                               && mesh->blendfunc2 == blendfunc2
+                               && mesh->depthmask == depthmask
+                               && mesh->textures[0] == texture[0]
+                               && mesh->textures[1] == texture[1]
+                               && mesh->textures[2] == texture[2]
+                               && mesh->textures[3] == texture[3]
+                               && mesh->texturergbscale[0] == texturergbscale[0]
+                               && mesh->texturergbscale[1] == texturergbscale[1]
+                               && mesh->texturergbscale[2] == texturergbscale[2]
+                               && mesh->texturergbscale[3] == texturergbscale[3])
+                       {
+                               triangles += mesh->triangles;
+                               mesh = &buf_mesh[++k];
+                       }
+               }
+
+               glDrawElements(GL_TRIANGLES, triangles * 3, GL_UNSIGNED_INT, (unsigned int *)&buf_tri[firsttriangle]);
+CHECKGLERROR
+       }
+
+       currentmesh = 0;
+       currenttriangle = 0;
+       currentvertex = 0;
+
+       GL_UnlockArray();
+CHECKGLERROR
+
+       if (backendunits > 1)
+       {
+               for (i = backendunits - 1;i >= 0;i--)
+               {
+                       glActiveTextureARB(GL_TEXTURE0_ARB + (unit = i));
+CHECKGLERROR
+                       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+CHECKGLERROR
+                       if (gl_combine.integer)
+                       {
+                               glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1.0f);
+CHECKGLERROR
+                       }
+                       if (i > 0)
+                       {
+                               glDisable(GL_TEXTURE_2D);
+CHECKGLERROR
+                       }
+                       else
+                       {
+                               glEnable(GL_TEXTURE_2D);
+CHECKGLERROR
+                       }
+                       glBindTexture(GL_TEXTURE_2D, 0);
+CHECKGLERROR
+
+                       glClientActiveTextureARB(GL_TEXTURE0_ARB + (clientunit = i));
+CHECKGLERROR
+                       glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+               }
+       }
+       else
+       {
+               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+CHECKGLERROR
+               glEnable(GL_TEXTURE_2D);
+CHECKGLERROR
+               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+CHECKGLERROR
+       }
+       glDisableClientState(GL_COLOR_ARRAY);
+CHECKGLERROR
+       glDisableClientState(GL_VERTEX_ARRAY);
+CHECKGLERROR
+
+       glDisable(GL_BLEND);
+CHECKGLERROR
+       glDepthMask(1);
+CHECKGLERROR
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+CHECKGLERROR
+}
+
+void R_Mesh_AddTransparent(void)
+{
+       int i, j, k;
+       float viewdistcompare, centerscaler, dist1, dist2, dist3, center, maxdist;
+       buf_vertex_t *vert1, *vert2, *vert3;
+       buf_transtri_t *tri;
+       buf_mesh_t *mesh;
+
+       // process and add transparent mesh triangles
+       if (!currenttranstriangle)
+               return;
+
+       // map farclip to 0-4095 list range
+       centerscaler = (TRANSDEPTHRES / r_farclip) * (1.0f / 3.0f);
+       viewdistcompare = viewdist + 4.0f;
+
+       memset(buf_transtri_list, 0, TRANSDEPTHRES * sizeof(buf_transtri_t *));
+
+       // process in reverse because transtri_list adding code is in reverse as well
+       k = 0;
+       for (j = currenttranstriangle - 1;j >= 0;j--)
+       {
+               tri = &buf_transtri[j];
+
+               vert1 = &buf_transvertex[tri->index[0]];
+               vert2 = &buf_transvertex[tri->index[1]];
+               vert3 = &buf_transvertex[tri->index[2]];
+
+               dist1 = DotProduct(vert1->v, vpn);
+               dist2 = DotProduct(vert2->v, vpn);
+               dist3 = DotProduct(vert3->v, vpn);
+
+               maxdist = max(dist1, max(dist2, dist3));
+               if (maxdist < viewdistcompare)
+                       continue;
+
+               center = (dist1 + dist2 + dist3) * centerscaler - viewdist;
+#if SLOWMATH
+               i = (int) center;
+               i = bound(0, i, (TRANSDEPTHRES - 1));
+#else
+               if (center < 0.0f)
+                       center = 0.0f;
+               center += 8388608.0f;
+               i = *((long *)&center) & 0x7FFFFF;
+               i = min(i, (TRANSDEPTHRES - 1));
+#endif
+               tri->next = buf_transtri_list[i];
+               buf_transtri_list[i] = tri;
+               k++;
+       }
+
+       if (currentmesh + k > max_meshs || currenttriangle + k > max_batch || currentvertex + currenttransvertex > max_verts)
+               R_Mesh_Render();
+
+       // note: can't batch these because they can be rendered in any order
+       // there can never be more transparent triangles than fit in main buffers
+       memcpy(&buf_vertex[currentvertex], &buf_transvertex[0], currenttransvertex * sizeof(buf_vertex_t));
+       if (floatcolors)
+               memcpy(&buf_fcolor[currentvertex], &buf_transfcolor[0], currenttransvertex * sizeof(buf_fcolor_t));
+       else
+               memcpy(&buf_fcolor[currentvertex], &buf_transbcolor[0], currenttransvertex * sizeof(buf_bcolor_t));
+       for (i = 0;i < backendunits;i++)
+               memcpy(&buf_texcoord[i][currentvertex], &buf_transtexcoord[i][0], currenttransvertex * sizeof(buf_texcoord_t));
+
+       for (j = TRANSDEPTHRES - 1;j >= 0;j--)
+       {
+               if ((tri = buf_transtri_list[j]))
+               {
+                       while(tri)
+                       {
+                               mesh = &buf_mesh[currentmesh++];
+                               *mesh = *tri->mesh; // copy mesh properties
+                               buf_tri[currenttriangle].index[0] = tri->index[0] + currentvertex;
+                               buf_tri[currenttriangle].index[1] = tri->index[1] + currentvertex;
+                               buf_tri[currenttriangle].index[2] = tri->index[2] + currentvertex;
+                               mesh->firsttriangle = currenttriangle++;
+                               mesh->triangles = 1;
+                               tri = tri->next;
+                       }
+               }
+       }
+       currentvertex += currenttransvertex;
+       currenttransmesh = 0;
+       currenttranstriangle = 0;
+       currenttransvertex = 0;
+}
+
+void R_Mesh_Draw(const rmeshinfo_t *m)
+{
+       static int i, j, *index, overbright;
+       static float c, *in, scaler, cr, cg, cb, ca;
+       static buf_mesh_t *mesh;
+       static buf_vertex_t *vert;
+       static buf_fcolor_t *fcolor;
+       static buf_bcolor_t *bcolor;
+       static buf_texcoord_t *texcoord[MAX_TEXTUREUNITS];
+       static buf_transtri_t *tri;
+       static byte br, bg, bb, ba;
+
+       if (m->index == NULL
+        || !m->numtriangles
+        || m->vertex == NULL
+        || !m->numverts)
+               return;
+
+       if (!backendactive)
+               Sys_Error("R_DrawMesh: called when backend is not active\n");
+
+       if (m->transparent)
+       {
+               if (currenttransmesh >= max_meshs || (currenttranstriangle + m->numtriangles) > max_meshs || (currenttransvertex + m->numverts) > max_verts)
+               {
+                       if (!transranout)
+                       {
+                               Con_Printf("R_DrawMesh: ran out of room for transparent meshs\n");
+                               transranout = true;
+                       }
+                       return;
+               }
+
+               vert = &buf_transvertex[currenttransvertex];
+               fcolor = &buf_transfcolor[currenttransvertex];
+               bcolor = &buf_transbcolor[currenttransvertex];
+               for (i = 0;i < backendunits;i++)
+                       texcoord[i] = &buf_transtexcoord[i][currenttransvertex];
+       }
+       else
+       {
+               if (m->numtriangles > max_meshs || m->numverts > max_verts)
+               {
+                       Con_Printf("R_DrawMesh: mesh too big for buffers\n");
+                       return;
+               }
+
+               if (currentmesh >= max_meshs || (currenttriangle + m->numtriangles) > max_batch || (currentvertex + m->numverts) > max_verts)
+                       R_Mesh_Render();
+
+               vert = &buf_vertex[currentvertex];
+               fcolor = &buf_fcolor[currentvertex];
+               bcolor = &buf_bcolor[currentvertex];
+               for (i = 0;i < backendunits;i++)
+                       texcoord[i] = &buf_texcoord[i][currentvertex];
+       }
+
+       // vertex array code is shared for transparent and opaque meshs
+
+       for (i = 0, in = m->vertex;i < m->numverts;i++, (int)in += m->vertexstep)
+       {
+               vert[i].v[0] = in[0];
+               vert[i].v[1] = in[1];
+               vert[i].v[2] = in[2];
+               // push out farclip based on vertices encountered
+               c = DotProduct(vert[i].v, vpn);
+               if (meshfarclip < c)
+                       meshfarclip = c;
+       }
+
+       scaler = 1;
+       if (m->blendfunc2 == GL_SRC_COLOR)
+       {
+               if (m->blendfunc1 == GL_DST_COLOR) // 2x modulate with framebuffer
+                       scaler *= 0.5f;
+       }
+       else
+       {
+               if (m->tex[0])
+               {
+                       overbright = gl_combine.integer;
+                       if (overbright)
+                               scaler *= 0.25f;
+               }
+               if (lighthalf)
+                       scaler *= 0.5f;
+       }
+
+       if (floatcolors)
+       {
+               if (m->color)
+               {
+                       for (i = 0, in = m->color;i < m->numverts;i++, (int)in += m->colorstep)
+                       {
+                               fcolor[i].c[0] = in[0] * scaler;
+                               fcolor[i].c[1] = in[1] * scaler;
+                               fcolor[i].c[2] = in[2] * scaler;
+                               fcolor[i].c[3] = in[3];
+                       }
+               }
+               else
+               {
+                       cr = m->cr * scaler;
+                       cg = m->cg * scaler;
+                       cb = m->cb * scaler;
+                       ca = m->ca;
+                       for (i = 0;i < m->numverts;i++)
+                       {
+                               fcolor[i].c[0] = cr;
+                               fcolor[i].c[1] = cg;
+                               fcolor[i].c[2] = cb;
+                               fcolor[i].c[3] = ca;
+                       }
+               }
+       }
+       else
+       {
+               if (m->color)
+               {
+                       for (i = 0, in = m->color;i < m->numverts;i++, (int)in += m->colorstep)
+                       {
+                               // shift float to have 8bit fraction at base of number,
+                               // then read as integer and kill float bits...
+                               c = in[0] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bcolor[i].c[0] = (byte) j;
+                               c = in[1] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bcolor[i].c[1] = (byte) j;
+                               c = in[2] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bcolor[i].c[2] = (byte) j;
+                               c = in[3]          + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bcolor[i].c[3] = (byte) j;
+                       }
+               }
+               else
+               {
+                       c = in[0] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;br = (byte) j;
+                       c = in[1] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bg = (byte) j;
+                       c = in[2] * scaler + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;bb = (byte) j;
+                       c = in[3]          + 32768.0f;j = (*((long *)&c) & 0x7FFFFF);if (j > 255) j = 255;ba = (byte) j;
+                       for (i = 0;i < m->numverts;i++)
+                       {
+                               bcolor[i].c[0] = br;
+                               bcolor[i].c[1] = bg;
+                               bcolor[i].c[2] = bb;
+                               bcolor[i].c[3] = ba;
+                       }
+               }
+       }
+
+       for (j = 0;j < MAX_TEXTUREUNITS && m->tex[j];j++)
+       {
+               if (j >= backendunits)
+                       Sys_Error("R_DrawMesh: texture %i supplied when there are only %i texture units\n", j + 1, backendunits);
+               for (i = 0, in = m->texcoords[j];i < m->numverts;i++, (int)in += m->texcoordstep[j])
+               {
+                       texcoord[j][i].t[0] = in[0];
+                       texcoord[j][i].t[1] = in[1];
+               }
+       }
+       for (;j < backendunits;j++)
+       {
+               for (i = 0;i < m->numverts;i++)
+               {
+                       texcoord[j][i].t[0] = 0;
+                       texcoord[j][i].t[1] = 0;
+               }
+       }
+
+       if (m->transparent)
+       {
+               // transmesh is only for storage of tranparent meshs until they
+               // are inserted into the main mesh array
+               mesh = &buf_transmesh[currenttransmesh++];
+               mesh->blendfunc1 = m->blendfunc1;
+               mesh->blendfunc2 = m->blendfunc2;
+               mesh->depthmask = false;
+               j = -1;
+               for (i = 0;i < backendunits;i++)
+               {
+                       if ((mesh->textures[i] = m->tex[i]))
+                               j = i;
+                       mesh->texturergbscale[i] = m->texrgbscale[i];
+                       if (mesh->texturergbscale[i] != 1 && mesh->texturergbscale[i] != 2 && mesh->texturergbscale[i] != 4)
+                               mesh->texturergbscale[i] = 1;
+               }
+               if (overbright && j >= 0)
+                       mesh->texturergbscale[j] = 4;
+
+               // transparent meshs are broken up into individual triangles which can
+               // be sorted by depth
+               index = m->index;
+               for (i = 0;i < m->numtriangles;i++)
+               {
+                       tri = &buf_transtri[currenttranstriangle++];
+                       tri->mesh = mesh;
+                       tri->index[0] = *index++ + currenttransvertex;
+                       tri->index[1] = *index++ + currenttransvertex;
+                       tri->index[2] = *index++ + currenttransvertex;
+               }
+               currenttransvertex += m->numverts;
+       }
+       else
+       {
+               mesh = &buf_mesh[currentmesh++];
+               mesh->blendfunc1 = m->blendfunc1;
+               mesh->blendfunc2 = m->blendfunc2;
+               mesh->depthmask = (m->blendfunc2 == GL_ZERO || m->depthwrite);
+               mesh->firsttriangle = currenttriangle;
+               mesh->triangles = m->numtriangles;
+               j = -1;
+               for (i = 0;i < backendunits;i++)
+               {
+                       if ((mesh->textures[i] = m->tex[i]))
+                               j = i;
+                       mesh->texturergbscale[i] = m->texrgbscale[i];
+                       if (mesh->texturergbscale[i] != 1 && mesh->texturergbscale[i] != 2 && mesh->texturergbscale[i] != 4)
+                               mesh->texturergbscale[i] = 1;
+               }
+               if (overbright && j >= 0)
+                       mesh->texturergbscale[j] = 4;
+
+               // opaque meshs are rendered directly
+               index = (int *)&buf_tri[currenttriangle];
+               for (i = 0;i < m->numtriangles * 3;i++)
+                       index[i] = m->index[i] + currentvertex;
+               currenttriangle += m->numtriangles;
+               currentvertex += m->numverts;
+       }
+
+       c_meshtris += m->numtriangles;
+}
+
+void R_Mesh_DrawPolygon(rmeshinfo_t *m, int numverts)
+{
+       m->index = polyindexarray;
+       m->numverts = numverts;
+       m->numtriangles = numverts - 2;
+       if (m->numtriangles < 1)
+       {
+               Con_Printf("R_Mesh_DrawPolygon: invalid vertex count\n");
+               return;
+       }
+       if (m->numtriangles >= 256)
+       {
+               Con_Printf("R_Mesh_DrawPolygon: only up to 256 triangles (258 verts) supported\n");
+               return;
+       }
+       R_Mesh_Draw(m);
+}
diff --git a/gl_backend.h b/gl_backend.h
new file mode 100644 (file)
index 0000000..c9f98d4
--- /dev/null
@@ -0,0 +1,41 @@
+
+#define MAX_TEXTUREUNITS 4
+
+extern int c_meshtris;
+
+typedef struct
+{
+       int transparent;
+       int depthwrite; // force depth writing enabled even if polygon is not opaque
+       int blendfunc1;
+       int blendfunc2;
+       int numtriangles;
+       int *index;
+       int numverts;
+       float *vertex;
+       int vertexstep;
+       float *color;
+       int colorstep;
+       // if color is NULL, these are used for all vertices
+       float cr, cg, cb, ca;
+       int tex[MAX_TEXTUREUNITS];
+       float *texcoords[MAX_TEXTUREUNITS];
+       int texcoordstep[MAX_TEXTUREUNITS];
+       float texrgbscale[MAX_TEXTUREUNITS]; // used only if COMBINE is present
+}
+rmeshinfo_t;
+
+// adds console variables and registers the render module (only call from GL_Init)
+void gl_backend_init(void);
+// sets up mesh renderer for the frame
+void R_Mesh_Clear(void);
+// renders queued meshs
+void R_Mesh_Render(void);
+// queues a mesh to be rendered (invokes Render if queue is full)
+void R_Mesh_Draw(const rmeshinfo_t *m);
+// renders the queued transparent meshs
+void R_Mesh_AddTransparent(void);
+// ease-of-use frontend to R_Mesh_Draw, set up meshinfo, except for index and numtriangles and numverts, then call this
+void R_Mesh_DrawPolygon(rmeshinfo_t *m, int numverts);
+// ease-of-use frontend to R_Mesh_Draw for particles, no speed gain
+void R_Mesh_DrawParticle(vec3_t org, vec3_t right, vec3_t up, vec_t scale, int texnum, float cr, float cg, float cb, float ca, float s1, float t1, float s2, float t2, float fs1, float ft1, float fs2, float ft2);
\ No newline at end of file
index bf3de8f..d9eebeb 100644 (file)
--- a/gl_draw.c
+++ b/gl_draw.c
@@ -8,7 +8,7 @@ of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 See the GNU General Public License for more details.
 
@@ -18,18 +18,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 */
 
-// draw.c -- this is the only file outside the refresh that touches the
-// vid buffer
-
 #include "quakedef.h"
 
 //#define GL_COLOR_INDEX8_EXT     0x80E5
 
 cvar_t         scr_conalpha = {CVAR_SAVE, "scr_conalpha", "1"};
 
-byte           *draw_chars;                            // 8*8 graphic characters
-qpic_t         *draw_disc;
-
 rtexture_t     *char_texture;
 
 typedef struct
@@ -45,45 +39,38 @@ rtexture_t  *conbacktex;
 typedef struct cachepic_s
 {
        char            name[MAX_QPATH];
+       // FIXME: qpic is evil
        qpic_t          pic;
        byte            padding[32];    // for appended glpic
-} cachepic_t;
+}
+cachepic_t;
 
-#define        MAX_CACHED_PICS         128
+#define        MAX_CACHED_PICS         256
 cachepic_t     menu_cachepics[MAX_CACHED_PICS];
 int                    menu_numcachepics;
 
 byte           menuplyr_pixels[4096];
 
-int            pic_texels;
-int            pic_count;
-
-qpic_t *Draw_PicFromWad (char *name)
-{
-       qpic_t  *p;
-       glpic_t *gl;
-
-       p = W_GetLumpName (name);
-       gl = (glpic_t *)p->data;
-
-       gl->tex = R_LoadTexture (name, p->width, p->height, p->data, TEXF_ALPHA | TEXF_PRECACHE);
-       return p;
-}
+int                    pic_texels;
+int                    pic_count;
 
+rtexturepool_t *drawtexturepool;
 
 /*
 ================
 Draw_CachePic
 ================
 */
+// FIXME: qpic is evil
 qpic_t *Draw_CachePic (char *path)
 {
        cachepic_t      *pic;
        int                     i;
        qpic_t          *dat;
        glpic_t         *gl;
+       rtexture_t      *tex;
 
-       for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
+       for (pic = menu_cachepics, i = 0;i < menu_numcachepics;pic++, i++)
                if (!strcmp (path, pic->name))
                        return &pic->pic;
 
@@ -92,31 +79,43 @@ qpic_t      *Draw_CachePic (char *path)
        menu_numcachepics++;
        strcpy (pic->name, path);
 
-//
-// load the pic from disk
-//
-       dat = (qpic_t *)COM_LoadMallocFile (path, false);
-       if (!dat)
-               Sys_Error ("Draw_CachePic: failed to load %s", path);
-       SwapPic (dat);
-
+       // FIXME: move this to menu code
        // HACK HACK HACK --- we need to keep the bytes for
        // the translatable player picture just for the menu
        // configuration dialog
        if (!strcmp (path, "gfx/menuplyr.lmp"))
-               memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
-
-       pic->pic.width = dat->width;
-       pic->pic.height = dat->height;
-
-       gl = (glpic_t *)pic->pic.data;
-       gl->tex = loadtextureimage(path, 0, 0, false, false, true);
-       if (!gl->tex)
-               gl->tex = R_LoadTexture (path, dat->width, dat->height, dat->data, TEXF_ALPHA | TEXF_PRECACHE);
+       {
+               dat = (qpic_t *)COM_LoadFile (path, false);
+               if (!dat)
+                       Sys_Error("unable to load gfx/menuplyr.lmp");
+               SwapPic (dat);
 
-       qfree(dat);
+               memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
+       }
 
-       return &pic->pic;
+       // load the pic from disk
+       if ((tex = loadtextureimage(drawtexturepool, path, 0, 0, false, false, true)))
+       {
+               // load the pic from an image file
+               pic->pic.width = image_width;
+               pic->pic.height = image_height;
+               gl = (glpic_t *)pic->pic.data;
+               gl->tex = tex;
+               return &pic->pic;
+       }
+       else
+       {
+               qpic_t *p;
+               // load the pic from gfx.wad
+               p = W_GetLumpName (path);
+               if (!p)
+                       Sys_Error ("Draw_CachePic: failed to load %s", path);
+               pic->pic.width = p->width;
+               pic->pic.height = p->height;
+               gl = (glpic_t *)pic->pic.data;
+               gl->tex = R_LoadTexture (drawtexturepool, path, p->width, p->height, p->data, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
+               return &pic->pic;
+       }
 }
 
 /*
@@ -124,51 +123,58 @@ qpic_t    *Draw_CachePic (char *path)
 Draw_Init
 ===============
 */
-void gl_draw_start(void)
+static void gl_draw_start(void)
 {
-       int             i;
+       int i;
+       byte *draw_chars;
+
+       menu_numcachepics = 0;
 
-       char_texture = loadtextureimage ("conchars", 0, 0, false, false, true);
+       drawtexturepool = R_AllocTexturePool();
+       char_texture = loadtextureimage (drawtexturepool, "conchars", 0, 0, false, false, true);
        if (!char_texture)
        {
                draw_chars = W_GetLumpName ("conchars");
-               for (i=0 ; i<128*128 ; i++)
+               // convert font to proper transparent color
+               for (i = 0;i < 128 * 128;i++)
                        if (draw_chars[i] == 0)
-                               draw_chars[i] = 255;    // proper transparent color
+                               draw_chars[i] = 255;
 
-               // now turn them into textures
-               char_texture = R_LoadTexture ("charset", 128, 128, draw_chars, TEXF_ALPHA | TEXF_PRECACHE);
+               // now turn into texture
+               char_texture = R_LoadTexture (drawtexturepool, "charset", 128, 128, draw_chars, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
        }
 
-       conbacktex = loadtextureimage("gfx/conback", 0, 0, false, false, true);
-
-       // get the other pics we need
-       draw_disc = Draw_PicFromWad ("disc");
+       conbacktex = loadtextureimage(drawtexturepool, "gfx/conback", 0, 0, false, false, true);
 }
 
-void gl_draw_shutdown(void)
+static void gl_draw_shutdown(void)
 {
+       R_FreeTexturePool(&drawtexturepool);
+
+       menu_numcachepics = 0;
 }
 
-void gl_draw_newmap(void)
+void SHOWLMP_clear(void);
+static void gl_draw_newmap(void)
 {
+       SHOWLMP_clear();
 }
 
 extern char engineversion[40];
 int engineversionx, engineversiony;
 
-extern void R_Textures_Init();
 void GL_Draw_Init (void)
 {
        int i;
        Cvar_RegisterVariable (&scr_conalpha);
 
        for (i = 0;i < 40 && engineversion[i];i++)
-               engineversion[i] += 0x80; // shift to orange
+               engineversion[i] |= 0x80; // shift to orange
        engineversionx = vid.conwidth - strlen(engineversion) * 8 - 8;
        engineversiony = vid.conheight - 8;
 
-       R_Textures_Init();
+       menu_numcachepics = 0;
+
        R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
 }
 
@@ -201,25 +207,31 @@ void Draw_Character (int x, int y, int num)
        fcol = col*0.0625;
        size = 0.0625;
 
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        glBindTexture(GL_TEXTURE_2D, R_GetTexture(char_texture));
+       CHECKGLERROR
        // LordHavoc: NEAREST mode on text if not scaling up
        if (vid.realwidth <= (int) vid.conwidth)
        {
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               CHECKGLERROR
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+               CHECKGLERROR
        }
        else
        {
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               CHECKGLERROR
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+               CHECKGLERROR
        }
 
        if (lighthalf)
                glColor3f(0.5f,0.5f,0.5f);
        else
                glColor3f(1.0f,1.0f,1.0f);
+       CHECKGLERROR
        glBegin (GL_QUADS);
        glTexCoord2f (fcol, frow);
        glVertex2f (x, y);
@@ -230,6 +242,7 @@ void Draw_Character (int x, int y, int num)
        glTexCoord2f (fcol, frow + size);
        glVertex2f (x, y+8);
        glEnd ();
+       CHECKGLERROR
 
        // LordHavoc: revert to LINEAR mode
 //     if (vid.realwidth <= (int) vid.conwidth)
@@ -249,7 +262,7 @@ void Draw_String (int x, int y, char *str, int maxlen)
 {
        int num;
        float frow, fcol;
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        if (y <= -8 || y >= (int) vid.conheight || x >= (int) vid.conwidth || *str == 0) // completely offscreen or no text to print
                return;
@@ -263,18 +276,23 @@ void Draw_String (int x, int y, char *str, int maxlen)
        if (vid.realwidth <= (int) vid.conwidth)
        {
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+               CHECKGLERROR
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+               CHECKGLERROR
        }
        else
        {
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               CHECKGLERROR
                glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+               CHECKGLERROR
        }
 
        if (lighthalf)
                glColor3f(0.5f,0.5f,0.5f);
        else
                glColor3f(1.0f,1.0f,1.0f);
+       CHECKGLERROR
        glBegin (GL_QUADS);
        while (maxlen-- && x < (int) vid.conwidth) // stop rendering when out of characters or room
        {
@@ -290,6 +308,7 @@ void Draw_String (int x, int y, char *str, int maxlen)
                x += 8;
        }
        glEnd ();
+       CHECKGLERROR
 
        // LordHavoc: revert to LINEAR mode
 //     if (vid.realwidth < (int) vid.conwidth)
@@ -301,28 +320,33 @@ void Draw_String (int x, int y, char *str, int maxlen)
 
 void Draw_AdditiveString (int x, int y, char *str, int maxlen)
 {
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+       CHECKGLERROR
        Draw_String(x, y, str, maxlen);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       CHECKGLERROR
 }
 
 void Draw_GenericPic (rtexture_t *tex, float red, float green, float blue, float alpha, int x, int y, int width, int height)
 {
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        if (lighthalf)
                glColor4f(red * 0.5f, green * 0.5f, blue * 0.5f, alpha);
        else
                glColor4f(red, green, blue, alpha);
+       CHECKGLERROR
        glBindTexture(GL_TEXTURE_2D, R_GetTexture(tex));
+       CHECKGLERROR
        glBegin (GL_QUADS);
        glTexCoord2f (0, 0);glVertex2f (x, y);
        glTexCoord2f (1, 0);glVertex2f (x+width, y);
        glTexCoord2f (1, 1);glVertex2f (x+width, y+height);
        glTexCoord2f (0, 1);glVertex2f (x, y+height);
        glEnd ();
+       CHECKGLERROR
 }
 
 /*
@@ -354,8 +378,10 @@ void Draw_AdditivePic (int x, int y, qpic_t *pic)
        if (pic)
        {
                glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+               CHECKGLERROR
                Draw_GenericPic(((glpic_t *)pic->data)->tex, 1,1,1,1, x,y,pic->width, pic->height);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               CHECKGLERROR
        }
 }
 
@@ -378,14 +404,14 @@ void Draw_PicTranslate (int x, int y, qpic_t *pic, byte *translation)
 
        c = pic->width * pic->height;
        src = menuplyr_pixels;
-       dest = trans = qmalloc(c);
+       dest = trans = Mem_Alloc(tempmempool, c);
        for (i = 0;i < c;i++)
                *dest++ = translation[*src++];
 
-       rt = R_LoadTexture ("translatedplayerpic", pic->width, pic->height, trans, TEXF_ALPHA | TEXF_PRECACHE);
-       qfree(trans);
+       rt = R_LoadTexture (drawtexturepool, "translatedplayerpic", pic->width, pic->height, trans, TEXTYPE_QPALETTE, TEXF_ALPHA | TEXF_PRECACHE);
+       Mem_Free(trans);
 
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        Draw_GenericPic (rt, 1,1,1,1, x, y, pic->width, pic->height);
 }
@@ -413,9 +439,10 @@ Fills a box of pixels with a single color
 */
 void Draw_Fill (int x, int y, int w, int h, int c)
 {
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        glDisable (GL_TEXTURE_2D);
+       CHECKGLERROR
        if (lighthalf)
        {
                byte *tempcolor = (byte *)&d_8to24table[c];
@@ -423,6 +450,7 @@ void Draw_Fill (int x, int y, int w, int h, int c)
        }
        else
                glColor4ubv ((byte *)&d_8to24table[c]);
+       CHECKGLERROR
 
        glBegin (GL_QUADS);
 
@@ -432,8 +460,11 @@ void Draw_Fill (int x, int y, int w, int h, int c)
        glVertex2f (x, y+h);
 
        glEnd ();
+       CHECKGLERROR
        glColor3f(1,1,1);
+       CHECKGLERROR
        glEnable (GL_TEXTURE_2D);
+       CHECKGLERROR
 }
 //=============================================================================
 
@@ -448,28 +479,40 @@ Setup as if the screen was 320*200
 */
 void GL_Set2D (void)
 {
-       if (!r_render.value)
+       if (!r_render.integer)
                return;
        glViewport (vid.realx, vid.realy, vid.realwidth, vid.realheight);
+       CHECKGLERROR
 
        glMatrixMode(GL_PROJECTION);
+       CHECKGLERROR
     glLoadIdentity ();
+       CHECKGLERROR
        glOrtho  (0, vid.conwidth, vid.conheight, 0, -99999, 99999);
+       CHECKGLERROR
 
        glMatrixMode(GL_MODELVIEW);
+       CHECKGLERROR
     glLoadIdentity ();
+       CHECKGLERROR
 
        glDisable (GL_DEPTH_TEST);
+       CHECKGLERROR
        glDisable (GL_CULL_FACE);
+       CHECKGLERROR
        glEnable (GL_BLEND);
-       glDisable (GL_ALPHA_TEST);
+       CHECKGLERROR
        glEnable(GL_TEXTURE_2D);
+       CHECKGLERROR
 
        // LordHavoc: added this
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       CHECKGLERROR
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       CHECKGLERROR
 
        glColor3f(1,1,1);
+       CHECKGLERROR
 }
 
 // LordHavoc: SHOWLMP stuff
index 1a18c95..8684a8f 100644 (file)
@@ -1,7 +1,7 @@
 
 #include "quakedef.h"
 
-cvar_t gl_transform = {0, "gl_transform", "1"};
+//cvar_t gl_transform = {0, "gl_transform", "1"};
 cvar_t gl_lockarrays = {0, "gl_lockarrays", "1"};
 
 typedef struct
@@ -12,17 +12,19 @@ typedef