From 4db8b46c4bf9708b863ec9fa09f644b9eb88ecae Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 14 May 2012 01:41:08 +0000 Subject: [PATCH] implement ripplemaps. currently requires explicit mod support to use properly. fix hexen2 conchars fix audio sample mixups fix recent beam-related crashes. added orthographic bsp culling routines. fix crash on vid_restart. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4050 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 37 ++ engine/client/cl_main.c | 3 - engine/client/cl_tent.c | 29 +- engine/client/client.h | 1 + engine/client/p_script.c | 97 ++- engine/client/pr_csqc.c | 5 +- engine/client/r_surf.c | 74 ++- engine/client/render.h | 6 +- engine/client/renderer.c | 6 +- engine/client/snd_dma.c | 4 +- engine/common/q1bsp.c | 2 +- engine/gl/gl_alias.c | 2 +- engine/gl/gl_backend.c | 122 ++-- engine/gl/gl_draw.c | 1 - engine/gl/gl_font.c | 50 +- engine/gl/gl_model.h | 1 + engine/gl/gl_rmain.c | 20 +- engine/gl/gl_shader.c | 161 +++-- engine/gl/gl_shadow.c | 12 +- engine/gl/glquake.h | 2 - engine/gl/glsupp.h | 4 + engine/gl/shader.h | 8 +- engine/server/sv_ents.c | 6 + engine/server/sv_main.c | 2 +- engine/shaders/glsl/altwater.glsl | 55 +- engine/sw/sw.h | 150 +++++ engine/sw/sw_backend.c | 654 ++++++++++++++++++++ engine/sw/sw_image.c | 122 ++++ engine/sw/sw_rast.c | 994 ++++++++++++++++++++++++++++++ engine/sw/sw_spans.h | 323 ++++++++++ engine/sw/sw_vidwin.c | 761 +++++++++++++++++++++++ 31 files changed, 3533 insertions(+), 181 deletions(-) create mode 100644 engine/sw/sw.h create mode 100644 engine/sw/sw_backend.c create mode 100644 engine/sw/sw_image.c create mode 100644 engine/sw/sw_rast.c create mode 100644 engine/sw/sw_spans.h create mode 100644 engine/sw/sw_vidwin.c diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index c87f43dc..4eab73f5 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2698,6 +2698,11 @@ void CL_LinkPacketEntities (void) if (ent->flags & Q2RF_EXTERNALMODEL) ent->externalmodelview = ~0; +/* if (le->origin[2] < r_refdef.waterheight != le->lastorigin[2] < r_refdef.waterheight) + { + P_RunParticleEffectTypeString(le->origin, NULL, 1, "te_watertransition"); + } +*/ // set colormap if (state->colormap && (state->colormap <= MAX_CLIENTS) && (gl_nocolors.value == -1 || (ent->model/* && state->modelindex == cl_playerindex*/))) @@ -4157,7 +4162,37 @@ void CL_SwapEntityLists(void) cl_numstrisvert = 0; cl_numstris = 0; } +/* +static void CL_WaterSplashes(void) +{ + int i; + entity_t *ent; + vec3_t org; + static unsigned int ltime; + unsigned int ntime = cl.time*1000; + if (ntime - ltime < 200) + return; + ltime = ntime; + + for (i = 0; i < cl_numvisedicts; i++) + { + ent = &cl_visedicts[i]; + + if (ent->model) + { + if (ent->origin[2] + ent->model->mins[2] < r_refdef.waterheight && + ent->origin[2] + ent->model->maxs[2] > r_refdef.waterheight) + { + org[0] = ent->origin[0]; + org[1] = ent->origin[1]; + org[2] = r_refdef.waterheight; + P_RunParticleEffectTypeString(org, NULL, 1, "te_watertransition"); + } + } + } +} +*/ void CL_EmitEntities (void) { if (cls.state != ca_active) @@ -4181,6 +4216,8 @@ void CL_EmitEntities (void) CL_LinkPacketEntities (); CL_LinkProjectiles (); CL_UpdateTEnts (); + +// CL_WaterSplashes(); } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ec499816..9da6fb1a 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3094,9 +3094,6 @@ void CL_Init (void) #endif #ifdef CSQC_DAT CSQC_RegisterCvarsAndThings(); -#endif -#if defined(CSQC_DAT) || defined(MENU_DAT) - PF_Common_RegisterCvars(); #endif Cvar_Register (&host_speeds, cl_controlgroup); Cvar_Register (&developer, cl_controlgroup); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index a1a87b92..d7733be4 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -314,12 +314,7 @@ void CL_AssociateEffect_f(void) CL_RegisterParticles(); } -/* -================= -CL_ParseTEnts -================= -*/ -void CL_InitTEnts (void) +void CL_InitTEntSounds (void) { int i; for (i = 0; i < sizeof(tentsfx)/sizeof(tentsfx[0]); i++) @@ -329,6 +324,18 @@ void CL_InitTEnts (void) else *tentsfx[i].sfx = NULL; } +} + +/* +================= +CL_ParseTEnts +================= +*/ +void CL_InitTEnts (void) +{ + int i; + for (i = 0; i < sizeof(tentsfx)/sizeof(tentsfx[0]); i++) + *tentsfx[i].sfx = NULL; Cmd_AddCommand("r_effect", CL_AssociateEffect_f); Cmd_AddCommand("r_trail", CL_AssociateEffect_f); @@ -513,6 +520,8 @@ CL_ClearTEnts */ void CL_ClearTEnts (void) { + CL_ClearTEntParticleState(); + cl_beams_max = 0; BZ_Free(cl_beams); cl_beams = NULL; @@ -618,8 +627,12 @@ beam_t *CL_NewBeam (int entity, int tag) { if (i == cl_beams_max) { - cl_beams_max = (i+1)*2; - cl_beams = BZ_Realloc(cl_beams, cl_beams_max*sizeof(*cl_beams)); + int nm = (i+1)*2; + CL_ClearTEntParticleState(); + + cl_beams = BZ_Realloc(cl_beams, nm*sizeof(*cl_beams)); + memset(cl_beams + cl_beams_max, 0, sizeof(*cl_beams)*(nm-cl_beams_max)); + cl_beams_max = nm; } beams_running++; diff --git a/engine/client/client.h b/engine/client/client.h index cc2ac050..258a736e 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -991,6 +991,7 @@ void DropPunchAngle (int pnum); // void CL_RegisterParticles(void); void CL_InitTEnts (void); +void CL_InitTEntSounds (void); void CL_ClearTEnts (void); void CL_ClearTEntParticleState (void); void CL_ClearCustomTEnts(void); diff --git a/engine/client/p_script.c b/engine/client/p_script.c index f3ab3fde..0a5e938e 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -142,7 +142,7 @@ typedef struct skytris_s { //these is the required render state for each particle //dynamic per-particle stuff isn't important. only static state. typedef struct { - enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_DECAL} type; + enum {PT_NORMAL, PT_SPARK, PT_SPARKFAN, PT_TEXTUREDSPARK, PT_BEAM, PT_CDECAL, PT_UDECAL} type; blendmode_t blendmode; shader_t *shader; @@ -1181,8 +1181,10 @@ static void P_ParticleEffect_f(void) ptype->looks.type = PT_SPARKFAN; else if (!strcmp(value, "texturedspark")) ptype->looks.type = PT_TEXTUREDSPARK; - else if (!strcmp(value, "decal")) - ptype->looks.type = PT_DECAL; + else if (!strcmp(value, "decal") || !strcmp(value, "cdecal")) + ptype->looks.type = PT_CDECAL; + else if (!strcmp(value, "udecal")) + ptype->looks.type = PT_UDECAL; else ptype->looks.type = PT_NORMAL; settype = true; @@ -1805,9 +1807,14 @@ static void P_ImportEffectInfo_f(void) ptype->count = atof(arg[1]); else if (!strcmp(arg[0], "type") && args == 2) { - if (!strcmp(arg[1], "decal")) + if (!strcmp(arg[1], "decal") || !strcmp(arg[1], "cdecal")) { - ptype->looks.type = PT_DECAL; + ptype->looks.type = PT_CDECAL; + ptype->looks.blendmode = BM_INVMOD; + } + else if (!strcmp(arg[1], "udecal")) + { + ptype->looks.type = PT_UDECAL; ptype->looks.blendmode = BM_INVMOD; } else if (!strcmp(arg[1], "alphastatic")) @@ -2923,7 +2930,7 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, { PScript_EffectSpawned(ptype, org, dir, 0, count); - if (ptype->looks.type == PT_DECAL) + if (ptype->looks.type == PT_CDECAL) { clippeddecal_t *d; int decalcount; @@ -4601,6 +4608,79 @@ static void R_AddClippedDecal(scenetris_t *t, clippeddecal_t *d, plooks_t *type) t->numidx += 3; } +static void R_AddUnclippedDecal(scenetris_t *t, particle_t *p, plooks_t *type) +{ + float x, y; + vec3_t sdir, tdir; + + if (cl_numstrisvert+4 > cl_maxstrisvert) + { + cl_maxstrisvert+=64*4; + cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); + cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); + cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); + } + + Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+0]); + Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+1]); + Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+2]); + Vector4Copy(p->rgba, cl_strisvertc[cl_numstrisvert+3]); + + Vector2Set(cl_strisvertt[cl_numstrisvert+0], p->s1, p->t1); + Vector2Set(cl_strisvertt[cl_numstrisvert+1], p->s1, p->t2); + Vector2Set(cl_strisvertt[cl_numstrisvert+2], p->s2, p->t2); + Vector2Set(cl_strisvertt[cl_numstrisvert+3], p->s2, p->t1); + +// if (p->vel[1] == 1) + { + VectorSet(sdir, 1, 0, 0); + VectorSet(tdir, 0, 1, 0); + } + + if (p->angle) + { + x = sin(p->angle)*p->scale; + y = cos(p->angle)*p->scale; + + cl_strisvertv[cl_numstrisvert+0][0] = p->org[0] - x*sdir[0] - y*tdir[0]; + cl_strisvertv[cl_numstrisvert+0][1] = p->org[1] - x*sdir[1] - y*tdir[1]; + cl_strisvertv[cl_numstrisvert+0][2] = p->org[2] - x*sdir[2] - y*tdir[2]; + cl_strisvertv[cl_numstrisvert+1][0] = p->org[0] - y*sdir[0] + x*tdir[0]; + cl_strisvertv[cl_numstrisvert+1][1] = p->org[1] - y*sdir[1] + x*tdir[1]; + cl_strisvertv[cl_numstrisvert+1][2] = p->org[2] - y*sdir[2] + x*tdir[2]; + cl_strisvertv[cl_numstrisvert+2][0] = p->org[0] + x*sdir[0] + y*tdir[0]; + cl_strisvertv[cl_numstrisvert+2][1] = p->org[1] + x*sdir[1] + y*tdir[1]; + cl_strisvertv[cl_numstrisvert+2][2] = p->org[2] + x*sdir[2] + y*tdir[2]; + cl_strisvertv[cl_numstrisvert+3][0] = p->org[0] + y*sdir[0] - x*tdir[0]; + cl_strisvertv[cl_numstrisvert+3][1] = p->org[1] + y*sdir[1] - x*tdir[1]; + cl_strisvertv[cl_numstrisvert+3][2] = p->org[2] + y*sdir[2] - x*tdir[2]; + } + else + { + VectorMA(p->org, -p->scale, tdir, cl_strisvertv[cl_numstrisvert+0]); + VectorMA(p->org, -p->scale, sdir, cl_strisvertv[cl_numstrisvert+1]); + VectorMA(p->org, p->scale, tdir, cl_strisvertv[cl_numstrisvert+2]); + VectorMA(p->org, p->scale, sdir, cl_strisvertv[cl_numstrisvert+3]); + } + + if (cl_numstrisidx+6 > cl_maxstrisidx) + { + cl_maxstrisidx += 64*6; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 1; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 0; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 2; + cl_strisidx[cl_numstrisidx++] = (cl_numstrisvert - t->firstvert) + 3; + + cl_numstrisvert += 4; + + t->numvert += 4; + t->numidx += 6; +} + static void R_AddTexturedParticle(scenetris_t *t, particle_t *p, plooks_t *type) { float scale, x, y; @@ -4863,7 +4943,10 @@ static void PScript_DrawParticleTypes (void) else bdraw = GL_DrawParticleBeam; break; - case PT_DECAL: + case PT_CDECAL: + break; + case PT_UDECAL: + tdraw = R_AddUnclippedDecal; break; case PT_NORMAL: pdraw = GL_DrawTexturedParticle; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index e909176e..c20b0b7f 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1632,10 +1632,7 @@ static void QCBUILTIN PF_cs_pointcontents(progfuncs_t *prinst, struct globalvars v = G_VECTOR(OFS_PARM0); - if (!cl.worldmodel) - return FTECONTENTS_EMPTY; - - cont = World_PointContents(w, v); + cont = cl.worldmodel?World_PointContents(w, v):FTECONTENTS_EMPTY; if (cont & FTECONTENTS_SOLID) G_FLOAT(OFS_RETURN) = Q1CONTENTS_SOLID; else if (cont & FTECONTENTS_SKY) diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index adca1f8a..8d149150 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -1512,6 +1512,75 @@ start: goto start; } +static void Surf_OrthoRecursiveWorldNode (mnode_t *node, unsigned int clipflags) +{ + //when rendering as ortho the front and back sides are technically equal. the only culling comes from frustum culling. + + int c, side, clipped; + mplane_t *plane, *clipplane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + if (node->contents == Q1CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + + for (c = 0, clipplane = frustum; c < FRUSTUMPLANES; c++, clipplane++) + { + if (!(clipflags & (1 << c))) + continue; // don't need to clip against it + + clipped = BOX_ON_PLANE_SIDE (node->minmaxs, node->minmaxs + 3, clipplane); + if (clipped == 2) + return; + else if (clipped == 1) + clipflags -= (1<contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark++)->visframe = r_framecount; + } while (--c); + } + return; + } + +// recurse down the children + Surf_OrthoRecursiveWorldNode (node->children[0], clipflags); + Surf_OrthoRecursiveWorldNode (node->children[1], clipflags); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + Surf_RenderDynamicLightmaps (surf); + surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh; + } + } + return; +} + #ifdef Q2BSPS static void Surf_RecursiveQ2WorldNode (mnode_t *node) { @@ -2168,7 +2237,10 @@ void Surf_DrawWorld (void) if (!(r_novis.ival & 2)) VectorCopy (r_refdef.vieworg, modelorg); - Surf_RecursiveWorldNode (cl.worldmodel->nodes, 0x1f); + if (r_refdef.useperspective) + Surf_RecursiveWorldNode (cl.worldmodel->nodes, 0x1f); + else + Surf_OrthoRecursiveWorldNode (cl.worldmodel->nodes, 0x1f); } } diff --git a/engine/client/render.h b/engine/client/render.h index c90311fc..b5942e78 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -153,6 +153,7 @@ typedef struct int currentplayernum; float time; +// float waterheight; //updated by the renderer. stuff sitting at this height generate ripple effects float m_projection[16]; float m_view[16]; @@ -240,7 +241,7 @@ void GLR_InitTextures (void); void GLR_InitEfrags (void); void GLR_RenderView (void); // must set r_refdef first // called whenever r_refdef or vid change -void GLR_DrawPortal(struct batch_s *batch, struct batch_s **blist); +void GLR_DrawPortal(struct batch_s *batch, struct batch_s **blist, int portaltype); void GLR_PreNewMap(void); void GLR_NewMap (void); @@ -410,8 +411,7 @@ extern cvar_t r_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows; extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows; extern cvar_t r_mirroralpha; extern cvar_t r_wateralpha; -extern cvar_t r_water_refract; -extern cvar_t r_water_reflect; +extern cvar_t r_waterstyle; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_netgraph; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 510c3684..8e2fff21 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -326,8 +326,7 @@ cvar_t r_shadow_realtime_dlight_shadows = SCVARF ("r_shadow_realtime_dlight_sha cvar_t r_shadow_realtime_world_lightmaps = SCVARF ("r_shadow_realtime_world_lightmaps", "0", 0); cvar_t r_sun_dir = SCVAR ("r_sun_dir", "0.2 0.5 0.8"); cvar_t r_sun_colour = SCVARF ("r_sun_colour", "0 0 0", CVAR_ARCHIVE); -cvar_t r_water_refract = CVARF ("r_water_refract", "0", CVAR_ARCHIVE|CVAR_SHADERSYSTEM); -cvar_t r_water_reflect = CVARF ("r_water_reflect", "0", CVAR_ARCHIVE|CVAR_SHADERSYSTEM); +cvar_t r_waterstyle = CVARF ("r_waterstyle", "0", CVAR_ARCHIVE|CVAR_SHADERSYSTEM); cvar_t r_vertexdlights = SCVAR ("r_vertexdlights", "0"); @@ -578,8 +577,7 @@ void Renderer_Init(void) Cvar_Register (&r_shadow_realtime_world_lightmaps, GRAPHICALNICETIES); Cvar_Register (&r_sun_dir, GRAPHICALNICETIES); Cvar_Register (&r_sun_colour, GRAPHICALNICETIES); - Cvar_Register (&r_water_refract, GRAPHICALNICETIES); - Cvar_Register (&r_water_reflect, GRAPHICALNICETIES); + Cvar_Register (&r_waterstyle, GRAPHICALNICETIES); Cvar_Register(&scr_viewsize, SCREENOPTIONS); Cvar_Register(&scr_fov, SCREENOPTIONS); diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 3c3ecde8..ad9f9e24 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -879,6 +879,8 @@ void S_Startup (void) sound_started = !!sndcardinfo; S_ClearRaw(); + + CL_InitTEntSounds(); } void SNDDMA_SetUnderWater(qboolean underwater) @@ -1753,7 +1755,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) { newmusic = S_PrecacheSound(nexttrack); - if (!newmusic->failedload) + if (newmusic && !newmusic->failedload) { chan->sfx = newmusic; chan->rate = 1<hulls[0], transformed); } - if (1) + if (!model->firstmodelsurface) { return Q1BSP_TranslateContents(Q1_ModelPointContents(model->nodes, point)); } diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 530e5845..efc0952c 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -957,7 +957,7 @@ void R_GAlias_GenerateBatches(entity_t *e, batch_t **batches) texnums_t *skin; - if (r_refdef.externalview && e->flags & Q2RF_WEAPONMODEL) + if ((r_refdef.externalview || r_refdef.recurse) && e->flags & Q2RF_WEAPONMODEL) return; /*switch model if we're the player model, and the player skin says a new model*/ diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index e1de560f..61b0ff1b 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -121,8 +121,9 @@ struct { texid_t tex_sourcedepth; int fbo_depthless; int fbo_reflection; - texid_t tex_reflection; - texid_t tex_refraction; + texid_t tex_reflection; /*basically a portal rendered to texture*/ + texid_t tex_refraction; /*the (culled) underwater view*/ + texid_t tex_ripplemap; /*temp image for waves and things.*/ qboolean force2d; int currenttmu; @@ -169,6 +170,8 @@ struct { texid_t fogtexture; texid_t normalisationcubemap; float fogfar; + + batch_t **mbatches; //model batches (ie: not world) }; //exterior state (paramters) @@ -670,6 +673,7 @@ void GLBE_SetupVAO(vbo_t *vbo, unsigned vaodynamic) shaderstate.sourcevbo = vbo; shaderstate.pendingvertexvbo = shaderstate.sourcevbo->coord.gl.vbo; shaderstate.pendingvertexpointer = shaderstate.sourcevbo->coord.gl.addr; + shaderstate.colourarraytype = GL_FLOAT; shaderstate.currentvao = vbo->vao; qglBindVertexArray(vbo->vao); @@ -876,19 +880,6 @@ void R_IBrokeTheArrays(void) RevertToKnownState(); } -void GL_FlushBackEnd(void) -{ - memset(&shaderstate, 0, sizeof(shaderstate)); - shaderstate.curcull = ~0; -} -void R_BackendInit(void) -{ -} -qboolean R_MeshWillExceed(mesh_t *mesh) -{ - return false; -} - #ifdef RTLIGHTS //called from gl_shadow void BE_SetupForShadowMap(void) @@ -1026,6 +1017,9 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) case T_GEN_REFRACTION: t = shaderstate.tex_refraction; break; + case T_GEN_RIPPLEMAP: + t = shaderstate.tex_ripplemap; + break; } GL_LazyBind(tmu, GL_TEXTURE_2D, t); } @@ -3550,7 +3544,7 @@ static void GLBE_SubmitMeshesPortals(batch_t **worldlist, batch_t *dynamiclist) } GLBE_SelectMode(BEM_STANDARD); - GLR_DrawPortal(batch, worldlist); + GLR_DrawPortal(batch, worldlist, 0); /*clear depth again*/ GL_ForceDepthWritable(); @@ -3600,9 +3594,9 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) continue; } - if (batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT)) + if (batch->shader->flags & (SHADER_HASREFLECT | SHADER_HASREFRACT | SHADER_HASRIPPLEMAP)) { - //these two flags require rendering some view as an fbo + //these flags require rendering some view as an fbo if (r_refdef.recurse) return; @@ -3610,39 +3604,70 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { if (!shaderstate.tex_reflection.num) { - shaderstate.tex_reflection = GL_AllocNewTexture("***tex_reflection***", vid.pixelwidth, vid.pixelheight); + shaderstate.tex_reflection = GL_AllocNewTexture("***tex_reflection***", vid.pixelwidth/2, vid.pixelheight/2); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_reflection); - qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vid.pixelwidth, vid.pixelheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vid.pixelwidth/2, vid.pixelheight/2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } GL_ForceDepthWritable(); GLBE_RenderToTexture(r_nulltex, r_nulltex, shaderstate.tex_reflection, true); + qglViewport (0, 0, vid.pixelwidth/2, vid.pixelheight/2); GL_ForceDepthWritable(); qglClear(GL_DEPTH_BUFFER_BIT); - GLR_DrawPortal(batch, cl.worldmodel->batches); + GLR_DrawPortal(batch, cl.worldmodel->batches, 1); GLBE_RenderToTexture(r_nulltex, r_nulltex, r_nulltex, false); + qglViewport (0, 0, vid.pixelwidth, vid.pixelheight); } if (batch->shader->flags & SHADER_HASREFRACT) { if (!shaderstate.tex_refraction.num) { - shaderstate.tex_refraction = GL_AllocNewTexture("***tex_refraction***", vid.pixelwidth, vid.pixelheight); + shaderstate.tex_refraction = GL_AllocNewTexture("***tex_refraction***", vid.pixelwidth/2, vid.pixelheight/2); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refraction); - qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vid.pixelwidth, vid.pixelheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, vid.pixelwidth/2, vid.pixelheight/2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } GL_ForceDepthWritable(); GLBE_RenderToTexture(r_nulltex, r_nulltex, shaderstate.tex_refraction, true); + qglViewport (0, 0, vid.pixelwidth/2, vid.pixelheight/2); GL_ForceDepthWritable(); qglClear(GL_DEPTH_BUFFER_BIT); - GLR_DrawPortal(batch, cl.worldmodel->batches); + GLR_DrawPortal(batch, cl.worldmodel->batches, 2); GLBE_RenderToTexture(r_nulltex, r_nulltex, r_nulltex, false); + + qglViewport (0, 0, vid.pixelwidth, vid.pixelheight); + } + if (batch->shader->flags & SHADER_HASRIPPLEMAP) + { + if (!shaderstate.tex_ripplemap.num) + { + shaderstate.tex_ripplemap = GL_AllocNewTexture("***tex_ripplemap***", vid.pixelwidth/2, vid.pixelheight/2); + GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap); + qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, vid.pixelwidth/2, vid.pixelheight/2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + GLBE_RenderToTexture(r_nulltex, r_nulltex, shaderstate.tex_ripplemap, false); + qglViewport (0, 0, vid.pixelwidth/2, vid.pixelheight/2); + qglClearColor(0, 0, 0, 0); + qglClear(GL_COLOR_BUFFER_BIT); + +// r_refdef.waterheight = DotProduct(batch->mesh[0]->xyz_array[0], batch->mesh[0]->normals_array[0]); + + r_refdef.recurse = true; //paranoid, should stop potential infinite loops + GLBE_SubmitMeshes(true, SHADER_SORT_RIPPLE, SHADER_SORT_RIPPLE); + r_refdef.recurse = false; + GLBE_RenderToTexture(r_nulltex, r_nulltex, r_nulltex, false); + + qglViewport (0, 0, vid.pixelwidth, vid.pixelheight); } } @@ -3650,7 +3675,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) } } -void GLBE_SubmitMeshes (qboolean drawworld, batch_t **blist, int start, int stop) +void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop) { model_t *model = cl.worldmodel; int i; @@ -3660,11 +3685,11 @@ void GLBE_SubmitMeshes (qboolean drawworld, batch_t **blist, int start, int stop if (drawworld) { if (i == SHADER_SORT_PORTAL && !r_noportals.ival && !r_refdef.recurse) - GLBE_SubmitMeshesPortals(model->batches, blist[i]); + GLBE_SubmitMeshesPortals(model->batches, shaderstate.mbatches[i]); GLBE_SubmitMeshesSortList(model->batches[i]); } - GLBE_SubmitMeshesSortList(blist[i]); + GLBE_SubmitMeshesSortList(shaderstate.mbatches[i]); } } @@ -3736,9 +3761,12 @@ batch_t *GLBE_GetTempBatch(void) void GLBE_BaseEntTextures(void) { batch_t *batches[SHADER_SORT_COUNT]; + batch_t **ob = shaderstate.mbatches; + shaderstate.mbatches = batches; BE_GenModelBatches(batches); - GLBE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); + GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); BE_SelectEntity(&r_worldentity); + shaderstate.mbatches = ob; } #endif @@ -3762,7 +3790,7 @@ void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destco //create an unnamed depth buffer qglGenRenderbuffersEXT(1, &drb); qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, drb); - qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_ARB, vid.pixelwidth, vid.pixelheight); + qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24_ARB, vid.pixelwidth/2, vid.pixelheight/2); qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, drb); // qglDeleteRenderbuffersEXT(1, &drb); @@ -3790,7 +3818,7 @@ void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destco } } -void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) +void GLBE_DrawLightPrePass(qbyte *vis) { extern cvar_t temp1; if (!shaderstate.initeddepthnorm) @@ -3814,7 +3842,7 @@ void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) } /*do portals*/ BE_SelectMode(BEM_STANDARD); - GLBE_SubmitMeshes(true, batches, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); + GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_PORTAL); BE_SelectMode(BEM_DEPTHNORM); if (!shaderstate.depthnormshader) @@ -3822,9 +3850,7 @@ void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) BE_SelectMode(BEM_STANDARD); return; } - -#define GL_RGBA16F_ARB 0x881A -#define GL_RGBA32F_ARB 0x8814 + if (!TEXVALID(shaderstate.tex_normals)) { shaderstate.tex_normals = GL_AllocNewTexture("***prepass normals***", vid.pixelwidth, vid.pixelheight); @@ -3883,7 +3909,7 @@ void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) } /*draw surfaces that can be drawn this way*/ - GLBE_SubmitMeshes(true, batches, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE); + GLBE_SubmitMeshes(true, SHADER_SORT_OPAQUE, SHADER_SORT_OPAQUE); /*reconfigure - now drawing diffuse light info using the previous fb image as a source image*/ shaderstate.tex_sourcecol = shaderstate.tex_normals; @@ -3895,7 +3921,7 @@ void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) BE_SelectEntity(&r_worldentity); /*now draw the prelights*/ - GLBE_SubmitMeshes(true, batches, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT); + GLBE_SubmitMeshes(true, SHADER_SORT_PRELIGHT, SHADER_SORT_PRELIGHT); /*final reconfigure - now drawing final surface data onto true framebuffer*/ qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); @@ -3904,12 +3930,12 @@ void GLBE_DrawLightPrePass(qbyte *vis, batch_t **batches) /*now draw the postlight passes (this includes blended stuff which will NOT be lit)*/ BE_SelectEntity(&r_worldentity); - GLBE_SubmitMeshes(true, batches, SHADER_SORT_SKY, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(true, SHADER_SORT_SKY, SHADER_SORT_NEAREST); #ifdef RTLIGHTS /*regular lighting now*/ BE_SelectEntity(&r_worldentity); - Sh_DrawLights(vis, batches); + Sh_DrawLights(vis); #endif shaderstate.tex_sourcecol = r_nulltex; @@ -3922,7 +3948,9 @@ void GLBE_DrawWorld (qbyte *vis) { extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_lightmaps; batch_t *batches[SHADER_SORT_COUNT]; + batch_t **ob = shaderstate.mbatches; RSpeedLocals(); + shaderstate.mbatches = batches; GL_DoSwap(); @@ -4003,7 +4031,7 @@ void GLBE_DrawWorld (qbyte *vis) #ifdef RTLIGHTS if (r_lightprepass.ival) { - GLBE_DrawLightPrePass(vis, batches); + GLBE_DrawLightPrePass(vis); } else #endif @@ -4014,20 +4042,20 @@ void GLBE_DrawWorld (qbyte *vis) BE_SelectMode(BEM_STANDARD); RSpeedRemark(); - GLBE_SubmitMeshes(true, batches, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); + GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); RSpeedEnd(RSPEED_WORLD); } #ifdef RTLIGHTS RSpeedRemark(); BE_SelectEntity(&r_worldentity); - Sh_DrawLights(vis, batches); + Sh_DrawLights(vis); RSpeedEnd(RSPEED_STENCILSHADOWS); #endif shaderstate.identitylighting = 1; - GLBE_SubmitMeshes(true, batches, SHADER_SORT_DECAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(true, SHADER_SORT_DECAL, SHADER_SORT_NEAREST); /* if (r_refdef.gfog_alpha) { @@ -4039,12 +4067,14 @@ void GLBE_DrawWorld (qbyte *vis) } else { - GLBE_SubmitMeshes(false, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); + GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); } BE_SelectEntity(&r_worldentity); shaderstate.curtime = shaderstate.updatetime = realtime; shaderstate.identitylighting = 1; + + shaderstate.mbatches = ob; } #endif diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index d23a6d67..6e361e68 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -420,7 +420,6 @@ void GLDraw_Init (void) memset(gltexturetablebuckets, 0, sizeof(gltexturetablebuckets)); Hash_InitTable(&gltexturetable, sizeof(gltexturetablebuckets)/sizeof(gltexturetablebuckets[0]), gltexturetablebuckets); - GL_FlushBackEnd(); // GL_FlushSkinCache(); TRACE(("dbg: GLDraw_ReInit: GL_GAliasFlushSkinCache\n")); GL_GAliasFlushSkinCache(); diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index ebe51b38..0a81e647 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -776,8 +776,9 @@ static texid_t Font_LoadHexen2Conchars(qboolean iso88591) outbuf = BZ_Malloc(8*8*256*8); out = outbuf; + i = 0; /*read the low chars*/ - for (i = 0; i < 8*8*(iso88591?2:1); i+=1) + for (; i < 8*8*1; i+=1) { if (i&(1<<3)) in = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128; @@ -786,15 +787,42 @@ static texid_t Font_LoadHexen2Conchars(qboolean iso88591) for (x = 0; x < 16*8; x++) *out++ = *in++; } - /*read the high chars*/ - for (; i < 8*8*2; i+=1) + if (iso88591) { - if (i&(1<<3)) - in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8 - 256*4+128; - else - in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8; - for (x = 0; x < 16*8; x++) - *out++ = *in++; + /*read the non 8859-1 quake-compat control chars*/ + for (; i < 8*8*1 + 16; i+=1) + { + if (i&(1<<3)) + in = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8 - 256*4+128; + else + in = tempchars+128*128 + ((i>>3)&7)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + + /*read the final low chars (final if 8859-1 anyway)*/ + for (; i < 8*8*2; i+=1) + { + if (i&(1<<3)) + in = tempchars + (i>>3)*16*8*8+(i&7)*32*8 - 256*4+128; + else + in = tempchars + (i>>3)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } + } + else + { + /*read the high chars*/ + for (; i < 8*8*2; i+=1) + { + if (i&(1<<3)) + in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8 - 256*4+128; + else + in = tempchars+128*128 + ((i>>3)&15)*16*8*8+(i&7)*32*8; + for (x = 0; x < 16*8; x++) + *out++ = *in++; + } } FS_FreeFile(tempchars); @@ -846,7 +874,7 @@ static texid_t Font_LoadDefaultConchars(void) tex = Font_LoadQuakeConchars(); if (TEXVALID(tex)) return tex; - tex = Font_LoadHexen2Conchars(false); + tex = Font_LoadHexen2Conchars(true); if (TEXVALID(tex)) return tex; tex = Font_LoadFallbackConchars(); @@ -1065,7 +1093,7 @@ struct font_s *Font_LoadFont(int height, char *fontfilename) if (!strcmp(fontfilename, "gfx/hexen2")) { - f->singletexture = Font_LoadHexen2Conchars(true); + f->singletexture = Font_LoadHexen2Conchars(false); defaultplane = DEFAULTPLANE; } if (!TEXVALID(f->singletexture)) diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index 47017f42..c5f0acbf 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -32,6 +32,7 @@ struct world_s; typedef enum { SHADER_SORT_NONE, + SHADER_SORT_RIPPLE, SHADER_SORT_PRELIGHT, SHADER_SORT_PORTAL, SHADER_SORT_SKY, diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index 319253ab..29a9e940 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -437,7 +437,7 @@ void R_SetupGL (void) else { if (gl_maxdist.value>=1) - Matrix4x4_CM_Orthographic(r_refdef.m_projection, -fov_x/2, fov_x/2, fov_y/2, -fov_y/2, -gl_maxdist.value, gl_maxdist.value); + Matrix4x4_CM_Orthographic(r_refdef.m_projection, -fov_x/2, fov_x/2, -fov_y/2, fov_y/2, -gl_maxdist.value, gl_maxdist.value); else Matrix4x4_CM_Orthographic(r_refdef.m_projection, 0, r_refdef.vrect.width, 0, r_refdef.vrect.height, -9999, 9999); } @@ -637,7 +637,7 @@ static void TransformDir(vec3_t in, vec3_t planea[3], vec3_t viewa[3], vec3_t re VectorMA(result, d, viewa[i], result); } } -void GLR_DrawPortal(batch_t *batch, batch_t **blist) +void GLR_DrawPortal(batch_t *batch, batch_t **blist, int portaltype) { entity_t *view; GLdouble glplane[4]; @@ -663,7 +663,6 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist) if (DotProduct(r_refdef.vieworg, plane.normal)-plane.dist < 0) return; - view = R_NearestPortal(&plane); //if (!view) // return; @@ -672,7 +671,20 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist) r_refdef.externalview = true; - if (!view || VectorCompare(view->origin, view->oldorigin)) + if (portaltype == 1) + { + /*explicit mirror*/ + r_refdef.flipcull ^= true; + R_MirrorMatrix(&plane); + } + else if (portaltype == 2) + { + /*refraction image (same view, just with things culled*/ + r_refdef.externalview = false; + VectorNegate(plane.normal, plane.normal); + plane.dist = -plane.dist; + } + else if (!(view = R_NearestPortal(&plane)) || VectorCompare(view->origin, view->oldorigin)) { r_refdef.flipcull ^= true; R_MirrorMatrix(&plane); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 310c0786..d9715d11 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -766,6 +766,8 @@ static void Shader_Sort ( shader_t *shader, shaderpass_t *pass, char **ptr ) shader->sort = SHADER_SORT_BLEND; else if ( !Q_stricmp( token, "lpp_light" ) ) shader->sort = SHADER_SORT_PRELIGHT; + else if ( !Q_stricmp( token, "ripple" ) ) + shader->sort = SHADER_SORT_RIPPLE; else { shader->sort = atoi ( token ); @@ -2205,6 +2207,12 @@ static qboolean ShaderPass_MapGen (shader_t *shader, shaderpass_t *pass, char *t pass->texgen = T_GEN_REFRACTION; pass->tcgen = TC_GEN_BASE; //FIXME: moo! } + else if (!Q_stricmp (tname, "$ripplemap")) + { + shader->flags |= SHADER_HASRIPPLEMAP; + pass->texgen = T_GEN_RIPPLEMAP; + pass->tcgen = TC_GEN_BASE; //FIXME: moo! + } else return false; return true; @@ -4196,73 +4204,114 @@ void Shader_DefaultBSPQ1(char *shortname, shader_t *s, const void *args) if (!builtin && (*shortname == '*')) { - if ((r_water_refract.ival || r_water_reflect.ival) && !r_fastturb.ival && strncmp(shortname, "*lava", 5)) + int wstyle; + if (r_wateralpha.value == 0) + wstyle = -1; + else if (r_fastturb.ival) + wstyle = 0; + else if (gl_config.arb_shader_objects && r_waterstyle.ival>0 && !r_fastturb.ival && strncmp(shortname, "*lava", 5)) + wstyle = r_waterstyle.ival; //r_waterstyle does not apply to lava, and requires glsl and stuff + else + wstyle = 1; + { - builtin = ( - "{\n" + switch(wstyle) + { + case -1: //invisible + builtin = ( "{\n" - "map $currentrender\n" + "sort blend\n" + "surfaceparm nodraw\n" + "surfaceparm nodlight\n" "}\n" + ); + break; + case 0: //fastturb + builtin = ( "{\n" - "map $normalmap\n" + "sort blend\n" + "{\n" + "map $whiteimage\n" + "rgbgen const $r_fastturbcolour\n" + "}\n" + "surfaceparm nodlight\n" "}\n" + ); + break; + default: + case 1: //vanilla style + builtin = ( "{\n" - "map $diffuse\n" + "sort blend\n" /*make sure it always has the same sort order, so switching on/off wateralpha doesn't break stuff*/ + "program defaultwarp\n" + "{\n" + "map $diffuse\n" + "tcmod turb 0.02 0.1 0.5 0.1\n" + "if r_wateralpha != 1\n" + "[\n" + "alphagen const $r_wateralpha\n" + "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" + "]\n" + "}\n" + "surfaceparm nodlight\n" "}\n" - "if r_water_reflect\n" - "[\n" + ); + break; + case 2: //refraction of the underwater surface, with a fresnel + builtin = ( + "{\n" + "{\n" + "map $currentrender\n" + "}\n" + "{\n" + "map $normalmap\n" + "}\n" + "{\n" + "map $diffuse\n" + "}\n" + "program altwater#FRESNEL=4\n" + "}\n" + ); + break; + case 3: //ripples + builtin = ( + "{\n" + "{\n" + "map $currentrender\n" + "}\n" + "{\n" + "map $normalmap\n" + "}\n" + "{\n" + "map $diffuse\n" + "}\n" + "{\n" + "map $ripplemap\n" + "}\n" + "program altwater#RIPPLEMAP#FRESNEL=4\n" + "}\n" + ); + break; + case 4: //reflections + builtin = ( + "{\n" + "{\n" + "map $currentrender\n" + "}\n" + "{\n" + "map $normalmap\n" + "}\n" "{\n" "map $reflection\n" "}\n" - "program altwater#REFLECT#FRESNEL=4\n" - "][\n" - "program altwater#FRESNEL=4\n" - "]\n" - "}\n" - ); - } - //q1 water - else if (r_wateralpha.value == 0) - { - builtin = ( - "{\n" - "sort blend\n" - "surfaceparm nodraw\n" - "surfaceparm nodlight\n" - "}\n" - ); - } - else if (r_fastturb.ival) - { - builtin = ( - "{\n" - "sort blend\n" - "{\n" - "map $whiteimage\n" - "rgbgen const $r_fastturbcolour\n" + "{\n" + "map $ripplemap\n" + "}\n" + "program altwater#REFLECT#RIPPLEMAP#FRESNEL=4\n" "}\n" - "surfaceparm nodlight\n" - "}\n" - ); - } - else - { - builtin = ( - "{\n" - "sort blend\n" /*make sure it always has the same sort order, so switching on/off wateralpha doesn't break stuff*/ - "program defaultwarp\n" - "{\n" - "map $diffuse\n" - "tcmod turb 0.02 0.1 0.5 0.1\n" - "if r_wateralpha != 1\n" - "[\n" - "alphagen const $r_wateralpha\n" - "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" - "]\n" - "}\n" - "surfaceparm nodlight\n" - "}\n" - ); + ); + break; + } } } if (!builtin && !strncmp(shortname, "sky", 3)) diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 5aade016..5786ae73 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -2749,10 +2749,10 @@ static void Sh_DrawShadowlessLight(dlight_t *dl, vec3_t colour, qbyte *vvis) Sh_DrawEntLighting(dl, colour); } -void GLBE_SubmitMeshes (qboolean drawworld, batch_t **blist, int start, int stop); +void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, qboolean usedepth); -void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours, batch_t **batches) +void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) { #ifdef GLQUAKE static mesh_t mesh; @@ -2822,7 +2822,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours, batch_t **batches) BE_SelectMode(BEM_CREPUSCULAR); BE_SelectDLight(dl, colours); - GLBE_SubmitMeshes(true, batches, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); + GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); GLBE_RenderToTexture(crepuscular_texture_id, r_nulltex, r_nulltex, false); @@ -2894,7 +2894,7 @@ void Com_ParseVector(char *str, vec3_t out) out[2] = atof(com_token); } -void Sh_DrawLights(qbyte *vis, batch_t **mbatches) +void Sh_DrawLights(qbyte *vis) { vec3_t colour; dlight_t *dl; @@ -2969,7 +2969,7 @@ void Sh_DrawLights(qbyte *vis, batch_t **mbatches) continue; //just switch these off. if (dl->flags & LFLAG_CREPUSCULAR) - Sh_DrawCrepuscularLight(dl, colour, mbatches); + Sh_DrawCrepuscularLight(dl, colour); else if (((i >= RTL_FIRST)?!r_shadow_realtime_world_shadows.ival:!r_shadow_realtime_dlight_shadows.ival) || dl->flags & LFLAG_NOSHADOWS) { Sh_DrawShadowlessLight(dl, colour, vis); @@ -3014,7 +3014,7 @@ void Sh_DrawLights(qbyte *vis, batch_t **mbatches) { VectorNormalize(sundir); VectorMA(r_origin, 1000, sundir, sun.origin); - Sh_DrawCrepuscularLight(&sun, colour, mbatches); + Sh_DrawCrepuscularLight(&sun, colour); } } } diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 52be1c79..d854e123 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -313,7 +313,6 @@ void GL_MTBind(int tmu, int target, texid_t texnum); /*use this if you're going void GL_LazyBind(int tmu, int target, texid_t texnum); /*use this if you don't care about the active tmu, only that it is bound on that tmu (does not guarentee glActiveTexture) (ie: no glTexImage etc)*/ void GL_CullFace(unsigned int sflags); void GL_TexEnv(GLenum mode); -void GL_FlushBackEnd (void); // Multitexture #define GL_TEXTURE0_SGIS 0x835E @@ -355,7 +354,6 @@ void GL_DoSwap (void); // gl_backend.c // #ifdef GLQUAKE -void FTE_DEPRECATED R_BackendInit(void); void FTE_DEPRECATED R_IBrokeTheArrays(void); #endif diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index 59ba6d42..a11ab8f8 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -707,5 +707,9 @@ typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void); #define GL_TEXTURE_MAX_LEVEL 0x813d #endif +#ifndef GL_RGBA16F_ARB +#define GL_RGBA16F_ARB 0x881A +#define GL_RGBA32F_ARB 0x8814 +#endif #endif diff --git a/engine/gl/shader.h b/engine/gl/shader.h index fcca32bb..367bdd37 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -229,6 +229,7 @@ typedef struct shaderpass_s { T_GEN_REFLECTION, //reflection image (mirror-as-fbo) T_GEN_REFRACTION, //refraction image (portal-as-fbo) + T_GEN_RIPPLEMAP, //ripplemap image (water surface distortions-as-fbo) T_GEN_SOURCECUBE, //used for render-to-texture targets @@ -421,7 +422,8 @@ struct shader_s SHADER_STATICDATA = 1 << 18, //set if true: no deforms, no tcgen, rgbgen=identitylighting, alphagen=identity, tmu0=st + tmu1=lm(if available) for every pass, no norms SHADER_HASREFLECT = 1 << 19, //says that we need to generate a reflection image first SHADER_HASREFRACT = 1 << 20, //says that we need to generate a refraction image first - SHADER_HASNORMALMAP = 1 << 21 //says that we need to load a normalmap texture + SHADER_HASNORMALMAP = 1 << 21, //says that we need to load a normalmap texture + SHADER_HASRIPPLEMAP = 1 << 22, //water surface disturbances for water splashes } flags; program_t *prog; @@ -465,7 +467,6 @@ void Shader_DefaultCinematic(char *shortname, shader_t *s, const void *args); void Shader_DefaultScript(char *shortname, shader_t *s, const void *args); void Shader_DoReload(void); -void R_BackendInit (void); void Shader_Shutdown (void); qboolean Shader_Init (void); void Shader_NeedReload(qboolean rescanfs); @@ -497,6 +498,7 @@ void GLBE_DrawWorld (qbyte *vis); qboolean GLBE_LightCullModel(vec3_t org, model_t *model); void GLBE_SelectEntity(entity_t *ent); void GLBE_SelectDLight(dlight_t *dl, vec3_t colour); +void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); #endif #ifdef D3DQUAKE void D3DBE_Init(void); @@ -535,7 +537,7 @@ void D3DBE_BaseEntTextures(void); //prebuilds shadow volumes void Sh_PreGenerateLights(void); //Draws lights, called from the backend -void Sh_DrawLights(qbyte *vis, batch_t **mbatches); +void Sh_DrawLights(qbyte *vis); void SH_FreeShadowMesh(struct shadowmesh_s *sm); void Sh_Shutdown(void); //Draws the depth of ents in the world near the current light diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 1b1bd6cc..4d3823ca 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -3145,6 +3145,12 @@ qbyte *SV_Snapshot_SetupPVS(client_t *client, qbyte *pvs, unsigned int pvsbufsiz vec3_t org; int leavepvs = false; + if (r_novis.ival) + { + memset(pvs, 0xff, (sv.world.worldmodel->numleafs+31)>>3); + return pvs; + } + for (; client; client = client->controlled) { if (client->viewent) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index f37b89de..30bb77d4 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -470,7 +470,7 @@ void SV_DropClient (client_t *drop) case GT_PROGS: if (svprogfuncs) { - if (drop->state == cs_spawned) + if (drop->state == cs_spawned && host_initialized) { #ifdef VM_Q1 if (svs.gametype == GT_Q1QVM) diff --git a/engine/shaders/glsl/altwater.glsl b/engine/shaders/glsl/altwater.glsl index 267f33c9..6f696338 100644 --- a/engine/shaders/glsl/altwater.glsl +++ b/engine/shaders/glsl/altwater.glsl @@ -1,5 +1,22 @@ -//modifier: REFLECT +//modifier: REFLECT (s_t2 is a reflection instead of diffusemap) +//modifier: STRENGTH (0.1 = fairly gentle, 0.2 = big waves) //modifier: FRESNEL (5=water) +//modifier: TXSCALE (0.2 - wave strength) +//modifier: RIPPLEMAP (s_t3 contains a ripplemap +//modifier: TINT (some colour value) + +#ifndef FRESNEL +#define FRESNEL 5.0 +#endif +#ifndef STRENGTH +#define STRENGTH 0.1 +#endif +#ifndef TXSCALE +#define TXSCALE 0.2 +#endif +#ifndef TINT +#define TINT vec3(0.7, 0.8, 0.7) +#endif varying vec2 tc; varying vec4 tf; @@ -21,9 +38,9 @@ void main (void) #ifdef FRAGMENT_SHADER uniform sampler2D s_t0; //refract uniform sampler2D s_t1; //normalmap -uniform sampler2D s_t2; //diffuse -#ifdef REFLECT -uniform sampler2D s_t3; //reflect +uniform sampler2D s_t2; //diffuse/reflection +#ifdef RIPPLEMAP +uniform sampler2D s_t3; //ripplemap #endif uniform float e_time; @@ -39,27 +56,29 @@ void main (void) ntc.t = tc.t + sin(tc.s+e_time)*0.125; //generate the two wave patterns from the normalmap - n = (texture2D(s_t1, 0.2*tc + vec2(e_time*0.1, 0)).xyz); - n += (texture2D(s_t1, 0.2*tc - vec2(0, e_time*0.097)).xyz); + n = (texture2D(s_t1, TXSCALE*tc + vec2(e_time*0.1, 0)).xyz); + n += (texture2D(s_t1, TXSCALE*tc - vec2(0, e_time*0.097)).xyz); n -= 1.0 - 4.0/256.0; - n = normalize(n); - -#if 1//def REFRACT - refr = texture2D(s_t0, stc + n.st*0.2).rgb; -#else - refr = texture2D(s_t2, ntc).xyz; -#endif -#ifdef REFLECT - refl = texture2D(s_t3, stc - n.st*0.2).rgb; -#else - refl = texture2D(s_t2, ntc).xyz; +#ifdef RIPPLEMAP + n += texture2D(s_t3, stc)*3; #endif //the fresnel term decides how transparent the water should be - f = pow(1.0-abs(dot(n, normalize(eye))), float(FRESNEL)); + f = pow(1.0-abs(dot(normalize(n), normalize(eye))), float(FRESNEL)); + + refr = texture2D(s_t0, stc + n.st*STRENGTH).rgb * TINT; +#ifdef REFLECT + refl = texture2D(s_t2, stc - n.st*STRENGTH).rgb; +#else + refl = texture2D(s_t2, ntc).xyz; +#endif +// refl += 0.1*pow(dot(n, vec3(0.0,0.0,1.0)), 64.0); + fres = refr * (1.0-f) + refl*f; +// fres = texture2D(s_t2, stc).xyz; + gl_FragColor = vec4(fres, 1.0); } #endif diff --git a/engine/sw/sw.h b/engine/sw/sw.h new file mode 100644 index 00000000..272440b4 --- /dev/null +++ b/engine/sw/sw.h @@ -0,0 +1,150 @@ + +typedef struct +{ + texcom_t com; + + char name[64]; + int width; + int height; + int pitch; + unsigned int data[1]; +} swimage_t; + +typedef struct +{ + volatile unsigned int readpoint; //the command queue point its reading from + void *thread; + +#ifdef _DEBUG + float idletime; + float activetime; +#endif + + unsigned int interlaceline; + unsigned int interlacemod; + unsigned int threadnum; //for relocating viewport info + unsigned int *vpdbuf; + unsigned int *vpcbuf; + unsigned int vpwidth; + unsigned int vpheight; + qintptr_t vpcstride; +} swthread_t; + +typedef struct +{ + vec4_t vcoord; + vec2_t tccoord; + vec2_t lmcoord; + byte_vec4_t colour; + unsigned int clipflags; /*1=left,2=right,4=top,8=bottom,16=near*/ +} swvert_t; + +swthread_t *SWRast_CreateThread(int tno); + + +enum wqcmd_e +{ + WTC_DIE, + WTC_SYNC, + WTC_NEWFRAME, + WTC_NOOP, + WTC_VIEWPORT, + WTC_TRIFAN, + WTC_TRISOUP +}; + +enum +{ + CLIP_LEFT_FLAG = 1, + CLIP_RIGHT_FLAG = 2, + CLIP_TOP_FLAG = 4, + CLIP_BOTTOM_FLAG = 8, + CLIP_NEAR_FLAG = 16 +}; + +typedef union +{ + unsigned char align[16]; + + struct wqcom_s + { + enum wqcmd_e command; + unsigned int cmdsize; + } com; + struct + { + struct wqcom_s com; + + swimage_t *texture; + int numverts; + swvert_t verts[1]; + } trifan; + struct + { + struct wqcom_s com; + + swimage_t *texture; + int numverts; + int numidx; + swvert_t verts[1]; + } trisoup; + struct + { + struct wqcom_s com; + unsigned int *cbuf; + unsigned int *dbuf; + unsigned int width; + unsigned int height; + qintptr_t stride; + unsigned int interlace; + unsigned int framenum; + + qboolean cleardepth; + qboolean clearcolour; + } viewport; +} wqcom_t; + + + +void SWRast_EndCommand(wqcom_t *com); +wqcom_t *SWRast_BeginCommand(int cmdtype, unsigned int size); +void SWRast_Sync(void); + + + +qboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette); +void SW_VID_DeInit(void); +void SW_VID_SetPalette(unsigned char *palette); +void SW_VID_ShiftPalette(unsigned char *palette); +char *SW_VID_GetRGBInfo(int prepad, int *truevidwidth, int *truevidheight); +void SW_VID_SetWindowCaption(char *msg); +void SW_VID_SwapBuffers(void); +void SW_VID_UpdateViewport(wqcom_t *com); + + + + +texid_tf SW_LoadTexture(char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); +texid_tf SW_LoadTexture8Pal24(char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); +texid_tf SW_LoadTexture8Pal32(char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); +texid_tf SW_LoadCompressed(char *name); +texid_tf SW_FindTexture(char *identifier); +texid_tf SW_AllocNewTexture(char *identifier, int w, int h); +void SW_Upload(texid_t tex, char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); +void SW_DestroyTexture(texid_t tex); + + +void SWBE_SelectMode(backendmode_t mode); +void SWBE_DrawMesh_List(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags); +void SWBE_DrawMesh_Single(shader_t *shader, struct mesh_s *meshchain, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags); +void SWBE_SubmitBatch(struct batch_s *batch); +struct batch_s *SWBE_GetTempBatch(void); +void SWBE_DrawWorld(qbyte *vis); +void SWBE_Init(void); +void SWBE_GenBrushModelVBO(struct model_s *mod); +void SWBE_ClearVBO(struct vbo_s *vbo); +void SWBE_UploadAllLightmaps(void); +void SWBE_SelectEntity(struct entity_s *ent); +void SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour); +qboolean SWBE_LightCullModel(vec3_t org, struct model_s *model); +void SWBE_Set2D(void); diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c new file mode 100644 index 00000000..a2f0ed87 --- /dev/null +++ b/engine/sw/sw_backend.c @@ -0,0 +1,654 @@ +#include "quakedef.h" +#ifdef SWQUAKE +#include "sw.h" +#include "shader.h" +#include "glquake.h" + +vecV_t vertbuf[65535]; + +static struct +{ + int foo; + int numrthreads; + void *threads[4]; + backendmode_t mode; + + float m_mvp[16]; + + entity_t *curentity; + shader_t *curshader; + + float curtime; + //this stuff should probably be moved out of the backend + int wbatch; + int maxwbatches; + batch_t *wbatches; +} shaderstate; + +//////////////////////////////////////////////////////////////// +//start generic tables +#define frand() (rand()*(1.0/RAND_MAX)) +#define FTABLE_SIZE 1024 +#define FTABLE_CLAMP(x) (((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1))) +#define FTABLE_EVALUATE(table,x) (table ? table[FTABLE_CLAMP(x)] : frand()*((x)-floor(x))) + +static float r_sintable[FTABLE_SIZE]; +static float r_triangletable[FTABLE_SIZE]; +static float r_squaretable[FTABLE_SIZE]; +static float r_sawtoothtable[FTABLE_SIZE]; +static float r_inversesawtoothtable[FTABLE_SIZE]; + +static float *FTableForFunc ( unsigned int func ) +{ + switch (func) + { + case SHADER_FUNC_SIN: + return r_sintable; + + case SHADER_FUNC_TRIANGLE: + return r_triangletable; + + case SHADER_FUNC_SQUARE: + return r_squaretable; + + case SHADER_FUNC_SAWTOOTH: + return r_sawtoothtable; + + case SHADER_FUNC_INVERSESAWTOOTH: + return r_inversesawtoothtable; + } + + //bad values allow us to crash (so I can debug em) + return NULL; +} + +static void BE_InitTables(void) +{ + int i; + double t; + for (i = 0; i < FTABLE_SIZE; i++) + { + t = (double)i / (double)FTABLE_SIZE; + + r_sintable[i] = sin(t * 2*M_PI); + + if (t < 0.25) + r_triangletable[i] = t * 4.0; + else if (t < 0.75) + r_triangletable[i] = 2 - 4.0 * t; + else + r_triangletable[i] = (t - 0.75) * 4.0 - 1.0; + + if (t < 0.5) + r_squaretable[i] = 1.0f; + else + r_squaretable[i] = -1.0f; + + r_sawtoothtable[i] = t; + r_inversesawtoothtable[i] = 1.0 - t; + } +} +#define R_FastSin(x) sin((x)*(2*M_PI)) //fixme: use r_sintable instead! + +//end generic tables +//////////////////////////////////////////////////////////////// +//start matrix functions + +typedef vec3_t mat3_t[3]; +static mat3_t axisDefault={{1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}}; + +static void Matrix3_Transpose (mat3_t in, mat3_t out) +{ + out[0][0] = in[0][0]; + out[1][1] = in[1][1]; + out[2][2] = in[2][2]; + + out[0][1] = in[1][0]; + out[0][2] = in[2][0]; + out[1][0] = in[0][1]; + out[1][2] = in[2][1]; + out[2][0] = in[0][2]; + out[2][1] = in[1][2]; +} +static void Matrix3_Multiply_Vec3 (mat3_t a, vec3_t b, vec3_t product) +{ + product[0] = a[0][0]*b[0] + a[0][1]*b[1] + a[0][2]*b[2]; + product[1] = a[1][0]*b[0] + a[1][1]*b[1] + a[1][2]*b[2]; + product[2] = a[2][0]*b[0] + a[2][1]*b[1] + a[2][2]*b[2]; +} + +static int Matrix3_Compare(mat3_t in, mat3_t out) +{ + return memcmp(in, out, sizeof(mat3_t)); +} + +//end matrix functions +//////////////////////////////////////////////////////////////// +//start xyz + +static void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *dst, const mesh_t *mesh) +{ + float *table; + int j, k; + float args[4]; + float deflect; + switch (deformv->type) + { + default: + case DEFORMV_NONE: + if (src != dst) + memcpy(dst, src, sizeof(*src)*cnt); + break; + + case DEFORMV_WAVE: + if (!mesh->normals_array) + { + if (src != dst) + memcpy(dst, src, sizeof(*src)*cnt); + return; + } + args[0] = deformv->func.args[0]; + args[1] = deformv->func.args[1]; + args[3] = deformv->func.args[2] + deformv->func.args[3] * shaderstate.curtime; + table = FTableForFunc(deformv->func.type); + + for ( j = 0; j < cnt; j++ ) + { + deflect = deformv->args[0] * (src[j][0]+src[j][1]+src[j][2]) + args[3]; + deflect = FTABLE_EVALUATE(table, deflect) * args[1] + args[0]; + + // Deflect vertex along its normal by wave amount + VectorMA(src[j], deflect, mesh->normals_array[j], dst[j]); + } + break; + + case DEFORMV_NORMAL: + //normal does not actually move the verts, but it does change the normals array + //we don't currently support that. + if (src != dst) + memcpy(dst, src, sizeof(*src)*cnt); +/* + args[0] = deformv->args[1] * shaderstate.curtime; + + for ( j = 0; j < cnt; j++ ) + { + args[1] = normalsArray[j][2] * args[0]; + + deflect = deformv->args[0] * R_FastSin(args[1]); + normalsArray[j][0] *= deflect; + deflect = deformv->args[0] * R_FastSin(args[1] + 0.25); + normalsArray[j][1] *= deflect; + VectorNormalizeFast(normalsArray[j]); + } +*/ break; + + case DEFORMV_MOVE: + table = FTableForFunc(deformv->func.type); + deflect = deformv->func.args[2] + shaderstate.curtime * deformv->func.args[3]; + deflect = FTABLE_EVALUATE(table, deflect) * deformv->func.args[1] + deformv->func.args[0]; + + for ( j = 0; j < cnt; j++ ) + VectorMA(src[j], deflect, deformv->args, dst[j]); + break; + + case DEFORMV_BULGE: + args[0] = deformv->args[0]/(2*M_PI); + args[1] = deformv->args[1]; + args[2] = shaderstate.curtime * deformv->args[2]/(2*M_PI); + + for (j = 0; j < cnt; j++) + { + deflect = R_FastSin(mesh->st_array[j][0]*args[0] + args[2])*args[1]; + dst[j][0] = src[j][0]+deflect*mesh->normals_array[j][0]; + dst[j][1] = src[j][1]+deflect*mesh->normals_array[j][1]; + dst[j][2] = src[j][2]+deflect*mesh->normals_array[j][2]; + } + break; + + case DEFORMV_AUTOSPRITE: + if (mesh->numindexes < 6) + break; + + for (j = 0; j < cnt-3; j+=4, src+=4, dst+=4) + { + vec3_t mid, d; + float radius; + mid[0] = 0.25*(src[0][0] + src[1][0] + src[2][0] + src[3][0]); + mid[1] = 0.25*(src[0][1] + src[1][1] + src[2][1] + src[3][1]); + mid[2] = 0.25*(src[0][2] + src[1][2] + src[2][2] + src[3][2]); + VectorSubtract(src[0], mid, d); + radius = 2*VectorLength(d); + + for (k = 0; k < 4; k++) + { + dst[k][0] = mid[0] + radius*((mesh->st_array[k][0]-0.5)*r_refdef.m_view[0+0]-(mesh->st_array[k][1]-0.5)*r_refdef.m_view[0+1]); + dst[k][1] = mid[1] + radius*((mesh->st_array[k][0]-0.5)*r_refdef.m_view[4+0]-(mesh->st_array[k][1]-0.5)*r_refdef.m_view[4+1]); + dst[k][2] = mid[2] + radius*((mesh->st_array[k][0]-0.5)*r_refdef.m_view[8+0]-(mesh->st_array[k][1]-0.5)*r_refdef.m_view[8+1]); + } + } + break; + + case DEFORMV_AUTOSPRITE2: + if (mesh->numindexes < 6) + break; + + for (k = 0; k < mesh->numindexes; k += 6) + { + int long_axis, short_axis; + vec3_t axis; + float len[3]; + mat3_t m0, m1, m2, result; + float *quad[4]; + vec3_t rot_centre, tv; + + quad[0] = (float *)(dst + mesh->indexes[k+0]); + quad[1] = (float *)(dst + mesh->indexes[k+1]); + quad[2] = (float *)(dst + mesh->indexes[k+2]); + + for (j = 2; j >= 0; j--) + { + quad[3] = (float *)(dst + mesh->indexes[k+3+j]); + if (!VectorEquals (quad[3], quad[0]) && + !VectorEquals (quad[3], quad[1]) && + !VectorEquals (quad[3], quad[2])) + { + break; + } + } + + // build a matrix were the longest axis of the billboard is the Y-Axis + VectorSubtract(quad[1], quad[0], m0[0]); + VectorSubtract(quad[2], quad[0], m0[1]); + VectorSubtract(quad[2], quad[1], m0[2]); + len[0] = DotProduct(m0[0], m0[0]); + len[1] = DotProduct(m0[1], m0[1]); + len[2] = DotProduct(m0[2], m0[2]); + + if ((len[2] > len[1]) && (len[2] > len[0])) + { + if (len[1] > len[0]) + { + long_axis = 1; + short_axis = 0; + } + else + { + long_axis = 0; + short_axis = 1; + } + } + else if ((len[1] > len[2]) && (len[1] > len[0])) + { + if (len[2] > len[0]) + { + long_axis = 2; + short_axis = 0; + } + else + { + long_axis = 0; + short_axis = 2; + } + } + else //if ( (len[0] > len[1]) && (len[0] > len[2]) ) + { + if (len[2] > len[1]) + { + long_axis = 2; + short_axis = 1; + } + else + { + long_axis = 1; + short_axis = 2; + } + } + + if (DotProduct(m0[long_axis], m0[short_axis])) + { + VectorNormalize2(m0[long_axis], axis); + VectorCopy(axis, m0[1]); + + if (axis[0] || axis[1]) + { + VectorVectors(m0[1], m0[2], m0[0]); + } + else + { + VectorVectors(m0[1], m0[0], m0[2]); + } + } + else + { + VectorNormalize2(m0[long_axis], axis); + VectorNormalize2(m0[short_axis], m0[0]); + VectorCopy(axis, m0[1]); + CrossProduct(m0[0], m0[1], m0[2]); + } + + for (j = 0; j < 3; j++) + rot_centre[j] = (quad[0][j] + quad[1][j] + quad[2][j] + quad[3][j]) * 0.25; + + if (shaderstate.curentity) + { + VectorAdd(shaderstate.curentity->origin, rot_centre, tv); + } + else + { + VectorCopy(rot_centre, tv); + } + VectorSubtract(r_origin, tv, tv); + + // filter any longest-axis-parts off the camera-direction + deflect = -DotProduct(tv, axis); + + VectorMA(tv, deflect, axis, m1[2]); + VectorNormalizeFast(m1[2]); + VectorCopy(axis, m1[1]); + CrossProduct(m1[1], m1[2], m1[0]); + + Matrix3_Transpose(m1, m2); + Matrix3_Multiply(m2, m0, result); + + for (j = 0; j < 4; j++) + { + VectorSubtract(quad[j], rot_centre, tv); + Matrix3_Multiply_Vec3(result, tv, quad[j]); + VectorAdd(rot_centre, quad[j], quad[j]); + } + } + break; + +// case DEFORMV_PROJECTION_SHADOW: +// break; + } +} +//end xyz +//////////////////////////////////////////////////////////////// + +void SWBE_SelectMode(backendmode_t mode) +{ +} + +void SWBE_TransformVerticies(swvert_t *v, mesh_t *mesh) +{ + extern cvar_t temp1; + int i; + + vecV_t *xyz; + + /*generate vertex blends*/ + if (mesh->xyz2_array) + { + xyz = vertbuf; + for (i = 0; i < mesh->numvertexes; i++) + { + VectorInterpolate(mesh->xyz_array[i], mesh->xyz_blendw[1], mesh->xyz2_array[i], xyz[i]); + } + } + /*else if (skeletal) + { + } + */ + else + { + xyz = mesh->xyz_array; + } + + /*now apply any shader deforms*/ + if (shaderstate.curshader->numdeforms) + { + deformgen(&shaderstate.curshader->deforms[0], mesh->numvertexes, xyz, vertbuf, mesh); + xyz = vertbuf; + for (i = 1; i < shaderstate.curshader->numdeforms; i++) + { + deformgen(&shaderstate.curshader->deforms[i], mesh->numvertexes, xyz, xyz, mesh); + } + } + + for (i = 0; i < mesh->numvertexes; i++, v++) + { + Matrix4x4_CM_Transform34(shaderstate.m_mvp, xyz[i], v->vcoord); + + if (v->vcoord[3] != 1) + { + v->vcoord[0] /= v->vcoord[3]; + v->vcoord[1] /= v->vcoord[3]; + v->vcoord[2] /= v->vcoord[3]; + } + v->vcoord[0] = (v->vcoord[0]+1)/2 * vid.pixelwidth; + if (v->vcoord[0] < 0) + v->clipflags = CLIP_LEFT_FLAG; + else + v->clipflags = 0; + if (v->vcoord[0] > vid.pixelwidth-1) + v->clipflags |= CLIP_RIGHT_FLAG; + v->vcoord[1] = (v->vcoord[1]+1)/2 * vid.pixelheight; + if (v->vcoord[1] < temp1.value) + v->clipflags |= CLIP_TOP_FLAG; + if (v->vcoord[1] > vid.pixelheight-1) + v->clipflags |= CLIP_BOTTOM_FLAG; + v->vcoord[2] = (v->vcoord[2]+1)/2; + if (v->vcoord[3] < 0) + v->clipflags |= CLIP_NEAR_FLAG; + if (v->vcoord[2] >= 1) + v->vcoord[2] = 1; + + Vector2Copy(mesh->st_array[i], v->tccoord); + +// v->colour[0] = mesh->colors4b_array[i][0]; +// v->colour[1] = mesh->colors4b_array[i][1]; +// v->colour[2] = mesh->colors4b_array[i][2]; +// v->colour[3] = mesh->colors4b_array[i][3]; + } +} +void SWBE_DrawMesh_Single(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags) +{ + wqcom_t *com; + + shaderstate.curshader = shader; + + if (mesh->istrifan) + { + com = SWRast_BeginCommand(WTC_TRIFAN, mesh->numvertexes*sizeof(swvert_t) + sizeof(com->trifan) - sizeof(com->trifan.verts)); + + com->trifan.texture = texnums->base.ptr; + com->trifan.numverts = mesh->numvertexes; + + SWBE_TransformVerticies(com->trifan.verts, mesh); + + SWRast_EndCommand(com); + } + else + { + com = SWRast_BeginCommand(WTC_TRISOUP, (mesh->numvertexes*sizeof(swvert_t)) + sizeof(com->trisoup) - sizeof(com->trisoup.verts) + (sizeof(index_t)*mesh->numindexes)); + + com->trisoup.texture = texnums->base.ptr; + com->trisoup.numverts = mesh->numvertexes; + com->trisoup.numidx = mesh->numindexes; + + SWBE_TransformVerticies(com->trisoup.verts, mesh); + memcpy(com->trisoup.verts + mesh->numvertexes, mesh->indexes, sizeof(index_t)*mesh->numindexes); + + SWRast_EndCommand(com); + } +} +void SWBE_DrawMesh_List(shader_t *shader, int nummeshes, struct mesh_s **mesh, struct vbo_s *vbo, struct texnums_s *texnums, unsigned int be_flags) +{ + while(nummeshes-->0) + { + SWBE_DrawMesh_Single(shader, *mesh++, vbo, texnums, be_flags); + } +} +void SWBE_SubmitBatch(struct batch_s *batch) +{ + int m; + SWBE_SelectEntity(batch->ent); + for (m = 0; m < batch->meshes; m++) + { + SWBE_DrawMesh_Single(batch->shader, batch->mesh[m], batch->vbo, batch->skin?batch->skin:&batch->shader->defaulttextures, batch->flags); + } +} +struct batch_s *SWBE_GetTempBatch(void) +{ + if (shaderstate.wbatch >= shaderstate.maxwbatches) + { + shaderstate.wbatch++; + return NULL; + } + return &shaderstate.wbatches[shaderstate.wbatch++]; +} + +static void SWBE_SubmitMeshesSortList(batch_t *sortlist) +{ + batch_t *batch; + for (batch = sortlist; batch; batch = batch->next) + { + if (batch->meshes == batch->firstmesh) + continue; + + if (batch->flags & BEF_NODLIGHT) + if (shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_SMAPLIGHT) + continue; + if (batch->flags & BEF_NOSHADOWS) + if (shaderstate.mode == BEM_STENCIL) + continue; + + if (batch->buildmeshes) + batch->buildmeshes(batch); + else if (batch->texture) + batch->shader = R_TextureAnimation(batch->ent->framestate.g[FS_REG].frame[0], batch->texture)->shader; + + if (batch->shader->flags & SHADER_NODRAW) + continue; + if (batch->shader->flags & SHADER_NODLIGHT) + if (shaderstate.mode == BEM_LIGHT || shaderstate.mode == BEM_SMAPLIGHT) + continue; + if (batch->shader->flags & SHADER_SKY) + { + if (shaderstate.mode == BEM_STANDARD || shaderstate.mode == BEM_DEPTHDARK) + { + if (!batch->shader->prog) + { + R_DrawSkyChain (batch); + continue; + } + } + else if (shaderstate.mode != BEM_FOG && shaderstate.mode != BEM_CREPUSCULAR) + continue; + } + + SWBE_SubmitBatch(batch); + } +} + +void SWBE_SubmitMeshes (qboolean drawworld, batch_t **blist, int start, int stop) +{ + model_t *model = cl.worldmodel; + int i; + + for (i = start; i <= stop; i++) + { + if (drawworld) + { +// if (i == SHADER_SORT_PORTAL && !r_noportals.ival && !r_refdef.recurse) +// SWBE_SubmitMeshesPortals(model->batches, blist[i]); + + SWBE_SubmitMeshesSortList(model->batches[i]); + } + SWBE_SubmitMeshesSortList(blist[i]); + } +} +void SWBE_Set2D(void) +{ + extern cvar_t gl_screenangle; + float ang, rad, w, h; + float tmp[16]; + float tmp2[16]; + ang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90; + ang = (int)ang * 90; + if (ang) + { /*more expensive maths*/ + rad = (ang * M_PI) / 180; + + w = fabs(cos(rad)) * (vid.width) + fabs(sin(rad)) * (vid.height); + h = fabs(sin(rad)) * (vid.width) + fabs(cos(rad)) * (vid.height); + + Matrix4x4_CM_Orthographic(r_refdef.m_projection, w/-2.0f, w/2.0f, h/2.0f, h/-2.0f, -99999, 99999); + + Matrix4x4_Identity(tmp); + Matrix4_Multiply(Matrix4x4_CM_NewTranslation((vid.width/-2.0f), (vid.height/-2.0f), 0), tmp, tmp2); + Matrix4_Multiply(Matrix4x4_CM_NewRotation(-ang, 0, 0, 1), tmp2, r_refdef.m_view); + } + else + { + if (0) + Matrix4x4_CM_Orthographic(r_refdef.m_projection, 0, vid.width, 0, vid.height, 0, 99999); + else + Matrix4x4_CM_Orthographic(r_refdef.m_projection, 0, vid.width, vid.height, 0, 0, 99999); + Matrix4x4_Identity(r_refdef.m_view); + } + + memcpy(shaderstate.m_mvp, r_refdef.m_projection, sizeof(shaderstate.m_mvp)); +} +void SWBE_DrawWorld(qbyte *vis) +{ + batch_t *batches[SHADER_SORT_COUNT]; + + if (!r_refdef.recurse) + { + if (shaderstate.wbatch + 50 > shaderstate.maxwbatches) + { + int newm = shaderstate.wbatch + 100; + shaderstate.wbatches = BZ_Realloc(shaderstate.wbatches, newm * sizeof(*shaderstate.wbatches)); + memset(shaderstate.wbatches + shaderstate.maxwbatches, 0, (newm - shaderstate.maxwbatches) * sizeof(*shaderstate.wbatches)); + shaderstate.maxwbatches = newm; + } + + shaderstate.wbatch = 0; + } + BE_GenModelBatches(batches); +// R_GenDlightBatches(batches); + + shaderstate.curentity = NULL; + SWBE_SelectEntity(&r_worldentity); + + SWBE_SubmitMeshes(vis!=NULL, batches, SHADER_SORT_PORTAL, SHADER_SORT_NEAREST); + + SWBE_Set2D(); +} +void SWBE_Init(void) +{ + BE_InitTables(); +} +void SWBE_GenBrushModelVBO(struct model_s *mod) +{ +} +void SWBE_ClearVBO(struct vbo_s *vbo) +{ +} +void SWBE_UploadAllLightmaps(void) +{ +} +void SWBE_SelectEntity(struct entity_s *ent) +{ + float modelmatrix[16]; + float modelviewmatrix[16]; + + if (shaderstate.curentity == ent) + return; + shaderstate.curentity = ent; + + R_RotateForEntity(modelmatrix, modelviewmatrix, shaderstate.curentity, shaderstate.curentity->model); + Matrix4_Multiply(r_refdef.m_projection, modelviewmatrix, shaderstate.m_mvp); +} +void SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour) +{ +} +qboolean SWBE_LightCullModel(vec3_t org, struct model_s *model) +{ + return false; +} +#endif diff --git a/engine/sw/sw_image.c b/engine/sw/sw_image.c new file mode 100644 index 00000000..77f1cdbf --- /dev/null +++ b/engine/sw/sw_image.c @@ -0,0 +1,122 @@ +#include "quakedef.h" +#ifdef SWQUAKE +#include "sw.h" + + + +texid_tf SW_AllocNewTexture(char *identifier, int w, int h) +{ + texid_t n; + swimage_t *img; + if (w & 3) + return r_nulltex; + img = BZ_Malloc(sizeof(*img) - sizeof(img->data) + (w * h * 4)); + + Q_strncpy(img->name, identifier, sizeof(img->name)); + img->width = w; + img->height = h; + img->pitch = w; + + n.ptr = img; + n.ref = &img->com; + return n; +} +texid_tf SW_FindTexture(char *identifier) +{ + return r_nulltex; +} + +void SW_RGBToBGR(swimage_t *img) +{ + int x, y; + unsigned int *d = img->data; + for (y = 0; y < img->height; y++) + { + for (x = 0; x < img->width; x++) + { + d[x] = (d[x]&0xff00ff00) | ((d[x]&0xff)<<16) | ((d[x]&0xff0000)>>16); + } + d += img->pitch; + } +} +void SW_Upload32(swimage_t *img, int w, int h, unsigned int *data) +{ + int x, y; + unsigned int *out = img->data; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + out[x] = *data++; + } + out += img->pitch; + } + SW_RGBToBGR(img); +} +void SW_Upload8(swimage_t *img, int w, int h, unsigned char *data) +{ + int x, y; + unsigned int *out = img->data; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + out[x] = d_8to24rgbtable[*data++]; + } + out += img->pitch; + } + SW_RGBToBGR(img); +} + +texid_tf SW_LoadTexture(char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags) +{ + texid_t img = SW_FindTexture(identifier); + if (!img.ptr) + img = SW_AllocNewTexture(identifier, width, height); + switch(fmt) + { + case TF_SOLID8: + SW_Upload8(img.ptr, width, height, data); + break; + case TF_TRANS8: + SW_Upload8(img.ptr, width, height, data); + break; + case TF_TRANS8_FULLBRIGHT: + SW_Upload8(img.ptr, width, height, data); + break; + case TF_RGBA32: + SW_Upload32(img.ptr, width, height, data); + break; + default: + //shouldn't happen, so I'm gonna leak + Con_Printf("SW_LoadTexture: unsupported format %i\n", fmt); + return r_nulltex; + } + return img; +} +texid_tf SW_LoadTexture8Pal24(char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) +{ + return r_nulltex; +} +texid_tf SW_LoadTexture8Pal32(char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) +{ + return r_nulltex; +} +texid_tf SW_LoadCompressed(char *name) +{ + return r_nulltex; +} +void SW_Upload(texid_t tex, char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) +{ +} +void SW_DestroyTexture(texid_t tex) +{ + swimage_t *img = tex.ptr; + + /*make sure its not in use by the renderer*/ + SWRast_Sync(); + + /*okay, it can be killed*/ + BZ_Free(img); +} +#endif diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c new file mode 100644 index 00000000..c5235a92 --- /dev/null +++ b/engine/sw/sw_rast.c @@ -0,0 +1,994 @@ +#include "quakedef.h" +#ifdef SWQUAKE +#include "sw.h" +#include "gl_draw.h" +#include "shader.h" +#include "renderque.h" +#include "glquake.h" + +#if __STDC_VERSION__ >= 199901L + //no need to do anything +#elif defined(_MSC_VER) + #define restrict __restrict +#else + #define restrict +#endif + +/* +Our software rendering basically works like this: + +main thread builds command: + command contains vertex data in the command block + main thread runs the vertex programs (much like q3) and performs matrix transforms (much like d3d) + +worker threads read each command sequentially: + clip to viewport + +division of labour between worker threads works by interlacing. +each thread gets a different set of scanlines to render. +we can also trivially implement interlacing with this method + +*/ + + +#define MAXWORKERTHREADS 64 +static swthread_t swthreads[MAXWORKERTHREADS]; + +cvar_t sw_interlace = CVAR("sw_interlace", "0"); +cvar_t sw_threads = CVAR("sw_threads", "0"); + +struct +{ + unsigned int numthreads; + qbyte queue[1024*1024*8]; + volatile unsigned int pos; +} wq; +#define WQ_MASK (sizeof(wq.queue)-1) + +static void WT_Triangle(swthread_t *th, swimage_t *img, swvert_t *v1, swvert_t *v2, swvert_t *v3) +{ + unsigned int tpix; +#define SPAN_ST +#define SPAN_Z +#define PLOT_PIXEL(o) \ + { \ + if (*zb >= z) \ + { \ + *zb = z; \ + tpix = img->data[ \ + ((unsigned)(s*img->width)%img->width) \ + + (((unsigned)(t*img->height)%img->height) * img->width) \ + ]; \ + if (tpix&0xff000000) \ + o = tpix; \ + } \ + } + +#ifdef MSVCWORKSPROPERLY +#include "sw_spans.h" +#else +/* +this file is expected to be #included as the body of a real function +to define create a new pixel shader, define PLOT_PIXEL(outval) at the top of your function and you're good to go + +//modifiers: +SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' + affine... no perspective correction. + + +*/ + +{ + swvert_t *vt; + int y; + int secondhalf; + int xl,xld, xr,xrd; +#ifdef SPAN_ST + float sl,sld, sd; + float tl,tld, td; +#endif +#ifdef SPAN_Z + unsigned int zl,zld, zd; +#endif + unsigned int *restrict outbuf; + unsigned int *restrict ti; + int i; + const swvert_t *vlt,*vlb,*vrt,*vrb; + int spanlen; + int numspans; + unsigned int *vplout; + int dx, dy; + int recalcside; + int interlace; + + float fdx1,fdy1,fdx2,fdy2,fz,d1,d2; + + if (!img) + return; + + /*we basically render a diamond + that is, the single triangle is split into two triangles, outwards towards the midpoint and inwards to the final position. + */ + + /*reorder the verticies for height*/ + if (v1->vcoord[1] > v2->vcoord[1]) + { + vt = v1; + v1 = v2; + v2 = vt; + } + if (v1->vcoord[1] > v3->vcoord[1]) + { + vt = v1; + v1 = v3; + v3 = vt; + } + if (v2->vcoord[1] > v3->vcoord[1]) + { + vt = v3; + v3 = v2; + v2 = vt; + } + + { + const swvert_t *v[3]; + + v[0] = v1; + v[1] = v2; + v[2] = v3; + + //reject triangles with any point offscreen, for now + for (i = 0; i < 3; i++) + { + if (v[i]->vcoord[0] < 0 || v[i]->vcoord[0] >= th->vpwidth) + return; + if (v[i]->vcoord[1] < 0 || v[i]->vcoord[1] >= th->vpheight) + return; + if (v[i]->vcoord[2] < 0) + return; + } + + for (i = 0; i < 2; i++) + { + if (v[i]->vcoord[1] > v[i+1]->vcoord[1]) + return; + } + } + + fdx1 = v2->vcoord[0] - v1->vcoord[0]; + fdy1 = v2->vcoord[1] - v1->vcoord[1]; + + fdx2 = v3->vcoord[0] - v1->vcoord[0]; + fdy2 = v3->vcoord[1] - v1->vcoord[1]; + + fz = fdx1*fdy2 - fdx2*fdy1; + + if (fz == 0) + { + //weird angle... + return; + } + + fz = 1.0 / fz; + fdx1 *= fz; + fdy1 *= fz; + fdx2 *= fz; + fdy2 *= fz; + +#ifdef SPAN_ST //affine + d1 = v2->tccoord[0] - v1->tccoord[0]; + d2 = v3->tccoord[0] - v1->tccoord[0]; + sld = fdx1*d2 - fdx2*d1; + sd = fdy2*d1 - fdy1*d2; + + d1 = v2->tccoord[1] - v1->tccoord[1]; + d2 = v3->tccoord[1] - v1->tccoord[1]; + tld = fdx1*d2 - fdx2*d1; + td = fdy2*d1 - fdy1*d2; +#endif +#ifdef SPAN_Z + d1 = (v2->vcoord[2] - v1->vcoord[2])*UINT_MAX; + d2 = (v3->vcoord[2] - v1->vcoord[2])*UINT_MAX; + zld = fdx1*d2 - fdx2*d1; + zd = fdy2*d1 - fdy1*d2; +#endif + + ti = img->data; + + y = v1->vcoord[1]; + + for (secondhalf = 0; secondhalf <= 1; secondhalf++) + { + if (secondhalf) + { + if (numspans < 0) + { + interlace = -numspans; + y+=interlace; + numspans-=interlace; + + xl += xld*interlace; + xr += xrd*interlace; + vplout += th->vpcstride*interlace; + +#ifdef SPAN_ST + sl += sld*interlace; + tl += tld*interlace; +#endif +#ifdef SPAN_Z + zl += zld*interlace; +#endif + } + + /*v2->v3*/ + if (fz <= 0) + { + vlt = v2; + //vrt == v1; + vlb = v3; + //vrb == v3; + + recalcside = 1; + +#ifdef SPAN_ST + sld -= sd*xld/(float)(1<<16); + tld -= td*xld/(float)(1<<16); +#endif +#ifdef SPAN_Z + zld -= zd*xld/(float)(1<<16); +#endif + } + else + { + //vlt == v1; + vrt = v2; + ///vlb == v3; + vrb = v3; + + recalcside = 2; + } + + //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) + numspans = v3->vcoord[1] - y; + } + else + { + vlt = v1; + vrt = v1; + /*v1->v2*/ + if (fz < 0) + { + vlb = v2; + vrb = v3; + } + else + { + vlb = v3; + vrb = v2; + } + recalcside = 3; + + //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) + numspans = v2->vcoord[1] - y; + } + + if (recalcside & 1) + { + dx = (vlb->vcoord[0] - vlt->vcoord[0]); + dy = (vlb->vcoord[1] - vlt->vcoord[1]); + if (dy > 0) + xld = (dx<<16) / dy; + else + xld = 0; + xl = (int)vlt->vcoord[0]<<16; + +#ifdef SPAN_ST + sl = vlt->tccoord[0]; + sld = sld + sd*xld/(float)(1<<16); + tl = vlt->tccoord[1]; + tld = tld + td*xld/(float)(1<<16); +#endif +#ifdef SPAN_Z + zl = vlt->vcoord[2]*UINT_MAX; + zld = zld + zd*xld/(float)(1<<16); +#endif + } + + if (recalcside & 2) + { + dx = (vrb->vcoord[0] - vrt->vcoord[0]); + dy = (vrb->vcoord[1] - vrt->vcoord[1]); + if (dy) + xrd = (dx<<16) / dy; + else + xrd = 0; + xr = (int)vrt->vcoord[0]<<16; + } + + + + if (y + numspans >= th->vpheight) + numspans = th->vpheight - y - 1; + + if (numspans <= 0) + continue; + + + vplout = th->vpcbuf + y * th->vpcstride; //this is a pointer to the left of the viewport buffer. + + interlace = ((y + th->interlaceline) % th->interlacemod); + if (interlace) + { + if (interlace > numspans) + { + interlace = numspans; + y+=interlace; + } + else + { + y+=interlace; + numspans-=interlace; + } + xl += xld*interlace; + xr += xrd*interlace; + vplout += th->vpcstride*interlace; + +#ifdef SPAN_ST + sl += sld*interlace; + tl += tld*interlace; +#endif +#ifdef SPAN_Z + zl += zld*interlace; +#endif + } + + for (; numspans > 0; + numspans -= th->interlacemod + ,xl += xld*th->interlacemod + ,xr += xrd*th->interlacemod + ,vplout += th->vpcstride*th->interlacemod + ,y += th->interlacemod + +#ifdef SPAN_ST + ,sl += sld*th->interlacemod + ,tl += tld*th->interlacemod +#endif +#ifdef SPAN_Z + ,zl += zld*th->interlacemod +#endif + ) + { +#ifdef SPAN_ST + float s = sl; + float t = tl; +#endif +#ifdef SPAN_Z + unsigned int z = zl; + unsigned int *restrict zb = th->vpdbuf + y * th->vpwidth + (xl>>16); +#endif + + spanlen = (xr - xl)>>16; + outbuf = vplout + (xl>>16); + + while(spanlen-->=0) + { + PLOT_PIXEL(*outbuf); + outbuf++; + +#ifdef SPAN_ST + s += sd; + t += td; +#endif +#ifdef SPAN_Z + z += zd; + zb++; +#endif + } + } + } +} + +#undef SPAN_ST +#undef PLOT_PIXEL +#endif + +} + +static void WT_Clip_Top(swvert_t *out, swvert_t *in, swvert_t *result) +{ + float frac; + frac = (0 - in->vcoord[1]) / + (out->vcoord[1] - in->vcoord[1]); + VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + result->vcoord[1] = 0; + Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); +} +static void WT_Clip_Bottom(swvert_t *out, swvert_t *in, swvert_t *result) +{ + float frac; + frac = (vid.pixelheight-1 - in->vcoord[1]) / + (out->vcoord[1] - in->vcoord[1]); + VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + result->vcoord[1] = vid.pixelheight-1; + Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); +} +static void WT_Clip_Left(swvert_t *out, swvert_t *in, swvert_t *result) +{ + float frac; + frac = (0 - in->vcoord[0]) / + (out->vcoord[0] - in->vcoord[0]); + VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + result->vcoord[0] = 0; + Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); +} +static void WT_Clip_Right(swvert_t *out, swvert_t *in, swvert_t *result) +{ + float frac; + frac = (vid.pixelwidth-1 - in->vcoord[0]) / + (out->vcoord[0] - in->vcoord[0]); + VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + result->vcoord[0] = vid.pixelwidth-1; + Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); +} +static void WT_Clip_Near(swvert_t *out, swvert_t *in, swvert_t *result) +{ + extern cvar_t temp1; + double frac; + frac = (temp1.value - in->vcoord[2]) / + (out->vcoord[2] - in->vcoord[2]); + VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + result->vcoord[2] = temp1.value; + Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); +} + +static int WT_ClipPoly(int incount, swvert_t *inv, swvert_t *outv, int flag, void (*clip)(swvert_t *out, swvert_t *in, swvert_t *result)) +{ + int p, c; + int result = 0; + int pf, cf; + if (incount < 3) + return 0; + + for (p = incount - 1, c = 0; c < incount; p = c, c++) + { + pf = inv[p].clipflags & flag; + cf = inv[c].clipflags & flag; + + if (pf && cf) + continue; //both clipped, skip it now + if (pf ^ cf) + { + //crossed... emit a new vertex on the boundary + if (cf) //new is offscreen + clip(&inv[c], &inv[p], &outv[result]); + else + clip(&inv[p], &inv[c], &outv[result]); + outv[result].clipflags = 0; + + if (outv[result].vcoord[0] < 0) + outv[result].clipflags = CLIP_LEFT_FLAG; + if (outv[result].vcoord[0] > vid.pixelwidth-1) + outv[result].clipflags |= CLIP_RIGHT_FLAG; + if (outv[result].vcoord[1] < 0) + outv[result].clipflags |= CLIP_TOP_FLAG; + if (outv[result].vcoord[1] > vid.pixelheight-1) + outv[result].clipflags |= CLIP_BOTTOM_FLAG; + + result++; + } + if (!cf) + { + outv[result] = inv[c]; + result++; + } + } + return result; +} + +static void WT_ClipTriangle(swthread_t *th, swimage_t *img, swvert_t *v1, swvert_t *v2, swvert_t *v3) +{ + unsigned int cflags; + swvert_t final[2][64]; + int list = 0; + int i; + int count; + + if (v1->clipflags & v2->clipflags & v3->clipflags) + return; //all verticies are off at least one single side + + cflags = v1->clipflags | v2->clipflags | v3->clipflags; + + if (!cflags) + { + //no clipping to be done + WT_Triangle(th, img, v1, v2, v3); + return; + } + + final[list][0] = *v1; + final[list][1] = *v2; + final[list][2] = *v3; + count = 3; + + if (cflags & CLIP_NEAR_FLAG) + { + count = WT_ClipPoly(count, final[list], final[list^1], CLIP_NEAR_FLAG, WT_Clip_Near); + list ^= 1; + } + if (cflags & CLIP_TOP_FLAG) + { + count = WT_ClipPoly(count, final[list], final[list^1], CLIP_TOP_FLAG, WT_Clip_Top); + list ^= 1; + } + if (cflags & CLIP_BOTTOM_FLAG) + { + count = WT_ClipPoly(count, final[list], final[list^1], CLIP_BOTTOM_FLAG, WT_Clip_Bottom); + list ^= 1; + } + if (cflags & CLIP_LEFT_FLAG) + { + count = WT_ClipPoly(count, final[list], final[list^1], CLIP_LEFT_FLAG, WT_Clip_Left); + list ^= 1; + } + if (cflags & CLIP_RIGHT_FLAG) + { + count = WT_ClipPoly(count, final[list], final[list^1], CLIP_RIGHT_FLAG, WT_Clip_Right); + list ^= 1; + } + + for (i = 2; i < count; i++) + { + WT_Triangle(th, img, &final[list][0], &final[list][i-1], &final[list][i]); + } +} + +void WQ_ClearBuffer(swthread_t *t, unsigned int *mbuf, qintptr_t stride, unsigned int clearval) +{ + int y; + int x; + unsigned int *buf; + + for (y = t->interlaceline; y < t->vpheight; y += t->interlacemod) + { + buf = mbuf + stride*y; + for (x = 0; x < (t->vpwidth & ~15);) + { + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + buf[x++] = clearval; + } + for (; x < t->vpwidth; ) + buf[x++] = clearval; + } +} + +qboolean WT_HandleCommand(swthread_t *t, wqcom_t *com) +{ + index_t *idx; + int i; + switch(com->com.command) + { + case WTC_DIE: + t->readpoint += com->com.cmdsize; + return 1; + case WTC_NOOP: + break; + case WTC_NEWFRAME: + break; + case WTC_VIEWPORT: + t->vpcbuf = com->viewport.cbuf; + t->vpdbuf = com->viewport.dbuf; + t->vpwidth = com->viewport.width; + t->vpheight = com->viewport.height; + t->vpcstride = com->viewport.stride; + if (!wq.numthreads) + { + t->interlacemod = com->viewport.interlace; //this many vthreads + t->interlaceline = com->viewport.framenum%com->viewport.interlace; //this vthread + } + else + { + t->interlacemod = wq.numthreads*com->viewport.interlace; //this many vthreads + t->interlaceline = (t->threadnum*com->viewport.interlace) + (com->viewport.framenum%com->viewport.interlace); //this vthread + } + + if (com->viewport.clearcolour) + { + WQ_ClearBuffer(t, t->vpcbuf, t->vpcstride, 0); + } + if (com->viewport.cleardepth) + { + WQ_ClearBuffer(t, t->vpdbuf, t->vpwidth, ~0u); + } + break; + case WTC_TRIFAN: + for (i = 2; i < com->trifan.numverts; i++) + { + WT_ClipTriangle(t, com->trifan.texture, &com->trifan.verts[0], &com->trifan.verts[i-1], &com->trifan.verts[i]); + } + break; + case WTC_TRISOUP: + idx = (index_t*)(com->trisoup.verts + com->trisoup.numverts); + for (i = 0; i < com->trisoup.numidx; i+=3, idx+=3) + { + WT_ClipTriangle(t, com->trisoup.texture, &com->trisoup.verts[idx[0]], &com->trisoup.verts[idx[1]], &com->trisoup.verts[idx[2]]); + } + break; + default: + Sys_Printf("Unknown render command!\n"); + break; + } + t->readpoint += com->com.cmdsize; + return false; +} + +int WT_Main(void *ptr) +{ + wqcom_t *com; + swthread_t *t = ptr; + for(;;) + { + if (t->readpoint == wq.pos) + { + Sys_Sleep(0); + continue; + } + com = (wqcom_t*)&wq.queue[t->readpoint & WQ_MASK]; + if (WT_HandleCommand(t, com)) + break; + } + return 0; +} +void SWRast_EndCommand(wqcom_t *com) +{ + wq.pos += com->com.cmdsize; + + if (!wq.numthreads) + { + //immediate mode + WT_HandleCommand(swthreads, com); + } +} +wqcom_t *SWRast_BeginCommand(int cmdtype, unsigned int size) +{ + wqcom_t *com; + //round the command size up, so we always have space for a noop/wrap if needed + size = (size + sizeof(com->align)) & ~(sizeof(com->align)-1); + + //generate a noop if we don't have enough space for the command + if ((wq.pos&WQ_MASK) + size > sizeof(wq.queue)) + { +// SWRast_Sync(); + com = (wqcom_t *)&wq.queue[wq.pos&WQ_MASK]; + com->com.cmdsize = sizeof(wq.queue) - wq.pos&WQ_MASK; + com->com.command = WTC_NOOP; + SWRast_EndCommand(com); + } + + com = (wqcom_t *)&wq.queue[wq.pos&WQ_MASK]; + com->com.cmdsize = size; + com->com.command = cmdtype; + + return com; +} +void SWRast_Sync(void) +{ + int i; + swthread_t *t; + + for (i = 0; i < wq.numthreads; i++) + { + t = &swthreads[i]; + while (t->readpoint != wq.pos) + ; + } + + //all worker threads are up to speed +} +void SWRast_CreateThreadPool(int numthreads) +{ + int i = 0; + swthread_t *t; + wq.pos = 0; + numthreads = ((numthreads > MAXWORKERTHREADS)?MAXWORKERTHREADS:numthreads); +#ifdef MULTITHREAD + for (; i < numthreads; i++) + { + t = &swthreads[i]; + t->threadnum = i; + t->readpoint = wq.pos; + t->thread = Sys_CreateThread(WT_Main, t, THREADP_NORMAL, 0); + if (!t->thread) + break; + } +#endif + wq.numthreads = i; +} +void SWRast_TerminateThreadPool(void) +{ + int i; + wqcom_t *com = SWRast_BeginCommand(WTC_DIE, sizeof(com->com)); + SWRast_EndCommand(com); +#ifdef MULTITHREAD + for (i = 0; i < wq.numthreads; i++) + { + Sys_WaitOnThread(swthreads[i].thread); + } +#endif + wq.numthreads = 0; +} + + + + + + + + + + + + + +void SW_Draw_Init(void) +{ + R2D_Init(); + + R_InitFlashblends(); +} +void SW_Draw_Shutdown(void) +{ +} +void SW_R_Init(void) +{ + SWRast_CreateThreadPool(sw_threads.ival); + sw_threads.modified = false; +} +void SW_R_DeInit(void) +{ + SWRast_TerminateThreadPool(); +} +void SW_R_RenderView(void) +{ + extern cvar_t gl_screenangle; + extern cvar_t gl_mindist; + vec3_t newa; + int tmpvisents = cl_numvisedicts; /*world rendering is allowed to add additional ents, but we don't want to keep them for recursive views*/ + if (!cl.worldmodel || (!cl.worldmodel->nodes && cl.worldmodel->type != mod_heightmap)) + r_refdef.flags |= Q2RDF_NOWORLDMODEL; + +// R_SetupGL (); + + AngleVectors (r_refdef.viewangles, vpn, vright, vup); + VectorCopy (r_refdef.vieworg, r_origin); + if (r_refdef.useperspective) + Matrix4x4_CM_Projection_Inf(r_refdef.m_projection, r_refdef.fov_x, r_refdef.fov_y, gl_mindist.value); + else + { + if (gl_maxdist.value>=1) + Matrix4x4_CM_Orthographic(r_refdef.m_projection, -r_refdef.fov_x/2, r_refdef.fov_x/2, -r_refdef.fov_y/2, r_refdef.fov_y/2, -gl_maxdist.value, gl_maxdist.value); + else + Matrix4x4_CM_Orthographic(r_refdef.m_projection, 0, r_refdef.vrect.width, 0, r_refdef.vrect.height, -9999, 9999); + } + VectorCopy(r_refdef.viewangles, newa); + newa[0] = r_refdef.viewangles[0]; + newa[1] = r_refdef.viewangles[1]; + newa[2] = r_refdef.viewangles[2] + gl_screenangle.value; + Matrix4x4_CM_ModelViewMatrix(r_refdef.m_view, newa, r_refdef.vieworg); + + R_SetFrustum (r_refdef.m_projection, r_refdef.m_view); + + RQ_BeginFrame(); + + if (!(r_refdef.flags & Q2RDF_NOWORLDMODEL)) + { + Surf_DrawWorld (); // adds static entities to the list + } + else + SWBE_DrawWorld(NULL); + + S_ExtraUpdate (); // don't let sound get messed up if going slow + +// R_DrawDecals(); + + R_RenderDlights (); + + RQ_RenderBatchClear(); + + cl_numvisedicts = tmpvisents; +} +void SW_R_NewMap(void) +{ + char namebuf[MAX_QPATH]; + extern cvar_t host_mapname, r_shadow_realtime_dlight, r_shadow_realtime_world; + int i; + + for (i=0 ; i<256 ; i++) + d_lightstylevalue[i] = 264; // normal light value + + memset (&r_worldentity, 0, sizeof(r_worldentity)); + AngleVectors(r_worldentity.angles, r_worldentity.axis[0], r_worldentity.axis[1], r_worldentity.axis[2]); + VectorInverse(r_worldentity.axis[1]); + r_worldentity.model = cl.worldmodel; + Vector4Set(r_worldentity.shaderRGBAf, 1, 1, 1, 1); + + + COM_StripExtension(COM_SkipPath(cl.worldmodel->name), namebuf, sizeof(namebuf)); + Cvar_Set(&host_mapname, namebuf); + + Surf_DeInit(); + + r_viewleaf = NULL; + r_viewcluster = -1; + r_oldviewcluster = 0; + r_viewcluster2 = -1; + + Mod_ParseInfoFromEntityLump(cl.worldmodel, cl.worldmodel->entities, cl.worldmodel->name); + + P_ClearParticles (); + Surf_WipeStains(); + CL_RegisterParticles(); + Surf_BuildLightmaps (); + + +#ifdef VM_UI + UI_Reset(); +#endif + + TP_NewMap(); + R_SetSky(cl.skyname); + +#ifdef MAP_PROC + if (cl.worldmodel->fromgame == fg_doom3) + D3_GenerateAreas(cl.worldmodel); +#endif + +#ifdef RTLIGHTS + if (r_shadow_realtime_dlight.ival || r_shadow_realtime_world.ival) + { + R_LoadRTLights(); + if (rtlights_first == rtlights_max) + R_ImportRTLights(cl.worldmodel->entities); + } + Sh_PreGenerateLights(); +#endif +} +void SW_R_PreNewMap(void) +{ +} +void SW_SCR_UpdateScreen(void) +{ + wqcom_t *com; + + extern cvar_t gl_screenangle; + float w = vid.width, h = vid.height; + + r_refdef.time = realtime; + + SWBE_Set2D(); + + SWRast_Sync(); + SW_VID_SwapBuffers(); + if (sw_threads.modified) + { + SWRast_TerminateThreadPool(); + SWRast_CreateThreadPool(sw_threads.ival); + sw_threads.modified = false; + } + + com = SWRast_BeginCommand(WTC_VIEWPORT, sizeof(com->viewport)); + com->viewport.interlace = bound(0, sw_interlace.ival, 15)+1; + com->viewport.clearcolour = r_clear.ival; + com->viewport.cleardepth = true; + SW_VID_UpdateViewport(com); + SWRast_EndCommand(com); + + Shader_DoReload(); + + //FIXME: playfilm/editor+q3ui + if (vid.recalc_refdef) + SCR_CalcRefdef (); + SCR_SetUpToDrawConsole (); + + if (cls.state == ca_active) + { + if (!CSQC_DrawView()) + V_RenderView (); + + R2D_PolyBlend (); + R2D_BrightenScreen(); + } + + SCR_DrawTwoDimensional(0, 0); + + V_UpdatePalette (false); +} + +rendererinfo_t swrendererinfo = +{ + "Software Renderer", + { + "sw", + "Software", + "SoftRast", + }, + QR_SOFTWARE, + + SW_Draw_Init, + SW_Draw_Shutdown, + + SW_LoadTexture, + SW_LoadTexture8Pal24, + SW_LoadTexture8Pal32, + SW_LoadCompressed, + SW_FindTexture, + SW_AllocNewTexture, + SW_Upload, + SW_DestroyTexture, + + SW_R_Init, + SW_R_DeInit, + SW_R_RenderView, + + SW_R_NewMap, + SW_R_PreNewMap, + + Surf_AddStain, + Surf_LessenStains, + + RMod_Init, + RMod_Shutdown, + RMod_ClearAll, + RMod_ForName, + RMod_FindName, + RMod_Extradata, + RMod_TouchModel, + + RMod_NowLoadExternal, + RMod_Think, + Mod_GetTag, + Mod_TagNumForName, + Mod_SkinNumForName, + Mod_FrameNumForName, + Mod_FrameDuration, + + + SW_VID_Init, + SW_VID_DeInit, + SW_VID_SetPalette, + SW_VID_ShiftPalette, + SW_VID_GetRGBInfo, + SW_VID_SetWindowCaption, + + SW_SCR_UpdateScreen, + + SWBE_SelectMode, + SWBE_DrawMesh_List, + SWBE_DrawMesh_Single, + SWBE_SubmitBatch, + SWBE_GetTempBatch, + SWBE_DrawWorld, + SWBE_Init, + SWBE_GenBrushModelVBO, + SWBE_ClearVBO, + SWBE_UploadAllLightmaps, + SWBE_SelectEntity, + SWBE_SelectDLight, + SWBE_LightCullModel, + + "no more" +}; +#endif diff --git a/engine/sw/sw_spans.h b/engine/sw/sw_spans.h new file mode 100644 index 00000000..94a8baad --- /dev/null +++ b/engine/sw/sw_spans.h @@ -0,0 +1,323 @@ +/* +this file is expected to be #included as the body of a real function +to define create a new pixel shader, define PLOT_PIXEL(outval) at the top of your function and you're good to go + +//modifiers: +SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' + affine... no perspective correction. + + +*/ + +{ + swvert_t *vt; + int y; + int secondhalf; + int xl,xld, xr,xrd; +#ifdef SPAN_ST + float sl,sld, sd; + float tl,tld, td; +#endif +#ifdef SPAN_Z + unsigned int zl,zld, zd; +#endif + unsigned int *restrict outbuf; + unsigned int *restrict ti; + int i; + const swvert_t *vlt,*vlb,*vrt,*vrb; + int spanlen; + int numspans; + unsigned int *vplout; + int dx, dy; + int recalcside; + int interlace; + + float fdx1,fdy1,fdx2,fdy2,fz,d1,d2; + + if (!img) + return; + + /*we basically render a diamond + that is, the single triangle is split into two triangles, outwards towards the midpoint and inwards to the final position. + */ + + /*reorder the verticies for height*/ + if (v1->vcoord[1] > v2->vcoord[1]) + { + vt = v1; + v1 = v2; + v2 = vt; + } + if (v1->vcoord[1] > v3->vcoord[1]) + { + vt = v1; + v1 = v3; + v3 = vt; + } + if (v2->vcoord[1] > v3->vcoord[1]) + { + vt = v3; + v3 = v2; + v2 = vt; + } + + { + const swvert_t *v[3]; + + v[0] = v1; + v[1] = v2; + v[2] = v3; + + //reject triangles with any point offscreen, for now + for (i = 0; i < 3; i++) + { + if (v[i]->vcoord[0] < 0 || v[i]->vcoord[0] >= th->vpwidth) + return; + if (v[i]->vcoord[1] < 0 || v[i]->vcoord[1] >= th->vpheight) + return; + if (v[i]->vcoord[2] < 0) + return; + } + + for (i = 0; i < 2; i++) + { + if (v[i]->vcoord[1] > v[i+1]->vcoord[1]) + return; + } + } + + fdx1 = v2->vcoord[0] - v1->vcoord[0]; + fdy1 = v2->vcoord[1] - v1->vcoord[1]; + + fdx2 = v3->vcoord[0] - v1->vcoord[0]; + fdy2 = v3->vcoord[1] - v1->vcoord[1]; + + fz = fdx1*fdy2 - fdx2*fdy1; + + if (fz == 0) + { + //weird angle... + return; + } + + fz = 1.0 / fz; + fdx1 *= fz; + fdy1 *= fz; + fdx2 *= fz; + fdy2 *= fz; + +#ifdef SPAN_ST //affine + d1 = v2->tccoord[0] - v1->tccoord[0]; + d2 = v3->tccoord[0] - v1->tccoord[0]; + sld = fdx1*d2 - fdx2*d1; + sd = fdy2*d1 - fdy1*d2; + + d1 = v2->tccoord[1] - v1->tccoord[1]; + d2 = v3->tccoord[1] - v1->tccoord[1]; + tld = fdx1*d2 - fdx2*d1; + td = fdy2*d1 - fdy1*d2; +#endif +#ifdef SPAN_Z + d1 = (v2->vcoord[2] - v1->vcoord[2])*UINT_MAX; + d2 = (v3->vcoord[2] - v1->vcoord[2])*UINT_MAX; + zld = fdx1*d2 - fdx2*d1; + zd = fdy2*d1 - fdy1*d2; +#endif + + ti = img->data; + + y = v1->vcoord[1]; + + for (secondhalf = 0; secondhalf <= 1; secondhalf++) + { + if (secondhalf) + { + if (numspans < 0) + { + interlace = -numspans; + y+=interlace; + numspans-=interlace; + + xl += xld*interlace; + xr += xrd*interlace; + vplout += th->vpcstride*interlace; + +#ifdef SPAN_ST + sl += sld*interlace; + tl += tld*interlace; +#endif +#ifdef SPAN_Z + zl += zld*interlace; +#endif + } + + /*v2->v3*/ + if (fz <= 0) + { + vlt = v2; + //vrt == v1; + vlb = v3; + //vrb == v3; + + recalcside = 1; + +#ifdef SPAN_ST + sld -= sd*xld/(float)(1<<16); + tld -= td*xld/(float)(1<<16); +#endif +#ifdef SPAN_Z + zld -= zd*xld/(float)(1<<16); +#endif + } + else + { + //vlt == v1; + vrt = v2; + ///vlb == v3; + vrb = v3; + + recalcside = 2; + } + + //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) + numspans = v3->vcoord[1] - y; + } + else + { + vlt = v1; + vrt = v1; + /*v1->v2*/ + if (fz < 0) + { + vlb = v2; + vrb = v3; + } + else + { + vlb = v3; + vrb = v2; + } + recalcside = 3; + + //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) + numspans = v2->vcoord[1] - y; + } + + if (recalcside & 1) + { + dx = (vlb->vcoord[0] - vlt->vcoord[0]); + dy = (vlb->vcoord[1] - vlt->vcoord[1]); + if (dy > 0) + xld = (dx<<16) / dy; + else + xld = 0; + xl = (int)vlt->vcoord[0]<<16; + +#ifdef SPAN_ST + sl = vlt->tccoord[0]; + sld = sld + sd*xld/(float)(1<<16); + tl = vlt->tccoord[1]; + tld = tld + td*xld/(float)(1<<16); +#endif +#ifdef SPAN_Z + zl = vlt->vcoord[2]*UINT_MAX; + zld = zld + zd*xld/(float)(1<<16); +#endif + } + + if (recalcside & 2) + { + dx = (vrb->vcoord[0] - vrt->vcoord[0]); + dy = (vrb->vcoord[1] - vrt->vcoord[1]); + if (dy) + xrd = (dx<<16) / dy; + else + xrd = 0; + xr = (int)vrt->vcoord[0]<<16; + } + + + + if (y + numspans >= th->vpheight) + numspans = th->vpheight - y - 1; + + if (numspans <= 0) + continue; + + + vplout = th->vpcbuf + y * th->vpcstride; //this is a pointer to the left of the viewport buffer. + + interlace = ((y + th->interlaceline) % th->interlacemod); + if (interlace) + { + if (interlace > numspans) + { + interlace = numspans; + y+=interlace; + } + else + { + y+=interlace; + numspans-=interlace; + } + xl += xld*interlace; + xr += xrd*interlace; + vplout += th->vpcstride*interlace; + +#ifdef SPAN_ST + sl += sld*interlace; + tl += tld*interlace; +#endif +#ifdef SPAN_Z + zl += zld*interlace; +#endif + } + + for (; numspans > 0; + numspans -= th->interlacemod + ,xl += xld*th->interlacemod + ,xr += xrd*th->interlacemod + ,vplout += th->vpcstride*th->interlacemod + ,y += th->interlacemod + +#ifdef SPAN_ST + ,sl += sld*th->interlacemod + ,tl += tld*th->interlacemod +#endif +#ifdef SPAN_Z + ,zl += zld*th->interlacemod +#endif + ) + { +#ifdef SPAN_ST + float s = sl; + float t = tl; +#endif +#ifdef SPAN_Z + unsigned int z = zl; + unsigned int *restrict zb = th->vpdbuf + y * th->vpwidth + (xl>>16); +#endif + + spanlen = (xr - xl)>>16; + outbuf = vplout + (xl>>16); + + while(spanlen-->=0) + { + PLOT_PIXEL(*outbuf); + outbuf++; + +#ifdef SPAN_ST + s += sd; + t += td; +#endif +#ifdef SPAN_Z + z += zd; + zb++; +#endif + } + } + } +} + +#undef SPAN_ST +#undef PLOT_PIXEL diff --git a/engine/sw/sw_vidwin.c b/engine/sw/sw_vidwin.c new file mode 100644 index 00000000..b6c2717d --- /dev/null +++ b/engine/sw/sw_vidwin.c @@ -0,0 +1,761 @@ +#include "quakedef.h" +#ifdef SWQUAKE +#include "sw.h" + +#include "winquake.h" +/*Fixup outdated windows headers*/ +#ifndef WM_XBUTTONDOWN + #define WM_XBUTTONDOWN 0x020B + #define WM_XBUTTONUP 0x020C +#endif +#ifndef MK_XBUTTON1 + #define MK_XBUTTON1 0x0020 +#endif +#ifndef MK_XBUTTON2 + #define MK_XBUTTON2 0x0040 +#endif +// copied from DarkPlaces in an attempt to grab more buttons +#ifndef MK_XBUTTON3 + #define MK_XBUTTON3 0x0080 +#endif +#ifndef MK_XBUTTON4 + #define MK_XBUTTON4 0x0100 +#endif +#ifndef MK_XBUTTON5 + #define MK_XBUTTON5 0x0200 +#endif +#ifndef MK_XBUTTON6 + #define MK_XBUTTON6 0x0400 +#endif +#ifndef MK_XBUTTON7 + #define MK_XBUTTON7 0x0800 +#endif +#ifndef WM_INPUT + #define WM_INPUT 255 +#endif + + + +typedef struct dibinfo +{ + BITMAPINFOHEADER header; + RGBQUAD acolors[256]; +} dibinfo_t; + +//struct +//{ + HBITMAP hDIBSection; + qbyte *pDIBBase; + + HDC mainhDC; + HDC hdcDIBSection; + HGDIOBJ previously_selected_GDI_obj; + + int framenumber; + void *screenbuffer; + qintptr_t screenpitch; + int window_x, window_y; + unsigned int *depthbuffer; +struct +{ + qboolean isfullscreen; +} w32sw; +HWND mainwindow; + +void DIB_Shutdown(void) +{ + if (depthbuffer) + { + BZ_Free(depthbuffer); + depthbuffer = NULL; + } + + if (hdcDIBSection) + { + SelectObject(hdcDIBSection, previously_selected_GDI_obj); + DeleteDC(hdcDIBSection); + hdcDIBSection = NULL; + } + + if (hDIBSection) + { + DeleteObject(hDIBSection); + hDIBSection = NULL; + pDIBBase = NULL; + } + + if (mainhDC) + { + ReleaseDC(mainwindow, mainhDC); + mainhDC = 0; + } +} +qboolean DIB_Init(void) +{ + dibinfo_t dibheader; + BITMAPINFO *pbmiDIB = ( BITMAPINFO * ) &dibheader; + int i; + + memset( &dibheader, 0, sizeof( dibheader ) ); + + /* + ** grab a DC + */ + if ( !mainhDC ) + { + if ( ( mainhDC = GetDC( mainwindow ) ) == NULL ) + return false; + } + + /* + ** fill in the BITMAPINFO struct + */ + pbmiDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmiDIB->bmiHeader.biWidth = vid.pixelwidth; + pbmiDIB->bmiHeader.biHeight = vid.pixelheight; + pbmiDIB->bmiHeader.biPlanes = 1; + pbmiDIB->bmiHeader.biBitCount = 32; + pbmiDIB->bmiHeader.biCompression = BI_RGB; + pbmiDIB->bmiHeader.biSizeImage = 0; + pbmiDIB->bmiHeader.biXPelsPerMeter = 0; + pbmiDIB->bmiHeader.biYPelsPerMeter = 0; + pbmiDIB->bmiHeader.biClrUsed = 0; + pbmiDIB->bmiHeader.biClrImportant = 0; + + /* + ** fill in the palette + */ + for ( i = 0; i < 256; i++ ) + { + dibheader.acolors[i].rgbRed = ( d_8to24rgbtable[i] >> 0 ) & 0xff; + dibheader.acolors[i].rgbGreen = ( d_8to24rgbtable[i] >> 8 ) & 0xff; + dibheader.acolors[i].rgbBlue = ( d_8to24rgbtable[i] >> 16 ) & 0xff; + } + + /* + ** create the DIB section + */ + hDIBSection = CreateDIBSection( mainhDC, + pbmiDIB, + DIB_RGB_COLORS, + (void**)&pDIBBase, + NULL, + 0 ); + + if ( hDIBSection == NULL ) + { + Con_Printf( "DIB_Init() - CreateDIBSection failed\n" ); + goto fail; + } + + if (pbmiDIB->bmiHeader.biHeight < 0) + { + // bottom up + screenbuffer = pDIBBase + ( vid.pixelheight - 1 ) * vid.pixelwidth * 4; + screenpitch = -(int)vid.pixelwidth; + } + else + { + // top down + screenbuffer = pDIBBase; + screenpitch = vid.pixelwidth; + } + + /* + ** clear the DIB memory buffer + */ + memset( pDIBBase, 0, vid.pixelwidth * vid.pixelheight * 4); + + if ( ( hdcDIBSection = CreateCompatibleDC( mainhDC ) ) == NULL ) + { + Con_Printf( "DIB_Init() - CreateCompatibleDC failed\n" ); + goto fail; + } + if ( ( previously_selected_GDI_obj = SelectObject( hdcDIBSection, hDIBSection ) ) == NULL ) + { + Con_Printf( "DIB_Init() - SelectObject failed\n" ); + goto fail; + } + + depthbuffer = BZ_Malloc(vid.pixelwidth * vid.pixelheight * sizeof(*depthbuffer)); + if (depthbuffer) + return true; + +fail: + DIB_Shutdown(); + return false; +} +void DIB_SwapBuffers(void) +{ +// extern float usingstretch; + +// if (usingstretch == 1) + BitBlt( mainhDC, + 0, 0, + vid.pixelwidth, + vid.pixelheight, + hdcDIBSection, + 0, 0, + SRCCOPY ); +/* else + StretchBlt( mainhDC, //Why is StretchBlt not optimised for a scale of 2? Surly that would be a frequently used quantity? + 0, 0, + vid.width*usingstretch, + vid.height*usingstretch, + hdcDIBSection, + 0, 0, + vid.width, vid.height, + SRCCOPY ); +*/ +} + +extern int window_width; +extern int window_height; +void SWV_UpdateWindowStatus(void) +{ + POINT p; + RECT nr; + RECT WindowRect; + GetClientRect(mainwindow, &nr); + + //if its bad then we're probably minimised + if (nr.right <= nr.left) + return; + if (nr.bottom <= nr.top) + return; + + WindowRect = nr; + p.x = 0; + p.y = 0; + ClientToScreen(mainwindow, &p); + window_x = p.x; + window_y = p.y; + window_width = WindowRect.right - WindowRect.left; + window_height = WindowRect.bottom - WindowRect.top; + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +qboolean SWAppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + static BOOL sound_active; + + if (ActiveApp == fActive && Minimized == minimize) + return false; //so windows doesn't crash us over and over again. + + ActiveApp = fActive; + Minimized = minimize; + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + sound_active = true; + } + + IN_UpdateGrabs(false, ActiveApp); + +/* + if (fActive) + { + if (modestate != MS_WINDOWED) + { + if (vid_canalttab && vid_wassuspended) { + vid_wassuspended = false; + ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN); + ShowWindow(mainwindow, SW_SHOWNORMAL); + } + } + } + if (!fActive) + { + if (modestate != MS_WINDOWED) + { + if (vid_canalttab) { + ChangeDisplaySettings (NULL, 0); + vid_wassuspended = true; + } + } + } +*/ + return true; +} + +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 0; + int fActive, fMinimized, temp; + HDC hdc; + PAINTSTRUCT ps; + extern unsigned int uiWheelMessage; +// static int recursiveflag; + + if ( uMsg == uiWheelMessage ) { + uMsg = WM_MOUSEWHEEL; + wParam <<= 16; + } + + switch (uMsg) + { + case WM_CREATE: + break; + + case WM_SYSCOMMAND: + + // Check for maximize being hit + switch (wParam & ~0x0F) + { + case SC_MAXIMIZE: + Cbuf_AddText("vid_fullscreen 1;vid_restart\n", RESTRICT_LOCAL); + // if minimized, bring up as a window before going fullscreen, + // so MGL will have the right state to restore +/* if (Minimized) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + + VID_SetMode ((int)vid_fullscreen_mode.value, vid_curpal); +*/ break; + + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (w32sw.isfullscreen) + { + // don't call DefWindowProc() because we don't want to start + // the screen saver fullscreen + break; + } + + // fall through windowed and allow the screen saver to start + + default: +// if (!vid_initializing) +// { +// S_BlockSound (); +// } + + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + +// if (!vid_initializing) +// { +// S_UnblockSound (); +// } + } + break; + + case WM_MOVE: + SWV_UpdateWindowStatus (); + +// if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) +// VID_RememberWindowPos (); + + break; + + case WM_SIZE: + SWV_UpdateWindowStatus (); +/* Minimized = false; + + if (!(wParam & SIZE_RESTORED)) + { + if (wParam & SIZE_MINIMIZED) + Minimized = true; + } + + if (!Minimized && !w32sw.isfullscreen) + { + int nt, nl; + int nw, nh; + qboolean move = false; + RECT r; + GetClientRect (hWnd, &r); + nw = (int)(r.right - r.left)&~3; + nh = (int)(r.bottom - r.top)&~3; + + window_width = nw; + window_height = nh; + VID2_UpdateWindowStatus(); + + if (nw < 320) + { + move = true; + nw = 320; + } + if (nh < 200) + { + move = true; + nh = 200; + } + if (nh > MAXHEIGHT) + { + move = true; + nh = MAXHEIGHT; + } + if ((r.right - r.left) & 3) + move = true; + if ((r.bottom - r.top) & 3) + move = true; + + GetWindowRect (hWnd, &r); + nl = r.left; + nt = r.top; + r.left =0; + r.top = 0; + r.right = nw*usingstretch; + r.bottom = nh*usingstretch; + AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW, FALSE, 0); + vid.recalc_refdef = true; + if (move) + MoveWindow(hWnd, nl, nt, r.right - r.left, r.bottom - r.top, true); + else + { + if (vid.width != nw || vid.height != nh) + { + M_RemoveAllMenus(); //can cause probs + DIB_Shutdown(); + vid.conwidth = vid.width = nw;//vid_stretch.value; + vid.conheight = vid.height = nh;///vid_stretch.value; + + DIB_Init( &vid.buffer, &vid.rowbytes ); + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + + if (VID_AllocBuffers(vid.width, vid.height, r_pixbytes)) + D_InitCaches (vid_surfcache, vid_surfcachesize); + + SCR_UpdateWholeScreen(); + } + else + SCR_UpdateWholeScreen(); + } + + } +*/ + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + SWAppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away +// ClearAllStates (); + + break; + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + +// if (!in_mode_set && host_initialized) +// SCR_UpdateWholeScreen (); + + EndPaint(hWnd, &ps); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: +// if (!vid_initializing) + IN_TranslateKeyEvent(wParam, lParam, true, 0); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: +// if (!vid_initializing) + IN_TranslateKeyEvent(wParam, lParam, false, 0); + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: +// if (!vid_initializing) + { + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + // extra buttons + if (wParam & MK_XBUTTON1) + temp |= 8; + + if (wParam & MK_XBUTTON2) + temp |= 16; + + if (wParam & MK_XBUTTON3) + temp |= 32; + + if (wParam & MK_XBUTTON4) + temp |= 64; + + if (wParam & MK_XBUTTON5) + temp |= 128; + + if (wParam & MK_XBUTTON6) + temp |= 256; + + if (wParam & MK_XBUTTON7) + temp |= 512; + + IN_MouseEvent (temp); + } + break; + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + { + if ((short) HIWORD(wParam) > 0) + { + Key_Event(0, K_MWHEELUP, 0, true); + Key_Event(0, K_MWHEELUP, 0, false); + } + else + { + Key_Event(0, K_MWHEELDOWN, 0, true); + Key_Event(0, K_MWHEELDOWN, 0, false); + } + } + break; + case WM_INPUT: + // raw input handling + IN_RawInput_Read((HANDLE)lParam); + break; +/* case WM_DISPLAYCHANGE: + if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + break; +*/ + case WM_CLOSE: + // this causes Close in the right-click task bar menu not to work, but right + // now bad things happen if Close is handled in that case (garbage and a + // crash on Win95) +// if (!vid_initializing) + { + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Cbuf_AddText("\nquit\n", RESTRICT_LOCAL); + } + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 0 if handled message, 1 if not */ + return lRet; +} + + + +/* +** VID_CreateWindow +*/ +#define WINDOW_CLASS_NAME FULLENGINENAME +#define WINDOW_TITLE_NAME FULLENGINENAME + + +void VID_CreateWindow(int width, int height, qboolean fullscreen) +{ + WNDCLASS wc; + RECT r; + int x, y, w, h; + int exstyle; + int stylebits; + + + if (fullscreen) + { + exstyle = WS_EX_TOPMOST; + stylebits = WS_POPUP; + } + else + { + exstyle = 0; + stylebits = WS_OVERLAPPEDWINDOW; + } + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = (void *)COLOR_GRAYTEXT; + wc.lpszMenuName = 0; + wc.lpszClassName = WINDOW_CLASS_NAME; + + RegisterClass(&wc); + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + + AdjustWindowRectEx (&r, stylebits, FALSE, exstyle); + + window_rect = r; + + w = r.right - r.left; + h = r.bottom - r.top; + x = 0;//vid_xpos.value; + y = 0;//vid_ypos.value; + + mainwindow = CreateWindowEx( + exstyle, + WINDOW_CLASS_NAME, + WINDOW_TITLE_NAME, + stylebits, + x, y, w, h, + NULL, + NULL, + global_hInstance, + NULL); + + if (!mainwindow) + Sys_Error("Couldn't create window"); + + ShowWindow(mainwindow, SW_SHOWNORMAL); + UpdateWindow(mainwindow); + SetForegroundWindow(mainwindow); + SetFocus(mainwindow); + + SWV_UpdateWindowStatus(); +} + + + + + + + + +void SW_VID_UpdateViewport(wqcom_t *com) +{ + com->viewport.cbuf = screenbuffer; + com->viewport.dbuf = depthbuffer; + com->viewport.width = vid.pixelwidth; + com->viewport.height = vid.pixelheight; + com->viewport.stride = screenpitch; + com->viewport.framenum = framenumber; + +/* com->viewport.buf = pDIBBase; + com->viewport.width = vid.pixelwidth; + com->viewport.height = vid.pixelheight; + com->viewport.stride = vid.pixelwidth; + com->viewport.framenum = framenumber;*/ +} + +void SW_VID_SwapBuffers(void) +{ + extern cvar_t vid_conautoscale; + DIB_SwapBuffers(); + framenumber++; + + IN_UpdateGrabs(false, ActiveApp); + +// memset( pDIBBase, 0, vid.pixelwidth * vid.pixelheight * 4); + + + if (window_width != vid.pixelwidth || window_height != vid.pixelheight) + { + DIB_Shutdown(); + vid.pixelwidth = window_width; + vid.pixelheight = window_height; + if (!DIB_Init()) + Sys_Error("resize reinitialization error"); + + Cvar_ForceCallback(&vid_conautoscale); + } +} + +qboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette) +{ + vid.pixelwidth = info->width; + vid.pixelheight = info->height; + + VID_CreateWindow(vid.pixelwidth, vid.pixelheight, false); + + if (!DIB_Init()) + return false; + + return true; +} +void SW_VID_DeInit(void) +{ + DIB_Shutdown(); + DestroyWindow(mainwindow); +} +void SW_VID_SetPalette(unsigned char *palette) +{ +} +void SW_VID_ShiftPalette(unsigned char *palette) +{ +} +char *SW_VID_GetRGBInfo(int prepad, int *truevidwidth, int *truevidheight) +{ + return NULL; +} +void SW_VID_SetWindowCaption(char *msg) +{ +} +#endif