From 7f6c2054d968354a1361196979edd175b4c40064 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 5 Oct 2014 20:04:11 +0000 Subject: [PATCH] threaded loading code and associated/extensive tweaks. unified image loading code a little between renderers. support switching worldmodel in csqc. also associated bugfixes. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4758 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/Makefile | 9 +- engine/client/cl_cam.c | 4 +- engine/client/cl_cg.c | 59 +- engine/client/cl_demo.c | 6 +- engine/client/cl_ents.c | 99 +- engine/client/cl_input.c | 9 +- engine/client/cl_main.c | 43 +- engine/client/cl_parse.c | 116 +- engine/client/cl_plugin.inc | 17 +- engine/client/cl_pred.c | 6 +- engine/client/cl_screen.c | 69 +- engine/client/cl_tent.c | 60 +- engine/client/cl_ui.c | 57 +- engine/client/client.h | 8 +- engine/client/clq2_ents.c | 31 +- engine/client/clq3_parse.c | 3 +- engine/client/console.c | 40 +- engine/client/fragstats.c | 4 +- engine/client/image.c | 1760 ++++++++++++++---- engine/client/in_win.c | 5 +- engine/client/m_download.c | 5 +- engine/client/m_items.c | 112 +- engine/client/m_mp3.c | 7 +- engine/client/m_multi.c | 26 +- engine/client/m_options.c | 89 +- engine/client/m_single.c | 320 ++-- engine/client/menu.c | 65 +- engine/client/merged.h | 172 +- engine/client/p_classic.c | 35 +- engine/client/p_null.c | 2 +- engine/client/p_script.c | 135 +- engine/client/pr_clcmd.c | 2 +- engine/client/pr_csqc.c | 519 +++++- engine/client/pr_menu.c | 29 +- engine/client/quakedef.h | 32 + engine/client/r_2d.c | 75 +- engine/client/r_part.c | 176 +- engine/client/r_partset.c | 41 +- engine/client/r_partset.h | 23 +- engine/client/r_surf.c | 136 +- engine/client/render.h | 109 +- engine/client/renderer.c | 165 +- engine/client/resource.h | 1 + engine/client/roq.h | 2 +- engine/client/roq_read.c | 5 +- engine/client/sbar.c | 203 ++- engine/client/skin.c | 99 +- engine/client/snd_dma.c | 45 +- engine/client/snd_mem.c | 64 +- engine/client/snd_mix.c | 2 +- engine/client/snd_ov.c | 2 +- engine/client/sound.h | 8 +- engine/client/sys_plugfte.c | 3 + engine/client/sys_win.c | 10 +- engine/client/textedit.c | 3 + engine/client/valid.c | 5 +- engine/client/vid.h | 2 + engine/client/vid_headless.c | 63 +- engine/client/view.c | 7 +- engine/client/wad.c | 41 +- engine/client/winquake.h | 3 + engine/client/winquake.rc | 12 +- engine/client/zqtp.c | 2 +- engine/common/bothdefs.h | 39 +- engine/common/cmd.c | 79 +- engine/common/com_mesh.c | 1052 +++++------ engine/common/com_mesh.h | 25 +- engine/common/common.c | 632 ++++++- engine/common/common.h | 46 +- engine/common/console.h | 4 +- engine/common/fs.c | 355 ++-- engine/common/fs.h | 10 + engine/common/fs_pak.c | 40 +- engine/common/fs_stdio.c | 2 +- engine/common/fs_zip.c | 53 +- engine/common/gl_q2bsp.c | 1076 ++++++----- engine/common/net_wins.c | 2 +- engine/common/particles.h | 4 +- engine/common/plugin.c | 16 +- engine/common/pmovetst.c | 4 +- engine/common/pr_bgcmd.c | 39 +- engine/common/pr_common.h | 23 +- engine/common/protocol.h | 43 +- engine/common/q3common.c | 5 +- engine/common/sys.h | 15 +- engine/common/sys_linux_threads.c | 391 ++-- engine/common/sys_win_threads.c | 280 +-- engine/common/translate.c | 4 +- engine/common/world.h | 11 + engine/common/zone.c | 5 + engine/d3d/d3d11_backend.c | 124 +- engine/d3d/d3d11_image.c | 742 ++------ engine/d3d/d3d_backend.c | 125 +- engine/d3d/d3d_image.c | 523 +----- engine/d3d/vid_d3d.c | 50 +- engine/d3d/vid_d3d11.c | 69 +- engine/dotnet2005/ftequake.sln | 15 +- engine/dotnet2005/ftequake.vcproj | 2 +- engine/gl/gl_alias.c | 626 ++++--- engine/gl/gl_backend.c | 127 +- engine/gl/gl_bloom.c | 3 +- engine/gl/gl_draw.c | 2462 ++------------------------ engine/gl/gl_font.c | 49 +- engine/gl/gl_heightmap.c | 705 ++++---- engine/gl/gl_hlmdl.c | 1 - engine/gl/gl_model.c | 1091 +++++++----- engine/gl/gl_model.h | 65 +- engine/gl/gl_ngraph.c | 6 +- engine/gl/gl_rlight.c | 96 +- engine/gl/gl_rmain.c | 38 +- engine/gl/gl_rmisc.c | 89 +- engine/gl/gl_rsurf.c | 6 +- engine/gl/gl_shader.c | 358 ++-- engine/gl/gl_shadow.c | 126 +- engine/gl/gl_vidcommon.c | 83 +- engine/gl/gl_vidlinuxglx.c | 12 +- engine/gl/gl_vidnt.c | 23 +- engine/gl/gl_vidrpi.c | 14 +- engine/gl/gl_vidwayland.c | 14 +- engine/gl/gl_warp.c | 24 +- engine/gl/glmod_doom.c | 3 - engine/gl/glquake.h | 39 +- engine/gl/r_bishaders.h | 27 +- engine/gl/shader.h | 39 +- engine/http/httpclient.c | 7 +- engine/partcfgs/generatebuiltin.c | 61 +- engine/partcfgs/h2part.cfg | 13 + engine/partcfgs/high.cfg | 2 + engine/qclib/initlib.c | 9 +- engine/qclib/pr_edict.c | 24 +- engine/qclib/pr_exec.c | 8 +- engine/qclib/pr_multi.c | 4 +- engine/qclib/progsint.h | 4 +- engine/qclib/progslib.h | 52 +- engine/qclib/qcc.h | 4 +- engine/qclib/qcc_cmdlib.c | 6 +- engine/qclib/qcc_pr_comp.c | 28 +- engine/qclib/qcc_pr_lex.c | 22 +- engine/qclib/qccmain.c | 6 +- engine/qclib/qcd.h | 4 +- engine/qclib/qcd_main.c | 6 +- engine/qclib/qcdecomp.c | 7 +- engine/server/net_preparse.c | 41 +- engine/server/pr_cmds.c | 370 ++-- engine/server/progdefs.h | 3 +- engine/server/savegame.c | 11 +- engine/server/server.h | 15 +- engine/server/sv_ccmds.c | 4 +- engine/server/sv_chat.c | 2 +- engine/server/sv_ents.c | 26 +- engine/server/sv_init.c | 69 +- engine/server/sv_main.c | 23 +- engine/server/sv_phys.c | 23 +- engine/server/sv_rankin.c | 2 +- engine/server/sv_send.c | 2 +- engine/server/sv_sys_unix.c | 1 + engine/server/sv_sys_win.c | 2 +- engine/server/sv_user.c | 46 +- engine/server/svhl_game.c | 2 +- engine/server/svmodel.c | 1944 -------------------- engine/server/svq2_game.c | 4 +- engine/server/svq3_game.c | 14 +- engine/server/world.c | 13 +- engine/shaders/glsl/defaultskin.glsl | 3 +- engine/shaders/glsl/rtlight.glsl | 32 +- engine/sw/sw.h | 17 +- engine/sw/sw_backend.c | 8 +- engine/sw/sw_image.c | 229 +-- engine/sw/sw_rast.c | 113 +- engine/web/fs_web.c | 2 +- plugins/plugin.c | 8 + plugins/plugin.h | 3 + 172 files changed, 9730 insertions(+), 10862 deletions(-) delete mode 100644 engine/server/svmodel.c diff --git a/engine/Makefile b/engine/Makefile index 9e40f60b..0ff2e11f 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -50,6 +50,11 @@ NATIVE_BASE_DIR?=$(BASE_DIR) NATIVE_RELEASE_DIR?=$(RELEASE_DIR) NATIVE_DEBUG_DIR?=$(DEBUG_DIR) +#include the appropriate games. +ifneq (,$(BRANDING)) + CFLAGS+=-DBRANDING_INC=../game_$(BRANDING).h + -include game_$(BRANDING).mak +endif #correct the gcc build when cross compiling ifneq (,$(findstring win32,$(FTE_TARGET))) @@ -1322,11 +1327,11 @@ endif # This is for linking the FTE icon to the MinGW target $(OUT_DIR)/resources.o : winquake.rc - @$(WINDRES) -I$(CLIENT_DIR) -O coff $< $@ + @$(WINDRES) $(CFLAGS) -I$(CLIENT_DIR) -O coff $< $@ #npAPI stuff requires some extra resources $(OUT_DIR)/npplug.o : ftequake/npplug.rc - @$(WINDRES) -I$(CLIENT_DIR) -O coff $< $@ + @$(WINDRES) $(CFLAGS) -I$(CLIENT_DIR) -O coff $< $@ #$(OUT_DIR)/%.d: %.c diff --git a/engine/client/cl_cam.c b/engine/client/cl_cam.c index 7aa67cfe..94c17ffe 100644 --- a/engine/client/cl_cam.c +++ b/engine/client/cl_cam.c @@ -340,7 +340,7 @@ static void Cam_CheckHighTarget(playerview_t *pv) void Cam_SelfTrack(playerview_t *pv) { vec3_t vec; - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) return; if (selfcam == 1) @@ -405,7 +405,7 @@ void Cam_Track(playerview_t *pv, usercmd_t *cmd) if (cl_hightrack.value && !pv->cam_locked) Cam_CheckHighTarget(pv); - if (!pv->cam_auto || cls.state != ca_active || cl.worldmodel || cl.worldmodel->needload) + if (!pv->cam_auto || cls.state != ca_active || cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) return; if (pv->cam_locked && (!cl.players[pv->cam_spec_track].name[0] || cl.players[pv->cam_spec_track].spectator)) diff --git a/engine/client/cl_cg.c b/engine/client/cl_cg.c index cb939297..4eba9e1a 100644 --- a/engine/client/cl_cg.c +++ b/engine/client/cl_cg.c @@ -586,7 +586,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con } else mod = cl.model_precache[modhandle+1]; - if (mod && !mod->needload) + if (mod && mod->loadstate == MLS_LOADED) pc = mod->funcs.NativeContents(mod, 0, 0, NULL, VM_POINTER(arg[0]), vec3_origin, vec3_origin); else pc = 1;//FTECONTENTS_SOLID; @@ -603,11 +603,16 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con float *angles = VM_POINTER(arg[3]); model_t *mod; if (modhandle >= MAX_PRECACHE_MODELS) - mod = &box_model; + { +// if (modhandle == MAX_PRECACHE_MODELS+1) +// mod = &capsule_model; +// else + mod = &box_model; + } else mod = cl.model_precache[modhandle+1]; - if (mod) + if (mod && mod->loadstate == MLS_LOADED) { vec3_t p_l; vec3_t axis[3]; @@ -650,7 +655,12 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con float *angles = VM_POINTER(arg[8]); model_t *mod; if (modhandle >= MAX_PRECACHE_MODELS) - mod = &box_model; + { +// if (modhandle == MAX_PRECACHE_MODELS+1) +// mod = &capsule_model; +// else + mod = &box_model; + } else mod = cl.model_precache[modhandle+1]; @@ -662,7 +672,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con origin = vec3_origin; if (!angles) angles = vec3_origin; - if (mod && !mod->needload) + if (mod && mod->loadstate == MLS_LOADED) #if !defined(CLIENTONLY) || defined(CSQC_DAT) World_TransformedTrace(mod, 0, 0, start, end, mins, maxs, fn==CG_CM_TRANSFORMEDCAPSULETRACE, &tr, origin, angles, brushmask); #else @@ -708,10 +718,25 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con int brushmask = VM_LONG(arg[6]); model_t *mod; if (modhandle >= MAX_PRECACHE_MODELS) - mod = &box_model; + { +// if (modhandle == MAX_PRECACHE_MODELS+1) +// mod = &capsule_model; +// else + mod = &box_model; + } else mod = cl.model_precache[modhandle+1]; + if (mod->loadstate != MLS_LOADED) + { + if (mod->loadstate == MLS_NOTLOADED) + Mod_LoadModel(mod, MLV_SILENT); + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (mod->loadstate != MLS_LOADED) + mod = &box_model; //stop crashes, even if this is wrong. + } + if (!mins) mins = vec3_origin; if (!maxs) @@ -737,14 +762,16 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con int i; char *mapname = VM_POINTER(arg[0]); strcpy(cl.model_name[1], mapname); - cl.worldmodel = cl.model_precache[1] = Mod_ForName(mapname, MLV_SILENT); - if (cl.worldmodel->needload) + cl.worldmodel = cl.model_precache[1] = Mod_ForName(Mod_FixName(mapname, mapname), MLV_SILENT); + if (cl.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); + if (cl.worldmodel->loadstate != MLS_LOADED) Host_EndGame("Couldn't load map %s", mapname); for (i=1 ; inumsubmodels ; i++) { strcpy(cl.model_name[1+i], va("*%i", i)); - cl.model_precache[i+1] = Mod_ForName (cl.model_name[i+1], MLV_SILENT); + cl.model_precache[i+1] = Mod_ForName (Mod_FixName(cl.model_name[i+1], mapname), MLV_SILENT); } } @@ -775,6 +802,9 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con model_t *mod = VM_FROMMHANDLE(arg[0]); if (mod) { + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + VectorCopy(mod->mins, ((float*)VM_POINTER(arg[1]))); VectorCopy(mod->maxs, ((float*)VM_POINTER(arg[2]))); } @@ -785,8 +815,13 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { char *name = VM_POINTER(arg[0]); model_t *mod; - mod = Mod_ForName(name, MLV_SILENT); - if (mod->needload || mod->type == mod_dummy) + mod = Mod_ForName(Mod_FixName(name, cl.model_name[1]), MLV_SILENT); + if (mod->loadstate == MLS_LOADING) + { //needed to ensure it really is missing + if (!COM_FCheckExists(mod->name)) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + } + if (mod->loadstate == MLS_FAILED || mod->type == mod_dummy) VM_LONG(ret) = 0; else VM_LONG(ret) = VM_TOMHANDLE(mod); @@ -1091,7 +1126,7 @@ static qintptr_t CG_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case CG_FTE_SPAWNPARTICLEEFFECT: return pe->RunParticleEffectState(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_FLOAT(arg[2]), VM_LONG(arg[3]), VM_POINTER(arg[4])); case CG_FTE_SPAWNPARTICLETRAIL: - return pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, VM_POINTER(arg[3])); + return pe->ParticleTrail(VM_POINTER(arg[0]), VM_POINTER(arg[1]), VM_LONG(arg[2]), 0, NULL, VM_POINTER(arg[3])); case CG_FTE_FREEPARTICLESTATE: pe->DelinkTrailstate(VM_POINTER(arg[0])); break; diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index cf925250..2deeab6c 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -497,6 +497,8 @@ qboolean CL_GetDemoMessage (void) } demoframe = host_framecount; } + if (cls.signon < 4) + demtime = 0; if (readdemobytes(&demopos, &msglength, 4) != 4) { return 0; @@ -956,7 +958,7 @@ void CL_RecordMap_f (void) { char demoname[MAX_QPATH]; char mapname[MAX_QPATH]; - char *demoext; + char demoext[8]; Q_strncpyz(demoname, Cmd_Argv(1), sizeof(demoname)); Q_strncpyz(mapname, Cmd_Argv(2), sizeof(mapname)); CL_Disconnect_f(); @@ -964,7 +966,7 @@ void CL_RecordMap_f (void) SV_SpawnServer (mapname, NULL, false, false); COM_DefaultExtension(demoname, ".mvd", sizeof(demoname)); - demoext = COM_FileExtension(demoname); + COM_FileExtension(demoname, demoext, sizeof(demoext)); if (!strcmp(demoext, "mvd")) { diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index be2693b7..6b59fad9 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2822,7 +2822,7 @@ void CL_LinkStaticEntities(void *pvs) stat = &cl_static_entities[i].ent; clmodel = stat->model; - if (!clmodel || clmodel->needload) + if (!clmodel || clmodel->loadstate != MLS_LOADED) continue; if ((!r_drawflame.ival) && (clmodel->engineflags & MDLF_FLAME)) @@ -3238,6 +3238,7 @@ void CL_TransitionEntities (void) void CL_LinkPacketEntities (void) { + extern cvar_t gl_part_flame; entity_t *ent; packet_entities_t *pack; entity_state_t *state; @@ -3252,7 +3253,7 @@ void CL_LinkPacketEntities (void) vec3_t angles; static int flickertime; static int flicker; - int trailef; + int trailef, trailidx; int modelflags; pack = cl.currentpackentities; @@ -3382,15 +3383,17 @@ void CL_LinkPacketEntities (void) dl->style = state->lightstyle; dl->flags &= ~LFLAG_FLASHBLEND; dl->flags |= (state->lightpflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0; +#ifdef RTLIGHTS if (state->skinnum) { - VectorCopy(ent->angles, angles); - angles[0]*=-1; //pflags matches alias models. + VectorCopy(le->angles, angles); + //if (model && model->type == mod_alias) + angles[0]*=-1; //pflags matches alias models. AngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]); VectorInverse(dl->axis[1]); - snprintf(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", state->skinnum); - dl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, "", IF_CUBEMAP); + R_LoadNumberedLightTexture(dl, state->skinnum); } +#endif } // if set to invisible, skip @@ -3417,7 +3420,7 @@ void CL_LinkPacketEntities (void) if (cl.model_precache_vwep[0]) { - if (state->modelindex == cl_playerindex && !cl.model_precache_vwep[0]->needload) + if (state->modelindex == cl_playerindex && !cl.model_precache_vwep[0]->loadstate != MLS_LOADED) { model = cl.model_precache_vwep[0]; model2 = cl.model_precache_vwep[state->modelindex2]; @@ -3582,24 +3585,23 @@ void CL_LinkPacketEntities (void) } } + //figure out which trail this entity is using trailef = model->particletrail; + trailidx = model->traildefaultindex; + if (state->effects & 0xff800000) + P_DefaultTrail (modelflags, &trailef, &trailidx); if (state->u.q1.traileffectnum) trailef = CL_TranslateParticleFromServer(state->u.q1.traileffectnum); - if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, ent->keynum, &(le->trailstate))) + //and emit it + if (trailef == P_INVALID || pe->ParticleTrail (old_origin, ent->origin, trailef, ent->keynum, ent->axis, &(le->trailstate))) if (model->traildefaultindex >= 0) - pe->ParticleTrailIndex(old_origin, ent->origin, model->traildefaultindex, 0, &(le->trailstate)); - - { - extern cvar_t gl_part_flame; - if (model->particleeffect != P_INVALID && cls.allow_anyparticles && gl_part_flame.ival) - { - P_EmitEffect (ent->origin, model->particleeffect, &(le->emitstate)); - } - } + pe->ParticleTrailIndex(old_origin, ent->origin, trailidx, 0, &(le->trailstate)); + if (model->particleeffect != P_INVALID && cls.allow_anyparticles && gl_part_flame.ival) + P_EmitEffect (ent->origin, model->particleeffect, &(le->emitstate)); //dlights are not so customisable. - if (r_rocketlight.value) + if (r_rocketlight.value && (modelflags & MF_ROCKET)) { float rad = 0; vec3_t dclr; @@ -3607,55 +3609,17 @@ void CL_LinkPacketEntities (void) dclr[0] = 2.0; dclr[1] = 1.0; dclr[2] = 0.25; + rad = 200; + rad += r_lightflicker.value?((flicker + state->number)&31):0; + dl = CL_AllocDlight (state->number); + memcpy(dl->axis, ent->axis, sizeof(dl->axis)); + VectorCopy (ent->origin, dl->origin); + dl->die = (float)cl.time; if (modelflags & MF_ROCKET) - { -#ifdef warningmsg -#pragma warningmsg("Replace this flag on load for hexen2 models") -#endif -#ifdef HEXEN2 - if (strncmp(model->name, "models/sflesh", 13)) -#endif - { //hmm. hexen spider gibs... - rad = 200; - rad += r_lightflicker.value?((flicker + state->number)&31):0; - } - } -#ifdef HEXEN2 - else if (modelflags & MFH2_FIREBALL) - { - rad = 120 - (r_lightflicker.value?(rand() % 20):10); - } - else if (modelflags & MFH2_ACIDBALL) - { - rad = 120 - (r_lightflicker.value?(rand() % 20):10); - dclr[0] = 0.5; - dclr[1] = 1; - dclr[2] = 0.25; - } - else if (modelflags & MFH2_SPIT) - { - // as far as I can tell this effect inverses the light... - dclr[0] = -dclr[0]; - dclr[1] = -dclr[1]; - dclr[2] = -dclr[2]; - rad = 120 - (r_lightflicker.value?(rand() % 20):10); - } -#endif - - if (rad) - { - dl = CL_AllocDlight (state->number); - memcpy(dl->axis, ent->axis, sizeof(dl->axis)); - VectorCopy (ent->origin, dl->origin); - dl->die = (float)cl.time; - if (modelflags & MF_ROCKET) - dl->origin[2] += 1; // is this even necessary - dl->radius = rad * r_rocketlight.value; - VectorCopy(dclr, dl->color); - } - - + dl->origin[2] += 1; // is this even necessary + dl->radius = rad * r_rocketlight.value; + VectorCopy(dclr, dl->color); } } #ifdef CSQC_DAT @@ -4337,7 +4301,7 @@ void CL_LinkPlayers (void) float predictmsmult = 1000*cl_predict_players_frac.value; int modelindex2; - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) return; if (cl.paused) @@ -4474,6 +4438,7 @@ void CL_LinkPlayers (void) ent->flags = 0; ent->model = model; ent->forcedshader = NULL; + ent->customskin = 0; ent->skinnum = state->skinnum; @@ -4881,7 +4846,7 @@ void CL_SetUpPlayerPrediction(qboolean dopred) if (playertime > realtime) playertime = realtime; - if (cl_nopred.value || /*cls.demoplayback ||*/ cl.paused || cl.worldmodel->needload) + if (cl_nopred.value || /*cls.demoplayback ||*/ cl.paused || cl.worldmodel->loadstate != MLS_LOADED) return; frame = &cl.inframes[cl.parsecount&UPDATE_MASK]; diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 73902040..9d69215b 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1550,10 +1550,10 @@ void CL_SendCmd (double frametime, qboolean mainloop) CL_ProxyMenuHooks(); - if (cls.demoplayback != DPB_NONE || cls.netchan.remote_address.type == NA_INVALID) + if (cls.demoplayback != DPB_NONE || !cls.state) { cursor_active = false; - if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) + if (!cls.state || cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { extern cvar_t cl_splitscreen; cl.ackedmovesequence = cl.movesequence; @@ -1601,6 +1601,11 @@ void CL_SendCmd (double frametime, qboolean mainloop) Cam_FinishMove(&cl.playerview[plnum], cmd); +#ifdef CSQC_DAT + CSQC_Input_Frame(plnum, cmd); +#endif + + if (cls.state == ca_active) { player_state_t *from, *to; playerview_t *pv = &cl.playerview[plnum]; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 7bd07dd8..341508aa 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1356,6 +1356,8 @@ void CL_ClearState (void) { VectorSet(cl.playerview[i].gravitydir, 0, 0, -1); cl.playerview[i].viewheight = DEFAULT_VIEWHEIGHT; + cl.playerview[i].maxspeed = 320; + cl.playerview[i].entgravity = 1; } cl.minpitch = -70; cl.maxpitch = 80; @@ -3123,7 +3125,8 @@ qboolean CL_AllowArbitaryDownload(char *localfile) allow = cl_download_redirection.ival; if (allow == 2) { - char *ext = COM_FileExtension(localfile); + char ext[8]; + COM_FileExtension(localfile, ext, sizeof(ext)); if (!strcmp(ext, "pak") || !strcmp(ext, "pk3") || !strcmp(ext, "pk4")) return true; else @@ -3707,6 +3710,8 @@ void CL_Init (void) #endif Ignore_Init(); + + CL_ClearState(); //make sure the cl.* fields are set properly if there's no ssqc or whatever. } @@ -3722,11 +3727,14 @@ void VARGS Host_EndGame (char *message, ...) va_list argptr; char string[1024]; - SCR_EndLoadingPlaque(); - va_start (argptr,message); vsnprintf (string,sizeof(string)-1, message,argptr); va_end (argptr); + + COM_AssertMainThread(string); + + SCR_EndLoadingPlaque(); + Con_TPrintf ("^&C0Host_EndGame: %s\n", string); Con_Printf ("\n"); @@ -3931,7 +3939,8 @@ void Host_BeginFileDownload(struct dl_download *dl, char *mimetype) if (!(f->flags & HRF_FILETYPES)) { - char *ext = COM_FileExtension(f->fname); + char ext[8]; + COM_FileExtension(f->fname, ext, sizeof(ext)); if (!strcmp(ext, "qwd")) f->flags |= HRF_DEMO_QWD; else if (!strcmp(ext, "mvd")) @@ -4044,7 +4053,7 @@ void Host_DoRunFile(hrf_t *f) if (!(f->flags & HRF_FILETYPES)) { - char *ext; + char ext[8]; #ifdef WEBCLIENT if (isurl(f->fname) && !f->srcfile) @@ -4068,7 +4077,7 @@ void Host_DoRunFile(hrf_t *f) #endif //if we get here, we have no mime type to give us any clues. - ext = COM_FileExtension(f->fname); + COM_FileExtension(f->fname, ext, sizeof(ext)); if (!strcmp(ext, "qwd")) f->flags |= HRF_DEMO_QWD; else if (!strcmp(ext, "mvd")) @@ -4393,6 +4402,8 @@ double Host_Frame (double time) if (startuppending) CL_StartCinematicOrMenu(); + COM_MainThreadWork(); + #ifdef PLUGINS Plug_Tick(); #endif @@ -4429,6 +4440,8 @@ double Host_Frame (double time) RSpeedEnd(RSPEED_SERVER); } #endif + while(COM_DoWork(0, false)) + ; return idlesec - (realtime - oldrealtime); } } @@ -4471,7 +4484,11 @@ double Host_Frame (double time) // realtime += spare/1000; //don't use it all! spare = CL_FilterTime((realtime - oldrealtime)*1000, maxfps, maxfpsignoreserver); if (!spare) + { + while(COM_DoWork(0, false)) + ; return (cl_yieldcpu.ival || vid.isminimized)? (1.0 / maxfps - (realtime - oldrealtime)) : 0; + } if (spare < 0 || cls.state < ca_onserver) spare = 0; //uncapped. if (spare > cl_sparemsec.ival) @@ -4666,7 +4683,7 @@ void CL_ReadCDKey(void) { //q3 cdkey //you don't need one, just use a server without sv_strictauth set to 0. char *buffer; - buffer = COM_LoadTempFile("q3key"); + buffer = COM_LoadTempFile("q3key", NULL); if (buffer) //a cdkey is meant to be 16 chars { char *chr; @@ -4807,6 +4824,8 @@ void CL_ExecInitialConfigs(char *resetcommand) Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway... SCR_ShowPic_Clear(true); + Cbuf_AddText("alias restart \"changelevel .\"\n",RESTRICT_LOCAL); + Cbuf_AddText("alias startmap_sp \"map start\"\n", RESTRICT_LOCAL); Cbuf_AddText("unbindall\n", RESTRICT_LOCAL); Cbuf_AddText("bind volup \"inc volume 0.1\"\n", RESTRICT_LOCAL); Cbuf_AddText("bind voldown \"inc volume -0.1\"\n", RESTRICT_LOCAL); @@ -4815,6 +4834,7 @@ void CL_ExecInitialConfigs(char *resetcommand) Cbuf_AddText("cvarreset *\n", RESTRICT_LOCAL); //reset all cvars to their current (engine) defaults Cbuf_AddText(resetcommand, RESTRICT_LOCAL); Cbuf_AddText("\n", RESTRICT_LOCAL); + COM_ParsePlusSets(true); //who should we imitate? qrc = COM_FDepthFile("quake.rc", true); //q1 @@ -4835,7 +4855,9 @@ void CL_ExecInitialConfigs(char *resetcommand) Cbuf_AddText ("exec q3config.cfg\n", RESTRICT_LOCAL); Cbuf_AddText ("exec autoexec.cfg\n", RESTRICT_LOCAL); } +#ifndef QUAKETC Cbuf_AddText ("exec fte.cfg\n", RESTRICT_LOCAL); +#endif #ifdef QUAKESPYAPI if (COM_FCheckExists ("frontend.cfg")) Cbuf_AddText ("exec frontend.cfg\n", RESTRICT_LOCAL); @@ -4922,7 +4944,7 @@ void Host_Init (quakeparms_t *parms) Sys_Init(); - COM_ParsePlusSets(); + COM_ParsePlusSets(false); Cbuf_Init (); Cmd_Init (); V_Init (); @@ -5044,15 +5066,20 @@ void Host_Shutdown(void) NET_Shutdown (); #endif + Stats_Clear(); + #ifdef Q3CLIENT VMQ3_FlushStringHandles(); #endif + COM_DestroyWorkerThread(); + Cvar_Shutdown(); Validation_FlushFileList(); Cmd_Shutdown(); Key_Unbindall_f(); + Con_History_Save(); //do this outside of the console code so that the filesystem is still running at this point but still allowing the filesystem to make console prints (you might not see them, but they should be visible to sys_printf still, for debugging). FS_Shutdown(); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 5380161f..53f8ea70 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -426,9 +426,9 @@ int CL_IsDownloading(const char *localname) qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags) { extern cvar_t cl_downloads; - char *ext; downloadlist_t *dl; qboolean webdl = false; + char ext[8]; if (!strncmp(filename, "http://", 7)) { if (!localname) @@ -444,7 +444,7 @@ qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned if (cls.demoplayback && cls.demoplayback != DPB_EZTV) return false; } - ext = COM_FileExtension(localname); + COM_FileExtension(localname, ext, sizeof(ext)); if (!stricmp(ext, "dll") || !stricmp(ext, "so") || strchr(localname, '\\') || strchr(localname, ':') || strstr(localname, "..")) { Con_Printf("Denying download of \"%s\"\n", filename); @@ -640,7 +640,7 @@ void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int f void CL_DownloadFinished(qdownload_t *dl) { int i; - char *ext; + char ext[8]; char filename[MAX_QPATH]; char tempname[MAX_QPATH]; @@ -652,7 +652,7 @@ void CL_DownloadFinished(qdownload_t *dl) COM_RefreshFSCache_f(); - ext = COM_FileExtension(filename); + COM_FileExtension(filename, ext, sizeof(ext)); //should probably ask the filesytem code if its a package format instead. @@ -757,6 +757,7 @@ Returns true if the file exists, returns false if it triggered a download. qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localname, unsigned int flags) { //returns false if we don't have the file yet. + COM_AssertMainThread("CL_CheckOrEnqueDownloadFile"); if (flags & DLLF_NONGAME) { /*pak/pk3 downloads have an explicit leading package/ as an internal/network marker*/ @@ -976,10 +977,9 @@ Model_NextDownload */ void Model_CheckDownloads (void) { -// char *twf; char *s; int i; -// extern char gamedirfile[]; + char ext[8]; // Con_TPrintf (TLC_CHECKINGMODELS); @@ -1006,7 +1006,7 @@ void Model_CheckDownloads (void) if (s[0] == '*') continue; // inline brush model - if (!stricmp(COM_FileExtension(s), "dsp")) //doom sprites are weird, and not really downloadable via this system + if (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), "dsp")) //doom sprites are weird, and not really downloadable via this system continue; #ifdef Q2CLIENT @@ -1022,7 +1022,7 @@ void Model_CheckDownloads (void) { s = cl.model_name_vwep[i]; - if (!stricmp(COM_FileExtension(s), "dsp")) //doom sprites are weird, and not really downloadable via this system + if (!stricmp(COM_FileExtension(s, ext, sizeof(ext)), "dsp")) //doom sprites are weird, and not really downloadable via this system continue; if (!*s) @@ -1035,10 +1035,9 @@ void Model_CheckDownloads (void) int CL_LoadModels(int stage, qboolean dontactuallyload) { - extern model_t *loadmodel; int i; - float giveuptime = Sys_DoubleTime()+0.3; //small things get padded into a single frame + float giveuptime = Sys_DoubleTime()+1; //small things get padded into a single frame #define atstage() ((cl.contentstage == stage++ && !dontactuallyload)?++cl.contentstage:false) #define endstage() if (!cls.timedemo && giveuptimetype == mod_dummy) -// Host_EndGame("No worldmodel was loaded\n"); - Mod_NowLoadExternal(); + if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); + if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED) + Mod_NowLoadExternal(cl.worldmodel); endstage(); } @@ -1245,11 +1247,10 @@ int CL_LoadModels(int stage, qboolean dontactuallyload) if (atstage()) { SCR_SetLoadingFile("newmap"); - loadmodel = cl.worldmodel; -// if (!loadmodel || loadmodel->type == mod_dummy) +// if (!cl.worldmodel || cl.worldmodel->type == mod_dummy) // Host_EndGame("No worldmodel was loaded\n"); cl.model_precaches_added = false; - R_NewMap (); + Surf_NewMap (); pmove.physents[0].model = cl.worldmodel; @@ -1292,9 +1293,11 @@ int CL_LoadSounds(int stage, qboolean dontactuallyload) if (atstage()) { +#if 0 SCR_SetLoadingFile(cl.sound_name[i]); #ifdef CSQC_DAT CSQC_LoadResource(cl.sound_name[i], "sound"); +#endif #endif cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]); @@ -1466,13 +1469,23 @@ void CL_RequestNextDownload (void) current_loading_size = cl.contentstage; if (stage < 0) return; + if (requiredownloads.ival && COM_HasWork()) + { + SCR_SetLoadingFile("loading content"); + return; + } SCR_SetLoadingFile("receiving game state"); + cl.sendprespawn = false; + + if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); + #ifdef warningmsg #pragma warningmsg("timedemo timer should start here") #endif - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) { Con_Printf("\n\n-------------\nCouldn't download %s - cannot fully connect\n", cl.worldmodel->name); SCR_SetLoadingStage(LS_NONE); @@ -2794,7 +2807,6 @@ void CLQW_ParseServerData (void) #ifndef CLIENTONLY Info_SetValueForStarKey (svs.info, "*gamedir", str, MAX_SERVERINFO_STRING); #endif - COM_FlushFSCache(); Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc")); } @@ -3025,7 +3037,6 @@ void CLQ2_ParseServerData (void) COM_Gamedir("baseq2"); else COM_Gamedir(str); - COM_FlushFSCache(); // if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) // Cvar_Set("game", str); @@ -3079,8 +3090,7 @@ void CLQ2_ParseServerData (void) Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc")); - if (R_PreNewMap) - R_PreNewMap(); + Surf_PreNewMap(); } #endif @@ -3246,8 +3256,7 @@ void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution. SCR_BeginLoadingPlaque(); - if (R_PreNewMap) - R_PreNewMap(); + Surf_PreNewMap(); memset (cl.model_name, 0, sizeof(cl.model_name)); for (nummodels=1 ; ; nummodels++) @@ -3341,6 +3350,7 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon); { case 1: cl.sendprespawn = true; + SCR_SetLoadingFile("loading data"); CL_RequestNextDownload(); break; @@ -3583,6 +3593,7 @@ void CL_ParseSoundlist (qboolean lots) CL_AllowIndependantSendCmd(false); //stop it now, the indep stuff *could* require model tracing. cl.sendprespawn = true; + SCR_SetLoadingFile("loading data"); } else #endif @@ -3681,6 +3692,7 @@ void CL_ParseModellist (qboolean lots) //set the flag to load models and send prespawn cl.sendprespawn = true; + SCR_SetLoadingFile("loading data"); } void CL_ProcessUserInfo (int slot, player_info_t *player); @@ -3925,6 +3937,7 @@ void CLQ2_Precache_f (void) cl.contentstage = 0; cl.sendprespawn = true; + SCR_SetLoadingFile("loading data"); #ifdef VM_CG CG_Start(); @@ -3998,6 +4011,7 @@ void CL_ParseStatic (int version) cl_static_entities[i].mdlidx = es.modelindex; cl_static_entities[i].emit = NULL; + cl_static_entities[i].state = es; ent = &cl_static_entities[i].ent; V_ClearEntity(ent); memset(&cl_static_entities[i].pvscache, 0, sizeof(cl_static_entities[i].pvscache)); @@ -4041,7 +4055,7 @@ void CL_ParseStatic (int version) AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]); VectorInverse(ent->axis[1]); - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) { Con_TPrintf ("Warning: Parsestatic and no map loaded yet\n"); return; @@ -4161,6 +4175,7 @@ void CLQ2_ParseStartSoundPacket(void) float attenuation; int flags; float ofs; + sfx_t *sfx; flags = MSG_ReadByte (); sound_num = MSG_ReadByte (); @@ -4211,20 +4226,25 @@ void CLQ2_ParseStartSoundPacket(void) if (!cl.sound_precache[sound_num]) return; - if (cl.sound_precache[sound_num]->name[0] == '*' && ent > 0 && ent <= MAX_CLIENTS) + sfx = cl.sound_precache[sound_num]; + if (sfx->name[0] == '*') { //a 'sexed' sound - char *model = Info_ValueForKey(cl.players[ent-1].userinfo, "skin"); - char *skin; - skin = strchr(model, '/'); - if (skin) - *skin = '\0'; - if (*model) + if (ent > 0 && ent <= MAX_CLIENTS) { - S_StartSound (ent, channel, S_PrecacheSound(va("players/%s/%s", model, cl.sound_precache[sound_num]->name+1)), pos, volume, attenuation, ofs, 0); - return; + char *model = Info_ValueForKey(cl.players[ent-1].userinfo, "skin"); + char *skin; + skin = strchr(model, '/'); + if (skin) + *skin = '\0'; + if (*model) + sfx = S_PrecacheSound(va("players/%s/%s", model, cl.sound_precache[sound_num]->name+1)); } + //fall back to male if it failed to load. + //note: threaded loading can still make it silent the first time we hear it. + if (sfx->loadstate == SLS_FAILED) + sfx = S_PrecacheSound(va("players/male/%s", cl.sound_precache[sound_num]->name+1)); } - S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume, attenuation, ofs, 0); + S_StartSound (ent, channel, sfx, pos, volume, attenuation, ofs, 0); } #endif @@ -4801,7 +4821,7 @@ void CL_MuzzleFlash (int entnum) if (!dlightkey) return; - if (P_RunParticleEffectType(org, NULL, 1, pt_muzzleflash)) + if (P_RunParticleEffectType(org, axis[0], 1, pt_muzzleflash)) { dl = CL_AllocDlight (dlightkey); VectorMA (org, 15, axis[0], dl->origin); @@ -5411,12 +5431,12 @@ void CL_PrintChat(player_info_t *plr, char *rawmsg, char *msg, int plrflags) con_chat = Con_Create("chat", CONF_HIDDEN|CONF_NOTIFY|CONF_NOTIFY_BOTTOM); if (con_chat) { - Con_PrintCon(con_chat, fullchatmessage); + Con_PrintCon(con_chat, fullchatmessage, con_chat->parseflags); if (con_separatechat.ival == 1) { con_main.flags |= CONF_NOTIMES; - Con_PrintCon(&con_main, fullchatmessage); + Con_PrintCon(&con_main, fullchatmessage, con_main.parseflags); con_main.flags &= CONF_NOTIMES; return; } @@ -5631,7 +5651,7 @@ void CL_ParsePrecache(void) { model_t *model; CL_CheckOrEnqueDownloadFile(s, s, 0); - model = Mod_ForName(s, (i == 1)?MLV_ERROR:MLV_WARN); + model = Mod_ForName(Mod_FixName(s, cl.model_name[1]), (i == 1)?MLV_ERROR:MLV_WARN); if (!model) Con_Printf("svc_precache: Mod_ForName(\"%s\") failed\n", s); cl.model_precache[i] = model; @@ -6777,17 +6797,17 @@ void CLNQ_ParseServerMessage (void) break; case svc_setview: + i=MSGCL_ReadEntity(); if (!cl.playerview[destsplit].viewentity) { - cl.playerview[destsplit].playernum = (cl.playerview[destsplit].viewentity = MSGCL_ReadEntity())-1; + cl.playerview[destsplit].playernum = (unsigned int)i; if (cl.playerview[destsplit].playernum >= cl.allocated_client_slots) { - Con_Printf(CON_WARNING "WARNING: Server put us in slot %i. We are not on the scoreboard.\n", cl.playerview[destsplit].playernum); + Con_DPrintf(CON_WARNING "WARNING: Server put us in slot %i. We are not on the scoreboard.\n", i); cl.playerview[destsplit].playernum = cl.allocated_client_slots; //pretend it's an mvd (we have that spare slot) } } - else - cl.playerview[destsplit].viewentity=MSGCL_ReadEntity(); + cl.playerview[destsplit].viewentity = i; break; case svc_signonnum: diff --git a/engine/client/cl_plugin.inc b/engine/client/cl_plugin.inc index 6cf2011a..93105410 100644 --- a/engine/client/cl_plugin.inc +++ b/engine/client/cl_plugin.inc @@ -138,7 +138,7 @@ qintptr_t VARGS Plug_Draw_LoadImageData(void *offset, quintptr_t mask, const qin // char *mimetype = VM_POINTER(arg[1]); void *codeddata = VM_POINTER(arg[2]); unsigned int datalength = VM_LONG(arg[3]); - texid_t t; + image_t *t; qbyte *rgbdata; unsigned int width, height; @@ -148,12 +148,13 @@ qintptr_t VARGS Plug_Draw_LoadImageData(void *offset, quintptr_t mask, const qin if ((rgbdata = Read32BitImageFile(codeddata, datalength, &width, &height, NULL, name))) { name = va("%s/", name); - t = R_FindTexture(name, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); + + t = Image_FindTexture(name, NULL, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); if (!TEXVALID(t)) - t = R_AllocNewTexture(name, width, height, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); + t = Image_CreateTexture(name, NULL, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); if (TEXVALID(t)) { - R_Upload(t, name, TF_RGBA32, rgbdata, NULL, width, height, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); + Image_Upload(t, TF_RGBA32, rgbdata, NULL, width, height, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); ret = Plug_Draw_LoadImage(name, 3, NULL); } @@ -506,7 +507,7 @@ qintptr_t VARGS Plug_Con_SubPrint(void *offset, quintptr_t mask, const qintptr_t } } - Con_PrintCon(con, text); + Con_PrintCon(con, text, con->parseflags); return 1; } @@ -635,11 +636,7 @@ qintptr_t VARGS Plug_Mod_GetPluginModelFuncs(void *offset, quintptr_t mask, cons Matrix3x4_Invert_Simple, COM_StripExtension, GenMatrixPosQuat4Scale, - Alias_ForceConvertBoneData, - - R_RegisterShader, - R_RegisterSkin, - R_BuildDefaultTexnums + Alias_ForceConvertBoneData }; if (VM_LONG(arg[0]) >= sizeof(funcs)) return (qintptr_t)&funcs; diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index d169b61f..d015812a 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -377,7 +377,7 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state CL_PredictUsercmd (pnum, entnum, &temp, to, &split); return; } - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) return; VectorCopy (from->origin, pmove.origin); @@ -667,7 +667,7 @@ static void CL_DecodeStateSize(unsigned short solid, int modelindex, vec3_t mins { if (solid == ES_SOLID_BSP) { - if (modelindex < MAX_PRECACHE_MODELS && cl.model_precache[modelindex] && !cl.model_precache[modelindex]->needload) + if (modelindex < MAX_PRECACHE_MODELS && cl.model_precache[modelindex] && cl.model_precache[modelindex]->loadstate == MLS_LOADED) { VectorCopy(cl.model_precache[modelindex]->mins, mins); VectorCopy(cl.model_precache[modelindex]->maxs, maxs); @@ -869,7 +869,7 @@ void CL_PredictMovePNum (int seat) #ifdef Q2CLIENT if (cls.protocol == CP_QUAKE2) { - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) return; pv->crouch = 0; CLQ2_PredictMovement(); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index e91f4cb7..f17b1290 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -382,7 +382,13 @@ void SCR_CenterPrint (int pnum, char *str, qboolean skipgamecode) p->flags |= CPRINT_PERSIST | CPRINT_BACKGROUND; p->flags &= ~CPRINT_TALIGN; } - else if (str[1] == 'O') + else if (str[1] == 'W') //wait between each char + p->flags ^= CPRINT_TYPEWRITER; + else if (str[1] == 'S') //Stay + p->flags ^= CPRINT_PERSIST; + else if (str[1] == 'M') //'Mask' the background so that its readable. + p->flags ^= CPRINT_BACKGROUND; + else if (str[1] == 'O') //Obituaries are shown at the bottom, ish. p->flags ^= CPRINT_OBITUARTY; else if (str[1] == 'B') p->flags ^= CPRINT_BALIGN; //Note: you probably want to add some blank lines... @@ -506,9 +512,11 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) { if (!(p->flags & CPRINT_BACKGROUND)) { + int w, h; + R_GetShaderSizes(pic, &w, &h, false); y+= 16; - R2D_ScalePic ( (vid.width-pic->width)/2, 16, pic->width, pic->height, pic); - y+= pic->height; + R2D_ScalePic ( (vid.width-w)/2, 16, w, h, pic); + y+= h; y+= 8; } } @@ -526,7 +534,7 @@ void SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font) y = (bottom-top - Font_CharHeight()*linecount) * 0.65 + top; else { - if (linecount <= 4) + if (linecount <= 5) { //small messages appear above and away from the crosshair y = (bottom-top - Font_CharHeight()*linecount) * 0.35 + top; @@ -616,6 +624,7 @@ void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int p.charcount = COM_ParseFunString(defaultmask, text, p.string, sizeof(p.string), false) - p.string; p.time_off = scr_centertime.value; p.time_start = cl.time; + *p.titleimage = 0; SCR_DrawCenterString(&r, &p, font_default); } @@ -677,6 +686,10 @@ void SCR_DrawCursor(void) if (rf->VID_CreateCursor) { key_customcursor[cmod].handle = rf->VID_CreateCursor(key_customcursor[cmod].name, key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1], key_customcursor[cmod].scale); + if (!key_customcursor[cmod].handle) + key_customcursor[cmod].handle = rf->VID_CreateCursor("gfx/cursor.tga", key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1], key_customcursor[cmod].scale); //try the fallback + if (!key_customcursor[cmod].handle) + key_customcursor[cmod].handle = rf->VID_CreateCursor("gfx/cursor.png", key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1], key_customcursor[cmod].scale); //try the fallback if (!key_customcursor[cmod].handle) key_customcursor[cmod].handle = rf->VID_CreateCursor("gfx/cursor.lmp", key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1], key_customcursor[cmod].scale); //try the fallback } @@ -697,9 +710,9 @@ void SCR_DrawCursor(void) //system doesn't support a hardware cursor, so try to draw a software one. p = R2D_SafeCachePic(key_customcursor[cmod].name); - if (!p) + if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) p = R2D_SafeCachePic("gfx/cursor.lmp"); - if (p) + if (p && R_GetShaderSizes(p, NULL, NULL, false)) { R2D_ImageColours(1, 1, 1, 1); R2D_Image(mousecursor_x-key_customcursor[cmod].hotspot[0], mousecursor_y-key_customcursor[cmod].hotspot[1], p->width*cl_cursorscale.value, p->height*cl_cursorscale.value, 0, 0, 1, 1, p); @@ -1209,6 +1222,17 @@ void SCR_DrawTurtle (void) R2D_ScalePic (scr_vrect.x, scr_vrect.y, 64, 64, scr_turtle); } +void SCR_DrawDisk (void) +{ + if (!draw_disc) + return; + + if (!COM_HasWork()) + return; + + R2D_ScalePic (scr_vrect.x + vid.width-24, scr_vrect.y, 24, 24, draw_disc); +} + /* ============== SCR_DrawNet @@ -1498,6 +1522,9 @@ void SCR_SetLoadingStage(int stage) } void SCR_SetLoadingFile(char *str) { + if (loadingfile && !strcmp(loadingfile, str)) + return; + if (loadingfile) Z_Free(loadingfile); loadingfile = Z_Malloc(strlen(str)+1); @@ -1511,7 +1538,7 @@ void SCR_SetLoadingFile(char *str) void SCR_DrawLoading (qboolean opaque) { - int sizex, x, y; + int sizex, x, y, w, h; mpic_t *pic; char *s; int qdepth; @@ -1522,6 +1549,7 @@ void SCR_DrawLoading (qboolean opaque) if (*levelshotname) { pic = R2D_SafeCachePic (levelshotname); + R_GetShaderSizes(pic, NULL, NULL, true); R2D_ImageColours(1, 1, 1, 1); R2D_ScalePic (0, 0, vid.width, vid.height, pic); } @@ -1535,13 +1563,13 @@ void SCR_DrawLoading (qboolean opaque) { //quake files pic = R2D_SafeCachePic ("gfx/loading.lmp"); - if (pic) + if (R_GetShaderSizes(pic, &w, &h, true)) { - x = (vid.width - pic->width)/2; - y = (vid.height - 48 - pic->height)/2; - R2D_ScalePic (x, y, pic->width, pic->height, pic); + x = (vid.width - w)/2; + y = (vid.height - 48 - h)/2; + R2D_ScalePic (x, y, w, h, pic); x = (vid.width/2) - 96; - y += pic->height + 8; + y += h + 8; } else { @@ -1586,15 +1614,15 @@ void SCR_DrawLoading (qboolean opaque) else { //hexen2 files pic = R2D_SafeCachePic ("gfx/menu/loading.lmp"); - if (pic) + if (R_GetShaderSizes(pic, &w, &h, true)) { int size, count, offset; if (!scr_drawloading && loading_stage == 0) return; - offset = (vid.width - pic->width)/2; - R2D_ScalePic (offset, 0, pic->width, pic->height, pic); + offset = (vid.width - w)/2; + R2D_ScalePic (offset, 0, w, h, pic); if (loading_stage == LS_NONE) return; @@ -1693,10 +1721,10 @@ void SCR_BeginLoadingPlaque (void) // redraw with no console and the loading plaque Sbar_Changed (); scr_drawloading = true; + scr_disabled_for_loading = true; SCR_UpdateScreen (); scr_drawloading = false; - scr_disabled_for_loading = true; scr_disabled_time = Sys_DoubleTime(); //realtime tends to change... Hmmm.... } @@ -1719,7 +1747,7 @@ void SCR_ImageName (char *mapname) #ifdef GLQUAKE if (qrenderer == QR_OPENGL) { - if (!R2D_SafeCachePic (levelshotname)) + if (!R_GetShaderSizes(R2D_SafeCachePic (levelshotname), NULL, NULL, true)) { *levelshotname = '\0'; return; @@ -1772,7 +1800,7 @@ void SCR_SetUpToDrawConsole (void) //android has an onscreen imm that we don't want to obscure fullscreenpercent = scr_consize.value; #endif - if (!con_stayhidden.ival && (!Key_Dest_Has(~(kdm_console|kdm_game))) && (!cl.sendprespawn && cl.worldmodel && cl.worldmodel->needload)) + if (!con_stayhidden.ival && (!Key_Dest_Has(~(kdm_console|kdm_game))) && (!cl.sendprespawn && cl.worldmodel && cl.worldmodel->loadstate != MLS_LOADED)) { //force console to fullscreen if we're loading stuff // Key_Dest_Add(kdm_console); @@ -1932,9 +1960,9 @@ qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height extern cvar_t scr_sshot_compression; #endif - char *ext; + char ext[8]; - ext = COM_FileExtension(filename); + COM_FileExtension(filename, ext, sizeof(ext)); if (!rgb_buffer) return false; @@ -2333,6 +2361,7 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) R2D_DrawCrosshair(); SCR_DrawNet (); + SCR_DrawDisk(); SCR_DrawFPS (); SCR_DrawUPS (); SCR_DrawClock(); diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 4fc00721..3a78a392 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -313,7 +313,7 @@ sfx_t *cl_sfx_ric2; sfx_t *cl_sfx_ric3; sfx_t *cl_sfx_r_exp3; -cvar_t cl_expsprite = CVARFD("cl_expsprite", "0", CVAR_ARCHIVE, "Display a central sprite in explosion effects. QuakeWorld typically does so, NQ mods should not."); +cvar_t cl_expsprite = CVARFD("cl_expsprite", "0", CVAR_ARCHIVE, "Display a central sprite in explosion effects. QuakeWorld typically does so, NQ mods should not (which is problematic when played with the qw protocol)."); cvar_t r_explosionlight = CVARFC("r_explosionlight", "1", CVAR_ARCHIVE, Cvar_Limiter_ZeroToOne_Callback); cvar_t cl_truelightning = CVARF("cl_truelightning", "0", CVAR_SEMICHEAT); cvar_t cl_beam_trace = CVAR("cl_beam_trace", "0"); @@ -400,8 +400,8 @@ void CL_AssociateEffect_f(void) associatedeffect = ae; } - //FIXME: overkill - CL_RegisterParticles(); + if (pe) + CL_RegisterParticles(); } void CL_InitTEntSounds (void) @@ -467,7 +467,7 @@ void P_LoadedModel(model_t *mod) mod->particleeffect = P_INVALID; mod->particletrail = P_INVALID; - mod->engineflags &= ~(MDLF_NODEFAULTTRAIL | MDLF_ENGULPHS); + mod->engineflags &= ~MDLF_ENGULPHS; for(ae = associatedeffect; ae; ae = ae->next) { if (!strcmp(ae->mname, mod->name)) @@ -489,7 +489,7 @@ void P_LoadedModel(model_t *mod) } } if (mod->particletrail == P_INVALID) - P_DefaultTrail(mod); + P_DefaultTrail(mod->flags, &mod->particletrail, &mod->traildefaultindex); } void CL_RefreshCustomTEnts(void); @@ -501,7 +501,7 @@ void CL_RegisterParticles(void) int i; for (i=0 , mod=mod_known ; ineedload) + if (mod->loadstate == MLS_LOADED) { P_LoadedModel(mod); } @@ -790,7 +790,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n if (ent < 0 && ent >= -512) //a zquake concept. ent between -1 and -maxplayers is to be taken to be a railtrail from a particular player instead of a beam. { // TODO: add support for those finnicky colored railtrails... - if (P_ParticleTrail(start, end, rtqw_railtrail, -ent, NULL)) + if (P_ParticleTrail(start, end, rtqw_railtrail, -ent, NULL, NULL)) P_ParticleTrailIndex(start, end, 208, 8, NULL); return; } @@ -844,7 +844,7 @@ void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ n else m = Mod_ForName(mname, MLV_WARN); - if (m && m->needload) + if (m && m->loadstate != MLS_LOADED) CL_CheckOrEnqueDownloadFile(m->name, NULL, 0); // save end position for truelightning @@ -1104,6 +1104,9 @@ void CL_ParseTEnt (void) case TENQ_BEAM: type = TEQW_BEAM; break; + case TE_EXPLOSION: + type = TE_EXPLOSIONNOSPRITE; + break; default: break; } @@ -1332,7 +1335,8 @@ void CL_ParseTEnt (void) ex->endalpha = ex->startalpha; //don't fade out } break; - case TE_EXPLOSION: // rocket explosion + case TE_EXPLOSIONNOSPRITE: //nq-style, no sprite + case TE_EXPLOSION: //qw-style, with (optional) sprite // particles pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); @@ -1364,7 +1368,7 @@ void CL_ParseTEnt (void) S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1, 0, 0); // sprite - if (cl_expsprite.ival && !nqprot) // temp hopefully + if (type == TE_EXPLOSION && cl_expsprite.ival) // temp hopefully { explosion_t *ex = CL_AllocExplosion (pos); ex->start = cl.time; @@ -1554,8 +1558,8 @@ void CL_ParseTEnt (void) pos2[1] = MSG_ReadCoord (); pos2[2] = MSG_ReadCoord (); - if (P_ParticleTrail(pos, pos2, rtqw_railtrail, 0, NULL)) - if (P_ParticleTrail(pos, pos2, rtq2_railtrail, 0, NULL)) + if (P_ParticleTrail(pos, pos2, rtqw_railtrail, 0, NULL, NULL)) + if (P_ParticleTrail(pos, pos2, rtq2_railtrail, 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 208, 8, NULL); break; @@ -1692,7 +1696,7 @@ void CL_ParseTEnt (void) // stain (Hopefully this is close to how DP does it) if (cl_legacystains.ival) Surf_AddStain(pos, -10, -10, -10, 30); - if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_plasmaburn"), 0, NULL)) + if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_plasmaburn"), 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 15, 0, NULL); break; @@ -1710,7 +1714,7 @@ void CL_ParseTEnt (void) MSG_ReadCoord (); MSG_ReadCoord (); - if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_nexbeam"), 0, NULL)) + if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_nexbeam"), 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 15, 0, NULL); break; @@ -1887,7 +1891,7 @@ void CL_SpawnCustomTEnt(custtentinst_t *info) } } else - failed = P_ParticleTrail(info->pos, info->pos2, t->particleeffecttype, 0, NULL); + failed = P_ParticleTrail(info->pos, info->pos2, t->particleeffecttype, 0, NULL, NULL); } else { @@ -2110,6 +2114,9 @@ void CL_RefreshCustomTEnts(void) cl.particle_csprecache[i] = P_INVALID; } } +#ifdef CSQC_DAT + CSQC_ResetTrails(); +#endif } void CL_ClearCustomTEnts(void) { @@ -2174,8 +2181,8 @@ void CL_ParseTrailParticles(void) else ts = NULL; - if (P_ParticleTrail(start, end, effectindex, entityindex, ts)) - P_ParticleTrail(start, end, rt_blood, entityindex, ts); + if (P_ParticleTrail(start, end, effectindex, entityindex, NULL, ts)) + P_ParticleTrail(start, end, rt_blood, entityindex, NULL, ts); } void CL_ParsePointParticles(qboolean compact) @@ -2468,7 +2475,7 @@ void CLQ2_ParseTEnt (void) case Q2TE_BLUEHYPERBLASTER: //TE_BLASTER without model+light MSG_ReadPos (pos); MSG_ReadPos (pos2); - P_ParticleTrail(pos, pos2, pt, 0, NULL); + P_ParticleTrail(pos, pos2, pt, 0, NULL, NULL); break; case Q2TE_EXPLOSION1: //column case Q2TE_EXPLOSION2: //splits @@ -2722,7 +2729,7 @@ fixme: case Q2TE_RAILTRAIL: // railgun effect MSG_ReadPos (pos); MSG_ReadPos (pos2); - if (P_ParticleTrail(pos, pos2, rtq2_railtrail, 0, NULL)) + if (P_ParticleTrail(pos, pos2, rtq2_railtrail, 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 0x74, 8, NULL); Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("weapons/railgf1a.wav"), 1, ATTN_NORM, 0); break; @@ -2930,7 +2937,7 @@ fixme: case Q2TE_BUBBLETRAIL: MSG_ReadPos (pos); MSG_ReadPos (pos2); - if (P_ParticleTrail(pos, pos2, rtq2_bubbletrail, 0, NULL)) + if (P_ParticleTrail(pos, pos2, rtq2_bubbletrail, 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 4, 8, NULL); break; @@ -3103,7 +3110,7 @@ fixme: case Q2TE_DEBUGTRAIL: MSG_ReadPos (pos); MSG_ReadPos (pos2); - if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_debugtrail"), 0, NULL)) + if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_debugtrail"), 0, NULL, NULL)) P_ParticleTrailIndex(pos, pos2, 116, 8, NULL); break; @@ -3381,6 +3388,7 @@ entity_t *CL_NewTempEntity (void) return ent; } +void CSQC_GetEntityOrigin(unsigned int csqcent, float *out); /* ================= @@ -3503,6 +3511,12 @@ void CL_UpdateBeams (void) } } } +#ifdef CSQC_DAT + else if ((b->bflags & 1) && b->entity > MAX_EDICTS) + { + CSQC_GetEntityOrigin(b->entity-MAX_EDICTS, b->start); + } +#endif else if (b->bflags & STREAM_ATTACHED) { player_state_t *pl; @@ -3542,7 +3556,7 @@ void CL_UpdateBeams (void) } if (ruleset_allow_particle_lightning.ival || !b->model) - if (b->particleeffect >= 0 && !P_ParticleTrail(b->start, b->end, b->particleeffect, b->entity, &b->trailstate)) + if (b->particleeffect >= 0 && !P_ParticleTrail(b->start, b->end, b->particleeffect, b->entity, NULL, &b->trailstate)) continue; if (!b->model) continue; @@ -3696,7 +3710,7 @@ void CL_UpdateExplosions (void) ent->drawflags = SCALE_ORIGIN_ORIGIN; if (ex->traileffect != P_INVALID) - pe->ParticleTrail(ent->oldorigin, ent->origin, ex->traileffect, 0, &(ex->trailstate)); + pe->ParticleTrail(ent->oldorigin, ent->origin, ex->traileffect, 0, ent->axis, &(ex->trailstate)); if (!(ex->flags & Q2RF_BEAM)) VectorCopy(ent->origin, ex->oldorigin); //don't corrupt q2 beams if (ex->flags & Q2RF_BEAM) diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index 5616c56d..3889ca7f 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -111,7 +111,7 @@ int Script_Read(int handle, struct pc_token_s *token) if (sc->originalfilestack[sc->stackdepth]) BZ_Free(sc->originalfilestack[sc->stackdepth]); - sc->filestack[sc->stackdepth] = sc->originalfilestack[sc->stackdepth] = FS_LoadMallocFile(com_token); + sc->filestack[sc->stackdepth] = sc->originalfilestack[sc->stackdepth] = FS_LoadMallocFile(com_token, NULL); Q_strncpyz(sc->filename[sc->stackdepth], com_token, MAX_QPATH); sc->stackdepth++; continue; @@ -205,7 +205,7 @@ int Script_LoadFile(char *filename) sc = scripts+i; memset(sc, 0, sizeof(*sc)); - sc->filestack[0] = sc->originalfilestack[0] = FS_LoadMallocFile(filename); + sc->filestack[0] = sc->originalfilestack[0] = FS_LoadMallocFile(filename, NULL); Q_strncpyz(sc->filename[sc->stackdepth], filename, MAX_QPATH); sc->stackdepth = 1; @@ -639,13 +639,14 @@ void UI_RegisterFont(char *fontName, int pointSize, fontInfo_t *font) } in; int i; char name[MAX_QPATH]; + size_t sz; #define readInt() LittleLong(*in.i++) #define readFloat() LittleFloat(*in.f++) snprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize); - in.c = COM_LoadTempFile(name); - if (com_filesize == sizeof(fontInfo_t)) + in.c = COM_LoadTempFile(name, &sz); + if (sz == sizeof(fontInfo_t)) { for(i=0; i= mask || VM_POINTER(o) < offset) Host_EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds. +#define VALIDATEPOINTER(o,l) if ((quintptr_t)o + l >= mask || VM_POINTER(o) < offset) Host_EndGame("Call to ui trap %i passes invalid pointer\n", (int)fn); //out of bounds. static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, const qintptr_t *arg) { @@ -846,7 +847,9 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con char *name = VM_POINTER(arg[0]); model_t *mod; mod = Mod_ForName(name, MLV_SILENT); - if (mod->needload || mod->type == mod_dummy) + if (mod && mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (!mod || mod->loadstate != MLS_LOADED || mod->type == mod_dummy) VM_LONG(ret) = 0; else VM_LONG(ret) = VM_TOMHANDLE(mod); @@ -922,7 +925,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case UI_S_REGISTERSOUND: { sfx_t *sfx; - sfx = S_PrecacheSound(va("../%s", (char*)VM_POINTER(arg[0]))); + sfx = S_PrecacheSound(va("%s", (char*)VM_POINTER(arg[0]))); if (sfx) VM_LONG(ret) = VM_TOSTRCACHE(arg[0]); //return handle is the parameter they just gave else @@ -1000,7 +1003,38 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case UI_GETCLIENTSTATE: //get client state //fixme: we need to fill in a structure. - Con_Printf("ui_getclientstate\n"); +// Con_Printf("ui_getclientstate\n"); + VALIDATEPOINTER(arg[0], sizeof(uiClientState_t)); + { + uiClientState_t *state = VM_POINTER(arg[0]); + state->connectPacketCount = 0;//clc.connectPacketCount; + + switch(cls.state) + { + case ca_disconnected: + if (CL_TryingToConnect()) + state->connState = Q3CA_CONNECTING; + else + state->connState = Q3CA_DISCONNECTED; + break; + case ca_demostart: + state->connState = Q3CA_CONNECTING; + break; + case ca_connected: + state->connState = Q3CA_CONNECTED; + break; + case ca_onserver: + state->connState = Q3CA_PRIMED; + break; + case ca_active: + state->connState = Q3CA_ACTIVE; + break; + } + Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) ); + Q_strncpyz( state->updateInfoString, "FTE!", sizeof( state->updateInfoString ) ); //warning/motd message from update server + Q_strncpyz( state->messageString, "", sizeof( state->messageString ) ); //error message from game server + state->clientNum = cl.playerview[0].playernum; + } break; case UI_GETCONFIGSTRING: @@ -1553,6 +1587,11 @@ void UI_Stop (void) VM_Destroy(uivm); VM_fcloseall(0); uivm = NULL; + + //mimic Q3 and save the config if anything got changed. + //note that q3 checks every frame. we only check when the ui is closed. + if (Cvar_UnsavedArchive()) + Cmd_ExecuteString("cfg_save", RESTRICT_LOCAL); } } @@ -1612,7 +1651,7 @@ qboolean UI_OpenMenu(void) qboolean UI_Command(void) { if (uivm) - return VM_Call(uivm, UI_CONSOLE_COMMAND); + return VM_Call(uivm, UI_CONSOLE_COMMAND, (int)(realtime * 1000)); return false; } diff --git a/engine/client/client.h b/engine/client/client.h index d497f9f3..ae18b32f 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -30,7 +30,7 @@ typedef struct qwskin_s //for hardware 32bit texture overrides texnums_t textures; - qboolean failedload; // the name isn't a valid skin + qbyte failedload; // the name isn't a valid skin void *skindata; } qwskin_t; @@ -268,6 +268,7 @@ typedef struct dlight_s int key; // so entities can reuse same entry vec3_t origin; vec3_t axis[3]; + vec3_t rotation; //cubemap/spotlight rotation float radius; float die; // stop lighting after this time float decay; // drop this each second @@ -851,7 +852,9 @@ extern cvar_t m_side; extern cvar_t _windowed_mouse; +#ifndef SERVERONLY extern cvar_t name; +#endif extern cvar_t ruleset_allow_playercount; @@ -873,6 +876,7 @@ extern client_state_t cl; typedef struct { entity_t ent; + entity_state_t state; trailstate_t *emit; int mdlidx; /*negative are csqc indexes*/ pvscache_t pvscache; @@ -1218,6 +1222,7 @@ qboolean CSQC_JoystickAxis(int axis, float value, int devid); qboolean CSQC_Accelerometer(float x, float y, float z); int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float vol, float attenuation, float pitchmod); void CSQC_ParseEntities(void); +void CSQC_ResetTrails(void); qboolean CSQC_DeltaPlayer(int playernum, player_state_t *state); void CSQC_DeltaStart(float time); @@ -1455,6 +1460,7 @@ int qm_strcmp(char *s1, char *s2); int qm_stricmp(char *s1, char *s2); void Stats_ParsePrintLine(char *line); void Stats_NewMap(void); +void Stats_Clear(void); enum uploadfmt; typedef struct diff --git a/engine/client/clq2_ents.c b/engine/client/clq2_ents.c index 22d45566..171b0e79 100644 --- a/engine/client/clq2_ents.c +++ b/engine/client/clq2_ents.c @@ -1314,7 +1314,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) player = &cl.players[(s1->skinnum&0xff)%cl.allocated_client_slots]; ent.model = player->model; - if (!ent.model || ent.model->needload) //we need to do better than this + if (!ent.model || ent.model->loadstate != MLS_LOADED) //we need to do better than this { ent.model = Mod_ForName("players/male/tris.md2", MLV_SILENT); ent.customskin = Mod_RegisterSkinFile("players/male/grunt.skin"); @@ -1603,8 +1603,9 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) { if (effects & Q2EF_ROCKET) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_rocket, ent.keynum, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, ent.keynum, ¢->trailstate)) + //FIXME: cubemap orientation + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_rocket, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 0xdc, 4, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.1, 0.05); @@ -1621,7 +1622,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) } else { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_blastertrail, ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_blastertrail, ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 0xe0, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0); } @@ -1636,14 +1637,14 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) } else if (effects & Q2EF_GIB) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_gib, ent.keynum, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_gib, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 0xe8, 8, ¢->trailstate); } else if (effects & Q2EF_GRENADE) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_grenade, ent.keynum, ¢->trailstate)) - if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_grenade, ent.keynum, NULL, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 4, 8, ¢->trailstate); } else if (effects & Q2EF_FLIES) @@ -1675,13 +1676,13 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) } else if (effects & Q2EF_FLAG1) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_flag1"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_flag1"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 242, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05); } else if (effects & Q2EF_FLAG2) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_flag2"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_flag2"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 115, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2); } @@ -1689,7 +1690,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) //ROGUE else if (effects & Q2EF_TAGTRAIL) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_tagtrail"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_tagtrail"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 220, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0); } @@ -1712,7 +1713,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) } else if (effects & Q2EF_TRACKER) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_tracker"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_tracker"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 0, 1, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 200, -0.2, -0.2, -0.2); } @@ -1721,13 +1722,13 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) // RAFAEL else if (effects & Q2EF_GREENGIB) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_greengib"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_greengib"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 219, 8, ¢->trailstate); } // RAFAEL else if (effects & Q2EF_IONRIPPER) { - if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_ionripper"), ent.keynum, ¢->trailstate)) + if (P_ParticleTrail(cent->lerp_origin, ent.origin, P_FindParticleType("ef_ionripper"), ent.keynum, NULL, ¢->trailstate)) P_ParticleTrailIndex(cent->lerp_origin, ent.origin, 228, 4, ¢->trailstate); V_AddLight (ent.keynum, ent.origin, 100, 0.2, 0.1, 0.1); } @@ -1741,7 +1742,7 @@ void CLQ2_AddPacketEntities (q2frame_t *frame) { if (effects & Q2EF_ANIM_ALLFAST) { - P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_blastertrail, ent.keynum, ¢->trailstate); + P_ParticleTrail(cent->lerp_origin, ent.origin, rtq2_blastertrail, ent.keynum, NULL, ¢->trailstate); } V_AddLight (ent.keynum, ent.origin, 130, 0.2, 0.1, 0.1); } diff --git a/engine/client/clq3_parse.c b/engine/client/clq3_parse.c index 4efb8318..f807e34a 100644 --- a/engine/client/clq3_parse.c +++ b/engine/client/clq3_parse.c @@ -515,7 +515,6 @@ qboolean CLQ3_SystemInfoChanged(char *str) #ifndef CLIENTONLY Info_SetValueForStarKey (svs.info, "*gamedir", value, MAX_SERVERINFO_STRING); #endif - COM_FlushFSCache(); } rc = Info_ValueForKey(str, "sv_referencedPaks"); //the ones that we should download. @@ -612,7 +611,7 @@ void CLQ3_ParseGameState(void) Host_EndGame("CGame didn't set a map.\n"); cl.model_precaches_added = false; - R_NewMap (); + Surf_NewMap (); SCR_EndLoadingPlaque(); diff --git a/engine/client/console.c b/engine/client/console.c index 1a007d7b..1f1584ae 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -204,12 +204,6 @@ qboolean Con_NameForNum(int num, char *buffer, int buffersize) return false; } -/*print text to a console*/ -void Con_PrintCon (console_t *con, char *txt); - - - - #ifdef QTERM void QT_Update(void) { @@ -506,8 +500,8 @@ void Cmd_ConEcho_f(void) if (con) { Cmd_ShiftArgs(1, false); - Con_PrintCon(con, Cmd_Args()); - Con_PrintCon(con, "\n"); + Con_PrintCon(con, Cmd_Args(), con->parseflags); + Con_PrintCon(con, "\n", con->parseflags); } } @@ -580,7 +574,7 @@ void Con_Init (void) Q_strncpyz(con_main.title, "MAIN", sizeof(con_main.title)); con_initialized = true; - Con_TPrintf ("Console initialized.\n"); +// Con_TPrintf ("Console initialized.\n"); // // register our commands @@ -618,8 +612,6 @@ void Con_Shutdown(void) { int i; - Con_History_Save(); - for (i = 0; i <= CON_EDIT_LINES_MASK; i++) { BZ_Free(key_lines[i]); @@ -674,14 +666,14 @@ void Con_PrintConChars (console_t *con, conchar_t *c, int len) con->current->length+=len; } -void Con_PrintCon (console_t *con, char *txt) +void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags) { conchar_t expanded[4096]; conchar_t *c; conline_t *oc; conline_t *reuse; - COM_ParseFunString(con->defaultcharbits, txt, expanded, sizeof(expanded), con->parseflags); + COM_ParseFunString(con->defaultcharbits, txt, expanded, sizeof(expanded), parseflags); c = expanded; if (*c) @@ -796,7 +788,13 @@ void Con_PrintCon (console_t *con, char *txt) void Con_Print (char *txt) { - Con_PrintCon(&con_main, txt); //client console + Con_PrintCon(&con_main, txt, con_main.parseflags); //client console +} +void Con_PrintFlags(char *txt, unsigned int setflags, unsigned int clearflags) +{ + setflags |= con_main.parseflags; + setflags &= ~clearflags; + Con_PrintCon(&con_main, txt, setflags); } void Con_CycleConsole(void) @@ -830,6 +828,12 @@ void SV_FlushRedirect (void); #endif #define MAXPRINTMSG 4096 +static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b) +{ + Con_Printf("%s", data); + BZ_Free(data); +} + // FIXME: make a buffer size safe vsprintf? void VARGS Con_Printf (const char *fmt, ...) { @@ -840,6 +844,12 @@ void VARGS Con_Printf (const char *fmt, ...) vsnprintf (msg,sizeof(msg), fmt,argptr); va_end (argptr); + if (!Sys_IsThread(NULL)) + { + COM_AddWork(0, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0); + return; + } + #ifndef CLIENTONLY // add to redirected message if (sv_redirected) @@ -939,7 +949,7 @@ void VARGS Con_DPrintf (const char *fmt, ...) else { Sys_Printf ("%s", msg); // also echo to debugging console - Con_PrintCon(&con_main, msg); + Con_PrintCon(&con_main, msg, con_main.parseflags); } } diff --git a/engine/client/fragstats.c b/engine/client/fragstats.c index 26ee7b05..2054e0be 100644 --- a/engine/client/fragstats.c +++ b/engine/client/fragstats.c @@ -327,7 +327,7 @@ static void Stats_StatMessage(fragfilemsgtypes_t type, int wid, char *token1, ch fragstats.readkills = true; } -static void Stats_Clear(void) +void Stats_Clear(void) { int i; statmessage_t *ms; @@ -364,7 +364,7 @@ static void Stats_LoadFragFile(char *name) strcpy(filename, name); COM_DefaultExtension(filename, ".dat", sizeof(filename)); - file = COM_LoadTempFile(filename); + file = COM_LoadTempFile(filename, NULL); if (!file || !*file) { Con_DPrintf("Couldn't load %s\n", filename); diff --git a/engine/client/image.c b/engine/client/image.c index 6f3543f3..b701ce73 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -1,11 +1,9 @@ #include "quakedef.h" -#ifdef GLQUAKE #include "glquake.h" -#endif -#ifdef D3DQUAKE -//#include "d3dquake.h" -#endif +//FIXME +texid_t GL_FindTextureFallback (const char *identifier, unsigned int flags, void *fallback, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt); +//FIXME #ifdef NPFTE //#define Con_Printf(f, ...) @@ -13,15 +11,8 @@ #define LittleShort(s) s #define LittleLong(s) s #else -cvar_t r_dodgytgafiles = SCVAR("r_dodgytgafiles", "0"); //Certain tgas are upside down. - //This is due to a bug in tenebrae. - //(normally) the textures are actually the right way around. - //but some people have gone and 'fixed' those broken ones by flipping. - //these images appear upside down in any editor but correct in tenebrae - //set this to 1 to emulate tenebrae's bug. -cvar_t r_dodgypcxfiles = SCVAR("r_dodgypcxfiles", "0"); //Quake 2's PCX loading isn't complete, - //and some Q2 mods include PCX files - //that only work with this assumption +cvar_t r_dodgytgafiles = CVARD("r_dodgytgafiles", "0", "Many old glquake engines had a buggy tga loader that ignored bottom-up flags. Naturally people worked around this and the world was plagued with buggy images. Most engines have now fixed the bug, but you can reenable it if you have bugged tga files."); +cvar_t r_dodgypcxfiles = CVARD("r_dodgypcxfiles", "0", "When enabled, this will ignore the palette stored within pcx files, for compatibility with quake2."); char *r_defaultimageextensions = #ifdef IMAGEFMT_DDS @@ -41,7 +32,16 @@ char *r_defaultimageextensions = " pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs. ; void R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue); -cvar_t r_imageexensions = CVARC("r_imageexensions", NULL, R_ImageExtensions_Callback); +cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load."); +extern cvar_t gl_lerpimages; +extern cvar_t gl_picmip2d; +extern cvar_t gl_picmip; +extern cvar_t r_shadow_bumpscale_basetexture; +extern cvar_t r_shadow_bumpscale_bumpmap; + +static bucket_t *imagetablebuckets[256]; +static hashtable_t imagetable; +static image_t *imagelist; #endif #ifndef _WIN32 @@ -235,7 +235,7 @@ qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean * if (asgrey == 2) //grey only, load as 8 bit.. { - if (!tgaheader.version == 1 && !tgaheader.version == 3) + if (!(tgaheader.version == 1) && !(tgaheader.version == 3)) return NULL; } if (tgaheader.version == 1 || tgaheader.version == 3) @@ -314,7 +314,7 @@ qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean * } } - for(row=rows-1; row>=0; row--) + for(row=rows; row-->0; ) { if (flipped) pixbuf = targa_rgba + row*columns*(asgrey?1:4); @@ -1786,7 +1786,12 @@ qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height) pix[2] = palette[dataByte*3+2]; pix[3] = 255; if (dataByte == 255) + { + pix[0] = 0; //linear filtering can mean transparent pixel colours are visible. black is a more neutral colour. + pix[1] = 0; + pix[2] = 0; pix[3] = 0; + } pix += 4; x++; } @@ -2264,7 +2269,23 @@ void BoostGamma(qbyte *rgba, int width, int height) } - +static void Image_LoadTexture_Failed(void *ctx, void *data, size_t a, size_t b) +{ + texid_t tex = ctx; + tex->status = TEX_FAILED; +} +static void Image_LoadTextureMips(void *ctx, void *data, size_t a, size_t b) +{ + texid_t tex = ctx; + struct pendingtextureinfo *mips = data; + tex->width = mips->mip[0].width; + tex->height = mips->mip[0].height; + if (rf->IMG_LoadTextureMips(tex, mips)) + tex->status = TEX_LOADED; + else + tex->status = TEX_FAILED; + BZ_Free(mips); +} #ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT #define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 @@ -2297,30 +2318,25 @@ typedef struct { } ddsheader; -texid_tf GL_ReadTextureDDS(const char *iname, unsigned char *buffer, int filesize) +static qboolean Image_ReadDDSFile(texid_t tex, unsigned int flags, char *fname, qbyte *filedata, size_t filesize) { - extern int gl_filter_min; - extern int gl_filter_max; - texid_t texnum; int nummips; int mipnum; int datasize; - int intfmt; int pad; unsigned int w, h; int divsize, blocksize; - qboolean warned = false; + struct pendingtextureinfo *mips; + int encoding; ddsheader fmtheader; - if (*(int*)buffer != *(int*)"DDS " || qrenderer != QR_OPENGL) - return r_nulltex; - buffer+=4; + if (*(int*)filedata != *(int*)"DDS ") + return false; + filedata+=4; - memcpy(&fmtheader, buffer, sizeof(fmtheader)); + memcpy(&fmtheader, filedata+4, sizeof(fmtheader)); if (fmtheader.dwSize != sizeof(fmtheader)) - return r_nulltex; //corrupt/different version - - buffer += fmtheader.dwSize; + return false; //corrupt/different version nummips = fmtheader.dwMipMapCount; if (nummips < 1) @@ -2328,86 +2344,67 @@ texid_tf GL_ReadTextureDDS(const char *iname, unsigned char *buffer, int filesiz if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == *(int*)"DXT1") { - intfmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; //alpha or not? Assume yes, and let the drivers decide. + encoding = PTI_S3RGBA1; //alpha or not? Assume yes, and let the drivers decide. pad = 8; divsize = 4; blocksize = 8; } else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == *(int*)"DXT2" || *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == *(int*)"DXT3") { - intfmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + encoding = PTI_S3RGBA3; pad = 8; divsize = 4; blocksize = 16; } else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == *(int*)"DXT4" || *(int*)&fmtheader.ddpfPixelFormat.dwFourCC == *(int*)"DXT5") { - intfmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + encoding = PTI_S3RGBA5; pad = 8; divsize = 4; blocksize = 16; } else { - Con_Printf("Unsupported dds fourcc in %s\n", iname); - return r_nulltex; + Con_Printf("Unsupported dds fourcc in %s\n", fname); + return false; } - if (!qglCompressedTexImage2DARB) - return r_nulltex; + mips = Z_Malloc(sizeof(*mips)); + mips->mipcount = 0; + mips->type = PTI_2D; + mips->extrafree = filedata; + mips->encoding = encoding; - texnum = GL_AllocNewTexture(iname, fmtheader.dwWidth, fmtheader.dwHeight, 0); - GL_MTBind(0, GL_TEXTURE_2D, texnum); + filedata += 4+fmtheader.dwSize; datasize = fmtheader.dwPitchOrLinearSize; w = fmtheader.dwWidth; h = fmtheader.dwHeight; for (mipnum = 0; mipnum < nummips; mipnum++) { -// (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data); if (datasize < pad) datasize = pad; datasize = max(divsize, w)/divsize * max(divsize, h)/divsize * blocksize; - qglCompressedTexImage2DARB(GL_TEXTURE_2D, mipnum, intfmt, w, h, 0, datasize, buffer); - if (qglGetError()) - { - if (!warned) - Con_Printf("Incompatible dds file %s (mip %i)\n", iname, mipnum); - warned = true; - } - buffer += datasize; + + mips->mip[mipnum].data = filedata; + mips->mip[mipnum].datasize = datasize; + mips->mip[mipnum].width = w; + mips->mip[mipnum].height = h; + filedata += datasize; w = (w+1)>>1; h = (h+1)>>1; } - if (nummips>1) - { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } + mips->mipcount = mipnum; - if (qglGetError()) - { - if (!warned) - Con_Printf("Incompatible dds file %s\n", iname); - warned = true; - } - - return texnum; + COM_AddWork(0, Image_LoadTextureMips, tex, mips, 0, 0); + return true; } #endif #ifdef IMAGEFMT_BLP -texid_tf GL_ReadBLPFile(const char *iname, unsigned char *buffer, int filesize, int *width, int *height) +static qboolean Image_ReadBLPFile(texid_t tex, unsigned int flags, char *fname, qbyte *filedata, size_t filesize) { - extern int gl_filter_min; - extern int gl_filter_max; - //FIXME: cba with endian. int miplevel; int w, h, i; @@ -2428,77 +2425,102 @@ texid_tf GL_ReadBLPFile(const char *iname, unsigned char *buffer, int filesize, unsigned int *tmpmem = NULL; unsigned char *in; unsigned int inlen; - texid_tf texnum; - blp = (void*)buffer; + struct pendingtextureinfo *mips; - if (memcmp(blp->blp2, "BLP2", 4) || blp->type != 1 || qrenderer != QR_OPENGL) - return r_nulltex; + blp = (void*)filedata; - *width = w = blp->xres; - *height = h = blp->yres; + if (memcmp(blp->blp2, "BLP2", 4) || blp->type != 1) + return false; - texnum = GL_AllocNewTexture(iname, w, h, 0); - GL_MTBind(0, GL_TEXTURE_2D, texnum); + mips = Z_Malloc(sizeof(*mips)); + mips->mipcount = 0; + mips->type = PTI_2D; - for (miplevel = 0; ; ) + w = LittleLong(blp->xres); + h = LittleLong(blp->yres); + + if (blp->encoding == 2) { - //if we ran out of mips to load, give up. - if (miplevel == 16 || !blp->mipoffset[miplevel] || !blp->mipsize[miplevel] || blp->mipoffset[miplevel]+blp->mipsize[miplevel] > filesize) + int blocksize; + + //s3tc/dxt + switch(blp->alphaencoding) { - //if we got at least one mip, cap the mips. might help save some ram? naaah... - //if this is the first mip, well, its completely fucked. - if (miplevel--) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, miplevel); + default: + case 0: //dxt1 + if (blp->alphadepth) + mips->encoding = PTI_S3RGBA1; + else + mips->encoding = PTI_S3RGB1; + blocksize = 8; + break; + case 1: //dxt2/3 + mips->encoding = PTI_S3RGBA3; + blocksize = 16; + break; + case 7: //dxt4/5 + mips->encoding = PTI_S3RGBA5; + blocksize = 16; break; } - in = buffer + blp->mipoffset[miplevel]; - inlen = blp->mipsize[miplevel]; - if (blp->encoding == 2) + for (miplevel = 0; miplevel < 16; ) { - int type; - int blocksize; - //dxt compression - switch(blp->alphaencoding) - { - default: - case 0: //dxt1 - if (blp->alphadepth) - type = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - else - type = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - blocksize = 8; + if (!w && !h) //shrunk to no size break; - case 1: //dxt2/3 - type = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - blocksize = 16; + if (!w) + w = 1; + if (!h) + h = 1; + if (!blp->mipoffset[miplevel] || !blp->mipsize[miplevel] || blp->mipoffset[miplevel]+blp->mipsize[miplevel] > filesize) //no data break; - case 7: //dxt4/5 - type = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - blocksize = 16; + mips->mip[miplevel].width = w; + mips->mip[miplevel].height = h; + mips->mip[miplevel].data = filedata + LittleLong(blp->mipoffset[miplevel]); + mips->mip[miplevel].datasize = LittleLong(blp->mipsize[miplevel]); + + miplevel++; + if (!blp->hasmips || (flags & IF_NOMIPMAP)) break; - } - if (inlen != ((w+3)/4) * ((h+3)/4) * blocksize) - { - Con_Printf("%s: mip level %i does not contain the correct amount of data\n", iname, miplevel); - if (miplevel--) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, miplevel); - break; - } - qglCompressedTexImage2DARB(GL_TEXTURE_2D, miplevel, type, w, h, 0, inlen, in); + w >>= 1; + h >>= 1; } - else + mips->mipcount = miplevel; + mips->extrafree = filedata; + } + else + { + mips->encoding = PTI_BGRA8; + for (miplevel = 0; miplevel < 16; ) { - if (inlen != w*h+((w*h*blp->alphadepth+7)>>3)) + if (!w && !h) + break; + if (!w) + w = 1; + if (!h) + h = 1; + //if we ran out of mips to load, give up. + if (!blp->mipoffset[miplevel] || !blp->mipsize[miplevel] || blp->mipoffset[miplevel]+blp->mipsize[miplevel] > filesize) { - Con_Printf("%s: mip level %i does not contain the correct amount of data\n", iname, miplevel); - if (miplevel--) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, miplevel); + //if we got at least one mip, cap the mips. might help save some ram? naaah... + //if this is the first mip, well, its completely fucked. break; } - if (!tmpmem) - tmpmem = malloc(4*w*h); + in = filedata + LittleLong(blp->mipoffset[miplevel]); + inlen = LittleLong(blp->mipsize[miplevel]); + + if (inlen != w*h+((w*h*blp->alphadepth+7)>>3)) + { + Con_Printf("%s: mip level %i does not contain the correct amount of data\n", fname, miplevel); + break; + } + + mips->mip[miplevel].width = w; + mips->mip[miplevel].height = h; + mips->mip[miplevel].datasize = 4*w*h; + mips->mip[miplevel].data = tmpmem = BZ_Malloc(4*w*h); + mips->mip[miplevel].needfree = true; //load the rgb data first (8-bit paletted) for (i = 0; i < w*h; i++) @@ -2538,29 +2560,20 @@ texid_tf GL_ReadBLPFile(const char *iname, unsigned char *buffer, int filesize, tmpmem[i] = (tmpmem[i] & 0xffffff) | (*in++<<24); break; } - qglTexImage2D(GL_TEXTURE_2D, miplevel, GL_RGBA, w, h, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, tmpmem); + + miplevel++; + if (!blp->hasmips || (flags & IF_NOMIPMAP)) + break; + w = w>>1; + h = h>>1; } + BZ_Free(filedata); + mips->mipcount = miplevel; + } - miplevel++; - if ((w <= 1 && h <= 1) || !blp->hasmips) - break; - w = (w+1)>>1; - h = (h+1)>>1; - } - if (tmpmem) - free(tmpmem); + COM_AddWork(0, Image_LoadTextureMips, tex, mips, 0, 0); - if (miplevel>1) - { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - return texnum; + return true; } #endif @@ -2730,33 +2743,702 @@ static struct {2, "override/%s%s", 1} /*tenebrae compatibility*/ }; -int image_width, image_height; -qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, const char *iname, char *fname, qbyte *filedata, int filesize) +static void Image_MipMap (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) +{ + int i, j; + qbyte *inrow; + + int rowwidth = inwidth*4; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + out[1] = (in[1] + in[5])>>1; + out[2] = (in[2] + in[6])>>1; + out[3] = (in[3] + in[7])>>1; + } + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + out[1] = (in[1] + in[rowwidth+1])>>1; + out[2] = (in[2] + in[rowwidth+2])>>1; + out[3] = (in[3] + in[rowwidth+3])>>1; + } + } + } + else + { + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; + out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; + out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; + } + } + } +} + +static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int flags) +{ + int mip; + + if (mips->type != PTI_2D) + return; //blurgh + + switch(mips->encoding) + { + case PTI_RGBA8: + case PTI_RGBX8: + case PTI_BGRA8: + case PTI_BGRX8: + break; + default: + return; //not supported. + } + + if (flags & IF_NOMIPMAP) + return; + + for (mip = 1; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } +} + +//stolen from DP +static void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth) +{ + int j, xi, oldx = 0, f, fstep, endx, lerp; + fstep = (int) (inwidth*65536.0f/outwidth); + endx = (inwidth-1); + for (j = 0,f = 0;j < outwidth;j++, f += fstep) + { + xi = f >> 16; + if (xi != oldx) + { + in += (xi - oldx) * 4; + oldx = xi; + } + if (xi < endx) + { + lerp = f & 0xFFFF; + *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]); + *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]); + *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]); + *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]); + } + else // last pixel of the line has no pixel to lerp to + { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + *out++ = in[3]; + } + } +} + +//yes, this is lordhavok's code too. +//superblur away! +#define LERPBYTE(i) r = row1[i];out[i] = (qbyte) ((((row2[i] - r) * lerp) >> 16) + r) +static void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight) +{ + int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4; + qbyte *out; + const qbyte *inrow; + qbyte *tmem, *row1, *row2; + + tmem = row1 = BZ_Malloc(2*(outwidth*4)); + row2 = row1 + (outwidth * 4); + + out = outdata; + fstep = (int) (inheight*65536.0f/outheight); + + inrow = indata; + oldy = 0; + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); + for (i = 0, f = 0;i < outheight;i++,f += fstep) + { + yi = f >> 16; + if (yi < endy) + { + lerp = f & 0xFFFF; + if (yi != oldy) + { + inrow = (qbyte *)indata + inwidth4*yi; + if (yi == oldy+1) + memcpy(row1, row2, outwidth4); + else + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); + oldy = yi; + } + j = outwidth - 4; + while(j >= 0) + { + LERPBYTE( 0); + LERPBYTE( 1); + LERPBYTE( 2); + LERPBYTE( 3); + LERPBYTE( 4); + LERPBYTE( 5); + LERPBYTE( 6); + LERPBYTE( 7); + LERPBYTE( 8); + LERPBYTE( 9); + LERPBYTE(10); + LERPBYTE(11); + LERPBYTE(12); + LERPBYTE(13); + LERPBYTE(14); + LERPBYTE(15); + out += 16; + row1 += 16; + row2 += 16; + j -= 4; + } + if (j & 2) + { + LERPBYTE( 0); + LERPBYTE( 1); + LERPBYTE( 2); + LERPBYTE( 3); + LERPBYTE( 4); + LERPBYTE( 5); + LERPBYTE( 6); + LERPBYTE( 7); + out += 8; + row1 += 8; + row2 += 8; + } + if (j & 1) + { + LERPBYTE( 0); + LERPBYTE( 1); + LERPBYTE( 2); + LERPBYTE( 3); + out += 4; + row1 += 4; + row2 += 4; + } + row1 -= outwidth4; + row2 -= outwidth4; + } + else + { + if (yi != oldy) + { + inrow = (qbyte *)indata + inwidth4*yi; + if (yi == oldy+1) + memcpy(row1, row2, outwidth4); + else + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + oldy = yi; + } + memcpy(out, row1, outwidth4); + } + } + BZ_Free(tmem); +} + + +/* +================ +GL_ResampleTexture +================ +*/ +void Image_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) +{ + int i, j; + unsigned *inrow; + unsigned frac, fracstep; + + if (gl_lerpimages.ival) + { + Image_Resample32Lerp(in, inwidth, inheight, out, outwidth, outheight); + return; + } + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i>16]; + } + for ( ; j>=4 ;) + { + j-=4; + frac -= fracstep; + out[j+3] = inrow[frac>>16]; + frac -= fracstep; + out[j+2] = inrow[frac>>16]; + frac -= fracstep; + out[j+1] = inrow[frac>>16]; + frac -= fracstep; + out[j+0] = inrow[frac>>16]; + } + } +} + +//ripped from tenebrae +static unsigned int * Image_GenerateNormalMap(qbyte *pixels, unsigned int *nmap, int w, int h, float scale) +{ + int i, j, wr, hr; + unsigned char r, g, b; + float sqlen, reciplen, nx, ny, nz; + + const float oneOver255 = 1.0f/255.0f; + + float c, cx, cy, dcx, dcy; + + wr = w; + hr = h; + + for (i=0; i Added support for big endian. + } + } + + return &nmap[0]; +} + +static void Image_RoundDimensions(int *scaled_width, int *scaled_height, unsigned int flags) +{ + if (r_config.texture_non_power_of_two) //NPOT is a simple extension that relaxes errors. + { + //lax form + TRACE(("dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\n")); + } + else if ((flags & IF_CLAMP) && (flags & IF_NOMIPMAP) && r_config.texture_non_power_of_two_pic) + { + //more strict form + TRACE(("dbg: GL_RoundDimensions: GL_OES_texture_npot\n")); + } + else + { + int width = *scaled_width; + int height = *scaled_height; + for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1) + ; + for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1) + ; + + /*round npot textures down if we're running on an embedded system*/ + if (r_config.npot_rounddown) + { + if (*scaled_width != width) + *scaled_width >>= 1; + if (*scaled_height != height) + *scaled_height >>= 1; + } + } + + if (flags & IF_NOMIPMAP) + { + *scaled_width >>= gl_picmip2d.ival; + *scaled_height >>= gl_picmip2d.ival; + } + else + { + TRACE(("dbg: GL_RoundDimensions: %f\n", gl_picmip.value)); + *scaled_width >>= gl_picmip.ival; + *scaled_height >>= gl_picmip.ival; + } + + TRACE(("dbg: GL_RoundDimensions: %f\n", gl_max_size.value)); + + if (r_config.maxtexturesize) + { + if (*scaled_width > r_config.maxtexturesize) + *scaled_width = r_config.maxtexturesize; + if (*scaled_height > r_config.maxtexturesize) + *scaled_height = r_config.maxtexturesize; + } + if (!(flags & IF_UIPIC)) + { + if (gl_max_size.value) + { + if (*scaled_width > gl_max_size.value) + *scaled_width = gl_max_size.value; + if (*scaled_height > gl_max_size.value) + *scaled_height = gl_max_size.value; + } + } + + if (*scaled_width < 1) + *scaled_width = 1; + if (*scaled_height < 1) + *scaled_height = 1; +} + +//resamples and depalettes as required +static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flags, void *rawdata, void *palettedata, int imgwidth, int imgheight, uploadfmt_t fmt, qboolean freedata) +{ + unsigned int *rgbadata = rawdata; + int i; + + mips->mip[0].width = imgwidth; + mips->mip[0].height = imgheight; + mips->mipcount = 1; + + switch(fmt) + { + default: + case TF_INVALID: + Con_Printf("R_LoadRawTexture: bad format"); + if (freedata) + BZ_Free(rawdata); + return false; + case TF_RGBX32: + mips->encoding = PTI_RGBX8; + break; + case TF_RGBA32: + mips->encoding = PTI_RGBA8; + break; + case TF_BGRX32: + mips->encoding = PTI_BGRX8; + break; + case TF_BGRA32: + mips->encoding = PTI_BGRA8; + break; + case TF_SOLID8: + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + rgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_H2_TRANS8_0: + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + if (((qbyte*)rawdata)[i] == 0) + { + rgbadata[i] = 0; + mips->encoding = PTI_RGBA8; + } + else + rgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_TRANS8: + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + if (((qbyte*)rawdata)[i] == 0xff) + { + rgbadata[i] = 0; + mips->encoding = PTI_RGBA8; + } + else + rgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_TRANS8_FULLBRIGHT: + mips->encoding = PTI_RGBA8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + if (((qbyte*)rawdata)[i] < 255-vid.fullbright) + rgbadata[i] = 0; + else + rgbadata[i] = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + + case TF_HEIGHT8PAL: + mips->encoding = PTI_RGBA8; + rgbadata = BZ_Malloc(imgwidth * imgheight*5); + { + qbyte *heights = (qbyte*)(rgbadata + (imgwidth*imgheight)); + for (i = 0; i < imgwidth * imgheight; i++) + { + unsigned int rgb = d_8to24rgbtable[((qbyte*)rawdata)[i]]; + heights[i] = (((rgb>>16)&0xff) + ((rgb>>8)&0xff) + ((rgb>>0)&0xff))/3; + } + Image_GenerateNormalMap(heights, rgbadata, imgwidth, imgheight, r_shadow_bumpscale_basetexture.value?r_shadow_bumpscale_basetexture.value:4); + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_HEIGHT8: + mips->encoding = PTI_RGBA8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + Image_GenerateNormalMap(rawdata, rgbadata, imgwidth, imgheight, r_shadow_bumpscale_bumpmap.value); + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + + case TF_8PAL24: + if (!palettedata) + { + Con_Printf("TF_8PAL24: no palette"); + if (freedata) + BZ_Free(rawdata); + return false; + } + mips->encoding = PTI_RGBX8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + qbyte *p = ((qbyte*)palettedata) + ((qbyte*)rawdata)[i]*3; + //FIXME: endian + rgbadata[i] = 0xff000000 | (p[0]<<0) | (p[1]<<8) | (p[2]<<16); + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + case TF_8PAL32: + { + Con_Printf("TF_8PAL24: no palette"); + if (freedata) + BZ_Free(rawdata); + return false; + } + mips->encoding = PTI_RGBA8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + rgbadata[i] = ((unsigned int*)palettedata)[((qbyte*)rawdata)[i]]; + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; + + case TF_H2_T7G1: /*8bit data, odd indexes give greyscale transparence*/ + mips->encoding = PTI_RGBA8; + rgbadata = BZ_Malloc(imgwidth * imgheight*4); + for (i = 0; i < imgwidth * imgheight; i++) + { + qbyte p = ((qbyte*)rawdata)[i]; + rgbadata[i] = d_8to24rgbtable[p] & 0x00ffffff; + if (p == 0) + ; + else if (p&1) + rgbadata[i] |= 0x80000000; + else + rgbadata[i] |= 0xff000000; + } + if (freedata) + BZ_Free(rawdata); + freedata = true; + break; +// case TF_H2_T4A4: /*8bit data, weird packing*/ +// break; + } + + if (flags & IF_NOALPHA) + { + switch(mips->encoding) + { + case PTI_RGBA8: + mips->encoding = PTI_RGBX8; + break; + case PTI_BGRA8: + mips->encoding = PTI_RGBX8; + break; + case PTI_S3RGBA1: //mostly compatible, but I don't want to push it. + case PTI_S3RGBA3: + case PTI_S3RGBA5: + //erk. meh. + break; + case PTI_RGBX8: + case PTI_BGRX8: + case PTI_S3RGB1: + break; + } + //FIXME: fill alpha channel with 255? + } + + + Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags); + if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight) + mips->mip[0].data = rgbadata; + else + { + mips->mip[0].data = BZ_Malloc(((mips->mip[0].width+3)&~3)*mips->mip[0].height*4); +// memset(mips->mip[0].data, 0, mips->mip[0].width*mips->mip[0].height*4); + Image_ResampleTexture(rgbadata, imgwidth, imgheight, mips->mip[0].data, mips->mip[0].width, mips->mip[0].height); + if (freedata) + BZ_Free(rgbadata); + freedata = true; + } + mips->mip[0].datasize = mips->mip[0].width*mips->mip[0].height*4; + + if (mips->type == PTI_3D) + { + qbyte *data2d = mips->mip[0].data, *data3d; + mips->mip[0].data = NULL; + /*our 2d input image is interlaced as y0z0,y0z1,y1z0,y1z1 + however, hardware uses the more logical y0z0,y1z0,y0z1,y1z1 ordering (xis ordered properly already)*/ + if (mips->mip[0].height*mips->mip[0].height == mips->mip[0].width && (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_RGBX8 || mips->encoding == PTI_BGRA8 || mips->encoding == PTI_BGRX8)) + { + int d, r; + int size = mips->mip[0].height; + mips->mip[0].data = data3d = BZ_Malloc(size*size*size); + for (d = 0; d < size; d++) + for (r = 0; r < size; r++) + memcpy(data3d + (r + d*size) * size, data2d + (r*size + d) * size, size*4); + mips->mip[0].datasize = size*size*size*4; + } + if (freedata) + BZ_Free(data2d); + if (!mips->mip[0].data) + return false; + } + + if (flags & IF_PREMULTIPLYALPHA) + { + //works for rgba or bgra + int i; + unsigned char *fte_restrict premul = (unsigned char*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) + { + premul[0] = (premul[0] * premul[3])>>8; + premul[1] = (premul[1] * premul[3])>>8; + premul[2] = (premul[2] * premul[3])>>8; + } + } + + mips->mip[0].needfree = freedata; + return true; +} +//loads from a single mip. takes ownership of the data. +static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawdata, int imgwidth, int imgheight, uploadfmt_t fmt) +{ + struct pendingtextureinfo *mips; + mips = Z_Malloc(sizeof(*mips)); + mips->type = (flags & IF_3DMAP)?PTI_3D:PTI_2D; + + if (!Image_GenMip0(mips, flags, rawdata, NULL, imgwidth, imgheight, fmt, true)) + { + Z_Free(mips); + return false; + } + Image_GenerateMips(mips, flags); + + tex->width = imgwidth; + tex->height = imgheight; + + if (flags & IF_NOWORKER) + Image_LoadTextureMips(tex, mips, 0, 0); + else + COM_AddWork(0, Image_LoadTextureMips, tex, mips, 0, 0); + return true; +} + +//always frees filedata, even on failure. +//also frees the textures fallback data, but only on success +qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, char *fname, qbyte *filedata, int filesize) { qboolean hasalpha; qbyte *rgbadata; + int imgwidth, imgheight; + //these formats have special handling, because they cannot be implemented via Read32BitImageFile - they don't result in rgba images. #ifdef IMAGEFMT_DDS - *tex = GL_ReadTextureDDS(iname, filedata, filesize); - if (TEXVALID(*tex)) + if (Image_ReadDDSFile(tex, flags, fname, filedata, filesize)) return true; #endif #ifdef IMAGEFMT_BLP if (filedata[0] == 'B' && filedata[1] == 'L' && filedata[2] == 'P' && filedata[3] == '2') { - *tex = GL_ReadBLPFile(iname, filedata, filesize, &image_width, &image_height); - if (TEXVALID(*tex)) + if (Image_ReadBLPFile(tex, flags, fname, filedata, filesize)) return true; } #endif hasalpha = false; - if ((rgbadata = Read32BitImageFile(filedata, filesize, &image_width, &image_height, &hasalpha, fname))) + if ((rgbadata = Read32BitImageFile(filedata, filesize, &imgwidth, &imgheight, &hasalpha, fname))) { extern cvar_t vid_hardwaregamma; if (!(flags&IF_NOGAMMA) && !vid_hardwaregamma.value) - BoostGamma(rgbadata, image_width, image_height); + BoostGamma(rgbadata, imgwidth, imgheight); if (hasalpha) flags &= ~IF_NOALPHA; @@ -2766,14 +3448,17 @@ qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, const char *iname, cha char aname[MAX_QPATH]; unsigned char *alphadata; char *alph; + size_t alphsize; + char ext[8]; COM_StripExtension(fname, aname, sizeof(aname)); + COM_FileExtension(fname, ext, sizeof(ext)); Q_strncatz(aname, "_alpha.", sizeof(aname)); - Q_strncatz(aname, COM_FileExtension(fname), sizeof(aname)); - if ((alph = COM_LoadFile (aname, 5))) + Q_strncatz(aname, ext, sizeof(aname)); + if ((alph = COM_LoadFile (aname, 5, &alphsize))) { - if ((alphadata = Read32BitImageFile(alph, com_filesize, &alpha_width, &alpha_height, &hasalpha, aname))) + if ((alphadata = Read32BitImageFile(alph, alphsize, &alpha_width, &alpha_height, &hasalpha, aname))) { - if (alpha_width == image_width && alpha_height == image_height) + if (alpha_width == imgwidth && alpha_height == imgheight) { for (p = 0; p < alpha_width*alpha_height; p++) { @@ -2786,247 +3471,626 @@ qboolean R_LoadTextureFromMemory(texid_t *tex, int flags, const char *iname, cha } } - TRACE(("dbg: Mod_LoadHiResTexture: %s loaded\n", iname)); - *tex = R_LoadTexture32 (iname, image_width, image_height, rgbadata, flags); + if (Image_LoadRawTexture(tex, flags, rgbadata, imgwidth, imgheight, TF_RGBA32)) + { + BZ_Free(filedata); + //and kill the fallback now that its loaded, as it won't be needed any more. + BZ_Free(tex->fallbackdata); + tex->fallbackdata = NULL; + return true; + } BZ_Free(rgbadata); - - return true; } else - Con_Printf("Unable to read file %s (format unsupported)\n", fname); + Sys_Printf("Unable to read file %s (format unsupported)\n", fname); + + BZ_Free(filedata); return false; } -texid_t R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags) + +//loads from a single mip. takes ownership of the data. +static qboolean Image_LoadCubemapTexture(texid_t tex, char *nicename) { - qboolean alphaed; - char *buf; - unsigned char *data; - texid_t tex; -// int h; - char fname[MAX_QPATH], nicename[MAX_QPATH], iname[MAX_QPATH]; + static struct + { + char *suffix; + qboolean flipx, flipy, flipd; + } cmscheme[] = + { + {"rt", true, false, true}, + {"lf", false, true, true}, + {"ft", true, true, false}, + {"bk", false, false, false}, + {"up", true, false, true}, + {"dn", true, false, true}, + + {"px", false, false, false}, + {"nx", false, false, false}, + {"py", false, false, false}, + {"ny", false, false, false}, + {"pz", false, false, false}, + {"nz", false, false, false}, + + {"posx", false, false, false}, + {"negx", false, false, false}, + {"posy", false, false, false}, + {"negy", false, false, false}, + {"posz", false, false, false}, + {"negz", false, false, false} + }; + int i, j, e; + struct pendingtextureinfo *mips; + char fname[MAX_QPATH]; + size_t filesize; + int width, height; qboolean hasalpha; + mips = Z_Malloc(sizeof(*mips)); + mips->type = PTI_CUBEMAP; + mips->mipcount = 6; + mips->encoding = PTI_RGBA8; + mips->extrafree = NULL; - int i, e; - - if (!*name) - return r_nulltex; - - image_width = 0; - image_height = 0; - - if (flags & IF_EXACTEXTENSION) - Q_strncpyz(nicename, name, sizeof(nicename)); - else - COM_StripExtension(name, nicename, sizeof(nicename)); - - while((data = strchr(nicename, '*'))) + for (i = 0; i < 6; i++) { - *data = '#'; - } - - if (subpath) - { - snprintf(fname, sizeof(fname)-1, "%s/%s", subpath, name); /*should be safe if its null*/ - if (*subpath && !(flags & IF_REPLACE)) + for (e = (tex->flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++) { - tex = R_FindTexture(fname, flags); - if (TEXVALID(tex)) //don't bother if it already exists. + //try and open one + qbyte *buf = NULL, *data; + filesize = 0; + for (j = 0; j < sizeof(cmscheme)/sizeof(cmscheme[0])/6; j++) { - image_width = tex.ref->width; - image_height = tex.ref->height; - return tex; + Q_snprintfz(fname, sizeof(fname), "%s%s%s", nicename, cmscheme[i + 6*j].suffix, tex_extensions[e].name); + buf = COM_LoadFile(fname, 5, &filesize); + if (buf) + break; + } + + //now read it + if (buf) + { + if ((data = Read32BitImageFile(buf, filesize, &width, &height, &hasalpha, fname))) + { + extern cvar_t vid_hardwaregamma; + if (!(tex->flags&IF_NOGAMMA) && !vid_hardwaregamma.value) + BoostGamma(data, width, height); + mips->mip[i].data = R_FlipImage32(data, &width, &height, cmscheme[i + 6*j].flipx, cmscheme[i + 6*j].flipy, cmscheme[i + 6*j].flipd); + mips->mip[i].datasize = width*height*4; + mips->mip[i].width = width; + mips->mip[i].height = height; + mips->mip[i].needfree = true; + + if (i == 0) + { + tex->width = width; + tex->height = height; + } + BZ_Free(buf); + break; + } + BZ_Free(buf); } } } - if (!(flags & IF_SUBDIRONLY) && !(flags & IF_REPLACE)) - { - tex = R_FindTexture(name, flags); - if (TEXVALID(tex)) //don't bother if it already exists. + + if (tex->flags & IF_NOWORKER) + Image_LoadTextureMips(tex, mips, 0, 0); + else + COM_AddWork(0, Image_LoadTextureMips, tex, mips, 0, 0); + return true; +} + +void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t b) +{ + image_t *tex = ctx; + char fname[MAX_QPATH], iname[MAX_QPATH], nicename[MAX_QPATH]; + int i, e; + char *buf; + size_t fsize; + int firstex = (tex->flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; + +// Sys_Sleep(0.3); + + //see if we recognise the extension, and only strip it if we do. + COM_FileExtension(tex->ident, nicename, sizeof(nicename)); + e = 0; + if (strcmp(nicename, "lmp")) + for (; e < tex_extensions_count; e++) { - image_width = tex.ref->width; - image_height = tex.ref->height; - return tex; + if (!strcmp(nicename, (*tex_extensions[e].name=='.')?tex_extensions[e].name+1:tex_extensions[e].name)) + break; } + + //strip it and try replacements if we do, otherwise assume that we're meant to be loading progs/foo.mdl_0.tga or whatever + if (e == tex_extensions_count || (tex->flags & IF_EXACTEXTENSION)) + Q_strncpyz(nicename, tex->ident, sizeof(nicename)); + else + COM_StripExtension(tex->ident, nicename, sizeof(nicename)); + + if ((tex->flags & IF_TEXTYPE) == IF_CUBEMAP) + { //cubemaps require special handling because they are (normally) 6 files instead of 1. + //the exception is single-file dds cubemaps, but we don't support those. + if (!Image_LoadCubemapTexture(tex, nicename)) + { + if (tex->flags & IF_NOWORKER) + Image_LoadTexture_Failed(tex, NULL, 0, 0); + else + COM_AddWork(0, Image_LoadTexture_Failed, tex, NULL, 0, 0); + } + return; } - //cubemaps need special all-at-once handling or something, and is not individual textures. - if ((flags & IF_TEXTYPE) == IF_CUBEMAP) + if (!tex->fallbackdata || (gl_load24bit.ival && !(tex->flags & IF_NOREPLACE))) { - int j; - static struct - { - char *suffix; - qboolean flipx, flipy, flipd; - } cmscheme[] = - { - {"rt", true, false, true}, - {"lf", false, true, true}, - {"ft", true, true, false}, - {"bk", false, false, false}, - {"up", true, false, true}, - {"dn", true, false, true}, + if (strchr(nicename, '/') || strchr(nicename, '\\')) //never look in a root dir for the pic + i = 0; + else + i = 1; - {"px", false, false, false}, - {"nx", false, false, false}, - {"py", false, false, false}, - {"ny", false, false, false}, - {"pz", false, false, false}, - {"nz", false, false, false}, - - {"posx", false, false, false}, - {"negx", false, false, false}, - {"posy", false, false, false}, - {"negy", false, false, false}, - {"posz", false, false, false}, - {"negz", false, false, false} - }; - flags |= IF_REPLACE; - - tex = r_nulltex; - for (i = 0; i < 6; i++) + for (; i < sizeof(tex_path)/sizeof(tex_path[0]); i++) { - tex = r_nulltex; - for (e = (flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++) - { - buf = NULL; - for (j = 0; j < sizeof(cmscheme)/sizeof(cmscheme[0])/6; j++) + if (!tex_path[i].enabled) + continue; + if (tex_path[i].args >= 3) + { //this is a path that needs subpaths + char subpath[MAX_QPATH]; + char basename[MAX_QPATH]; + char *s, *n; + if (!tex->subpath || !*nicename) + continue; + + s = COM_SkipPath(nicename); + n = basename; + while (*s && *s != '.' && n < basename+sizeof(basename)-5) + *n++ = *s++; + s = strchr(s, '_'); + if (s) { - snprintf(fname, sizeof(fname)-1, "%s%s%s", nicename, cmscheme[i + 6*j].suffix, tex_extensions[e].name); - buf = COM_LoadFile (fname, 5); - if (buf) - break; + while (*s && n < basename+sizeof(basename)-5) + *n++ = *s++; } + *n = 0; - if (buf) + for(s = tex->subpath; s; s = n) { - hasalpha = false; - if ((data = Read32BitImageFile(buf, com_filesize, &image_width, &image_height, &hasalpha, fname))) + //subpath a:b:c tries multiple possible sub paths, for compatibility + n = strchr(s, ':'); + if (n) { - extern cvar_t vid_hardwaregamma; - if (!(flags&IF_NOGAMMA) && !vid_hardwaregamma.value) - BoostGamma(data, image_width, image_height); - data = R_FlipImage32(data, &image_width, &image_height, cmscheme[i + 6*j].flipx, cmscheme[i + 6*j].flipy, cmscheme[i + 6*j].flipd); - tex = R_LoadTexture32 (name, image_width, image_height, data, (flags | IF_REPLACE) + (i << IF_TEXTYPESHIFT)); - - BZ_Free(data); - BZ_Free(buf); - if (TEXVALID(tex)) - break; + if (n-s >= sizeof(subpath)) + *subpath = 0; + else + { + memcpy(subpath, s, n-s); + subpath[n-s] = 0; + } + n++; + } + else + Q_strncpyz(subpath, s, sizeof(subpath)); + for (e = firstex; e < tex_extensions_count; e++) + { + if (tex->flags & IF_NOPCX) + if (!strcmp(tex_extensions[e].name, ".pcx")) + continue; + Q_snprintfz(fname, sizeof(fname), tex_path[i].path, subpath, basename, tex_extensions[e].name); + if ((buf = COM_LoadFile (fname, 5, &fsize))) + { + Q_snprintfz(iname, sizeof(iname), "%s/%s", subpath, nicename); /*should be safe if its null*/ + if (Image_LoadTextureFromMemory(tex, tex->flags, iname, fname, buf, fsize)) + return; + } } } } - if (!TEXVALID(tex)) - return r_nulltex; - } - return tex; - } - - - if (subpath && *subpath) - { - tex = R_LoadCompressed(fname); - if (TEXVALID(tex)) - return tex; - } - if (!(flags & IF_SUBDIRONLY)) - { - tex = R_LoadCompressed(name); - if (TEXVALID(tex)) - return tex; - } - -#ifdef IMAGEFMT_DDS - snprintf(fname, sizeof(fname)-1, "dds/%s.dds", nicename); /*should be safe if its null*/ - if ((buf = COM_LoadFile (fname, 5))) - { - tex = GL_ReadTextureDDS(name, buf, com_filesize); - if (TEXVALID(tex)) - { - BZ_Free(buf); - return tex; - } - Con_Printf("%s is not a dds file\n", fname); - BZ_Free(buf); - } -#endif - - if (strchr(name, '/') || strchr(name, '\\')) //never look in a root dir for the pic - i = 0; - else - i = 1; - - //should write this nicer. - for (; i < sizeof(tex_path)/sizeof(tex_path[0]); i++) - { - if (!tex_path[i].enabled) - continue; - for (e = (flags & IF_EXACTEXTENSION)?tex_extensions_count-1:0; e < tex_extensions_count; e++) - { - if (tex_path[i].args >= 3) - { - if (!subpath) - continue; - snprintf(fname, sizeof(fname)-1, tex_path[i].path, subpath, nicename, tex_extensions[e].name); - } else { - if (flags & IF_SUBDIRONLY) - continue; - snprintf(fname, sizeof(fname)-1, tex_path[i].path, nicename, tex_extensions[e].name); + for (e = firstex; e < tex_extensions_count; e++) + { + if (tex->flags & IF_NOPCX) + if (!strcmp(tex_extensions[e].name, ".pcx")) + continue; + Q_snprintfz(fname, sizeof(fname), tex_path[i].path, nicename, tex_extensions[e].name); + if ((buf = COM_LoadFile (fname, 5, &fsize))) + if (Image_LoadTextureFromMemory(tex, tex->flags, nicename, fname, buf, fsize)) + return; + } } - TRACE(("dbg: Mod_LoadHiResTexture: trying %s\n", fname)); - if ((buf = COM_LoadFile (fname, 5))) + + //support expansion of _bump textures to _norm textures. + if (tex->flags & IF_TRYBUMP) { if (tex_path[i].args >= 3) - snprintf(iname, sizeof(iname)-1, "%s/%s", subpath, name); /*should be safe if its null*/ - else - snprintf(iname, sizeof(iname)-1, "%s", name); /*should be safe if its null*/ - - if (R_LoadTextureFromMemory(&tex, flags, iname, fname, buf, com_filesize)) { - BZ_Free(buf); - return tex; + /*no legacy compat needed, hopefully*/ + } + else + { + char bumpname[MAX_QPATH], *n, *b; + b = bumpname; + n = nicename; + while(*n) + { + if (*n == '_' && !strcmp(n, "_norm")) + { + strcpy(b, "_bump"); + b += 5; + n += 5; + break; + } + *b++ = *n++; + } + if (*n) //no _norm, give up with that + continue; + *b = 0; + for (e = firstex; e < tex_extensions_count; e++) + { + if (!strcmp(tex_extensions[e].name, ".tga")) + { + Q_snprintfz(fname, sizeof(fname), tex_path[i].path, bumpname, tex_extensions[e].name); + if ((buf = COM_LoadFile (fname, 5, &fsize))) + { + int w, h; + qboolean a; + qbyte *d; + if ((d = ReadTargaFile(buf, fsize, &w, &h, &a, 2))) //Only load a greyscale image. + { + BZ_Free(buf); + if (Image_LoadRawTexture(tex, tex->flags, d, w, h, TF_HEIGHT8)) + { + BZ_Free(tex->fallbackdata); + tex->fallbackdata = NULL; + return; + } + } + else + BZ_Free(buf); + } + } + } + } + } + } + + + /*still failed? attempt to load quake lmp files, which have no real format id (hence why they're not above)*/ + Q_strncpyz(fname, nicename, sizeof(fname)); + COM_DefaultExtension(fname, ".lmp", sizeof(fname)); + if ((buf = COM_LoadFile (fname, 5, &fsize))) + { + if (Image_LoadTextureFromMemory(tex, tex->flags, nicename, fname, buf, fsize)) + return; + } + else + { + int imgwidth; + int imgheight; + qboolean alphaed; + //now look in wad files. (halflife compatability) + buf = W_GetTexture(nicename, &imgwidth, &imgheight, &alphaed); + if (buf) + { + if (Image_LoadRawTexture(tex, tex->flags, buf, imgwidth, imgheight, TF_RGBA32)) + { + BZ_Free(tex->fallbackdata); + tex->fallbackdata = NULL; + return; } BZ_Free(buf); } } } - if (!(flags & IF_SUBDIRONLY)) + if (tex->fallbackdata) { - /*still failed? attempt to load quake lmp files, which have no real format id*/ - Q_strncpyz(fname, name, sizeof(fname)); - COM_DefaultExtension(fname, ".lmp", sizeof(fname)); - if ((buf = COM_LoadFile (fname, 5))) + if (Image_LoadRawTexture(tex, tex->flags, tex->fallbackdata, tex->fallbackwidth, tex->fallbackheight, tex->fallbackfmt)) { - if (R_LoadTextureFromMemory(&tex, flags, name, fname, buf, com_filesize)) - { - BZ_Free(buf); - return tex; - } - BZ_Free(buf); - return r_nulltex; + tex->fallbackdata = NULL; + return; } + BZ_Free(tex->fallbackdata); + tex->fallbackdata = NULL; + } - //now look in wad files. (halflife compatability) - data = W_GetTexture(name, &image_width, &image_height, &alphaed); - if (data) +// Sys_Printf("Texture %s failed\n", nicename); + //signal the main thread to set the final status instead of just setting it to avoid deadlock (it might already be waiting for it). + if (tex->flags & IF_NOWORKER) + Image_LoadTexture_Failed(tex, NULL, 0, 0); + else + COM_AddWork(0, Image_LoadTexture_Failed, tex, NULL, 0, 0); +} + + + + +//find an existing texture +image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned int flags) +{ + image_t *tex; + tex = Hash_Get(&imagetable, identifier); + while(tex) + { + if ((tex->flags ^ flags) & IF_CLAMP) { - tex = R_LoadTexture32 (name, image_width, image_height, (unsigned*)data, flags); - BZ_Free(data); - return tex; + tex = Hash_GetNext(&imagetable, identifier, tex); + continue; + } + return tex; + } + return NULL; +} +//create a texture, with dupes. you'll need to load something into it too. +static image_t *Image_CreateTexture_Internal (const char *identifier, const char *subdir, unsigned int flags) +{ + image_t *tex; + bucket_t *buck; + + tex = Z_Malloc(sizeof(*tex) + sizeof(bucket_t) + strlen(identifier)+1 + (subdir?strlen(subdir)+1:0)); + buck = (bucket_t*)(tex+1); + tex->ident = (char*)(buck+1); + strcpy(tex->ident, identifier); + if (subdir && *subdir) + { + tex->subpath = tex->ident + strlen(identifier)+1; + strcpy(tex->subpath, subdir); + } + + tex->next = imagelist; + imagelist = tex; + + tex->flags = flags; + tex->width = 0; + tex->height = 0; + tex->regsequence = r_regsequence; + tex->status = TEX_NOTLOADED; + tex->fallbackdata = NULL; + tex->fallbackwidth = 0; + tex->fallbackheight = 0; + tex->fallbackfmt = TF_INVALID; + if (*tex->ident) + Hash_Add(&imagetable, tex->ident, tex, buck); + return tex; +} + +image_t *Image_CreateTexture (const char *identifier, const char *subdir, unsigned int flags) +{ + image_t *image; +#ifdef LOADERTHREAD + Sys_LockMutex(com_resourcemutex); +#endif + image = Image_CreateTexture_Internal(identifier, subdir, flags); +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); +#endif + return image; +} + +//find a texture. will try to load it from disk, using the fallback if it would fail. +image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt) +{ + image_t *tex; +#ifdef LOADERTHREAD + Sys_LockMutex(com_resourcemutex); +#endif + tex = Image_FindTexture(identifier, subpath, flags); + if (tex) + { +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); +#endif + return tex; //already exists + } + + tex = Image_CreateTexture_Internal(identifier, subpath, flags); + + tex->status = TEX_LOADING; + if (fallbackdata) + { + int b, pb = 0; + switch(fallbackfmt) + { + case TF_8PAL24: + pb = 3*256; + b = 1; + break; + case TF_8PAL32: + pb = 4*256; + b = 1; + break; + case TF_SOLID8: + case TF_TRANS8: + case TF_TRANS8_FULLBRIGHT: + case TF_H2_T7G1: + case TF_H2_TRANS8_0: + case TF_H2_T4A4: + case TF_HEIGHT8: + case TF_HEIGHT8PAL: //we don't care about the actual palette. + b = 1; + break; + case TF_RGBX32: + case TF_RGBA32: + case TF_BGRX32: + case TF_BGRA32: + b = 4; + break; + default: + Sys_Error("GL_FindTextureFallback: bad format"); + } + tex->fallbackdata = BZ_Malloc(fallbackwidth*fallbackheight*b + pb); + memcpy(tex->fallbackdata, fallbackdata, fallbackwidth*fallbackheight*b); + if (pb) + memcpy((qbyte*)tex->fallbackdata + fallbackwidth*fallbackheight*b, fallbackpalette, pb); + tex->fallbackwidth = fallbackwidth; + tex->fallbackheight = fallbackheight; + tex->fallbackfmt = fallbackfmt; + } + else + { + tex->fallbackdata = NULL; + tex->fallbackwidth = 0; + tex->fallbackheight = 0; + tex->fallbackfmt = TF_INVALID; + } +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); +#endif + //FIXME: pass fallback through this way instead? + + if (flags & IF_NOWORKER) + Image_LoadHiResTextureWorker(tex, NULL, 0, 0); + else + COM_AddWork(1, Image_LoadHiResTextureWorker, tex, NULL, 0, 0); + return tex; +} +void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) +{ + struct pendingtextureinfo mips; + + mips.extrafree = NULL; + mips.type = (flags & IF_3DMAP)?PTI_3D:PTI_2D; + if (!Image_GenMip0(&mips, flags, data, palette, width, height, fmt, false)) + return; + rf->IMG_LoadTextureMips(tex, &mips); + tex->status = TEX_LOADED; +} + +typedef struct +{ + char *name; + char *legacyname; + int minimize, minmip, maximize; +} texmode_t; +static texmode_t texmodes[] = { + {"n", "GL_NEAREST", 0, -1, 0}, + {"l", "GL_LINEAR", 1, -1, 1}, + {"nn", "GL_NEAREST_MIPMAP_NEAREST", 0, 0, 0}, + {"ln", "GL_LINEAR_MIPMAP_NEAREST", 1, 0, 1}, + {"nl", "GL_NEAREST_MIPMAP_LINEAR", 0, 1, 0}, + {"ll", "GL_LINEAR_MIPMAP_LINEAR", 1, 1, 1}, + + //more explicit names + {"n.n", NULL, 0, -1, 0}, + {"l.l", NULL, 1, -1, 1}, + {"nnn", NULL, 0, 0, 0}, + {"lnl", NULL, 1, 0, 1}, + {"nln", NULL, 0, 1, 0}, + {"lll", NULL, 1, 1, 1}, + + //inverted mag filters + {"n.l", NULL, 0, -1, 1}, + {"l.n", NULL, 1, -1, 0}, + {"nnl", NULL, 0, 0, 1}, + {"lnn", NULL, 1, 0, 0}, + {"nll", NULL, 0, 1, 1}, + {"lln", NULL, 1, 1, 0} +}; +static void Image_ParseTextureMode(char *cvarname, char *modename, int modes[3]) +{ + int i; + modes[0] = 1; + modes[1] = 0; + modes[2] = 1; + for (i = 0; i < sizeof(texmodes) / sizeof(texmodes[0]); i++) + { + if (!Q_strcasecmp(modename, texmodes[i].name) || (texmodes[i].legacyname && !Q_strcasecmp(modename, texmodes[i].legacyname))) + { + modes[0] = texmodes[i].minimize; + modes[1] = texmodes[i].minmip; + modes[2] = texmodes[i].maximize; + return; } } - return r_nulltex; + Con_Printf("%s: mode %s was not recognised\n", cvarname, modename); } -texid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags) +void Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue) { - if (!gl_load24bit.value) - return r_nulltex; - return R_LoadHiResTexture(name, subpath, flags); + int mip[3]={1,0,1}, pic[3]={1,-1,1}, mipcap[2] = {0, 1000}; + float anis = 1; + char *s; + extern cvar_t gl_texturemode, gl_texturemode2d, gl_texture_anisotropic_filtering, gl_mipcap; + + Image_ParseTextureMode(gl_texturemode.name, gl_texturemode.string, mip); + Image_ParseTextureMode(gl_texturemode2d.name, gl_texturemode2d.string, pic); + anis = gl_texture_anisotropic_filtering.value; + //parse d_mipcap (two values, nearest furthest) + s = COM_Parse(gl_mipcap.string); + mipcap[0] = *com_token?atoi(com_token):0; +// if (mipcap[0] > 3) /*cap it to 3, so no 16*16 textures get bugged*/ +// mipcap[0] = 3; + s = COM_Parse(s); + mipcap[1] = *com_token?atoi(com_token):1000; + if (mipcap[1] < mipcap[0]) + mipcap[1] = mipcap[0]; + + if (rf && rf->IMG_UpdateFiltering) + rf->IMG_UpdateFiltering(imagelist, mip, pic, mipcap, anis); +} +//may not create any images yet. +void Image_Init(void) +{ + memset(imagetablebuckets, 0, sizeof(imagetablebuckets)); + Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets); +} +//destroys all textures +void Image_Shutdown(void) +{ + image_t *tex; + int i = 0, j = 0; + while (imagelist) + { + tex = imagelist; + if (*tex->ident) + Hash_RemoveData(&imagetable, tex->ident, tex); + imagelist = tex->next; + if (tex->status == TEX_LOADED) + j++; + rf->IMG_DestroyTexture(tex); + Z_Free(tex); + i++; + } + if (i) + Con_Printf("Destroyed %i/%i images\n", j, i); } +//load the named file, without failing. +texid_t R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags) +{ + char nicename[MAX_QPATH], *data; + if (!*name) + return r_nulltex; + Q_strncpyz(nicename, name, sizeof(nicename)); + while((data = strchr(nicename, '*'))) + *data = '#'; + return Image_GetTexture(nicename, subpath, flags, NULL, NULL, 0, 0, TF_INVALID); //queues the texture creation. +} + +//attempt to load the named texture +//will not load external textures if gl_load24bit is set (failing instantly if its just going to fail later on anyway) +//the specified data will be used if the high-res image is blocked/not found. +texid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags, void *lowres, int lowreswidth, int lowresheight, uploadfmt_t format) +{ + char nicename[MAX_QPATH], *data; + if (!*name) + return r_nulltex; + if (!gl_load24bit.ival && !lowres) + return r_nulltex; + Q_strncpyz(nicename, name, sizeof(nicename)); + while((data = strchr(nicename, '*'))) + *data = '#'; + return Image_GetTexture(nicename, subpath, flags, lowres, NULL, lowreswidth, lowresheight, format); //queues the texture creation. +} +#ifdef RTLIGHTS +void R_LoadNumberedLightTexture(dlight_t *dl, int cubetexnum) +{ + Q_snprintfz(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", cubetexnum); + if (!gl_load24bit.ival) + dl->cubetexture = r_nulltex; + else + dl->cubetexture = Image_GetTexture(dl->cubemapname, NULL, IF_CUBEMAP, NULL, NULL, 0, 0, TF_INVALID); +} +#endif + +#if 0 extern cvar_t r_shadow_bumpscale_bumpmap; texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) { @@ -3051,8 +4115,8 @@ texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) tex = R_FindTexture(name, 0); if (TEXVALID(tex)) //don't bother if it already exists. { - image_width = tex.ref->width; - image_height = tex.ref->height; + image_width = tex->width; + image_height = tex->height; return tex; } @@ -3072,6 +4136,7 @@ texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) continue; for (e = sizeof(extensions)/sizeof(char *)-1; e >=0 ; e--) { + size_t fsize; if (tex_path[i].args >= 3) { if (!subpath) @@ -3083,9 +4148,9 @@ texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) TRACE(("dbg: Mod_LoadBumpmapTexture: opening %s\n", fname)); - if ((buf = COM_LoadFile (fname, 5))) + if ((buf = COM_LoadFile (fname, 5, &fsize))) { - if ((data = ReadTargaFile(buf, com_filesize, &image_width, &image_height, &hasalpha, 2))) //Only load a greyscale image. + if ((data = ReadTargaFile(buf, fsize, &image_width, &image_height, &hasalpha, 2))) //Only load a greyscale image. { TRACE(("dbg: Mod_LoadBumpmapTexture: tga %s loaded\n", name)); TEXASSIGNF(tex, R_LoadTexture8Bump(name, image_width, image_height, data, IF_NOALPHA|IF_NOGAMMA)); @@ -3105,6 +4170,7 @@ texid_t R_LoadBumpmapTexture(const char *name, const char *subpath) } return r_nulltex; } +#endif // ocrana led functions static int ledcolors[8][3] = diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 9abaea42..e66a81c0 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -1613,7 +1613,10 @@ void INS_StartupJoystick (void) } } - Con_Printf ("found %i joysticks\n", joy_count); + if (joy_count) + Con_Printf ("found %i joysticks\n", joy_count); + else + Con_DPrintf ("found no joysticks\n"); } /* diff --git a/engine/client/m_download.c b/engine/client/m_download.c index dcf33363..7e509232 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -609,7 +609,7 @@ static void Menu_Download_Got(struct dl_download *dl) { char *fname = dl->localname; qboolean successful = dl->status == DL_FINISHED; - char *ext; + char ext[8]; package_t *p; int dlnum = atoi(fname+3); @@ -635,7 +635,7 @@ static void Menu_Download_Got(struct dl_download *dl) p->flags &= ~DPF_DOWNLOADING; - ext = COM_FileExtension(p->dest); + COM_FileExtension(p->dest, ext, sizeof(ext)); if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) FS_UnloadPackFiles(); //we reload them after @@ -657,7 +657,6 @@ static void Menu_Download_Got(struct dl_download *dl) WriteInstalledPackages(); - ext = COM_FileExtension(p->dest); if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) FS_ReloadPackFiles(); return; diff --git a/engine/client/m_items.c b/engine/client/m_items.c index b500d45f..97fa480f 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -14,10 +14,10 @@ void Draw_TextBox (int x, int y, int width, int lines) cy = y; p = R2D_SafeCachePic ("gfx/box_tl.lmp"); - if (!p) //assume none exist + if (R_GetShaderSizes(p, NULL, NULL, false) != true) //assume none exist { - R2D_ImageColours(0.0, 0.0, 0.0, 1.0); - R2D_FillBlock(x, y, width*16 + 16, 8 * (2 + lines)); + R2D_ImageColours(0.0, 0.0, 0.0, 0.5); + R2D_FillBlock(x, y, width*8 + 16, 8 * (2 + lines)); R2D_ImageColours(1.0, 1.0, 1.0, 1.0); return; } @@ -94,7 +94,7 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text) { int sx, sy; mpic_t *p; - unsigned int hack; + unsigned int hack; //FIXME: threads can't cope hack = d_8to24rgbtable[0]; d_8to24rgbtable[0] = 0; p = R2D_SafeCachePic ("gfx/menu/bigfont.lmp"); @@ -127,18 +127,20 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text) mpic_t *QBigFontWorks(void) { mpic_t *p; - p = R2D_SafeCachePic ("gfx/mcharset.lmp"); - if (p) - return p; - p = R2D_SafeCachePic ("mcharset.lmp"); - if (p) - return p; - p = R2D_SafeCachePic ("textures/gfx/mcharset.lmp"); - if (p) - return p; - p = R2D_SafeCachePic ("textures/mcharset.lmp"); - if (p) - return p; + int i; + char *names[] = { + "gfx/mcharset.lmp", + "mcharset.lmp", + "textures/gfx/mcharset.lmp", + "textures/mcharset.lmp", + NULL + }; + for (i = 0; names[i]; i++) + { + p = R2D_SafeCachePic (names[i]); + if (p && R_GetShaderSizes(p, NULL, NULL, true)) + return p; + } return NULL; } void Draw_BigFontString(int x, int y, const char *text) @@ -381,9 +383,11 @@ static void M_CheckMouseMove(void) { if (!option->common.noselectionsound) { +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu1.wav"); else +#endif S_LocalSound ("misc/menu1.wav"); } @@ -406,6 +410,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu { int i; mpic_t *p; + int pw,ph; while (option) { @@ -441,7 +446,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu case mt_menudot: i = (int)(realtime * 10)%maxdots; p = R2D_SafeCachePic(va(menudotstyle, i+mindot )); - R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, 20, 20, p); + R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, option->common.width, option->common.height, p); break; case mt_picturesel: p = NULL; @@ -453,14 +458,15 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu Q_strncatz(selname, "_sel", sizeof(selname)); p = R2D_SafeCachePic(selname); } - if (!p) + if (!R_GetShaderSizes(p, &pw, &ph, false)) p = R2D_SafeCachePic(option->picture.picturename); - R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:p->width, option->common.height?option->common.height:p->height, p); + R_GetShaderSizes(p, &pw, &ph, false); + R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:pw, option->common.height?option->common.height:ph, p); break; case mt_picture: p = R2D_SafeCachePic(option->picture.picturename); - if (p) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p); + if (R_GetShaderSizes(p, NULL, NULL, false)>=0) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p); break; case mt_childwindow: MenuDrawItems(xpos+option->common.posx, ypos+option->common.posy, ((menu_t *)option->custom.dptr)->options, (menu_t *)option->custom.dptr); @@ -809,7 +815,11 @@ menupicture_t *MC_AddCenterPicture(menu_t *menu, int y, int height, char *picnam } else { - width = (p->width * (float)height) / p->height; + int pwidth, pheight; + if (R_GetShaderSizes(p, &pwidth, &pheight, true)) + width = (pwidth * (float)height) / pheight; + else + width = 64; x = (320-(int)width)/2; } @@ -859,32 +869,44 @@ menupicture_t *MC_AddCursor(menu_t *menu, menuresel_t *reselection, int x, int y n->common.iszone = true; n->common.posx = x; n->common.posy = y; + n->common.width = 20; + n->common.height = 20; n->common.next = menu->options; menu->options = (menuoption_t *)n; - mgt = M_GameType(); - if (mgt == MGT_QUAKE2) - { //AND QUAKE 2 WINS!!! - menudotstyle = "m_cursor%i"; + switch(mgt) + { +#ifdef Q2CLIENT + case MGT_QUAKE2: + //AND QUAKE 2 WINS!!! + menudotstyle = "pics/m_cursor%i.pcx"; mindot = 0; maxdots = 15; dotofs=0; - } - else if (mgt == MGT_HEXEN2) - { //AND THE WINNER IS HEXEN 2!!! + + //this is *obviously* the correct size... not. + n->common.width = 22; + n->common.height = 29; + break; +#endif +#ifdef HEXEN2 + case MGT_HEXEN2: + //AND THE WINNER IS HEXEN 2!!! menudotstyle = "gfx/menu/menudot%i.lmp"; mindot = 1; maxdots = 8; dotofs=-2; - } - else - { //QUAKE 1 WINS BY DEFAULT! + break; +#endif + default: + //QUAKE 1 WINS BY DEFAULT! menudotstyle = "gfx/menudot%i.lmp"; mindot = 1; maxdots = 6; dotofs=0; + break; } if (menu->reselection) @@ -1709,9 +1731,11 @@ void M_Complex_Key(int key, int unicode) if (key == 0) return; +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu1.wav"); else +#endif S_LocalSound ("misc/menu1.wav"); if (key != K_ESCAPE && key != '`') @@ -1729,9 +1753,11 @@ void M_Complex_Key(int key, int unicode) case K_ESCAPE: //remove M_RemoveMenu(currentmenu); +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu3.wav"); else +#endif S_LocalSound ("misc/menu3.wav"); break; case K_TAB: @@ -1740,9 +1766,11 @@ void M_Complex_Key(int key, int unicode) if (currentmenu->selecteditem) { +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu1.wav"); else +#endif S_LocalSound ("misc/menu1.wav"); if (currentmenu->cursoritem) @@ -1754,9 +1782,11 @@ void M_Complex_Key(int key, int unicode) if (currentmenu->selecteditem) { +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu1.wav"); else +#endif S_LocalSound ("misc/menu1.wav"); if (currentmenu->cursoritem) @@ -1786,9 +1816,11 @@ void M_Complex_Key(int key, int unicode) else if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1) { Cbuf_AddText(currentmenu->selecteditem->button.command, RESTRICT_LOCAL); +#ifdef HEXEN2 if (mgt == MGT_HEXEN2) S_LocalSound ("raven/menu2.wav"); else +#endif S_LocalSound ("misc/menu2.wav"); } break; @@ -1979,9 +2011,10 @@ void M_Menu_Main_f (void) S_LocalSound ("misc/menu2.wav"); mgt = M_GameType(); +#ifdef Q2CLIENT if (mgt == MGT_QUAKE2) //quake2 main menu. { - if (R2D_SafeCachePic("pics/m_main_game")) + if (R_GetShaderSizes(R2D_SafeCachePic("pics/m_main_quit"), NULL, NULL, true) > 0) { m_state = m_complex; Key_Dest_Add(kdm_menu); @@ -2032,10 +2065,13 @@ void M_Menu_Main_f (void) mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 42, mainm->selecteditem->common.posy); } } - else if (mgt == MGT_HEXEN2) + else +#endif +#ifdef HEXEN2 + if (mgt == MGT_HEXEN2) { p = R2D_SafeCachePic("gfx/menu/title0.lmp"); - if (!p) + if (R_GetShaderSizes(p, NULL, NULL, true) <= 0) return; m_state = m_complex; @@ -2073,14 +2109,16 @@ void M_Menu_Main_f (void) mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 56, mainm->selecteditem->common.posy); } - else if (QBigFontWorks()) + else +#endif + if (QBigFontWorks()) { m_state = m_complex; Key_Dest_Add(kdm_menu); mainm = M_CreateMenu(0); p = R2D_SafeCachePic("gfx/ttl_main.lmp"); - if (!p) + if (R_GetShaderSizes(p, NULL, NULL, true) <= 0) { MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false); @@ -2118,7 +2156,7 @@ void M_Menu_Main_f (void) mainm = M_CreateMenu(0); p = R2D_SafeCachePic("gfx/ttl_main.lmp"); - if (!p) + if (R_GetShaderSizes(p, NULL, NULL, true) <= 0) { MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index e8af7003..73e4298a 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -2665,8 +2665,8 @@ texid_tf Media_UpdateForShader(cin_t *cin) if (!cin->outunchanged) { if (!TEXVALID(cin->texture)) - TEXASSIGN(cin->texture, R_AllocNewTexture("***cin***", cin->outwidth, cin->outheight, IF_NOMIPMAP|IF_NOALPHA)); - R_Upload(cin->texture, "cin", cin->outtype, cin->outdata, cin->outpalette, cin->outwidth, cin->outheight, IF_NOMIPMAP|IF_NOALPHA|IF_NOGAMMA); + TEXASSIGN(cin->texture, Image_CreateTexture("***cin***", NULL, IF_NOMIPMAP|IF_NOALPHA)); + Image_Upload(cin->texture, cin->outtype, cin->outdata, cin->outpalette, cin->outwidth, cin->outheight, IF_NOMIPMAP|IF_NOALPHA|IF_NOGAMMA); } if (cin->doneframe) @@ -4365,7 +4365,8 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed) HACMDRIVER drv = NULL; mp3decoder_t *dec; - char *ext = COM_FileExtension(s->name); + char ext[8]; + COM_FileExtension(s->name, ext, sizeof(ext)); if (stricmp(ext, "mp3")) return false; diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index f4d1a5be..7efaa459 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -25,6 +25,7 @@ void M_Menu_MultiPlayer_f (void) menu = M_CreateMenu(0); +#ifdef Q2CLIENT if (mgt == MGT_QUAKE2) { MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_multiplayer"); @@ -39,7 +40,10 @@ void M_Menu_MultiPlayer_f (void) menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false); return; } - else if (mgt == MGT_HEXEN2) + else +#endif +#ifdef HEXEN2 + if (mgt == MGT_HEXEN2) { MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title4.lmp"); @@ -53,7 +57,9 @@ void M_Menu_MultiPlayer_f (void) menu->cursoritem = (menuoption_t *)MC_AddCursor(menu, &resel, 48, 64); return; } - else if (QBigFontWorks()) + else +#endif + if (QBigFontWorks()) { MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); MC_AddCenterPicture(menu, 4, 24, "gfx/p_multi.lmp"); @@ -112,12 +118,14 @@ typedef struct { menuedit_t *nameedit; menuedit_t *teamedit; menuedit_t *skinedit; +#ifdef HEXEN2 menucombo_t *classedit; + int ticlass; +#endif menucombo_t *modeledit; int topcolour; int lowercolour; - int ticlass; int tiwidth, tiheight; qbyte translationimage[128*128]; } setupmenu_t; @@ -130,8 +138,10 @@ qboolean ApplySetupMenu (union menuoption_s *option,struct menu_s *menu, int key Cvar_Set(&team, info->teamedit->text); if (info->skinedit) Cvar_Set(&skin, info->skinedit->text); +#ifdef HEXEN2 if (info->classedit) Cvar_SetValue(Cvar_FindVar("cl_playerclass"), info->classedit->selectedoption+1); +#endif Cbuf_AddText(va("color %i %i\n", info->lowercolour, info->topcolour), RESTRICT_LOCAL); S_LocalSound ("misc/menu2.wav"); M_RemoveMenu(menu); @@ -281,19 +291,23 @@ void MSetup_TransDraw (int x, int y, menucustom_t *option, menu_t *menu) info->skinedit->modified = false; reloadtimage = true; } +#ifdef HEXEN2 if (info->classedit && info->classedit->selectedoption != info->ticlass) { info->ticlass = info->classedit->selectedoption; reloadtimage = true; } +#endif if (reloadtimage) { +#ifdef HEXEN2 if (info->classedit) //quake2 main menu. { FS_LoadFile(va("gfx/menu/netp%i.lmp", info->ticlass+1), &f); } else +#endif { FS_LoadFile(va("gfx/player/%s.lmp", info->skinedit->text), &f); if (!f) @@ -330,6 +344,7 @@ void M_Menu_Setup_f (void) static menuresel_t resel; mgt = M_GameType(); +#ifdef Q2CLIENT if (mgt == MGT_QUAKE2) //quake2 main menu. { if (R2D_SafeCachePic("pics/m_banner_player_setup")) @@ -367,6 +382,7 @@ void M_Menu_Setup_f (void) } return; } +#endif Key_Dest_Add(kdm_menu); m_state = m_complex; @@ -383,6 +399,8 @@ void M_Menu_Setup_f (void) menu->selecteditem = (menuoption_t*) (info->nameedit = MC_AddEdit(menu, 64, 160, 40, "Your name", name.string)); (info->teamedit = MC_AddEdit(menu, 64, 160, 56, "Your team", team.string)); +#ifdef HEXEN2 + info->ticlass = -1; if (mgt == MGT_HEXEN2) { static const char *classnames[] = @@ -398,6 +416,7 @@ void M_Menu_Setup_f (void) (info->classedit = MC_AddCombo(menu, 64, 160, 72, "Your class", (const char **)classnames, pc->ival-1)); } else +#endif (info->skinedit = MC_AddEdit(menu, 64, 160, 72, "Your skin", skin.string)); ci = MC_AddCustom(menu, 172+32, 88, NULL, 0); @@ -419,7 +438,6 @@ void M_Menu_Setup_f (void) info->topcolour = topcolor.value; if (info->skinedit) info->skinedit->modified = true; - info->ticlass = -1; } diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 42a2a078..a244cacb 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -114,11 +114,13 @@ menu_t *M_Options_Title(int *y, int infosize) MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_options"); *y += 32; break; +#ifdef HEXEN2 case MGT_HEXEN2://h2 MC_AddPicture(menu, 16, 0, 35, 176, "gfx/menu/hplaque.lmp"); MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title3.lmp"); *y += 32; break; +#endif default: //q1 MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); MC_AddCenterPicture(menu, 4, 24, "gfx/p_option.lmp"); @@ -708,8 +710,8 @@ const char *presetexec[] = "cl_bob 0.02;" //these things are perhaps a little extreme "r_loadlit 0;" - "gl_texturemode nn;" //yup, we went there. - "gl_texturemode2d n;" //yeah, 2d too. + "gl_texturemode nnl;" //yup, we went there. + "gl_texturemode2d n.l;" //yeah, 2d too. "r_part_classic_square 1;" //blocky baby! "r_part_classic_expgrav 1;" //vanillaery "r_particlesystem script;" //q2 or hexen2 particle effects need to be loadable @@ -1547,7 +1549,7 @@ void M_Menu_Singleplayer_Cheats_Quake (void) #endif MC_AddRedText(menu, 16, 170, y, " Quake Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, 170, y, " €‚ ", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, " ^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 ", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; @@ -1661,7 +1663,7 @@ void M_Menu_Singleplayer_Cheats_Quake2 (void) #endif MC_AddRedText(menu, 16, 170, y, "Quake2 Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, 170, y, "€‚ ", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, "^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; @@ -2019,7 +2021,7 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void) currentmap = 0; MC_AddRedText(menu, 16, 170, y, "Hexen2 Singleplayer Cheats", false); y+=8; - MC_AddWhiteText(menu, 16, 170, y, "€‚ ", false); y+=8; + MC_AddWhiteText(menu, 16, 170, y, "^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 ", false); y+=8; y+=8; #ifndef CLIENTONLY info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8; @@ -2072,12 +2074,16 @@ void M_Menu_Singleplayer_Cheats_f (void) case MGT_QUAKE1: M_Menu_Singleplayer_Cheats_Quake(); break; +#ifdef Q2CLIENT case MGT_QUAKE2: M_Menu_Singleplayer_Cheats_Quake2(); break; +#endif +#ifdef HEXEN2 case MGT_HEXEN2: M_Menu_Singleplayer_Cheats_Hexen2(); break; +#endif } } @@ -2527,6 +2533,11 @@ void M_Menu_Video_f (void) #ifndef MINIMAL typedef struct { + enum { + MV_NONE, + MV_BONES, + MV_SHADER + } mode; int skingroup; int framegroup; double framechangetime; @@ -2536,6 +2547,8 @@ typedef struct float dist; char modelname[MAX_QPATH]; char forceshader[MAX_QPATH]; + + char *shadertext; } modelview_t; static unsigned int genhsv(float h_, float s, float v) @@ -2666,18 +2679,50 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ Draw_FunString(0, y, va("%i: %s", mods->skingroup, fname)); y+=8; -#ifdef SKELETALMODELS + switch(mods->mode) { - int bonecount; - galiasbone_t *b = Mod_GetBoneInfo(ent.model, &bonecount); - if (b && bonecount) + case MV_NONE: + R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, + "arrows: pitch/rotate\n" + "w: zoom in\n" + "s: zoom out\n" + "m: mode\n" + "r: reset times\n" + "home: skin-=1\n" + "end: skin+=1\n" + "pgup: frame+=1\n" + "pgdn: frame-=1\n" + , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN); + break; + case MV_BONES: +#ifdef SKELETALMODELS { - Draw_FunString(0, y, va("Bones: ")); - y+=8; - M_BoneDisplay(&ent, b, &y, 0, -1, 0, bonecount); + int bonecount; + galiasbone_t *b = Mod_GetBoneInfo(ent.model, &bonecount); + if (b && bonecount) + { + Draw_FunString(0, y, va("Bones: ")); + y+=8; + M_BoneDisplay(&ent, b, &y, 0, -1, 0, bonecount); + } + else + R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+y, r_refdef.grect.width, r_refdef.grect.height-y, "No bones in model", CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN); } - } #endif + break; + case MV_SHADER: + { + if (!mods->shadertext) + { + char *body = Shader_GetShaderBody(Mod_ShaderForSkin(ent.model, mods->skingroup)); + mods->shadertext = Z_StrDup(body); + } + R_DrawTextField(r_refdef.grect.x, r_refdef.grect.y+16, r_refdef.grect.width, r_refdef.grect.height-16, mods->shadertext, CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN); + + //fixme: draw the shader's textures. + } + break; + } } static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int key) { @@ -2691,6 +2736,17 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k } else if (key == 's') mods->dist /= 0.9; + else if (key == 'm') + { + Z_Free(mods->shadertext); + mods->shadertext = NULL; + switch (mods->mode) + { + case MV_NONE: mods->mode = MV_BONES; break; + case MV_BONES: mods->mode = MV_SHADER; break; + case MV_SHADER: mods->mode = MV_NONE; break; + } + } else if (key == 'r') { mods->framechangetime = realtime; @@ -2853,8 +2909,11 @@ void M_Menu_Mods_f (void) menu = M_CreateMenu(sizeof(modmenu_t)); *(modmenu_t*)menu->data = mods; - MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); - MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); + if (COM_FCheckExists("gfx/p_option.lmp")) + { + MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); + MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); + } c = MC_AddCustom(menu, 64, 32, menu->data, 0); menu->cursoritem = (menuoption_t*)c; diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 5b409252..9c727b94 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -126,7 +126,6 @@ void M_Menu_SinglePlayer_f (void) { menu_t *menu; #ifndef CLIENTONLY - int mgt; menubutton_t *b; mpic_t *p; #endif @@ -144,9 +143,10 @@ void M_Menu_SinglePlayer_f (void) MC_AddBox (menu, 60, 10*8, 25, 4); #else - mgt = M_GameType(); - if (mgt == MGT_QUAKE2) - { //q2... + switch(M_GameType()) + { +#ifdef Q2CLIENT + case MGT_QUAKE2: menu = M_CreateMenu(0); MC_AddCenterPicture(menu, 4, 24, "pics/m_banner_game"); @@ -162,174 +162,180 @@ void M_Menu_SinglePlayer_f (void) menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 48, 0, 40, NULL, false); return; - } - else if (mgt == MGT_HEXEN2) - { //h2 - int y; - int i; - cvar_t *pc; - qboolean havemp; - static char *classlistmp[] = { - "Paladin", - "Crusader", - "Necromancer", - "Assasin", - "Demoness" - }; - menubutton_t *b; - havemp = COM_FCheckExists("maps/keep1.bsp"); - menu = M_CreateMenu(0); - MC_AddPicture(menu, 16, 0, 35, 176, "gfx/menu/hplaque.lmp"); - - Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); - - y = 64-20; - - if (!strncmp(Cmd_Argv(1), "class", 5)) +#endif +#ifdef HEXEN2 + case MGT_HEXEN2: { - int pnum; - extern cvar_t cl_splitscreen; - pnum = atoi(Cmd_Argv(1)+5); - if (!pnum) - pnum = 1; - - MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title2.lmp"); - - if (cl_splitscreen.ival) - MC_AddBufferedText(menu, 80, 0, (y+=8)+12, va("Player %i\n", pnum), false, true); - - for (i = 0; i < 4+havemp; i++) - { - b = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, classlistmp[i], - va("p%i setinfo cl_playerclass %i; menu_single %s %s\n", - pnum, - i+1, - ((pnum+1 > cl_splitscreen.ival+1)?"skill":va("class%i",pnum+1)), - Cmd_Argv(2))); - if (!menu->selecteditem) - menu->selecteditem = (menuoption_t*)b; - } - } - else if (!strncmp(Cmd_Argv(1), "skill", 5)) - { - static char *skillnames[6][4] = - { - { - "Easy", - "Medium", - "Hard", - "Nightmare" - }, - { - "Apprentice", - "Squire", - "Adept", - "Lord" - }, - { - "Gallant", - "Holy Avenger", - "Divine Hero", - "Legend" - }, - { - "Sorcerer", - "Dark Servant", - "Warlock", - "Lich King" - }, - { - "Rogue", - "Cutthroat", - "Executioner", - "Widow Maker" - }, - { - "Larva", - "Spawn", - "Fiend", - "She Bitch" - } + int y; + int i; + cvar_t *pc; + qboolean havemp; + static char *classlistmp[] = { + "Paladin", + "Crusader", + "Necromancer", + "Assasin", + "Demoness" }; - char **sn = skillnames[0]; - pc = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); - if (pc && (unsigned)pc->ival <= 5) - sn = skillnames[pc->ival]; + menubutton_t *b; + havemp = COM_FCheckExists("maps/keep1.bsp"); + menu = M_CreateMenu(0); + MC_AddPicture(menu, 16, 0, 35, 176, "gfx/menu/hplaque.lmp"); - MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title5.lmp"); - for (i = 0; i < 4; i++) + Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); + + y = 64-20; + + if (!strncmp(Cmd_Argv(1), "class", 5)) { - b = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, sn[i], va("skill %i; closemenu; disconnect; deathmatch 0; coop 0;wait;map %s\n", i, Cmd_Argv(2))); - if (!menu->selecteditem) - menu->selecteditem = (menuoption_t*)b; + int pnum; + extern cvar_t cl_splitscreen; + pnum = atoi(Cmd_Argv(1)+5); + if (!pnum) + pnum = 1; + + MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title2.lmp"); + + if (cl_splitscreen.ival) + MC_AddBufferedText(menu, 80, 0, (y+=8)+12, va("Player %i\n", pnum), false, true); + + for (i = 0; i < 4+havemp; i++) + { + b = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, classlistmp[i], + va("p%i setinfo cl_playerclass %i; menu_single %s %s\n", + pnum, + i+1, + ((pnum+1 > cl_splitscreen.ival+1)?"skill":va("class%i",pnum+1)), + Cmd_Argv(2))); + if (!menu->selecteditem) + menu->selecteditem = (menuoption_t*)b; + } } - } - else - { - MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title1.lmp"); - //startmap selection in hexen2 is nasty. - if (havemp) + else if (!strncmp(Cmd_Argv(1), "skill", 5)) { - menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "New Mission", "menu_single class keep1\n"); - MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Old Mission", "menu_single class demo1\n"); + static char *skillnames[6][4] = + { + { + "Easy", + "Medium", + "Hard", + "Nightmare" + }, + { + "Apprentice", + "Squire", + "Adept", + "Lord" + }, + { + "Gallant", + "Holy Avenger", + "Divine Hero", + "Legend" + }, + { + "Sorcerer", + "Dark Servant", + "Warlock", + "Lich King" + }, + { + "Rogue", + "Cutthroat", + "Executioner", + "Widow Maker" + }, + { + "Larva", + "Spawn", + "Fiend", + "She Bitch" + } + }; + char **sn = skillnames[0]; + pc = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); + if (pc && (unsigned)pc->ival <= 5) + sn = skillnames[pc->ival]; + + MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title5.lmp"); + for (i = 0; i < 4; i++) + { + b = MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, sn[i], va("skill %i; closemenu; disconnect; deathmatch 0; coop 0;wait;map %s\n", i, Cmd_Argv(2))); + if (!menu->selecteditem) + menu->selecteditem = (menuoption_t*)b; + } } else { - menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "New Game", "menu_single class demo1\n"); + MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title1.lmp"); + //startmap selection in hexen2 is nasty. + if (havemp) + { + menu->selecteditem = (menuoption_t*) + MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "New Mission", "menu_single class keep1\n"); + MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Old Mission", "menu_single class demo1\n"); + } + else + { + menu->selecteditem = (menuoption_t*) + MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "New Game", "menu_single class demo1\n"); + } + MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Save Game", "menu_save\n"); + MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Load Game", "menu_load\n"); } - MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Save Game", "menu_save\n"); - MC_AddConsoleCommandHexen2BigFont(menu, 80, y+=20, "Load Game", "menu_load\n"); - } - /* - pc = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); - if (pc) - MC_AddCvarCombo (menu, 64, y+=8, "Player class", pc, havemp?(const char **)classlistmp:(const char **)classlist, (const char **)(classvalues+havemp)); - y+=8; - - menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Easy", "closemenu\nskill 0;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); - MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Medium", "closemenu\nskill 1;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); - MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Hard", "closemenu\nskill 2;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); - y+=8; - - if (havemp) - { - MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Easy", "closemenu\nskill 0;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); - MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Medium", "closemenu\nskill 1;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); - MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Hard", "closemenu\nskill 2;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); + /* + pc = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2"); + if (pc) + MC_AddCvarCombo (menu, 64, y+=8, "Player class", pc, havemp?(const char **)classlistmp:(const char **)classlist, (const char **)(classvalues+havemp)); y+=8; + + menu->selecteditem = (menuoption_t*) + MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Easy", "closemenu\nskill 0;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); + MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Medium", "closemenu\nskill 1;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); + MC_AddConsoleCommand (menu, 64, y+=8, "Classic: Hard", "closemenu\nskill 2;deathmatch 0; coop 0;disconnect;wait;map demo1\n"); + y+=8; + + if (havemp) + { + MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Easy", "closemenu\nskill 0;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); + MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Medium", "closemenu\nskill 1;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); + MC_AddConsoleCommand(menu, 64, y+=8, "Expansion: Hard", "closemenu\nskill 2;deathmatch 0; coop 0;disconnect;wait;map keep1\n"); + y+=8; + } + + MC_AddConsoleCommand (menu, 64, y+=8, "Load Game", "menu_load\n"); + MC_AddConsoleCommand (menu, 64, y+=8, "Save Game", "menu_save\n"); + */ + + menu->cursoritem = (menuoption_t *)MC_AddCursor(menu, &resel, 56, menu->selecteditem?menu->selecteditem->common.posy:0); + + return; } + break; +#endif + default: + if (QBigFontWorks()) + { + menu = M_CreateMenu(0); + MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); + MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); - MC_AddConsoleCommand (menu, 64, y+=8, "Load Game", "menu_load\n"); - MC_AddConsoleCommand (menu, 64, y+=8, "Save Game", "menu_save\n"); - */ + menu->selecteditem = (menuoption_t*) + MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", "closemenu;disconnect;maxclients 1;deathmatch 0;coop 0;startmap_sp\n"); + MC_AddConsoleCommandQBigFont (menu, 72, 52, "Load Game", "menu_load\n"); + MC_AddConsoleCommandQBigFont (menu, 72, 72, "Save Game", "menu_save\n"); - menu->cursoritem = (menuoption_t *)MC_AddCursor(menu, &resel, 56, menu->selecteditem?menu->selecteditem->common.posy:0); - - return; - } - else if (QBigFontWorks()) - { - menu = M_CreateMenu(0); - MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); - MC_AddCenterPicture(menu, 0, 24, "gfx/p_option.lmp"); - - menu->selecteditem = (menuoption_t*) - MC_AddConsoleCommandQBigFont (menu, 72, 32, "New Game", "closemenu;disconnect;maxclients 1;deathmatch 0;coop 0;startmap_sp\n"); - MC_AddConsoleCommandQBigFont (menu, 72, 52, "Load Game", "menu_load\n"); - MC_AddConsoleCommandQBigFont (menu, 72, 72, "Save Game", "menu_save\n"); - - menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32); - return; - } - else - { //q1 - menu = M_CreateMenu(0); - MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); - MC_AddCenterPicture(menu, 4, 24, "gfx/p_option.lmp"); + menu->cursoritem = (menuoption_t*)MC_AddCursor(menu, &resel, 54, 32); + return; + } + else + { //q1 + menu = M_CreateMenu(0); + MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp"); + MC_AddCenterPicture(menu, 4, 24, "gfx/p_option.lmp"); + } + break; } p = R2D_SafeCachePic("gfx/sp_menu.lmp"); diff --git a/engine/client/menu.c b/engine/client/menu.c index d7e685dc..5e147667 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -438,7 +438,6 @@ void M_Menu_Keys_f (void) { int y; menu_t *menu; - int mgt; extern cvar_t cl_splitscreen, cl_forcesplitclient; vfsfile_t *bindslist; @@ -446,27 +445,27 @@ void M_Menu_Keys_f (void) m_state = m_complex; menu = M_CreateMenu(0); - mgt = M_GameType(); -#ifdef Q2CLIENT - if (mgt == MGT_QUAKE2) //quake2 main menu. + switch(M_GameType()) { +#ifdef Q2CLIENT + case MGT_QUAKE2: + //fixme: no art? y = 48; bindnames = q2bindnames; - } - else + break; #endif - if (mgt == MGT_HEXEN2) - { +#ifdef HEXEN2 + case MGT_HEXEN2: MC_AddCenterPicture(menu, 0, 60, "gfx/menu/title6.lmp"); y = 64; bindnames = h2bindnames; - } - else - { + break; +#endif + default: MC_AddCenterPicture(menu, 4, 24, "gfx/ttl_cstm.lmp"); y = 48; - bindnames = qwbindnames; + break; } if (cl_splitscreen.ival) @@ -591,7 +590,11 @@ void M_Help_Draw (void) int i; mpic_t *pic = NULL; for (i = 0; i < sizeof(helpstyles)/sizeof(helpstyles[0]) && !pic; i++) + { pic = R2D_SafeCachePic(va(helpstyles[i].pattern, help_page+helpstyles[i].base)); + if (R_GetShaderSizes(pic, NULL, NULL, true) <= 0) + pic = NULL; + } if (!pic) M_Menu_Main_f (); else @@ -1415,19 +1418,33 @@ int M_GameType (void) if (FS_Restarted(&cachedrestarts)) { - int q1 = COM_FDepthFile("gfx/sp_menu.lmp", true); - int h2 = COM_FDepthFile("gfx/menu/title2.lmp", true); -#if defined(Q2CLIENT) - int q2 = COM_FDepthFile("pics/m_banner_game.pcx", true); - - if (q2 < h2 && q2 < q1) - cached = MGT_QUAKE2; - else + struct + { + int gametype; + char *path; + } configs[] = + { + {MGT_QUAKE1, "gfx/sp_menu.lmp"}, +#ifdef Q2CLIENT + {MGT_QUAKE2, "pics/m_banner_game.pcx"}, #endif - if (h2 < q1) - cached = MGT_HEXEN2; - else - cached = MGT_QUAKE1; +#ifdef HEXEN2 + {MGT_HEXEN2, "gfx/menu/title2.lmp"}, +#endif + {0, NULL} + }; + int bd = COM_FDepthFile(configs[0].path, true); + int i; + cached = configs[0].gametype; + for (i = 1; configs[i].path; i++) + { + int gd = COM_FDepthFile(configs[i].path, true); + if (bd > gd) + { + bd = gd; + cached = configs[i].gametype; + } + } } return cached; diff --git a/engine/client/merged.h b/engine/client/merged.h index be987111..c0aef8c3 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -9,6 +9,7 @@ struct batch_s; struct entity_s; struct dlight_s; struct galiasbone_s; +struct dlight_s; typedef enum { @@ -88,9 +89,6 @@ extern void (*R_Init) (void); extern void (*R_DeInit) (void); extern void (*R_RenderView) (void); // must set r_refdef first -extern void (*R_NewMap) (void); -extern void (*R_PreNewMap) (void); - extern qboolean (*VID_Init) (rendererstate_t *info, unsigned char *palette); extern void (*VID_DeInit) (void); extern char *(*VID_GetRGBInfo) (int prepad, int *truevidwidth, int *truevidheight); @@ -140,8 +138,7 @@ extern struct model_s *Mod_ForName (const char *name, enum mlverbosity_e verb extern struct model_s *Mod_LoadModel (struct model_s *mod, enum mlverbosity_e verbose); //makes sure a model is loaded extern void *Mod_Extradata (struct model_s *mod); // handles caching extern void Mod_TouchModel (const char *name); - -extern void Mod_NowLoadExternal (void); +extern const char *Mod_FixName (const char *modname, const char *worldname); //remaps the name appropriately extern void Mod_Think (void); extern int Mod_SkinNumForName (struct model_s *model, const char *name); @@ -170,29 +167,79 @@ extern int r_regsequence; // qbyte *Mod_LeafPVS (struct mleaf_s *leaf, struct model_s *model, qbyte *buffer); #endif -typedef struct +typedef enum uploadfmt { + TF_INVALID, + TF_RGBA32, /*rgba byte order*/ + TF_BGRA32, /*bgra byte order*/ + TF_RGBX32, /*rgb byte order, with extra wasted byte after blue*/ + TF_BGRX32, /*rgb byte order, with extra wasted byte after blue*/ + TF_RGB24, /*rgb byte order, no alpha channel nor pad, and regular top down*/ + TF_BGR24, /*bgr byte order, no alpha channel nor pad, and regular top down*/ + TF_BGR24_FLIP, /*bgr byte order, no alpha channel nor pad, and bottom up*/ + TF_LUM8, /*8bit greyscale image*/ + TF_SOLID8, /*8bit quake-palette image*/ + TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/ + TF_TRANS8_FULLBRIGHT, /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/ + TF_HEIGHT8, /*image data is greyscale, convert to a normalmap and load that, uploaded alpha contains the original heights*/ + TF_HEIGHT8PAL, /*source data is palette values rather than actual heights, generate a fallback heightmap*/ + TF_H2_T7G1, /*8bit data, odd indexes give greyscale transparence*/ + TF_H2_TRANS8_0, /*8bit data, 0 is transparent, not 255*/ + TF_H2_T4A4, /*8bit data, weird packing*/ + + /*this block requires a palette*/ + TF_PALETTES, + TF_8PAL24, + TF_8PAL32, + + /*for render targets*/ + TF_DEPTH16, + TF_DEPTH24, + TF_DEPTH32, + TF_RGBA16F, + TF_RGBA32F +} uploadfmt_t; + +enum +{ + TEX_NOTLOADED, + TEX_LOADING, + TEX_LOADED, + TEX_FAILED +}; +typedef struct image_s +{ + char *ident; //allocated on end + char *subpath; //allocated on end int regsequence; int width; int height; -} texcom_t; -struct texid_s -{ - union - { - unsigned int num; + int status; //TEX_ + unsigned int flags; + struct image_s *next; + struct image_s *prev; #if defined(D3DQUAKE) || defined(SWQUAKE) - void *ptr; + void *ptr; //texture + void *ptr2; //view #endif - }; - texcom_t *ref; -}; +#ifdef GLQUAKE + int num; +#endif + + void *fallbackdata; + int fallbackwidth; + int fallbackheight; + uploadfmt_t fallbackfmt; +} image_t; + #if 1 -typedef struct texid_s texid_t; +typedef image_t *texid_t; #define texid_tf texid_t #define TEXASSIGN(d,s) d=s #define TEXASSIGNF(d,s) d=s -#define TEXVALID(t) ((t).ref!=NULL) +#define TEXVALID(t) ((t)) +#define TEXLOADED(tex) ((tex) && (tex)->status == TEX_LOADED) +#define TEXDOWAIT(tex) if ((tex) && (tex)->status == TEX_LOADING) COM_WorkerPartialSync((tex), &(tex)->status, TEX_LOADING) #else typedef struct texid_s texid_t[1]; typedef struct texid_s texid_tf; @@ -201,6 +248,38 @@ typedef struct texid_s texid_tf; #define TEXVALID(t) 1 #endif +struct pendingtextureinfo +{ + enum + { + PTI_2D, + PTI_3D, + PTI_CUBEMAP //mips are packed (to make d3d11 happy) + } type; + enum + { + PTI_RGBA8, //rgba byte ordering + PTI_RGBX8, //rgb pad byte ordering + PTI_BGRA8, //alpha channel + PTI_BGRX8, //no alpha channel + //compressed formats + PTI_S3RGB1, + PTI_S3RGBA1, + PTI_S3RGBA3, + PTI_S3RGBA5 + } encoding; //0 + int mipcount; + struct + { + void *data; + size_t datasize; + int width; + int height; + qboolean needfree; + } mip[32]; + void *extrafree; +}; + //small context for easy vbo creation. typedef struct { @@ -214,7 +293,7 @@ typedef struct vboarray_s { union { - void *dummy; + void *sysptr; #ifdef GLQUAKE struct { @@ -251,38 +330,6 @@ typedef struct texnums_s { texid_t loweroverlay; texid_t fullbright; } texnums_t; -typedef enum uploadfmt -{ - TF_INVALID, - TF_RGBA32, /*rgba byte order*/ - TF_BGRA32, /*bgra byte order*/ - TF_RGBX32, /*rgb byte order, with extra wasted byte after blue*/ - TF_BGRX32, /*rgb byte order, with extra wasted byte after blue*/ - TF_RGB24, /*rgb byte order, no alpha channel nor pad, and regular top down*/ - TF_BGR24, /*bgr byte order, no alpha channel nor pad, and regular top down*/ - TF_BGR24_FLIP, /*bgr byte order, no alpha channel nor pad, and bottom up*/ - TF_LUM8, /*8bit greyscale image*/ - TF_SOLID8, /*8bit quake-palette image*/ - TF_TRANS8, /*8bit quake-palette image, index 255=transparent*/ - TF_TRANS8_FULLBRIGHT, /*fullbright 8 - fullbright texels have alpha 255, everything else 0*/ - TF_HEIGHT8, /*image data is greyscale, convert to a normalmap and load that, uploaded alpha contains the original heights*/ - TF_HEIGHT8PAL, /*source data is palette values rather than actual heights, generate a fallback heightmap*/ - TF_H2_T7G1, /*8bit data, odd indexes give greyscale transparence*/ - TF_H2_TRANS8_0, /*8bit data, 0 is transparent, not 255*/ - TF_H2_T4A4, /*8bit data, weird packing*/ - - /*this block requires a palette*/ - TF_PALETTES, - TF_8PAL24, - TF_8PAL32, - - /*for render targets*/ - TF_DEPTH16, - TF_DEPTH24, - TF_DEPTH32, - TF_RGBA16F, - TF_RGBA32F -} uploadfmt_t; //not all modes accept meshes - STENCIL(intentional) and DEPTHONLY(not implemented) typedef enum backendmode_e @@ -306,22 +353,14 @@ typedef struct rendererinfo_s { void (*Draw_Init) (void); void (*Draw_Shutdown) (void); - texid_tf (*IMG_LoadTexture) (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); - texid_tf (*IMG_LoadTexture8Pal24) (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); - texid_tf (*IMG_LoadTexture8Pal32) (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); - texid_tf (*IMG_LoadCompressed) (const char *name); - texid_tf (*IMG_FindTexture) (const char *identifier, unsigned int flags); - texid_tf (*IMG_AllocNewTexture) (const char *identifier, int w, int h, unsigned int flags); - void (*IMG_Upload) (texid_t tex, const char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); + void (*IMG_UpdateFiltering) (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); + qboolean (*IMG_LoadTextureMips) (texid_t tex, struct pendingtextureinfo *mips); void (*IMG_DestroyTexture) (texid_t tex); void (*R_Init) (void); //FIXME - merge implementations void (*R_DeInit) (void); //FIXME - merge implementations void (*R_RenderView) (void); // must set r_refdef first - void (*R_NewMap) (void); //FIXME - merge implementations - void (*R_PreNewMap) (void); //FIXME - merge implementations - qboolean (*VID_Init) (rendererstate_t *info, unsigned char *palette); void (*VID_DeInit) (void); void (*VID_SwapBuffers) (void); //force a buffer swap, regardless of what's displayed. @@ -356,7 +395,7 @@ typedef struct rendererinfo_s { //Uploads all modified lightmaps void (*BE_UploadAllLightmaps)(void); void (*BE_SelectEntity)(struct entity_s *ent); - qboolean (*BE_SelectDLight)(struct dlight_s *dl, vec3_t colour, unsigned int lmode); + qboolean (*BE_SelectDLight)(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); void (*BE_Scissor)(srect_t *rect); /*check to see if an ent should be drawn for the selected light*/ qboolean (*BE_LightCullModel)(vec3_t org, struct model_s *model); @@ -372,15 +411,6 @@ typedef struct rendererinfo_s { #define VID_SwapBuffers rf->VID_SwapBuffers -#define R_LoadTexture rf->IMG_LoadTexture -#define R_LoadTexture8Pal24 rf->IMG_LoadTexture8Pal24 -#define R_LoadTexture8Pal32 rf->IMG_LoadTexture8Pal32 -#define R_LoadCompressed rf->IMG_LoadCompressed -#define R_FindTexture rf->IMG_FindTexture -#define R_AllocNewTexture rf->IMG_AllocNewTexture -#define R_Upload rf->IMG_Upload -#define R_DestroyTexture rf->IMG_DestroyTexture - #define BE_Init rf->BE_Init #define BE_SelectMode rf->BE_SelectMode #define BE_GenBrushModelVBO rf->BE_GenBrushModelVBO diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index 5a44f9f3..9a075dd6 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -43,6 +43,7 @@ typedef enum { LAVASPLASH_POINT, EXPLOSION_POINT, TELEPORTSPLASH_POINT, + MUZZLEFLASH_POINT, EFFECTTYPE_MAX } effect_type_t; @@ -127,6 +128,8 @@ static int PClassic_FindParticleType(const char *name) return EXPLOSION_POINT; if (!stricmp("te_teleport", name)) return TELEPORTSPLASH_POINT; + if (!stricmp("te_muzzleflash", name)) + return MUZZLEFLASH_POINT; return P_INVALID; } @@ -867,7 +870,7 @@ done: //builds a trail from here to there. The trail state can be used to remember how far you got last frame. -static int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, trailstate_t **tsk) +static int PClassic_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk) { float leftover; @@ -897,6 +900,36 @@ static int PClassic_RunParticleEffectState (vec3_t org, vec3_t dir, float count, case TELEPORTSPLASH_POINT: Classic_TeleportSplash(org); break; + case MUZZLEFLASH_POINT: + { + dlight_t *dl = CL_AllocDlight (0); + if (dir) + VectorCopy(dir, dl->axis[0]); + else + VectorSet(dir, 0, 0, 1); + VectorVectors(dl->axis[0], dl->axis[1], dl->axis[2]); + VectorInverse(dl->axis[1]); + if (dir) + VectorMA (org, 15, dl->axis[0], dl->origin); + else + VectorCopy (org, dl->origin); + + dl->radius = 200 + (rand()&31); + dl->minlight = 32; + dl->die = cl.time + 0.1; + dl->color[0] = 1.5; + dl->color[1] = 1.3; + dl->color[2] = 1.0; + + dl->channelfade[0] = 1.5; + dl->channelfade[1] = 0.75; + dl->channelfade[2] = 0.375; + dl->decay = 1000; +#ifdef RTLIGHTS + dl->lightcolourscales[2] = 4; +#endif + } + break; default: return 1; } diff --git a/engine/client/p_null.c b/engine/client/p_null.c index 62885f9e..4ebc3a86 100644 --- a/engine/client/p_null.c +++ b/engine/client/p_null.c @@ -12,7 +12,7 @@ static int PNULL_FindParticleType(const char *name) } static int PNULL_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, char *name){return 1;} -static int PNULL_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, trailstate_t **tsk){return 1;} +static int PNULL_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk){return 1;} static int PNULL_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk){return 1;} static void PNULL_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname){} static void PNULL_RunParticleCube(int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter){} diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 52384f96..21f18451 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -273,7 +273,7 @@ typedef struct part_type_s { float stainonimpact; vec3_t dl_rgb; - float dl_radius; + float dl_radius[2]; float dl_time; vec4_t dl_decay; float dl_corona_intensity; @@ -622,6 +622,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -638,6 +640,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -655,6 +659,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -672,6 +678,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -689,6 +697,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -706,6 +716,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -723,6 +735,8 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; @@ -739,14 +753,23 @@ static void P_LoadTexture(part_type_t *ptype, qboolean warn) "alphagen vertex\n" "}\n" "polygonoffset\n" + "surfaceparm noshadows\n" + "surfaceparm nodlight\n" "}\n" ; break; } memset(&tn, 0, sizeof(tn)); - tn.base = R_LoadHiResTexture(ptype->texname, "particles", IF_NOMIPMAP|(ptype->looks.premul?IF_PREMULTIPLYALPHA:0)); //mipmapping breaks particlefont stuff - if (!TEXVALID(tn.base)) + if (*ptype->texname) + { + tn.base = R_LoadHiResTexture(ptype->texname, "particles", IF_NOMIPMAP|(ptype->looks.premul?IF_PREMULTIPLYALPHA:0)); //mipmapping breaks particlefont stuff + if (tn.base && tn.base->status == TEX_LOADING) + COM_WorkerPartialSync(tn.base, &tn.base->status, TEX_LOADING); + } + else + tn.base = NULL; + if (!TEXLOADED(tn.base)) { /*okay, so the texture they specified wasn't valid either. use a fully default one*/ @@ -1719,7 +1742,12 @@ static void P_ParticleEffect_f(void) ptype->flags |= PT_NOSPREADLAST; else if (!strcmp(var, "lightradius")) - ptype->dl_radius = atof(value); + { //float version + ptype->dl_radius[0] = ptype->dl_radius[1] = atof(value); + if (Cmd_Argc()>3) + ptype->dl_radius[1] = atof(Cmd_Argv(2)); + ptype->dl_radius[1] -= ptype->dl_radius[0]; + } else if (!strcmp(var, "lightradiusfade")) ptype->dl_decay[3] = atof(value); else if (!strcmp(var, "lightrgb")) @@ -1805,7 +1833,7 @@ static void P_ParticleEffect_f(void) ptype->alphachange = (-ptype->alphachange / ptype->die) * ptype->alpha; if (!ptype->dl_time && ptype->dl_decay[3]) - ptype->dl_time = ptype->dl_radius / ptype->dl_decay[3]; + ptype->dl_time = ptype->dl_radius[0] / ptype->dl_decay[3]; if (ptype->rampmode && !ptype->ramp) { @@ -2009,7 +2037,7 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) if (ptype->dl_radius) { - Q_strncatz(outstr, va("lightradius %g\n", ptype->dl_radius), outstrlen); + Q_strncatz(outstr, va("lightradius %g\n", ptype->dl_radius[0]), outstrlen); Q_strncatz(outstr, va("lightradiusfade %g\n", ptype->dl_decay[3]), outstrlen); Q_strncatz(outstr, va("lightrgb %g %g %g\n", ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]), outstrlen); Q_strncatz(outstr, va("lightrgbfade %g %g %g\n", ptype->dl_decay[0], ptype->dl_decay[1], ptype->dl_decay[2]), outstrlen); @@ -2243,7 +2271,7 @@ void FinishParticleType(part_type_t *ptype) ptype->die = 15; } if (ptype->dl_decay[3] && !ptype->dl_time) - ptype->dl_time = ptype->dl_radius / ptype->dl_decay[3]; + ptype->dl_time = ptype->dl_radius[0] / ptype->dl_decay[3]; if (ptype->looks.scalefactor > 1 && !ptype->looks.invscalefactor) { ptype->scale *= ptype->looks.scalefactor; @@ -2561,7 +2589,10 @@ static void P_ImportEffectInfo(char *config, char *line) Con_Printf("effectinfo 'orientation %s' not supported\n", arg[1]); } else if (!strcmp(arg[0], "lightradius") && args == 2) - ptype->dl_radius = atof(arg[1]); + { + ptype->dl_radius[0] = atof(arg[1]); + ptype->dl_radius[1] = 0; + } else if (!strcmp(arg[0], "lightradiusfade") && args == 2) ptype->dl_decay[3] = atof(arg[1]); else if (!strcmp(arg[0], "lightcolor") && args == 4) @@ -2986,6 +3017,7 @@ static void R_Particles_KillAllEffects(void) static void R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue) { + char token[256]; qboolean first; char *c; @@ -2996,12 +3028,12 @@ static void R_ParticleDesc_Callback(struct cvar_s *var, char *oldvalue) R_Particles_KillAllEffects(); first = true; - for (c = COM_ParseStringSet(var->string); com_token[0]; c = COM_ParseStringSet(c)) + for (c = COM_ParseStringSet(var->string, token, sizeof(token)); token[0]; c = COM_ParseStringSet(c, token, sizeof(token))) { /*set up a default*/ - if (first && !*com_token) - strcpy(com_token, "faithful"); - P_LoadParticleSet(com_token, false); + if (first && !*token) + strcpy(token, "classic"); + P_LoadParticleSet(token, false); first = false; } @@ -3556,8 +3588,10 @@ static void PScript_ApplyOrgVel(vec3_t oorg, vec3_t ovel, vec3_t eforg, vec3_t e VectorAdd(oorg, ptype->orgbias, oorg); } -static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t dir, int dlkey, float countscale) +static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t axis[3], int dlkey, float countscale) { + extern cvar_t r_rocketlight; + extern cvar_t r_lightflicker; if (ptype->nummodels) { int count = ptype->countextra + countscale*(ptype->count+ptype->countrand*frandom()); @@ -3570,17 +3604,30 @@ static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t dir, in mod = &ptype->models[rand() % ptype->nummodels]; if (!mod->model) mod->model = Mod_ForName(mod->name, MLV_WARN); - if (mod->model && !mod->model->needload) + if (mod->model && mod->model->loadstate == MLS_LOADED) { vec3_t morg, mdir; - PScript_ApplyOrgVel(morg, mdir, org, dir, i, count, ptype); - CL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?dir:NULL, mod->model, mod->framestart, (mod->frameend?mod->frameend:(mod->model->numframes - mod->framestart)), mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, mod->rflags & ~RF_USEORIENTATION); + PScript_ApplyOrgVel(morg, mdir, org, axis[0], i, count, ptype); + CL_SpawnSpriteEffect(morg, mdir, (mod->rflags&RF_USEORIENTATION)?axis[0]:NULL, mod->model, mod->framestart, (mod->frameend?mod->frameend:(mod->model->numframes - mod->framestart)), mod->framerate?mod->framerate:10, mod->alpha?mod->alpha:1, ptype->rotationmin*180/M_PI, ptype->gravity, mod->traileffect, mod->rflags & ~RF_USEORIENTATION); } } } - if (ptype->dl_radius) + if (ptype->dl_radius)// && r_rocketlight.value) { - dlight_t *dl = CL_NewDlight(dlkey, org, ptype->dl_radius, ptype->dl_time, ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]); + float radius; + dlight_t *dl; + + static int flickertime; + static int flicker; + int i = realtime*20; + if (flickertime != i) + { + flickertime = i; + flicker = rand(); + } + radius = ptype->dl_radius[0] + (r_lightflicker.ival?((flicker + dlkey*2000)&0xffff)*(1.0f/0xffff):0.5)*ptype->dl_radius[1]; + + dl = CL_NewDlight(dlkey, org, radius, ptype->dl_time, ptype->dl_rgb[0], ptype->dl_rgb[1], ptype->dl_rgb[2]); dl->channelfade[0] = ptype->dl_decay[0]; dl->channelfade[1] = ptype->dl_decay[1]; dl->channelfade[2] = ptype->dl_decay[2]; @@ -3595,7 +3642,7 @@ static void PScript_EffectSpawned(part_type_t *ptype, vec3_t org, vec3_t dir, in if (ptype->flags & PT_NODLSHADOW) dl->flags |= LFLAG_NOSHADOWS; if (ptype->dl_cubemapnum) - snprintf(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", ptype->dl_cubemapnum); + Q_snprintfz(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", ptype->dl_cubemapnum); } if (ptype->numsounds) { @@ -3695,8 +3742,17 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, goto skip; } - - PScript_EffectSpawned(ptype, org, dir, 0, count); + if (dir) + { + void PerpendicularVector( vec3_t dst, const vec3_t src ); + VectorCopy(dir, axis[2]); + VectorNormalize(axis[2]); + PerpendicularVector(axis[0], axis[2]); + VectorNormalize(axis[0]); + CrossProduct(axis[2], axis[0], axis[1]); + VectorNormalize(axis[1]); + } + PScript_EffectSpawned(ptype, org, axis, 0, count); if (ptype->looks.type == PT_CDECAL) { @@ -3897,17 +3953,6 @@ static int PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, break; } - if (dir) - { - void PerpendicularVector( vec3_t dst, const vec3_t src ); - VectorCopy(dir, axis[2]); - VectorNormalize(axis[2]); - PerpendicularVector(axis[0], axis[2]); - VectorNormalize(axis[0]); - CrossProduct(axis[2], axis[0], axis[1]); - VectorNormalize(axis[1]); - } - // time limit (for completeness) if (ptype->spawntime && ts) { @@ -4497,7 +4542,7 @@ static void PScript_RunParticleWeather(vec3_t minb, vec3_t maxb, vec3_t dir, flo } } -static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype, trailstate_t **tsk, int dlkey) +static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype, trailstate_t **tsk, int dlkey, vec3_t dlaxis[3]) { vec3_t vec, vstep, right, up, start; float len; @@ -4546,14 +4591,14 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype else ts = NULL; - PScript_EffectSpawned(ptype, start, vec3_origin, dlkey, 1); + PScript_EffectSpawned(ptype, start, dlaxis, dlkey, 1); if (ptype->assoc>=0) { if (ts) - P_ParticleTrail(start, end, ptype->assoc, dlkey, &(ts->assoc)); + P_ParticleTrail(start, end, ptype->assoc, dlkey, NULL, &(ts->assoc)); else - P_ParticleTrail(start, end, ptype->assoc, dlkey, NULL); + P_ParticleTrail(start, end, ptype->assoc, dlkey, NULL, NULL); } if (r_part_contentswitch.ival && (ptype->flags & (PT_TRUNDERWATER | PT_TROVERWATER)) && cl.worldmodel) @@ -4989,14 +5034,14 @@ static void P_ParticleTrailDraw (vec3_t startpos, vec3_t end, part_type_t *ptype return; } -static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, trailstate_t **tsk) +static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t axis[3], trailstate_t **tsk) { part_type_t *ptype = &part_type[type]; // TODO: fallback particle system won't have a decent trailstate which will mess up // high fps trails if (type >= FALLBACKBIAS && fallback) - return fallback->ParticleTrail(startpos, end, type-FALLBACKBIAS, dlkey, NULL); + return fallback->ParticleTrail(startpos, end, type-FALLBACKBIAS, dlkey, axis, NULL); if (type < 0 || type >= numparticletypes) return 1; //bad value @@ -5014,7 +5059,7 @@ static int PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, int dlk ptype = &part_type[ptype->inwater]; } - P_ParticleTrailDraw (startpos, end, ptype, tsk, dlkey); + P_ParticleTrailDraw (startpos, end, ptype, tsk, dlkey, axis); return 0; } @@ -5022,7 +5067,7 @@ static void PScript_ParticleTrailIndex (vec3_t start, vec3_t end, int color, int { part_type[pe_defaulttrail].colorindex = color; part_type[pe_defaulttrail].colorrand = crnd; - P_ParticleTrail(start, end, pe_defaulttrail, 0, tsk); + P_ParticleTrail(start, end, pe_defaulttrail, 0, NULL, tsk); } static vec3_t pright, pup; @@ -5766,7 +5811,7 @@ static void PScript_DrawParticleTypes (void) { if (type->clippeddecals) { - if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS)) + if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == 0) scenetri = &cl_stris[cl_numstris-1]; else { @@ -5777,7 +5822,7 @@ static void PScript_DrawParticleTypes (void) } scenetri = &cl_stris[cl_numstris++]; scenetri->shader = type->looks.shader; - scenetri->flags = BEF_NODLIGHT|BEF_NOSHADOWS; + scenetri->flags = 0; scenetri->firstidx = cl_numstrisidx; scenetri->firstvert = cl_numstrisvert; scenetri->numvert = 0; @@ -5890,7 +5935,7 @@ static void PScript_DrawParticleTypes (void) if (!tdraw || type->looks.shader->sort == SHADER_SORT_BLEND) scenetri = NULL; - else if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == (BEF_NODLIGHT|BEF_NOSHADOWS)) + else if (cl_numstris && cl_stris[cl_numstris-1].shader == type->looks.shader && cl_stris[cl_numstris-1].flags == 0) scenetri = &cl_stris[cl_numstris-1]; else { @@ -5903,7 +5948,7 @@ static void PScript_DrawParticleTypes (void) scenetri->shader = type->looks.shader; scenetri->firstidx = cl_numstrisidx; scenetri->firstvert = cl_numstrisvert; - scenetri->flags = BEF_NODLIGHT|BEF_NOSHADOWS; + scenetri->flags = 0; scenetri->numvert = 0; scenetri->numidx = 0; } @@ -6139,7 +6184,7 @@ static void PScript_DrawParticleTypes (void) if (type->emit >= 0) { if (type->emittime < 0) - P_ParticleTrail(oldorg, p->org, type->emit, 0, &p->state.trailstate); + P_ParticleTrail(oldorg, p->org, type->emit, 0, NULL, &p->state.trailstate); else if (p->state.nextemit < particletime) { p->state.nextemit = particletime + type->emittime + frandom()*type->emitrand; diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 3f05bb12..8d29363f 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -469,7 +469,7 @@ void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_g const char *sample = PR_GetStringOfs(prinst, OFS_PARM0); sfx_t *sfx = S_PrecacheSound(sample); - if (!sfx || sfx->failedload) + if (!sfx || sfx->loadstate != SLS_LOADED) G_FLOAT(OFS_RETURN) = 0; else { diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 592011ff..d9cb9989 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -70,6 +70,7 @@ static playerview_t *csqc_playerview; static qboolean csqc_isdarkplaces; static qboolean csqc_singlecheats; /*single player or cheats active, allowing custom addons*/ static qboolean csqc_mayread; //csqc is allowed to ReadByte(); +static qboolean csqc_worldchanged; //make sure any caches are rebuilt properly before the next renderscene static char csqc_printbuffer[8192]; @@ -476,9 +477,10 @@ static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t out->g[FS_REG].frametime[1] = in->xv->frame2time; } + +#if defined(SKELETALOBJECTS) || defined(RAGDOLL) out->bonecount = 0; out->bonestate = NULL; -#if defined(SKELETALOBJECTS) || defined(RAGDOLL) if (in->xv->skeletonindex) skel_lookup(csqcprogs, in->xv->skeletonindex, out); #endif @@ -740,7 +742,12 @@ static void QCBUILTIN PF_cs_makestatic (pubprogfuncs_t *prinst, struct globalvar ent = &cl_static_entities[cl.num_statics].ent; if (CopyCSQCEdictToEntity(in, ent)) { + cl_static_entities[cl.num_statics].emit = NULL; cl_static_entities[cl.num_statics].mdlidx = in->v->modelindex; + if (cl.worldmodel && cl.worldmodel->funcs.FindTouchedLeafs) + cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[cl.num_statics].pvscache, in->v->absmin, in->v->absmax); + else + memset(&cl_static_entities[cl.num_statics].pvscache, 0, sizeof(cl_static_entities[cl.num_statics].pvscache)); cl.num_statics++; } @@ -828,7 +835,7 @@ static void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globa s = PR_GetStringOfs(prinst, OFS_PARM2); Q_strncpyz(l->cubemapname, s, sizeof(l->cubemapname)); if (*l->cubemapname) - l->cubetexture = R_LoadReplacementTexture(l->cubemapname, "", IF_CUBEMAP); + l->cubetexture = R_LoadReplacementTexture(l->cubemapname, "", IF_CUBEMAP, NULL, 0, 0, TF_INVALID); else l->cubetexture = r_nulltex; break; @@ -842,6 +849,11 @@ static void QCBUILTIN PF_R_DynamicLight_Set(pubprogfuncs_t *prinst, struct globa case lfield_specularscale: l->lightcolourscales[2] = G_FLOAT(OFS_PARM2); break; + case lfield_rotation: + l->rotation[0] = G_FLOAT(OFS_PARM2+0); + l->rotation[1] = G_FLOAT(OFS_PARM2+1); + l->rotation[2] = G_FLOAT(OFS_PARM2+2); + break; #endif default: break; @@ -907,6 +919,11 @@ static void QCBUILTIN PF_R_DynamicLight_Get(pubprogfuncs_t *prinst, struct globa case lfield_specularscale: G_FLOAT(OFS_RETURN) = l->lightcolourscales[2]; break; + case lfield_rotation: + G_FLOAT(OFS_RETURN+0) = l->rotation[0]; + G_FLOAT(OFS_RETURN+1) = l->rotation[1]; + G_FLOAT(OFS_RETURN+2) = l->rotation[2]; + break; #endif default: G_INT(OFS_RETURN) = 0; @@ -938,9 +955,12 @@ void QCBUILTIN PF_R_DynamicLight_Add(pubprogfuncs_t *prinst, struct globalvars_s //if the org matches self, then attach it. dl = CL_NewDlight (dlkey, org, radius, -0.1, rgb[0], rgb[1], rgb[2]); - VectorCopy(csqcg.forward, dl->axis[0]); - VectorCopy(csqcg.right, dl->axis[1]); - VectorCopy(csqcg.up, dl->axis[2]); + if (*dl->cubemapname) + { + VectorCopy(csqcg.forward, dl->axis[0]); + VectorCopy(csqcg.right, dl->axis[1]); + VectorCopy(csqcg.up, dl->axis[2]); + } if (pflags & PFLAGS_NOSHADOW) dl->flags |= LFLAG_NOSHADOWS; @@ -951,7 +971,7 @@ void QCBUILTIN PF_R_DynamicLight_Add(pubprogfuncs_t *prinst, struct globalvars_s dl->style = style; Q_strncpyz(dl->cubemapname, cubemapname, sizeof(dl->cubemapname)); if (*dl->cubemapname) - dl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, "", IF_CUBEMAP); + dl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, "", IF_CUBEMAP, NULL, 0, 0, TF_INVALID); else dl->cubetexture = r_nulltex; @@ -1344,14 +1364,14 @@ void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ break; case VF_CL_VIEWANGLES_V: - if (r_refdef.playerview) - VectorCopy(r_refdef.playerview->viewangles, r); + if (csqc_playerview) + VectorCopy(csqc_playerview->viewangles, r); break; case VF_CL_VIEWANGLES_X: case VF_CL_VIEWANGLES_Y: case VF_CL_VIEWANGLES_Z: - if (r_refdef.playerview) - *r = r_refdef.playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X]; + if (csqc_playerview) + *r = csqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X]; break; case VF_CARTESIAN_ANGLES: @@ -1473,13 +1493,13 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ case VF_ORIGIN: VectorCopy(p, r_refdef.vieworg); - if (r_refdef.playerview) - r_refdef.playerview->crouch = 0; + if (csqc_playerview) + csqc_playerview->crouch = 0; break; case VF_ORIGIN_Z: - if (r_refdef.playerview) - r_refdef.playerview->crouch = 0; + if (csqc_playerview) + csqc_playerview->crouch = 0; case VF_ORIGIN_X: case VF_ORIGIN_Y: r_refdef.vieworg[parametertype-VF_ORIGIN_X] = *p; @@ -1495,14 +1515,14 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ break; case VF_CL_VIEWANGLES_V: - if (r_refdef.playerview) - VectorCopy(p, r_refdef.playerview->viewangles); + if (csqc_playerview) + VectorCopy(p, csqc_playerview->viewangles); break; case VF_CL_VIEWANGLES_X: case VF_CL_VIEWANGLES_Y: case VF_CL_VIEWANGLES_Z: - if (r_refdef.playerview) - r_refdef.playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X] = *p; + if (csqc_playerview) + csqc_playerview->viewangles[parametertype-VF_CL_VIEWANGLES_X] = *p; break; case VF_CARTESIAN_ANGLES: @@ -1611,6 +1631,12 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ void R2D_PolyBlend (void); static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + if (csqc_worldchanged) + { + csqc_worldchanged = false; + Surf_NewMap(); + } + if (cl.worldmodel) R_PushDlights (); @@ -1704,24 +1730,42 @@ static void QCBUILTIN PF_cs_getstats(pubprogfuncs_t *prinst, struct globalvars_s static void QCBUILTIN PF_cs_SetOrigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); + world_t *w = prinst->parms->user; + wedict_t *ent = (void*)G_WEDICT(prinst, OFS_PARM0); float *org = G_VECTOR(OFS_PARM1); - + if (ent->readonly) + { + Con_Printf("setorigin on entity %i\n", ent->entnum); + return; + } VectorCopy(org, ent->v->origin); - - World_LinkEdict(&csqc_world, (wedict_t*)ent, false); + World_LinkEdict(w, (wedict_t*)ent, false); } -static void QCBUILTIN PF_cs_SetSize(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +static void QCBUILTIN PF_cs_SetSize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); - float *mins = G_VECTOR(OFS_PARM1); - float *maxs = G_VECTOR(OFS_PARM2); + world_t *w = prinst->parms->user; + wedict_t *e; + float *min, *max; - VectorCopy(mins, ent->v->mins); - VectorCopy(maxs, ent->v->maxs); - - World_LinkEdict(&csqc_world, (wedict_t*)ent, false); + e = G_WEDICT(prinst, OFS_PARM0); + if (e->isfree) + { + Con_TPrintf("%s edict was free\n", "setsize"); + prinst->pr_trace = 1; + return; + } + if (e->readonly) + { + Con_TPrintf("setsize on entity %i\n", e->entnum); + return; + } + min = G_VECTOR(OFS_PARM1); + max = G_VECTOR(OFS_PARM2); + VectorCopy (min, e->v->mins); + VectorCopy (max, e->v->maxs); + VectorSubtract (max, min, e->v->size); + World_LinkEdict (w, (wedict_t*)e, false); } static void cs_settracevars(trace_t *tr, struct globalvars_s *pr_globals) @@ -1880,7 +1924,7 @@ static void QCBUILTIN PF_cs_pointcontents(pubprogfuncs_t *prinst, struct globalv G_FLOAT(OFS_RETURN) = Q1CONTENTS_EMPTY; } -static int FindModel(const char *name, int *free) +static int CS_FindModel(const char *name, int *free) { int i; @@ -1889,6 +1933,8 @@ static int FindModel(const char *name, int *free) if (!name || !*name) return 0; + name = Mod_FixName(name, csqc_world.worldmodel->name); + for (i = 1; i < MAX_CSMODELS; i++) { if (!*cl.model_csqcname[i]) @@ -1907,31 +1953,48 @@ static int FindModel(const char *name, int *free) return 0; } -static void csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelindex) +static model_t *csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelindex) { model_t *model; + + if (ent->readonly) + { + Con_Printf("setmodel on readonly entity %i\n", ent->entnum); + return NULL; + } ent->v->modelindex = modelindex; if (modelindex < 0) { if (modelindex <= -MAX_CSMODELS) - return; + return NULL; ent->v->model = PR_SetString(prinst, cl.model_csqcname[-modelindex]); if (!cl.model_csqcprecache[-modelindex]) - cl.model_csqcprecache[-modelindex] = Mod_ForName(cl.model_csqcname[-modelindex], MLV_WARN); + cl.model_csqcprecache[-modelindex] = Mod_ForName(Mod_FixName(cl.model_csqcname[-modelindex], csqc_world.worldmodel->name), MLV_WARN); model = cl.model_csqcprecache[-modelindex]; } else { if (modelindex >= MAX_PRECACHE_MODELS) - return; + return NULL; ent->v->model = PR_SetString(prinst, cl.model_name[modelindex]); model = cl.model_precache[modelindex]; } if (model) { + //csqc probably needs to know the actual model size for any entity. it might as well. + while(model->loadstate == MLS_LOADING) + COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING); + VectorCopy(model->mins, ent->v->mins); VectorCopy(model->maxs, ent->v->maxs); + VectorSubtract (model->maxs, model->mins, ent->v->size); + + if (!ent->entnum) + { + cl.worldmodel = r_worldentity.model = csqc_world.worldmodel = model; + csqc_worldchanged = true; + } } else { @@ -1940,6 +2003,8 @@ static void csqc_setmodel(pubprogfuncs_t *prinst, csqcedict_t *ent, int modelind } World_LinkEdict(&csqc_world, (wedict_t*)ent, false); + + return model; } static void QCBUILTIN PF_cs_SetModel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -1947,7 +2012,8 @@ static void QCBUILTIN PF_cs_SetModel(pubprogfuncs_t *prinst, struct globalvars_s csqcedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); const char *modelname = PR_GetStringOfs(prinst, OFS_PARM1); int freei; - int modelindex = FindModel(modelname, &freei); + int modelindex = CS_FindModel(modelname, &freei); + model_t *mod; if (!modelindex && modelname && *modelname) { @@ -1960,7 +2026,9 @@ static void QCBUILTIN PF_cs_SetModel(pubprogfuncs_t *prinst, struct globalvars_s cl.model_csqcprecache[-freei] = NULL; } - csqc_setmodel(prinst, ent, modelindex); + mod = csqc_setmodel(prinst, ent, modelindex); + if (mod) + ent->xv->modelflags = mod->flags; } static void QCBUILTIN PF_cs_SetModelIndex(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -1973,6 +2041,7 @@ static void QCBUILTIN PF_cs_PrecacheModel(pubprogfuncs_t *prinst, struct globalv { int modelindex, freei; const char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); + const char *fixedname; int i; if (!*modelname) @@ -1981,28 +2050,32 @@ static void QCBUILTIN PF_cs_PrecacheModel(pubprogfuncs_t *prinst, struct globalv return; } + fixedname = Mod_FixName(modelname, csqc_world.worldmodel->name); + for (i = 1; i < MAX_PRECACHE_MODELS; i++) //Make sure that the server specified model is loaded.. { if (!*cl.model_name[i]) break; - if (!strcmp(cl.model_name[i], modelname)) + if (!strcmp(cl.model_name[i], fixedname)) { - cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); + if (!cl.model_precache[i]) + cl.model_precache[i] = Mod_ForName(cl.model_name[i], MLV_WARN); break; } } - modelindex = FindModel(modelname, &freei); //now load it + modelindex = CS_FindModel(modelname, &freei); //now load it if (!modelindex) { if (!freei) Host_EndGame("CSQC ran out of model slots\n"); - Q_strncpyz(cl.model_csqcname[-freei], modelname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now + fixedname = Mod_FixName(modelname, csqc_world.worldmodel->name); + Q_strncpyz(cl.model_csqcname[-freei], fixedname, sizeof(cl.model_csqcname[-freei])); //allocate a slot now modelindex = freei; CL_CheckOrEnqueDownloadFile(modelname, modelname, 0); - cl.model_csqcprecache[-freei] = NULL; + cl.model_csqcprecache[-freei] = Mod_ForName(fixedname, MLV_WARN); } G_FLOAT(OFS_RETURN) = modelindex; @@ -2197,7 +2270,7 @@ static void QCBUILTIN PF_cs_boxparticles(pubprogfuncs_t *prinst, struct globalva if (flags & 128) { flags &= ~128; - P_ParticleTrail(org_from, org_to, effectnum, 0, NULL); + P_ParticleTrail(org_from, org_to, effectnum, 0, NULL, NULL); } else { @@ -2244,9 +2317,25 @@ static void QCBUILTIN PF_cs_trailparticles (pubprogfuncs_t *prinst, struct globa efnum = CL_TranslateParticleFromServer(efnum); if (!ent->entnum) //world trails are non-state-based. - pe->ParticleTrail(start, end, efnum, 0, NULL); + pe->ParticleTrail(start, end, efnum, 0, NULL, NULL); else - pe->ParticleTrail(start, end, efnum, -ent->entnum, &ent->trailstate); + pe->ParticleTrail(start, end, efnum, -ent->entnum, NULL, &ent->trailstate); +} + +void CSQC_ResetTrails(void) +{ + pubprogfuncs_t *prinst = csqc_world.progs; + int i; + csqcedict_t *ent; + + if (!prinst) + return; + + for (i = 0; i < *prinst->parms->sv_num_edicts; i++) + { + ent = (csqcedict_t*)EDICT_NUM(prinst, i); + ent->trailstate = NULL; + } } //0 for error, non-0 for success. @@ -2535,6 +2624,25 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo if (ent) { + int mt = ent->v->movetype; + if (ent->xv->entnum) + pmove.skipent = ent->xv->entnum; + else + pmove.skipent = -1; + mt &= 255; + switch(mt) + { + default: + case MOVETYPE_WALK: + pmove.pm_type = PM_NORMAL; + break; + case MOVETYPE_NOCLIP: + pmove.pm_type = PM_SPECTATOR; + break; + case MOVETYPE_FLY: + pmove.pm_type = PM_FLY; + break; + } pmove.jump_held = (int)ent->xv->pmove_flags & PMF_JUMP_HELD; pmove.waterjumptime = 0; VectorCopy(ent->v->origin, pmove.origin); @@ -2576,6 +2684,9 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo ent->xv->pmove_flags = 0; ent->xv->pmove_flags += pmove.jump_held ? PMF_JUMP_HELD : 0; ent->xv->pmove_flags += pmove.onladder ? PMF_LADDER : 0; + + //fixme: touch triggers? + World_LinkEdict (&csqc_world, (wedict_t*)ent, true); } else { @@ -2587,10 +2698,6 @@ static void QCBUILTIN PF_cs_runplayerphysics (pubprogfuncs_t *prinst, struct glo VectorCopy(pmove.origin, csqcg.pmove_org); VectorCopy(pmove.velocity, csqcg.pmove_vel); } - - //fixme: touch solids - - World_LinkEdict (&csqc_world, (wedict_t*)ent, true); } static void QCBUILTIN PF_cs_getentitytoken (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3465,7 +3572,7 @@ static void QCBUILTIN PF_cs_addprogs (pubprogfuncs_t *prinst, struct globalvars_ newp = -1; else { - newp = PR_LoadProgs(prinst, s, 0, NULL, 0); + newp = PR_LoadProgs(prinst, s, NULL, 0); if (newp >= 0) PR_ProgsAdded(csqcprogs, newp, s); } @@ -3474,6 +3581,7 @@ static void QCBUILTIN PF_cs_addprogs (pubprogfuncs_t *prinst, struct globalvars_ static void QCBUILTIN PF_cs_OpenPortal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { +/* #ifdef Q2BSPS if (cl.worldmodel->fromgame == fg_quake2) { @@ -3482,15 +3590,16 @@ static void QCBUILTIN PF_cs_OpenPortal (pubprogfuncs_t *prinst, struct globalvar if (G_INT(OFS_PARM1) >= MAX_EDICTS) portal = G_FLOAT(OFS_PARM0); //old legacy crap. else - portal = G_EDICT(prinst, OFS_PARM0)->xv->style; //read the func_areaportal's style field. + portal = G_WEDICT(prinst, OFS_PARM0)->xv->style; //read the func_areaportal's style field. CMQ2_SetAreaPortalState(portal, state); } #endif +*/ #ifdef Q3BSPS if (cl.worldmodel->fromgame == fg_quake3) { int state = G_FLOAT(OFS_PARM1)!=0; - edict_t *portal = G_EDICT(prinst, OFS_PARM0); + wedict_t *portal = G_WEDICT(prinst, OFS_PARM0); int area1 = portal->pvsinfo.areanum, area2 = portal->pvsinfo.areanum2; if (area1 == area2 || area1<0 || area2<0) return; @@ -3756,7 +3865,7 @@ void CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src //use entnum as a test to see if its new (if the old origin isn't usable) if (ent->xv->entnum) { - if (model->particletrail == P_INVALID || pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, src->number, &(le->trailstate))) + if (model->particletrail == P_INVALID || pe->ParticleTrail (ent->v->origin, src->origin, model->particletrail, src->number, NULL, &(le->trailstate))) if (model->traildefaultindex >= 0) pe->ParticleTrailIndex(ent->v->origin, src->origin, model->traildefaultindex, 0, &(le->trailstate)); } @@ -4345,6 +4454,143 @@ static void QCBUILTIN PF_ReadServerEntityState(pubprogfuncs_t *prinst, struct gl newlist->numents = newidx; } #endif +//be careful to not touch the resource unless we're meant to, to avoid stalling +static void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int restype = G_FLOAT(OFS_PARM0); + int doload = G_FLOAT(OFS_PARM1); + const char *resname = PR_GetStringOfs(prinst, OFS_PARM2); + int idx, idx2; + model_t *mod; + image_t *img; +// shader_t *sh; + sfx_t *sfx; + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + switch(restype) + { + case RESTYPE_MODEL: + idx = CS_FindModel(resname, &idx2); + if (idx < 0) + { + mod = cl.model_csqcprecache[-idx]; + if (!cl.model_csqcprecache[-idx] && doload) + mod = cl.model_csqcprecache[-idx] = Mod_ForName(Mod_FixName(cl.model_csqcname[-idx], cl.model_name[1]), MLV_WARN); + } + else if (idx > 0) + { + mod = cl.model_precache[idx]; + if (!cl.model_precache[idx] && doload) + mod = cl.model_precache[idx] = Mod_ForName(Mod_FixName(cl.model_name[idx], cl.model_name[1]), MLV_WARN); + } + else + return; + + if (!mod) + G_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED; + else + { + if (doload && mod->loadstate == MLS_NOTLOADED) + Mod_LoadModel (mod, MLV_SILENT); //should avoid blocking. + switch(mod->loadstate) + { + default: + case MLS_NOTLOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED; + break; + case MLS_LOADING: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADING; + break; + case MLS_LOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADED; + break; + case MLS_FAILED: + G_FLOAT(OFS_RETURN) = RESSTATE_FAILED; + break; + } + } + return; + case RESTYPE_SOUND: + sfx = NULL; + for (idx=1 ; idxloadstate == SLS_NOTLOADED) + S_LoadSound(sfx); + switch(sfx->loadstate) + { + case SLS_NOTLOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED; + break; + case SLS_LOADING: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADING; + break; + case SLS_LOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADED; + break; + case SLS_FAILED: + G_FLOAT(OFS_RETURN) = RESSTATE_FAILED; + break; + } + } + break; +/* + case RESTYPE_PARTICLE: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + break; + case RESTYPE_SHADER: + sh = R_RegisterCustom(resname, 0, NULL, NULL); + if (sh) + { + //FIXME: scan through the images. + } + else + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + break; + case RESTYPE_SKIN: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + break; +*/ + case RESTYPE_TEXTURE: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + img = Image_FindTexture(resname, NULL, 0); + if (!img) + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + else + { + switch(img->status) + { + case TEX_NOTLOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED; + break; + case TEX_LOADING: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADING; + break; + case TEX_LOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADED; + break; + case TEX_FAILED: + G_FLOAT(OFS_RETURN) = RESSTATE_FAILED; + break; + } + } + break; + default: + G_FLOAT(OFS_RETURN) = RESSTATE_UNSUPPORTED; + break; + } +} + #define PF_FixTen PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme,PF_Fixme @@ -4585,6 +4831,7 @@ static struct { {"skel_set_bone_world", PF_skel_set_bone_world, 283}, {"frametoname", PF_frametoname, 284},//string(float modidx, float framenum) frametoname {"skintoname", PF_skintoname, 285},//string(float modidx, float skin) skintoname + {"resourcestatus", PF_resourcestatus, 286}, {"hash_createtab", PF_hash_createtab, 287}, {"hash_destroytab", PF_hash_destroytab, 288}, @@ -5146,6 +5393,7 @@ void CSQC_World_GetFrameState(world_t *w, wedict_t *win, framestate_t *out) void CSQC_Shutdown(void) { + int i; if (csqcprogs) { key_dest_absolutemouse &= ~kdm_game; @@ -5174,6 +5422,12 @@ void CSQC_Shutdown(void) csqcent = NULL; maxcsqcentities = 0; + for (i = 0; i < MAX_CSPARTICLESPRE && cl.particle_csname[i]; i++) + { + free(cl.particle_csname[i]); + cl.particle_csname[i] = NULL; + } + csqcmapentitydata = NULL; csqcmapentitydataloaded = false; @@ -5182,9 +5436,12 @@ void CSQC_Shutdown(void) } //when the qclib needs a file, it calls out to this function. -qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize) +qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize, size_t *sz) { qbyte *file; + size_t ssz; + if (!sz) + sz = &ssz; if (!strcmp(path, "csprogs.dat")) { @@ -5193,40 +5450,40 @@ qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize) if (csprogs_checksum) { - file = COM_LoadStackFile(newname, buffer, bufsize); + file = COM_LoadStackFile(newname, buffer, bufsize, sz); if (file) { if (cls.protocol == CP_NETQUAKE) { - if (QCRC_Block(file, com_filesize) == csprogs_checksum) + if (QCRC_Block(file, *sz) == csprogs_checksum) return file; } else { - if (LittleLong(Com_BlockChecksum(file, com_filesize)) == csprogs_checksum) //and the user wasn't trying to be cunning. + if (LittleLong(Com_BlockChecksum(file, *sz)) == csprogs_checksum) //and the user wasn't trying to be cunning. return file; } } } - file = COM_LoadStackFile(path, buffer, bufsize); + file = COM_LoadStackFile(path, buffer, bufsize, sz); if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum { if (csprogs_checksum && !csprogs_promiscuous) { if (cls.protocol == CP_NETQUAKE) { - if (QCRC_Block(file, com_filesize) != csprogs_checksum) + if (QCRC_Block(file, *sz) != csprogs_checksum) return NULL; } else { - if (LittleLong(Com_BlockChecksum(file, com_filesize)) != csprogs_checksum) + if (LittleLong(Com_BlockChecksum(file, *sz)) != csprogs_checksum) return NULL; //not valid } //back it up - COM_WriteFile(newname, file, com_filesize); + COM_WriteFile(newname, file, *sz); } } @@ -5234,12 +5491,13 @@ qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize) } - return COM_LoadStackFile(path, buffer, bufsize); + return COM_LoadStackFile(path, buffer, bufsize, NULL); } int QDECL CSQC_PRFileSize (const char *path) { qbyte *file; + size_t sz; if (!strcmp(path, "csprogs.dat")) { @@ -5248,35 +5506,35 @@ int QDECL CSQC_PRFileSize (const char *path) if (csprogs_checksum) { - file = COM_LoadTempFile (newname); + file = COM_LoadTempFile (newname, &sz); if (file) { if (cls.protocol == CP_NETQUAKE) { - if (QCRC_Block(file, com_filesize) == csprogs_checksum) - return com_filesize+1; + if (QCRC_Block(file, sz) == csprogs_checksum) + return sz+1; } else { - if (LittleLong(Com_BlockChecksum(file, com_filesize)) == csprogs_checksum) //and the user wasn't trying to be cunning. - return com_filesize+1; + if (LittleLong(Com_BlockChecksum(file, sz)) == csprogs_checksum) //and the user wasn't trying to be cunning. + return sz+1; } } } - file = COM_LoadTempFile(path); + file = COM_LoadTempFile(path, &sz); if (file && !cls.demoplayback) //allow them to use csprogs.dat if playing a demo, and don't care about the checksum { if (csprogs_checksum && !csprogs_promiscuous) { if (cls.protocol == CP_NETQUAKE) { - if (QCRC_Block(file, com_filesize) != csprogs_checksum) + if (QCRC_Block(file, sz) != csprogs_checksum) return -1; //not valid } else { - if (LittleLong(Com_BlockChecksum(file, com_filesize)) != csprogs_checksum) + if (LittleLong(Com_BlockChecksum(file, sz)) != csprogs_checksum) return -1; //not valid } } @@ -5284,7 +5542,7 @@ int QDECL CSQC_PRFileSize (const char *path) if (!file) return -1; - return com_filesize; + return sz; } return COM_FileSize(path); @@ -5318,6 +5576,44 @@ qboolean CSQC_UnconnectedInit(void) return true; return CSQC_Init(true, true, 0); } +void ASMCALL CSQC_StateOp(pubprogfuncs_t *prinst, float var, func_t func) +{ + world_t *w = prinst->parms->user; + stdentvars_t *vars = PROG_TO_EDICT(prinst, *w->g.self)->v; + vars->nextthink = *w->g.time+0.1; + vars->think = func; + vars->frame = var; +} +void ASMCALL CSQC_CStateOp(pubprogfuncs_t *progs, float min, float max, func_t func) +{ + Con_Printf("CSQC_CStateOp: not implemented\n"); +} +void ASMCALL CSQC_CWStateOp(pubprogfuncs_t *progs, float min, float max, func_t func) +{ + Con_Printf("CSQC_CWStateOp: not implemented\n"); +} +void ASMCALL CSQC_ThinkTimeOp(pubprogfuncs_t *progs, edict_t *ed, float var) +{ + world_t *w = progs->parms->user; + stdentvars_t *vars = ed->v; + vars->nextthink = *w->g.time+var; +} + +pbool PDECL CSQC_CheckHeaderCrc(pubprogfuncs_t *progs, progsnum_t num, int crc) +{ + if (!num) + { + if (crc == 22390) + csqc_isdarkplaces = false; + else + { + if (crc == 52195) + csqc_isdarkplaces = true; + Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); + } + } + return true; +} double csqctime; qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checksum) @@ -5354,6 +5650,22 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks if (cl_nocsqc.value) return false; + if (cls.state == ca_disconnected) + { + movevars.gravity = 800; + movevars.entgravity = 1; + movevars.maxspeed = 320; + movevars.bunnyspeedcap = 0;//pm_bunnyspeedcap.value; + movevars.ktjump = false;//pm_ktjump.value; + movevars.slidefix = true;//(pm_slidefix.value != 0); + movevars.airstep = true;//(pm_airstep.value != 0); + movevars.walljump = false;//(pm_walljump.value); + movevars.slidyslopes = false;//(pm_slidyslopes.value!=0); + movevars.watersinkspeed = 60;//*pm_watersinkspeed.string?pm_watersinkspeed.value:60; + movevars.flyfriction = 4;//*pm_flyfriction.string?pm_flyfriction.value:4; + movevars.stepheight = PM_DEFAULTSTEPHEIGHT; + } + for (i = 0; i < sizeof(csqc_builtin)/sizeof(csqc_builtin[0]); i++) csqc_builtin[i] = PF_Fixme; for (i = 0; BuiltinList[i].bifunc; i++) @@ -5373,14 +5685,15 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks csqcprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...); csqcprogparms.Sys_Error = Sys_Error; csqcprogparms.Abort = CSQC_Abort; + csqcprogparms.CheckHeaderCrc = CSQC_CheckHeaderCrc; csqcprogparms.edictsize = sizeof(csqcedict_t); csqcprogparms.entspawn = CSQC_EntSpawn;//void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set csqcprogparms.entcanfree = CSQC_EntFree;//bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed - csqcprogparms.stateop = NULL;//StateOp;//void (*stateop) (float var, func_t func); - csqcprogparms.cstateop = NULL;//CStateOp; - csqcprogparms.cwstateop = NULL;//CWStateOp; - csqcprogparms.thinktimeop = NULL;//ThinkTimeOp; + csqcprogparms.stateop = CSQC_StateOp;//StateOp;//void (*stateop) (float var, func_t func); + csqcprogparms.cstateop = CSQC_CStateOp;//CStateOp; + csqcprogparms.cwstateop = CSQC_CWStateOp;//CWStateOp; + csqcprogparms.thinktimeop = CSQC_ThinkTimeOp;//ThinkTimeOp; //used when loading a game csqcprogparms.builtinsfor = NULL;//builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved. @@ -5414,7 +5727,7 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks csqcprogs = InitProgs(&csqcprogparms); csqc_world.progs = csqcprogs; csqc_world.usesolidcorpse = true; - PR_Configure(csqcprogs, pr_csqc_memsize.ival, 16, pr_enable_profiling.ival); + PR_Configure(csqcprogs, pr_csqc_memsize.ival, MAX_PROGS, pr_enable_profiling.ival); csqc_world.worldmodel = cl.worldmodel; csqc_world.Event_Touch = CSQC_Event_Touch; csqc_world.Event_Think = CSQC_Event_Think; @@ -5435,25 +5748,14 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks csqc_isdarkplaces = false; if (csdatenabled || csqc_singlecheats || anycsqc) { - csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat", 22390, NULL, 0); - if (csprogsnum == -1) - { - csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat", 52195, NULL, 0); - if (csprogsnum >= 0) - csqc_isdarkplaces = true; - else - csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat", 0, NULL, 0); - - if (csprogsnum != -1) - Con_Printf(CON_WARNING "Running outdated or unknown csprogs.dat version\n"); - } + csprogsnum = PR_LoadProgs(csqcprogs, "csprogs.dat", NULL, 0); if (csprogsnum == -1) Con_DPrintf("Loaded csprogs.dat\n"); } if (csqc_singlecheats || anycsqc) { - csaddonnum = PR_LoadProgs(csqcprogs, "csaddon.dat", 0, NULL, 0); + csaddonnum = PR_LoadProgs(csqcprogs, "csaddon.dat", NULL, 0); if (csaddonnum >= 0) Con_DPrintf("loaded csaddon.dat...\n"); else @@ -5474,6 +5776,9 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks PF_InitTempStrings(csqcprogs); csqc_world.physicstime = 0; + if (!csqc_world.worldmodel) + csqc_world.worldmodel = Mod_ForName("", MLV_SILENT); + csqc_worldchanged = false; memset(csqcent, 0, sizeof(*csqcent)*maxcsqcentities); //clear the server->csqc entity translations. @@ -5496,6 +5801,17 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks worldent = (csqcedict_t *)EDICT_NUM(csqcprogs, 0); worldent->isfree = false; + for (i = 0; i < csqcprogs->numprogs; i++) + { + func_t f = PR_FindFunction (csqcprogs, "initents", i); + if (f) + { + void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); + G_PROG(OFS_PARM0) = i-1; + PR_ExecuteProgram(csqcprogs, f); + } + } + /*DP compat*/ str = (string_t*)csqcprogs->GetEdictFieldValue(csqcprogs, (edict_t*)worldent, "message", NULL); if (str) @@ -5521,6 +5837,8 @@ qboolean CSQC_Init (qboolean anycsqc, qboolean csdatenabled, unsigned int checks Con_DPrintf("Loaded csqc\n"); csqcmapentitydataloaded = false; + + csqc_world.physicstime = 0.1; } return true; //success! @@ -5544,19 +5862,14 @@ void CSQC_RendererRestarted(void) void CSQC_WorldLoaded(void) { - char *map; csqcedict_t *worldent; - const char *entfile; if (!csqcprogs) return; if (csqcmapentitydataloaded) return; csqcmapentitydataloaded = true; - map = Info_ValueForKey(cl.serverinfo, "map"); - entfile = csqcmapentitydata = map?FS_LoadMallocFile(va("maps/%s.ent", map)):NULL; - if (!csqcmapentitydata) - csqcmapentitydata = cl.worldmodel->entities; + csqcmapentitydata = cl.worldmodel->entities; csqc_world.worldmodel = cl.worldmodel; #ifdef USEODE @@ -5572,9 +5885,10 @@ void CSQC_WorldLoaded(void) if (csqcg.worldloaded) PR_ExecuteProgram(csqcprogs, csqcg.worldloaded); csqcmapentitydata = NULL; - BZ_Free((char*)entfile); worldent->readonly = true; + + csqc_worldchanged = false; //should be done elsewhere, don't do it for double-cost. } void CSQC_CoreDump(void) @@ -5872,13 +6186,13 @@ qboolean CSQC_DrawView(void) } if (host_frametime > mintic) host_frametime = mintic; - csqc_world.physicstime += host_frametime; #ifdef USEODE World_ODE_Frame(&csqc_world, host_frametime, 800); #endif World_Physics_Frame(&csqc_world); + csqc_world.physicstime += host_frametime; } } @@ -6341,6 +6655,15 @@ int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float return false; } +void CSQC_GetEntityOrigin(unsigned int csqcent, float *out) +{ + wedict_t *ent; + if (!csqcprogs) + return; + ent = WEDICT_NUM(csqcprogs, csqcent); + VectorCopy(ent->v->origin, out); +} + void CSQC_ParseEntities(void) { csqcedict_t *ent; @@ -6355,7 +6678,7 @@ void CSQC_ParseEntities(void) if (!csqcg.ent_update || !csqcg.self) Host_EndGame("CSQC has no CSQC_Ent_Update function\n"); - if (!csqc_world.worldmodel || csqc_world.worldmodel->needload) + if (!csqc_world.worldmodel || csqc_world.worldmodel->loadstate != MLS_LOADED) Host_EndGame("world is not yet initialised\n"); pr_globals = PR_globals(csqcprogs, PR_CURRENT); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 008c7a22..fa4f7559 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -437,7 +437,7 @@ void QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl mpic_t *p; p = R2D_SafeCachePic(picname); - if (!p) + if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) p = R2D_SafePicFromWad(picname); if (!p) @@ -511,7 +511,7 @@ void QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s * { pic = R2D_SafeCachePic(str); - if ((!pic || (pic->flags & SHADER_NOIMAGE)) && cls.state + if ((!pic || !R_GetShaderSizes(pic, NULL, NULL, true)) && cls.state #ifndef CLIENTONLY && !sv.active #endif @@ -671,11 +671,12 @@ void QCBUILTIN PF_CL_drawgetimagesize (pubprogfuncs_t *prinst, struct globalvars mpic_t *p = R2D_SafeCachePic(picname); float *ret = G_VECTOR(OFS_RETURN); - - if (p) + int iw, ih; + + if (R_GetShaderSizes(p, &iw, &ih, true) > 0) { - ret[0] = p->width; - ret[1] = p->height; + ret[0] = iw; + ret[1] = ih; ret[2] = 0; } else @@ -814,7 +815,7 @@ void QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_ if (!con) return; PF_sprintf_internal(prinst, pr_globals, fmt, 2, outbuf, sizeof(outbuf)); - Con_PrintCon(con, outbuf); + Con_PrintCon(con, outbuf, con->parseflags); } void QCBUILTIN PF_SubConDraw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -2071,6 +2072,11 @@ void MP_CvarChanged(cvar_t *var) } } +pbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc) +{ + return crc == 10020; +} + double menutime; qboolean MP_Init (void) { @@ -2097,6 +2103,7 @@ qboolean MP_Init (void) menuprogparms.Printf = (void *)Con_Printf;//Con_Printf;//void (*printf) (char *, ...); menuprogparms.Sys_Error = Sys_Error; menuprogparms.Abort = Menu_Abort; + menuprogparms.CheckHeaderCrc = Menu_CheckHeaderCrc; menuprogparms.edictsize = sizeof(menuedict_t); menuprogparms.entspawn = NULL;//void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set @@ -2136,7 +2143,7 @@ qboolean MP_Init (void) Con_DPrintf("Initializing menu.dat\n"); menu_world.progs = InitProgs(&menuprogparms); PR_Configure(menu_world.progs, 64*1024*1024, 1, pr_enable_profiling.ival); - mprogs = PR_LoadProgs(menu_world.progs, "menu.dat", 10020, NULL, 0); + mprogs = PR_LoadProgs(menu_world.progs, "menu.dat", NULL, 0); if (mprogs < 0) //no per-progs builtins. { //failed to load or something @@ -2236,10 +2243,10 @@ void MP_Reload_f(void) static void MP_Poke_f(void) { - if (!SV_MayCheat()) + /*if (!SV_MayCheat()) Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n"); - else if (svprogfuncs && svprogfuncs->EvaluateDebugString) - Con_TPrintf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args())); + else */if (menu_world.progs && menu_world.progs->EvaluateDebugString) + Con_TPrintf("Result: %s\n", menu_world.progs->EvaluateDebugString(menu_world.progs, Cmd_Args())); else Con_TPrintf ("not supported.\n"); } diff --git a/engine/client/quakedef.h b/engine/client/quakedef.h index 50832933..3b04901a 100644 --- a/engine/client/quakedef.h +++ b/engine/client/quakedef.h @@ -256,6 +256,7 @@ extern cvar_t com_protocolname; extern cvar_t com_modname; extern cvar_t com_nogamedirnativecode; extern cvar_t com_parseutf8; +extern cvar_t com_parseezquake; extern cvar_t sys_ticrate; extern cvar_t sys_nostdout; extern cvar_t developer; @@ -275,6 +276,7 @@ void Host_InitCommands (void); void Host_Init (quakeparms_t *parms); void Host_FinishInit(void); void Host_Shutdown(void); +qboolean com_fatalerror; //supresses shutdown prints+threads NORETURN void VARGS Host_Error (char *error, ...) LIKEPRINTF(1); NORETURN void VARGS Host_EndGame (char *message, ...) LIKEPRINTF(1); qboolean Host_SimulationTime(float time); @@ -284,6 +286,36 @@ void Host_Quit_f (void); void VARGS Host_ClientCommands (char *fmt, ...) LIKEPRINTF(1); void Host_ShutdownServer (qboolean crash); +#ifdef LOADERTHREAD +extern cvar_t worker_flush; +qboolean COM_DoWork(int thread, qboolean leavelocked); +#define COM_MainThreadWork() while (COM_DoWork(0, false) && worker_flush.ival) /*called each frame to do any gl uploads or whatever*/ +#define COM_MainThreadFlush() while (COM_DoWork(0, false)) /*make sure the main thread has done ALL work pending*/ +void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); +qboolean COM_HasWork(void); +void COM_WorkerFullSync(void); +void COM_DestroyWorkerThread(void); +void COM_WorkerPartialSync(void *priorityctx, int *address, int value); +void *com_resourcemutex; //random mutex to simplify resource creation type stuff. +void COM_WorkerAbort(char *message); //calls sys_error on the main thread, if running on a worker. +#ifdef _DEBUG +void COM_AssertMainThread(const char *msg); +#else +#define COM_AssertMainThread(msg) +#endif +#else +#define COM_AddWork(t,f,a,b,c,d) (f)((a),(b),(c),(d)) +#define COM_WorkerPartialSync(c,a,v) +#define COM_WorkerFullSync() +#define COM_HasWork() false +#define COM_DoWork(t,l) false +#define COM_AssertMainThread(msg) +#define COM_MainThreadWork() +#define COM_MainThreadFlush() +#define COM_DestroyWorkerThread() +#define COM_WorkerAbort(m) +#endif + extern qboolean msg_suppress_1; // suppresses resolution and cache size console output // an fullscreen DIB focus gain/loss diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index ce23d732..51534dd9 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -143,7 +143,7 @@ void R2D_Init(void) int i; unsigned int glossval; unsigned int normval; - extern cvar_t gl_specular_fallback; + extern cvar_t gl_specular_fallback, gl_specular_fallbackexp; conback = NULL; Shader_Init(); @@ -166,7 +166,7 @@ void R2D_Init(void) glossval = min(gl_specular_fallback.value*255, 255); glossval *= 0x10101; - glossval |= 0xff000000; + glossval |= 0x01000000 * bound(0, (int)(gl_specular_fallbackexp.value*255), 255); glossval = LittleLong(glossval); normval = 0xffff8080; normval = LittleLong(normval); @@ -352,8 +352,6 @@ mpic_t *R2D_SafeCachePic (const char *path) if (!qrenderer) return NULL; s = R_RegisterPic(path); - if (s->flags & SHADER_NOIMAGE) - return NULL; return s; } @@ -367,15 +365,16 @@ mpic_t *R2D_SafePicFromWad (const char *name) snprintf(newnamewad, sizeof(newnamewad), "wad/%s", name); snprintf(newnamegfx, sizeof(newnamegfx), "gfx/%s", name); + /* s = R_RegisterPic(newnamewad); if (!(s->flags & SHADER_NOIMAGE)) return s; - + */ s = R_RegisterPic(newnamegfx); - if (!(s->flags & SHADER_NOIMAGE)) +// if (!(s->flags & SHADER_NOIMAGE)) return s; - return NULL; +// return NULL; } @@ -403,9 +402,19 @@ void R2D_ImagePaletteColour(unsigned int i, float a) //awkward and weird to use void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, float t2, mpic_t *pic) { + int i; if (!pic) return; + //don't draw pics if they have an image which is still loading. + for (i = 0; i < pic->numpasses; i++) + { + if (pic->passes[i].texgen == T_GEN_SINGLEMAP && pic->passes[i].anim_frames[0] && pic->passes[i].anim_frames[0]->status == TEX_LOADING) + return; + if (pic->passes[i].texgen == T_GEN_DIFFUSE && pic->defaulttextures.base && pic->defaulttextures.base->status == TEX_LOADING) + return; + } + draw_mesh_xyz[0][0] = x; draw_mesh_xyz[0][1] = y; draw_mesh_st[0][0] = s1; @@ -485,7 +494,7 @@ void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, if (!TEXVALID(translate_texture)) { - translate_texture = R_AllocNewTexture("***translatedpic***", 64, 64, 0); + translate_texture = Image_CreateTexture("***translatedpic***", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); translate_shader = R_RegisterShader("translatedpic", SUF_2D, "{\n" "if $nofixed\n" @@ -502,7 +511,7 @@ void R2D_TransPicTranslate (float x, float y, int width, int height, qbyte *pic, translate_shader->defaulttextures.base = translate_texture; } /* could avoid reuploading already translated textures but this func really isn't used enough anyway */ - R_Upload(translate_texture, NULL, TF_RGBA32, trans, NULL, 64, 64, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); + Image_Upload(translate_texture, TF_RGBA32, trans, NULL, 64, 64, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); R2D_ScalePic(x, y, width, height, translate_shader); } @@ -537,7 +546,7 @@ void R2D_ConsoleBackground (int firstline, int lastline, qboolean forceopaque) h>>=1; w>>=1; } - if (!conback) + if (R_GetShaderSizes(conback, NULL, NULL, false) <= 0) { R2D_ImageColours(0, 0, 0, a); R2D_FillBlock(0, lastline-(int)vid.height, w, h); @@ -615,14 +624,17 @@ void R2D_Conback_Callback(struct cvar_s *var, char *oldvalue) if (*var->string) conback = R_RegisterPic(var->string); - if (!conback || conback->flags & SHADER_NOIMAGE) + if (!R_GetShaderSizes(conback, NULL, NULL, true)) { - conback = R_RegisterCustom("console", SUF_2D, NULL, NULL); - if (!conback || conback->flags & SHADER_NOIMAGE) + conback = R_RegisterCustom("console", SUF_2D, NULL, NULL); //quake3 + if (!R_GetShaderSizes(conback, NULL, NULL, true)) { +#ifdef HEXEN2 if (M_GameType() == MGT_HEXEN2) conback = R_RegisterPic("gfx/menu/conback.lmp"); - else if (M_GameType() == MGT_QUAKE2) + else +#endif + if (M_GameType() == MGT_QUAKE2) conback = R_RegisterPic("pics/conback.pcx"); else conback = R_RegisterPic("gfx/conback.lmp"); @@ -1187,7 +1199,7 @@ void R2D_Crosshair_Update(void) c = c % (sizeof(crosshair_pixels) / (CS_HEIGHT*sizeof(*crosshair_pixels))); if (!TEXVALID(ch_int_texture)) - ch_int_texture = R_AllocNewTexture("***crosshair***", CS_WIDTH, CS_HEIGHT, IF_UIPIC|IF_NOMIPMAP); + ch_int_texture = Image_CreateTexture("***crosshair***", NULL, IF_UIPIC|IF_NOMIPMAP); shader_crosshair->defaulttextures.base = ch_int_texture; Q_memset(crossdata, 0, sizeof(crossdata)); @@ -1204,7 +1216,7 @@ void R2D_Crosshair_Update(void) } } - R_Upload(ch_int_texture, NULL, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); + Image_Upload(ch_int_texture, TF_RGBA32, crossdata, NULL, CS_WIDTH, CS_HEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); } @@ -1299,6 +1311,7 @@ void R2D_DrawCrosshair(void) R2D_ImageColours(1, 1, 1, 1); } +#define RT_IMAGEFLAGS IF_NOMIPMAP|IF_CLAMP|IF_LINEAR static texid_t internalrt; //resize a texture for a render target and specify the format of it. //pass TF_INVALID and sizes=0 to get without configuring (shaders that hardcode an $rt1 etc). @@ -1307,14 +1320,15 @@ texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfm texid_t tid; if (!strcmp(id, "-")) { - internalrt = tid = R_AllocNewTexture("", 0, 0, IF_NOMIPMAP); + internalrt = tid = Image_CreateTexture("", NULL, RT_IMAGEFLAGS); } else { - tid = R_FindTexture(id, IF_NOMIPMAP|IF_CLAMP|IF_LINEAR); + tid = Image_FindTexture(id, NULL, RT_IMAGEFLAGS); if (!TEXVALID(tid)) - tid = R_AllocNewTexture(id, 0, 0, IF_NOMIPMAP|IF_CLAMP|IF_LINEAR); + tid = Image_CreateTexture(id, NULL, RT_IMAGEFLAGS); } + if (rtfmt) { switch(rtfmt) @@ -1327,9 +1341,9 @@ texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfm case 6: rtfmt = TF_DEPTH32; break; default:rtfmt = TF_INVALID; break; } - R_Upload(tid, id, rtfmt, NULL, NULL, width, height, IF_NOMIPMAP|IF_CLAMP|IF_LINEAR); - tid.ref->width = width; - tid.ref->height = height; + Image_Upload(tid, rtfmt, NULL, NULL, width, height, RT_IMAGEFLAGS); + tid->width = width; + tid->height = height; } return tid; } @@ -1337,14 +1351,17 @@ texid_t R2D_RT_GetTexture(const char *id, unsigned int *width, unsigned int *hei { texid_t tid; if (!strcmp(id, "-")) - tid = internalrt; - else - tid = R_FindTexture(id, IF_NOMIPMAP); - - if (tid.ref) { - *width = tid.ref->width; - *height = tid.ref->height; + tid = internalrt; + internalrt = r_nulltex; + } + else + tid = Image_FindTexture(id, NULL, RT_IMAGEFLAGS); + + if (tid) + { + *width = tid->width; + *height = tid->height; } else { diff --git a/engine/client/r_part.c b/engine/client/r_part.c index 694bca4d..913ecc89 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -37,9 +37,9 @@ void R_Rockettrail_Callback(struct cvar_s *var, char *oldvalue) for (i=0 , mod=mod_known ; ineedload) + if (mod->loadstate == MLS_LOADED) if (mod->flags & MF_ROCKET) - P_DefaultTrail(mod); + P_LoadedModel(mod); } } @@ -55,9 +55,9 @@ void R_Grenadetrail_Callback(struct cvar_s *var, char *oldvalue) for (i=0 , mod=mod_known ; ineedload) + if (mod->loadstate == MLS_LOADED) if (mod->flags & MF_GRENADE) - P_DefaultTrail(mod); + P_LoadedModel(mod); } } @@ -219,7 +219,7 @@ qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal) pe = &pmove.physents[i]; if (pe->nonsolid) continue; - if (pe->model && !pe->model->needload) + if (pe->model && pe->model->loadstate == MLS_LOADED) { VectorSubtract(start, pe->origin, ts); VectorSubtract(end, pe->origin, te); @@ -292,7 +292,7 @@ void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk) // P_SelectableTrail: given default/opposite effects, model pointer, and a user selection cvar // changes model to the appropriate trail effect and default trail index -void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdlcidx, int oppeffect, int oppcidx) +static void P_SelectableTrail(int *trailid, int *trailpalidx, cvar_t *selection, int mdleffect, int mdlcidx, int oppeffect, int oppcidx) { int select = (int)(selection->value); @@ -301,8 +301,8 @@ void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdl case 0: // check for string, otherwise no trail if (selection->string[0] == '0') { - model->particletrail = P_INVALID; - model->traildefaultindex = -1; + *trailid = P_INVALID; + *trailpalidx = -1; break; } else @@ -311,48 +311,48 @@ void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdl if (effect >= 0) { - model->particletrail = effect; - model->traildefaultindex = mdlcidx; + *trailid = effect; + *trailpalidx = mdlcidx; break; } } // fall through to default (so semicheat will work properly) case 1: // default model effect default: - model->particletrail = mdleffect; - model->traildefaultindex = mdlcidx; + *trailid = mdleffect; + *trailpalidx = mdlcidx; break; case 2: // opposite effect - model->particletrail = oppeffect; - model->traildefaultindex= oppcidx; + *trailid = oppeffect; + *trailpalidx= oppcidx; break; case 3: // alt rocket effect - model->particletrail = P_FindParticleType("TR_ALTROCKET"); - model->traildefaultindex = 107; + *trailid = P_FindParticleType("TR_ALTROCKET"); + *trailpalidx = 107; break; case 4: // gib - model->particletrail = P_FindParticleType("TR_BLOOD"); - model->traildefaultindex = 70; + *trailid = P_FindParticleType("TR_BLOOD"); + *trailpalidx = 70; break; case 5: // zombie gib - model->particletrail = P_FindParticleType("TR_SLIGHTBLOOD"); - model->traildefaultindex = 70; + *trailid = P_FindParticleType("TR_SLIGHTBLOOD"); + *trailpalidx = 70; break; case 6: // Scrag tracer - model->particletrail = P_FindParticleType("TR_WIZSPIKE"); - model->traildefaultindex = 60; + *trailid = P_FindParticleType("TR_WIZSPIKE"); + *trailpalidx = 60; break; case 7: // Knight tracer - model->particletrail = P_FindParticleType("TR_KNIGHTSPIKE"); - model->traildefaultindex = 238; + *trailid = P_FindParticleType("TR_KNIGHTSPIKE"); + *trailpalidx = 238; break; case 8: // Vore tracer - model->particletrail = P_FindParticleType("TR_VORESPIKE"); - model->traildefaultindex = 154; + *trailid = P_FindParticleType("TR_VORESPIKE"); + *trailpalidx = 154; break; case 9: // rail trail - model->particletrail = P_FindParticleType("TE_RAILTRAIL"); - model->traildefaultindex = 15; + *trailid = P_FindParticleType("TE_RAILTRAIL"); + *trailpalidx = 15; break; } } @@ -360,102 +360,108 @@ void P_SelectableTrail(model_t *model, cvar_t *selection, int mdleffect, int mdl //figure out which particle trail to use for the given model, filling in its values as required. -void P_DefaultTrail (model_t *model) +void P_DefaultTrail (unsigned int modelflags, int *trailid, int *trailpalidx) { // TODO: EF_BRIGHTFIELD should probably be handled in here somewhere // TODO: make trail default color into RGB values instead of indexes - if (model->engineflags & MDLF_NODEFAULTTRAIL) - return; if (!pe) return; - if (model->flags & MF_ROCKET) - P_SelectableTrail(model, &r_rockettrail, P_FindParticleType("TR_ROCKET"), 109, P_FindParticleType("TR_GRENADE"), 6); - else if (model->flags & MF_GRENADE) - P_SelectableTrail(model, &r_grenadetrail, P_FindParticleType("TR_GRENADE"), 6, P_FindParticleType("TR_ROCKET"), 109); - else if (model->flags & MF_GIB) + if (modelflags & MF_ROCKET) + P_SelectableTrail(trailid, trailpalidx, &r_rockettrail, P_FindParticleType("TR_ROCKET"), 109, P_FindParticleType("TR_GRENADE"), 6); + else if (modelflags & MF_GRENADE) + P_SelectableTrail(trailid, trailpalidx, &r_grenadetrail, P_FindParticleType("TR_GRENADE"), 6, P_FindParticleType("TR_ROCKET"), 109); + else if (modelflags & MF_GIB) { - model->particletrail = P_FindParticleType("TR_BLOOD"); - model->traildefaultindex = 70; + *trailid = P_FindParticleType("TR_BLOOD"); + *trailpalidx = 70; } - else if (model->flags & MF_TRACER) + else if (modelflags & MF_TRACER) { - model->particletrail = P_FindParticleType("TR_WIZSPIKE"); - model->traildefaultindex = 60; + *trailid = P_FindParticleType("TR_WIZSPIKE"); + *trailpalidx = 60; } - else if (model->flags & MF_ZOMGIB) + else if (modelflags & MF_ZOMGIB) { - model->particletrail = P_FindParticleType("TR_SLIGHTBLOOD"); - model->traildefaultindex = 70; + *trailid = P_FindParticleType("TR_SLIGHTBLOOD"); + *trailpalidx = 70; } - else if (model->flags & MF_TRACER2) + else if (modelflags & MF_TRACER2) { - model->particletrail = P_FindParticleType("TR_KNIGHTSPIKE"); - model->traildefaultindex = 238; + *trailid = P_FindParticleType("TR_KNIGHTSPIKE"); + *trailpalidx = 238; } - else if (model->flags & MF_TRACER3) + else if (modelflags & MF_TRACER3) { - model->particletrail = P_FindParticleType("TR_VORESPIKE"); - model->traildefaultindex = 154; + *trailid = P_FindParticleType("TR_VORESPIKE"); + *trailpalidx = 154; } - else if (model->flags & MFH2_BLOODSHOT) //these are the hexen2 ones. +#ifdef HEXEN2 + else if (modelflags & MFH2_BLOODSHOT) //these are the hexen2 ones. { - model->particletrail = P_FindParticleType("tr_bloodshot"); - model->traildefaultindex = 136; + *trailid = P_FindParticleType("tr_bloodshot"); + *trailpalidx = 136; } - else if (model->flags & MFH2_FIREBALL) + else if (modelflags & MFH2_FIREBALL) { - model->particletrail = P_FindParticleType("tr_fireball"); - model->traildefaultindex = 424; + *trailid = P_FindParticleType("tr_fireball"); + *trailpalidx = 424; } - else if (model->flags & MFH2_ACIDBALL) + else if (modelflags & MFH2_ACIDBALL) { - model->particletrail = P_FindParticleType("tr_acidball"); - model->traildefaultindex = 440; + *trailid = P_FindParticleType("tr_acidball"); + *trailpalidx = 440; } - else if (model->flags & MFH2_ICE) + else if (modelflags & MFH2_ICE) { - model->particletrail = P_FindParticleType("tr_ice"); - model->traildefaultindex = 408; + *trailid = P_FindParticleType("tr_ice"); + *trailpalidx = 408; } - else if (model->flags & MFH2_SPIT) + else if (modelflags & MFH2_SPIT) { - model->particletrail = P_FindParticleType("tr_spit"); - model->traildefaultindex = 260; + *trailid = P_FindParticleType("tr_spit"); + *trailpalidx = 260; } - else if (model->flags & MFH2_SPELL) + else if (modelflags & MFH2_SPELL) { - model->particletrail = P_FindParticleType("tr_spell"); - model->traildefaultindex = 260; + *trailid = P_FindParticleType("tr_spell"); + *trailpalidx = 260; } - else if (model->flags & MFH2_VORP_MISSILE) + else if (modelflags & MFH2_VORP_MISSILE) { - model->particletrail = P_FindParticleType("tr_vorpmissile"); - model->traildefaultindex = 302; + *trailid = P_FindParticleType("tr_vorpmissile"); + *trailpalidx = 302; } - else if (model->flags & MFH2_SET_STAFF) + else if (modelflags & MFH2_SET_STAFF) { - model->particletrail = P_FindParticleType("tr_setstaff"); - model->traildefaultindex = 424; + *trailid = P_FindParticleType("tr_setstaff"); + *trailpalidx = 424; } - else if (model->flags & MFH2_MAGICMISSILE) + else if (modelflags & MFH2_MAGICMISSILE) { - model->particletrail = P_FindParticleType("tr_magicmissile"); - model->traildefaultindex = 149; + *trailid = P_FindParticleType("tr_magicmissile"); + *trailpalidx = 149; } - else if (model->flags & MFH2_BONESHARD) + else if (modelflags & MFH2_BONESHARD) { - model->particletrail = P_FindParticleType("tr_boneshard"); - model->traildefaultindex = 384; + *trailid = P_FindParticleType("tr_boneshard"); + *trailpalidx = 384; } - else if (model->flags & MFH2_SCARAB) + else if (modelflags & MFH2_SCARAB) { - model->particletrail = P_FindParticleType("tr_scarab"); - model->traildefaultindex = 254; + *trailid = P_FindParticleType("tr_scarab"); + *trailpalidx = 254; } + else if (modelflags & MFH2_ROCKET) + { + //spiders + *trailid = P_FindParticleType("TR_GREENBLOOD"); + *trailpalidx = 70; //fixme + } +#endif else { - model->particletrail = P_INVALID; - model->traildefaultindex = -1; + *trailid = P_INVALID; + *trailpalidx = -1; } } diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 8f068048..6cecbb48 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -3,6 +3,7 @@ WARNING: THIS FILE IS GENERATED BY 'generatebuiltin.c'. YOU SHOULD NOT EDIT THIS FILE BY HAND */ +#include "bothdefs.h" #include "r_partset.h" @@ -1593,14 +1594,30 @@ char *particle_set_high = //r_effect "progs/s_explod.spr" hidden 1 ////////////////////////////////////////// +//for when a spawn dies. +//also used by TF for emp explosions. //r_part te_tarexplosion //{ //} ////////////////////////////////////////// -//r_part te_lavasplash -//{ -//} +//cthon falling into lava. +//often also used for TF gas grenades. +"r_part te_lavasplash\n" +"{\n" +"texture \"particles/fteparticlefont.tga\"\n" +"tcoords 129 1 191 63 256\n" +"count 654\n" +"scale 15\n" +"alpha 0.7\n" +"die 4\n" +"randomvel 64\n" +"rgb 255 128 128\n" +"gravity 50\n" +"blend add\n" +"spawnorg 192 64\n" +"up 48\n" +"}\n" ////////////////////////////////////////// //FIXME: what if we don't have glsl support? @@ -2083,6 +2100,7 @@ char *particle_set_minimal = ////////////////////////////////////////////////////// +#ifdef HEXEN2 char *particle_set_h2part = //hexen2-compatible particle config //for the purposes of faithfulness, I'm using uhexen2 (with gl_missile_glows etc set to 0) as a baseline. @@ -2340,6 +2358,10 @@ char *particle_set_h2part = "rgbrand 0 0 0\n" "gravity 200\n" "scalefactor 0.4\n" +"lighttime 0\n" +"lightshadows 1\n" +"lightradius 100 120\n" +"lightrgb 0.50 1.00 0.25\n" "}\n" //fixme:test @@ -2381,6 +2403,11 @@ char *particle_set_h2part = "rgbrand 0 0 0\n" "gravity 0\n" "scalefactor 0.3\n" + +"lighttime 0\n" +"lightshadows 1\n" +"lightradius 100 120\n" +"lightrgb -2.00 -1.00 -0.25\n" "}\n" //famine missiles "r_part tr_spell\n" @@ -2473,6 +2500,10 @@ char *particle_set_h2part = "blend add\n" "scalefactor 1\n" "scaledelta -15\n" +"lighttime 0\n" +"lightshadows 1\n" +"lightradius 100 120\n" +"lightrgb 2.00 1.00 0.25\n" "}\n" "r_part +tr_fireball\n" "{\n" @@ -3204,13 +3235,16 @@ char *particle_set_h2part = //h2part.ce_grey_smoke_100 was not loaded //h2part.ce_chunk_fire was not loaded ; +#endif ////////////////////////////////////////////////////// +#ifdef Q2CLIENT char *particle_set_q2part = +"r_part namespace q2part\n" "r_part pe_default\n" "{\n" @@ -3370,6 +3404,7 @@ char *particle_set_q2part = "colorindex 0 15\n" "}\n" ; +#endif diff --git a/engine/client/r_partset.h b/engine/client/r_partset.h index 098ef26d..0435cea1 100644 --- a/engine/client/r_partset.h +++ b/engine/client/r_partset.h @@ -1,9 +1,30 @@ +/* +WARNING: THIS FILE IS GENERATED BY 'generatebuiltin.c'. +YOU SHOULD NOT EDIT THIS FILE BY HAND +*/ + extern char *particle_set_spikeset; +#define R_PARTSET_BUILTINS_spikeset {"spikeset", &particle_set_spikeset}, extern char *particle_set_faithful; +#define R_PARTSET_BUILTINS_faithful {"faithful", &particle_set_faithful}, extern char *particle_set_highfps; +#define R_PARTSET_BUILTINS_highfps {"highfps", &particle_set_highfps}, extern char *particle_set_high; +#define R_PARTSET_BUILTINS_high {"high", &particle_set_high}, extern char *particle_set_minimal; +#define R_PARTSET_BUILTINS_minimal {"minimal", &particle_set_minimal}, +#ifdef HEXEN2 extern char *particle_set_h2part; +#define R_PARTSET_BUILTINS_h2part {"h2part", &particle_set_h2part}, +#else +#define R_PARTSET_BUILTINS_h2part +#endif +#ifdef Q2CLIENT extern char *particle_set_q2part; +#define R_PARTSET_BUILTINS_q2part {"q2part", &particle_set_q2part}, +#else +#define R_PARTSET_BUILTINS_q2part +#endif extern char *particle_set_tsshaft; -#define R_PARTSET_BUILTINS {"spikeset", &particle_set_spikeset},{"faithful", &particle_set_faithful},{"highfps", &particle_set_highfps},{"high", &particle_set_high},{"minimal", &particle_set_minimal},{"h2part", &particle_set_h2part},{"q2part", &particle_set_q2part},{"tsshaft", &particle_set_tsshaft}, +#define R_PARTSET_BUILTINS_tsshaft {"tsshaft", &particle_set_tsshaft}, +#define R_PARTSET_BUILTINS R_PARTSET_BUILTINS_spikeset R_PARTSET_BUILTINS_faithful R_PARTSET_BUILTINS_highfps R_PARTSET_BUILTINS_high R_PARTSET_BUILTINS_minimal R_PARTSET_BUILTINS_h2part R_PARTSET_BUILTINS_q2part R_PARTSET_BUILTINS_tsshaft diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index b9c32557..f87cabf9 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -197,7 +197,7 @@ void Surf_AddStain(vec3_t org, float red, float green, float blue, float radius) int i; float parms[7]; - if (!cl.worldmodel || cl.worldmodel->needload || r_stains.value <= 0) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED || r_stains.value <= 0) return; parms[0] = radius; parms[1] = org[0]; @@ -210,12 +210,12 @@ void Surf_AddStain(vec3_t org, float red, float green, float blue, float radius) cl.worldmodel->funcs.StainNode(cl.worldmodel->rootnode, parms); - //now stain bsp models other than world. + //now stain inline bsp models other than world. for (i=1 ; i< pmove.numphysent ; i++) //0 is world... { pe = &pmove.physents[i]; - if (pe->model && pe->model->surfaces == cl.worldmodel->surfaces && !pe->model->needload) + if (pe->model && pe->model->surfaces == cl.worldmodel->surfaces && pe->model->loadstate == MLS_LOADED) { parms[1] = org[0] - pe->origin[0]; parms[2] = org[1] - pe->origin[1]; @@ -1190,7 +1190,7 @@ void Surf_RenderDynamicLightmaps (msurface_t *fa) lightmapinfo_t *lm, *dlm; //surfaces without lightmaps - if (fa->lightmaptexturenums[0]<0) + if (fa->lightmaptexturenums[0]<0 || !lightmap) return; // check for lightmap modification @@ -2005,7 +2005,7 @@ void Surf_SetupFrame(void) if (r_refdef.flags & RDF_NOWORLDMODEL) { } - else if (!cl.worldmodel || cl.worldmodel->needload || cl.worldmodel->fromgame == fg_doom3 ) + else if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED || cl.worldmodel->fromgame == fg_doom3 ) { r_viewleaf = NULL; r_viewleaf2 = NULL; @@ -2292,7 +2292,7 @@ void Surf_DrawWorld (void) BE_DrawWorld(false, NULL); return; } - if (!cl.worldmodel || cl.worldmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) { /*Don't act as a wallhack*/ return; @@ -2602,8 +2602,10 @@ int Surf_NewExternalLightmaps(int count, char *filepattern, qboolean deluxe) Q_snprintfz(nname, sizeof(nname), filepattern, i - numlightmaps); TEXASSIGN(lightmap[i]->lightmap_texture, R_LoadHiResTexture(nname, NULL, 0)); - lightmap[i]->width = image_width; - lightmap[i]->height = image_height; + if (lightmap[i]->lightmap_texture->status == TEX_LOADING) + COM_WorkerPartialSync(lightmap[i]->lightmap_texture, &lightmap[i]->lightmap_texture->status, TEX_LOADING); + lightmap[i]->width = lightmap[i]->lightmap_texture->width; + lightmap[i]->height = lightmap[i]->lightmap_texture->height; } if (odd) @@ -2636,6 +2638,7 @@ void Surf_BuildModelLightmaps (model_t *m) return; #ifdef TERRAIN + //easiest way to deal with heightmap lightmaps is to just purge the entire thing. if (m->terrain) Terr_PurgeTerrainModel(m, true, false); #endif @@ -2645,7 +2648,7 @@ void Surf_BuildModelLightmaps (model_t *m) if (!m->lightmaps.count) return; - if (m->needload) + if (m->loadstate != MLS_LOADED) return; currentmodel = m; @@ -2653,7 +2656,7 @@ void Surf_BuildModelLightmaps (model_t *m) if (*m->name == '*' && m->fromgame == fg_quake3) //FIXME: should be all bsp formats { - if (!cl.model_precache[1] || cl.model_precache[1]->needload) + if (!cl.model_precache[1] || cl.model_precache[1]->loadstate != MLS_LOADED) return; newfirst = cl.model_precache[1]->lightmaps.first; } @@ -2752,11 +2755,11 @@ void Surf_BuildModelLightmaps (model_t *m) lightmapinfo_t *lm, *dlm; qbyte *deluxemap; - if (*m->name == '*') - { - if (!cl.worldmodel || cl.worldmodel->needload) - return; - } +// if (*m->name == '*') +// { +// if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) +// return; +// } //fixup surface lightmaps, and paint for (i=0; inummodelsurfaces; i++) { @@ -2809,6 +2812,9 @@ void Surf_BuildLightmaps (void) int i, j; model_t *m; + extern model_t *mod_known; + extern int mod_numknown; + r_framecount = 1; // no dlightcache for (i = 0; i < numlightmaps; i++) @@ -2832,7 +2838,7 @@ void Surf_BuildLightmaps (void) m = cl.model_precache[j]; if (!m) break; - if (m->needload) + if (m->loadstate != MLS_LOADED) continue; Surf_BuildModelLightmaps(m); } @@ -2845,4 +2851,102 @@ void Surf_BuildLightmaps (void) } BE_UploadAllLightmaps(); } + + + +/* +=============== +Surf_NewMap +=============== +*/ +void Surf_NewMap (void) +{ + char namebuf[MAX_QPATH]; + extern cvar_t host_mapname; + 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); + VectorSet(r_worldentity.light_avg, 1, 1, 1); + + + if (cl.worldmodel) + COM_StripExtension(COM_SkipPath(cl.worldmodel->name), namebuf, sizeof(namebuf)); + else + *namebuf = '\0'; + Cvar_Set(&host_mapname, namebuf); + + Surf_DeInit(); + + r_viewleaf = NULL; + r_oldviewleaf = NULL; + r_viewcluster = -1; + r_oldviewcluster = 0; + r_viewcluster2 = -1; + + if (cl.worldmodel) + { + if (cl.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); + Mod_ParseInfoFromEntityLump(cl.worldmodel, cl.worldmodel->entities, cl.worldmodel->name); + } + + if (!pe) + Cvar_ForceCallback(&r_particlesystem); +TRACE(("dbg: Surf_NewMap: clear particles\n")); + P_ClearParticles (); +TRACE(("dbg: Surf_NewMap: wiping them stains (getting the cloth out)\n")); + Surf_WipeStains(); + CL_RegisterParticles(); +TRACE(("dbg: Surf_NewMap: building lightmaps\n")); + Surf_BuildLightmaps (); + + +TRACE(("dbg: Surf_NewMap: ui\n")); +#ifdef VM_UI + UI_Reset(); +#endif +TRACE(("dbg: Surf_NewMap: tp\n")); + TP_NewMap(); + R_SetSky(cl.skyname); + +#ifdef MAP_PROC + if (cl.worldmodel->fromgame == fg_doom3) + D3_GenerateAreas(cl.worldmodel); +#endif + + for (i = 0; i < cl.num_statics; i++) + { + vec3_t mins, maxs; + //fixme: no rotation + VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->mins, mins); + VectorAdd(cl_static_entities[i].ent.origin, cl_static_entities[i].ent.model->maxs, maxs); + cl.worldmodel->funcs.FindTouchedLeafs(cl.worldmodel, &cl_static_entities[i].pvscache, mins, maxs); + cl_static_entities[i].emit = NULL; + } + +#ifdef RTLIGHTS + Sh_PreGenerateLights(); +#endif +} + +void Surf_PreNewMap(void) +{ + r_loadbumpmapping = r_deluxemapping.ival || r_glsl_offsetmapping.ival; +#ifdef RTLIGHTS + r_loadbumpmapping |= r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival; +#endif + r_viewleaf = NULL; + r_oldviewleaf = NULL; + r_viewleaf2 = NULL; + r_oldviewleaf2 = NULL; +} + + #endif diff --git a/engine/client/render.h b/engine/client/render.h index d0244f6e..1fe25200 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -35,7 +35,7 @@ struct model_s; struct texnums_s; struct texture_s; -static const texid_t r_nulltex = {{0}}; +static const texid_t r_nulltex = NULL; #if 1 || defined(MINIMAL) || defined(D3DQUAKE) || defined(ANDROID) @@ -280,6 +280,8 @@ void R_DrawSkyChain (struct batch_s *batch); /*called from the backend, and call void R_InitSky (struct texnums_s *ret, struct texture_s *mt, qbyte *src); /*generate q1 sky texnums*/ //r_surf.c +void Surf_NewMap (void); +void Surf_PreNewMap(void); void Surf_SetupFrame(void); //determine pvs+viewcontents void Surf_DrawWorld(void); void Surf_GenBrushBatches(struct batch_s **batches, entity_t *ent); @@ -335,9 +337,6 @@ 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, struct batch_s *depthmasklist[2], int portaltype); -void GLR_PreNewMap(void); -void GLR_NewMap (void); - void GLR_PushDlights (void); void GLR_DrawWaterSurfaces (void); @@ -352,67 +351,62 @@ void R_RenderDlights (void); enum imageflags { /*warning: many of these flags only apply the first time it is requested*/ - IF_CLAMP = 1<<0, - IF_NEAREST = 1<<1, - IF_NOPICMIP = 1<<2, - IF_NOMIPMAP = 1<<3, - IF_NOALPHA = 1<<4, - IF_NOGAMMA = 1<<5, - IF_3DMAP = 1<<6, /*waning - don't test directly*/ - IF_CUBEMAP = 1<<7, /*waning - don't test directly*/ - IF_CUBEMAPEXTRA = 1<<8, - IF_TEXTYPE = (1<<6) | (1<<7) | (1<<8), /*0=2d, 1=3d, 2-7=cubeface*/ - IF_TEXTYPESHIFT = 6, /*0=2d, 1=3d, 2-7=cubeface*/ - IF_MIPCAP = 1<<9, - IF_UIPIC = 1<<10, //subject to texturemode2d - IF_LINEAR = 1<<11, - IF_PREMULTIPLYALPHA = 1<<12, //rgb *= alpha + IF_CLAMP = 1<<0, //disable texture coord wrapping. + IF_NOMIPMAP = 1<<1, //disable mipmaps. + IF_NEAREST = 1<<2, //force nearest + IF_LINEAR = 1<<3, //force linear + IF_UIPIC = 1<<4, //subject to texturemode2d + /*WARNING: If the above are changed, be sure to change shader pass flags*/ + IF_NOPICMIP = 1<<5, + IF_NOALPHA = 1<<6, + IF_NOGAMMA = 1<<7, + IF_3DMAP = 1<<8, /*waning - don't test directly*/ + IF_CUBEMAP = 1<<9, /*waning - don't test directly*/ + IF_TEXTYPE = (1<<8) | (1<<9), /*0=2d, 1=3d, 2=cubeface, 3=?*/ + IF_TEXTYPESHIFT = 8, /*0=2d, 1=3d, 2-7=cubeface*/ + IF_MIPCAP = 1<<10, + IF_PREMULTIPLYALPHA = 1<<12, //rgb *= alpha + IF_NOPCX = 1<<26, /*block pcx format. meaning qw skins can use team colours and cropping*/ + IF_TRYBUMP = 1<<27, /*attempt to load _bump if the specified _norm texture wasn't found*/ IF_RENDERTARGET = 1<<28, /*never loaded from disk, loading can't fail*/ - IF_EXACTEXTENSION = 1<<29, - IF_REPLACE = 1<<30, - IF_SUBDIRONLY = 1<<31 + IF_EXACTEXTENSION = 1<<29, /*don't mangle extensions, use what is specified and ONLY that*/ + IF_NOREPLACE = 1<<30, /*don't load a replacement, for some reason*/ + IF_NOWORKER = 1u<<31 /*don't pass the work to a loader thread. this gives synchronous loading.*/ }; -#define R_LoadTexture8(id,w,h,d,f,t) R_LoadTexture(id,w,h,t?TF_TRANS8:TF_SOLID8,d,f) -#define R_LoadTexture32(id,w,h,d,f) R_LoadTexture(id,w,h,TF_RGBA32,d,f) -#define R_LoadTextureFB(id,w,h,d,f) R_LoadTexture(id,w,h,TF_TRANS8_FULLBRIGHT,d,f) -#define R_LoadTexture8BumpPal(id,w,h,d,f) R_LoadTexture(id,w,h,TF_HEIGHT8PAL,d,f) -#define R_LoadTexture8Bump(id,w,h,d,f) R_LoadTexture(id,w,h,TF_HEIGHT8,d,f) +#define R_LoadTexture8(id,w,h,d,f,t) Image_GetTexture(id, NULL, f, d, NULL, w, h, t?TF_TRANS8:TF_SOLID8) +#define R_LoadTexture32(id,w,h,d,f) Image_GetTexture(id, NULL, f, d, NULL, w, h, TF_RGBA32) +#define R_LoadTextureFB(id,w,h,d,f) Image_GetTexture(id, NULL, f, d, NULL, w, h, TF_TRANS8_FULLBRIGHT) +#define R_LoadTexture(id,w,h,fmt,d,fl) Image_GetTexture(id, NULL, fl, d, NULL, w, h, fmt) -/*it seems a little excessive to have to include glquake (and windows headers), just to load some textures/shaders for the backend*/ -#ifdef GLQUAKE -texid_tf GL_AllocNewTexture(const char *name, int w, int h, unsigned int flags); -void GL_UploadFmt(texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); -texid_tf GL_LoadTextureFmt (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); -void GL_DestroyTexture(texid_t tex); +image_t *Image_FindTexture (const char *identifier, const char *subpath, unsigned int flags); +image_t *Image_CreateTexture(const char *identifier, const char *subpath, unsigned int flags); +image_t *Image_GetTexture (const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt); +void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); +void Image_Init(void); +void Image_Shutdown(void); + +image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); + +#define R_DestroyTexture(id) //FIXME + +#ifdef D3D9QUAKE +void D3D9_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); +qboolean D3D9_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips); +void D3D9_DestroyTexture (texid_t tex); #endif -#ifdef D3DQUAKE -texid_t D3D9_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); -texid_t D3D9_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_t D3D9_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_t D3D9_LoadCompressed (const char *name); -texid_t D3D9_FindTexture (const char *identifier, unsigned int flags); -texid_t D3D9_AllocNewTexture(const char *ident, int width, int height, unsigned int flags); -void D3D9_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); -void D3D9_DestroyTexture (texid_t tex); -void D3D9_Image_Shutdown(void); - -texid_t D3D11_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags); -texid_t D3D11_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_t D3D11_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_t D3D11_LoadCompressed (const char *name); -texid_t D3D11_FindTexture (const char *identifier, unsigned int flags); -texid_t D3D11_AllocNewTexture(const char *ident, int width, int height, unsigned int flags); -void D3D11_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags); -void D3D11_DestroyTexture (texid_t tex); -void D3D11_Image_Shutdown(void); +#ifdef D3D11QUAKE +void D3D11_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); +qboolean D3D11_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips); +void D3D11_DestroyTexture (texid_t tex); #endif -extern int image_width, image_height; -texid_tf R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags); +//extern int image_width, image_height; +texid_t R_LoadReplacementTexture(const char *name, const char *subpath, unsigned int flags, void *lowres, int lowreswidth, int lowresheight, uploadfmt_t fmt); texid_tf R_LoadHiResTexture(const char *name, const char *subpath, unsigned int flags); texid_tf R_LoadBumpmapTexture(const char *name, const char *subpath); +void R_LoadNumberedLightTexture(struct dlight_s *dl, int cubetexnum); qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, char *fname); @@ -445,7 +439,7 @@ void Mod_RebuildLightmaps (void); struct mleaf_s *Mod_PointInLeaf (struct model_s *model, float *p); void Mod_Think (void); -void Mod_NowLoadExternal(void); +void Mod_NowLoadExternal(struct model_s *loadmodel); void GLR_LoadSkys (void); void R_BloomRegister(void); @@ -453,6 +447,7 @@ int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magi int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)); void Mod_UnRegisterModelFormat(void *module, int idx); void Mod_UnRegisterAllModelFormats(void *module); +void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b); #ifdef RUNTIMELIGHTING void LightFace (int surfnum); @@ -532,7 +527,7 @@ extern cvar_t r_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows; extern cvar_t r_shadow_realtime_dlight_ambient; extern cvar_t r_shadow_realtime_dlight_diffuse; extern cvar_t r_shadow_realtime_dlight_specular; -extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows; +extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows, r_shadow_realtime_world_lightmaps; extern cvar_t r_shadow_shadowmapping; extern cvar_t r_editlights_import_radius; extern cvar_t r_editlights_import_ambient; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index b2e0d897..c0da9787 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -36,12 +36,7 @@ extern int gl_anisotropy_factor; // callbacks used for cvars void SCR_Viewsize_Callback (struct cvar_s *var, char *oldvalue); void SCR_Fov_Callback (struct cvar_s *var, char *oldvalue); -#if defined(GLQUAKE) -void GL_Mipcap_Callback (struct cvar_s *var, char *oldvalue); -void GL_Texturemode_Callback (struct cvar_s *var, char *oldvalue); -void GL_Texturemode2d_Callback (struct cvar_s *var, char *oldvalue); -void GL_Texture_Anisotropic_Filtering_Callback (struct cvar_s *var, char *oldvalue); -#endif +void Image_TextureMode_Callback (struct cvar_s *var, char *oldvalue); cvar_t vid_vsync = CVARAF ("vid_wait", "0", "vid_vsync", CVAR_ARCHIVE); @@ -325,23 +320,22 @@ cvar_t gl_maxdist = CVARD ("gl_maxdist", "8192", "The distance of the far #ifdef SPECULAR cvar_t gl_specular = CVARF ("gl_specular", "1", CVAR_ARCHIVE); cvar_t gl_specular_fallback = CVARF ("gl_specular_fallback", "0.05", CVAR_ARCHIVE|CVAR_RENDERERLATCH); +cvar_t gl_specular_fallbackexp = CVARF ("gl_specular_fallbackexp", "1", CVAR_ARCHIVE|CVAR_RENDERERLATCH); #endif // The callbacks are not in D3D yet (also ugly way of seperating this) -#ifdef GLQUAKE cvar_t gl_texture_anisotropic_filtering = CVARFC("gl_texture_anisotropic_filtering", "0", CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, - GL_Texture_Anisotropic_Filtering_Callback); -cvar_t gl_texturemode = CVARFC("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", + Image_TextureMode_Callback); +cvar_t gl_texturemode = CVARFDC("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE | CVAR_RENDERERCALLBACK | CVAR_SAVE, - GL_Texturemode_Callback); + "Specifies how world/model textures appear. Typically 3 letters eg lln.\nFirst letter can be l(inear) or n(earest) and says how to sample from the mip (when downsampling).\nThe middle letter can . to disable mipmaps, or l or n to describe whether to blend between mipmaps.\nThe third letter says what to do when the texture is too low resolution and is thus the most noticable with low resolution textures, a n will make it look like lego, while an l will keep it smooth.", Image_TextureMode_Callback); cvar_t gl_mipcap = CVARFC("d_mipcap", "0 1000", CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, - GL_Mipcap_Callback); -cvar_t gl_texturemode2d = CVARFC("gl_texturemode2d", "GL_LINEAR", + Image_TextureMode_Callback); +cvar_t gl_texturemode2d = CVARFDC("gl_texturemode2d", "GL_LINEAR", CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, - GL_Texturemode2d_Callback); -#endif + "Specifies how 2d images are sampled. format is a 3-tupple ", Image_TextureMode_Callback); cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffer", CVAR_ARCHIVE, "Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten."); @@ -453,10 +447,6 @@ void GLRenderer_Init(void) Cvar_Register (&gl_picmip2d, GLRENDEREROPTIONS); Cvar_Register (&r_shaderblobs, GLRENDEREROPTIONS); - Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); - Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS); - Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS); - Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS); Cvar_Register (&gl_savecompressedtex, GLRENDEREROPTIONS); Cvar_Register (&gl_compress, GLRENDEREROPTIONS); // Cvar_Register (&gl_detail, GRAPHICALNICETIES); @@ -640,6 +630,7 @@ void Renderer_Init(void) Cvar_Register (&r_flashblendscale, GRAPHICALNICETIES); Cvar_Register (&gl_specular, GRAPHICALNICETIES); Cvar_Register (&gl_specular_fallback, GRAPHICALNICETIES); + Cvar_Register (&gl_specular_fallbackexp, GRAPHICALNICETIES); Sh_RegisterCvars(); @@ -715,6 +706,10 @@ void Renderer_Init(void) Cvar_Register (&gl_max_size, GLRENDEREROPTIONS); Cvar_Register (&gl_maxdist, GLRENDEREROPTIONS); Cvar_Register (&gl_miptexLevel, GRAPHICALNICETIES); + Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS); + Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS); + Cvar_Register (&gl_mipcap, GLRENDEREROPTIONS); + Cvar_Register (&gl_texture_anisotropic_filtering, GLRENDEREROPTIONS); Cvar_Register (&r_drawflat, GRAPHICALNICETIES); Cvar_Register (&r_menutint, GRAPHICALNICETIES); @@ -776,9 +771,6 @@ void (*R_Init) (void); void (*R_DeInit) (void); void (*R_RenderView) (void); // must set r_refdef first -void (*R_NewMap) (void); -void (*R_PreNewMap) (void); - qboolean (*VID_Init) (rendererstate_t *info, unsigned char *palette); void (*VID_DeInit) (void); char *(*VID_GetRGBInfo) (int prepad, int *truevidwidth, int *truevidheight); @@ -805,22 +797,14 @@ rendererinfo_t dedicatedrendererinfo = { NULL, //Draw_Init; NULL, //Draw_Shutdown; - NULL, //R_LoadTexture - NULL, //R_LoadTexture8Pal24 - NULL, //R_LoadTexture8Pal32 - NULL, //R_LoadCompressed - NULL, //R_FindTexture - NULL, //R_AllocNewTexture - NULL, //R_Upload - NULL, //R_DestroyTexture + NULL, //IMG_UpdateFiltering + NULL, //IMG_LoadTextureMips + NULL, //IMG_DestroyTexture NULL, //R_Init; NULL, //R_DeInit; NULL, //R_RenderView; - NULL, //R_NewMap; - NULL, //R_PreNewMap - NULL, //VID_Init, NULL, //VID_DeInit, NULL, //VID_SwapBuffers @@ -915,8 +899,6 @@ void R_SetRenderer(rendererinfo_t *ri) R_Init = ri->R_Init; R_DeInit = ri->R_DeInit; R_RenderView = ri->R_RenderView; - R_NewMap = ri->R_NewMap; - R_PreNewMap = ri->R_PreNewMap; VID_Init = ri->VID_Init; VID_DeInit = ri->VID_DeInit; @@ -951,6 +933,9 @@ void D3DSucks(void) void R_ShutdownRenderer(qboolean videotoo) { + //make sure the worker isn't still loading stuff + COM_WorkerFullSync(); + CL_AllowIndependantSendCmd(false); //FIXME: figure out exactly which parts are going to affect the model loading. P_Shutdown(); @@ -1010,12 +995,17 @@ void R_GenPaletteLookup(void) qboolean R_ApplyRenderer (rendererstate_t *newr) { + double time; if (newr->bpp == -1) return false; if (!newr->renderer) return false; + time = Sys_DoubleTime(); + M_Shutdown(false); + Media_CaptureDemoEnd(); R_ShutdownRenderer(true); + Con_DPrintf("video shutdown took %f seconds\n", Sys_DoubleTime() - time); if (qrenderer == QR_NONE) { @@ -1025,17 +1015,19 @@ qboolean R_ApplyRenderer (rendererstate_t *newr) Sys_CloseTerminal (); } + time = Sys_DoubleTime(); R_SetRenderer(newr->renderer); + Con_DPrintf("video startup took %f seconds\n", Sys_DoubleTime() - time); return R_ApplyRenderer_Load(newr); } qboolean R_ApplyRenderer_Load (rendererstate_t *newr) { int i, j; - extern model_t *loadmodel; + double start = Sys_DoubleTime(); Cache_Flush(); - COM_FlushFSCache(); //make sure the fs cache is built if needed. there's lots of loading here. + COM_FlushFSCache(false, true); //make sure the fs cache is built if needed. there's lots of loading here. TRACE(("dbg: R_ApplyRenderer: old renderer closed\n")); @@ -1043,6 +1035,7 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) if (qrenderer != QR_NONE) //graphics stuff only when not dedicated { + size_t sz; qbyte *data; #ifndef CLIENTONLY isDedicated = false; @@ -1052,15 +1045,17 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) if (host_basepal) BZ_Free(host_basepal); - host_basepal = (qbyte *)FS_LoadMallocFile ("gfx/palette.lmp"); - if (!host_basepal) - host_basepal = (qbyte *)FS_LoadMallocFile ("wad/playpal"); + host_basepal = (qbyte *)FS_LoadMallocFile ("gfx/palette.lmp", &sz); if (!host_basepal) + host_basepal = (qbyte *)FS_LoadMallocFile ("wad/playpal", &sz); + if (!host_basepal || sz != 768) { qbyte *pcx=NULL; + if (host_basepal) + Z_Free(host_basepal); host_basepal = BZ_Malloc(768); - pcx = COM_LoadTempFile("pics/colormap.pcx"); - if (!pcx || !ReadPCXPalette(pcx, com_filesize, host_basepal)) + pcx = COM_LoadTempFile("pics/colormap.pcx", &sz); + if (!pcx || !ReadPCXPalette(pcx, sz, host_basepal)) { memcpy(host_basepal, default_quakepal, 768); } @@ -1072,7 +1067,7 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) } { - qbyte *colormap = (qbyte *)FS_LoadMallocFile ("gfx/colormap.lmp"); + qbyte *colormap = (qbyte *)FS_LoadMallocFile ("gfx/colormap.lmp", NULL); if (!colormap) { vid.fullbright=0; @@ -1096,7 +1091,7 @@ qboolean R_ApplyRenderer_Load (rendererstate_t *newr) #ifdef HEXEN2 if (h2playertranslations) BZ_Free(h2playertranslations); - h2playertranslations = FS_LoadMallocFile ("gfx/player.lmp"); + h2playertranslations = FS_LoadMallocFile ("gfx/player.lmp", NULL); #endif if (vid.fullbright < 2) @@ -1116,6 +1111,7 @@ TRACE(("dbg: R_ApplyRenderer: vid applied\n")); W_LoadWadFile("gfx.wad"); TRACE(("dbg: R_ApplyRenderer: wad loaded\n")); + Image_Init(); Draw_Init(); TRACE(("dbg: R_ApplyRenderer: draw inited\n")); R_Init(); @@ -1156,12 +1152,9 @@ TRACE(("dbg: R_ApplyRenderer: initing mods\n")); // host_hunklevel = Hunk_LowMark(); - if (R_PreNewMap) - if (cl.worldmodel) - { - TRACE(("dbg: R_ApplyRenderer: R_PreNewMap (how handy)\n")); - R_PreNewMap(); - } + + TRACE(("dbg: R_ApplyRenderer: R_PreNewMap (how handy)\n")); + Surf_PreNewMap(); #ifndef CLIENTONLY if (sv.world.worldmodel) @@ -1172,27 +1165,28 @@ TRACE(("dbg: R_ApplyRenderer: initing mods\n")); #endif TRACE(("dbg: R_ApplyRenderer: reloading server map\n")); - sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_WARN); + sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR); TRACE(("dbg: R_ApplyRenderer: loaded\n")); - + if (sv.world.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(sv.world.worldmodel, &sv.world.worldmodel->loadstate, MLS_LOADING); TRACE(("dbg: R_ApplyRenderer: doing that funky phs thang\n")); SV_CalcPHS (); TRACE(("dbg: R_ApplyRenderer: clearing world\n")); - World_ClearWorld (&sv.world); - if (sv.world.worldmodel->needload) + if (sv.world.worldmodel->loadstate != MLS_LOADED) SV_UnspawnServer(); else if (svs.gametype == GT_PROGS) { for (i = 0; i < MAX_PRECACHE_MODELS; i++) { if (sv.strings.model_precache[i] && *sv.strings.model_precache[i] && (!strcmp(sv.strings.model_precache[i] + strlen(sv.strings.model_precache[i]) - 4, ".bsp") || i-1 < sv.world.worldmodel->numsubmodels)) - sv.models[i] = Mod_FindName(sv.strings.model_precache[i]); + sv.models[i] = Mod_FindName(Mod_FixName(sv.strings.model_precache[i], sv.strings.model_precache[1])); else sv.models[i] = NULL; } + World_ClearWorld (&sv.world); ent = sv.world.edicts; // ent->v->model = PR_NewString(svprogfuncs, sv.worldmodel->name); //FIXME: is this a problem for normal ents? for (i=0 ; inumsubmodels)) - sv.models[i] = Mod_FindName(sv.strings.configstring[Q2CS_MODELS+i]); + sv.models[i] = Mod_FindName(Mod_FixName(sv.strings.configstring[Q2CS_MODELS+i], sv.modelname)); else sv.models[i] = NULL; } + World_ClearWorld (&sv.world); q2ent = ge->edicts; for (i=0 ; inum_edicts ; i++, q2ent = (q2edict_t *)((char *)q2ent + ge->edict_size)) { @@ -1240,6 +1235,8 @@ TRACE(("dbg: R_ApplyRenderer: clearing world\n")); #ifdef Q3SERVER else if (svs.gametype == GT_QUAKE3) { + memset(&sv.models, 0, sizeof(sv.models)); + sv.models[1] = Mod_FindName(sv.modelname); //traditionally a q3 server can just keep hold of its world cmodel and nothing is harmed. //this means we just need to reload the worldmodel and all is fine... //there are some edge cases however, like lingering pointers refering to entities. @@ -1261,13 +1258,17 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n")); if (newr) memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); + TRACE(("dbg: R_ApplyRenderer: S_Restart_f\n")); + if (!isDedicated) + S_DoRestart(); + #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) { CG_Stop(); CG_Start(); if (cl.worldmodel) - R_NewMap(); + Surf_NewMap(); } else #endif @@ -1290,7 +1291,10 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); cl.model_precache[i] = NULL; else #endif - cl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_SILENT); + if (i == 1) + cl.model_precache[i] = Mod_ForName (cl.model_name[i], MLV_SILENT); + else + cl.model_precache[i] = Mod_FindName (Mod_FixName(cl.model_name[i], cl.model_name[1])); if ((!cl.model_precache[i] || cl.model_precache[i]->type == mod_dummy) && i == 1) { @@ -1322,7 +1326,7 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); cl.model_csqcprecache[i] = NULL; TRACE(("dbg: R_ApplyRenderer: reloading csqc model %s\n", cl.model_csqcname[i])); - cl.model_csqcprecache[i] = Mod_ForName (cl.model_csqcname[i], MLV_SILENT); + cl.model_csqcprecache[i] = Mod_ForName (Mod_FixName(cl.model_csqcname[i], cl.model_name[1]), MLV_SILENT); if (!cl.model_csqcprecache[i]) { @@ -1339,23 +1343,26 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n")); } #endif - loadmodel = cl.worldmodel = cl.model_precache[1]; + cl.worldmodel = cl.model_precache[1]; + + if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING) + COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING); + TRACE(("dbg: R_ApplyRenderer: done the models\n")); - if (loadmodel->needload) + if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) { CL_Disconnect (); #ifdef VM_UI UI_Reset(); #endif - memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); + if (newr) + memcpy(¤trendererstate, newr, sizeof(currentrendererstate)); return true; } TRACE(("dbg: R_ApplyRenderer: checking any wad textures\n")); - Mod_NowLoadExternal(); -TRACE(("dbg: R_ApplyRenderer: R_NewMap\n")); - R_NewMap(); -TRACE(("dbg: R_ApplyRenderer: efrags\n")); + Mod_NowLoadExternal(cl.worldmodel); + for (i = 0; i < cl.num_statics; i++) //make the static entities reappear. { cl_static_entities[i].ent.model = NULL; @@ -1371,6 +1378,10 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n")); } } +TRACE(("dbg: R_ApplyRenderer: Surf_NewMap\n")); + Surf_NewMap(); +TRACE(("dbg: R_ApplyRenderer: efrags\n")); + Skin_FlushAll(); #ifdef CSQC_DAT @@ -1389,18 +1400,18 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n")); Con_TPrintf("%s renderer initialized\n", newr->renderer->description); } - TRACE(("dbg: R_ApplyRenderer: S_Restart_f\n")); - if (!isDedicated) - S_DoRestart(); - TRACE(("dbg: R_ApplyRenderer: done\n")); + + Con_DPrintf("video restart took %f seconds\n", Sys_DoubleTime() - start); return true; } void R_ReloadRenderer_f (void) { + float time = Sys_DoubleTime(); R_ShutdownRenderer(false); + Con_DPrintf("teardown = %f\n", Sys_DoubleTime() - time); //reloads textures without destroying video context. R_ApplyRenderer_Load(NULL); } @@ -1477,7 +1488,7 @@ qboolean R_BuildRenderstate(rendererstate_t *newr, char *rendererstring) int dbpp, dheight, dwidth, drate; extern qboolean isPlugin; - if ((!newr->fullscreen && !vid_desktopsettings.value && !isPlugin) || !Sys_GetDesktopParameters(&dwidth, &dheight, &dbpp, &drate)) + if ((!newr->fullscreen && !isPlugin) || (!vid_desktopsettings.value && !isPlugin) || !Sys_GetDesktopParameters(&dwidth, &dheight, &dbpp, &drate)) { // force default values for systems not supporting desktop parameters dwidth = DEFAULT_WIDTH; @@ -1526,9 +1537,6 @@ void R_RestartRenderer (rendererstate_t *newr) return; } - M_Shutdown(false); - Media_CaptureDemoEnd(); - TRACE(("dbg: R_RestartRenderer_f renderer %i\n", newr.renderer)); memcpy(&oldr, ¤trendererstate, sizeof(rendererstate_t)); @@ -1598,6 +1606,7 @@ void R_RestartRenderer (rendererstate_t *newr) void R_RestartRenderer_f (void) { + double time; rendererstate_t newr; Cvar_ApplyLatches(CVAR_RENDERERLATCH); @@ -1610,7 +1619,11 @@ void R_RestartRenderer_f (void) return; } + time = Sys_DoubleTime(); R_RestartRenderer(&newr); + Con_DPrintf("main thread video restart took %f secs\n", Sys_DoubleTime() - time); +// COM_WorkerFullSync(); +// Con_Printf("full video restart took %f secs\n", Sys_DoubleTime() - time); } void R_SetRenderer_f (void) @@ -2487,7 +2500,7 @@ void R_InitParticleTexture (void) } } - TEXASSIGN(particletexture, R_LoadTexture32("", 8, 8, data, IF_NOMIPMAP|IF_NOPICMIP)); + TEXASSIGN(particletexture, R_LoadTexture32("dotparticle", 8, 8, data, IF_NOMIPMAP|IF_NOPICMIP)); // @@ -2574,7 +2587,7 @@ void R_InitParticleTexture (void) data[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d; } } - beamtexture = R_LoadTexture32("", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); + beamtexture = R_LoadTexture32("beamparticle", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); for (y = 0;y < PARTICLETEXTURESIZE;y++) { @@ -2592,6 +2605,6 @@ void R_InitParticleTexture (void) data[y*PARTICLETEXTURESIZE+x][3] = (qbyte) d/2; } } - ptritexture = R_LoadTexture32("", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); + ptritexture = R_LoadTexture32("ptritexture", PARTICLETEXTURESIZE, PARTICLETEXTURESIZE, data, IF_NOMIPMAP|IF_NOPICMIP); } diff --git a/engine/client/resource.h b/engine/client/resource.h index 361a693e..633b0a71 100644 --- a/engine/client/resource.h +++ b/engine/client/resource.h @@ -5,6 +5,7 @@ #define IDC_STATIC -1 #define IDI_ICON1 1 #define IDI_ICON2 2 +#define IDI_ICON3 3 // Next default values for new objects // diff --git a/engine/client/roq.h b/engine/client/roq.h index cab0e4b1..e57d9ed4 100644 --- a/engine/client/roq.h +++ b/engine/client/roq.h @@ -22,7 +22,7 @@ typedef struct { typedef struct { vfsfile_t *fp; - unsigned int maxpos; //addition for pack files. all seeks add this, all tells subtract this. + qofs_t maxpos; //addition for pack files. all seeks add this, all tells subtract this. int buf_size; unsigned char *buf; roq_cell cells[256]; diff --git a/engine/client/roq_read.c b/engine/client/roq_read.c index c5afb931..481d18a4 100644 --- a/engine/client/roq_read.c +++ b/engine/client/roq_read.c @@ -307,7 +307,6 @@ vfsfile_t *fp; roq_info *ri; int i; -// if (COM_FOpenFile(fname, &fp)==-1) if((fp = FS_OpenVFS(fname, "rb", FS_GAME)) == NULL) { return NULL; @@ -321,9 +320,7 @@ int i; memset(ri, 0, sizeof(roq_info)); - com_filesize = VFS_GETLEN(fp); - - ri->maxpos = VFS_TELL(fp)+com_filesize;//no adds/subracts for fileoffset here + ri->maxpos = VFS_TELL(fp)+VFS_GETLEN(fp);//no adds/subracts for fileoffset here ri->fp = fp; if(roq_parse_file(fp, ri)) diff --git a/engine/client/sbar.c b/engine/client/sbar.c index 92b2c1f6..e0197e57 100644 --- a/engine/client/sbar.c +++ b/engine/client/sbar.c @@ -255,6 +255,7 @@ static void SCR_DrawField (float x, float y, int color, float width, int value) int l; int frame; mpic_t *p; + int pw,ph; if (width < 1) return; @@ -278,8 +279,8 @@ static void SCR_DrawField (float x, float y, int color, float width, int value) frame = *ptr -'0'; p = Sbar_Q2CachePic(q2sb_nums[color][frame]); - if (p) - R2D_ScalePic (x,y,p->width, p->height, p); + if (p && R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic (x,y,pw, ph, p); x += CHAR_WIDTH; ptr++; l--; @@ -311,6 +312,7 @@ void Sbar_ExecuteLayoutString (char *s) int value; int width; int index; + int pw, ph; // q2clientinfo_t *ci; mpic_t *p; @@ -342,7 +344,7 @@ void Sbar_ExecuteLayoutString (char *s) if (!strcmp(com_token, "xv")) { s = COM_Parse (s); - x = sbar_rect.x + sbar_rect.width/2 - 160 + atoi(com_token); + x = sbar_rect.x + (sbar_rect.width-320)/2 + atoi(com_token); continue; } @@ -361,7 +363,7 @@ void Sbar_ExecuteLayoutString (char *s) if (!strcmp(com_token, "yv")) { s = COM_Parse (s); - y = sbar_rect.y + sbar_rect.height/2 - 120 + atoi(com_token); + y = sbar_rect.y + (sbar_rect.height-240)/2 + atoi(com_token); continue; } @@ -376,8 +378,8 @@ void Sbar_ExecuteLayoutString (char *s) // SCR_AddDirtyPoint (x, y); // SCR_AddDirtyPoint (x+23, y+23); p = Sbar_Q2CachePic(Get_Q2ConfigString(Q2CS_IMAGES+value)); - if (p) - R2D_ScalePic (x, y, p->width, p->height, p); + if (p && R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic (x, y, pw, ph, p); } continue; } @@ -414,7 +416,7 @@ void Sbar_ExecuteLayoutString (char *s) Draw_FunString (x+32, y+24, va("Time: %i", time)); p = R2D_SafeCachePic(va("players/%s_i.pcx", cl.players[value].qwskin->name)); - if (!p) //display a default if the icon couldn't be found. + if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) //display a default if the icon couldn't be found. p = R2D_SafeCachePic("players/male/grunt_i.pcx"); R2D_ScalePic (x, y, 32, 32, p); continue; @@ -460,8 +462,8 @@ void Sbar_ExecuteLayoutString (char *s) // SCR_AddDirtyPoint (x, y); // SCR_AddDirtyPoint (x+23, y+23); p = Sbar_Q2CachePic(com_token); - if (p) - R2D_ScalePic (x, y, p->width, p->height, p); + if (p && R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic (x, y, pw, ph, p); continue; } @@ -491,8 +493,8 @@ void Sbar_ExecuteLayoutString (char *s) if (cl.q2frame.playerstate.stats[Q2STAT_FLASHES] & 1) { p = Sbar_Q2CachePic("field_3"); - if (p) - R2D_ScalePic (x, y, p->width, p->height, p); + if (p && R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic (x, y, pw, ph, p); } SCR_DrawField (x, y, color, width, value); @@ -515,8 +517,8 @@ void Sbar_ExecuteLayoutString (char *s) if (cl.q2frame.playerstate.stats[Q2STAT_FLASHES] & 4) { p = Sbar_Q2CachePic("field_3"); - if (p) - R2D_ScalePic (x, y, p->width, p->height, p); + if (p && R_GetShaderSizes(p, &pw, &ph, false)>0) + R2D_ScalePic (x, y, pw, ph, p); } SCR_DrawField (x, y, color, width, value); @@ -791,7 +793,6 @@ Sbar_Init static qboolean sbar_loaded; -static qboolean failedpic; mpic_t *Sbar_PicFromWad(char *name) { mpic_t *ret; @@ -802,7 +803,6 @@ mpic_t *Sbar_PicFromWad(char *name) if (ret) return ret; - failedpic = true; return NULL; } void Sbar_Flush (void) @@ -817,14 +817,13 @@ void Sbar_Start (void) //if one of these fails, skip the entire status bar. sbar_loaded = true; + COM_FlushFSCache(false, true); //make sure the fs cache is built if needed. there's lots of loading here. + if (!wad_base) //the wad isn't loaded. This is an indication that it doesn't exist. { sbarfailed = true; return; } - failedpic = false; - - COM_FlushFSCache(); //make sure the fs cache is built if needed. there's lots of loading here. sbarfailed = false; @@ -835,8 +834,10 @@ void Sbar_Start (void) //if one of these fails, skip the entire status bar. } #ifdef HEXEN2 - if (sb_nums[0][0] && sb_nums[0][0]->width < 13) + if (W_SafeGetLumpName("tinyfont")) sbar_hexen2 = true; +// if (sb_nums[0][0] && sb_nums[0][0]->width < 13) +// sbar_hexen2 = true; #endif sb_nums[0][10] = Sbar_PicFromWad ("num_minus"); @@ -909,66 +910,57 @@ void Sbar_Start (void) //if one of these fails, skip the entire status bar. sb_face_invis_invuln = Sbar_PicFromWad ("face_inv2"); sb_face_quad = Sbar_PicFromWad ("face_quad"); - sb_sbar = Sbar_PicFromWad ("sbar"); sb_ibar = Sbar_PicFromWad ("ibar"); + sb_sbar = Sbar_PicFromWad ("sbar"); sb_scorebar = Sbar_PicFromWad ("scorebar"); - if (failedpic) - sbarfailed = true; //try to detect rogue wads, and thus the stats we will be getting from the server. - failedpic = false; - - rsb_invbar[0] = Sbar_PicFromWad ("r_invbar1"); - rsb_invbar[1] = Sbar_PicFromWad ("r_invbar2"); - - rsb_weapons[0] = Sbar_PicFromWad ("r_lava"); - rsb_weapons[1] = Sbar_PicFromWad ("r_superlava"); - rsb_weapons[2] = Sbar_PicFromWad ("r_gren"); - rsb_weapons[3] = Sbar_PicFromWad ("r_multirock"); - rsb_weapons[4] = Sbar_PicFromWad ("r_plasma"); - - rsb_items[0] = Sbar_PicFromWad ("r_shield1"); - rsb_items[1] = Sbar_PicFromWad ("r_agrav1"); - - rsb_teambord = Sbar_PicFromWad ("r_teambord"); - - rsb_ammo[0] = Sbar_PicFromWad ("r_ammolava"); - rsb_ammo[1] = Sbar_PicFromWad ("r_ammomulti"); - rsb_ammo[2] = Sbar_PicFromWad ("r_ammoplasma"); - - if (!failedpic || COM_CheckParm("-rogue")) - sbar_rogue = true; - else - sbar_rogue = false; - - //try to detect hipnotic wads, and thus the stats we will be getting from the server. - failedpic = false; - - hsb_weapons[0][0] = Sbar_PicFromWad ("inv_laser"); - hsb_weapons[0][1] = Sbar_PicFromWad ("inv_mjolnir"); - hsb_weapons[0][2] = Sbar_PicFromWad ("inv_gren_prox"); - hsb_weapons[0][3] = Sbar_PicFromWad ("inv_prox_gren"); - hsb_weapons[0][4] = Sbar_PicFromWad ("inv_prox"); - hsb_weapons[1][0] = Sbar_PicFromWad ("inv2_laser"); - hsb_weapons[1][1] = Sbar_PicFromWad ("inv2_mjolnir"); - hsb_weapons[1][2] = Sbar_PicFromWad ("inv2_gren_prox"); - hsb_weapons[1][3] = Sbar_PicFromWad ("inv2_prox_gren"); - hsb_weapons[1][4] = Sbar_PicFromWad ("inv2_prox"); - for (i=0 ; i<5 ; i++) + sbar_rogue = COM_CheckParm("-rogue") || !!W_SafeGetLumpName("r_lava"); + if (sbar_rogue) { - hsb_weapons[2+i][0] = Sbar_PicFromWad (va("inva%i_laser",i+1)); - hsb_weapons[2+i][1] = Sbar_PicFromWad (va("inva%i_mjolnir",i+1)); - hsb_weapons[2+i][2] = Sbar_PicFromWad (va("inva%i_gren_prox",i+1)); - hsb_weapons[2+i][3] = Sbar_PicFromWad (va("inva%i_prox_gren",i+1)); - hsb_weapons[2+i][4] = Sbar_PicFromWad (va("inva%i_prox",i+1)); - } - hsb_items[0] = Sbar_PicFromWad ("sb_wsuit"); - hsb_items[1] = Sbar_PicFromWad ("sb_eshld"); + rsb_invbar[0] = Sbar_PicFromWad ("r_invbar1"); + rsb_invbar[1] = Sbar_PicFromWad ("r_invbar2"); - if (!failedpic || COM_CheckParm("-hipnotic")) - sbar_hipnotic = true; - else - sbar_hipnotic = false; + rsb_weapons[0] = Sbar_PicFromWad ("r_lava"); + rsb_weapons[1] = Sbar_PicFromWad ("r_superlava"); + rsb_weapons[2] = Sbar_PicFromWad ("r_gren"); + rsb_weapons[3] = Sbar_PicFromWad ("r_multirock"); + rsb_weapons[4] = Sbar_PicFromWad ("r_plasma"); + + rsb_items[0] = Sbar_PicFromWad ("r_shield1"); + rsb_items[1] = Sbar_PicFromWad ("r_agrav1"); + + rsb_teambord = Sbar_PicFromWad ("r_teambord"); + + rsb_ammo[0] = Sbar_PicFromWad ("r_ammolava"); + rsb_ammo[1] = Sbar_PicFromWad ("r_ammomulti"); + rsb_ammo[2] = Sbar_PicFromWad ("r_ammoplasma"); + } + + sbar_hipnotic = COM_CheckParm("-hipnotic") || !!W_SafeGetLumpName("inv_mjolnir"); + if (sbar_hipnotic) + { + hsb_weapons[0][0] = Sbar_PicFromWad ("inv_laser"); + hsb_weapons[0][1] = Sbar_PicFromWad ("inv_mjolnir"); + hsb_weapons[0][2] = Sbar_PicFromWad ("inv_gren_prox"); + hsb_weapons[0][3] = Sbar_PicFromWad ("inv_prox_gren"); + hsb_weapons[0][4] = Sbar_PicFromWad ("inv_prox"); + hsb_weapons[1][0] = Sbar_PicFromWad ("inv2_laser"); + hsb_weapons[1][1] = Sbar_PicFromWad ("inv2_mjolnir"); + hsb_weapons[1][2] = Sbar_PicFromWad ("inv2_gren_prox"); + hsb_weapons[1][3] = Sbar_PicFromWad ("inv2_prox_gren"); + hsb_weapons[1][4] = Sbar_PicFromWad ("inv2_prox"); + for (i=0 ; i<5 ; i++) + { + hsb_weapons[2+i][0] = Sbar_PicFromWad (va("inva%i_laser",i+1)); + hsb_weapons[2+i][1] = Sbar_PicFromWad (va("inva%i_mjolnir",i+1)); + hsb_weapons[2+i][2] = Sbar_PicFromWad (va("inva%i_gren_prox",i+1)); + hsb_weapons[2+i][3] = Sbar_PicFromWad (va("inva%i_prox_gren",i+1)); + hsb_weapons[2+i][4] = Sbar_PicFromWad (va("inva%i_prox",i+1)); + } + hsb_items[0] = Sbar_PicFromWad ("sb_wsuit"); + hsb_items[1] = Sbar_PicFromWad ("sb_eshld"); + } } void Sbar_Init (void) @@ -1065,7 +1057,7 @@ void Draw_TinyString (float x, float y, const qbyte *str) if (!font_tiny) { - font_tiny = Font_LoadFont(6, "gfx/tinyfont"); + font_tiny = Font_LoadFont(8, "gfx/tinyfont"); if (!font_tiny) return; } @@ -1090,6 +1082,18 @@ void Sbar_DrawTinyString (float x, float y, char *str) { Draw_TinyString (sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, str); } +void Sbar_DrawTinyStringf (float x, float y, char *fmt, ...) +{ + va_list argptr; + char string[256]; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt, argptr); + va_end (argptr); + + Draw_TinyString (sbar_rect.x + x /*+ ((sbar_rect.width - 320)>>1) */, sbar_rect.y + y+ sbar_rect.height-SBAR_HEIGHT, string); +} + void Sbar_FillPC (float x, float y, float w, float h, unsigned int pcolour) { @@ -2210,26 +2214,26 @@ static void Sbar_Hexen2DrawExtra (playerview_t *pv) Sbar_DrawTinyString (11, 48, pclassname[pclass]); - Sbar_DrawTinyString (11, 58, va("int")); - Sbar_DrawTinyString (33, 58, va("%02d", pv->stats[STAT_H2_INTELLIGENCE])); + Sbar_DrawTinyString (11, 58, "int"); + Sbar_DrawTinyStringf (33, 58, "%02d", pv->stats[STAT_H2_INTELLIGENCE]); - Sbar_DrawTinyString (11, 64, va("wis")); - Sbar_DrawTinyString (33, 64, va("%02d", pv->stats[STAT_H2_WISDOM])); + Sbar_DrawTinyString (11, 64, "wis"); + Sbar_DrawTinyStringf (33, 64, "%02d", pv->stats[STAT_H2_WISDOM]); - Sbar_DrawTinyString (11, 70, va("dex")); - Sbar_DrawTinyString (33, 70, va("%02d", pv->stats[STAT_H2_DEXTERITY])); + Sbar_DrawTinyString (11, 70, "dex"); + Sbar_DrawTinyStringf (33, 70, "%02d", pv->stats[STAT_H2_DEXTERITY]); - Sbar_DrawTinyString (58, 58, va("str")); - Sbar_DrawTinyString (80, 58, va("%02d", pv->stats[STAT_H2_STRENGTH])); + Sbar_DrawTinyString (58, 58, "str"); + Sbar_DrawTinyStringf (80, 58, "%02d", pv->stats[STAT_H2_STRENGTH]); - Sbar_DrawTinyString (58, 64, va("lvl")); - Sbar_DrawTinyString (80, 64, va("%02d", pv->stats[STAT_H2_LEVEL])); + Sbar_DrawTinyString (58, 64, "lvl"); + Sbar_DrawTinyStringf (80, 64, "%02d", pv->stats[STAT_H2_LEVEL]); - Sbar_DrawTinyString (58, 70, va("exp")); - Sbar_DrawTinyString (80, 70, va("%06d", pv->stats[STAT_H2_EXPERIENCE])); + Sbar_DrawTinyString (58, 70, "exp"); + Sbar_DrawTinyStringf (80, 70, "%06d", pv->stats[STAT_H2_EXPERIENCE]); - Sbar_DrawTinyString (11, 79, va("abilities")); + Sbar_DrawTinyString (11, 79, "abilities"); if (pv->stats[STAT_H2_FLAGS] & (1<<22)) Sbar_DrawTinyString (8, 89, T_GetString(400 + 2*(pclass-1) + 0)); if (pv->stats[STAT_H2_FLAGS] & (1<<23)) @@ -2240,7 +2244,7 @@ static void Sbar_Hexen2DrawExtra (playerview_t *pv) if (pv->stats[STAT_H2_ARMOUR1+i] > 0) { Sbar_DrawPic (164+i*40, 115, 28, 19, R2D_SafeCachePic(va("gfx/armor%d.lmp", i+1))); - Sbar_DrawTinyString (168+i*40, 136, va("+%d", pv->stats[STAT_H2_ARMOUR1+i])); + Sbar_DrawTinyStringf (168+i*40, 136, "+%d", pv->stats[STAT_H2_ARMOUR1+i]); } } for (i = 0; i < 4; i++) @@ -2261,7 +2265,7 @@ static void Sbar_Hexen2DrawExtra (playerview_t *pv) slot = 0; for (i = 0; i < 8; i++) { - if (pv->statsstr[STAT_H2_PUZZLE1+i]) + if (pv->statsstr[STAT_H2_PUZZLE1+i] && *pv->statsstr[STAT_H2_PUZZLE1+i]) { Sbar_DrawPic (194+(slot%4)*31, slot<4?51:82, 26, 26, R2D_SafeCachePic(va("gfx/puzzle/%s.lmp", pv->statsstr[STAT_H2_PUZZLE1+i]))); slot++; @@ -2319,7 +2323,7 @@ static void Sbar_Hexen2DrawBasic(playerview_t *pv) maxval = pv->stats[STAT_H2_MAXMANA]; val = pv->stats[STAT_H2_BLUEMANA]; val = bound(0, val, maxval); - Sbar_DrawTinyString(201, 22, va("%03d", val)); + Sbar_DrawTinyStringf(201, 22, "%03d", val); if(val) { Sbar_DrawPic(190, 26-(int)((val*18.0)/(float)maxval+0.5), 3, 19, R2D_SafeCachePic("gfx/bmana.lmp")); @@ -2330,7 +2334,7 @@ static void Sbar_Hexen2DrawBasic(playerview_t *pv) maxval = pv->stats[STAT_H2_MAXMANA]; val = pv->stats[STAT_H2_GREENMANA]; val = bound(0, val, maxval); - Sbar_DrawTinyString(243, 22, va("%03d", val)); + Sbar_DrawTinyStringf(243, 22, "%03d", val); if(val) { Sbar_DrawPic(232, 26-(int)((val*18.0)/(float)maxval+0.5), 3, 19, R2D_SafeCachePic("gfx/gmana.lmp")); @@ -2368,8 +2372,8 @@ static void Sbar_Hexen2DrawMinimal(playerview_t *pv) Sbar_DrawPic(3, y, 31, 17, R2D_SafeCachePic("gfx/bmmana.lmp")); Sbar_DrawPic(3, y+18, 31, 17, R2D_SafeCachePic("gfx/gmmana.lmp")); - Sbar_DrawTinyString(10, y+6, va("%03d", pv->stats[STAT_H2_BLUEMANA])); - Sbar_DrawTinyString(10, y+18+6, va("%03d", pv->stats[STAT_H2_GREENMANA])); + Sbar_DrawTinyStringf(10, y+6, "%03d", pv->stats[STAT_H2_BLUEMANA]); + Sbar_DrawTinyStringf(10, y+18+6, "%03d", pv->stats[STAT_H2_GREENMANA]); Sbar_Hexen2DrawNum(38, y+18, pv->stats[STAT_HEALTH], 3); } @@ -2786,6 +2790,7 @@ void Sbar_TeamOverlay (void) char num[12]; team_t *tm; int plow, phigh, pavg; + int pw,ph; playerview_t *pv = r_refdef.playerview; if (!pv) @@ -2803,9 +2808,9 @@ void Sbar_TeamOverlay (void) if (scr_scoreboard_drawtitle.ival) { pic = R2D_SafeCachePic ("gfx/ranking.lmp"); - if (pic) + if (pic && R_GetShaderSizes(pic, &pw, &ph, false)>0) { - k = (pic->width * 24) / pic->height; + k = (pw * 24) / ph; R2D_ScalePic ((vid.width-k)/2, 0, k, 24, pic); } y += 24; @@ -3033,8 +3038,12 @@ void Sbar_DeathmatchOverlay (int start) pic = R2D_SafeCachePic ("gfx/ranking.lmp"); if (pic) { - k = (pic->width * 24) / pic->height; - R2D_ScalePic ((vid.width-k)/2, 0, k, 24, pic); + int w, h; + if (R_GetShaderSizes(pic, &w, &h, false)>0) + { + k = (w * 24) / h; + R2D_ScalePic ((vid.width-k)/2, 0, k, 24, pic); + } } y += 24; } diff --git a/engine/client/skin.c b/engine/client/skin.c index 0bafd5d8..c13f461e 100644 --- a/engine/client/skin.c +++ b/engine/client/skin.c @@ -216,25 +216,18 @@ qbyte *Skin_Cache8 (qwskin_t *skin) int runLength; int fbremap[256]; char *skinpath; + size_t pcxsize; if (noskins.value==1) // JACK: So NOSKINS > 1 will show skins, but return NULL; // not download new ones. - if (skin->failedload) + if (skin->failedload==true) return NULL; - TEXASSIGN(skin->textures.base, r_nulltex); - TEXASSIGN(skin->textures.loweroverlay, r_nulltex); - TEXASSIGN(skin->textures.upperoverlay, r_nulltex); - out = skin->skindata; if (out) return out; - // TODO: we build a fullbright remap.. can we get rid of this? - for (x = 0; x < vid.fullbright; x++) - fbremap[x] = x + (256-vid.fullbright); //fullbrights don't exist, so don't loose palette info. - // // load the pic from disk // @@ -267,63 +260,60 @@ qbyte *Skin_Cache8 (qwskin_t *skin) skinpath = "skins"; - //favour 24bit+recoloured skins if gl_load24bit is enabled. - Q_snprintfz (name, sizeof(name), "%s_shirt", skin->name); - TEXASSIGN(skin->textures.upperoverlay, R_LoadReplacementTexture(name, skinpath, 0)); - Q_snprintfz (name, sizeof(name), "%s_pants", skin->name); - TEXASSIGN(skin->textures.loweroverlay, R_LoadReplacementTexture(name, skinpath, 0)); - if (TEXVALID(skin->textures.upperoverlay) || TEXVALID(skin->textures.loweroverlay)) + //2 is used to try to force the skin if it load24bit is false. + if (gl_load24bit.ival || skin->failedload==2) { - TEXASSIGN(skin->textures.base, R_LoadReplacementTexture(skin->name, skinpath, IF_NOALPHA)); - if (TEXVALID(skin->textures.base)) + if (!skin->textures.base) + skin->textures.base = R_LoadHiResTexture(skin->name, skinpath, IF_NOALPHA|IF_NOPCX); + if (skin->textures.base->status == TEX_LOADING) + return NULL; //don't spam the others until we actually know this one will load. + if (skin->failedload == 2) + skin->failedload = 1; + if (TEXLOADED(skin->textures.base)) { - Q_snprintfz (name, sizeof(name), "%s_luma", skin->name); - TEXASSIGN(skin->textures.fullbright, R_LoadReplacementTexture(skin->name, skinpath, IF_NOALPHA)); - Q_snprintfz (name, sizeof(name), "%s_gloss", skin->name); - TEXASSIGN(skin->textures.specular, R_LoadReplacementTexture(skin->name, skinpath, 0)); - skin->failedload = true; - return NULL; + if (!skin->textures.upperoverlay) + { + Q_snprintfz (name, sizeof(name), "%s_shirt", skin->name); + TEXASSIGN(skin->textures.upperoverlay, R_LoadHiResTexture(name, skinpath, 0)); + } + if (!skin->textures.loweroverlay) + { + Q_snprintfz (name, sizeof(name), "%s_pants", skin->name); + TEXASSIGN(skin->textures.loweroverlay, R_LoadHiResTexture(name, skinpath, 0)); + } + if (!skin->textures.fullbright) + { + Q_snprintfz (name, sizeof(name), "%s_luma", skin->name); + TEXASSIGN(skin->textures.fullbright, R_LoadHiResTexture(skin->name, skinpath, 0)); + } + if (!skin->textures.specular) + { + Q_snprintfz (name, sizeof(name), "%s_gloss", skin->name); + TEXASSIGN(skin->textures.specular, R_LoadHiResTexture(skin->name, skinpath, 0)); + } + skin->failedload = 1; + return NULL; //can use the high-res textures instead. } } Q_snprintfz (name, sizeof(name), "%s/%s.pcx", skinpath, skin->name); - raw = COM_LoadTempFile (name); + raw = COM_LoadTempFile (name, &pcxsize); if (!raw) { //use 24bit skins even if gl_load24bit is failed if (strcmp(skin->name, baseskin.string)) { -// if (!gl_load24bit.value) - { - TEXASSIGN(skin->textures.base, R_LoadHiResTexture(skin->name, skinpath, IF_NOALPHA)); - if (TEXVALID(skin->textures.base)) - { - Q_snprintfz (name, sizeof(name), "%s_shirt", skin->name); - TEXASSIGN(skin->textures.upperoverlay, R_LoadHiResTexture(name, skinpath, 0)); - Q_snprintfz (name, sizeof(name), "%s_pants", skin->name); - TEXASSIGN(skin->textures.loweroverlay, R_LoadHiResTexture(name, skinpath, 0)); - - if (!TEXVALID(skin->textures.upperoverlay) && !TEXVALID(skin->textures.loweroverlay)) - Con_DPrintf("skin \"%s\" has no colourmapping info\n", skin->name); - - Q_snprintfz (name, sizeof(name), "%s_luma", skin->name); - TEXASSIGN(skin->textures.fullbright, R_LoadHiResTexture(skin->name, skinpath, IF_NOALPHA)); - Q_snprintfz (name, sizeof(name), "%s_gloss", skin->name); - TEXASSIGN(skin->textures.specular, R_LoadHiResTexture(skin->name, skinpath, 0)); - - skin->failedload = true; - return NULL; - } - } - //if its not already the base skin, try the base (and warn if anything not base couldn't load). Con_Printf ("Couldn't load skin %s\n", name); Q_snprintfz (name, sizeof(name), "skins/%s.pcx", baseskin.string); - raw = COM_LoadTempFile (name); + raw = COM_LoadTempFile (name, &pcxsize); } if (!raw) { - skin->failedload = true; + if (skin->failedload) + skin->failedload = true; + else + skin->failedload = 2; return NULL; } } @@ -366,6 +356,11 @@ qbyte *Skin_Cache8 (qwskin_t *skin) if (!out) Sys_Error ("Skin_Cache: couldn't allocate"); + // TODO: we build a fullbright remap.. can we get rid of this? + for (x = 0; x < vid.fullbright; x++) + fbremap[x] = x + (256-vid.fullbright); //fullbrights don't exist, so don't loose palette info. + + pix = out; // memset (out, 0, skin->width*skin->height); @@ -374,7 +369,7 @@ qbyte *Skin_Cache8 (qwskin_t *skin) { for (x=0 ; x < srcw ; ) { - if (raw - (qbyte*)pcx > com_filesize) + if (raw - (qbyte*)pcx > pcxsize) { BZ_Free(skin->skindata); skin->skindata = NULL; @@ -387,7 +382,7 @@ qbyte *Skin_Cache8 (qwskin_t *skin) if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; - if (raw - (qbyte*)pcx > com_filesize) + if (raw - (qbyte*)pcx > pcxsize) { BZ_Free(skin->skindata); skin->skindata = NULL; @@ -426,7 +421,7 @@ qbyte *Skin_Cache8 (qwskin_t *skin) for (x = 0; x < skin->width; ) pix[x++] = dataByte; - if ( raw - (qbyte *)pcx > com_filesize) + if ( raw - (qbyte *)pcx > pcxsize) { BZ_Free(skin->skindata); skin->skindata = NULL; diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index c1d670dc..3dd69ac6 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -30,7 +30,7 @@ static void S_StopAllSounds_f (void); static void S_UpdateCard(soundcardinfo_t *sc); static void S_ClearBuffer (soundcardinfo_t *sc); -static sfx_t *S_FindName (const char *name); +sfx_t *S_FindName (const char *name, qboolean create); // ======================================================================= // Internal sound data & structures @@ -1762,7 +1762,7 @@ void S_DoRestart (void) { if (!cl.sound_name[i][0]) break; - cl.sound_precache[i] = S_FindName (cl.sound_name[i]); + cl.sound_precache[i] = S_FindName (cl.sound_name[i], true); } } @@ -1993,6 +1993,14 @@ void S_Shutdown(qboolean final) Z_Free(known_sfx); known_sfx = NULL; num_sfx = 0; + +#ifdef MULTITHREAD + if (final && mixermutex) + { + Sys_DestroyMutex(mixermutex); + mixermutex = NULL; + } +#endif } @@ -2007,7 +2015,7 @@ S_FindName also touches it ================== */ -static sfx_t *S_FindName (const char *name) +sfx_t *S_FindName (const char *name, qboolean create) { int i; sfx_t *sfx; @@ -2029,11 +2037,16 @@ static sfx_t *S_FindName (const char *name) if (num_sfx == MAX_SFX) Sys_Error ("S_FindName: out of sfx_t"); - sfx = &known_sfx[i]; - strcpy (sfx->name, name); - known_sfx[i].touched = true; + if (create) + { + sfx = &known_sfx[i]; + strcpy (sfx->name, name); + known_sfx[i].touched = true; - num_sfx++; + num_sfx++; + } + else + sfx = NULL; return sfx; } @@ -2057,15 +2070,18 @@ void S_Purge(qboolean retaintouched) for (i=0 ; i < num_sfx ; i++) { sfx = &known_sfx[i]; + /*don't hurt sounds if they're being processed by a worker thread*/ + if (sfx->loadstate == SLS_LOADING) + continue; /*don't purge the file if its still relevent*/ if (retaintouched && sfx->touched) continue; + sfx->loadstate = SLS_NOTLOADED; /*nothing to do if there's no data within*/ if (!sfx->decoder.buf) continue; - /*stop the decoder first*/ if (sfx->decoder.purge) sfx->decoder.purge(sfx); @@ -2084,7 +2100,10 @@ void S_ResetFailedLoad(void) { int i; for (i=0 ; i < num_sfx ; i++) - known_sfx[i].failedload = false; + { + if (known_sfx[i].loadstate == SLS_FAILED) + known_sfx[i].loadstate = SLS_NOTLOADED; + } } void S_UntouchAll(void) @@ -2105,7 +2124,7 @@ void S_TouchSound (char *name) if (!sound_started) return; - S_FindName (name); + S_FindName (name, true); } /* @@ -2118,10 +2137,10 @@ sfx_t *S_PrecacheSound (const char *name) { sfx_t *sfx; - if (nosound.ival || !known_sfx) + if (nosound.ival || !known_sfx || !*name) return NULL; - sfx = S_FindName (name); + sfx = S_FindName (name, true); // cache it in if (precache.ival && sndcardinfo) @@ -2597,7 +2616,7 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc) { newmusic = S_PrecacheSound(nexttrack); - if (newmusic && !newmusic->failedload) + if (newmusic && newmusic->loadstate != SLS_FAILED) { chan->sfx = newmusic; chan->rate = 1<name, data, datalen); if (info.numchannels < 1 || info.numchannels > 2) { - s->failedload = true; + s->loadstate = SLS_FAILED; Con_Printf ("%s has an unsupported quantity of channels.\n",s->name); return false; } @@ -762,26 +762,19 @@ S_LoadSound ============== */ -qboolean S_LoadSound (sfx_t *s) +qboolean S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { - char stackbuf[65536]; + sfx_t *s = ctx; char namebuffer[256]; qbyte *data; int i; size_t result; char *name = s->name; - - if (s->failedload) - return false; //it failed to load once before, don't bother trying again. - -// see if still in memory - if (s->decoder.buf || s->decoder.decodedata) - return true; + size_t filesize; if (name[1] == ':' && name[2] == '\\') { vfsfile_t *f; - int fsize; #ifndef _WIN32 //convert from windows to a suitable alternative. char unixname[128]; Q_snprintfz(unixname, sizeof(unixname), "/mnt/%c/%s", name[0]-'A'+'a', name+3); @@ -798,18 +791,19 @@ qboolean S_LoadSound (sfx_t *s) if ((f = VFSOS_Open(name, "rb"))) { - fsize = VFS_GETLEN(f); - data = Hunk_TempAlloc (fsize); - result = VFS_READ(f, data, fsize); + filesize = VFS_GETLEN(f); + data = BZ_Malloc (filesize); + result = VFS_READ(f, data, filesize); - if (result != fsize) - Con_SafePrintf("S_LoadSound() fread: Filename: %s, expected %i, result was %u\n", name, fsize, (unsigned int)result); + if (result != filesize) + Con_SafePrintf("S_LoadSound() fread: Filename: %s, expected %i, result was %u\n", name, filesize, (unsigned int)result); VFS_CLOSE(f); } else { Con_SafePrintf ("Couldn't load %s\n", namebuffer); + s->loadstate = SLS_FAILED; return false; } } @@ -819,12 +813,12 @@ qboolean S_LoadSound (sfx_t *s) // load it in data = NULL; + filesize = 0; if (*name == '*') //q2 sexed sounds { - //clq2_parsestartsound detects this also - //here we just precache the male sound name, which provides us with our default - Q_strcpy(namebuffer, "players/male/"); //q2 - Q_strcat(namebuffer, name+1); //q2 + //clq2_parsestartsound detects this also, and should not try playing these sounds. + s->loadstate = SLS_FAILED; + return false; } else if (name[0] == '.' && name[1] == '.' && name[2] == '/') { @@ -836,19 +830,19 @@ qboolean S_LoadSound (sfx_t *s) //q1 behaviour, relative to sound/ Q_strcpy(namebuffer, "sound/"); Q_strcat(namebuffer, name); - data = COM_LoadStackFile(name, stackbuf, sizeof(stackbuf)); + data = COM_LoadFile(namebuffer, 5, &filesize); } // Con_Printf ("loading %s\n",namebuffer); if (!data) - data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); + data = COM_LoadFile(name, 5, &filesize); if (!data) { char altname[sizeof(namebuffer)]; COM_StripExtension(namebuffer, altname, sizeof(altname)); COM_DefaultExtension(altname, ".ogg", sizeof(altname)); - data = COM_LoadStackFile(altname, stackbuf, sizeof(stackbuf)); + data = COM_LoadFile(altname, 5, &filesize); if (data) Con_DPrintf("found a mangled name\n"); } @@ -858,31 +852,43 @@ qboolean S_LoadSound (sfx_t *s) { //FIXME: check to see if queued for download. Con_DPrintf ("Couldn't load %s\n", namebuffer); - s->failedload = true; + s->loadstate = SLS_FAILED; return false; } - s->failedload = false; - for (i = sizeof(AudioInputPlugins)/sizeof(AudioInputPlugins[0])-1; i >= 0; i--) { if (AudioInputPlugins[i]) { - if (AudioInputPlugins[i](s, data, com_filesize, snd_speed)) + if (AudioInputPlugins[i](s, data, filesize, snd_speed)) { + s->loadstate = SLS_LOADED; + BZ_Free(data); return true; } } } - if (!s->failedload) + if (s->loadstate != SLS_FAILED) Con_Printf ("Format not recognised: %s\n", namebuffer); - s->failedload = true; + s->loadstate = SLS_FAILED; + BZ_Free(data); return false; } +qboolean S_LoadSound (sfx_t *s) +{ + if (s->loadstate == SLS_NOTLOADED && sndcardinfo) + { + s->loadstate = SLS_LOADING; + COM_AddWork(1, S_LoadSoundWorker, s, NULL, 0, 0); + } + if (s->loadstate == SLS_FAILED) + return false; //it failed to load once before, don't bother trying again. + return true; //loaded okay, or still loading +} /* =============================================================================== diff --git a/engine/client/snd_mix.c b/engine/client/snd_mix.c index d087cb27..6373e4e5 100644 --- a/engine/client/snd_mix.c +++ b/engine/client/snd_mix.c @@ -143,7 +143,7 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime) for (i=0; itotal_chans ; i++, ch++) { s = ch->sfx; - if (!s) + if (!s || s->loadstate == SLS_LOADING) continue; if (!ch->vol[0] && !ch->vol[1] && !ch->vol[2] && !ch->vol[3] && !ch->vol[4] && !ch->vol[5]) { diff --git a/engine/client/snd_ov.c b/engine/client/snd_ov.c index d3730b5d..786fa117 100644 --- a/engine/client/snd_ov.c +++ b/engine/client/snd_ov.c @@ -108,7 +108,7 @@ qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed) buffer->decodedbytecount = 0; Z_Free(s->decoder.buf); s->decoder.buf = NULL; - s->failedload = true; + s->loadstate = SLS_FAILED; //failed! return false; } diff --git a/engine/client/sound.h b/engine/client/sound.h index 92a8e221..7e1823ae 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -50,7 +50,12 @@ typedef struct sfx_s char name[MAX_OSPATH]; sfxdecode_t decoder; - qboolean failedload:1; //no more super-spammy + enum { + SLS_NOTLOADED, //not tried to load it + SLS_LOADING, //loading it on a worker thread. + SLS_LOADED, //currently in memory and usable. + SLS_FAILED //already tried to load it. it won't work. not found, invalid format, etc + } loadstate; //no more super-spammy qboolean touched:1; //if the sound is still relevent #ifdef AVAIL_OPENAL @@ -136,6 +141,7 @@ qboolean S_HaveOutput(void); void S_Music_Clear(sfx_t *onlyifsample); void S_Music_Seek(float time); +sfx_t *S_FindName (const char *name, qboolean create); sfx_t *S_PrecacheSound (const char *sample); void S_TouchSound (char *sample); void S_UntouchAll(void); diff --git a/engine/client/sys_plugfte.c b/engine/client/sys_plugfte.c index 3a2797c9..027fdf56 100644 --- a/engine/client/sys_plugfte.c +++ b/engine/client/sys_plugfte.c @@ -348,6 +348,9 @@ char *COM_ParseOut (const char *data, char *out, int outlen) int c; int len; + if (out == com_token) + COM_AssertMainThread("COM_ParseOut: com_token"); + len = 0; out[0] = 0; diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 983db598..e3acd17d 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -29,6 +29,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include #include +#include "pr_common.h" //#define RESTARTTEST @@ -591,8 +592,8 @@ DWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTI } #ifdef PRINTGLARRAYS - if (!iswatchdog && qrenderer == QR_OPENGL) - DumpGLState(); +// if (!iswatchdog && qrenderer == QR_OPENGL) +// DumpGLState(); #endif hKernel = LoadLibrary ("kernel32"); @@ -1428,7 +1429,6 @@ void Sys_Shutdown(void) } } - void VARGS Sys_Error (const char *error, ...) { va_list argptr; @@ -1440,6 +1440,8 @@ void VARGS Sys_Error (const char *error, ...) vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); + COM_WorkerAbort(text); + #ifndef SERVERONLY SetHookState(false); Host_Shutdown (); @@ -2472,6 +2474,7 @@ void Win7_TaskListInit(void) link->lpVtbl->Release(link); } break; +#ifdef HEXEN2 case MGT_HEXEN2: link = CreateShellLink("+menu_servers", "", "Hexen2 Server List", "Pick a multiplayer server to join"); if (link) @@ -2486,6 +2489,7 @@ void Win7_TaskListInit(void) link->lpVtbl->Release(link); } break; +#endif } if (SUCCEEDED(col->lpVtbl->QueryInterface(col, &qIID_IObjectArray, (void**)&arr))) diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 6a59841a..48f5371a 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -16,6 +16,7 @@ F11 will step through. #include "quakedef.h" #ifdef TEXTEDITOR +#include "pr_common.h" #ifdef _WIN32 #define editaddcr_default "1" @@ -361,6 +362,7 @@ static void EditorOpenFile(char *name, qboolean readonly) firstblock->flags |= FB_BREAK; } } +#ifndef CLIENTONLY else { if (svprogfuncs) @@ -371,6 +373,7 @@ static void EditorOpenFile(char *name, qboolean readonly) } } } +#endif i++; } diff --git a/engine/client/valid.c b/engine/client/valid.c index d14c84ef..ac865a73 100644 --- a/engine/client/valid.c +++ b/engine/client/valid.c @@ -66,9 +66,8 @@ static void Validation_Version(void) char authbuf[256]; char *auth = authbuf; - extern cvar_t r_shadow_realtime_world, r_drawflat; + extern cvar_t r_drawflat; - s = sr; //print certain allowed 'cheat' options. //realtime lighting (shadows can show around corners) //drawflat is just lame @@ -174,7 +173,7 @@ void Validation_CheckIfResponse(char *text) } { - char *match = DISTRIBUTION"Quake v"; + char *match = DISTRIBUTION" v"; if (strncmp(versionstring, match, strlen(match))) return; //this is not us } diff --git a/engine/client/vid.h b/engine/client/vid.h index 48ad32bb..7fac47ee 100644 --- a/engine/client/vid.h +++ b/engine/client/vid.h @@ -42,7 +42,9 @@ typedef struct { char subrenderer[MAX_QPATH]; struct rendererinfo_s *renderer; } rendererstate_t; +#ifndef SERVERONLY extern rendererstate_t currentrendererstate; +#endif typedef struct vrect_s { diff --git a/engine/client/vid_headless.c b/engine/client/vid_headless.c index 0c0c4657..40634b8c 100644 --- a/engine/client/vid_headless.c +++ b/engine/client/vid_headless.c @@ -14,45 +14,26 @@ static texid_tf dummytex; static void Headless_Draw_Init(void) { - //we always return some valid texture. this avoids having to hit the disk for each and every possibility until it fails, thus 'loading' textures much faster (hurrah for findtexture always finding one). - static texcom_t dummytexinfo; - dummytexinfo.width = 64; - dummytexinfo.height = 64; - dummytex.ref = &dummytexinfo; R2D_Init(); } static void Headless_Draw_Shutdown(void) { Shader_Shutdown(); } -static texid_tf Headless_IMG_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags) -{ - return dummytex; -} -static texid_tf Headless_IMG_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) -{ - return dummytex; -} -static texid_tf Headless_IMG_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) -{ - return dummytex; -} -static texid_tf Headless_IMG_LoadCompressed (const char *name) -{ - return dummytex; -} -static texid_tf Headless_IMG_FindTexture (const char *identifier, unsigned int flags) -{ - return dummytex; -} -static texid_tf Headless_IMG_AllocNewTexture (const char *identifier, int w, int h, unsigned int flags) -{ - return dummytex; -} -static void Headless_IMG_Upload (texid_t tex, const char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags) +static void Headless_IMG_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis) { } -static void Headless_IMG_DestroyTexture (texid_t tex) +static qboolean Headless_IMG_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips) +{ + int i; + for (i = 0; i < mips->mipcount; i++) + if (mips->mip[i].needfree) + Z_Free(mips->mip[i].data); + if (mips->extrafree) + Z_Free(mips->extrafree); + return true; +} +static void Headless_IMG_DestroyTexture (texid_t tex) { } static void Headless_R_Init (void) @@ -64,13 +45,6 @@ static void Headless_R_DeInit (void) static void Headless_R_RenderView (void) { } -static void Headless_R_NewMap (void) -{ -} -static void Headless_R_PreNewMap (void) -{ -} - #ifdef _WIN32 //tray icon crap, so the user can still restore the game. LRESULT CALLBACK HeadlessWndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) @@ -202,7 +176,7 @@ static void Headless_BE_UploadAllLightmaps (void) static void Headless_BE_SelectEntity (struct entity_s *ent) { } -static qboolean Headless_BE_SelectDLight (struct dlight_s *dl, vec3_t colour, unsigned int lmode) +static qboolean Headless_BE_SelectDLight (struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode) { return false; } @@ -237,19 +211,12 @@ rendererinfo_t headlessrenderer = Headless_Draw_Init, Headless_Draw_Shutdown, - Headless_IMG_LoadTexture, - Headless_IMG_LoadTexture8Pal24, - Headless_IMG_LoadTexture8Pal32, - Headless_IMG_LoadCompressed, - Headless_IMG_FindTexture, - Headless_IMG_AllocNewTexture, - Headless_IMG_Upload, + Headless_IMG_UpdateFiltering, + Headless_IMG_LoadTextureMips, Headless_IMG_DestroyTexture, Headless_R_Init, Headless_R_DeInit, Headless_R_RenderView, - Headless_R_NewMap, - Headless_R_PreNewMap, Headless_VID_Init, Headless_VID_DeInit, Headless_VID_SwapBuffers, diff --git a/engine/client/view.c b/engine/client/view.c index 897c270b..1c8e2ad2 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1302,10 +1302,6 @@ void V_CalcRefdef (playerview_t *pv) r_refdef.time = cl.servertime; - { - extern model_t *loadmodel; - loadmodel = cl.worldmodel; - } if (chase_active.ival && cls.allow_cheats) //cheat restriction might be lifted some time when any wallhacks are solved. { @@ -1323,7 +1319,6 @@ void V_CalcRefdef (playerview_t *pv) cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, 0, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, true, MASK_WORLDSOLID, &tr); if (!tr.startsolid) { - float extralen; if (tr.fraction < 1) { //we found a plane, bisect it weirdly to push 4qu infront @@ -1675,7 +1670,7 @@ void V_RenderView (void) } else { - if (r_worldentity.model && !r_worldentity.model->needload) + if (r_worldentity.model && r_worldentity.model->loadstate == MLS_LOADED) { RSpeedMark(); diff --git a/engine/client/wad.c b/engine/client/wad.c index c8921673..f451e799 100644 --- a/engine/client/wad.c +++ b/engine/client/wad.c @@ -81,7 +81,7 @@ void W_LoadWadFile (char *filename) if (wad_base) Z_Free(wad_base); - wad_base = COM_LoadFile (filename, 0); + wad_base = COM_LoadFile (filename, 0, NULL); if (!wad_base) { wad_numlumps = 0; @@ -432,7 +432,7 @@ qbyte *W_GetTexture(const char *name, int *width, int *height, qboolean *usesalp miptex_t *tex; qbyte *data; - if (!strncmp(name, "gfx/", 4)) + if ((!strncmp(name, "gfx/", 4) || !strncmp(name, "wad/", 4)) && strcmp(name, "gfx/conchars")) { qpic_t *p; p = W_SafeGetLumpName(name+4); @@ -580,6 +580,7 @@ void CL_Skygroup_f(void) char wads[4096]; void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //actually, this should be in the model code. { + char token[4096]; char key[128]; mapskys_t *msky; @@ -600,31 +601,31 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //a cl.skyname[0] = '\0'; if (data) - if ((data=COM_Parse(data))) //read the map info. - if (com_token[0] == '{') + if ((data=COM_ParseOut(data, token, sizeof(token)))) //read the map info. + if (token[0] == '{') while (1) { - if (!(data=COM_Parse(data))) + if (!(data=COM_ParseOut(data, token, sizeof(token)))) break; // error - if (com_token[0] == '}') + if (token[0] == '}') break; // end of worldspawn - if (com_token[0] == '_') - strcpy(key, com_token + 1); //_ vars are for comments/utility stuff that arn't visible to progs. Ignore them. + if (token[0] == '_') + Q_strncpyz(key, token + 1, sizeof(key)); //_ vars are for comments/utility stuff that arn't visible to progs. Ignore them. else - strcpy(key, com_token); - if (!((data=COM_Parse(data)))) + Q_strncpyz(key, token, sizeof(key)); + if (!((data=COM_ParseOut(data, token, sizeof(token))))) break; // error if (!strcmp("wad", key)) // for HalfLife maps { if (wmodel->fromgame == fg_halflife) { - strncat(wads, ";", 4095); //cache it for later (so that we don't play with any temp memory yet) - strncat(wads, com_token, 4095); //cache it for later (so that we don't play with any temp memory yet) + Q_strncatz(wads, ";", sizeof(wads)); //cache it for later (so that we don't play with any temp memory yet) + Q_strncatz(wads, token, sizeof(wads)); //cache it for later (so that we don't play with any temp memory yet) } } else if (!strcmp("skyname", key)) // for HalfLife maps { - Q_strncpyz(cl.skyname, com_token, sizeof(cl.skyname)); + Q_strncpyz(cl.skyname, token, sizeof(cl.skyname)); } else if (!strcmp("fog", key)) //q1 extension. FIXME: should be made temporary. { @@ -632,7 +633,7 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //a void CL_Fog_f(void); key[0] = 'f'; key[1] = ' '; - Q_strncpyz(key+2, com_token, sizeof(key)-2); + Q_strncpyz(key+2, token, sizeof(key)-2); Cmd_TokenizeString(key, false, false); Cmd_ExecLevel=RESTRICT_LOCAL; CL_Fog_f(); @@ -666,25 +667,25 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel, char *data, char *mapname) //a } else if (!strcmp("sky", key)) // for Quake2 maps { - Q_strncpyz(cl.skyname, com_token, sizeof(cl.skyname)); + Q_strncpyz(cl.skyname, token, sizeof(cl.skyname)); } else if (!strcmp("skyrotate", key)) //q2 feature { - cl.skyrotate = atof(com_token); + cl.skyrotate = atof(token); } else if (!strcmp("skyaxis", key)) //q2 feature { char *s; - Q_strncpyz(key, com_token, sizeof(key)); - s = COM_Parse(key); + Q_strncpyz(key, token, sizeof(key)); + s = COM_ParseOut(key, token, sizeof(token)); if (s) { cl.skyaxis[0] = atof(s); - s = COM_Parse(s); + s = COM_ParseOut(s, token, sizeof(token)); if (s) { cl.skyaxis[1] = atof(s); - COM_Parse(s); + COM_ParseOut(s, token, sizeof(token)); if (s) cl.skyaxis[2] = atof(s); } diff --git a/engine/client/winquake.h b/engine/client/winquake.h index f9dfb512..55494093 100644 --- a/engine/client/winquake.h +++ b/engine/client/winquake.h @@ -32,6 +32,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #pragma warning( disable : 4229 ) // mgraph gets this #endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif #define WIN32_LEAN_AND_MEAN #define byte winbyte #include diff --git a/engine/client/winquake.rc b/engine/client/winquake.rc index 7f576c4f..989bc9cc 100644 --- a/engine/client/winquake.rc +++ b/engine/client/winquake.rc @@ -53,7 +53,7 @@ END // // Dialog // - +#if 0 IDD_DIALOG1 DIALOGEX 0, 0, 67, 40 STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE @@ -62,8 +62,7 @@ BEGIN CTEXT FULLENGINENAME,IDC_STATIC,0,0,67,21,SS_CENTERIMAGE CTEXT ENGINEWEBSITE,IDC_STATIC,0,23,66,17,SS_CENTERIMAGE END - - +#endif #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// @@ -85,7 +84,14 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. +#ifdef BRANDING_ICON +IDI_ICON1 ICON BRANDING_ICON +IDI_ICON2 ICON "bymorphed.ico" +IDI_ICON3 ICON "q2.ico" +#else IDI_ICON1 ICON "bymorphed.ico" +IDI_ICON2 ICON "q2.ico" +#endif ///////////////////////////////////////////////////////////////////////////// // diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index 54d5b522..72400081 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1392,7 +1392,7 @@ static void TP_LoadLocFile (char *filename, qbool quiet) Q_snprintfz (fullpath, sizeof(fullpath) - 4, "locs/%s", filename); COM_DefaultExtension (fullpath, ".loc", sizeof(fullpath)); - buf = (char *) COM_LoadTempFile (fullpath); + buf = (char *) COM_LoadTempFile (fullpath, NULL); if (!buf) { if (!quiet) diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index e5058055..d056990c 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -25,15 +25,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define FTE_VER_MAJOR 1 #define FTE_VER_MINOR 3 -//#define VERSION 2.56 -#ifndef DISTRIBUTION - #define DISTRIBUTION "FTE" //short name used to identify this engine. must be a single word - #define DISTRIBUTIONLONG "Forethought Entertainment" //effectively the 'company' name - #define FULLENGINENAME "FTE QuakeWorld" //the posh name for the engine - #define ENGINEWEBSITE "http://www.fteqw.com" //url for program -#endif - - #if defined(__APPLE__) && defined(__MACH__) #define MACOSX #endif @@ -159,6 +150,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //set any additional defines or libs in win32 #define SVRANKING + #define LOADERTHREAD #ifdef MINIMAL #define CL_MASTER //this is useful @@ -191,7 +183,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define SIDEVIEWS 4 //enable secondary/reverse views. - #define DSPMODELS //doom sprites (only needs DOOMWADS to generate the right wad file names) +// #define DSPMODELS //doom sprites (only needs DOOMWADS to generate the right wad file names) #define SPRMODELS //quake1 sprite models #define SP2MODELS //quake2 sprite models #define MD2MODELS //quake2 alias models @@ -200,7 +192,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ZYMOTICMODELS //zymotic skeletal models. #define DPMMODELS //darkplaces model format (which I've never seen anyone use) #define PSKMODELS //PSK model format (ActorX stuff from UT, though not the format the game itself uses) - #define HALFLIFEMODELS //halflife model support (experimental) +// #define HALFLIFEMODELS //halflife model support (experimental) #define INTERQUAKEMODELS #define RAGDOLL @@ -265,7 +257,25 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif -//#define QUAKETC + +//include a file to update the various configurations for game-specific configs (hopefully just names) +#ifdef BRANDING_INC + #define STRINGIFY2(s) #s + #define STRINGIFY(s) STRINGIFY2(s) + #include STRINGIFY(BRANDING_INC) +#endif +#ifndef DISTRIBUTION + #define DISTRIBUTION "FTE" //short name used to identify this engine. must be a single word +#endif +#ifndef DISTRIBUTIONLONG + #define DISTRIBUTIONLONG "Forethought Entertainment" //effectively the 'company' name +#endif +#ifndef FULLENGINENAME + #define FULLENGINENAME "FTE QuakeWorld" //the posh name for the engine +#endif +#ifndef ENGINEWEBSITE + #define ENGINEWEBSITE "http://www.fteqw.com" //url for program +#endif #ifdef QUAKETC #define NOBUILTINMENUS //kill engine menus (should be replaced with ewither csqc or menuqc) @@ -348,6 +358,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #ifndef MULTITHREAD + //database code requires threads to do stuff async. #undef USE_SQLITE #undef USE_MYSQL #endif @@ -369,6 +380,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #endif +#if (defined(NOLOADERTHREAD) || !defined(MULTITHREAD)) && defined(LOADERTHREAD) + #undef LOADERTHREAD +#endif + #ifndef _WIN32 #undef QTERM //not supported - FIXME: move to native plugin #endif diff --git a/engine/common/cmd.c b/engine/common/cmd.c index ab98597c..9dfcc68e 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -546,9 +546,19 @@ void Cmd_Exec_f (void) Con_DPrintf("Ignoring UTF-8 BOM\n"); s+=3; } + + if (!strcmp(name, "config.cfg") || !strcmp(name, "fte.cfg")) + { + //if the config is from id1 and the default.cfg was from some mod, make sure the default.cfg overrides the config. + //we won't just exec the default instead, because we can at least retain things which are not specified (ie: a few binds) + int cfgdepth = COM_FDepthFile(name, true); + int defdepth = COM_FDepthFile("default.cfg", true); + if (defdepth < cfgdepth) + Cbuf_InsertText("exec default.cfg\n", ((Cmd_FromGamecode() || com_file_untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), false); + } // don't execute anything if it was from server (either the stuffcmd/localcmd, or the file) if (!strcmp(name, "default.cfg") && !(Cmd_FromGamecode() || com_file_untrusted)) - Cbuf_InsertText ("\ncvar_lockdefaults 1\n", ((Cmd_FromGamecode() || com_file_untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), true); + Cbuf_InsertText ("\ncvar_lockdefaults 1\n", ((Cmd_FromGamecode() || com_file_untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), false); Cbuf_InsertText (s, ((Cmd_FromGamecode() || com_file_untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), true); FS_FreeFile(f); } @@ -566,7 +576,13 @@ void Cmd_Echo_f (void) int i; for (i=1 ; inext) + for (var=grp->cvars ; var ; var=var->next) + { + if (var->name && Q_strcasestr(var->name, query)) + name = var->name; + else if (var->name2 && Q_strcasestr(var->name2, query)) + name = var->name2; + else if (var->description && Q_strcasestr(var->description, query)) + name = var->name; + else + continue; + + COM_QuotedString(var->string, escapedvalue, sizeof(escapedvalue)); + + if (var->latched_string) + { + COM_QuotedString(var->latched_string, latchedvalue, sizeof(latchedvalue)); + Con_Printf("cvar ^2%s^7: %s (effective %s): %s\n", name, latchedvalue, escapedvalue, var->description?var->description:"no description"); + } + else + Con_Printf("cvar ^2%s^7: %s : %s\n", name, escapedvalue, var->description?var->description:"no description"); + } + + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (cmd->name && Q_strcasestr(cmd->name, query)) + ; + else if (cmd->description && strstr(cmd->description, query)) + ; + else + continue; + Con_Printf("command ^2%s^7: %s\n", cmd->name, cmd->description?cmd->description:"no description"); + } + //FIXME: add aliases. +} #ifndef SERVERONLY // FIXME /* @@ -1997,7 +2060,11 @@ void Cmd_ExecuteString (char *text, int level) return; #endif if (Cmd_AliasExist(cmd_argv[0], level)) - break; //server stuffed an alias for a command that it would already have received. use that instead. + return; //server stuffed an alias for a command that it would already have received. use that instead. +#if defined(CSQC_DAT) && !defined(SERVERONLY) + if (CSQC_ConsoleCommand(text)) + return; //let the csqc handle it if it wants. +#endif Cmd_ForwardToServer (); } else @@ -2852,7 +2919,11 @@ void Cmd_WriteConfig_f(void) filename = Cmd_Argv(1); if (!*filename) { +#ifdef QUAKETC + snprintf(fname, sizeof(fname), "config.cfg"); +#else snprintf(fname, sizeof(fname), "fte.cfg"); +#endif FS_NativePath(fname, FS_GAMEONLY, sysname, sizeof(sysname)); FS_CreatePath(fname, FS_GAMEONLY); f = FS_OpenVFS(fname, "wbp", FS_GAMEONLY); @@ -3113,6 +3184,8 @@ void Cmd_Init (void) Cmd_AddCommand ("cvar_purgedefaults", Cvar_PurgeDefaults_f); Cmd_AddCommand ("fs_flush", COM_RefreshFSCache_f); + Cmd_AddCommandD ("apropos", Cmd_Apropos_f, "Lists all cvars or commands with the specified substring somewhere in their name or descrition."); + Cmd_AddMacro("time", Macro_Time, true); Cmd_AddMacro("ukdate", Macro_UKDate, false); Cmd_AddMacro("usdate", Macro_USDate, false); diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index f5ee1915..08c9c463 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -2,44 +2,45 @@ #include "com_mesh.h" -extern model_t *loadmodel; -extern char loadname[]; qboolean r_loadbumpmapping; extern cvar_t dpcompat_psa_ungroup; extern cvar_t r_noframegrouplerp; cvar_t r_lerpmuzzlehack = CVARF ("r_lerpmuzzlehack", "1", CVAR_ARCHIVE); +#ifndef SERVERONLY +void Mod_UpdateCRC(void *ctx, void *data, size_t a, size_t b) +{ + char st[40]; + Q_snprintfz(st, sizeof(st), "%d", (int) a); + if (strcmp(st, Info_ValueForKey(cls.userinfo[0], ctx))) + { + Info_SetValueForKey (cls.userinfo[0], ctx, st, sizeof(cls.userinfo[0])); + if (cls.state >= ca_connected) + CL_SendClientCommand(true, "setinfo %s %s", (char*)ctx, st); + } +} +#endif + //Common loader function. void Mod_DoCRC(model_t *mod, char *buffer, int buffersize) { #ifndef SERVERONLY //we've got to have this bit - if (loadmodel->engineflags & MDLF_DOCRC) + if (mod->engineflags & MDLF_DOCRC) { unsigned short crc; qbyte *p; int len; - char st[40]; QCRC_Init(&crc); for (len = buffersize, p = buffer; len; len--, p++) QCRC_ProcessByte(&crc, *p); - sprintf(st, "%d", (int) crc); - Info_SetValueForKey (cls.userinfo[0], - (loadmodel->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, - st, sizeof(cls.userinfo[0])); + COM_AddWork(0, Mod_UpdateCRC, (mod->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, NULL, crc, 0); - if (cls.state >= ca_connected) - { - CL_SendClientCommand(true, "setinfo %s %d", - (loadmodel->engineflags & MDLF_PLAYER) ? pmodel_name : emodel_name, - (int)crc); - } - - if (!(loadmodel->engineflags & MDLF_PLAYER)) + if (!(mod->engineflags & MDLF_PLAYER)) { //eyes - loadmodel->tainted = (crc != 6967); + mod->tainted = (crc != 6967); } } #endif @@ -1665,6 +1666,15 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in #endif mesh->trneighbors = inf->ofs_trineighbours; +#ifdef SKELETALMODELS + if (!inf->numbones) + usebones = false; + else if (inf->ofs_skel_xyz && !inf->ofs_skel_weight) + usebones = false; + else if (e->fatness || !inf->ofs_skel_idx || inf->numswtransforms || inf->numbones > MAX_GPU_BONES) +#endif + usebones = false; + if (meshcache.ent == e) { if (meshcache.vertgroup == inf->shares_verts && meshcache.ent == e && usebones == meshcache.usebones) @@ -1725,8 +1735,6 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in *vbop = NULL; if (inf->ofs_skel_xyz && !inf->ofs_skel_weight) { - usebones = false; - //if we have skeletal xyz info, but no skeletal weights, then its a partial model that cannot possibly be animated. meshcache.usebonepose = NULL; mesh->xyz_array = inf->ofs_skel_xyz; @@ -1749,7 +1757,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in meshcache.vbo.colours[0] = inf->vborgba; meshcache.vbo.bonenums = inf->vbo_skel_bonenum; meshcache.vbo.boneweights = inf->vbo_skel_bweight; - if (meshcache.vbo.indicies.dummy) + if (meshcache.vbo.indicies.sysptr) *vbop = meshcache.vbop = &meshcache.vbo; } } @@ -1757,9 +1765,8 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in { mesh->xyz2_array = NULL; //skeltal animations blend bones, not verticies. - if (e->fatness || !inf->ofs_skel_idx || !usebones || inf->numswtransforms || inf->numbones > MAX_GPU_BONES) + if (!usebones) { - usebones = false; if (inf->numindexes) { //software bone animation @@ -1805,8 +1812,6 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in else #endif { - usebones = false; - frame1 = e->framestate.g[FS_REG].frame[0]; frame2 = e->framestate.g[FS_REG].frame[1]; lerp = e->framestate.g[FS_REG].lerpfrac; @@ -1919,7 +1924,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in mesh->xyz2_array = p2->ofsverts; } #endif - if (vbop && meshcache.vbo.indicies.dummy) + if (vbop && meshcache.vbo.indicies.sysptr) *vbop = meshcache.vbop = &meshcache.vbo; } } @@ -2287,7 +2292,7 @@ static void Mod_BuildTriangleNeighbours ( int *neighbours, index_t *indexes, int n[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i); } } -void Mod_CompileTriangleNeighbours(galiasinfo_t *galias) +void Mod_CompileTriangleNeighbours(model_t *loadmodel, galiasinfo_t *galias) { #ifndef SERVERONLY if (Sh_StencilShadowsActive()) @@ -2312,30 +2317,44 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) { int count = 0; int maxcount = 0; - const char *line; + char *line, *eol; char *file; frameinfo_t *frames = NULL; - line = file = FS_LoadMallocFile(va("%s.framegroups", modelname)); + char fname[MAX_QPATH]; + char tok[64]; + size_t fsize; + Q_snprintfz(fname, sizeof(fname), "%s.framegroups", modelname); + line = file = COM_LoadFile(fname, 5, &fsize); if (!file) return NULL; while(line && *line) { - line = Cmd_TokenizeString(line, false, false); - if (Cmd_Argc()) + eol = strchr(line, '\n'); + if (eol) + *eol = 0; + + if (count == maxcount) { - if (count == maxcount) - { - maxcount += 32; - frames = realloc(frames, sizeof(*frames)*maxcount); - } - - frames[count].firstpose = atoi(Cmd_Argv(0)); - frames[count].posecount = atoi(Cmd_Argv(1)); - frames[count].fps = atof(Cmd_Argv(2)); - frames[count].loop = !!atoi(Cmd_Argv(3)); - Q_strncpyz(frames[count].name, Cmd_Argv(4), sizeof(frames[count].name)); - count++; + maxcount += 32; + frames = realloc(frames, sizeof(*frames)*maxcount); } + + line = COM_ParseOut(line, tok, sizeof(tok)); + frames[count].firstpose = atoi(tok); + line = COM_ParseOut(line, tok, sizeof(tok)); + frames[count].posecount = atoi(tok); + line = COM_ParseOut(line, tok, sizeof(tok)); + frames[count].fps = atof(tok); + line = COM_ParseOut(line, tok, sizeof(tok)); + frames[count].loop = !!atoi(tok); + line = COM_ParseOut(line, frames[count].name, sizeof(frames[count].name)); + if (frames[count].posecount>0 && frames[count].fps) + count++; + + if (eol) + line = eol+1; + else + break; } BZ_Free(file); @@ -2343,6 +2362,103 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) return frames; } +void Mod_DestroyMesh(galiasinfo_t *galias) +{ +#ifndef SERVERONLY + if (!BE_VBO_Destroy) + return; + while(galias) + { + BE_VBO_Destroy(&galias->vbotexcoords); + BE_VBO_Destroy(&galias->vboindicies); + galias = galias->nextsurf; + } +#endif +} + +void Mod_GenerateMeshVBO(galiasinfo_t *galias) +//vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts) +{ +#ifndef SERVERONLY + int i, p; + galiasgroup_t *group; + galiaspose_t *pose; + int vbospace = 0; + vbobctx_t vboctx; + + //don't fail on dedicated servers + if (!BE_VBO_Begin) + return; + + group = galias->groupofs; + + //determine the amount of space we need for our vbos. + if (galias->ofs_st_array) + vbospace += sizeof(*galias->ofs_st_array) * galias->numverts; + if (galias->ofs_rgbaf) + vbospace += sizeof(*galias->ofs_rgbaf) * galias->numverts; + else if (galias->ofs_rgbaub) + vbospace += sizeof(*galias->ofs_rgbaub) * galias->numverts; +#ifdef SKELETALMODELS + if (galias->ofs_skel_xyz) + vbospace += sizeof(*galias->ofs_skel_xyz) * galias->numverts; + if (galias->ofs_skel_norm) + vbospace += sizeof(*galias->ofs_skel_norm) * galias->numverts; + if (galias->ofs_skel_svect) + vbospace += sizeof(*galias->ofs_skel_svect) * galias->numverts; + if (galias->ofs_skel_tvect) + vbospace += sizeof(*galias->ofs_skel_tvect) * galias->numverts; + if (galias->ofs_skel_idx) + vbospace += sizeof(*galias->ofs_skel_idx) * galias->numverts; + if (galias->ofs_skel_weight) + vbospace += sizeof(*galias->ofs_skel_weight) * galias->numverts; +#endif + for (i = 0; i < galias->groups; i++) + { + if (group->poseofs) + vbospace += group[i].numposes * galias->numverts * (sizeof(vecV_t)+sizeof(vec3_t)*3); + } + BE_VBO_Begin(&vboctx, vbospace); + if (galias->ofs_st_array) + BE_VBO_Data(&vboctx, galias->ofs_st_array, sizeof(*galias->ofs_st_array) * galias->numverts, &galias->vbotexcoords); + if (galias->ofs_rgbaf) + BE_VBO_Data(&vboctx, galias->ofs_rgbaf, sizeof(*galias->ofs_rgbaf) * galias->numverts, &galias->vborgba); + else if (galias->ofs_rgbaub) + BE_VBO_Data(&vboctx, galias->ofs_rgbaub, sizeof(*galias->ofs_rgbaub) * galias->numverts, &galias->vborgba); +#ifdef SKELETALMODELS + if (galias->ofs_skel_xyz) + BE_VBO_Data(&vboctx, galias->ofs_skel_xyz, sizeof(*galias->ofs_skel_xyz) * galias->numverts, &galias->vbo_skel_verts); + if (galias->ofs_skel_norm) + BE_VBO_Data(&vboctx, galias->ofs_skel_norm, sizeof(*galias->ofs_skel_norm) * galias->numverts, &galias->vbo_skel_normals); + if (galias->ofs_skel_svect) + BE_VBO_Data(&vboctx, galias->ofs_skel_svect, sizeof(*galias->ofs_skel_svect) * galias->numverts, &galias->vbo_skel_svector); + if (galias->ofs_skel_tvect) + BE_VBO_Data(&vboctx, galias->ofs_skel_tvect, sizeof(*galias->ofs_skel_tvect) * galias->numverts, &galias->vbo_skel_tvector); + if (galias->ofs_skel_idx) + BE_VBO_Data(&vboctx, galias->ofs_skel_idx, sizeof(*galias->ofs_skel_idx) * galias->numverts, &galias->vbo_skel_bonenum); + if (galias->ofs_skel_weight) + BE_VBO_Data(&vboctx, galias->ofs_skel_weight, sizeof(*galias->ofs_skel_weight) * galias->numverts, &galias->vbo_skel_bweight); +#endif + + for (i = 0; i < galias->groups; i++, group++) + { + pose = group->poseofs; + if (pose) + for (p = 0; p < group->numposes; p++, pose++) + { + BE_VBO_Data(&vboctx, pose->ofsverts, sizeof(*pose->ofsverts) * galias->numverts, &pose->vboverts); + BE_VBO_Data(&vboctx, pose->ofsnormals, sizeof(*pose->ofsnormals) * galias->numverts, &pose->vbonormals); + if (pose->ofssvector) + BE_VBO_Data(&vboctx, pose->ofssvector, sizeof(*pose->ofssvector) * galias->numverts, &pose->vbosvector); + if (pose->ofstvector) + BE_VBO_Data(&vboctx, pose->ofstvector, sizeof(*pose->ofstvector) * galias->numverts, &pose->vbotvector); + } + } + BE_VBO_Finish(&vboctx, galias->ofs_indexes, sizeof(*galias->ofs_indexes) * galias->numindexes, &galias->vboindicies); +#endif +} + + //called for non-skeletal model formats. void Mod_BuildTextureVectors(galiasinfo_t *galias) //vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts) @@ -2356,7 +2472,6 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) vec2_t *tc; index_t *idx; int vbospace = 0; - vbobctx_t vboctx; //don't fail on dedicated servers if (!BE_VBO_Begin) @@ -2366,15 +2481,6 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) tc = galias->ofs_st_array; group = galias->groupofs; - //determine the amount of space we need for our vbos. - vbospace += sizeof(*tc) * galias->numverts; - for (i = 0; i < galias->groups; i++) - { - vbospace += group[i].numposes * galias->numverts * (sizeof(vecV_t)+sizeof(vec3_t)*3); - } - BE_VBO_Begin(&vboctx, vbospace); - BE_VBO_Data(&vboctx, tc, sizeof(*tc) * galias->numverts, &galias->vbotexcoords); - for (i = 0; i < galias->groups; i++, group++) { pose = group->poseofs; @@ -2390,19 +2496,8 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) Mod_AccumulateTextureVectors(vc, tc, nv, sv, tv, idx, galias->numindexes); Mod_NormaliseTextureVectors(nv, sv, tv, galias->numverts); } - else - { //shouldn't really happen... make error? - sv = NULL; - tv = NULL; - } - - BE_VBO_Data(&vboctx, vc, sizeof(*vc) * galias->numverts, &pose->vboverts); - BE_VBO_Data(&vboctx, nv, sizeof(*nv) * galias->numverts, &pose->vbonormals); - BE_VBO_Data(&vboctx, sv, sizeof(*sv) * galias->numverts, &pose->vbosvector); - BE_VBO_Data(&vboctx, tv, sizeof(*tv) * galias->numverts, &pose->vbotvector); } } - BE_VBO_Finish(&vboctx, idx, sizeof(*idx) * galias->numindexes, &galias->vboindicies); #endif } @@ -2481,154 +2576,101 @@ static void Mod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight ) } } -skinid_t *skinfilelist; -int skinfilecount; - -static qboolean VARGS Mod_TryAddSkin(qboolean force, const char *skinname, ...) -{ - va_list argptr; - char string[MAX_QPATH]; - char *filedata; - - //make sure we don't add it twice - int i; - - - va_start (argptr, skinname); - vsnprintf (string,sizeof(string)-1, skinname,argptr); - va_end (argptr); - string[MAX_QPATH-1] = '\0'; - - for (i = 0; i < skinfilecount; i++) - { - if (!strcmp(Mod_LookupSkin(skinfilelist[i])->skinname, string)) - return true; //already added - } - - filedata = FS_LoadMallocFile(string); - if (!filedata && !force) - return false; - - skinfilelist = BZ_Realloc(skinfilelist, sizeof(*skinfilelist)*(skinfilecount+1)); - skinfilelist[skinfilecount++] = Mod_ReadSkinFile(string, filedata); - Z_Free(filedata); - return true; -} - -int QDECL Mod_EnumerateSkins(const char *name, qofs_t size, void *param, searchpathfuncs_t *spath) -{ - Mod_TryAddSkin(false, name); - return true; -} - //looks for foo.md3_0.skin files, for dp compat -int Mod_BuildSkinFileList(qboolean forcedefault, char *modelname) +//also try foo_0.skin, because people appear to use that too. *sigh*. +int Mod_CountSkinFiles(char *modelname) { int i; char skinfilename[MAX_QPATH]; - - //flush the old list - for (i = 0; i < skinfilecount; i++) - { - Mod_WipeSkin(skinfilelist[i]); - skinfilelist[i] = ~0u; - } - skinfilecount=0; - - COM_StripExtension(modelname, skinfilename, sizeof(skinfilename)); - //try and add numbered skins, and then try fixed names. for (i = 0; ; i++) { - if (!Mod_TryAddSkin(false, "%s_%i.skin", modelname, i)) + Q_snprintfz(skinfilename, sizeof(skinfilename), "%s_%i.skin", modelname, i); + if (!COM_FCheckExists(skinfilename)) { - /*FIXME: only use this logic in q1, and not q3. - if (i == 0) - { - if (!Mod_TryAddSkin(forcedefault, "%s_default.skin", skinfilename, i)) - break; - } - else if (i == 1) - { - if (!Mod_TryAddSkin(false, "%s_blue.skin", skinfilename, i)) - break; - } - else if (i == 2) - { - if (!Mod_TryAddSkin(false, "%s_red.skin", skinfilename, i)) - break; - } - else if (i == 3) - { - if (!Mod_TryAddSkin(false, "%s_green.skin", skinfilename, i)) - break; - } - else if (i == 4) - { - if (!Mod_TryAddSkin(false, "%s_yellow.skin", skinfilename, i)) - break; - } - else*/ + COM_StripExtension(modelname, skinfilename, sizeof(skinfilename)); + Q_snprintfz(skinfilename+strlen(skinfilename), sizeof(skinfilename)-strlen(skinfilename), "_%i.skin", i); + if (!COM_FCheckExists(skinfilename)) break; } } - - COM_EnumerateFiles(va("%s_*.skin", modelname), Mod_EnumerateSkins, NULL); -// COM_EnumerateFiles(va("%s_*.skin", skinfilename), Mod_EnumerateSkins, NULL); - - return skinfilecount; + return i; } - //support for foo.md3_0.skin -shader_t *Mod_ShaderFromQ3SkinFile(char *out, galiasinfo_t *surf, char *modelname, int skinnum, char *skinfilename) +shader_t *Mod_ShaderFromQ3SkinFile(galiasinfo_t *surf, char *modelname, int skinnum) { + shader_t *result = NULL; + skinid_t skinid; skinfile_t *skinfile; int i; + char *filedata; + char skinfilename[MAX_QPATH]; if (qrenderer == QR_NONE) return NULL; - if (skinnum < skinfilecount) + Q_snprintfz(skinfilename, sizeof(skinfilename), "%s_%i.skin", modelname, skinnum); + filedata = FS_LoadMallocFile(skinfilename, NULL); + if (!filedata) { - skinfile = Mod_LookupSkin(skinfilelist[skinnum]); + COM_StripExtension(modelname, skinfilename, sizeof(skinfilename)); + Q_snprintfz(skinfilename+strlen(skinfilename), sizeof(skinfilename)-strlen(skinfilename), "_%i.skin", skinnum); + filedata = FS_LoadMallocFile(skinfilename, NULL); + } + if (filedata) + { + skinid = Mod_ReadSkinFile(skinfilename, filedata); + Z_Free(filedata); - if (skinfilename) - strcpy(skinfilename, skinfile->skinname); - - //check if this skinfile has a mapping. - for (i = 0; i < skinfile->nummappings; i++) + skinfile = Mod_LookupSkin(skinid); + if (skinfile) { - if (!strcmp(surf->surfacename, skinfile->mappings[i].surface)) + //check if this skinfile has a mapping. + for (i = 0; i < skinfile->nummappings; i++) { - skinfile->mappings[i].shader->uses++; //so it doesn't blow up when the skin gets freed. - return skinfile->mappings[i].shader; + if (!strcmp(surf->surfacename, skinfile->mappings[i].surface)) + { + skinfile->mappings[i].shader->uses++; //so it doesn't blow up when the skin gets freed. + result = skinfile->mappings[i].shader; + break; + } } + Mod_WipeSkin(skinid); } } - return NULL; + return result; } -shader_t *Mod_LoadSkinFile(char *defaultshadername, galiasinfo_t *surf, int skinnumber, unsigned char *rawdata, int width, int height, unsigned char *palette, char *outskinname) +void Mod_LoadAliasShaders(model_t *mod) { - shader_t *shader; - char shadername[MAX_QPATH]; - Q_strncpyz(shadername, defaultshadername?defaultshadername:surf->surfacename, sizeof(shadername)); - - shader = Mod_ShaderFromQ3SkinFile(shadername, surf, loadmodel->name, skinnumber, outskinname); - if (!shader) - shader = R_RegisterSkin(defaultshadername, loadmodel->name); - if (!shader) - shader = R_RegisterSkin(surf->surfacename, loadmodel->name); - if (shader) + galiasinfo_t *ai = mod->meshinfo; + galiasskin_t *s; + skinframe_t *f; + int i, j; + for (ai = mod->meshinfo; ai; ai = ai->nextsurf) { - R_BuildDefaultTexnums(&shader->defaulttextures, shader); - if (shader->flags & SHADER_NOIMAGE) - Con_Printf("Unable to load texture for shader \"%s\" for model \"%s\"\n", shader->name, loadmodel->name); + Mod_GenerateMeshVBO(ai); //FIXME: shares verts + for (i = 0, s = ai->ofsskins; i < ai->numskins; i++, s++) + { + for (j = 0, f = s->frame; j < s->numframes; j++, f++) + { + if (j == 0) + f->shader = Mod_ShaderFromQ3SkinFile(ai, mod->name, i); + else + f->shader = NULL; + if (!f->shader) + { + if (!f->defaultshader) + f->shader = R_RegisterSkin(f->shadername, mod->name); + else + f->shader = R_RegisterShader(f->shadername, SUF_NONE, f->defaultshader); + } + R_BuildDefaultTexnums(&f->texnums, f->shader); + } + } } - - return shader; } #endif @@ -2688,7 +2730,7 @@ static void Alias_LoadPose(vecV_t *verts, vec3_t *normals, vec3_t *svec, vec3_t } } } -static void *Alias_LoadFrameGroup (daliasframetype_t *pframetype, int *seamremaps, int mdltype) +static void *Alias_LoadFrameGroup (model_t *loadmodel, daliasframetype_t *pframetype, int *seamremaps, int mdltype) { galiaspose_t *pose; galiasgroup_t *frame = galias->groupofs; @@ -2856,11 +2898,11 @@ static void *Q1_LoadSkins_SV (daliasskintype_t *pskintype, unsigned int skintran } #ifndef SERVERONLY -static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintranstype) +static void *Q1_LoadSkins_GL (model_t *loadmodel, daliasskintype_t *pskintype, uploadfmt_t skintranstype) { - shader_t **shaders; - qbyte **ofstexels; + skinframe_t *frames; char skinname[MAX_QPATH]; + char alttexpath[MAX_QPATH]; int i; int s, t; float sinter; @@ -2868,10 +2910,26 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran daliasskininterval_t *intervals; qbyte *data, *saved; galiasskin_t *outskin = galias->ofsskins; + const char *slash; + unsigned int texflags; - texid_t texture; - texid_t fbtexture; - texid_t bumptexture; + char basename[32]; + COM_FileBase(loadmodel->name, basename, sizeof(basename)); + + if (loadmodel->engineflags & MDLF_NOTREPLACEMENTS) + texflags = IF_NOREPLACE; + else + texflags = 0; + + slash = COM_SkipPath(loadmodel->name); + if (slash != loadmodel->name && slash-loadmodel->name < sizeof(alttexpath)) + { + slash--; + memcpy(alttexpath, loadmodel->name, slash-loadmodel->name); + Q_strncpyz(alttexpath+(slash-loadmodel->name), ":models", sizeof(alttexpath)-(slash-loadmodel->name)); //fuhquake compat + } + else + strcpy(alttexpath, "models"); //fuhquake compat s = pq1inmodel->skinwidth*pq1inmodel->skinheight; for (i = 0; i < pq1inmodel->numskins; i++) @@ -2883,7 +2941,7 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran outskin->skinheight = pq1inmodel->skinheight; //LH's naming scheme ("models" is likly to be ignored) - fbtexture = r_nulltex; +/* fbtexture = r_nulltex; bumptexture = r_nulltex; snprintf(skinname, sizeof(skinname), "%s_%i.", loadmodel->name, i); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); @@ -2903,102 +2961,102 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran else { //try with a stripped model name - snprintf(skinname, sizeof(skinname), "%s_%i.", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i.", basename, i); texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); if (TEXVALID(texture)) { if (r_fb_models.ival) { - snprintf(skinname, sizeof(skinname), "%s_%i_luma.", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_luma.", basename, i); fbtexture = R_LoadReplacementTexture(skinname, "models", 0); } if (r_loadbumpmapping) { - snprintf(skinname, sizeof(skinname), "%s_%i_bump.", loadname, i); + snprintf(skinname, sizeof(skinname), "%s_%i_bump.", basename, i); bumptexture = R_LoadBumpmapTexture(skinname, "models"); } } //else ... } - +*/ //but only preload it if we have no replacement. - if (!TEXVALID(texture) || (loadmodel->engineflags & MDLF_NOTREPLACEMENTS)) + if (1 || /*!TEXVALID(texture) ||*/ (loadmodel->engineflags & MDLF_NOTREPLACEMENTS)) { //we're not using 24bits - shaders = ZG_Malloc(&loadmodel->memgroup, sizeof(*shaders)+sizeof(*ofstexels)+s); - ofstexels = (qbyte**)(shaders+1); - saved = (qbyte*)(ofstexels+1); - outskin->ofstexels = ofstexels; - ofstexels[0] = saved; + frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)+s); + saved = (qbyte*)(frames+1); + frames[0].texels = saved; memcpy(saved, pskintype+1, s); Mod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); +#if 0 //the extra underscore is to stop replacement matches if (!TEXVALID(texture)) { - snprintf(skinname, sizeof(skinname), "%s__%i.", loadname, i); + snprintf(skinname, sizeof(skinname), "%s__%i.", basename, i); switch (skintranstype) { default: - texture = R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_SOLID8, saved, IF_NOALPHA|IF_NOGAMMA); + texture = r_nulltex;//R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_SOLID8, saved, IF_NOALPHA|IF_NOGAMMA); if (r_fb_models.ival) { - snprintf(skinname, sizeof(skinname), "%s__%i_luma.", loadname, i); - fbtexture = R_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); + snprintf(skinname, sizeof(skinname), "%s__%i_luma.", basename, i); + fbtexture = r_nulltex;//R_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); } if (r_loadbumpmapping) { - snprintf(skinname, sizeof(skinname), "%s__%i_bump.", loadname, i); - bumptexture = R_LoadTexture8BumpPal(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); + snprintf(skinname, sizeof(skinname), "%s__%i_bump.", basename, i); + bumptexture = r_nulltex;//R_LoadTexture8BumpPal(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); } break; case 2: - texture = R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_T7G1, saved, IF_NOGAMMA); + texture = r_nulltex;//R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_T7G1, saved, IF_NOGAMMA); break; case 3: - texture = R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_TRANS8_0, saved, IF_NOGAMMA); + texture = r_nulltex;//R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_TRANS8_0, saved, IF_NOGAMMA); break; case 4: - texture = R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_T4A4, saved, IF_NOGAMMA); + texture = r_nulltex;//R_LoadTexture(skinname,outskin->skinwidth,outskin->skinheight, TF_H2_T4A4, saved, IF_NOGAMMA); break; } } +#endif } else - shaders = ZG_Malloc(&loadmodel->memgroup, sizeof(*shaders)); - outskin->numshaders=1; + frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)); + outskin->numframes=1; - outskin->ofsshaders = shaders; + outskin->frame = frames; + frames[0].shader = NULL; + frames[0].defaultshader = NULL; + Q_snprintfz(frames[0].shadername, sizeof(frames[0].shadername), "%s_%i.lmp", basename, i); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i.lmp", loadmodel->name, i); + frames[0].texnums.base = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, skintranstype); + if (r_fb_models.ival) + { + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_luma.lmp", loadmodel->name, i); + frames[0].texnums.fullbright = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_TRANS8_FULLBRIGHT); + } + if (r_loadbumpmapping) + { + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_norm.lmp", loadmodel->name, i); + frames[0].texnums.bump = R_LoadReplacementTexture(skinname, alttexpath, texflags|IF_TRYBUMP, frames[0].texels, outskin->skinwidth, outskin->skinheight, TF_HEIGHT8PAL); + } + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_shirt.lmp", loadmodel->name, i); + frames[0].texnums.upperoverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_pants.lmp", loadmodel->name, i); + frames[0].texnums.loweroverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - Q_snprintfz(skinname, sizeof(skinname), "%s_%i.", loadname, i); - if (skintranstype == 4) - shaders[0] = R_RegisterShader(skinname, SUF_NONE, - "{\n" - "{\n" - "map $diffuse\n" - "blendfunc gl_one_minus_src_alpha gl_src_alpha\n" - "alphagen entity\n" - "rgbgen lightingDiffuse\n" - "cull disable\n" - "depthwrite\n" - "}\n" - "}\n"); - else if (skintranstype == 3) - shaders[0] = R_RegisterShader(skinname, SUF_NONE, - "{\n" - "{\n" - "map $diffuse\n" - "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" - "alphafunc ge128\n" - "rgbgen lightingDiffuse\n" - "alphagen entity\n" - "depthwrite\n" - "}\n" - "}\n"); - else if (skintranstype) - shaders[0] = R_RegisterShader(skinname, SUF_NONE, + switch(skintranstype) + { + default: //urk + case TF_SOLID8: + frames[0].defaultshader = NULL; + break; + case TF_H2_T7G1: + frames[0].defaultshader = "{\n" // "program defaultskin\n" "{\n" @@ -3008,15 +3066,37 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran "rgbgen lightingDiffuse\n" "depthwrite\n" "}\n" - "}\n"); - else - shaders[0] = R_RegisterSkin(skinname, loadmodel->name); - - shaders[0]->defaulttextures.base = texture; - shaders[0]->defaulttextures.fullbright = fbtexture; - shaders[0]->defaulttextures.bump = bumptexture; - - //13/4/08 IMPLEMENTME + "}\n"; + break; + case TF_H2_TRANS8_0: + frames[0].defaultshader = + "{\n" + "{\n" + "map $diffuse\n" + "blendfunc gl_src_alpha gl_one_minus_src_alpha\n" + "alphafunc ge128\n" + "rgbgen lightingDiffuse\n" + "alphagen entity\n" + "depthwrite\n" + "}\n" + "}\n"; + break; + case TF_H2_T4A4: + frames[0].defaultshader = + "{\n" + "{\n" + "map $diffuse\n" + "blendfunc gl_one_minus_src_alpha gl_src_alpha\n" + "alphagen entity\n" + "rgbgen lightingDiffuse\n" + "cull disable\n" + "depthwrite\n" + "}\n" + "}\n"; + break; + } + +/* //13/4/08 IMPLEMENTME if (r_skin_overlays.ival) { snprintf(skinname, sizeof(skinname), "%s_%i_pants.", loadname, i); @@ -3025,9 +3105,7 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran snprintf(skinname, sizeof(skinname), "%s_%i_shirt.", loadname, i); shaders[0]->defaulttextures.upperoverlay = R_LoadReplacementTexture(skinname, "models", 0); } - - R_BuildDefaultTexnums(&shaders[0]->defaulttextures, shaders[0]); - +*/ pskintype = (daliasskintype_t *)((char *)(pskintype+1)+s); break; @@ -3036,75 +3114,40 @@ static void *Q1_LoadSkins_GL (daliasskintype_t *pskintype, unsigned int skintran outskin->skinheight = pq1inmodel->skinheight; count = (daliasskingroup_t*)(pskintype+1); intervals = (daliasskininterval_t *)(count+1); - outskin->numshaders = LittleLong(count->numskins); - data = (qbyte *)(intervals + outskin->numshaders); - shaders = ZG_Malloc(&loadmodel->memgroup, sizeof(*shaders)*outskin->numshaders + sizeof(*ofstexels)*outskin->numshaders); - ofstexels = (qbyte**)(shaders+outskin->numshaders); - outskin->ofsshaders = shaders; - outskin->ofstexels = ofstexels; + outskin->numframes = LittleLong(count->numskins); + data = (qbyte *)(intervals + outskin->numframes); + frames = ZG_Malloc(&loadmodel->memgroup, sizeof(*frames)*outskin->numframes); + outskin->frame = frames; sinter = LittleFloat(intervals[0].interval); if (sinter <= 0) sinter = 0.1; outskin->skinspeed = 1/sinter; - for (t = 0; t < outskin->numshaders; t++,data+=s) + for (t = 0; t < outskin->numframes; t++,data+=s) { - texture = r_nulltex; - fbtexture = r_nulltex; + frames[t].texels = ZG_Malloc(&loadmodel->memgroup, s); + memcpy(frames[t].texels, data, s); + Mod_FloodFillSkin(frames[t].texels, outskin->skinwidth, outskin->skinheight); - //LH naming scheme - if (!TEXVALID(texture)) + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.lmp", loadmodel->name, i, t); + frames[t].texnums.base = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[t].texels, outskin->skinwidth, outskin->skinheight, skintranstype); + if (r_fb_models.ival) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadmodel->name, i, t); - texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.lmp", loadmodel->name, i, t); + frames[t].texnums.fullbright = R_LoadReplacementTexture(skinname, alttexpath, texflags, frames[t].texels, outskin->skinwidth, outskin->skinheight, TF_TRANS8_FULLBRIGHT); } - if (!TEXVALID(fbtexture) && r_fb_models.ival) + if (r_loadbumpmapping) { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadmodel->name, i, t); - fbtexture = R_LoadReplacementTexture(skinname, "models", 0); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_norm.lmp", loadmodel->name, i, t); + frames[t].texnums.bump = R_LoadReplacementTexture(skinname, alttexpath, texflags|IF_TRYBUMP, frames[t].texels, outskin->skinwidth, outskin->skinheight, TF_HEIGHT8PAL); } + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_shirt.lmp", loadmodel->name, i, t); + frames[t].texnums.upperoverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); + Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_pants.lmp", loadmodel->name, i, t); + frames[t].texnums.loweroverlay = R_LoadReplacementTexture(skinname, alttexpath, texflags, NULL, outskin->skinwidth, outskin->skinheight, TF_INVALID); - //Fuhquake naming scheme - if (!TEXVALID(texture)) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); - texture = R_LoadReplacementTexture(skinname, "models", IF_NOALPHA); - } - if (!TEXVALID(fbtexture) && r_fb_models.ival) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadname, i, t); - fbtexture = R_LoadReplacementTexture(skinname, "models", 0); - } - - if (!TEXVALID(texture) || (!TEXVALID(fbtexture) && r_fb_models.ival)) - { - saved = ZG_Malloc(&loadmodel->memgroup, s); - ofstexels[t] = saved; - memcpy(saved, data, s); - Mod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight); - if (!TEXVALID(texture)) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); - texture = R_LoadTexture8(skinname, outskin->skinwidth, outskin->skinheight, saved, (skintranstype?0:IF_NOALPHA)|IF_NOGAMMA, skintranstype); - } - - - if (!TEXVALID(fbtexture) && r_fb_models.value) - { - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i_luma.", loadname, i, t); - fbtexture = R_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, IF_NOGAMMA); - } - } - - Q_snprintfz(skinname, sizeof(skinname), "%s_%i_%i.", loadname, i, t); - shaders[t] = R_RegisterSkin(skinname, loadmodel->name); - - TEXASSIGN(shaders[t]->defaulttextures.base, texture); - TEXASSIGN(shaders[t]->defaulttextures.fullbright, fbtexture); - TEXASSIGN(shaders[t]->defaulttextures.loweroverlay, r_nulltex); - TEXASSIGN(shaders[t]->defaulttextures.upperoverlay, r_nulltex); - - R_BuildDefaultTexnums(&shaders[t]->defaulttextures, shaders[t]); + Q_snprintfz(frames[t].shadername, sizeof(frames[t].shadername), "%s_%i_%i.lmp", basename, i, t); + frames[t].defaultshader = NULL; } pskintype = (daliasskintype_t *)data; break; @@ -3129,7 +3172,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) int *seamremap; index_t *indexes; daliasskintype_t *skinstart; - int skintranstype; + uploadfmt_t skintranstype; int size; unsigned int hdrsize; @@ -3140,13 +3183,11 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) qboolean rapo = false; #endif - loadmodel=mod; - pq1inmodel = (dmdl_t *)buffer; hdrsize = sizeof(dmdl_t) - sizeof(int); - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; version = LittleLong(pq1inmodel->version); if (version == QTESTALIAS_VERSION) @@ -3197,32 +3238,32 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) #endif + pq1inmodel->numframes*sizeof(galiasgroup_t); - galias = ZG_Malloc(&loadmodel->memgroup, size); + galias = ZG_Malloc(&mod->memgroup, size); galias->groupofs = (galiasgroup_t*)(galias+1); #ifndef SERVERONLY galias->ofsskins = (galiasskin_t*)(galias->groupofs+pq1inmodel->numframes); #endif galias->nextsurf = 0; - loadmodel->numframes = pq1inmodel->numframes; + mod->numframes = pq1inmodel->numframes; //skins skinstart = (daliasskintype_t *)((char*)pq1inmodel+hdrsize); if( mod->flags & MFH2_TRANSPARENT ) - skintranstype = 2; //hexen2 + skintranstype = TF_H2_T7G1; //hexen2 else if( mod->flags & MFH2_HOLEY ) - skintranstype = 3; //hexen2 + skintranstype = TF_H2_TRANS8_0; //hexen2 else if( mod->flags & MFH2_SPECIAL_TRANS ) - skintranstype = 4; //hexen2 + skintranstype = TF_H2_T4A4; //hexen2 else - skintranstype = 0; + skintranstype = TF_SOLID8; switch(qrenderer) { default: #ifndef SERVERONLY - pinstverts = (dstvert_t *)Q1_LoadSkins_GL(skinstart, skintranstype); + pinstverts = (dstvert_t *)Q1_LoadSkins_GL(mod, skinstart, skintranstype); break; #endif case QR_NONE: @@ -3242,7 +3283,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) galias->numverts = pq1inmodel->numverts; galias->numindexes = pq1inmodel->numtris*3; - indexes = ZG_Malloc(&loadmodel->memgroup, galias->numindexes*sizeof(*indexes)); + indexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = indexes; for (i = 0; i < pq1inmodel->numverts; i++) seamremap[i] = i; @@ -3263,7 +3304,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) /*output the indicies as we figure out which verts we want*/ galias->numindexes = pq1inmodel->numtris*3; - indexes = ZG_Malloc(&loadmodel->memgroup, galias->numindexes*sizeof(*indexes)); + indexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = indexes; for (i = 0; i < pq1inmodel->numtris; i++) { @@ -3288,7 +3329,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) } } - st_array = ZG_Malloc(&loadmodel->memgroup, sizeof(*st_array)*(galias->numverts)); + st_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(galias->numverts)); galias->ofs_st_array = st_array; /*generate our st_array now we know which vertexes we want*/ for (k = 0; k < galias->numverts; k++) @@ -3307,10 +3348,10 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) #endif end = &pinh2triangles[pq1inmodel->numtris]; - if (Alias_LoadFrameGroup((daliasframetype_t *)end, seamremap, 2) == NULL) + if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, 2) == NULL) { BZ_Free(seamremap); - ZG_FreeGroup(&loadmodel->memgroup); + ZG_FreeGroup(&mod->memgroup); return false; } @@ -3336,7 +3377,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) //st #ifndef SERVERONLY - st_array = ZG_Malloc(&loadmodel->memgroup, sizeof(*st_array)*(pq1inmodel->numverts+onseams)); + st_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(pq1inmodel->numverts+onseams)); galias->ofs_st_array = st_array; for (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++) { @@ -3364,7 +3405,7 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) pinq1triangles = (dtriangle_t *)&pinstverts[pq1inmodel->numverts]; galias->numindexes = pq1inmodel->numtris*3; - indexes = ZG_Malloc(&loadmodel->memgroup, galias->numindexes*sizeof(*indexes)); + indexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = indexes; for (i=0 ; inumtris ; i++) { @@ -3384,17 +3425,17 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) end = &pinq1triangles[pq1inmodel->numtris]; //frames - if (Alias_LoadFrameGroup((daliasframetype_t *)end, seamremap, qtest ? 1 : 0) == NULL) + if (Alias_LoadFrameGroup(mod, (daliasframetype_t *)end, seamremap, qtest ? 1 : 0) == NULL) { BZ_Free(seamremap); - ZG_FreeGroup(&loadmodel->memgroup); + ZG_FreeGroup(&mod->memgroup); return false; } BZ_Free(seamremap); } - Mod_CompileTriangleNeighbours(galias); + Mod_CompileTriangleNeighbours(mod, galias); Mod_BuildTextureVectors(galias); VectorCopy (pq1inmodel->scale_origin, mod->mins); @@ -3421,6 +3462,10 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) galias->lerpcutoff = 30; else if (!strcmp(mod->name, "progs/v_light.mdl")) galias->lerpcutoff = 30; +#ifdef HEXEN2 + if ((mod->flags == MF_ROCKET) && !strncmp(mod->name, "models/sflesh", 13)) + mod->flags = MFH2_ROCKET; +#endif return true; } @@ -3429,6 +3474,8 @@ qboolean QDECL Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize) int Mod_ReadFlagsFromMD1(char *name, int md3version) { + int result = 0; + size_t fsize; dmdl_t *pinmodel; char fname[MAX_QPATH]; COM_StripExtension(name, fname, sizeof(fname)); @@ -3441,16 +3488,15 @@ int Mod_ReadFlagsFromMD1(char *name, int md3version) return 0; } - pinmodel = (dmdl_t *)COM_LoadTempFile(fname); - - if (!pinmodel) //not found - return 0; - - if (LittleLong(pinmodel->ident) != IDPOLYHEADER) - return 0; - if (LittleLong(pinmodel->version) != ALIAS_VERSION) - return 0; - return LittleLong(pinmodel->flags); + pinmodel = (dmdl_t *)COM_LoadFile(fname, 5, &fsize); + if (pinmodel) + { + if (fsize >= sizeof(dmdl_t) && LittleLong(pinmodel->ident) == IDPOLYHEADER) + if (LittleLong(pinmodel->version) == ALIAS_VERSION) + result = LittleLong(pinmodel->flags); + BZ_Free(pinmodel); + } + return result; } #ifdef MD2MODELS @@ -3471,23 +3517,21 @@ typedef struct #define Q2NUMVERTEXNORMALS 162 extern vec3_t bytedirs[Q2NUMVERTEXNORMALS]; -static void Q2_LoadSkins(md2_t *pq2inmodel, char *skins) +static void Q2_LoadSkins(model_t *mod, md2_t *pq2inmodel, char *skins) { #ifndef SERVERONLY int i; - shader_t **shaders; + skinframe_t *frames; galiasskin_t *outskin = galias->ofsskins; for (i = 0; i < LittleLong(pq2inmodel->num_skins); i++, outskin++) { - shaders = ZG_Malloc(&loadmodel->memgroup, sizeof(*shaders)); - outskin->ofsshaders = shaders; - outskin->numshaders=1; + frames = ZG_Malloc(&mod->memgroup, sizeof(*frames)); + outskin->frame = frames; + outskin->numframes=1; COM_CleanUpPath(skins); //blooming tanks. - shaders[0] = R_RegisterSkin(skins, loadmodel->name); - TEXASSIGN(shaders[0]->defaulttextures.base, R_LoadReplacementTexture(skins, "models", IF_NOALPHA)); - R_BuildDefaultTexnums(NULL, shaders[0]); + Q_strncpyz(frames->shadername, skins, sizeof(frames->shadername)); outskin->skinwidth = 0; outskin->skinheight = 0; @@ -3545,10 +3589,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) int size; - - loadmodel=mod; - - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; pq2inmodel = (md2_t *)buffer; @@ -3574,7 +3615,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) mod->flags = 0; - loadmodel->numframes = LittleLong(pq2inmodel->num_frames); + mod->numframes = LittleLong(pq2inmodel->num_frames); size = sizeof(galiasinfo_t) #ifndef SERVERONLY @@ -3582,7 +3623,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) #endif + LittleLong(pq2inmodel->num_frames)*sizeof(galiasgroup_t); - galias = ZG_Malloc(&loadmodel->memgroup, size); + galias = ZG_Malloc(&mod->memgroup, size); galias->groupofs = (galiasgroup_t*)(galias+1); #ifndef SERVERONLY galias->ofsskins = (galiasskin_t*)(galias->groupofs + LittleLong(pq2inmodel->num_frames)); @@ -3590,7 +3631,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) galias->nextsurf = 0; //skins - Q2_LoadSkins(pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins))); + Q2_LoadSkins(mod, pq2inmodel, ((char *)pq2inmodel+LittleLong(pq2inmodel->ofs_skins))); //trianglelists; pintri = (dmd2triangle_t *)((char *)pq2inmodel + LittleLong(pq2inmodel->ofs_tris)); @@ -3606,7 +3647,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) } numindexes = galias->numindexes = LittleLong(pq2inmodel->num_tris)*3; - indexes = ZG_Malloc(&loadmodel->memgroup, galias->numindexes*sizeof(*indexes)); + indexes = ZG_Malloc(&mod->memgroup, galias->numindexes*sizeof(*indexes)); galias->ofs_indexes = indexes; memset ( indremap, -1, sizeof(indremap) ); numverts=0; @@ -3655,7 +3696,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) // s and t vertices #ifndef SERVERONLY pinstverts = ( dmd2stvert_t * ) ( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_st) ); - st_array = ZG_Malloc(&loadmodel->memgroup, sizeof(*st_array)*(numverts)); + st_array = ZG_Malloc(&mod->memgroup, sizeof(*st_array)*(numverts)); galias->ofs_st_array = st_array; for (j=0 ; jmemgroup, size); + pose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size); poutframe->poseofs = pose; poutframe->numposes = 1; galias->groups++; @@ -3730,7 +3771,7 @@ qboolean QDECL Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize) - Mod_CompileTriangleNeighbours(galias); + Mod_CompileTriangleNeighbours(mod, galias); Mod_BuildTextureVectors(galias); /* VectorCopy (pq2inmodel->scale_origin, mod->mins); @@ -4159,6 +4200,22 @@ qboolean Mod_FrameInfoForNum(model_t *model, int num, char **name, int *numframe return true; } +#ifndef SERVERONLY +shader_t *Mod_ShaderForSkin(model_t *model, int num) +{ + galiasinfo_t *inf; + galiasskin_t *skin; + + if (!model || model->type != mod_alias) + return NULL; + inf = Mod_Extradata(model); + + if (num >= inf->numskins) + return NULL; + skin = inf->ofsskins; + return skin[num].frame[0].shader; +} +#endif const char *Mod_SkinNameForNum(model_t *model, int num) { #ifdef SERVERONLY @@ -4174,7 +4231,10 @@ const char *Mod_SkinNameForNum(model_t *model, int num) if (num >= inf->numskins) return NULL; skin = inf->ofsskins; - return skin[num].name; + if (!*skin[num].name) + return skin[num].frame[0].shadername; + else + return skin[num].name; #endif } @@ -4275,7 +4335,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; - shader_t **shaders; + skinframe_t *frames; float lat, lng; md3St_t *inst; vec3_t *normals; @@ -4308,9 +4368,6 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) md3Header_t *header; md3Surface_t *surf; - - loadmodel=mod; - header = buffer; // if (header->version != sdfs) @@ -4320,7 +4377,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) root = NULL; #ifndef SERVERONLY - externalskins = Mod_BuildSkinFileList(false, mod->name); + externalskins = Mod_CountSkinFiles(mod->name); #else externalskins = 0; #endif @@ -4334,7 +4391,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) if (LittleLong(surf->ident) != MD3_IDENT) Con_Printf(CON_WARNING "Warning: md3 sub-surface doesn't match ident\n"); size = sizeof(galiasinfo_t) + sizeof(galiasgroup_t)*LittleLong(header->numFrames); - galias = ZG_Malloc(&loadmodel->memgroup, size); + galias = ZG_Malloc(&mod->memgroup, size); galias->groupofs = (galiasgroup_t*)(galias+1); //frame groups galias->groups = LittleLong(header->numFrames); galias->numverts = LittleLong(surf->numVerts); @@ -4349,7 +4406,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) Q_strncpyz(galias->surfacename, surf->name, sizeof(galias->surfacename)); #ifndef SERVERONLY - st_array = ZG_Malloc(&loadmodel->memgroup, sizeof(vec2_t)*galias->numindexes); + st_array = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*galias->numindexes); galias->ofs_st_array = st_array; inst = (md3St_t*)((qbyte*)surf + LittleLong(surf->ofsSt)); for (i = 0; i < galias->numverts; i++) @@ -4359,7 +4416,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) } #endif - indexes = ZG_Malloc(&loadmodel->memgroup, sizeof(*indexes)*galias->numindexes); + indexes = ZG_Malloc(&mod->memgroup, sizeof(*indexes)*galias->numindexes); galias->ofs_indexes = indexes; intris = (md3Triangle_t *)((qbyte*)surf + LittleLong(surf->ofsTriangles)); for (i = 0; i < LittleLong(surf->numTriangles); i++) @@ -4377,7 +4434,7 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) #ifndef SERVERONLY size += 3*sizeof(vec3_t)*LittleLong(surf->numVerts); #endif - pose = (galiaspose_t *)ZG_Malloc(&loadmodel->memgroup, size); + pose = (galiaspose_t *)ZG_Malloc(&mod->memgroup, size); verts = (vecV_t*)(pose+1); pose->ofsverts = verts; @@ -4432,40 +4489,22 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) externalskins = LittleLong(surf->numShaders); if (externalskins) { - char shadname[1024]; - - skin = ZG_Malloc(&loadmodel->memgroup, (externalskins)*((sizeof(galiasskin_t)+sizeof(shader_t*)))); + skin = ZG_Malloc(&mod->memgroup, (externalskins)*((sizeof(galiasskin_t)+sizeof(skinframe_t)))); galias->ofsskins = skin; - shaders = (shader_t **)(skin + externalskins); + frames = (skinframe_t *)(skin + externalskins); inshader = (md3Shader_t *)((qbyte *)surf + LittleLong(surf->ofsShaders)); for (i = 0; i < externalskins; i++) { - skin->numshaders = 1; - skin->ofsshaders = &shaders[i]; - skin->ofstexels = 0; + skin->numframes = 1; + skin->frame = &frames[i]; skin->skinwidth = 0; skin->skinheight = 0; skin->skinspeed = 0; - shadname[0] = '\0'; - - shaders[i] = Mod_ShaderFromQ3SkinFile(shadname, galias, loadmodel->name, i, skin->name); - - if (!shaders[i]) - { - if (i >= LittleLong(surf->numShaders)) - Q_strncpyz(shadname, "", sizeof(shadname)); //this shouldn't be possible - else - Q_strncpyz(shadname, inshader->name, sizeof(shadname)); - - Q_strncpyz(skin->name, shadname, sizeof(skin->name)); - shaders[i] = R_RegisterSkin(shadname, loadmodel->name); - - R_BuildDefaultTexnums(NULL, shaders[i]); - - if ((shaders[i]->flags & SHADER_NOIMAGE) && *shadname) - Con_Printf("Unable to load texture for shader \"%s\" on mesh \"%s\" for model \"%s\"\n", shaders[i]->name, surf->name, loadmodel->name); - } + if (i >= LittleLong(surf->numShaders)) + Q_strncpyz(frames->shadername, "", sizeof(frames->shadername)); //this shouldn't be possible + else + Q_strncpyz(frames->shadername, inshader->name, sizeof(frames->shadername)); inshader++; skin++; @@ -4474,22 +4513,22 @@ qboolean QDECL Mod_LoadQ3Model(model_t *mod, void *buffer, size_t fsize) } #endif - VectorCopy(min, loadmodel->mins); - VectorCopy(max, loadmodel->maxs); + VectorCopy(min, mod->mins); + VectorCopy(max, mod->maxs); - Mod_CompileTriangleNeighbours (galias); + Mod_CompileTriangleNeighbours (mod, galias); Mod_BuildTextureVectors(galias); surf = (md3Surface_t *)((qbyte *)surf + LittleLong(surf->ofsEnd)); } if (!root) - root = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasinfo_t)); + root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)); root->numtagframes = LittleLong(header->numFrames); root->numtags = LittleLong(header->numTags); - root->ofstags = ZG_Malloc(&loadmodel->memgroup, LittleLong(header->numTags)*sizeof(md3tag_t)*LittleLong(header->numFrames)); + root->ofstags = ZG_Malloc(&mod->memgroup, LittleLong(header->numTags)*sizeof(md3tag_t)*LittleLong(header->numFrames)); { md3tag_t *src; @@ -4603,7 +4642,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; - shader_t **shaders; + skinframe_t *skinframe; int skinfiles; int j; #endif @@ -4635,9 +4674,6 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) char *surfname; - - loadmodel=mod; - header = buffer; if (memcmp(header->id, "ZYMOTICMODEL", 12)) @@ -4670,10 +4706,10 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) VectorCopy(header->mins, mod->mins); VectorCopy(header->maxs, mod->maxs); - root = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasinfo_t)*header->numsurfaces); + root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)*header->numsurfaces); root->numswtransforms = header->lump_verts.length/sizeof(zymvertex_t); - transforms = ZG_Malloc(&loadmodel->memgroup, root->numswtransforms*sizeof(*transforms)); + transforms = ZG_Malloc(&mod->memgroup, root->numswtransforms*sizeof(*transforms)); root->ofsswtransforms = transforms; vertbonecounts = (int *)((char*)header + header->lump_vertbonecounts.start); @@ -4716,7 +4752,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) root->numverts = v+1; root->numbones = header->numbones; - bone = ZG_Malloc(&loadmodel->memgroup, root->numswtransforms*sizeof(*transforms)); + bone = ZG_Malloc(&mod->memgroup, root->numswtransforms*sizeof(*transforms)); inbone = (zymbone_t*)((char*)header + header->lump_bones.start); for (i = 0; i < root->numbones; i++) { @@ -4730,7 +4766,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) { count = BigLong(*renderlist++); count *= 3; - indexes = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*indexes)); + indexes = ZG_Malloc(&mod->memgroup, count*sizeof(*indexes)); root[i].ofs_indexes = indexes; root[i].numindexes = count; while(count) @@ -4748,15 +4784,15 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) return false; } - grp = ZG_Malloc(&loadmodel->memgroup, sizeof(*grp)*header->numscenes*header->numsurfaces); - matrix = ZG_Malloc(&loadmodel->memgroup, header->lump_poses.length); + grp = ZG_Malloc(&mod->memgroup, sizeof(*grp)*header->numscenes*header->numsurfaces); + matrix = ZG_Malloc(&mod->memgroup, header->lump_poses.length); inmatrix = (float*)((char*)header + header->lump_poses.start); for (i = 0; i < header->lump_poses.length/4; i++) matrix[i] = BigFloat(inmatrix[i]); inscene = (zymscene_t*)((char*)header + header->lump_scenes.start); surfname = ((char*)header + header->lump_surfnames.start); - stcoords = ZG_Malloc(&loadmodel->memgroup, root[0].numverts*sizeof(vec2_t)); + stcoords = ZG_Malloc(&mod->memgroup, root[0].numverts*sizeof(vec2_t)); inst = (vec2_t *)((char *)header + header->lump_texcoords.start); for (i = 0; i < header->lump_texcoords.length/8; i++) { @@ -4765,7 +4801,7 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) } #ifndef SERVERONLY - skinfiles = Mod_BuildSkinFileList(true, loadmodel->name); + skinfiles = Mod_CountSkinFiles(mod->name); #endif for (i = 0; i < header->numsurfaces; i++, surfname+=32) @@ -4781,14 +4817,14 @@ qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fsize) root[i].ofs_st_array = stcoords; root[i].numskins = skinfiles; - skin = ZG_Malloc(&loadmodel->memgroup, (sizeof(galiasskin_t)+sizeof(shader_t*))*skinfiles); - shaders = (shader_t**)(skin+skinfiles); - for (j = 0; j < skinfiles; j++, shaders++) + skin = ZG_Malloc(&mod->memgroup, (sizeof(galiasskin_t)+sizeof(skinframe_t*))*skinfiles); + skinframe = (skinframe_t*)(skin+skinfiles); + for (j = 0; j < skinfiles; j++, skinframe++) { - skin[j].numshaders = 1; //non-sequenced skins. - skin[j].ofsshaders = shaders; + skin[j].numframes = 1; //non-sequenced skins. + skin[j].frame = skinframe; - shaders[0] = Mod_LoadSkinFile(NULL, &root[i], j, NULL, 0, 0, NULL, skin->name); +// shaders[0] = Mod_LoadSkinFile(NULL, &root[i], j, NULL, 0, 0, NULL, skin->name); } root[i].ofsskins = skin; @@ -4955,12 +4991,13 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) unsigned int i, j; qboolean fail = false; char basename[MAX_QPATH]; + char psaname[MAX_QPATH]; galiasinfo_t *gmdl; #ifndef SERVERONLY vec2_t *stcoord; galiasskin_t *skin; - shader_t **gshaders; + skinframe_t *sframes; #endif galiasbone_t *bones; galiasgroup_t *group; @@ -4969,6 +5006,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) float vrad; int bonemap[MAX_BONES]; char *e; + size_t psasize; pskpnts_t *pnts = NULL; pskvtxw_t *vtxw = NULL; @@ -4994,7 +5032,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) #endif /*load the psk*/ - while (pos < com_filesize && !fail) + while (pos < fsize && !fail) { chunk = (pskchunk_t*)((char*)buffer + pos); chunk->version = LittleLong(chunk->version); @@ -5107,12 +5145,13 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) fail = true; /*attempt to load a psa file. don't die if we can't find one*/ - COM_StripExtension(mod->name, basename, sizeof(basename)); - buffer = COM_LoadTempMoreFile(va("%s.psa", basename)); + COM_StripExtension(mod->name, psaname, sizeof(psaname)); + Q_strncatz(psaname, ".psa", sizeof(psaname)); + buffer = COM_LoadFile(psaname, 5, &psasize); if (buffer) { pos = 0; - while (pos < com_filesize && !fail) + while (pos < psasize && !fail) { chunk = (pskchunk_t*)((char*)buffer + pos); chunk->version = LittleLong(chunk->version); @@ -5220,6 +5259,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) num_animkeys = 0; fail = false; } + BZ_Free(buffer); } if (fail) @@ -5227,10 +5267,10 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) return false; } - gmdl = ZG_Malloc(&loadmodel->memgroup, sizeof(*gmdl)*num_matt); + gmdl = ZG_Malloc(&mod->memgroup, sizeof(*gmdl)*num_matt); /*bones!*/ - bones = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasbone_t) * num_boneinfo); + bones = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t) * num_boneinfo); for (i = 0; i < num_boneinfo; i++) { Q_strncpyz(bones[i].name, boneinfo[i].name, sizeof(bones[i].name)); @@ -5247,7 +5287,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) } } - basematrix = ZG_Malloc(&loadmodel->memgroup, num_boneinfo*sizeof(float)*12); + basematrix = ZG_Malloc(&mod->memgroup, num_boneinfo*sizeof(float)*12); for (i = 0; i < num_boneinfo; i++) { float tmp[12]; @@ -5280,7 +5320,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) } } } - trans = ZG_Malloc(&loadmodel->memgroup, sizeof(*trans)*num_trans); + trans = ZG_Malloc(&mod->memgroup, sizeof(*trans)*num_trans); num_trans = 0; for (i = 0; i < num_vtxw; i++) { @@ -5347,7 +5387,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) #ifndef SERVERONLY /*st coords, all share the same list*/ - stcoord = ZG_Malloc(&loadmodel->memgroup, sizeof(vec2_t)*num_vtxw); + stcoord = ZG_Malloc(&mod->memgroup, sizeof(vec2_t)*num_vtxw); for (i = 0; i < num_vtxw; i++) { stcoord[i][0] = vtxw[i].texcoord[0]; @@ -5356,7 +5396,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) #endif /*allocate faces in a single block, as we at least know an upper bound*/ - indexes = ZG_Malloc(&loadmodel->memgroup, sizeof(index_t)*num_face*3); + indexes = ZG_Malloc(&mod->memgroup, sizeof(index_t)*num_face*3); if (animinfo && animkeys) { @@ -5365,7 +5405,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) if (numgroups) { /*externally supplied listing of frames. ignore all framegroups in the model and use only the pose info*/ - group = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)*numgroups + num_animkeys*sizeof(float)*12); + group = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)*numgroups + num_animkeys*sizeof(float)*12); animmatrix = (float*)(group+numgroups); for (j = 0; j < numgroups; j++) { @@ -5397,7 +5437,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) iframe = 0; for (i = 0; i < num_animinfo; i++) iframe += animinfo[i].numframes; - group = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)*iframe + num_animkeys*sizeof(float)*12); + group = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)*iframe + num_animkeys*sizeof(float)*12); animmatrix = (float*)(group+iframe); iframe = 0; for (j = 0; j < num_animinfo; j++) @@ -5418,7 +5458,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) else { /*keep each framegroup as a group*/ - group = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)*num_animinfo + num_animkeys*sizeof(float)*12); + group = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)*num_animinfo + num_animkeys*sizeof(float)*12); animmatrix = (float*)(group+num_animinfo); for (i = 0; i < num_animinfo; i++) { @@ -5447,7 +5487,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) { num_animinfo = 1; /*build a base pose*/ - group = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t) + num_boneinfo*sizeof(float)*12); + group = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t) + num_boneinfo*sizeof(float)*12); animmatrix = basematrix; group->boneofs = animmatrix; group->numposes = 1; @@ -5458,18 +5498,16 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) } #ifndef SERVERONLY - skin = ZG_Malloc(&loadmodel->memgroup, num_matt * (sizeof(galiasskin_t) + sizeof(shader_t*))); - gshaders = (shader_t**)(skin + num_matt); + skin = ZG_Malloc(&mod->memgroup, num_matt * (sizeof(galiasskin_t) + sizeof(skinframe_t))); + sframes = (skinframe_t*)(skin + num_matt); for (i = 0; i < num_matt; i++, skin++) { - skin->ofsshaders = &gshaders[i]; - skin->numshaders = 1; + skin->frame = &sframes[i]; + skin->numframes = 1; skin->skinspeed = 10; Q_strncpyz(skin->name, matt[i].name, sizeof(skin->name)); - gshaders[i] = R_RegisterSkin(matt[i].name, mod->name); - R_BuildDefaultTexnums(NULL, gshaders[i]); - if (gshaders[i]->flags & SHADER_NOIMAGE) - Con_Printf("Unable to load texture for shader \"%s\" for model \"%s\"\n", gshaders[i]->name, loadmodel->name); + Q_strncpyz(sframes[i].shadername, matt[i].name, sizeof(sframes[i].shadername)); + sframes[i].shader = NULL; gmdl[i].ofsskins = skin; gmdl[i].numskins = 1; @@ -5633,7 +5671,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) { #ifndef SERVERONLY galiasskin_t *skin; - shader_t **shaders; + skinframe_t *skinframe; int skinfiles; float *inst; float *outst; @@ -5662,9 +5700,6 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) int numtransforms; int numverts; - - loadmodel=mod; - header = buffer; if (memcmp(header->id, "DARKPLACESMODEL\0", 16)) @@ -5702,7 +5737,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) VectorCopy(header->mins, mod->mins); VectorCopy(header->maxs, mod->maxs); - root = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasinfo_t)*header->num_meshs); + root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)*header->num_meshs); mesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs); for (i = 0; i < header->num_meshs; i++, mesh++) @@ -5731,9 +5766,9 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) m = &root[i]; #ifdef SERVERONLY - transforms = ZG_Malloc(&loadmodel->memgroup, numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t)); + transforms = ZG_Malloc(&mod->memgroup, numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t)); #else - outst = ZG_Malloc(&loadmodel->memgroup, numverts*sizeof(vec2_t) + numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t)); + outst = ZG_Malloc(&mod->memgroup, numverts*sizeof(vec2_t) + numtransforms*sizeof(galisskeletaltransforms_t) + mesh->num_tris*3*sizeof(index_t)); m->ofs_st_array = (vec2_t*)outst; m->numverts = mesh->num_verts; inst = (float*)((char*)buffer + mesh->ofs_texcoords); @@ -5776,7 +5811,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) } } - outbone = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasbone_t)*header->num_bones); + outbone = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t)*header->num_bones); inbone = (dpmbone_t*)((char*)buffer + header->ofs_bones); for (i = 0; i < header->num_bones; i++) { @@ -5791,7 +5826,7 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) //throw away the flags. } - outgroups = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)*header->num_frames + sizeof(float)*header->num_frames*header->num_bones*12); + outgroups = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)*header->num_frames + sizeof(float)*header->num_frames*header->num_bones*12); outposedata = (float*)(outgroups+header->num_frames); inframes = (dpmframe_t*)((char*)buffer + header->ofs_frames); @@ -5820,7 +5855,9 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) } #ifndef SERVERONLY - skinfiles = Mod_BuildSkinFileList(true, loadmodel->name); + skinfiles = Mod_CountSkinFiles(mod->name); + if (skinfiles < 1) + skinfiles = 1; #endif mesh = (dpmmesh_t*)((char*)buffer + header->ofs_meshs); @@ -5845,14 +5882,12 @@ qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize) #else m->numskins = skinfiles; - skin = ZG_Malloc(&loadmodel->memgroup, (sizeof(galiasskin_t)+sizeof(shader_t*))*skinfiles); - shaders = (shader_t**)(skin+skinfiles); - for (j = 0; j < skinfiles; j++, shaders++) + skin = ZG_Malloc(&mod->memgroup, (sizeof(galiasskin_t)+sizeof(skinframe_t*))*skinfiles); + skinframe = (skinframe_t*)(skin+skinfiles); + for (j = 0; j < skinfiles; j++, skinframe++) { - skin[j].numshaders = 1; //non-sequenced skins. - skin[j].ofsshaders = shaders; - - shaders[0] = Mod_LoadSkinFile(NULL, m, j, NULL, 0, 0, NULL, skin->name); + skin[j].numframes = 1; //non-sequenced skins. + skin[j].frame = skinframe; } m->ofsskins = skin; @@ -6022,7 +6057,7 @@ galisskeletaltransforms_t *IQM_ImportTransforms(int *resultcount, int inverts, f } */ -galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) +galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer, size_t fsize) { struct iqmheader *h = (struct iqmheader *)buffer; struct iqmmesh *mesh; @@ -6052,7 +6087,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) galiasinfo_t *gai=NULL; #ifndef SERVERONLY galiasskin_t *skin=NULL; - shader_t **shaders=NULL; + skinframe_t *frame=NULL; int skinfiles; #endif galiasgroup_t *fgroup=NULL; @@ -6073,9 +6108,9 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) Con_Printf("%s: unsupported IQM version\n", mod->name); return NULL; } - if (h->filesize != com_filesize) + if (h->filesize != fsize) { - Con_Printf("%s: size (%u != %u)\n", mod->name, h->filesize, com_filesize); + Con_Printf("%s: size (%u != %u)\n", mod->name, h->filesize, fsize); return NULL; } @@ -6118,7 +6153,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) //we don't require weights, but such models won't animate. if (h->num_vertexes > 0 && (!vpos || !vtcoord)) { - Con_Printf("%s is missing vertex array data\n", loadmodel->name); + Con_Printf("%s is missing vertex array data\n", mod->name); return NULL; } noweights = !vbone || !vweight; @@ -6143,7 +6178,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) numgroups = 0; framegroups = NULL; if (!numgroups) - framegroups = ParseFrameInfo(loadmodel->name, &numgroups); + framegroups = ParseFrameInfo(mod->name, &numgroups); if (!numgroups && h->num_anims) { /*use the model's framegroups*/ @@ -6174,7 +6209,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) mesh = (struct iqmmesh*)(buffer + h->ofs_meshes); #ifndef SERVERONLY - skinfiles = Mod_BuildSkinFileList(true, loadmodel->name); + skinfiles = Mod_CountSkinFiles(mod->name); if (skinfiles < 1) skinfiles = 1; //iqms have 1 skin and one skin only and always. make sure its loaded. #endif @@ -6185,7 +6220,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) for (i = 0, memsize = 0, obase = NULL; i < 2; i++) { if (i) - obase = ZG_Malloc(&loadmodel->memgroup, memsize); + obase = ZG_Malloc(&mod->memgroup, memsize); memsize = 0; dalloc(gai, h->num_meshes); dalloc(bones, h->num_joints); @@ -6213,7 +6248,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) else orgbaf = NULL; dalloc(skin, h->num_meshes*skinfiles); - dalloc(shaders, h->num_meshes*skinfiles); + dalloc(frame, h->num_meshes*skinfiles); #endif dalloc(fgroup, numgroups); dalloc(oposebase, 12*h->num_joints); @@ -6374,13 +6409,12 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) { skin->skinwidth = 1; skin->skinheight = 1; - skin->ofstexels = 0; /*doesn't support 8bit colourmapping*/ skin->skinspeed = 10; /*something to avoid div by 0*/ - skin->numshaders = 1; //non-sequenced skins. - skin->ofsshaders = shaders; + skin->numframes = 1; //non-sequenced skins. + skin->frame = frame; skin++; - *shaders++ = Mod_LoadSkinFile(strings+mesh[i].material, &gai[i], j, NULL, 0, 0, NULL, skin->name); + Q_strncpyz(frame[j].shadername, strings+mesh[i].material, sizeof(frame[j].shadername)); } #endif @@ -6388,7 +6422,7 @@ galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, char *buffer) tris = (struct iqmtriangle*)(buffer + LittleLong(h->ofs_triangles)); tris += LittleLong(mesh[i].first_triangle); gai[i].numindexes = nt*3; - idx = ZG_Malloc(&loadmodel->memgroup, sizeof(*idx)*gai[i].numindexes); + idx = ZG_Malloc(&mod->memgroup, sizeof(*idx)*gai[i].numindexes); gai[i].ofs_indexes = idx; for (t = 0; t < nt; t++) { @@ -6464,7 +6498,7 @@ qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) galiasinfo_t *root; struct iqmheader *h = (struct iqmheader *)buffer; - root = Mod_ParseIQMMeshModel(mod, buffer); + root = Mod_ParseIQMMeshModel(mod, buffer, fsize); if (!root) { return false; @@ -6491,7 +6525,7 @@ qboolean QDECL Mod_LoadInterQuakeModel(model_t *mod, void *buffer, size_t fsize) #ifdef MD5MODELS -qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, galiasgroup_t *gat) +qboolean Mod_ParseMD5Anim(model_t *mod, char *buffer, galiasinfo_t *prototype, void**poseofs, galiasgroup_t *gat) { #define MD5ERROR0PARAM(x) { Con_Printf(CON_ERROR x "\n"); return false; } #define MD5ERROR1PARAM(x, y) { Con_Printf(CON_ERROR x "\n", y); return false; } @@ -6545,7 +6579,7 @@ qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, boneflags = BZ_Malloc(sizeof(unsigned char)*numjoints); baseframe = BZ_Malloc(sizeof(float)*12*numjoints); - *poseofs = posedata = ZG_Malloc(&loadmodel->memgroup, sizeof(float)*12*numjoints*numframes); + *poseofs = posedata = ZG_Malloc(&mod->memgroup, sizeof(float)*12*numjoints*numframes); if (prototype->numbones) { @@ -6555,7 +6589,7 @@ qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, } else { - bonelist = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasbone_t)*numjoints); + bonelist = ZG_Malloc(&mod->memgroup, sizeof(galiasbone_t)*numjoints); prototype->ofsbones = bonelist; } @@ -6597,19 +6631,19 @@ qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, { EXPECT("("); buffer = COM_Parse(buffer);f=atoi(com_token); - if (f < loadmodel->mins[0]) loadmodel->mins[0] = f; + if (f < mod->mins[0]) mod->mins[0] = f; buffer = COM_Parse(buffer);f=atoi(com_token); - if (f < loadmodel->mins[1]) loadmodel->mins[1] = f; + if (f < mod->mins[1]) mod->mins[1] = f; buffer = COM_Parse(buffer);f=atoi(com_token); - if (f < loadmodel->mins[2]) loadmodel->mins[2] = f; + if (f < mod->mins[2]) mod->mins[2] = f; EXPECT(")"); EXPECT("("); buffer = COM_Parse(buffer);f=atoi(com_token); - if (f > loadmodel->maxs[0]) loadmodel->maxs[0] = f; + if (f > mod->maxs[0]) mod->maxs[0] = f; buffer = COM_Parse(buffer);f=atoi(com_token); - if (f > loadmodel->maxs[1]) loadmodel->maxs[1] = f; + if (f > mod->maxs[1]) mod->maxs[1] = f; buffer = COM_Parse(buffer);f=atoi(com_token); - if (f > loadmodel->maxs[2]) loadmodel->maxs[2] = f; + if (f > mod->maxs[2]) mod->maxs[2] = f; EXPECT(")"); } EXPECT("}"); @@ -6701,7 +6735,7 @@ qboolean Mod_ParseMD5Anim(char *buffer, galiasinfo_t *prototype, void**poseofs, #undef EXPECT } -galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) +galiasinfo_t *Mod_ParseMD5MeshModel(model_t *mod, char *buffer, char *modname) { #define MD5ERROR0PARAM(x) { Con_Printf(CON_ERROR x "\n"); return NULL; } #define MD5ERROR1PARAM(x, y) { Con_Printf(CON_ERROR x "\n", y); return NULL; } @@ -6717,7 +6751,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) float *posedata; #ifndef SERVERONLY galiasskin_t *skin; - shader_t **shaders; + skinframe_t *frames; #endif char *filestart = buffer; @@ -6732,7 +6766,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) MD5ERROR0PARAM("MD5 model with unsupported MD5Version"); - root = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasinfo_t)); + root = ZG_Malloc(&mod->memgroup, sizeof(galiasinfo_t)); lastsurf = NULL; for(;;) @@ -6744,8 +6778,8 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) if (!strcmp(com_token, "numFrames")) { void *poseofs; - galiasgroup_t *grp = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)); - Mod_ParseMD5Anim(filestart, root, &poseofs, grp); + galiasgroup_t *grp = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)); + Mod_ParseMD5Anim(mod, filestart, root, &poseofs, grp); root->groupofs = grp; root->groups = 1; grp->poseofs = poseofs; @@ -6783,9 +6817,9 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) if (!numjoints) MD5ERROR0PARAM("MD5MESH: joints section before (or without) numjoints"); - bones = ZG_Malloc(&loadmodel->memgroup, sizeof(*bones) * numjoints); - pose = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)); - posedata = ZG_Malloc(&loadmodel->memgroup, sizeof(float)*12 * numjoints); + bones = ZG_Malloc(&mod->memgroup, sizeof(*bones) * numjoints); + pose = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)); + posedata = ZG_Malloc(&mod->memgroup, sizeof(float)*12 * numjoints); pose->skeltype = SKEL_ABSOLUTE; pose->rate = 1; pose->numposes = 1; @@ -6864,7 +6898,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) } else { - inf = ZG_Malloc(&loadmodel->memgroup, sizeof(*inf)); + inf = ZG_Malloc(&mod->memgroup, sizeof(*inf)); lastsurf->nextsurf = inf; lastsurf = inf; } @@ -6876,13 +6910,13 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) inf->baseframeofs = pose->boneofs; #ifndef SERVERONLY - skin = ZG_Malloc(&loadmodel->memgroup, sizeof(*skin)); - shaders = ZG_Malloc(&loadmodel->memgroup, sizeof(*shaders)); + skin = ZG_Malloc(&mod->memgroup, sizeof(*skin)); + frames = ZG_Malloc(&mod->memgroup, sizeof(*frames)); inf->numskins = 1; inf->ofsskins = skin; - skin->numshaders = 1; + skin->numframes = 1; skin->skinspeed = 1; - skin->ofsshaders = shaders; + skin->frame = frames; #endif EXPECT("{"); for(;;) @@ -6896,10 +6930,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) buffer = COM_Parse(buffer); #ifndef SERVERONLY //FIXME: we probably want to support multiple skins some time - shaders[0] = R_RegisterSkin(com_token, modname); - R_BuildDefaultTexnums(NULL, shaders[0]); - if (shaders[0]->flags & SHADER_NOIMAGE) - Con_Printf("Unable to load texture for shader \"%s\" for model \"%s\"\n", shaders[0]->name, loadmodel->name); + Q_strncpyz(frames[0].shadername, com_token, sizeof(frames[0].shadername)); #endif } else if (!strcmp(com_token, "numverts")) @@ -6914,7 +6945,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) firstweightlist = Z_Malloc(sizeof(*firstweightlist) * numverts); numweightslist = Z_Malloc(sizeof(*numweightslist) * numverts); #ifndef SERVERONLY - stcoord = ZG_Malloc(&loadmodel->memgroup, sizeof(float)*2*numverts); + stcoord = ZG_Malloc(&mod->memgroup, sizeof(float)*2*numverts); inf->ofs_st_array = (vec2_t*)stcoord; inf->numverts = numverts; #endif @@ -6955,7 +6986,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) if (numtris < 0) MD5ERROR0PARAM("MD5MESH: numverts cannot be negative"); - indexes = ZG_Malloc(&loadmodel->memgroup, sizeof(int)*3*numtris); + indexes = ZG_Malloc(&mod->memgroup, sizeof(int)*3*numtris); inf->ofs_indexes = indexes; inf->numindexes = numtris*3; } @@ -7015,7 +7046,7 @@ galiasinfo_t *Mod_ParseMD5MeshModel(char *buffer, char *modname) } - trans = ZG_Malloc(&loadmodel->memgroup, sizeof(*trans)*numusableweights); + trans = ZG_Malloc(&mod->memgroup, sizeof(*trans)*numusableweights); inf->ofsswtransforms = trans; for (num = 0, vnum = 0; num < numverts; num++) @@ -7065,10 +7096,7 @@ qboolean QDECL Mod_LoadMD5MeshModel(model_t *mod, void *buffer, size_t fsize) { galiasinfo_t *root; - - loadmodel=mod; - - root = Mod_ParseMD5MeshModel(buffer, mod->name); + root = Mod_ParseMD5MeshModel(mod, buffer, mod->name); if (root == NULL) { return false; @@ -7111,12 +7139,6 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) float **poseofs; char com_token[8192]; - - loadmodel=mod; - - - - buffer = COM_Parse(buffer); if (strcmp(com_token, "EXTERNALANIM")) { @@ -7128,7 +7150,7 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) if (!strcmp(com_token, "model")) { buffer = COM_Parse(buffer); - file = COM_LoadTempMoreFile(com_token); + file = COM_LoadTempMoreFile(com_token, NULL); if (!file) //FIXME: make non fatal somehow.. { @@ -7136,7 +7158,7 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) return false; } - root = Mod_ParseMD5MeshModel(file, mod->name); + root = Mod_ParseMD5MeshModel(mod, file, mod->name); if (root == NULL) { return false; @@ -7170,12 +7192,12 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) grouplist = BZ_Realloc(grouplist, sizeof(galiasgroup_t)*(numgroups+1)); poseofs = BZ_Realloc(poseofs, sizeof(*poseofs)*(numgroups+1)); buffer = COM_Parse(buffer); - file = COM_LoadTempMoreFile(com_token); + file = COM_LoadTempMoreFile(com_token, NULL); if (file) //FIXME: make non fatal somehow.. { char namebkup[MAX_QPATH]; Q_strncpyz(namebkup, com_token, sizeof(namebkup)); - if (!Mod_ParseMD5Anim(file, root, (void**)&poseofs[numgroups], &grouplist[numgroups])) + if (!Mod_ParseMD5Anim(mod, file, root, (void**)&poseofs[numgroups], &grouplist[numgroups])) { return false; } @@ -7188,12 +7210,12 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) grouplist = BZ_Realloc(grouplist, sizeof(galiasgroup_t)*(numgroups+1)); poseofs = BZ_Realloc(poseofs, sizeof(*poseofs)*(numgroups+1)); buffer = COM_Parse(buffer); - file = COM_LoadTempMoreFile(com_token); + file = COM_LoadTempMoreFile(com_token, NULL); if (file) //FIXME: make non fatal somehow.. { char namebkup[MAX_QPATH]; Q_strncpyz(namebkup, com_token, sizeof(namebkup)); - if (!Mod_ParseMD5Anim(file, root, (void**)&poseofs[numgroups], &grouplist[numgroups])) + if (!Mod_ParseMD5Anim(mod, file, root, (void**)&poseofs[numgroups], &grouplist[numgroups])) { return false; } @@ -7208,12 +7230,12 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) void *np; buffer = COM_Parse(buffer); - file = COM_LoadTempMoreFile(com_token); + file = COM_LoadTempMoreFile(com_token, NULL); if (file) //FIXME: make non fatal somehow.. { char namebkup[MAX_QPATH]; Q_strncpyz(namebkup, com_token, sizeof(namebkup)); - if (!Mod_ParseMD5Anim(file, root, &np, &ng)) + if (!Mod_ParseMD5Anim(mod, file, root, &np, &ng)) { return false; } @@ -7244,7 +7266,7 @@ qboolean QDECL Mod_LoadCompositeAnim(model_t *mod, void *buffer, size_t fsize) } newgroup = grouplist; - grouplist = ZG_Malloc(&loadmodel->memgroup, sizeof(galiasgroup_t)*numgroups); + grouplist = ZG_Malloc(&mod->memgroup, sizeof(galiasgroup_t)*numgroups); for(surf = root;;) { surf->groupofs = grouplist; diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 25d9fea7..60b7cd59 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -68,14 +68,22 @@ typedef struct FTE_DEPRECATED //we can't be bothered with animating skins. //We'll load up to four of them but after that you're on your own #ifndef SERVERONLY + +typedef struct +{ + shader_t *shader; + qbyte *texels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins. + char *defaultshader; + char shadername[MAX_QPATH]; + texnums_t texnums; +} skinframe_t; typedef struct { int skinwidth; int skinheight; - qbyte **ofstexels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins. float skinspeed; - int numshaders; - shader_t **ofsshaders; + int numframes; + skinframe_t *frame; char name[MAX_QPATH]; } galiasskin_t; @@ -170,28 +178,27 @@ typedef struct void (QDECL *UnRegisterModelFormat)(int idx); void (QDECL *UnRegisterAllModelFormats)(void); - void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, int size); + void *(QDECL *ZG_Malloc)(zonegroup_t *ctx, int size); //ctx=&mod->memgroup and the data will be freed when the model is freed. void (QDECL *ConcatTransforms) (float in1[3][4], float in2[3][4], float out[3][4]); void (QDECL *M3x4_Invert) (const float *in1, float *out); void (QDECL *StripExtension) (const char *in, char *out, int outlen); void (QDECL *GenMatrixPosQuat4Scale)(vec3_t pos, vec4_t quat, vec3_t scale, float result[12]); void (QDECL *ForceConvertBoneData)(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount); - - shader_t *(QDECL *RegisterShader) (const char *name, unsigned int usageflags, const char *shaderscript); - shader_t *(QDECL *RegisterSkin) (const char *shadername, const char *modname); - void (QDECL *BuildDefaultTexnums)(texnums_t *tn, shader_t *shader); } modplugfuncs_t; -#define MODPLUGFUNCS_VERSION 0 +#define MODPLUGFUNCS_VERSION 1 #ifdef SKELETALMODELS void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout); void QDECL Alias_ForceConvertBoneData(skeltype_t sourcetype, const float *sourcedata, size_t bonecount, galiasbone_t *bones, skeltype_t desttype, float *destbuffer, size_t destbonecount); #endif qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean allowskel); +void Mod_DestroyMesh(galiasinfo_t *galias); void Alias_FlushCache(void); void Alias_Shutdown(void); void Alias_Register(void); +shader_t *Mod_ShaderForSkin(model_t *model, int num); +const char *Mod_SkinNameForNum(model_t *model, int num); void Mod_DoCRC(model_t *mod, char *buffer, int buffersize); diff --git a/engine/common/common.c b/engine/common/common.c index 1c97131a..03a50a0f 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -94,8 +94,9 @@ cvar_t fs_gamemanifest = CVARFD("fs_gamemanifest", "", CVAR_NOSET, "A small upda cvar_t com_protocolname = CVARD("com_gamename", "", "The game name used for dpmaster queries"); cvar_t com_modname = CVARD("com_modname", "", "dpmaster information"); cvar_t com_parseutf8 = CVARD("com_parseutf8", "0", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. +cvar_t com_parseezquake = CVARD("com_parseezquake", "0", "Treat chevron chars from configs as a per-character flag. You should use this only for compat with nquake's configs."); cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); -cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger remote exploits in any engine (including "FULLENGINENAME"which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.\n"); +cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger remote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.\n"); cvar_t sys_platform = CVAR("sys_platform", PLATFORM); qboolean com_modified; // set true if using non-id files @@ -369,6 +370,45 @@ int QDECL Q_stricmp (const char *s1, const char *s2) return Q_strncasecmp (s1, s2, 99999); } +char *Q_strcasestr(const char *haystack, const char *needle) +{ + int c1, c2, c2f; + int i; + c2f = *needle; + if (c2f >= 'a' && c2f <= 'z') + c2f -= ('a' - 'A'); + if (!c2f) + return (char*)haystack; + while (1) + { + c1 = *haystack; + if (!c1) + return NULL; + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c1 == c2f) + { + for (i = 1; ; i++) + { + c1 = haystack[i]; + c2 = needle[i]; + if (c1 >= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (!c2) + return (char*)haystack; //end of needle means we found a complete match + if (!c1) //end of haystack means we can't possibly find needle in it any more + return NULL; + if (c1 != c2) //mismatch means no match starting at haystack[0] + break; + } + } + haystack++; + } + return NULL; //didn't find it +} + int QDECL Q_vsnprintf(char *buffer, int size, const char *format, va_list argptr) { return vsnprintf(buffer, size, format, argptr); @@ -1719,14 +1759,13 @@ void SZ_Print (sizebuf_t *buf, const char *data) //============================================================================ -char *COM_TrimString(char *str) +char *COM_TrimString(char *str, char *buffer, int buffersize) { int i; - static char buffer[256]; while (*str <= ' ' && *str>'\0') str++; - for (i = 0; i < 255; i++) + for (i = 0; i < buffersize-1; i++) { if (*str <= ' ') break; @@ -1806,23 +1845,25 @@ void COM_StripAllExtensions (char *in, char *out, int outlen) COM_FileExtension ============ */ -char *COM_FileExtension (const char *in) +char *COM_FileExtension (const char *in, char *result, size_t sizeofresult) { - static char exten[8]; int i; const char *dot; for (dot = in + strlen(in); dot >= in && *dot != '.'; dot--) ; if (dot < in) - return ""; + { + *result = 0; + return result; + } in = dot; in++; - for (i=0 ; i<7 && *in ; i++,in++) - exten[i] = *in; - exten[i] = 0; - return exten; + for (i=0 ; i= '0' && str[1] <= '9') { @@ -3366,6 +3420,9 @@ char *COM_Parse (const char *data) int c; int len; + if (out == com_token) + COM_AssertMainThread("COM_ParseOut: com_token"); + len = 0; com_token[0] = 0; @@ -3431,13 +3488,16 @@ skipwhite: #endif //semi-colon delimited tokens -char *COM_ParseStringSet (const char *data) +char *COM_ParseStringSet (const char *data, char *out, size_t outsize) { int c; int len; + if (out == com_token) + COM_AssertMainThread("COM_ParseOut: com_token"); + len = 0; - com_token[0] = 0; + out[0] = 0; if (!data) return NULL; @@ -3453,19 +3513,19 @@ char *COM_ParseStringSet (const char *data) // parse a regular word do { - if (len >= TOKENSIZE-1) + if (len >= outsize-1) { - com_token[len] = 0; + out[len] = 0; return (char*)data; } - com_token[len] = c; + out[len] = c; data++; len++; c = *(unsigned char*)data; } while (c>32 && c != ';'); - com_token[len] = 0; + out[len] = 0; return (char*)data; } @@ -3475,6 +3535,9 @@ char *COM_ParseOut (const char *data, char *out, int outlen) int c; int len; + if (out == com_token) + COM_AssertMainThread("COM_ParseOut: com_token"); + len = 0; out[0] = 0; @@ -3569,6 +3632,9 @@ char *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qbo len = 0; token[0] = 0; + if (token == com_token) + COM_AssertMainThread("COM_ParseOut: com_token"); + if (!data) return NULL; @@ -3774,6 +3840,8 @@ char *COM_ParseToken (const char *data, const char *punctuation) if (!punctuation) punctuation = DEFAULT_PUNCTUATION; + COM_AssertMainThread("COM_ParseOut: com_token"); + len = 0; com_token[0] = 0; @@ -3953,6 +4021,9 @@ char *COM_ParseCString (const char *data, char *token, size_t sizeoftoken, size_ len = 0; token[0] = 0; + if (token == com_token) + COM_AssertMainThread("COM_ParseCString: com_token"); + if (lengthwritten) *lengthwritten = 0; @@ -4103,7 +4174,7 @@ functions may use the cvar before anything's loaded. This isn't really needed, but might make some thing nicer. =============== */ -void COM_ParsePlusSets (void) +void COM_ParsePlusSets (qboolean docbuf) { int i; for (i=1 ; inext = &work; + com_work_tail[0] = &work; + } + else + com_work_head[0] = com_work_tail[0] = &work; + Sys_ConditionSignal(com_workercondition[0]); + Sys_UnlockConditional(com_workercondition[0]); + + while(com_fatalerror) + Sys_Sleep(0.1); + Sys_ThreadAbort(); +} +//return if there's *any* loading that needs to be done anywhere. +qboolean COM_HasWork(void) +{ + return com_work_head[0] || com_work_head[1]; +} +//thread==0 is main thread, thread==1 is a worker thread +void COM_AddWork(int thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b) +{ + //build the work + struct com_work_s *work = Z_Malloc(sizeof(*work)); + work->func = func; + work->ctx = ctx; + work->data = data; + work->a = a; + work->b = b; + + if (!com_workerthread || com_fatalerror) + thread = 0; + + //queue it (fifo) + Sys_LockConditional(com_workercondition[thread]); + if (com_work_tail[thread]) + { + com_work_tail[thread]->next = work; + com_work_tail[thread] = work; + } + else + com_work_head[thread] = com_work_tail[thread] = work; + +// Sys_Printf("%x: Queued work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); + + Sys_ConditionSignal(com_workercondition[thread]); + Sys_UnlockConditional(com_workercondition[thread]); + + if (!com_workerthread) + while(COM_DoWork(0, false)) + ; +} +//leavelocked = false == poll mode. +//leavelocked = true == safe sleeping +qboolean COM_DoWork(int thread, qboolean leavelocked) +{ + struct com_work_s *work; + if (!leavelocked) + { + //skip the locks if it looks like we can be lazy. + if (!com_work_head[thread]) + return false; + Sys_LockConditional(com_workercondition[thread]); + } + work = com_work_head[thread]; + if (work) + com_work_head[thread] = work->next; + if (!com_work_head[thread]) + com_work_head[thread] = com_work_tail[thread] = NULL; + + if (work) + { +// Sys_Printf("%x: Doing work %p (%s)\n", thread, work->ctx, work->ctx?(char*)work->ctx:"?"); + Sys_UnlockConditional(com_workercondition[thread]); + + work->func(work->ctx, work->data, work->a, work->b); + Z_Free(work); + + if (leavelocked) + Sys_LockConditional(com_workercondition[thread]); + + return true; //did something, check again + } + + if (!leavelocked) + Sys_UnlockConditional(com_workercondition[thread]); + + //nothing going on, if leavelocked then noone can add anything until we sleep. + return false; +} +static int COM_WorkerThread(void *arg) +{ + int thread = *(int*)arg; + Sys_LockConditional(com_workercondition[thread]); + do + { + while(COM_DoWork(thread, true)) + ; + if (com_workerdone[thread]) + break; + } while (Sys_ConditionWait(com_workercondition[thread])); + Sys_UnlockConditional(com_workercondition[thread]); + return 0; +} +static void COM_WorkerSync_Stop(void *ctx, void *data, size_t a, size_t b) +{ + Sys_LockConditional(com_workercondition[a]); + com_workerdone[a] = true; + Sys_ConditionSignal(com_workercondition[a]); + Sys_UnlockConditional(com_workercondition[a]); +} +#ifndef COM_AssertMainThread +void COM_AssertMainThread(const char *msg) +{ + if (com_resourcemutex && !Sys_IsThread(NULL)) + { + Sys_Error("Not on main thread: %s", msg); + } +} +#endif +void COM_DestroyWorkerThread(void) +{ + com_fatalerror = false; + if (com_workerthread) + { + //send it the terminate message + COM_AddWork(1, COM_WorkerSync_Stop, NULL, NULL, 1, 0); + Sys_WaitOnThread(com_workerthread); + com_workerthread = NULL; + } + + if (com_workercondition[0]) + Sys_DestroyConditional(com_workercondition[0]); + com_workercondition[0] = NULL; + + if (com_workercondition[1]) + Sys_DestroyConditional(com_workercondition[1]); + com_workercondition[1] = NULL; + + if (com_resourcemutex) + Sys_DestroyMutex(com_resourcemutex); + com_resourcemutex = NULL; +} + +//partial means we're waiting for an explicit response. the caller will need to check when that response arrives. +void COM_WorkerFullSync(void) +{ + qboolean repeat; + if (!com_workerthread) + return; + + //main thread asks worker thread to set main thread's 'done' flag. + //the worker might be posting work to the main thread and back (shaders with texures) so make sure that the only work we do before the reply is the reply itself. + do + { + int cmds = 0; + com_workerdone[0] = false; + repeat = COM_HasWork(); + COM_AddWork(1, COM_WorkerSync_Stop, NULL, NULL, 0, 0); + Sys_LockConditional(com_workercondition[0]); + do + { + if (com_fatalerror) + break; + while(COM_DoWork(0, true)) + cmds++; + if (com_workerdone[0]) + break; + } while (Sys_ConditionWait(com_workercondition[0])); + Sys_UnlockConditional(com_workercondition[0]); + if (com_fatalerror) + break; + if (cmds > 1) + repeat = true; + } while (COM_DoWork(0, false) || repeat); //outer loop ensures there isn't anything pingponging between +} +//main thread wants a specific object to be prioritised. +//an ancestor of the work must be pending on either the main thread or the worker thread. +//typically the worker gives us a signal to handle the final activation of the object. +//the address should be the load status. the value is the current value. +//the work that we're waiting for will be considered complete when the address is no longer set to value. +void COM_WorkerPartialSync(void *priorityctx, int *address, int value) +{ + struct com_work_s **link, *work, *prev; + double time1 = Sys_DoubleTime(); + int thread = 1; + +// Con_Printf("waiting for %p %s\n", priorityctx, priorityctx); + + //boost the priority of the object that we're waiting for on the other thread, if we can find it. + //this avoids waiting for everything. + //if we can't find it, then its probably currently being processed anyway. + //main thread is meant to do all loadstate value changes anyway, ensuring that we're woken up properly in this case. + if (priorityctx) + { + qboolean found = false; + Sys_LockConditional(com_workercondition[thread]); + for (link = &com_work_head[thread], work = NULL; *link; link = &(*link)->next) + { + prev = work; + work = *link; + if (work->ctx == priorityctx) + { //unlink it + + *link = work->next; + if (!work->next) + com_work_tail[thread] = prev; + //link it in at the head, so its the next thing seen. + work->next = com_work_head[thread]; + com_work_head[thread] = work; + if (!work->next) + com_work_tail[thread] = work; + found = true; + + break; //found it, nothing else to do. + } + } + if (!found) + Con_DPrintf("Might be in for a long wait for %s\n", priorityctx); + //we've not actually added any work, so no need to signal + Sys_UnlockConditional(com_workercondition[thread]); + } + + Sys_LockConditional(com_workercondition[0]); + do + { + if (com_fatalerror) + break; + while(COM_DoWork(0, true)) + { + //give up as soon as we're done + if (*address != value) + break; + } + //if our object's state has changed, we're done + if (*address != value) + break; + } while (Sys_ConditionWait(com_workercondition[0])); + Sys_UnlockConditional(com_workercondition[0]); + +// Con_Printf("Waited %f for %s\n", Sys_DoubleTime() - time1, priorityctx); +} + +static void COM_WorkerPing(void *ctx, void *data, size_t a, size_t b) +{ + double *timestamp = data; + if (!b) + COM_AddWork(0, COM_WorkerPing, ctx, data , 0, 1); + else + { + Con_Printf("Ping: %g\n", Sys_DoubleTime() - *timestamp); + } +} +static void COM_WorkerTest_f(void) +{ + if (com_workerthread) + { + double *timestamp = Z_Malloc(sizeof(*timestamp)); + *timestamp = Sys_DoubleTime(); + COM_AddWork(1, COM_WorkerPing, NULL, timestamp, 0, 0); + } + else + Con_Printf("Worker is not active.\n"); +} + + +cvar_t worker_flush = CVARD("worker_flush", "1", "Is set, process the entire load queue, loading stuff faster but at the risk of stalling."); +static void COM_InitWorkerThread(void) +{ + static int tid = 1; + + //in theory, we could run multiple workers, signalling a different one in turn for each bit of work. + com_resourcemutex = Sys_CreateMutex(); + com_workercondition[0] = Sys_CreateConditional(); + com_workercondition[1] = Sys_CreateConditional(); + if (!COM_CheckParm("-noworker")) + com_workerthread = Sys_CreateThread("loadworker", COM_WorkerThread, &tid, 0, 256*1024); + + Cmd_AddCommand ("worker_test", COM_WorkerTest_f); + Cvar_Register(&worker_flush, NULL); +} +#endif + /* ================ COM_Init @@ -4532,6 +5034,13 @@ void COM_Init (void) LittleFloat = FloatSwap; } +#ifdef MULTITHREAD + Sys_ThreadsInit(); +#endif +#ifdef LOADERTHREAD + COM_InitWorkerThread(); +#endif + Cmd_AddCommand ("path", COM_Path_f); //prints a list of current search paths. Cmd_AddCommand ("dir", COM_Dir_f); //q3 like Cmd_AddCommand ("flocate", COM_Locate_f); //prints the pak or whatever where this file can be found. @@ -4548,6 +5057,7 @@ void COM_Init (void) Cvar_Register (&gameversion_max, "Gamecode"); Cvar_Register (&com_nogamedirnativecode, "Gamecode"); Cvar_Register (&com_parseutf8, "Internationalisation"); + Cvar_Register (&com_parseezquake, NULL); Cvar_Register (&com_highlightcolor, "Internationalisation"); com_parseutf8.ival = 1; @@ -4585,6 +5095,8 @@ char *VARGS va(const char *format, ...) static char string[VA_BUFFERS][1024]; static int bufnum; + COM_AssertMainThread("va"); + bufnum++; bufnum &= (VA_BUFFERS-1); @@ -4844,6 +5356,8 @@ char *Info_ValueForKey (const char *s, const char *key) static int valueindex; char *o; + COM_AssertMainThread("Info_ValueForKey"); + valueindex = (valueindex + 1) % 4; if (*s == '\\') s++; diff --git a/engine/common/common.h b/engine/common/common.h index 69b304f5..39c8f166 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -253,7 +253,8 @@ void QDECL Q_strncpyz(char*d, const char*s, int n); #endif*/ -/*replacement functions which do not care for locale in text formatting ('C' locale)*/ +/*replacement functions which do not care for locale in text formatting ('C' locale), or are non-standard*/ +char *Q_strcasestr(const char *haystack, const char *needle); int Q_strncasecmp (const char *s1, const char *s2, int n); int Q_strcasecmp (const char *s1, const char *s2); int Q_atoi (const char *str); @@ -274,11 +275,11 @@ extern qboolean com_eof; //char *COM_Parse (const char *data); #define COM_Parse(d) COM_ParseOut(d,com_token, sizeof(com_token)) char *COM_ParseOut (const char *data, char *out, int outlen); -char *COM_ParseStringSet (const char *data); +char *COM_ParseStringSet (const char *data, char *out, size_t outlen); char *COM_ParseCString (const char *data, char *out, size_t maxoutlen, size_t *writtenlen); char *COM_StringParse (const char *data, char *token, unsigned int tokenlen, qboolean expandmacros, qboolean qctokenize); char *COM_ParseToken (const char *data, const char *punctuation); -char *COM_TrimString(char *str); +char *COM_TrimString(char *str, char *buffer, int buffersize); const char *COM_QuotedString(const char *string, char *buf, int buflen); //inverse of COM_StringParse @@ -291,13 +292,14 @@ void COM_AddParm (const char *parm); void COM_Init (void); void COM_InitArgv (int argc, const char **argv); -void COM_ParsePlusSets (void); +void COM_ParsePlusSets (qboolean docbuf); typedef unsigned int conchar_t; char *COM_DeFunString(conchar_t *str, conchar_t *stop, char *out, int outsize, qboolean ignoreflags); -#define PFS_KEEPMARKUP 1 //leave markup in the final string (but do parse it) -#define PFS_FORCEUTF8 2 //force utf-8 decoding -#define PFS_NOMARKUP 4 //strip markup completely +#define PFS_KEEPMARKUP 1 //leave markup in the final string (but do parse it) +#define PFS_FORCEUTF8 2 //force utf-8 decoding +#define PFS_NOMARKUP 4 //strip markup completely +#define PFS_EZQUAKEMARKUP 8 //aim for compat with ezquake instead of q3 compat conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t *out, int outsize, int keepmarkup); //ext is usually CON_WHITEMASK, returns its null terminator unsigned int utf8_decode(int *error, const void *in, char **out); unsigned int utf8_encode(void *out, unsigned int unicode, int maxlen); @@ -326,7 +328,7 @@ void COM_FileBase (const char *in, char *out, int outlen); int QDECL COM_FileSize(const char *path); void COM_DefaultExtension (char *path, char *extension, int maxlen); qboolean COM_RequireExtension(char *path, char *extension, int maxlen); -char *COM_FileExtension (const char *in); +char *COM_FileExtension (const char *in, char *result, size_t sizeofresult); void COM_CleanUpPath(char *str); char *VARGS va(const char *format, ...) LIKEPRINTF(1); @@ -335,7 +337,6 @@ char *VARGS va(const char *format, ...) LIKEPRINTF(1); //============================================================================ extern qboolean com_file_copyprotected; -extern int com_filesize; extern qboolean com_file_untrusted; struct cache_user_s; @@ -386,15 +387,6 @@ char *FS_GetPackNames(char *buffer, int buffersize, int referencedonly, qboolean qboolean FS_GenCachedPakName(char *pname, char *crc, char *local, int llen); //returns false if the name is invalid. void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags); -FTE_DEPRECATED int COM_FOpenFile (const char *filename, FILE **file); -FTE_DEPRECATED int COM_FOpenWriteFile (const char *filename, FILE **file); - -//#ifdef _MSC_VER //this is enough to annoy me, without conflicting with other (more bizzare) platforms. -//#define fopen dont_use_fopen -//#endif - -FTE_DEPRECATED void COM_CloseFile (FILE *h); - #define COM_FDepthFile(filename,ignorepacks) FS_FLocateFile(filename,ignorepacks?FSLFRT_DEPTH_OSONLY:FSLFRT_DEPTH_ANYPATH, NULL) #define COM_FCheckExists(filename) FS_FLocateFile(filename,FSLFRT_IFFOUND, NULL) @@ -437,7 +429,7 @@ enum fs_relative{ FS_SYSTEM //a system path. absolute paths are explicitly allowed and expected. }; -void FS_FlushFSHashReally(void); +void FS_FlushFSHashReally(qboolean domutexes); void FS_FlushFSHashWritten(void); void FS_FlushFSHashRemoved(void); void FS_CreatePath(const char *pname, enum fs_relative relativeto); @@ -460,14 +452,14 @@ void FS_PureMode(int mode, char *purenamelist, char *purecrclist, char *refnamel //recursively tries to open files until it can get a zip. vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name); -qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize); -qbyte *COM_LoadTempFile (const char *path); -qbyte *COM_LoadTempMoreFile (const char *path); //allocates a little bit more without freeing old temp +qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize, size_t *fsize); +qbyte *COM_LoadTempFile (const char *path, size_t *fsize); +qbyte *COM_LoadTempMoreFile (const char *path, size_t *fsize); //allocates a little bit more without freeing old temp //qbyte *COM_LoadHunkFile (const char *path); -qbyte *COM_LoadMallocFile (const char *path); +qbyte *COM_LoadMallocFile (const char *path, size_t *fsize); searchpathfuncs_t *COM_IteratePaths (void **iterator, char *pathbuffer, int pathbuffersize, char *dirname, int dirnamesize); -void COM_FlushFSCache(void); //a file was written using fopen +void COM_FlushFSCache(qboolean purge, qboolean domutex); //a file was written using fopen void COM_RefreshFSCache_f(void); qboolean FS_Restarted(unsigned int *since); @@ -510,12 +502,12 @@ char *FS_GetBasedir(void); char *FS_GetManifestArgs(void); struct zonegroup_s; -void *FS_LoadMallocGroupFile(struct zonegroup_s *ctx, char *path); -qbyte *FS_LoadMallocFile (const char *path); +void *FS_LoadMallocGroupFile(struct zonegroup_s *ctx, char *path, size_t *fsize); +qbyte *FS_LoadMallocFile (const char *path, size_t *fsize); qofs_t FS_LoadFile(const char *name, void **file); void FS_FreeFile(void *file); -qbyte *COM_LoadFile (const char *path, int usehunk); +qbyte *COM_LoadFile (const char *path, int usehunk, size_t *filesize); qboolean COM_LoadMapPackFile(const char *name, qofs_t offset); void COM_FlushTempoaryPacks(void); diff --git a/engine/common/console.h b/engine/common/console.h index d40e25b8..c54fbb38 100644 --- a/engine/common/console.h +++ b/engine/common/console.h @@ -175,12 +175,14 @@ void Con_CheckResize (void); void Con_ForceActiveNow(void); void Con_Init (void); void Con_Shutdown (void); +void Con_History_Save(void); void Con_History_Load(void); struct font_s; void Con_DrawOneConsole(console_t *con, struct font_s *font, float fx, float fy, float fsx, float fsy); void Con_DrawConsole (int lines, qboolean noback); char *Con_CopyConsole(qboolean nomarkup, qboolean onlyiflink); void Con_Print (char *txt); +void Con_PrintFlags(char *text, unsigned int setflags, unsigned int clearflags); void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1); void VARGS Con_TPrintf (translation_t text, ...); void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1); @@ -204,7 +206,7 @@ qboolean Con_NameForNum(int num, char *buffer, int buffersize); console_t *Con_FindConsole(const char *name); console_t *Con_Create(const char *name, unsigned int flags); void Con_SetVisible (console_t *con); -void Con_PrintCon (console_t *con, char *txt); +void Con_PrintCon (console_t *con, char *txt, unsigned int parseflags); void Con_NotifyBox (char *text); // during startup for sound / cd warnings diff --git a/engine/common/fs.c b/engine/common/fs.c index e9de7a48..7d17b7cd 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -17,6 +17,7 @@ hashtable_t filesystemhash; qboolean com_fschanged = true; qboolean fs_readonly; static unsigned int fs_restarts; +void *fs_thread_mutex; cvar_t com_fs_cache = CVARF("fs_cache", IFMINIMAL("2","1"), CVAR_ARCHIVE); cvar_t cfg_reload_on_gamedir = CVAR("cfg_reload_on_gamedir", "1"); @@ -86,19 +87,22 @@ void FS_UnRegisterFileSystemModule(void *module) { int i; qboolean found = false; - for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) + if (Sys_LockMutex(fs_thread_mutex)) { - if (searchpathformats[i].module == module) + for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { - searchpathformats[i].OpenNew = NULL; - searchpathformats[i].module = NULL; - found = true; + if (searchpathformats[i].module == module) + { + searchpathformats[i].OpenNew = NULL; + searchpathformats[i].module = NULL; + found = true; + } + } + Sys_UnlockMutex(fs_thread_mutex); + if (found) + { + Cmd_ExecuteString("fs_restart", RESTRICT_LOCAL); } - } - - if (found) - { - Cmd_ExecuteString("fs_restart", RESTRICT_LOCAL); } } @@ -158,7 +162,6 @@ char pubgamedirfile[MAX_OSPATH]; //like gamedirfile, but not set to the fte-only //the various COM_LoadFiles set these on return -int com_filesize; qboolean com_file_copyprotected;//file should not be available for download. qboolean com_file_untrusted; //file was downloaded inside a package @@ -462,7 +465,11 @@ ftemanifest_t *FS_Manifest_Parse(const char *fname, const char *data) { //every manifest should have an internal name specified, so we can guess the correct basedir //if we don't recognise it, then we'll typically prompt (or just use the working directory), but always assuming a default at least ensures things are sane. //fixme: we should probably fill in the basegame here (and share that logic with the legacy manifest generation code) +#ifdef BRANDING_NAME + data = Cmd_TokenizeString((char*)"game "STRINGIFY(BRANDING_NAME), false, false); +#else data = Cmd_TokenizeString((char*)"game quake", false, false); +#endif FS_Manifest_ParseTokens(man); } @@ -494,6 +501,7 @@ typedef struct searchpath_s char purepath[256]; //server tracks the path used to load them so it can tell the client int crc_check; //client sorts packs according to this checksum int crc_reply; //client sends a different crc back to the server, for the paks it's actually loaded. + int orderkey; //used to check to see if the paths were actually changed or not. struct searchpath_s *next; struct searchpath_s *nextpure; @@ -750,23 +758,30 @@ struct fsbucketblock }; static struct fsbucketblock *fs_hash_filebuckets; -void FS_FlushFSHashReally(void) +void FS_FlushFSHashReally(qboolean domutexes) { - if (filesystemhash.numbuckets) + COM_AssertMainThread("FS_FlushFSHashReally"); + if (!domutexes || Sys_LockMutex(fs_thread_mutex)) { - int i; - for (i = 0; i < filesystemhash.numbuckets; i++) - filesystemhash.bucket[i] = NULL; - } + com_fschanged = true; - while (fs_hash_filebuckets) - { - struct fsbucketblock *n = fs_hash_filebuckets->prev; - Z_Free(fs_hash_filebuckets); - fs_hash_filebuckets = n; - } + if (filesystemhash.numbuckets) + { + int i; + for (i = 0; i < filesystemhash.numbuckets; i++) + filesystemhash.bucket[i] = NULL; + } - com_fschanged = true; + while (fs_hash_filebuckets) + { + struct fsbucketblock *n = fs_hash_filebuckets->prev; + Z_Free(fs_hash_filebuckets); + fs_hash_filebuckets = n; + } + + if (domutexes) + Sys_UnlockMutex(fs_thread_mutex); + } } void FS_FlushFSHashWritten(void) { @@ -774,7 +789,7 @@ void FS_FlushFSHashWritten(void) } void FS_FlushFSHashRemoved(void) { - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); } static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) @@ -819,13 +834,18 @@ static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *fileh fs_hash_files++; } -void FS_RebuildFSHash(void) +void FS_RebuildFSHash(qboolean domutex) { int depth = 1; searchpath_t *search; if (!com_fschanged) return; + COM_AssertMainThread("FS_RebuildFSHash"); + if (domutex && !Sys_LockMutex(fs_thread_mutex)) + return; //amg! + + if (!filesystemhash.numbuckets) { filesystemhash.numbuckets = 1024; @@ -833,7 +853,7 @@ void FS_RebuildFSHash(void) } else { - FS_FlushFSHashRemoved(); + FS_FlushFSHashReally(false); } Hash_InitTable(&filesystemhash, filesystemhash.numbuckets, filesystemhash.bucket); @@ -857,6 +877,9 @@ void FS_RebuildFSHash(void) com_fschanged = false; + if (domutex) + Sys_UnlockMutex(fs_thread_mutex); + Con_DPrintf("%i unique files, %i duplicates\n", fs_hash_files, fs_hash_dups); } @@ -1578,12 +1601,11 @@ Filename are reletive to the quake directory. Always appends a 0 qbyte to the loaded data. ============ */ -qbyte *COM_LoadFile (const char *path, int usehunk) +qbyte *COM_LoadFile (const char *path, int usehunk, size_t *filesize) { vfsfile_t *f; qbyte *buf; qofs_t len; - char base[MAX_OSPATH]; flocation_t loc; FS_FLocateFile(path, FSLFRT_LENGTH, &loc); @@ -1597,9 +1619,12 @@ qbyte *COM_LoadFile (const char *path, int usehunk) if (!f) return NULL; - com_filesize = len = VFS_GETLEN(f); - // extract the filename base name for hunk tag - COM_FileBase (path, base, sizeof(base)); + len = VFS_GETLEN(f); + if (filesize) + *filesize = len; + + if (usehunk == 2 || usehunk == 4 || usehunk == 6) + COM_AssertMainThread("COM_LoadFile+hunk"); if (usehunk == 0) buf = (qbyte*)Z_Malloc (len+1); @@ -1633,12 +1658,12 @@ qbyte *COM_LoadFile (const char *path, int usehunk) return buf; } -qbyte *FS_LoadMallocFile (const char *path) +qbyte *FS_LoadMallocFile (const char *path, size_t *fsize) { - return COM_LoadFile (path, 5); + return COM_LoadFile (path, 5, fsize); } -void *FS_LoadMallocGroupFile(zonegroup_t *ctx, char *path) +void *FS_LoadMallocGroupFile(zonegroup_t *ctx, char *path, size_t *fsize) { char *mem = NULL; vfsfile_t *f = FS_OpenVFS(path, "rb", FS_GAME); @@ -1650,7 +1675,7 @@ void *FS_LoadMallocGroupFile(zonegroup_t *ctx, char *path) { mem[len] = 0; if (VFS_READ(f, mem, len) == len) - com_filesize = len; + *fsize = len; else mem = NULL; } @@ -1660,23 +1685,25 @@ void *FS_LoadMallocGroupFile(zonegroup_t *ctx, char *path) return mem; } -qbyte *COM_LoadTempFile (const char *path) +qbyte *COM_LoadTempFile (const char *path, size_t *fsize) { - return COM_LoadFile (path, 2); + return COM_LoadFile (path, 2, fsize); } -qbyte *COM_LoadTempMoreFile (const char *path) +qbyte *COM_LoadTempMoreFile (const char *path, size_t *fsize) { - return COM_LoadFile (path, 6); + return COM_LoadFile (path, 6, fsize); } // uses temp hunk if larger than bufsize -qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize) +qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize, size_t *fsize) { qbyte *buf; + COM_AssertMainThread("COM_LoadStackFile"); + loadbuf = (qbyte *)buffer; loadsize = bufsize; - buf = COM_LoadFile (path, 4); + buf = COM_LoadFile (path, 4, fsize); return buf; } @@ -1685,10 +1712,11 @@ qbyte *QDECL COM_LoadStackFile (const char *path, void *buffer, int bufsize) /*warning: at some point I'll change this function to return only read-only buffers*/ qofs_t FS_LoadFile(const char *name, void **file) { - *file = FS_LoadMallocFile(name); + size_t fsz; + *file = COM_LoadFile (name, 5, &fsz); if (!*file) return (qofs_t)-1; - return com_filesize; + return fsz; } void FS_FreeFile(void *file) { @@ -1825,7 +1853,8 @@ searchpathfuncs_t *FS_OpenPackByExtension(vfsfile_t *f, const char *pakname) { searchpathfuncs_t *pak; int j; - char *ext = COM_FileExtension(pakname); + char ext[8]; + COM_FileExtension(pakname, ext, sizeof(ext)); for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) { if (!searchpathformats[j].extension || !searchpathformats[j].OpenNew) @@ -1899,7 +1928,8 @@ static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const ptlen = strlen(purepath); for (i = 0; i < sizeof(fs_manifest->package) / sizeof(fs_manifest->package[0]); i++) { - if (fs_manifest->package[i].path && !strcmp(COM_FileExtension(fs_manifest->package[i].path), extension)) + char ext[8]; + if (fs_manifest->package[i].path && !strcmp(COM_FileExtension(fs_manifest->package[i].path, ext, sizeof(ext)), extension)) { palen = strlen(fs_manifest->package[i].path); if (palen > ptlen && (fs_manifest->package[i].path[ptlen] == '/' || fs_manifest->package[i].path[ptlen] == '\\' )&& !strncmp(purepath, fs_manifest->package[i].path, ptlen)) @@ -2017,7 +2047,8 @@ void COM_RefreshFSCache_f(void) com_fschanged=true; } -void COM_FlushFSCache(void) +//optionally purges the cache and rebuilds it +void COM_FlushFSCache(qboolean purge, qboolean domutex) { searchpath_t *search; if (com_fs_cache.ival && com_fs_cache.ival != 2) @@ -2036,7 +2067,7 @@ void COM_FlushFSCache(void) if (com_fs_cache.ival && com_fschanged) { //rebuild it if needed - FS_RebuildFSHash(); + FS_RebuildFSHash(domutex); } #endif } @@ -2218,16 +2249,17 @@ void COM_Gamedir (const char *dir) FS_Manifest_PurgeGamedirs(man); if (*dir) { - char *dup = Z_StrDup(dir); + char token[MAX_QPATH]; + char *dup = Z_StrDup(dir); //FIXME: is this really needed? dir = dup; - while ((dir = COM_ParseStringSet(dir))) + while ((dir = COM_ParseStringSet(dir, token, sizeof(token)))) { if (!strcmp(dir, ";")) continue; - if (!*com_token) + if (!*token) continue; - Cmd_TokenizeString(va("gamedir \"%s\"", com_token), false, false); + Cmd_TokenizeString(va("gamedir \"%s\"", token), false, false); FS_Manifest_ParseTokens(man); } Z_Free(dup); @@ -2243,7 +2275,7 @@ void COM_Gamedir (const char *dir) /*some modern non-compat settings*/ #define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n" /*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/ -#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" +#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_land \"fx/thngland.wav\"\n" /*yay q2!*/ #define Q2CFG "com_nogamedirnativecode 0\n" /*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/ @@ -2288,26 +2320,28 @@ const gamemode_info_t gamemode_info[] = { "basesj/pak0.pak"}, NULL, {"basesj", }, "Scouts Journey"}, {"-rmq", "rmq", "RMQ", {NULL}, RMQCFG, {"id1", "qw", "rmq", "*fte"}, "Remake Quake"}, - //supported commercial mods (some are currently only partially supported) +#ifdef HEXEN2 + //hexen2's mission pack generally takes precedence if both are installed. {"-portals", "h2mp", "FTE-H2MP", {"portals/hexen.rc", "portals/pak3.pak"}, HEX2CFG,{"data1", "portals", "*fteh2"}, "Hexen II MP"}, {"-hexen2", "hexen2", "FTE-Hexen2", {"data1/pak0.pak"}, HEX2CFG,{"data1", "*fteh2"}, "Hexen II"}, +#endif +#if defined(Q2CLIENT) || defined(Q2SERVER) {"-quake2", "q2", "FTE-Quake2", {"baseq2/pak0.pak"}, Q2CFG, {"baseq2", "*fteq2"}, "Quake II"}, - {"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena"}, - //mods of the above that should generally work. {"-dday", "dday", "FTE-Quake2", {"dday/pak0.pak"}, Q2CFG, {"baseq2", "dday", "*fteq2"}, "D-Day: Normandy"}, +#endif - //can run in windows, needs - {"-halflife", "hl", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"}, - +#if defined(Q3CLIENT) || defined(Q3SERVER) + {"-quake3", "q3", "FTE-Quake3", {"baseq3/pak0.pk3"}, Q3CFG, {"baseq3", "*fteq3"}, "Quake III Arena"}, //the rest are not supported in any real way. maps-only mostly, if that {"-quake4", "q4", "FTE-Quake4", {"q4base/pak00.pk4"}, NULL, {"q4base", "*fteq4"}, "Quake 4"}, - {"-et", "et", "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"}, + {"-et", NULL, "FTE-EnemyTerritory", {"etmain/pak0.pk3"}, NULL, {"etmain", "*fteet"}, "Wolfenstein - Enemy Territory"}, {"-jk2", "jk2", "FTE-JK2", {"base/assets0.pk3"}, NULL, {"base", "*ftejk2"}, "Jedi Knight II: Jedi Outcast"}, {"-warsow", "warsow", "FTE-Warsow", {"basewsw/pak0.pk3"}, NULL, {"basewsw", "*ftewsw"}, "Warsow"}, - +#endif +#if !defined(QUAKETC) && !defined(MINIMAL) {"-doom", "doom", "FTE-Doom", {"doom.wad"}, NULL, {"*", "*ftedoom"}, "Doom"}, {"-doom2", "doom2", "FTE-Doom2", {"doom2.wad"}, NULL, {"*", "*ftedoom"}, "Doom2"}, {"-doom3", "doom3", "FTE-Doom3", {"doom3.wad"}, NULL, {"based3", "*ftedoom3"},"Doom3"}, @@ -2315,6 +2349,10 @@ const gamemode_info_t gamemode_info[] = { //for the luls {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"*", "*fted2"}, "Diablo 2"}, + //can run in windows, needs hl gamecode enabled. + {"-halflife", "halflife", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"}, +#endif + {NULL} }; @@ -2401,7 +2439,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) vfsfile_t *f; flocation_t loc; char e, *n; - char *ext; + char ext[8]; char *end; int i; @@ -2429,7 +2467,7 @@ vfsfile_t *CL_OpenFileInPackage(searchpathfuncs_t *search, char *name) } else { - ext = COM_FileExtension(name); + COM_FileExtension(name, ext, sizeof(ext)); for (i = 0; i < sizeof(searchpathformats)/sizeof(searchpathformats[0]); i++) { if (!searchpathformats[i].extension || !searchpathformats[i].OpenNew) @@ -2556,8 +2594,19 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) searchpath_t *oldpaths; searchpath_t *next; int i; + int orderkey; + char syspath[MAX_OSPATH]; - FS_FlushFSHashReally(); + COM_AssertMainThread("FS_ReloadPackFilesFlags"); + COM_WorkerFullSync(); + + orderkey = 0; + if (com_purepaths) + for (next = com_purepaths; next; next = next->nextpure) + next->orderkey = ++orderkey; + if (fs_puremode < 2) + for (next = com_purepaths; next; next = next->nextpure) + next->orderkey = ++orderkey; oldpaths = com_searchpaths; com_searchpaths = NULL; @@ -2616,15 +2665,23 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) else if (*dir == '*') { //paths with a leading * are private, and not announced to clients that ask what the current gamedir is. - FS_AddGameDirectory(&oldpaths, dir+1, va("%s%s", com_gamepath, dir+1), reloadflags, SPF_EXPLICIT|SPF_PRIVATE); + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir+1); + FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE); if (com_homepathenabled) - FS_AddGameDirectory(&oldpaths, dir+1, va("%s%s", com_homepath, dir+1), reloadflags, SPF_EXPLICIT|SPF_PRIVATE); + { + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir+1); + FS_AddGameDirectory(&oldpaths, dir+1, syspath, reloadflags, SPF_EXPLICIT|SPF_PRIVATE); + } } else { - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_gamepath, dir), reloadflags, SPF_EXPLICIT); + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, dir); + FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT); if (com_homepathenabled) - FS_AddGameDirectory(&oldpaths, dir, va("%s%s", com_homepath, dir), reloadflags, SPF_EXPLICIT); + { + Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, dir); + FS_AddGameDirectory(&oldpaths, dir, syspath, reloadflags, SPF_EXPLICIT); + } } } } @@ -2746,9 +2803,10 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) { char local[MAX_OSPATH]; vfsfile_t *vfs; - char *ext = COM_FileExtension(pname); + char ext[8]; void *handle; int i; + COM_FileExtension(pname, ext, sizeof(ext)); if (FS_GenCachedPakName(pname, va("%i", crc), local, sizeof(local))) { @@ -2825,6 +2883,21 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) } + i = orderkey; + orderkey = 0; + next = NULL; + if (com_purepaths) + for (next = com_purepaths; next; next = next->nextpure) + if (next->orderkey != ++orderkey) + break; + if (!next && fs_puremode < 2) + for (next = com_purepaths; next; next = next->nextpure) + if (next->orderkey != ++orderkey) + break; + + if (next || i != orderkey)//some path changed. make sure the fs cache is flushed. + FS_FlushFSHashReally(false); + #ifndef SERVERONLY Shader_NeedReload(true); #endif @@ -2834,20 +2907,32 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags) void FS_UnloadPackFiles(void) { - FS_ReloadPackFilesFlags(1); + if (Sys_LockMutex(fs_thread_mutex)) + { + FS_ReloadPackFilesFlags(1); + Sys_UnlockMutex(fs_thread_mutex); + } } void FS_ReloadPackFiles(void) { - FS_ReloadPackFilesFlags(~0); + if (Sys_LockMutex(fs_thread_mutex)) + { + FS_ReloadPackFilesFlags(~0); + Sys_UnlockMutex(fs_thread_mutex); + } } void FS_ReloadPackFiles_f(void) { - if (atoi(Cmd_Argv(1))) - FS_ReloadPackFilesFlags(atoi(Cmd_Argv(1))); - else - FS_ReloadPackFilesFlags(~0); + if (Sys_LockMutex(fs_thread_mutex)) + { + if (atoi(Cmd_Argv(1))) + FS_ReloadPackFilesFlags(atoi(Cmd_Argv(1))); + else + FS_ReloadPackFilesFlags(~0); + Sys_UnlockMutex(fs_thread_mutex); + } } #if defined(_WIN32) && !defined(WINRT) @@ -3120,10 +3205,10 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base } #endif -void FS_Shutdown(void) +static void FS_FreePaths(void) { searchpath_t *next; - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); // // free up any current game dir info @@ -3149,6 +3234,12 @@ void FS_Shutdown(void) FS_Manifest_Free(fs_manifest); fs_manifest = NULL; } +void FS_Shutdown(void) +{ + FS_FreePaths(); + Sys_DestroyMutex(fs_thread_mutex); + fs_thread_mutex = NULL; +} //returns false if the directory is not suitable. //returns true if it contains a known package. if we don't actually know of any packages that it should have, we just have to assume that its okay. @@ -3591,7 +3682,11 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) if (!man) { vfsfile_t *f; - f = VFSOS_Open(va("%sdefault.fmf", newbasedir), "rb"); +#ifdef BRANDING_NAME + f = VFSOS_Open(va("%s"STRINGIFY(BRANDING_NAME)".fmf", newbasedir), "rb"); + if (!f) +#endif + f = VFSOS_Open(va("%sdefault.fmf", newbasedir), "rb"); if (f) { size_t len = VFS_GETLEN(f); @@ -3641,7 +3736,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) } else { - FS_Shutdown(); + FS_FreePaths(); reloadconfigs = true; } @@ -3714,48 +3809,53 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs) } #endif - FS_ReloadPackFilesFlags(~0); - - FS_BeginManifestUpdates(); - - COM_CheckRegistered(); - - if (allowreloadconfigs) + if (Sys_LockMutex(fs_thread_mutex)) { - for (i = 0; conffile[i]; i++) + FS_ReloadPackFilesFlags(~0); + + FS_BeginManifestUpdates(); + + COM_CheckRegistered(); + + Sys_UnlockMutex(fs_thread_mutex); + + if (allowreloadconfigs) { - FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); - if (confpath[i] != (loc.search?loc.search->handle:NULL)) + for (i = 0; conffile[i]; i++) { - reloadconfigs = true; - Con_DPrintf("Reloading configs because %s has changed\n", conffile[i]); + FS_FLocateFile(conffile[i], FSLFRT_IFFOUND, &loc); + if (confpath[i] != (loc.search?loc.search->handle:NULL)) + { + reloadconfigs = true; + Con_DPrintf("Reloading configs because %s has changed\n", conffile[i]); + } + } + + if (reloadconfigs) + { + //FIXME: flag this instead and do it after a delay + Cvar_ForceSet(&fs_gamename, man->formalname?man->formalname:"FTE"); + Cvar_ForceSet(&com_protocolname, man->protocolname?man->protocolname:"FTE"); + + if (isDedicated) + { + #ifndef CLIENTONLY + SV_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); + #endif + } + else + { + #ifndef SERVERONLY + CL_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); + #endif + } } } - if (reloadconfigs) - { - //FIXME: flag this instead and do it after a delay - Cvar_ForceSet(&fs_gamename, man->formalname?man->formalname:"FTE"); - Cvar_ForceSet(&com_protocolname, man->protocolname?man->protocolname:"FTE"); - - if (isDedicated) - { -#ifndef CLIENTONLY - SV_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); -#endif - } - else - { -#ifndef SERVERONLY - CL_ExecInitialConfigs(man->defaultexec?man->defaultexec:""); -#endif - } - } + //rebuild the cache now, should be safe to waste some cycles on it + COM_FlushFSCache(false, true); } - //rebuild the cache now, should be safe to waste some cycles on it - FS_RebuildFSHash(); - // COM_Effectinfo_Clear(); #ifndef SERVERONLY Validation_FlushFileList(); //prevent previous hacks from making a difference. @@ -3916,6 +4016,33 @@ void FS_ShowManifest_f(void) else Con_Printf("no manifest loaded...\n"); } + +int FS_ThreadTest(void*arg) +{ + vfsfile_t *f = NULL; + char startdata[256]; + char lastdata[256]; + while(!f) + { + if (Sys_LockMutex(fs_thread_mutex)) + { + f = FS_OpenVFS("gfx/pop.lmp", "rb", FS_GAME); + Sys_UnlockMutex(fs_thread_mutex); + } + } + VFS_READ(f, startdata, sizeof(startdata)); + VFS_CLOSE(f); + for(;;) + { + Sys_LockMutex(fs_thread_mutex); + f = FS_OpenVFS("gfx/pop.lmp", "rb", FS_GAME); + Sys_UnlockMutex(fs_thread_mutex); + VFS_READ(f, lastdata, sizeof(lastdata)); + VFS_CLOSE(f); + if (memcmp(startdata, lastdata, sizeof(lastdata))) + Con_Printf("FS_ThreadTest failed\n"); + } +} /* ================ COM_InitFilesystem @@ -4075,6 +4202,8 @@ void COM_InitFilesystem (void) if (com_homepathenabled) Con_TPrintf("Using home directory \"%s\"\n", com_homepath); + fs_thread_mutex = Sys_CreateMutex(); + #ifdef PLUGINS Plug_Initialise(false); #endif diff --git a/engine/common/fs.h b/engine/common/fs.h index db93e48c..15e921e8 100644 --- a/engine/common/fs.h +++ b/engine/common/fs.h @@ -1,7 +1,16 @@ #include "hash.h" +/* +Threading: +When the main thread will harm the filesystem tree/hash, it will first lock fs_thread_mutex (FIXME: make a proper rwlock). +Worker threads must thus lock that mutex for any opens (to avoid it changing underneath it), but can unlock it as soon as the open call returns. +Files may be shared between threads, but not simultaneously. +The filesystem driver is responsible for closing the pak/pk3 once all files are closed, and must ensure that opens+reads+closes as well as archive closure are thread safe. +*/ + #define FSVER 2 + #define FF_NOTFOUND (0u) //file wasn't found #define FF_FOUND (1u<<0u) //file was found #define FF_SYMLINK (1u<<1u) //file contents are the name of a different file (symlink). do a recursive lookup on the name @@ -16,6 +25,7 @@ extern hashtable_t filesystemhash; //this table is the one to build your hash re extern int fs_hash_dups; //for tracking efficiency. no functional use. extern int fs_hash_files; //for tracking efficiency. no functional use. extern qboolean fs_readonly; //if true, fopen(, "w") should always fail. +extern void *fs_thread_mutex; struct searchpath_s; struct searchpathfuncs_s diff --git a/engine/common/fs_pak.c b/engine/common/fs_pak.c index 35d46a8c..f8dd395e 100644 --- a/engine/common/fs_pak.c +++ b/engine/common/fs_pak.c @@ -17,10 +17,12 @@ typedef struct pack_s { searchpathfuncs_t pub; char descname[MAX_OSPATH]; - vfsfile_t *handle; - unsigned int filepos; //the pos the subfiles left it at (to optimize calls to vfs_seek) int numfiles; mpackfile_t *files; + + void *mutex; + vfsfile_t *handle; + unsigned int filepos; //the pos the subfiles left it at (to optimize calls to vfs_seek) int references; //seeing as all vfiles from a pak file use the parent's vfsfile, we need to keep the parent open until all subfiles are closed. } pack_t; @@ -65,14 +67,20 @@ static void QDECL FSPAK_GetPathDetails(searchpathfuncs_t *handle, char *out, siz } static void QDECL FSPAK_ClosePath(searchpathfuncs_t *handle) { + qboolean stillopen; pack_t *pak = (void*)handle; - pak->references--; - if (pak->references > 0) + if (!Sys_LockMutex(pak->mutex)) + return; //ohnoes + stillopen = --pak->references > 0; + Sys_UnlockMutex(pak->mutex); + if (stillopen) return; //not free yet VFS_CLOSE (pak->handle); + + Sys_DestroyMutex(pak->mutex); if (pak->files) Z_Free(pak->files); Z_Free(pak); @@ -193,11 +201,17 @@ static int QDECL VFSPAK_ReadBytes (struct vfsfile_s *vfs, void *buffer, int byte return -1; } - if (vfsp->parentpak->filepos != vfsp->currentpos) - VFS_SEEK(vfsp->parentpak->handle, vfsp->currentpos); - read = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread); - vfsp->currentpos += read; - vfsp->parentpak->filepos = vfsp->currentpos; + if (Sys_LockMutex(vfsp->parentpak->mutex)) + { + if (vfsp->parentpak->filepos != vfsp->currentpos) + VFS_SEEK(vfsp->parentpak->handle, vfsp->currentpos); + read = VFS_READ(vfsp->parentpak->handle, buffer, bytestoread); + vfsp->currentpos += read; + vfsp->parentpak->filepos = vfsp->currentpos; + Sys_UnlockMutex(vfsp->parentpak->mutex); + } + else + read = 0; return read; } @@ -243,7 +257,13 @@ static vfsfile_t *QDECL FSPAK_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo vfs = Z_Malloc(sizeof(vfspack_t)); vfs->parentpak = pack; + if (!Sys_LockMutex(pack->mutex)) + { + Z_Free(vfs); + return NULL; + } vfs->parentpak->references++; + Sys_UnlockMutex(pack->mutex); vfs->startpos = loc->offset; vfs->length = loc->len; @@ -364,6 +384,8 @@ searchpathfuncs_t *QDECL FSPAK_LoadArchive (vfsfile_t *file, const char *desc) pack->references++; + pack->mutex = Sys_CreateMutex(); + // Con_TPrintf ("Added packfile %s (%i files)\n", desc, numpackfiles); pack->pub.fsver = FSVER; diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index 7c00b5f9..0e12b520 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -197,7 +197,7 @@ vfsfile_t *VFSOS_Open(const char *osname, const char *mode) qboolean needsflush; f = VFSSTDIO_Open(osname, mode, &needsflush); if (needsflush) - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); return f; } #endif diff --git a/engine/common/fs_zip.c b/engine/common/fs_zip.c index 5d861de4..f719d287 100644 --- a/engine/common/fs_zip.c +++ b/engine/common/fs_zip.c @@ -264,13 +264,12 @@ typedef struct zipfile_s unsigned int numfiles; zpackfile_t *files; -#ifdef HASH_FILESYSTEM - hashtable_t hash; -#endif - + //info about the underlying file + void *mutex; qofs_t curpos; //cache position to avoid excess seeks qofs_t rawsize; vfsfile_t *raw; + int references; //number of files open inside, so things don't crash if is closed in the wrong order. } zipfile_t; @@ -286,12 +285,18 @@ static void QDECL FSZIP_GetPathDetails(searchpathfuncs_t *handle, char *out, siz } static void QDECL FSZIP_ClosePath(searchpathfuncs_t *handle) { + qboolean stillopen; zipfile_t *zip = (void*)handle; - if (--zip->references > 0) - return; //not yet time + if (!Sys_LockMutex(zip->mutex)) + return; //ohnoes + stillopen = --zip->references > 0; + Sys_UnlockMutex(zip->mutex); + if (stillopen) + return; //not free yet VFS_CLOSE(zip->raw); + Sys_DestroyMutex(zip->mutex); if (zip->files) Z_Free(zip->files); Z_Free(zip); @@ -483,8 +488,14 @@ qofs_t FSZIP_Decompress_Read(struct decompressstate *st, qbyte *buffer, qofs_t b if (sz) { //feed it. - VFS_SEEK(st->source->raw, st->cofs); - st->strm.avail_in = VFS_READ(st->source->raw, st->inbuffer, sz); + if (Sys_LockMutex(st->source->mutex)) + { + VFS_SEEK(st->source->raw, st->cofs); + st->strm.avail_in = VFS_READ(st->source->raw, st->inbuffer, sz); + Sys_UnlockMutex(st->source->mutex); + } + else + st->strm.avail_in = 0; st->strm.next_in = st->inbuffer; st->cofs += st->strm.avail_in; } @@ -532,13 +543,16 @@ static int QDECL VFSZIP_ReadBytes (struct vfsfile_s *file, void *buffer, int byt { read = FSZIP_Decompress_Read(vfsz->decompress, buffer, bytestoread); } - else + else if (Sys_LockMutex(vfsz->parent->mutex)) { VFS_SEEK(vfsz->parent->raw, vfsz->pos+vfsz->startpos); if (vfsz->pos + bytestoread > vfsz->length) bytestoread = max(0, vfsz->length - vfsz->pos); read = VFS_READ(vfsz->parent->raw, buffer, bytestoread); + Sys_UnlockMutex(vfsz->parent->mutex); } + else + read = 0; vfsz->pos += read; return read; @@ -677,7 +691,18 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo } } - zip->references++; + if (Sys_LockMutex(zip->mutex)) + { + zip->references++; + Sys_UnlockMutex(zip->mutex); + } + else + { + if (vfsz->decompress) + FSZIP_Decompress_Destroy(vfsz->decompress); + Z_Free(vfsz); + return NULL; + } return (vfsfile_t*)vfsz; } @@ -784,8 +809,12 @@ static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qo qbyte localdata[SIZE_LOCALENTRY]; qofs_t localstart = zfile->localpos; + + if (!Sys_LockMutex(zip->mutex)) + return false; //ohnoes VFS_SEEK(zip->raw, localstart); VFS_READ(zip->raw, localdata, sizeof(localdata)); + Sys_UnlockMutex(zip->mutex); //make sure we found the right sort of table. if (localdata[0] != 'P' || @@ -818,8 +847,11 @@ static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qo unsigned short extrachunk_tag; unsigned short extrachunk_len; + if (!Sys_LockMutex(zip->mutex)) + return false; //ohnoes VFS_SEEK(zip->raw, localstart); VFS_READ(zip->raw, extradata, sizeof(extradata)); + Sys_UnlockMutex(zip->mutex); while(extra+4 < extraend) @@ -1278,6 +1310,7 @@ searchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, const char *d } zip->references = 1; + zip->mutex = Sys_CreateMutex(); // Con_TPrintf ("Added zipfile %s (%i files)\n", desc, zip->numfiles); diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 7c317834..a75a4ea0 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -47,11 +47,11 @@ cvar_t q3bsp_surf_meshcollision_force = CVARD("q3bsp_surf_meshcollision_force", extern cvar_t r_shadow_bumpscale_basetexture; //these are in model.c (or gl_model.c) -qboolean Mod_LoadVertexes (lump_t *l); -qboolean Mod_LoadEdges (lump_t *l, qboolean lm); -qboolean Mod_LoadMarksurfaces (lump_t *l, qboolean lm); -qboolean Mod_LoadSurfedges (lump_t *l); -void Mod_LoadLighting (lump_t *l); +qboolean Mod_LoadVertexes (model_t *loadmodel, qbyte *mod_base, lump_t *l); +qboolean Mod_LoadEdges (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm); +qboolean Mod_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm); +qboolean Mod_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, lump_t *l); +void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l); static qboolean CM_NativeTrace(model_t *model, int forcehullnum, int frame, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean capsule, unsigned int contents, trace_t *trace); @@ -60,10 +60,6 @@ static unsigned int Q2BSP_PointContents(model_t *mod, vec3_t axis[3], vec3_t p); static int CM_PointCluster (model_t *mod, vec3_t p); extern mplane_t *box_planes; - -extern char loadname[32]; -extern model_t *loadmodel; -void Mod_Batches_Build(mesh_t *meshlist, model_t *mod, void (*build)(model_t *mod, msurface_t *surf, void *cookie), void *buildcookie); float RadiusFromBounds (vec3_t mins, vec3_t maxs) { int i; @@ -77,7 +73,7 @@ float RadiusFromBounds (vec3_t mins, vec3_t maxs) return Length (corner); } -void CalcSurfaceExtents (msurface_t *s) +void CalcSurfaceExtents (model_t *mod, msurface_t *s) { float mins[2], maxs[2], val; int i,j, e; @@ -92,11 +88,11 @@ void CalcSurfaceExtents (msurface_t *s) for (i=0 ; inumedges ; i++) { - e = loadmodel->surfedges[s->firstedge+i]; + e = mod->surfedges[s->firstedge+i]; if (e >= 0) - v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + v = &mod->vertexes[mod->edges[e].v[0]]; else - v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + v = &mod->vertexes[mod->edges[-e].v[1]]; for (j=0 ; j<2 ; j++) { @@ -146,22 +142,22 @@ void ClearBounds (vec3_t mins, vec3_t maxs) } -void Mod_SortShaders(void) +void Mod_SortShaders(model_t *mod) { //surely this isn't still needed? texture_t *textemp; int i, j; //sort loadmodel->textures - for (i = 0; i < loadmodel->numtextures; i++) + for (i = 0; i < mod->numtextures; i++) { - for (j = i+1; j < loadmodel->numtextures; j++) + for (j = i+1; j < mod->numtextures; j++) { - if ((loadmodel->textures[i]->shader && loadmodel->textures[j]->shader) && (loadmodel->textures[j]->shader->sort < loadmodel->textures[i]->shader->sort)) + if ((mod->textures[i]->shader && mod->textures[j]->shader) && (mod->textures[j]->shader->sort < mod->textures[i]->shader->sort)) { - textemp = loadmodel->textures[j]; - loadmodel->textures[j] = loadmodel->textures[i]; - loadmodel->textures[i] = textemp; + textemp = mod->textures[j]; + mod->textures[j] = mod->textures[i]; + mod->textures[i] = textemp; } } } @@ -177,7 +173,6 @@ qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out); #define Host_Error SV_Error #endif -extern model_t *loadmodel; extern qbyte *mod_base; #define capsuledist(dist,plane,mins,maxs) \ @@ -187,7 +182,6 @@ extern qbyte *mod_base; dist = plane->dist - dist; \ break; - unsigned char d_q28to24table[1024]; @@ -327,13 +321,9 @@ typedef struct cmodel_s /*used to trace*/ static int checkcount; -static mfog_t *map_fogs; -static int map_numfogs; - static int numbrushsides; static q2cbrushside_t *map_brushsides; -static int numtexinfo; static q2mapsurface_t *map_surfaces; static int numleafbrushes; @@ -379,7 +369,6 @@ static int floodvalid; static qbyte portalopen[MAX_Q2MAP_AREAPORTALS]; //memset will work if it's a qbyte, really it should be a qboolean - static int mapisq3; cvar_t map_noareas = SCVAR("map_noareas", "0"); //1 for lack of mod support. cvar_t map_noCurves = SCVARF("map_noCurves", "0", CVAR_CHEAT); @@ -830,7 +819,7 @@ static int CM_CreateFacetFromPoints(q2cbrush_t *facet, vec3_t *verts, int numver /* * CM_CreatePatch */ -static void CM_CreatePatch( q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const int *patch_cp ) +static void CM_CreatePatch(model_t *loadmodel, q3cpatch_t *patch, q2mapsurface_t *shaderref, const vec_t *verts, const int *patch_cp ) { int step[2], size[2], flat[2]; int i, j, k ,u, v; @@ -952,7 +941,7 @@ static void CM_CreatePatch( q3cpatch_t *patch, q2mapsurface_t *shaderref, const CM_CreatePatchesForLeafs ================= */ -qboolean CM_CreatePatchesForLeafs (void) +qboolean CM_CreatePatchesForLeafs (model_t *loadmodel) { int i, j, k; mleaf_t *leaf; @@ -1098,7 +1087,7 @@ qboolean CM_CreatePatchesForLeafs (void) checkout[k] = numpatches++; //gcc warns without this cast - CM_CreatePatch ( patch, surf, (const vec_t *)(map_verts + face->firstvert), face->patch.cp ); + CM_CreatePatch (loadmodel, patch, surf, (const vec_t *)(map_verts + face->firstvert), face->patch.cp ); } leaf->contents |= patch->surface->c.value; leaf->numleafpatches++; @@ -1122,20 +1111,18 @@ qboolean CM_CreatePatchesForLeafs (void) =============================================================================== */ -qbyte *cmod_base; - /* ================= CMod_LoadSubmodels ================= */ -qboolean CModQ2_LoadSubmodels (lump_t *l) +qboolean CModQ2_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l) { q2dmodel_t *in; cmodel_t *out; int i, j, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1182,13 +1169,13 @@ qboolean CModQ2_LoadSubmodels (lump_t *l) CMod_LoadSurfaces ================= */ -qboolean CModQ2_LoadSurfaces (lump_t *l) +qboolean CModQ2_LoadSurfaces (model_t *mod, qbyte *mod_base, lump_t *l) { q2texinfo_t *in; q2mapsurface_t *out; int i, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1203,8 +1190,8 @@ qboolean CModQ2_LoadSurfaces (lump_t *l) // if (count > MAX_Q2MAP_TEXINFO) // Host_Error ("Map has too many surfaces"); - numtexinfo = count; - out = map_surfaces = ZG_Malloc(&loadmodel->memgroup, count * sizeof(*map_surfaces)); + mod->numtexinfo = count; + out = map_surfaces = ZG_Malloc(&mod->memgroup, count * sizeof(*map_surfaces)); for ( i=0 ; iname, name, sizeof(wal->name)); - wal->width = image_width; - wal->height = image_height; + Q_strncpyz(wal->name, walname, sizeof(wal->name)); + wal->width = 64; + wal->height = 64; } - else - tn.base = R_LoadReplacementTexture(wal->name, loadname, imageflags); wal->width = LittleLong(wal->width); wal->height = LittleLong(wal->height); @@ -1262,40 +1239,30 @@ texture_t *Mod_LoadWall(char *name, char *sname, unsigned int imageflags) tex->width = wal->width; tex->height = wal->height; - if (!TEXVALID(tn.base)) - { - tn.base = R_LoadReplacementTexture(wal->name, "bmodels", imageflags); - if (!TEXVALID(tn.base)) - { - if (!wal->offsets[0]) - { - //they will download eventually... - CL_CheckOrEnqueDownloadFile(name, NULL, 0); - return NULL; - } - tn.base = R_LoadTexture8Pal24 (wal->name, tex->width, tex->height, (qbyte *)wal+wal->offsets[0], d_q28to24table, imageflags); - } - } - if (wal->offsets[0]) + tex->texnums.base = R_LoadReplacementTexture(wal->name, "bmodels", imageflags, (qbyte *)wal+wal->offsets[0], wal->width, wal->height, TF_SOLID8); + else + tex->texnums.base = R_LoadReplacementTexture(wal->name, "bmodels", imageflags, NULL, 0, 0, TF_INVALID); + + if (wal == &replacementwal) { - in = Hunk_TempAllocMore(wal->width*wal->height); - oin = (qbyte *)wal+wal->offsets[0]; - for (j = 0; j < wal->width*wal->height; j++) - in[j] = (d_q28to24table[oin[j]*3+0] + d_q28to24table[oin[j]*3+1] + d_q28to24table[oin[j]*3+2])/3; - tn.bump = R_LoadTexture8BumpPal (va("%s_bump", wal->name), tex->width, tex->height, in, true); +/* FIXME: worker needs the texture to have already been loaded. it can't sync with itself however. + if (tex->texnums.base->status == TEX_LOADING) + COM_WorkerPartialSync(tex, &tex->texnums.base->status, TEX_LOADING); + if (tex->texnums.base->status == TEX_LOADED) + { + tex->width = tex->texnums.base->width; + tex->height = tex->texnums.base->height; + } +*/ } - - if (wal != &replacementwal) + else BZ_Free(wal); - tex->shader = R_RegisterCustom (sname, SUF_LIGHTMAP, Shader_DefaultBSPQ2, NULL); - R_BuildDefaultTexnums(&tn, tex->shader); - return tex; } -qboolean CModQ2_LoadTexInfo (lump_t *l) //yes I know these load from the same place +qboolean CModQ2_LoadTexInfo (model_t *mod, qbyte *mod_base, lump_t *l, char *mapname) //yes I know these load from the same place { q2texinfo_t *in; mtexinfo_t *out; @@ -1305,20 +1272,20 @@ qboolean CModQ2_LoadTexInfo (lump_t *l) //yes I know these load from the same pl float len1, len2; int texcount; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); + Con_Printf ("MOD_LoadBmodel: funny lump size in %s\n", mod->name); return false; } count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); - loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t *)*count); + mod->textures = ZG_Malloc(&mod->memgroup, sizeof(texture_t *)*count); texcount = 0; - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; + mod->texinfo = out; + mod->numtexinfo = count; for ( i=0 ; itextures[j]->name)) + if (!strcmp(sname, mod->textures[j]->name)) { - out->texture = loadmodel->textures[j]; + out->texture = mod->textures[j]; break; } } @@ -1370,10 +1337,10 @@ qboolean CModQ2_LoadTexInfo (lump_t *l) //yes I know these load from the same pl } snprintf (name, sizeof(name), "textures/%s.wal", in->texture); - out->texture = Mod_LoadWall (name, sname, (out->flags&TEX_SPECIAL)?0:IF_NOALPHA); + out->texture = Mod_LoadWall (mod, mapname, name, sname, (out->flags&TEX_SPECIAL)?0:IF_NOALPHA); if (!out->texture || !out->texture->width || !out->texture->height) { - out->texture = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t) + 16*16+8*8+4*4+2*2); + out->texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t) + 16*16+8*8+4*4+2*2); Con_Printf (CON_WARNING "Couldn't load %s\n", name); memcpy(out->texture, r_notexture_mip, sizeof(texture_t) + 16*16+8*8+4*4+2*2); @@ -1381,17 +1348,17 @@ qboolean CModQ2_LoadTexInfo (lump_t *l) //yes I know these load from the same pl Q_strncpyz(out->texture->name, sname, sizeof(out->texture->name)); - loadmodel->textures[texcount++] = out->texture; + mod->textures[texcount++] = out->texture; } if (in->nexttexinfo != -1) { - Con_DPrintf("FIXME: %s should animate to %s\n", in->texture, (in->nexttexinfo+(q2texinfo_t *)(cmod_base + l->fileofs))->texture); + Con_DPrintf("FIXME: %s should animate to %s\n", in->texture, (in->nexttexinfo+(q2texinfo_t *)(mod_base + l->fileofs))->texture); } } - in = (void *)(cmod_base + l->fileofs); - out = loadmodel->texinfo; + in = (void *)(mod_base + l->fileofs); + out = mod->texinfo; for (i=0 ; i= 0 && in[i].nexttexinfo < count) @@ -1408,9 +1375,9 @@ qboolean CModQ2_LoadTexInfo (lump_t *l) //yes I know these load from the same pl out[i].texture->anim_total++; } - loadmodel->numtextures = texcount; + mod->numtextures = texcount; - Mod_SortShaders(); + Mod_SortShaders(mod); return true; } #endif @@ -1469,7 +1436,7 @@ Mod_LoadFaces ================= */ #ifndef SERVERONLY -qboolean CModQ2_LoadFaces (lump_t *l) +qboolean CModQ2_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l) { dsface_t *in; msurface_t *out; @@ -1477,17 +1444,17 @@ qboolean CModQ2_LoadFaces (lump_t *l) int planenum, side; int ti; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); + Con_Printf ("MOD_LoadBmodel: funny lump size in %s\n",mod->name); return false; } count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, (count+6)*sizeof(*out)); //spare for skybox + out = ZG_Malloc(&mod->memgroup, (count+6)*sizeof(*out)); //spare for skybox - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; + mod->surfaces = out; + mod->numsurfaces = count; for ( surfnum=0 ; surfnumflags |= SURF_PLANEBACK; - out->plane = loadmodel->planes + planenum; + out->plane = mod->planes + planenum; ti = LittleShort (in->texinfo); - if (ti < 0 || ti >= loadmodel->numtexinfo) + if (ti < 0 || ti >= mod->numtexinfo) { Con_Printf (CON_ERROR "MOD_LoadBmodel: bad texinfo number\n"); return false; } - out->texinfo = loadmodel->texinfo + ti; + out->texinfo = mod->texinfo + ti; #ifndef SERVERONLY if (out->texinfo->flags & TI_SKY) @@ -1521,7 +1488,7 @@ qboolean CModQ2_LoadFaces (lump_t *l) } #endif - CalcSurfaceExtents (out); + CalcSurfaceExtents (mod, out); // lighting info @@ -1531,7 +1498,7 @@ qboolean CModQ2_LoadFaces (lump_t *l) if (i == -1) out->samples = NULL; else - out->samples = loadmodel->lightdata + i; + out->samples = mod->lightdata + i; // set the drawing flags @@ -1566,14 +1533,14 @@ CMod_LoadNodes ================= */ -qboolean CModQ2_LoadNodes (lump_t *l) +qboolean CModQ2_LoadNodes (model_t *mod, qbyte *mod_base, lump_t *l) { q2dnode_t *in; int child; mnode_t *out; int i, j, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1592,10 +1559,10 @@ qboolean CModQ2_LoadNodes (lump_t *l) return false; } - out = ZG_Malloc(&loadmodel->memgroup, sizeof(mnode_t)*count); + out = ZG_Malloc(&mod->memgroup, sizeof(mnode_t)*count); - loadmodel->nodes = out; - loadmodel->numnodes = count; + mod->nodes = out; + mod->numnodes = count; for (i=0 ; iminmaxs[3+j] = LittleShort (in->maxs[j]); } - out->plane = loadmodel->planes + LittleLong(in->planenum); + out->plane = mod->planes + LittleLong(in->planenum); out->firstsurface = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); @@ -1618,13 +1585,13 @@ qboolean CModQ2_LoadNodes (lump_t *l) child = LittleLong (in->children[j]); out->childnum[j] = child; if (child < 0) - out->children[j] = (mnode_t *)(loadmodel->leafs + -1-child); + out->children[j] = (mnode_t *)(mod->leafs + -1-child); else - out->children[j] = loadmodel->nodes + child; + out->children[j] = mod->nodes + child; } } - CMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs + CMod_SetParent (mod->nodes, NULL); // sets nodes and leafs return true; } @@ -1635,13 +1602,13 @@ CMod_LoadBrushes ================= */ -qboolean CModQ2_LoadBrushes (lump_t *l) +qboolean CModQ2_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l) { q2dbrush_t *in; q2cbrush_t *out; int i, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1655,7 +1622,7 @@ qboolean CModQ2_LoadBrushes (lump_t *l) return false; } - map_brushes = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * (count+1)); + map_brushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1)); out = map_brushes; @@ -1677,14 +1644,14 @@ qboolean CModQ2_LoadBrushes (lump_t *l) CMod_LoadLeafs ================= */ -qboolean CModQ2_LoadLeafs (lump_t *l) +qboolean CModQ2_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; mleaf_t *out; q2dleaf_t *in; int count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1704,11 +1671,11 @@ qboolean CModQ2_LoadLeafs (lump_t *l) return false; } - out = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * (count+1)); + out = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1)); numclusters = 0; - loadmodel->leafs = out; - loadmodel->numleafs = count; + mod->leafs = out; + mod->numleafs = count; for ( i=0 ; ifirstleafbrush = (unsigned short)LittleShort (in->firstleafbrush); out->numleafbrushes = (unsigned short)LittleShort (in->numleafbrushes); - out->firstmarksurface = loadmodel->marksurfaces + + out->firstmarksurface = mod->marksurfaces + (unsigned short)LittleShort(in->firstleafface); out->nummarksurfaces = (unsigned short)LittleShort(in->numleaffaces); if (out->cluster >= numclusters) numclusters = out->cluster + 1; } - out = loadmodel->leafs; + out = mod->leafs; if (out[0].contents != Q2CONTENTS_SOLID) { @@ -1752,7 +1719,7 @@ qboolean CModQ2_LoadLeafs (lump_t *l) CMod_LoadPlanes ================= */ -qboolean CModQ2_LoadPlanes (lump_t *l) +qboolean CModQ2_LoadPlanes (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; mplane_t *out; @@ -1760,7 +1727,7 @@ qboolean CModQ2_LoadPlanes (lump_t *l) int count; int bits; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1780,8 +1747,8 @@ qboolean CModQ2_LoadPlanes (lump_t *l) return false; } - loadmodel->planes = out = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * count); - loadmodel->numplanes = count; + mod->planes = out = ZG_Malloc(&mod->memgroup, sizeof(*out) * count); + mod->numplanes = count; for ( i=0 ; ifileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1847,7 +1814,7 @@ qboolean CModQ2_LoadLeafBrushes (lump_t *l) CMod_LoadBrushSides ================= */ -qboolean CModQ2_LoadBrushSides (lump_t *l) +qboolean CModQ2_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; q2cbrushside_t *out; @@ -1855,7 +1822,7 @@ qboolean CModQ2_LoadBrushSides (lump_t *l) int count; int num; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1870,15 +1837,15 @@ qboolean CModQ2_LoadBrushSides (lump_t *l) return false; } - out = map_brushsides = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * count); + out = map_brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count); numbrushsides = count; for ( i=0 ; iplanenum); - out->plane = &loadmodel->planes[num]; + out->plane = &mod->planes[num]; j = LittleShort (in->texinfo); - if (j >= numtexinfo) + if (j >= mod->numtexinfo) { Con_Printf (CON_ERROR "Bad brushside texinfo\n"); return false; @@ -1894,14 +1861,14 @@ qboolean CModQ2_LoadBrushSides (lump_t *l) CMod_LoadAreas ================= */ -qboolean CModQ2_LoadAreas (lump_t *l) +qboolean CModQ2_LoadAreas (model_t *mod, qbyte *mod_base, lump_t *l) { int i; q2carea_t *out; q2darea_t *in; int count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1934,14 +1901,14 @@ qboolean CModQ2_LoadAreas (lump_t *l) CMod_LoadAreaPortals ================= */ -qboolean CModQ2_LoadAreaPortals (lump_t *l) +qboolean CModQ2_LoadAreaPortals (model_t *mod, qbyte *mod_base, lump_t *l) { int i; q2dareaportal_t *out; q2dareaportal_t *in; int count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -1972,7 +1939,7 @@ qboolean CModQ2_LoadAreaPortals (lump_t *l) CMod_LoadVisibility ================= */ -qboolean CModQ2_LoadVisibility (lump_t *l) +qboolean CModQ2_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l) { int i; @@ -1983,10 +1950,10 @@ qboolean CModQ2_LoadVisibility (lump_t *l) // return false; // } - map_q2vis = ZG_Malloc(&loadmodel->memgroup, l->filelen); - memcpy (map_q2vis, cmod_base + l->fileofs, l->filelen); + map_q2vis = ZG_Malloc(&mod->memgroup, l->filelen); + memcpy (map_q2vis, mod_base + l->fileofs, l->filelen); - loadmodel->vis = map_q2vis; + mod->vis = map_q2vis; map_q2vis->numclusters = LittleLong (map_q2vis->numclusters); for (i=0 ; inumclusters ; i++) @@ -1994,7 +1961,7 @@ qboolean CModQ2_LoadVisibility (lump_t *l) map_q2vis->bitofs[i][0] = LittleLong (map_q2vis->bitofs[i][0]); map_q2vis->bitofs[i][1] = LittleLong (map_q2vis->bitofs[i][1]); } - loadmodel->numclusters = map_q2vis->numclusters; + mod->numclusters = map_q2vis->numclusters; return true; } @@ -2004,19 +1971,17 @@ qboolean CModQ2_LoadVisibility (lump_t *l) CMod_LoadEntityString ================= */ -void CMod_LoadEntityString (lump_t *l) +void CMod_LoadEntityString (model_t *mod, qbyte *mod_base, lump_t *l) { // if (l->filelen > MAX_Q2MAP_ENTSTRING) // Host_Error ("Map has too large entity lump"); - loadmodel->entities = ZG_Malloc(&loadmodel->memgroup, l->filelen+1); - memcpy (loadmodel->entities, cmod_base + l->fileofs, l->filelen); + mod->entities = ZG_Malloc(&mod->memgroup, l->filelen+1); + memcpy (mod->entities, mod_base + l->fileofs, l->filelen); } - - - -qboolean CModQ3_LoadMarksurfaces (lump_t *l) +#ifdef Q3BSPS +qboolean CModQ3_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j, count; int *in; @@ -2048,7 +2013,7 @@ qboolean CModQ3_LoadMarksurfaces (lump_t *l) return true; } -qboolean CModQ3_LoadSubmodels (lump_t *l) +qboolean CModQ3_LoadSubmodels (model_t *mod, qbyte *mod_base, lump_t *l) { q3dmodel_t *in; cmodel_t *out; @@ -2056,7 +2021,7 @@ qboolean CModQ3_LoadSubmodels (lump_t *l) q2cbrush_t **leafbrush; mleaf_t *bleaf; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -2075,11 +2040,11 @@ qboolean CModQ3_LoadSubmodels (lump_t *l) return false; } - out = map_cmodels = ZG_Malloc(&loadmodel->memgroup, count * sizeof(*map_cmodels)); + out = map_cmodels = ZG_Malloc(&mod->memgroup, count * sizeof(*map_cmodels)); numcmodels = count; if (count > 1) - bleaf = ZG_Malloc(&loadmodel->memgroup, (count-1) * sizeof(*bleaf)); + bleaf = ZG_Malloc(&mod->memgroup, (count-1) * sizeof(*bleaf)); else bleaf = NULL; @@ -2097,7 +2062,7 @@ qboolean CModQ3_LoadSubmodels (lump_t *l) out->numsurfaces = LittleLong (in->num_surfaces); if (!i) { - out->headnode = loadmodel->nodes; + out->headnode = mod->nodes; out->headleaf = NULL; } else @@ -2125,19 +2090,19 @@ qboolean CModQ3_LoadSubmodels (lump_t *l) //submodels } - AddPointToBounds(map_cmodels[0].mins, loadmodel->mins, loadmodel->maxs); - AddPointToBounds(map_cmodels[0].maxs, loadmodel->mins, loadmodel->maxs); + AddPointToBounds(map_cmodels[0].mins, mod->mins, mod->maxs); + AddPointToBounds(map_cmodels[0].maxs, mod->mins, mod->maxs); return true; } -qboolean CModQ3_LoadShaders (lump_t *l) +qboolean CModQ3_LoadShaders (model_t *mod, qbyte *mod_base, lump_t *l) { dq3shader_t *in; q2mapsurface_t *out; int i, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -2153,27 +2118,38 @@ qboolean CModQ3_LoadShaders (lump_t *l) // else if (count > MAX_Q2MAP_TEXINFO) // Host_Error ("Map has too many shaders"); - numtexinfo = count; - out = map_surfaces = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); + mod->numtexinfo = count; + out = map_surfaces = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); - loadmodel->texinfo = ZG_Malloc(&loadmodel->memgroup, sizeof(mtexinfo_t)*count); - loadmodel->numtextures = count; - loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t*)*count); + mod->texinfo = ZG_Malloc(&mod->memgroup, sizeof(mtexinfo_t)*(count*2+1)); //+1 is 'noshader' for flares. + mod->numtextures = count*2+1; + mod->textures = ZG_Malloc(&mod->memgroup, sizeof(texture_t*)*(count*2+1)); for ( i=0 ; itexinfo[i].texture = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t)); - Q_strncpyz(loadmodel->texinfo[i].texture->name, in->shadername, sizeof(loadmodel->texinfo[i].texture->name)); - loadmodel->textures[i] = loadmodel->texinfo[i].texture; + mod->texinfo[i].texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t)); + Q_strncpyz(mod->texinfo[i].texture->name, in->shadername, sizeof(mod->texinfo[i].texture->name)); + mod->textures[i] = mod->texinfo[i].texture; out->c.flags = LittleLong ( in->surfflags ); out->c.value = LittleLong ( in->contents ); } + for ( i=0, in-=count ; itexinfo[i+count].texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t)); + Q_strncpyz(mod->texinfo[i+count].texture->name, in->shadername, sizeof(mod->texinfo[i+count].texture->name)); + mod->textures[i+count] = mod->texinfo[i+count].texture; + } + + //and for flares, which are not supported at this time. + mod->texinfo[count*2].texture = ZG_Malloc(&mod->memgroup, sizeof(texture_t)); + Q_strncpyz(mod->texinfo[count*2].texture->name, "noshader", sizeof(mod->texinfo[count*2].texture->name)); + mod->textures[count*2] = mod->texinfo[count*2].texture; return true; } -qboolean CModQ3_LoadVertexes (lump_t *l) +qboolean CModQ3_LoadVertexes (model_t *mod, qbyte *mod_base, lump_t *l) { q3dvertex_t *in; vecV_t *out; @@ -2183,7 +2159,7 @@ qboolean CModQ3_LoadVertexes (lump_t *l) vec2_t *lmout, *stout; vec4_t *cout; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CMOD_LoadVertexes: funny lump size\n"); @@ -2197,13 +2173,13 @@ qboolean CModQ3_LoadVertexes (lump_t *l) return false; } - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - stout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*stout)); - lmout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*lmout)); - cout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*cout)); - nout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*nout)); -// sout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*nout)); -// tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*nout)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); + stout = ZG_Malloc(&mod->memgroup, count*sizeof(*stout)); + lmout = ZG_Malloc(&mod->memgroup, count*sizeof(*lmout)); + cout = ZG_Malloc(&mod->memgroup, count*sizeof(*cout)); + nout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout)); +// sout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout)); +// tout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout)); map_verts = out; map_vertstmexcoords = stout; for (i = 0; i < MAXRLIGHTMAPS; i++) @@ -2237,7 +2213,7 @@ qboolean CModQ3_LoadVertexes (lump_t *l) return true; } -qboolean CModRBSP_LoadVertexes (lump_t *l) +qboolean CModRBSP_LoadVertexes (model_t *mod, qbyte *mod_base, lump_t *l) { rbspvertex_t *in; vecV_t *out; @@ -2248,7 +2224,7 @@ qboolean CModRBSP_LoadVertexes (lump_t *l) vec4_t *cout; int sty; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CMOD_LoadVertexes: funny lump size\n"); @@ -2262,13 +2238,13 @@ qboolean CModRBSP_LoadVertexes (lump_t *l) return false; } - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - stout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*stout)); - lmout = ZG_Malloc(&loadmodel->memgroup, MAXRLIGHTMAPS*count*sizeof(*lmout)); - cout = ZG_Malloc(&loadmodel->memgroup, MAXRLIGHTMAPS*count*sizeof(*cout)); - nout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*nout)); -// sout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*sout)); -// tout = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*tout)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); + stout = ZG_Malloc(&mod->memgroup, count*sizeof(*stout)); + lmout = ZG_Malloc(&mod->memgroup, MAXRLIGHTMAPS*count*sizeof(*lmout)); + cout = ZG_Malloc(&mod->memgroup, MAXRLIGHTMAPS*count*sizeof(*cout)); + nout = ZG_Malloc(&mod->memgroup, count*sizeof(*nout)); +// sout = ZG_Malloc(&mod->memgroup, count*sizeof(*sout)); +// tout = ZG_Malloc(&mod->memgroup, count*sizeof(*tout)); map_verts = out; map_vertstmexcoords = stout; for (sty = 0; sty < MAXRLIGHTMAPS; sty++) @@ -2307,7 +2283,7 @@ qboolean CModRBSP_LoadVertexes (lump_t *l) } -qboolean CModQ3_LoadIndexes (lump_t *l) +qboolean CModQ3_LoadIndexes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, count; int *in; @@ -2343,13 +2319,13 @@ qboolean CModQ3_LoadIndexes (lump_t *l) CMod_LoadFaces ================= */ -qboolean CModQ3_LoadFaces (lump_t *l) +qboolean CModQ3_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l) { q3dface_t *in; q3cface_t *out; int i, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -2387,18 +2363,18 @@ qboolean CModQ3_LoadFaces (lump_t *l) } } - loadmodel->numsurfaces = i; + mod->numsurfaces = i; return true; } -qboolean CModRBSP_LoadFaces (lump_t *l) +qboolean CModRBSP_LoadFaces (model_t *mod, qbyte *mod_base, lump_t *l) { rbspface_t *in; q3cface_t *out; int i, count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -2436,7 +2412,7 @@ qboolean CModRBSP_LoadFaces (lump_t *l) } } - loadmodel->numsurfaces = i; + mod->numsurfaces = i; return true; } @@ -2447,7 +2423,7 @@ qboolean CModRBSP_LoadFaces (lump_t *l) Mod_LoadFogs ================= */ -qboolean CModQ3_LoadFogs (lump_t *l) +qboolean CModQ3_LoadFogs (model_t *mod, qbyte *mod_base, lump_t *l) { dfog_t *in; mfog_t *out; @@ -2458,14 +2434,14 @@ qboolean CModQ3_LoadFogs (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); + Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n", mod->name); return false; } count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); - map_fogs = out; - map_numfogs = count; + mod->fogs = out; + mod->numfogs = count; for ( i=0 ; ivisibleSide ); out->visibleplane = visibleside->plane; - out->shader = R_RegisterShader_Lightmap ( in->shader ); - R_BuildDefaultTexnums(&out->shader->defaulttextures, out->shader); + Q_strncpyz(out->shadername, in->shader, sizeof(out->shadername)); out->numplanes = brush->numsides; - out->planes = ZG_Malloc(&loadmodel->memgroup, out->numplanes*sizeof(cplane_t *)); + out->planes = ZG_Malloc(&mod->memgroup, out->numplanes*sizeof(cplane_t *)); for ( j = 0; j < out->numplanes; j++ ) { out->planes[j] = brushsides[j].plane; } - - if (!out->shader->fog_dist) - { - //invalid fog shader, don't use. - out->shader = NULL; - out->numplanes = 0; - } } return true; } -mfog_t *CM_FogForOrigin(vec3_t org) +mfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org) { int i, j; - mfog_t *ret = map_fogs; + mfog_t *ret; float dot; - if (!map_numfogs || !cl.worldmodel || cl.worldmodel->fromgame != fg_quake3) + + if (!wmodel || wmodel->loadstate != MLS_LOADED) return NULL; - for ( i=0 ; ifogs ; inumfogs ; i++, ret++) { - if (!ret->numplanes) + if (!ret->shader) continue; for (j = 0; j < ret->numplanes; j++) { @@ -2668,16 +2637,16 @@ void GL_CreateMeshForPatch (model_t *mod, mesh_t *mesh, int patchwidth, int patc memcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(index_t) ); } -void CModRBSP_BuildSurfMesh(model_t *mod, msurface_t *out, void *cookie) +void CModRBSP_BuildSurfMesh(model_t *mod, msurface_t *out, builddata_t *bd) { - rbspface_t *in = cookie; - int idx = out - loadmodel->surfaces; + rbspface_t *in = (rbspface_t*)(bd+1); + int idx = (out - mod->surfaces) - mod->firstmodelsurface; int sty; in += idx; if (LittleLong(in->facetype) == MST_PATCH) { - GL_CreateMeshForPatch(loadmodel, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); + GL_CreateMeshForPatch(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); } else if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP) { @@ -2741,15 +2710,15 @@ void CModRBSP_BuildSurfMesh(model_t *mod, msurface_t *out, void *cookie) Mod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes); } -void CModQ3_BuildSurfMesh(model_t *mod, msurface_t *out, void *cookie) +void CModQ3_BuildSurfMesh(model_t *mod, msurface_t *out, builddata_t *bd) { - q3dface_t *in = cookie; - int idx = out - loadmodel->surfaces; + q3dface_t *in = (q3dface_t*)(bd+1); + int idx = (out - mod->surfaces) - mod->firstmodelsurface; in += idx; if (LittleLong(in->facetype) == MST_PATCH) { - GL_CreateMeshForPatch(loadmodel, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); + GL_CreateMeshForPatch(mod, out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); } else if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP) { @@ -2810,12 +2779,14 @@ void CModQ3_BuildSurfMesh(model_t *mod, msurface_t *out, void *cookie) Mod_NormaliseTextureVectors(out->mesh->normals_array, out->mesh->snormals_array, out->mesh->tnormals_array, out->mesh->numvertexes); } -qboolean CModQ3_LoadRFaces (lump_t *l) +qboolean CModQ3_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l) { + extern cvar_t r_vertexlight; q3dface_t *in; msurface_t *out; mplane_t *pl; + int facetype; int count; int surfnum; @@ -2827,21 +2798,28 @@ qboolean CModQ3_LoadRFaces (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); + Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",mod->name); return false; } count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - pl = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*pl));//create a new array of planes for speed. - mesh = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*mesh)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); + pl = ZG_Malloc(&mod->memgroup, count*sizeof(*pl));//create a new array of planes for speed. + mesh = ZG_Malloc(&mod->memgroup, count*sizeof(*mesh)); - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; + mod->surfaces = out; + mod->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++) { out->plane = pl; - out->texinfo = loadmodel->texinfo + LittleLong(in->shadernum); + + facetype = LittleLong(in->facetype); + out->texinfo = mod->texinfo + LittleLong(in->shadernum); + if (in->facetype == MST_FLARE) + out->texinfo = mod->texinfo + mod->numtexinfo*2; + else if (in->facetype == MST_TRIANGLE_SOUP || r_vertexlight.value) + out->texinfo += mod->numtexinfo; //soup/vertex light uses a different version of the same shader (with all the lightmaps collapsed) + out->lightmaptexturenums[0] = LittleLong(in->lightmapnum); out->light_s[0] = LittleLong(in->lightmap_x); out->light_t[0] = LittleLong(in->lightmap_y); @@ -2855,8 +2833,8 @@ qboolean CModQ3_LoadRFaces (lump_t *l) out->extents[1] = (LittleLong(in->lightmap_height)-1)<<4; out->samples=NULL; - if (loadmodel->lightmaps.count < out->lightmaptexturenums[0]+1) - loadmodel->lightmaps.count = out->lightmaptexturenums[0]+1; + if (mod->lightmaps.count < out->lightmaptexturenums[0]+1) + mod->lightmaps.count = out->lightmaptexturenums[0]+1; fv = LittleLong(in->firstvertex); { @@ -2872,26 +2850,13 @@ qboolean CModQ3_LoadRFaces (lump_t *l) //q3dm10's thingie is 0 out->flags |= SURF_DRAWALPHA; - if (loadmodel->texinfo[LittleLong(in->shadernum)].flags & TI_SKY) + if (mod->texinfo[LittleLong(in->shadernum)].flags & TI_SKY) out->flags |= SURF_DRAWSKY; - if (!out->texinfo->texture->shader) - { - extern cvar_t r_vertexlight; - if (LittleLong(in->facetype) == MST_FLARE) - out->texinfo->texture->shader = R_RegisterShader_Flare (out->texinfo->texture->name); - else if (LittleLong(in->facetype) == MST_TRIANGLE_SOUP || r_vertexlight.value) - out->texinfo->texture->shader = R_RegisterShader_Vertex (out->texinfo->texture->name); - else - out->texinfo->texture->shader = R_RegisterShader_Lightmap(out->texinfo->texture->name); - - R_BuildDefaultTexnums(&out->texinfo->texture->shader->defaulttextures, out->texinfo->texture->shader); - } - - if (LittleLong(in->fognum) == -1 || !map_numfogs) + if (LittleLong(in->fognum) == -1 || !mod->numfogs) out->fog = NULL; else - out->fog = map_fogs + LittleLong(in->fognum); + out->fog = mod->fogs + LittleLong(in->fognum); if (map_surfaces[LittleLong(in->shadernum)].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP)) { @@ -2899,12 +2864,12 @@ qboolean CModQ3_LoadRFaces (lump_t *l) out->mesh->numindexes = 0; out->mesh->numvertexes = 0; } - else if (LittleLong(in->facetype) == MST_PATCH) + else if (facetype == MST_PATCH) { out->mesh = &mesh[surfnum]; GL_SizePatch(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); } - else if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP) + else if (facetype == MST_PLANAR || facetype == MST_TRIANGLE_SOUP) { out->mesh = &mesh[surfnum]; out->mesh->numindexes = LittleLong(in->num_indexes); @@ -2921,15 +2886,17 @@ qboolean CModQ3_LoadRFaces (lump_t *l) } } - Mod_SortShaders(); + Mod_SortShaders(mod); return true; } -qboolean CModRBSP_LoadRFaces (lump_t *l) +qboolean CModRBSP_LoadRFaces (model_t *mod, qbyte *mod_base, lump_t *l) { + extern cvar_t r_vertexlight; rbspface_t *in; msurface_t *out; mplane_t *pl; + int facetype; int count; int surfnum; @@ -2943,22 +2910,26 @@ qboolean CModRBSP_LoadRFaces (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); + Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",mod->name); return false; } count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - pl = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*pl));//create a new array of planes for speed. - mesh = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*mesh)); + out = ZG_Malloc(&mod->memgroup, count*sizeof(*out)); + pl = ZG_Malloc(&mod->memgroup, count*sizeof(*pl));//create a new array of planes for speed. + mesh = ZG_Malloc(&mod->memgroup, count*sizeof(*mesh)); - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; + mod->surfaces = out; + mod->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++) { out->plane = pl; - out->texinfo = loadmodel->texinfo + LittleLong(in->shadernum); - in->facetype = LittleLong(in->facetype); + facetype = LittleLong(in->facetype); + out->texinfo = mod->texinfo + LittleLong(in->shadernum); + if (facetype == MST_FLARE) + out->texinfo = mod->texinfo + mod->numtexinfo*2; + else if (facetype == MST_TRIANGLE_SOUP || r_vertexlight.value) + out->texinfo += mod->numtexinfo; //soup/vertex light uses a different version of the same shader (with all the lightmaps collapsed) for (j = 0; j < 4 && j < MAXRLIGHTMAPS; j++) { out->lightmaptexturenums[j] = LittleLong(in->lightmapnum[j]); @@ -2966,8 +2937,8 @@ qboolean CModRBSP_LoadRFaces (lump_t *l) out->light_t[j] = LittleLong(in->lightmap_offs[1][j]); out->styles[j] = in->lm_styles[j]; - if (loadmodel->lightmaps.count < out->lightmaptexturenums[j]+1) - loadmodel->lightmaps.count = out->lightmaptexturenums[j]+1; + if (mod->lightmaps.count < out->lightmaptexturenums[j]+1) + mod->lightmaps.count = out->lightmaptexturenums[j]+1; } out->extents[0] = (LittleLong(in->lightmap_width)-1)<<4; out->extents[1] = (LittleLong(in->lightmap_height)-1)<<4; @@ -2987,26 +2958,13 @@ qboolean CModRBSP_LoadRFaces (lump_t *l) //q3dm10's thingie is 0 out->flags |= SURF_DRAWALPHA; - if (loadmodel->texinfo[in->shadernum].flags & TI_SKY) + if (mod->texinfo[in->shadernum].flags & TI_SKY) out->flags |= SURF_DRAWSKY; - if (!out->texinfo->texture->shader) - { - extern cvar_t r_vertexlight; - if (in->facetype == MST_FLARE) - out->texinfo->texture->shader = R_RegisterShader_Flare (out->texinfo->texture->name); - else if (in->facetype == MST_TRIANGLE_SOUP || r_vertexlight.value) - out->texinfo->texture->shader = R_RegisterShader_Vertex (out->texinfo->texture->name); - else - out->texinfo->texture->shader = R_RegisterShader_Lightmap(out->texinfo->texture->name); - - R_BuildDefaultTexnums(&out->texinfo->texture->shader->defaulttextures, out->texinfo->texture->shader); - } - - if (in->fognum < 0 || in->fognum >= map_numfogs || !map_fogs[in->fognum].shader) + if (in->fognum < 0 || in->fognum >= mod->numfogs) out->fog = NULL; else - out->fog = map_fogs + in->fognum; + out->fog = mod->fogs + in->fognum; if (map_surfaces[LittleLong(in->shadernum)].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP)) { @@ -3014,12 +2972,12 @@ qboolean CModRBSP_LoadRFaces (lump_t *l) out->mesh->numindexes = 0; out->mesh->numvertexes = 0; } - else if (LittleLong(in->facetype) == MST_PATCH) + else if (facetype == MST_PATCH) { out->mesh = &mesh[surfnum]; GL_SizePatch(out->mesh, LittleLong(in->patchwidth), LittleLong(in->patchheight), LittleLong(in->num_vertices), LittleLong(in->firstvertex)); } - else if (LittleLong(in->facetype) == MST_PLANAR || LittleLong(in->facetype) == MST_TRIANGLE_SOUP) + else if (facetype == MST_PLANAR || facetype == MST_TRIANGLE_SOUP) { out->mesh = &mesh[surfnum]; out->mesh->numindexes = LittleLong(in->num_indexes); @@ -3036,18 +2994,18 @@ qboolean CModRBSP_LoadRFaces (lump_t *l) } } - Mod_SortShaders(); + Mod_SortShaders(mod); return true; } #endif -qboolean CModQ3_LoadLeafFaces (lump_t *l) +qboolean CModQ3_LoadLeafFaces (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j, count; int *in; int *out; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3081,7 +3039,7 @@ qboolean CModQ3_LoadLeafFaces (lump_t *l) return true; } -qboolean CModQ3_LoadNodes (lump_t *l) +qboolean CModQ3_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j, count, p; q3dnode_t *in; @@ -3142,14 +3100,14 @@ qboolean CModQ3_LoadNodes (lump_t *l) return true; } -qboolean CModQ3_LoadBrushes (lump_t *l) +qboolean CModQ3_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l) { q3dbrush_t *in; q2cbrush_t *out; int i, count; int shaderref; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3163,7 +3121,7 @@ qboolean CModQ3_LoadBrushes (lump_t *l) return false; } - map_brushes = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * (count+1)); + map_brushes = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1)); out = map_brushes; @@ -3180,7 +3138,7 @@ qboolean CModQ3_LoadBrushes (lump_t *l) return true; } -qboolean CModQ3_LoadLeafs (lump_t *l) +qboolean CModQ3_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; mleaf_t *out; @@ -3188,7 +3146,7 @@ qboolean CModQ3_LoadLeafs (lump_t *l) int count; q2cbrush_t *brush; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3209,11 +3167,11 @@ qboolean CModQ3_LoadLeafs (lump_t *l) return false; } - out = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * (count+1)); + out = ZG_Malloc(&mod->memgroup, sizeof(*out) * (count+1)); numclusters = 0; - loadmodel->leafs = out; - loadmodel->numleafs = count; + mod->leafs = out; + mod->numleafs = count; for ( i=0 ; ifirstleafbrush = LittleLong(in->firstleafbrush); out->numleafbrushes = LittleLong(in->num_leafbrushes); - out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstleafsurface); + out->firstmarksurface = mod->marksurfaces + LittleLong(in->firstleafsurface); out->nummarksurfaces = LittleLong(in->num_leafsurfaces); if (out->minmaxs[0] > out->minmaxs[3+0] || out->minmaxs[1] > out->minmaxs[3+1] || @@ -3254,7 +3212,7 @@ qboolean CModQ3_LoadLeafs (lump_t *l) return true; } -qboolean CModQ3_LoadPlanes (lump_t *l) +qboolean CModQ3_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j; mplane_t *out; @@ -3292,14 +3250,14 @@ qboolean CModQ3_LoadPlanes (lump_t *l) return true; } -qboolean CModQ3_LoadLeafBrushes (lump_t *l) +qboolean CModQ3_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l) { int i; q2cbrush_t **out; int *in; int count; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3328,7 +3286,7 @@ qboolean CModQ3_LoadLeafBrushes (lump_t *l) return true; } -qboolean CModQ3_LoadBrushSides (lump_t *l) +qboolean CModQ3_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; q2cbrushside_t *out; @@ -3336,7 +3294,7 @@ qboolean CModQ3_LoadBrushSides (lump_t *l) int count; int num; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3351,15 +3309,15 @@ qboolean CModQ3_LoadBrushSides (lump_t *l) return false; } - out = map_brushsides = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * count); + out = map_brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count); numbrushsides = count; for ( i=0 ; iplanenum); - out->plane = &loadmodel->planes[num]; + out->plane = &mod->planes[num]; j = LittleLong (in->texinfo); - if (j >= numtexinfo) + if (j >= mod->numtexinfo) { Con_Printf (CON_ERROR "Bad brushside texinfo\n"); return false; @@ -3370,7 +3328,7 @@ qboolean CModQ3_LoadBrushSides (lump_t *l) return true; } -qboolean CModRBSP_LoadBrushSides (lump_t *l) +qboolean CModRBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l) { int i, j; q2cbrushside_t *out; @@ -3378,7 +3336,7 @@ qboolean CModRBSP_LoadBrushSides (lump_t *l) int count; int num; - in = (void *)(cmod_base + l->fileofs); + in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size\n"); @@ -3393,15 +3351,15 @@ qboolean CModRBSP_LoadBrushSides (lump_t *l) return false; } - out = map_brushsides = ZG_Malloc(&loadmodel->memgroup, sizeof(*out) * count); + out = map_brushsides = ZG_Malloc(&mod->memgroup, sizeof(*out) * count); numbrushsides = count; for ( i=0 ; iplanenum); - out->plane = &loadmodel->planes[num]; + out->plane = &mod->planes[num]; j = LittleLong (in->texinfo); - if (j >= numtexinfo) + if (j >= mod->numtexinfo) { Con_Printf (CON_ERROR "Bad brushside texinfo\n"); return false; @@ -3412,19 +3370,19 @@ qboolean CModRBSP_LoadBrushSides (lump_t *l) return true; } -qboolean CModQ3_LoadVisibility (lump_t *l) +qboolean CModQ3_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l) { if (l->filelen == 0) { int i; numclusters = 0; - for (i = 0; i < loadmodel->numleafs; i++) - if (numclusters <= loadmodel->leafs[i].cluster) - numclusters = loadmodel->leafs[i].cluster+1; + for (i = 0; i < mod->numleafs; i++) + if (numclusters <= mod->leafs[i].cluster) + numclusters = mod->leafs[i].cluster+1; numclusters++; - map_q3pvs = ZG_Malloc(&loadmodel->memgroup, sizeof(*map_q3pvs) + (numclusters+7)/8 * numclusters); + map_q3pvs = ZG_Malloc(&mod->memgroup, sizeof(*map_q3pvs) + (numclusters+7)/8 * numclusters); memset (map_q3pvs, 0xff, sizeof(*map_q3pvs) + (numclusters+7)/8 * numclusters); map_q3pvs->numclusters = numclusters; numvisibility = 0; @@ -3434,20 +3392,20 @@ qboolean CModQ3_LoadVisibility (lump_t *l) { numvisibility = l->filelen; - map_q3pvs = ZG_Malloc(&loadmodel->memgroup, l->filelen); - loadmodel->vis = (q2dvis_t *)map_q3pvs; - memcpy (map_q3pvs, cmod_base + l->fileofs, l->filelen); + map_q3pvs = ZG_Malloc(&mod->memgroup, l->filelen); + mod->vis = (q2dvis_t *)map_q3pvs; + memcpy (map_q3pvs, mod_base + l->fileofs, l->filelen); numclusters = map_q3pvs->numclusters = LittleLong (map_q3pvs->numclusters); map_q3pvs->rowsize = LittleLong (map_q3pvs->rowsize); } - loadmodel->numclusters = numclusters; + mod->numclusters = numclusters; return true; } #ifndef SERVERONLY -void CModQ3_LoadLighting (lump_t *l) +void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l) { qbyte *in = mod_base + l->fileofs; qbyte *out; @@ -3502,7 +3460,7 @@ void CModQ3_LoadLighting (lump_t *l) } } -qboolean CModQ3_LoadLightgrid (lump_t *l) +qboolean CModQ3_LoadLightgrid (model_t *loadmodel, qbyte *mod_base, lump_t *l) { dq3gridlight_t *in; dq3gridlight_t *out; @@ -3528,7 +3486,7 @@ qboolean CModQ3_LoadLightgrid (lump_t *l) return true; } -qboolean CModRBSP_LoadLightgrid (lump_t *elements, lump_t *indexes) +qboolean CModRBSP_LoadLightgrid (model_t *loadmodel, qbyte *mod_base, lump_t *elements, lump_t *indexes) { unsigned short *iin; rbspgridlight_t *ein; @@ -3569,20 +3527,21 @@ qboolean CModRBSP_LoadLightgrid (lump_t *elements, lump_t *indexes) return true; } #endif - +#endif #ifndef SERVERONLY qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out); int CM_GetQ2Palette (void) { char *f; - FS_LoadFile("pics/colormap.pcx", (void**)&f); + size_t sz; + sz = FS_LoadFile("pics/colormap.pcx", (void**)&f); if (!f) { Con_Printf (CON_WARNING "Couldn't find pics/colormap.pcx\n"); return -1; } - if (!ReadPCXPalette(f, com_filesize, d_q28to24table)) + if (!ReadPCXPalette(f, sz, d_q28to24table)) { Con_Printf (CON_WARNING "Couldn't read pics/colormap.pcx\n"); FS_FreeFile(f); @@ -3667,7 +3626,7 @@ void CM_OpenAllPortals(char *ents) //this is a compleate hack. About as compleat #ifndef CLIENTONLY -void CMQ3_CalcPHS (void) +void CMQ3_CalcPHS (model_t *mod) { int rowbytes, rowwords; int i, j, k, l, index; @@ -3680,7 +3639,7 @@ void CMQ3_CalcPHS (void) Con_DPrintf ("Building PHS...\n"); - map_q3phs = ZG_Malloc(&loadmodel->memgroup, sizeof(*map_q3phs) + map_q3pvs->rowsize * map_q3pvs->numclusters); + map_q3phs = ZG_Malloc(&mod->memgroup, sizeof(*map_q3phs) + map_q3pvs->rowsize * map_q3pvs->numclusters); rowwords = map_q3pvs->rowsize / sizeof(int); rowbytes = map_q3pvs->rowsize; @@ -3695,7 +3654,7 @@ void CMQ3_CalcPHS (void) vcount = 0; for (i=0 ; i>3] & (1<<(j&7)) ) @@ -3865,7 +3824,7 @@ CM_LoadMap Loads in the map and all submodels ================== */ -cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *checksum) +static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboolean clientload, unsigned *checksum) { unsigned *buf; int i; @@ -3873,27 +3832,32 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c int length; static unsigned last_checksum; qboolean noerrors = true; - model_t *im = loadmodel; + model_t *wmod = mod; + char loadname[32]; + qbyte *mod_base = (qbyte *)filein; void (*buildmeshes)(model_t *mod, msurface_t *surf, void *cookie) = NULL; - void *buildcookie = NULL; + qbyte *facedata = NULL; + unsigned int facesize = 0; + + COM_FileBase (mod->name, loadname, sizeof(loadname)); // free old stuff numcmodels = 0; numvisibility = 0; box_planes = NULL; //so its rebuilt - loadmodel->type = mod_brush; + mod->type = mod_brush; - if (!name || !name[0]) + if (!mod->name[0]) { - map_cmodels = ZG_Malloc(&loadmodel->memgroup, 1 * sizeof(*map_cmodels)); - loadmodel->leafs = ZG_Malloc(&loadmodel->memgroup, 1 * sizeof(*loadmodel->leafs)); + map_cmodels = ZG_Malloc(&mod->memgroup, 1 * sizeof(*map_cmodels)); + mod->leafs = ZG_Malloc(&mod->memgroup, 1 * sizeof(*mod->leafs)); numcmodels = 1; numclusters = 1; numareas = 1; *checksum = 0; - map_cmodels[0].headnode = (mnode_t*)loadmodel->leafs; //directly start with the empty leaf + map_cmodels[0].headnode = (mnode_t*)mod->leafs; //directly start with the empty leaf return &map_cmodels[0]; // cinematic servers won't have anything at all } @@ -3901,10 +3865,10 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c // load the file // buf = (unsigned *)filein; - length = com_filesize; + length = filelen; if (!buf) { - Con_Printf (CON_ERROR "Couldn't load %s\n", name); + Con_Printf (CON_ERROR "Couldn't load %s\n", mod->name); return NULL; } @@ -3915,26 +3879,24 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c header.ident = LittleLong(header.ident); header.version = LittleLong(header.version); - cmod_base = mod_base = (qbyte *)buf; - if (header.ident == (('F'<<0)+('B'<<8)+('S'<<16)+('P'<<24))) { - loadmodel->lightmaps.width = 512; - loadmodel->lightmaps.height = 512; + mod->lightmaps.width = 512; + mod->lightmaps.height = 512; } else { - loadmodel->lightmaps.width = 128; - loadmodel->lightmaps.height = 128; + mod->lightmaps.width = 128; + mod->lightmaps.height = 128; } - ClearBounds(loadmodel->mins, loadmodel->maxs); + ClearBounds(mod->mins, mod->maxs); switch(header.version) { default: Con_Printf (CON_ERROR "Quake 2 or Quake 3 based BSP with unknown header (%s: %i should be %i or %i)\n" - , name, header.version, Q2BSPVERSION, Q3BSPVERSION); + , mod->name, header.version, Q2BSPVERSION, Q3BSPVERSION); return NULL; break; #ifdef Q3BSPS @@ -3942,7 +3904,7 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c case Q3BSPVERSION+1: //rtcw case Q3BSPVERSION: mapisq3 = true; - loadmodel->fromgame = fg_quake3; + mod->fromgame = fg_quake3; for (i=0 ; i com_filesize) + if (header.lumps[i].filelen && header.lumps[i].fileofs + header.lumps[i].filelen > filelen) { - Con_Printf (CON_ERROR "WARNING: q3bsp %s truncated (lump %i, %i+%i > %i)\n", name, i, header.lumps[i].fileofs, header.lumps[i].filelen, com_filesize); - header.lumps[i].filelen = com_filesize - header.lumps[i].fileofs; + Con_Printf (CON_ERROR "WARNING: q3bsp %s truncated (lump %i, %i+%i > %i)\n", mod->name, i, header.lumps[i].fileofs, header.lumps[i].filelen, filelen); + header.lumps[i].filelen = filelen - header.lumps[i].fileofs; if (header.lumps[i].filelen < 0) header.lumps[i].filelen = 0; } @@ -3966,114 +3928,109 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c } /* #ifndef SERVERONLY - GLMod_LoadVertexes (&header.lumps[Q3LUMP_DRAWVERTS]); -// GLMod_LoadEdges (&header.lumps[Q3LUMP_EDGES]); -// GLMod_LoadSurfedges (&header.lumps[Q3LUMP_SURFEDGES]); - GLMod_LoadLighting (&header.lumps[Q3LUMP_LIGHTMAPS]); + GLMod_LoadVertexes (mod, cmod_base, &header.lumps[Q3LUMP_DRAWVERTS]); +// GLMod_LoadEdges (mod, cmod_base, &header.lumps[Q3LUMP_EDGES]); +// GLMod_LoadSurfedges (mod, cmod_base, &header.lumps[Q3LUMP_SURFEDGES]); + GLMod_LoadLighting (mod, cmod_base, &header.lumps[Q3LUMP_LIGHTMAPS]); #endif - CModQ3_LoadShaders (&header.lumps[Q3LUMP_SHADERS]); - CModQ3_LoadPlanes (&header.lumps[Q3LUMP_PLANES]); - CModQ3_LoadLeafBrushes (&header.lumps[Q3LUMP_LEAFBRUSHES]); - CModQ3_LoadBrushes (&header.lumps[Q3LUMP_BRUSHES]); - CModQ3_LoadBrushSides (&header.lumps[Q3LUMP_BRUSHSIDES]); + CModQ3_LoadShaders (mod, cmod_base, &header.lumps[Q3LUMP_SHADERS]); + CModQ3_LoadPlanes (mod, cmod_base, &header.lumps[Q3LUMP_PLANES]); + CModQ3_LoadLeafBrushes (mod, cmod_base, &header.lumps[Q3LUMP_LEAFBRUSHES]); + CModQ3_LoadBrushes (mod, cmod_base, &header.lumps[Q3LUMP_BRUSHES]); + CModQ3_LoadBrushSides (mod, cmod_base, &header.lumps[Q3LUMP_BRUSHSIDES]); #ifndef SERVERONLY - CMod_LoadTexInfo (&header.lumps[Q3LUMP_SHADERS]); - CMod_LoadFaces (&header.lumps[Q3LUMP_SURFACES]); -// GLMod_LoadMarksurfaces (&header.lumps[Q3LUMP_LEAFFACES]); + CMod_LoadTexInfo (mod, cmod_base, &header.lumps[Q3LUMP_SHADERS]); + CMod_LoadFaces (mod, cmod_base, &header.lumps[Q3LUMP_SURFACES]); +// GLMod_LoadMarksurfaces (mod, cmod_base, &header.lumps[Q3LUMP_LEAFFACES]); #endif - CMod_LoadVisibility (&header.lumps[Q3LUMP_VISIBILITY]); - CModQ3_LoadSubmodels (&header.lumps[Q3LUMP_MODELS]); - CModQ3_LoadLeafs (&header.lumps[Q3LUMP_LEAFS]); - CModQ3_LoadNodes (&header.lumps[Q3LUMP_NODES]); -// CMod_LoadAreas (&header.lumps[Q3LUMP_AREAS]); -// CMod_LoadAreaPortals (&header.lumps[Q3LUMP_AREAPORTALS]); - CMod_LoadEntityString (&header.lumps[Q3LUMP_ENTITIES]); + CMod_LoadVisibility (mod, cmod_base, &header.lumps[Q3LUMP_VISIBILITY]); + CModQ3_LoadSubmodels (mod, cmod_base, &header.lumps[Q3LUMP_MODELS]); + CModQ3_LoadLeafs (mod, cmod_base, &header.lumps[Q3LUMP_LEAFS]); + CModQ3_LoadNodes (mod, cmod_base, &header.lumps[Q3LUMP_NODES]); +// CMod_LoadAreas (mod, cmod_base, &header.lumps[Q3LUMP_AREAS]); +// CMod_LoadAreaPortals (mod, cmod_base, &header.lumps[Q3LUMP_AREAPORTALS]); + CMod_LoadEntityString (mod, cmod_base, &header.lumps[Q3LUMP_ENTITIES]); */ map_faces = NULL; map_leaffaces = NULL; - Q1BSPX_Setup(loadmodel, mod_base, com_filesize, header.lumps, Q3LUMPS_TOTAL); + Q1BSPX_Setup(mod, mod_base, filelen, header.lumps, Q3LUMPS_TOTAL); mapisq3 = true; - noerrors = noerrors && CModQ3_LoadShaders (&header.lumps[Q3LUMP_SHADERS]); - noerrors = noerrors && CModQ3_LoadPlanes (&header.lumps[Q3LUMP_PLANES]); + noerrors = noerrors && CModQ3_LoadShaders (mod, mod_base, &header.lumps[Q3LUMP_SHADERS]); + noerrors = noerrors && CModQ3_LoadPlanes (mod, mod_base, &header.lumps[Q3LUMP_PLANES]); if (header.version == 1) { - noerrors = noerrors && CModRBSP_LoadBrushSides (&header.lumps[Q3LUMP_BRUSHSIDES]); - noerrors = noerrors && CModRBSP_LoadVertexes (&header.lumps[Q3LUMP_DRAWVERTS]); + noerrors = noerrors && CModRBSP_LoadBrushSides (mod, mod_base, &header.lumps[Q3LUMP_BRUSHSIDES]); + noerrors = noerrors && CModRBSP_LoadVertexes (mod, mod_base, &header.lumps[Q3LUMP_DRAWVERTS]); } else { - noerrors = noerrors && CModQ3_LoadBrushSides (&header.lumps[Q3LUMP_BRUSHSIDES]); - noerrors = noerrors && CModQ3_LoadVertexes (&header.lumps[Q3LUMP_DRAWVERTS]); + noerrors = noerrors && CModQ3_LoadBrushSides (mod, mod_base, &header.lumps[Q3LUMP_BRUSHSIDES]); + noerrors = noerrors && CModQ3_LoadVertexes (mod, mod_base, &header.lumps[Q3LUMP_DRAWVERTS]); } - noerrors = noerrors && CModQ3_LoadBrushes (&header.lumps[Q3LUMP_BRUSHES]); - noerrors = noerrors && CModQ3_LoadLeafBrushes (&header.lumps[Q3LUMP_LEAFBRUSHES]); + noerrors = noerrors && CModQ3_LoadBrushes (mod, mod_base, &header.lumps[Q3LUMP_BRUSHES]); + noerrors = noerrors && CModQ3_LoadLeafBrushes (mod, mod_base, &header.lumps[Q3LUMP_LEAFBRUSHES]); if (header.version == 1) - noerrors = noerrors && CModRBSP_LoadFaces (&header.lumps[Q3LUMP_SURFACES]); + noerrors = noerrors && CModRBSP_LoadFaces (mod, mod_base, &header.lumps[Q3LUMP_SURFACES]); else - noerrors = noerrors && CModQ3_LoadFaces (&header.lumps[Q3LUMP_SURFACES]); + noerrors = noerrors && CModQ3_LoadFaces (mod, mod_base, &header.lumps[Q3LUMP_SURFACES]); #ifndef SERVERONLY if (qrenderer != QR_NONE) { if (header.version == 1) - noerrors = noerrors && CModRBSP_LoadLightgrid (&header.lumps[Q3LUMP_LIGHTGRID], &header.lumps[RBSPLUMP_LIGHTINDEXES]); + noerrors = noerrors && CModRBSP_LoadLightgrid (mod, mod_base, &header.lumps[Q3LUMP_LIGHTGRID], &header.lumps[RBSPLUMP_LIGHTINDEXES]); else - noerrors = noerrors && CModQ3_LoadLightgrid (&header.lumps[Q3LUMP_LIGHTGRID]); - noerrors = noerrors && CModQ3_LoadIndexes (&header.lumps[Q3LUMP_DRAWINDEXES]); + noerrors = noerrors && CModQ3_LoadLightgrid (mod, mod_base, &header.lumps[Q3LUMP_LIGHTGRID]); + noerrors = noerrors && CModQ3_LoadIndexes (mod, mod_base, &header.lumps[Q3LUMP_DRAWINDEXES]); if (header.version != Q3BSPVERSION+1) - noerrors = noerrors && CModQ3_LoadFogs (&header.lumps[Q3LUMP_FOGS]); + noerrors = noerrors && CModQ3_LoadFogs (mod, mod_base, &header.lumps[Q3LUMP_FOGS]); else - map_numfogs = 0; + mod->numfogs = 0; - buildcookie = (void *)(mod_base + header.lumps[Q3LUMP_SURFACES].fileofs); + facedata = (void *)(mod_base + header.lumps[Q3LUMP_SURFACES].fileofs); if (header.version == 1) { - noerrors = noerrors && CModRBSP_LoadRFaces (&header.lumps[Q3LUMP_SURFACES]); + noerrors = noerrors && CModRBSP_LoadRFaces (mod, mod_base, &header.lumps[Q3LUMP_SURFACES]); buildmeshes = CModRBSP_BuildSurfMesh; - loadmodel->lightmaps.surfstyles = 4; + facesize = sizeof(rbspface_t); + mod->lightmaps.surfstyles = 4; } else { - noerrors = noerrors && CModQ3_LoadRFaces (&header.lumps[Q3LUMP_SURFACES]); + noerrors = noerrors && CModQ3_LoadRFaces (mod, mod_base, &header.lumps[Q3LUMP_SURFACES]); buildmeshes = CModQ3_BuildSurfMesh; - loadmodel->lightmaps.surfstyles = 1; + facesize = sizeof(q3dface_t); + mod->lightmaps.surfstyles = 1; } - noerrors = noerrors && CModQ3_LoadMarksurfaces (&header.lumps[Q3LUMP_LEAFSURFACES]); //fixme: duplicated loading. + noerrors = noerrors && CModQ3_LoadMarksurfaces (mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]); //fixme: duplicated loading. - /*make sure all textures have a shader*/ - for (i=0; inumtextures; i++) + if (noerrors && mod->fromgame == fg_quake3) { - if (!loadmodel->textures[i]->shader) - loadmodel->textures[i]->shader = R_RegisterShader_Lightmap(loadmodel->textures[i]->name); - } + i = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (mod->lightmaps.width*mod->lightmaps.height*3); + mod->lightmaps.deluxemapping = !(i&1); + mod->lightmaps.count = max(mod->lightmaps.count, i); - if (noerrors && loadmodel->fromgame == fg_quake3) - { - i = header.lumps[Q3LUMP_LIGHTMAPS].filelen / (loadmodel->lightmaps.width*loadmodel->lightmaps.height*3); - loadmodel->lightmaps.deluxemapping = !(i&1); - loadmodel->lightmaps.count = max(loadmodel->lightmaps.count, i); - - for (i = 0; i < loadmodel->numsurfaces && loadmodel->lightmaps.deluxemapping; i++) + for (i = 0; i < mod->numsurfaces && mod->lightmaps.deluxemapping; i++) { - if (loadmodel->surfaces[i].lightmaptexturenums[0] >= 0 && (loadmodel->surfaces[i].lightmaptexturenums[0] & 1)) - loadmodel->lightmaps.deluxemapping = false; + if (mod->surfaces[i].lightmaptexturenums[0] >= 0 && (mod->surfaces[i].lightmaptexturenums[0] & 1)) + mod->lightmaps.deluxemapping = false; } } if (noerrors) - CModQ3_LoadLighting (&header.lumps[Q3LUMP_LIGHTMAPS]); //fixme: duplicated loading. + CModQ3_LoadLighting (mod, mod_base, &header.lumps[Q3LUMP_LIGHTMAPS]); //fixme: duplicated loading. } #endif - noerrors = noerrors && CModQ3_LoadLeafFaces (&header.lumps[Q3LUMP_LEAFSURFACES]); - noerrors = noerrors && CModQ3_LoadLeafs (&header.lumps[Q3LUMP_LEAFS]); - noerrors = noerrors && CModQ3_LoadNodes (&header.lumps[Q3LUMP_NODES]); - noerrors = noerrors && CModQ3_LoadSubmodels (&header.lumps[Q3LUMP_MODELS]); - noerrors = noerrors && CModQ3_LoadVisibility (&header.lumps[Q3LUMP_VISIBILITY]); + noerrors = noerrors && CModQ3_LoadLeafFaces (mod, mod_base, &header.lumps[Q3LUMP_LEAFSURFACES]); + noerrors = noerrors && CModQ3_LoadLeafs (mod, mod_base, &header.lumps[Q3LUMP_LEAFS]); + noerrors = noerrors && CModQ3_LoadNodes (mod, mod_base, &header.lumps[Q3LUMP_NODES]); + noerrors = noerrors && CModQ3_LoadSubmodels (mod, mod_base, &header.lumps[Q3LUMP_MODELS]); + noerrors = noerrors && CModQ3_LoadVisibility (mod, mod_base, &header.lumps[Q3LUMP_VISIBILITY]); if (noerrors) - CMod_LoadEntityString (&header.lumps[Q3LUMP_ENTITIES]); + CMod_LoadEntityString (mod, mod_base, &header.lumps[Q3LUMP_ENTITIES]); if (!noerrors) { @@ -4085,28 +4042,28 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c } #ifndef CLIENTONLY - loadmodel->funcs.FatPVS = Q2BSP_FatPVS; - loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; - loadmodel->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; + mod->funcs.FatPVS = Q2BSP_FatPVS; + mod->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + mod->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; #endif - loadmodel->funcs.ClusterPVS = CM_ClusterPVS; - loadmodel->funcs.ClusterForPoint = CM_PointCluster; + mod->funcs.ClusterPVS = CM_ClusterPVS; + mod->funcs.ClusterForPoint = CM_PointCluster; #ifndef SERVERONLY - loadmodel->funcs.LightPointValues = GLQ3_LightGrid; - loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode; - loadmodel->funcs.MarkLights = Q2BSP_MarkLights; + mod->funcs.LightPointValues = GLQ3_LightGrid; + mod->funcs.StainNode = GLR_Q2BSP_StainNode; + mod->funcs.MarkLights = Q2BSP_MarkLights; #endif - loadmodel->funcs.PointContents = Q2BSP_PointContents; - loadmodel->funcs.NativeTrace = CM_NativeTrace; - loadmodel->funcs.NativeContents = CM_NativeContents; + mod->funcs.PointContents = Q2BSP_PointContents; + mod->funcs.NativeTrace = CM_NativeTrace; + mod->funcs.NativeContents = CM_NativeContents; #ifndef SERVERONLY //light grid info - if (loadmodel->lightgrid) + if (mod->lightgrid) { float maxs; - q3lightgridinfo_t *lg = loadmodel->lightgrid; + q3lightgridinfo_t *lg = mod->lightgrid; if ( lg->gridSize[0] < 1 || lg->gridSize[1] < 1 || lg->gridSize[2] < 1 ) { lg->gridSize[0] = 64; @@ -4125,14 +4082,14 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c } #endif - if (!CM_CreatePatchesForLeafs ()) //for clipping + if (!CM_CreatePatchesForLeafs (mod)) //for clipping { BZ_Free(map_faces); BZ_Free(map_leaffaces); return NULL; } #ifndef CLIENTONLY - CMQ3_CalcPHS(); + CMQ3_CalcPHS(mod); #endif // BZ_Free(map_verts); BZ_Free(map_faces); @@ -4141,14 +4098,14 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c #endif case Q2BSPVERSION: mapisq3 = false; - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; for (i=0 ; ifuncs.FatPVS = Q2BSP_FatPVS; - loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; - loadmodel->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; + mod->funcs.FatPVS = Q2BSP_FatPVS; + mod->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + mod->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; #endif - loadmodel->funcs.LightPointValues = NULL; - loadmodel->funcs.StainNode = NULL; - loadmodel->funcs.MarkLights = NULL; - loadmodel->funcs.ClusterPVS = CM_ClusterPVS; - loadmodel->funcs.ClusterForPoint = CM_PointCluster; - loadmodel->funcs.PointContents = Q2BSP_PointContents; - loadmodel->funcs.NativeTrace = CM_NativeTrace; - loadmodel->funcs.NativeContents = CM_NativeContents; + mod->funcs.LightPointValues = NULL; + mod->funcs.StainNode = NULL; + mod->funcs.MarkLights = NULL; + mod->funcs.ClusterPVS = CM_ClusterPVS; + mod->funcs.ClusterForPoint = CM_PointCluster; + mod->funcs.PointContents = Q2BSP_PointContents; + mod->funcs.NativeTrace = CM_NativeTrace; + mod->funcs.NativeContents = CM_NativeContents; break; #if defined(GLQUAKE) || defined(D3DQUAKE) @@ -4193,48 +4150,48 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c case QR_OPENGL: // load into heap #ifndef SERVERONLY - noerrors = noerrors && Mod_LoadVertexes (&header.lumps[Q2LUMP_VERTEXES]); - noerrors = noerrors && Mod_LoadEdges (&header.lumps[Q2LUMP_EDGES], false); - noerrors = noerrors && Mod_LoadSurfedges (&header.lumps[Q2LUMP_SURFEDGES]); + noerrors = noerrors && Mod_LoadVertexes (mod, mod_base, &header.lumps[Q2LUMP_VERTEXES]); + noerrors = noerrors && Mod_LoadEdges (mod, mod_base, &header.lumps[Q2LUMP_EDGES], false); + noerrors = noerrors && Mod_LoadSurfedges (mod, mod_base, &header.lumps[Q2LUMP_SURFEDGES]); if (noerrors) - Mod_LoadLighting (&header.lumps[Q2LUMP_LIGHTING]); + Mod_LoadLighting (mod, mod_base, &header.lumps[Q2LUMP_LIGHTING]); #endif - noerrors = noerrors && CModQ2_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]); - noerrors = noerrors && CModQ2_LoadPlanes (&header.lumps[Q2LUMP_PLANES]); + noerrors = noerrors && CModQ2_LoadSurfaces (mod, mod_base, &header.lumps[Q2LUMP_TEXINFO]); + noerrors = noerrors && CModQ2_LoadPlanes (mod, mod_base, &header.lumps[Q2LUMP_PLANES]); #ifndef SERVERONLY - noerrors = noerrors && CModQ2_LoadTexInfo (&header.lumps[Q2LUMP_TEXINFO]); - noerrors = noerrors && CModQ2_LoadFaces (&header.lumps[Q2LUMP_FACES]); - noerrors = noerrors && Mod_LoadMarksurfaces (&header.lumps[Q2LUMP_LEAFFACES], false); + noerrors = noerrors && CModQ2_LoadTexInfo (mod, mod_base, &header.lumps[Q2LUMP_TEXINFO], loadname); + noerrors = noerrors && CModQ2_LoadFaces (mod, mod_base, &header.lumps[Q2LUMP_FACES]); + noerrors = noerrors && Mod_LoadMarksurfaces (mod, mod_base, &header.lumps[Q2LUMP_LEAFFACES], false); #endif - noerrors = noerrors && CModQ2_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]); - noerrors = noerrors && CModQ2_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]); - noerrors = noerrors && CModQ2_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]); - noerrors = noerrors && CModQ2_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]); - noerrors = noerrors && CModQ2_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]); - noerrors = noerrors && CModQ2_LoadNodes (&header.lumps[Q2LUMP_NODES]); - noerrors = noerrors && CModQ2_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]); - noerrors = noerrors && CModQ2_LoadAreas (&header.lumps[Q2LUMP_AREAS]); - noerrors = noerrors && CModQ2_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]); + noerrors = noerrors && CModQ2_LoadVisibility (mod, mod_base, &header.lumps[Q2LUMP_VISIBILITY]); + noerrors = noerrors && CModQ2_LoadBrushSides (mod, mod_base, &header.lumps[Q2LUMP_BRUSHSIDES]); + noerrors = noerrors && CModQ2_LoadBrushes (mod, mod_base, &header.lumps[Q2LUMP_BRUSHES]); + noerrors = noerrors && CModQ2_LoadLeafBrushes (mod, mod_base, &header.lumps[Q2LUMP_LEAFBRUSHES]); + noerrors = noerrors && CModQ2_LoadLeafs (mod, mod_base, &header.lumps[Q2LUMP_LEAFS]); + noerrors = noerrors && CModQ2_LoadNodes (mod, mod_base, &header.lumps[Q2LUMP_NODES]); + noerrors = noerrors && CModQ2_LoadSubmodels (mod, mod_base, &header.lumps[Q2LUMP_MODELS]); + noerrors = noerrors && CModQ2_LoadAreas (mod, mod_base, &header.lumps[Q2LUMP_AREAS]); + noerrors = noerrors && CModQ2_LoadAreaPortals (mod, mod_base, &header.lumps[Q2LUMP_AREAPORTALS]); if (noerrors) - CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]); + CMod_LoadEntityString (mod, mod_base, &header.lumps[Q2LUMP_ENTITIES]); if (!noerrors) { return NULL; } #ifndef CLIENTONLY - loadmodel->funcs.FatPVS = Q2BSP_FatPVS; - loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; - loadmodel->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; + mod->funcs.FatPVS = Q2BSP_FatPVS; + mod->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS; + mod->funcs.FindTouchedLeafs = Q2BSP_FindTouchedLeafs; #endif - loadmodel->funcs.LightPointValues = GLQ2BSP_LightPointValues; - loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode; - loadmodel->funcs.MarkLights = Q2BSP_MarkLights; - loadmodel->funcs.ClusterPVS = CM_ClusterPVS; - loadmodel->funcs.ClusterForPoint = CM_PointCluster; - loadmodel->funcs.PointContents = Q2BSP_PointContents; - loadmodel->funcs.NativeTrace = CM_NativeTrace; - loadmodel->funcs.NativeContents = CM_NativeContents; + mod->funcs.LightPointValues = GLQ2BSP_LightPointValues; + mod->funcs.StainNode = GLR_Q2BSP_StainNode; + mod->funcs.MarkLights = Q2BSP_MarkLights; + mod->funcs.ClusterPVS = CM_ClusterPVS; + mod->funcs.ClusterForPoint = CM_PointCluster; + mod->funcs.PointContents = Q2BSP_PointContents; + mod->funcs.NativeTrace = CM_NativeTrace; + mod->funcs.NativeContents = CM_NativeContents; break; #endif default: @@ -4244,7 +4201,7 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c } #ifndef SERVERONLY - Mod_ParseInfoFromEntityLump(loadmodel, loadmodel->entities, loadname); //only done for client's world model (or server if the server is loading it for client) + Mod_ParseInfoFromEntityLump(mod, mod->entities, loadname); //only done for client's world model (or server if the server is loading it for client) #endif CM_InitBoxHull (); @@ -4255,79 +4212,91 @@ cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *c memset (portalopen, 0, sizeof(portalopen)); //make them start closed. FloodAreaConnections (); - loadmodel->checksum = loadmodel->checksum2 = *checksum; + mod->checksum = mod->checksum2 = *checksum; + + mod->nummodelsurfaces = mod->numsurfaces; + memset(&mod->batches, 0, sizeof(mod->batches)); + mod->vbos = NULL; + + mod->numsubmodels = CM_NumInlineModels(mod); + + mod->hulls[0].firstclipnode = map_cmodels[0].headnode-mod->nodes; + mod->rootnode = map_cmodels[0].headnode; + mod->nummodelsurfaces = map_cmodels[0].numsurfaces; - loadmodel->nummodelsurfaces = loadmodel->numsurfaces; - memset(&loadmodel->batches, 0, sizeof(loadmodel->batches)); - loadmodel->vbos = NULL; #ifndef SERVERONLY if (qrenderer != QR_NONE) - Mod_Batches_Build(NULL, loadmodel, buildmeshes, buildcookie); -#endif - - loadmodel->numsubmodels = CM_NumInlineModels(loadmodel); { - model_t *wmod = loadmodel; - model_t *mod = loadmodel; - - mod->hulls[0].firstclipnode = map_cmodels[0].headnode-mod->nodes; - mod->rootnode = map_cmodels[0].headnode; - mod->nummodelsurfaces = map_cmodels[0].numsurfaces; - - for (i=1 ; i< loadmodel->numsubmodels ; i++) + builddata_t *bd = NULL; + if (buildmeshes) { - cmodel_t *bm; - - char name[10]; - - sprintf (name, "*%i", i); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - memset(&mod->memgroup, 0, sizeof(mod->memgroup)); - - bm = CM_InlineModel (name); - - - - mod->hulls[0].firstclipnode = -1; //no nodes, - if (bm->headleaf) - { - mod->leafs = bm->headleaf; - mod->nodes = NULL; - mod->hulls[0].firstclipnode = -1; //make it refer directly to the first leaf, for things that still use numbers. - mod->rootnode = (mnode_t*)bm->headleaf; - } - else - { - mod->leafs = wmod->leafs; - mod->nodes = wmod->nodes; - mod->hulls[0].firstclipnode = bm->headnode - mod->nodes; //determine the correct node index - mod->rootnode = bm->headnode; - } - mod->nummodelsurfaces = bm->numsurfaces; - mod->firstmodelsurface = bm->firstsurface; - - memset(&mod->batches, 0, sizeof(mod->batches)); - mod->vbos = NULL; -#ifndef SERVERONLY - if (qrenderer != QR_NONE) - Mod_Batches_Build(NULL, mod, buildmeshes, buildcookie); -#endif - - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); -#ifndef SERVERONLY - mod->radius = RadiusFromBounds (mod->mins, mod->maxs); - - P_DefaultTrail(mod); -#endif + bd = BZ_Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces); + bd->buildfunc = buildmeshes; + memcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces); } + COM_AddWork(0, ModBrush_LoadGLStuff, mod, bd, 0, 0); + } +#endif + + for (i=1 ; i< mod->numsubmodels ; i++) + { + cmodel_t *bm; + + char name[MAX_QPATH]; + + Q_snprintfz (name, sizeof(name), "*%i:%s", i, wmod->name); + mod = Mod_FindName (name); + *mod = *wmod; + Q_strncpyz(mod->name, name, sizeof(mod->name)); + memset(&mod->memgroup, 0, sizeof(mod->memgroup)); + + bm = CM_InlineModel (name); + + + + mod->hulls[0].firstclipnode = -1; //no nodes, + if (bm->headleaf) + { + mod->leafs = bm->headleaf; + mod->nodes = NULL; + mod->hulls[0].firstclipnode = -1; //make it refer directly to the first leaf, for things that still use numbers. + mod->rootnode = (mnode_t*)bm->headleaf; + } + else + { + mod->leafs = wmod->leafs; + mod->nodes = wmod->nodes; + mod->hulls[0].firstclipnode = bm->headnode - mod->nodes; //determine the correct node index + mod->rootnode = bm->headnode; + } + mod->nummodelsurfaces = bm->numsurfaces; + mod->firstmodelsurface = bm->firstsurface; + + memset(&mod->batches, 0, sizeof(mod->batches)); + mod->vbos = NULL; + + VectorCopy (bm->maxs, mod->maxs); + VectorCopy (bm->mins, mod->mins); +#ifndef SERVERONLY + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + if (qrenderer != QR_NONE) + { + builddata_t *bd = NULL; + if (buildmeshes) + { + bd = BZ_Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces); + bd->buildfunc = buildmeshes; + memcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces); + } + COM_AddWork(0, ModBrush_LoadGLStuff, mod, bd, i, 0); + } +#endif + COM_AddWork(0, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0); } #ifdef TERRAIN - im->terrain = Mod_LoadTerrainInfo(im, loadname, false); + mod->terrain = Mod_LoadTerrainInfo(mod, loadname, false); #endif return &map_cmodels[0]; @@ -4451,6 +4420,7 @@ void CM_InitBoxHull (void) box_model.leafs = box_leaf; box_model.rootnode = box_model.nodes; + box_model.loadstate = MLS_LOADED; map_leafbrushes[numleafbrushes] = box_brush; @@ -4552,14 +4522,14 @@ static int CM_PointLeafnum_r (model_t *mod, vec3_t p, int num) int CM_PointLeafnum (model_t *mod, vec3_t p) { - if (!mod || mod->needload) + if (!mod || mod->loadstate != MLS_LOADED) return 0; // sound may call this without map loaded return CM_PointLeafnum_r (mod, p, 0); } static int CM_PointCluster (model_t *mod, vec3_t p) { - if (!mod || mod->needload) + if (!mod || mod->loadstate != MLS_LOADED) return 0; // sound may call this without map loaded return CM_LeafCluster(mod, CM_PointLeafnum_r (mod, p, 0)); } @@ -4659,7 +4629,7 @@ int CM_PointContents (model_t *mod, vec3_t p) i = CM_PointLeafnum_r (mod, p, mod->hulls[0].firstclipnode); - if (!mapisq3) + if (mod->fromgame == fg_quake2) contents = mod->leafs[i].contents; //q2 is simple. else { @@ -4723,8 +4693,8 @@ unsigned int CM_NativeContents(struct model_s *model, int hulloverride, int fram for (k--; k >= 0; k--) { leaf = &model->leafs[leaflist[k]]; - if (mapisq3) - { + if (model->fromgame != fg_quake2) + { //q3 is more complex for (i = 0; i < leaf->numleafbrushes; i++) { brush = map_leafbrushes[leaf->firstleafbrush + i]; @@ -4745,7 +4715,7 @@ unsigned int CM_NativeContents(struct model_s *model, int hulloverride, int fram contents |= brush->contents; } } - else + else //q2 is simple contents |= leaf->contents; } } @@ -6116,7 +6086,7 @@ qbyte *CM_ClusterPVS (model_t *mod, int cluster, qbyte *buffer, unsigned int buf if (buffersize < (numclusters+7)>>3) Sys_Error("CM_ClusterPVS with too small a buffer\n"); - if (mapisq3) + if (mod->fromgame != fg_quake2) { if (cluster != -1 && map_q3pvs->numclusters) { @@ -6138,7 +6108,7 @@ qbyte *CM_ClusterPVS (model_t *mod, int cluster, qbyte *buffer, unsigned int buf qbyte *CM_ClusterPHS (model_t *mod, int cluster) { - if (mapisq3) //phs not working yet. + if (mod->fromgame != fg_quake2) { if (cluster != -1 && map_q3phs->numclusters) { @@ -6396,10 +6366,10 @@ unsigned int Q2BSP_PointContents(model_t *mod, vec3_t axis[3], vec3_t p) int map_checksum; -qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer) +qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize) { mod->fromgame = fg_quake2; - return CM_LoadMap(mod->name, buffer, true, &map_checksum) != NULL; + return CM_LoadMap(mod, buffer, fsize, true, &map_checksum) != NULL; } void CM_Init(void) //register cvars. diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 5c02f266..f8e607cb 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -5701,7 +5701,7 @@ void NET_CloseServer(void) void NET_InitServer(void) { - if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value || sv_listen_q3.value) + if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value || sv_listen_q3.ival) { if (!svs.sockets) { diff --git a/engine/common/particles.h b/engine/common/particles.h index 48a43262..d2f2aae9 100644 --- a/engine/common/particles.h +++ b/engine/common/particles.h @@ -99,7 +99,7 @@ struct msurface_s; void P_InitParticleSystem(void); void P_Shutdown(void); void P_LoadedModel(struct model_s *mod); /*checks a model's various effects*/ -void P_DefaultTrail (struct model_s *model); +void P_DefaultTrail (unsigned int modelflags, int *trailid, int *trailpalidx); void P_EmitEffect (vec3_t pos, int type, trailstate_t **tsk);//this is just a wrapper #define P_FindParticleType pe->FindParticleType @@ -129,7 +129,7 @@ typedef struct { qboolean (*ParticleQuery) (int type, int body, char *outstr, int outstrlen); int (*RunParticleEffectTypeString) (vec3_t org, vec3_t dir, float count, char *name); - int (*ParticleTrail) (vec3_t startpos, vec3_t end, int type, int dlkey, trailstate_t **tsk); + int (*ParticleTrail) (vec3_t startpos, vec3_t end, int type, int dlkey, vec3_t dlaxis[3], trailstate_t **tsk); int (*RunParticleEffectState) (vec3_t org, vec3_t dir, float count, int typenum, trailstate_t **tsk); void (*RunParticleWeather) (vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, char *efname); void (*RunParticleCube) (int typenum, vec3_t minb, vec3_t maxb, vec3_t dir_min, vec3_t dir_max, float count, int colour, qboolean gravity, float jitter); //typenum may be P_INVALID diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 41221188..f189d60e 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -921,6 +921,17 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp #endif #endif +qintptr_t VARGS Plug_VFS_Open(void *offset, quintptr_t mask, const qintptr_t *arg) +{ + char *fname = VM_POINTER(arg[0]); + vfsfile_t **handle = VM_POINTER(arg[1]); + char *mode = VM_POINTER(arg[2]); + *handle = FS_OpenVFS(fname, mode, FS_GAME); + if (*handle) + return true; + return false; +} + qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg) { //modes: @@ -1307,6 +1318,8 @@ void Plug_Initialise(qboolean fromgamedir) Plug_RegisterBuiltin("Net_Close", Plug_Net_Close, 0); #endif + + Plug_RegisterBuiltin("VFS_Open", Plug_VFS_Open, PLUG_BIF_DLLONLY); Plug_RegisterBuiltin("FS_Open", Plug_FS_Open, 0); Plug_RegisterBuiltin("FS_Read", Plug_Net_Recv, 0); Plug_RegisterBuiltin("FS_Write", Plug_Net_Send, 0); @@ -1645,7 +1658,8 @@ void Plug_Close(plugin_t *plug) prev->next = plug->next; } - Con_Printf("Closing plugin %s\n", plug->name); + if (!com_fatalerror) + Con_Printf("Closing plugin %s\n", plug->name); //ensure any active contexts provided by the plugin are closed (stuff with destroy callbacks) #if defined(PLUGINS) && !defined(NOMEDIA) && !defined(SERVERONLY) diff --git a/engine/common/pmovetst.c b/engine/common/pmovetst.c index f4863517..e79cd35d 100644 --- a/engine/common/pmovetst.c +++ b/engine/common/pmovetst.c @@ -119,7 +119,7 @@ int PM_PointContents (vec3_t p) //check world. pm = pmove.physents[0].model; - if (!pm || pm->needload) + if (!pm || pm->loadstate != MLS_LOADED) return FTECONTENTS_EMPTY; pc = pm->funcs.PointContents(pm, NULL, p); @@ -471,7 +471,7 @@ trace_t PM_PlayerTrace (vec3_t start, vec3_t end, unsigned int solidmask) if (pe->forcecontentsmask && !(pe->forcecontentsmask & solidmask)) continue; - if (!pe->model || pe->model->needload) + if (!pe->model || pe->model->loadstate != MLS_LOADED) { vec3_t mins, maxs; diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 35df35dc..a4819977 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -19,6 +19,7 @@ cvar_t pr_droptofloorunits = CVAR("pr_droptofloorunits", ""); cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0"); cvar_t pr_tempstringcount = CVAR("pr_tempstringcount", "");//"16"); cvar_t pr_tempstringsize = CVAR("pr_tempstringsize", "4096"); +cvar_t pr_sourcedir = CVARD("pr_sourcedir", "src", "Subdirectory where your qc source is located. Used by the internal compiler and qc debugging functionality."); cvar_t pr_enable_uriget = CVAR("pr_enable_uriget", "1"); cvar_t pr_enable_profiling = CVARD("pr_enable_profiling", "0", "Enables profiling support. Will run more slowly. Change the map and then use the profile_ssqc/profile_csqc commands to see the results."); int tokenizeqc(const char *str, qboolean dpfuckage); @@ -34,6 +35,7 @@ void PF_Common_RegisterCvars(void) Cvar_Register (&pr_tempstringsize, cvargroup_progs); Cvar_Register (&pr_enable_uriget, cvargroup_progs); Cvar_Register (&pr_enable_profiling, cvargroup_progs); + Cvar_Register (&pr_sourcedir, cvargroup_progs); #ifdef RAGDOLL Cmd_AddCommand("skel_info", skel_info_f); @@ -158,7 +160,9 @@ int QDECL QCEditor (pubprogfuncs_t *prinst, char *filename, int line, int statem if (line == -1) return line; +#ifndef CLIENTONLY SV_EndRedirect(); +#endif if (developer.value) { f = FS_OpenVFS(filename, "rb", FS_GAME); @@ -320,7 +324,7 @@ void VARGS PR_BIError(pubprogfuncs_t *progfuncs, char *format, ...) else { PR_StackTrace(progfuncs, false); - PR_AbortStack(progfuncs); +// PR_AbortStack(progfuncs); progfuncs->parms->Abort ("%s", string); } } @@ -776,8 +780,10 @@ void QCBUILTIN PF_checkpvs(pubprogfuncs_t *prinst, struct globalvars_s *pr_globa float *viewpos = G_VECTOR(OFS_PARM0); wedict_t *ent = G_WEDICT(prinst, OFS_PARM1); - if (!world->worldmodel || world->worldmodel->needload) + if (!world->worldmodel || world->worldmodel->loadstate != MLS_LOADED) G_FLOAT(OFS_RETURN) = false; + else if (!world->worldmodel->funcs.FatPVS) + G_FLOAT(OFS_RETURN) = true; else { //FIXME: Make all alternatives of FatPVS not recalulate the pvs. @@ -1511,6 +1517,8 @@ pf_fopen_files_t pf_fopen_files[MAX_QC_FILES]; //fallbackread can be NULL, if the qc is not allowed to read that (original) file at all. qboolean QC_FixFileName(const char *name, const char **result, const char **fallbackread) { + char ext[8]; + if (strchr(name, ':') || //dos/win absolute path, ntfs ADS, amiga drives. reject them all. strchr(name, '\\') || //windows-only paths. *name == '/' || //absolute path was given - reject @@ -1521,7 +1529,7 @@ qboolean QC_FixFileName(const char *name, const char **result, const char **fall *fallbackread = name; //if its a user config, ban any fallback locations so that csqc can't read passwords or whatever. - if ((!strchr(name, '/') || strnicmp(name, "configs/", 8)) && !stricmp(COM_FileExtension(name), "cfg")) + if ((!strchr(name, '/') || strnicmp(name, "configs/", 8)) && !stricmp(COM_FileExtension(name, ext, sizeof(ext)), "cfg")) *fallbackread = NULL; *result = va("data/%s", name); return true; @@ -1534,6 +1542,7 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals int fsize = G_FLOAT(OFS_PARM2); const char *fallbackread; int i; + size_t insize; for (i = 0; i < MAX_QC_FILES; i++) if (!pf_fopen_files[i].data) @@ -1596,11 +1605,11 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals break; case FRIK_FILE_READ: //read case FRIK_FILE_READNL: //read whole file - pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name); + fsize = FS_LoadFile(pf_fopen_files[i].name, &pf_fopen_files[i].data); if (!pf_fopen_files[i].data && fallbackread) { Q_strncpyz(pf_fopen_files[i].name, fallbackread, sizeof(pf_fopen_files[i].name)); - pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name); + fsize = FS_LoadFile(pf_fopen_files[i].name, &pf_fopen_files[i].data); } if (pf_fopen_files[i].data) @@ -1611,12 +1620,12 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals else G_FLOAT(OFS_RETURN) = -1; - pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = com_filesize; + pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = fsize; pf_fopen_files[i].ofs = 0; break; case FRIK_FILE_APPEND: //append - pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name); - pf_fopen_files[i].ofs = pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = com_filesize; + pf_fopen_files[i].data = FS_LoadMallocFile(pf_fopen_files[i].name, &insize); + pf_fopen_files[i].ofs = pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = insize; if (pf_fopen_files[i].data) { G_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX; @@ -1691,7 +1700,7 @@ void QCBUILTIN PF_fclose (pubprogfuncs_t *prinst, struct globalvars_s *pr_global if (fnum < 0 || fnum >= MAX_QC_FILES) { - Con_Printf("PF_fclose: File out of range\n"); + Con_Printf("PF_fclose: File out of range (%g)\n", G_FLOAT(OFS_PARM0)); return; //out of range } @@ -2105,7 +2114,7 @@ void QCBUILTIN PF_callfunction (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_loadfromfile (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *filename = PR_GetStringOfs(prinst, OFS_PARM0); - const char *file = COM_LoadTempFile(filename); + const char *file = COM_LoadTempFile(filename, NULL); int size; @@ -4086,7 +4095,7 @@ void QCBUILTIN PF_droptofloor (pubprogfuncs_t *prinst, struct globalvars_s *pr_g const float *gravitydir; extern const vec3_t standardgravity; - ent = PROG_TO_WEDICT(prinst, pr_global_struct->self); + ent = PROG_TO_WEDICT(prinst, *world->g.self); if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0]) gravitydir = ent->xv->gravitydir; @@ -4164,10 +4173,11 @@ FIXME: add gravitydir support float World_changeyaw (wedict_t *ent); void QCBUILTIN PF_changeyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { + world_t *w = prinst->parms->user; wedict_t *ent; // float ideal, current, move, speed; - ent = PROG_TO_WEDICT(prinst, pr_global_struct->self); + ent = PROG_TO_WEDICT(prinst, *w->g.self); World_changeyaw(ent); /* @@ -4207,10 +4217,11 @@ void QCBUILTIN PF_changeyaw (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo //FIXME: support gravitydir void QCBUILTIN PF_changepitch (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { - edict_t *ent; + world_t *w = prinst->parms->user; + wedict_t *ent; float ideal, current, move, speed; - ent = PROG_TO_EDICT(prinst, pr_global_struct->self); + ent = PROG_TO_WEDICT(prinst, *w->g.self); current = anglemod( ent->v->angles[1] ); ideal = ent->xv->idealpitch; speed = ent->xv->pitch_speed; diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 84a23173..cc3091ce 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -534,6 +534,26 @@ pbool QDECL ED_CanFree (edict_t *ed); #define CLIENTTYPE_BOT 2 #define CLIENTTYPE_NOTACLIENT 3 +enum +{ + RESSTATE_NOTKNOWN = 0, + RESSTATE_NOTLOADED = 1, + RESSTATE_LOADING = 2, + RESSTATE_FAILED = 3, + RESSTATE_LOADED = 4, + + RESSTATE_UNSUPPORTED = -1 +}; +enum +{ + RESTYPE_MODEL, + RESTYPE_SOUND, + RESTYPE_PARTICLE, + RESTYPE_SHADER, + RESTYPE_SKIN, + RESTYPE_TEXTURE +}; + //shared constants typedef enum { @@ -629,7 +649,8 @@ enum lightfield_e lfield_cubemapname=9, lfield_ambientscale=10, lfield_diffusescale=11, - lfield_specularscale=12 + lfield_specularscale=12, + lfield_rotation=13 }; enum csqc_input_event { diff --git a/engine/common/protocol.h b/engine/common/protocol.h index c216efe3..69beb37c 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -813,32 +813,33 @@ enum clcq2_ops_e // temp entity events // enum { - TE_SPIKE = 0, - TE_SUPERSPIKE = 1, - TE_GUNSHOT = 2, - TE_EXPLOSION = 3, - TE_TAREXPLOSION = 4, - TE_LIGHTNING1 = 5, - TE_LIGHTNING2 = 6, - TE_WIZSPIKE = 7, - TE_KNIGHTSPIKE = 8, - TE_LIGHTNING3 = 9, - TE_LAVASPLASH = 10, - TE_TELEPORT = 11, + TE_SPIKE = 0, + TE_SUPERSPIKE = 1, + TE_GUNSHOT = 2, + TE_EXPLOSION = 3, + TE_TAREXPLOSION = 4, + TE_LIGHTNING1 = 5, + TE_LIGHTNING2 = 6, + TE_WIZSPIKE = 7, + TE_KNIGHTSPIKE = 8, + TE_LIGHTNING3 = 9, + TE_LAVASPLASH = 10, + TE_TELEPORT = 11, - TEQW_BLOOD = 12, //implemented as a particle() in nq - TENQ_EXPLOSION2 = 12, //remapped to TEQW_EXPLOSION2 for qw - TEQW_LIGHTNINGBLOOD = 13, //implemented as a particle() in nq - TENQ_BEAM = 13, //remapped to TEQW_BEAM for qw + TEQW_BLOOD = 12, //implemented as a particle() in nq + TENQ_EXPLOSION2 = 12, //remapped to TEQW_EXPLOSION2 for qw + TEQW_LIGHTNINGBLOOD = 13, //implemented as a particle() in nq + TENQ_BEAM = 13, //remapped to TEQW_BEAM for qw #ifdef PEXT_TE_BULLET - TE_BULLET = 14, - TE_SUPERBULLET = 15, + TE_BULLET = 14, + TE_SUPERBULLET = 15, #endif - TE_RAILTRAIL = 17, //use the builtin, luke. - TEQW_BEAM = 18, //use the builtin, luke. - TEQW_EXPLOSION2 = 19, //use the builtin, luke. + TE_RAILTRAIL = 17, //use the builtin, luke. + TEQW_BEAM = 18, //use the builtin, luke. + TEQW_EXPLOSION2 = 19, //use the builtin, luke. + TE_EXPLOSIONNOSPRITE = 20, //use the builtin, luke. // hexen 2 TEH2_STREAM_LIGHTNING_SMALL = 24, diff --git a/engine/common/q3common.c b/engine/common/q3common.c index 6f861ffb..93f68102 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -20,6 +20,7 @@ vm_fopen_files_t vm_fopen_files[MAX_VM_FILES]; int VM_fopen (char *name, int *handle, int fmode, int owner) { int i; + size_t insize; if (!handle) return FS_FLocateFile(name, FSLFRT_IFFOUND, NULL); @@ -45,8 +46,8 @@ int VM_fopen (char *name, int *handle, int fmode, int owner) switch (fmode) { case VM_FS_READ: - vm_fopen_files[i].data = FS_LoadMallocFile(name); - vm_fopen_files[i].bufferlen = vm_fopen_files[i].len = com_filesize; + vm_fopen_files[i].data = FS_LoadMallocFile(name, &insize); + vm_fopen_files[i].bufferlen = vm_fopen_files[i].len = insize; vm_fopen_files[i].ofs = 0; if (vm_fopen_files[i].data) break; diff --git a/engine/common/sys.h b/engine/common/sys.h index 1643bfca..7188876b 100644 --- a/engine/common/sys.h +++ b/engine/common/sys.h @@ -98,9 +98,12 @@ qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refres void Sys_SetThreadName(unsigned int dwThreadID, char *threadName); #endif +void Sys_ThreadsInit(void); +qboolean Sys_IsThread(void *thread); void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize); void Sys_WaitOnThread(void *thread); void Sys_DetachThread(void *thread); +void Sys_ThreadAbort(void); #define THREADP_IDLE -5 #define THREADP_NORMAL 0 @@ -116,10 +119,16 @@ void Sys_DestroyMutex(void *mutex); void *Sys_CreateConditional(void); qboolean Sys_LockConditional(void *condv); qboolean Sys_UnlockConditional(void *condv); -qboolean Sys_ConditionWait(void *condv); -qboolean Sys_ConditionSignal(void *condv); -qboolean Sys_ConditionBroadcast(void *condv); +qboolean Sys_ConditionWait(void *condv); //lock first +qboolean Sys_ConditionSignal(void *condv); //lock first +qboolean Sys_ConditionBroadcast(void *condv); //lock first void Sys_DestroyConditional(void *condv); +#else +#define Sys_CreateMutex() NULL +#define Sys_LockMutex(m) true +#define Sys_UnlockMutex(m) true +#define Sys_DestroyMutex(m) +#define Sys_IsThread(t) !t #endif void Sys_Sleep(double seconds); diff --git a/engine/common/sys_linux_threads.c b/engine/common/sys_linux_threads.c index 5164cac8..38519d3c 100644 --- a/engine/common/sys_linux_threads.c +++ b/engine/common/sys_linux_threads.c @@ -1,187 +1,204 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -//well, linux or cygwin (windows with posix emulation layer), anyway... - -#include "quakedef.h" - -#ifdef MULTITHREAD -#include -#include -/* Thread creation calls */ -typedef void *(*pfunction_t)(void *); - -void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) -{ - pthread_t *thread; - pthread_attr_t attr; - - thread = (pthread_t *)malloc(sizeof(pthread_t)); - if (!thread) - return NULL; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - if (stacksize < PTHREAD_STACK_MIN*2) - stacksize = PTHREAD_STACK_MIN*2; - pthread_attr_setstacksize(&attr, stacksize); - if (pthread_create(thread, &attr, (pfunction_t)func, args)) - { - free(thread); - thread = NULL; - } - pthread_attr_destroy(&attr); - - return (void *)thread; -} - -void Sys_WaitOnThread(void *thread) -{ - int err; - err = pthread_join(*(pthread_t *)thread, NULL); - if (err) - printf("pthread_join(%p) failed, error %s\n", thread, strerror(err)); - - free(thread); -} - -/* Mutex calls */ -void *Sys_CreateMutex(void) -{ - pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); - - if (mutex && !pthread_mutex_init(mutex, NULL)) - return mutex; - return NULL; -} - -qboolean Sys_TryLockMutex(void *mutex) -{ - return !pthread_mutex_trylock(mutex); -} - -qboolean Sys_LockMutex(void *mutex) -{ - return !pthread_mutex_lock(mutex); -} - -qboolean Sys_UnlockMutex(void *mutex) -{ - return !pthread_mutex_unlock(mutex); -} - -void Sys_DestroyMutex(void *mutex) -{ - pthread_mutex_destroy(mutex); - free(mutex); -} - -/* Conditional wait calls */ -typedef struct condvar_s -{ - pthread_mutex_t *mutex; - pthread_cond_t *cond; -} condvar_t; - -void *Sys_CreateConditional(void) -{ - condvar_t *condv; - pthread_mutex_t *mutex; - pthread_cond_t *cond; - - condv = (condvar_t *)malloc(sizeof(condvar_t)); - if (!condv) - return NULL; - - mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); - if (!mutex) - return NULL; - - cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); - if (!cond) - return NULL; - - if (!pthread_mutex_init(mutex, NULL)) - { - if (!pthread_cond_init(cond, NULL)) - { - condv->cond = cond; - condv->mutex = mutex; - - return (void *)condv; - } - else - pthread_mutex_destroy(mutex); - } - - free(cond); - free(mutex); - free(condv); - return NULL; -} - -qboolean Sys_LockConditional(void *condv) -{ - return !pthread_mutex_lock(((condvar_t *)condv)->mutex); -} - -qboolean Sys_UnlockConditional(void *condv) -{ - return !pthread_mutex_unlock(((condvar_t *)condv)->mutex); -} - -qboolean Sys_ConditionWait(void *condv) -{ - return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); -} - -qboolean Sys_ConditionSignal(void *condv) -{ - return !pthread_cond_signal(((condvar_t *)condv)->cond); -} - -qboolean Sys_ConditionBroadcast(void *condv) -{ - return !pthread_cond_broadcast(((condvar_t *)condv)->cond); -} - -void Sys_DestroyConditional(void *condv) -{ - condvar_t *cv = (condvar_t *)condv; - - pthread_cond_destroy(cv->cond); - pthread_mutex_destroy(cv->mutex); - free(cv->cond); - free(cv->mutex); - free(cv); -} -#endif - -void Sys_Sleep (double seconds) -{ - struct timespec ts; - - ts.tv_sec = (time_t)seconds; - seconds -= ts.tv_sec; - ts.tv_nsec = seconds * 1000000000.0; - - nanosleep(&ts, NULL); -} - +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +//well, linux or cygwin (windows with posix emulation layer), anyway... + +#include "quakedef.h" + +#ifdef MULTITHREAD +#include +#include +/* Thread creation calls */ +typedef void *(*pfunction_t)(void *); + +static pthread_t mainthread; + +void Sys_ThreadsInit(void) +{ + mainthread = pthread_self(); +} +qboolean Sys_IsThread(void *thread) +{ + if (!thread) + thread = &mainthread; + return pthread_equal(pthread_self(), *(pthread_t*)thread); +} +void Sys_ThreadAbort(void) +{ + pthread_exit(NULL); +} + +void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) +{ + pthread_t *thread; + pthread_attr_t attr; + + thread = (pthread_t *)malloc(sizeof(pthread_t)); + if (!thread) + return NULL; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + if (stacksize < PTHREAD_STACK_MIN*2) + stacksize = PTHREAD_STACK_MIN*2; + pthread_attr_setstacksize(&attr, stacksize); + if (pthread_create(thread, &attr, (pfunction_t)func, args)) + { + free(thread); + thread = NULL; + } + pthread_attr_destroy(&attr); + + return (void *)thread; +} + +void Sys_WaitOnThread(void *thread) +{ + int err; + err = pthread_join(*(pthread_t *)thread, NULL); + if (err) + printf("pthread_join(%p) failed, error %s\n", thread, strerror(err)); + + free(thread); +} + +/* Mutex calls */ +void *Sys_CreateMutex(void) +{ + pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + + if (mutex && !pthread_mutex_init(mutex, NULL)) + return mutex; + return NULL; +} + +qboolean Sys_TryLockMutex(void *mutex) +{ + return !pthread_mutex_trylock(mutex); +} + +qboolean Sys_LockMutex(void *mutex) +{ + return !pthread_mutex_lock(mutex); +} + +qboolean Sys_UnlockMutex(void *mutex) +{ + return !pthread_mutex_unlock(mutex); +} + +void Sys_DestroyMutex(void *mutex) +{ + pthread_mutex_destroy(mutex); + free(mutex); +} + +/* Conditional wait calls */ +typedef struct condvar_s +{ + pthread_mutex_t *mutex; + pthread_cond_t *cond; +} condvar_t; + +void *Sys_CreateConditional(void) +{ + condvar_t *condv; + pthread_mutex_t *mutex; + pthread_cond_t *cond; + + condv = (condvar_t *)malloc(sizeof(condvar_t)); + if (!condv) + return NULL; + + mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + if (!mutex) + return NULL; + + cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t)); + if (!cond) + return NULL; + + if (!pthread_mutex_init(mutex, NULL)) + { + if (!pthread_cond_init(cond, NULL)) + { + condv->cond = cond; + condv->mutex = mutex; + + return (void *)condv; + } + else + pthread_mutex_destroy(mutex); + } + + free(cond); + free(mutex); + free(condv); + return NULL; +} + +qboolean Sys_LockConditional(void *condv) +{ + return !pthread_mutex_lock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_UnlockConditional(void *condv) +{ + return !pthread_mutex_unlock(((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionWait(void *condv) +{ + return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); +} + +qboolean Sys_ConditionSignal(void *condv) +{ + return !pthread_cond_signal(((condvar_t *)condv)->cond); +} + +qboolean Sys_ConditionBroadcast(void *condv) +{ + return !pthread_cond_broadcast(((condvar_t *)condv)->cond); +} + +void Sys_DestroyConditional(void *condv) +{ + condvar_t *cv = (condvar_t *)condv; + + pthread_cond_destroy(cv->cond); + pthread_mutex_destroy(cv->mutex); + free(cv->cond); + free(cv->mutex); + free(cv); +} +#endif + +void Sys_Sleep (double seconds) +{ + struct timespec ts; + + ts.tv_sec = (time_t)seconds; + seconds -= ts.tv_sec; + ts.tv_nsec = seconds * 1000000000.0; + + nanosleep(&ts, NULL); +} + diff --git a/engine/common/sys_win_threads.c b/engine/common/sys_win_threads.c index 1d76d9fd..d4887e36 100644 --- a/engine/common/sys_win_threads.c +++ b/engine/common/sys_win_threads.c @@ -33,40 +33,17 @@ PVOID WINAPI AddVectoredExceptionHandler(ULONG FirstHandler, PVECTORED_EXCEPTION #endif #endif -#if !defined(WINRT) && defined(MULTITHREAD) -#include -/* Thread creation calls */ -typedef struct threadwrap_s -{ - int (*func)(void *); - void *args; -} threadwrap_t; -// the thread call is wrapped so we don't need WINAPI everywhere -unsigned int WINAPI threadwrapper(void *args) -{ - threadwrap_t tw; - tw.func = ((threadwrap_t *)args)->func; - tw.args = ((threadwrap_t *)args)->args; - - free(args); - tw.func(tw.args); - -#ifndef WIN32CRTDLL - _endthreadex(0); -#endif - return 0; -} #if defined(_DEBUG) && defined(_MSC_VER) const DWORD MS_VC_EXCEPTION=0x406D1388; #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) void Sys_SetThreadName(unsigned int dwThreadID, char *threadName) @@ -87,9 +64,42 @@ void Sys_SetThreadName(unsigned int dwThreadID, char *threadName) } #endif +#if !defined(WINRT) && defined(MULTITHREAD) +#include +/* Thread creation calls */ +typedef struct threadwrap_s +{ + int (*func)(void *); + void *args; + char name[1]; +} threadwrap_t; + +// the thread call is wrapped so we don't need WINAPI everywhere +unsigned int WINAPI threadwrapper(void *args) +{ + threadwrap_t tw; + tw.func = ((threadwrap_t *)args)->func; + tw.args = ((threadwrap_t *)args)->args; + +#if defined(_DEBUG) && defined(_MSC_VER) + Sys_SetThreadName(GetCurrentThreadId(), ((threadwrap_t *)args)->name); +#endif +#ifdef CATCHCRASH + AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler); +#endif + + free(args); + tw.func(tw.args); + +#ifndef WIN32CRTDLL + _endthreadex(0); +#endif + return 0; +} + void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) { - threadwrap_t *tw = (threadwrap_t *)malloc(sizeof(threadwrap_t)); + threadwrap_t *tw = (threadwrap_t *)malloc(sizeof(threadwrap_t)+strlen(name)); HANDLE handle; unsigned int tid; @@ -99,6 +109,7 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority stacksize += 128; // wrapper overhead, also prevent default stack size tw->func = func; tw->args = args; + strcpy(tw->name, name); #ifdef WIN32CRTDLL handle = (HANDLE)CreateThread(NULL, stacksize, &threadwrapper, (void *)tw, 0, &tid); #else @@ -110,12 +121,7 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority return NULL; } -#if defined(_DEBUG) && defined(_MSC_VER) - Sys_SetThreadName(tid, name); -#endif -#ifdef CATCHCRASH - AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler); -#endif + return (void *)handle; } @@ -131,30 +137,94 @@ void Sys_WaitOnThread(void *thread) CloseHandle((HANDLE)thread); } +//used on fatal errors. +void Sys_ThreadAbort(void) +{ + ExitThread(0); +} + +static DWORD mainthread; +void Sys_ThreadsInit(void) +{ + mainthread = GetCurrentThreadId(); +} +qboolean Sys_IsThread(void *thread) +{ + if (!thread) + return mainthread == GetCurrentThreadId(); + return GetThreadId(thread) == GetCurrentThreadId(); +} + /* Mutex calls */ +/* +Note that a 'mutex' in win32 terminology is a cross-process/kernel object +A critical section is a single-process object, and thus can be provided more cheaply +*/ void *Sys_CreateMutex(void) { - return (void *)CreateMutex(NULL, 0, NULL); +#ifdef _DEBUG + //linux's pthread code doesn't like me recursively locking mutexes, so add some debug-only code to catch that on windows too so that we don't get nasty surprises. + CRITICAL_SECTION *mutex = malloc(sizeof(*mutex)+sizeof(int)); + *(int*)(1+(CRITICAL_SECTION*)mutex) = 0; +#else + CRITICAL_SECTION *mutex = malloc(sizeof(*mutex)); +#endif + InitializeCriticalSection(mutex); + return (void *)mutex; } qboolean Sys_TryLockMutex(void *mutex) { - return WaitForSingleObject(mutex, 0) == WAIT_OBJECT_0; +#ifdef _DEBUG + if (!mutex) + { + Con_Printf("Invalid mutex\n"); + return false; + } +#endif + if (TryEnterCriticalSection(mutex)) + { +#ifdef _DEBUG + if (*(int*)(1+(CRITICAL_SECTION*)mutex)) + Con_Printf("Double lock\n"); + *(int*)(1+(CRITICAL_SECTION*)mutex)+=1; +#endif + return true; + } + return false; } qboolean Sys_LockMutex(void *mutex) { - return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0; +#ifdef _DEBUG + if (!mutex) + { + Con_Printf("Invalid mutex\n"); + return false; + } +#endif + EnterCriticalSection(mutex); +#ifdef _DEBUG + if (*(int*)(1+(CRITICAL_SECTION*)mutex)) + Con_Printf("Double lock\n"); + *(int*)(1+(CRITICAL_SECTION*)mutex)+=1; +#endif + return true; } qboolean Sys_UnlockMutex(void *mutex) { - return !!ReleaseMutex(mutex); +#ifdef _DEBUG + *(int*)(1+(CRITICAL_SECTION*)mutex)-=1; +#endif + LeaveCriticalSection(mutex); + return true; } void Sys_DestroyMutex(void *mutex) { - CloseHandle(mutex); + DeleteCriticalSection(mutex); + free(mutex); } /* Conditional wait calls */ @@ -164,19 +234,17 @@ http://msdn.microsoft.com/en-us/library/ms682052(VS.85).aspx Note this uses Slim Reader/Writer locks (Vista+ exclusive) or critical sections. -The condition variable implementation is based on the libSDL implementation. -This code could probably be made more efficient with the use of events or -different mechanisms but for now the main concern is a correct and -complete solution. +The condition variable implementation is based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. +(the libsdl-based stuff was too buggy) */ typedef struct condvar_s { - int waiting; - int signals; - CRITICAL_SECTION countlock; + int waiters; + int release; + int waitgeneration; + CRITICAL_SECTION countlock; CRITICAL_SECTION mainlock; - HANDLE wait_sem; - HANDLE wait_done; + HANDLE evnt; } condvar_t; void *Sys_CreateConditional(void) @@ -187,21 +255,17 @@ void *Sys_CreateConditional(void) if (!cv) return NULL; - cv->waiting = 0; - cv->signals = 0; + cv->waiters = 0; + cv->release = 0; + cv->waitgeneration = 0; InitializeCriticalSection (&cv->mainlock); InitializeCriticalSection (&cv->countlock); - cv->wait_sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); - cv->wait_done = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + cv->evnt = CreateEvent(NULL, TRUE, FALSE, NULL); - if (cv->wait_sem && cv->wait_done) + if (cv->evnt) return (void *)cv; // something failed so deallocate everything - if (cv->wait_done) - CloseHandle(cv->wait_done); - if (cv->wait_sem) - CloseHandle(cv->wait_sem); DeleteCriticalSection(&cv->countlock); DeleteCriticalSection(&cv->mainlock); free(cv); @@ -223,87 +287,94 @@ qboolean Sys_UnlockConditional(void *condv) qboolean Sys_ConditionWait(void *condv) { + qboolean done; condvar_t *cv = (condvar_t *)condv; qboolean success; - DWORD status; + int mygen; // increase count for non-signaled waiting threads EnterCriticalSection(&cv->countlock); - cv->waiting++; + cv->waiters++; + mygen = cv->waitgeneration; LeaveCriticalSection(&cv->countlock); LeaveCriticalSection(&cv->mainlock); // unlock as per condition variable definition // wait on a signal -#if 0 - success = (WaitForSingleObject(cv->wait_sem, INFINITE) != WAIT_FAILED); + for(;;) + { +#if 1 + success = (WaitForSingleObject(cv->evnt, INFINITE) != WAIT_FAILED); #else - do - { - MSG msg; - while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) - DispatchMessage (&msg); - status = MsgWaitForMultipleObjects(1, &cv->wait_sem, FALSE, INFINITE, QS_SENDMESSAGE|QS_POSTMESSAGE); - } while (status == (WAIT_OBJECT_0+1)); - success = status != WAIT_FAILED; + do + { + MSG msg; + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + DispatchMessage (&msg); + status = MsgWaitForMultipleObjects(1, &cv->evnt, FALSE, INFINITE, QS_SENDMESSAGE|QS_POSTMESSAGE); + } while (status == (WAIT_OBJECT_0+1)); + success = status != WAIT_FAILED; #endif - - // update waiting count and alert signaling thread that we're done to avoid the deadlock condition - EnterCriticalSection(&cv->countlock); - if (cv->signals > 0) - { - ReleaseSemaphore(cv->wait_done, cv->signals, NULL); - cv->signals = 0; + EnterCriticalSection(&cv->countlock); + done = cv->release > 0 && cv->waitgeneration != mygen; + LeaveCriticalSection(&cv->countlock); + if (done) + break; } - cv->waiting--; - LeaveCriticalSection(&cv->countlock); EnterCriticalSection(&cv->mainlock); // lock as per condition variable definition - return success; + // update waiting count and alert signaling thread that we're done to avoid the deadlock condition + EnterCriticalSection(&cv->countlock); + cv->waiters--; + cv->release--; + done = cv->release == 0; + LeaveCriticalSection(&cv->countlock); + + if (done) + ResetEvent(cv->evnt); + + return true; } qboolean Sys_ConditionSignal(void *condv) { condvar_t *cv = (condvar_t *)condv; + EnterCriticalSection(&cv->mainlock); + // if there are non-signaled waiting threads, we signal one and wait on the response EnterCriticalSection(&cv->countlock); - if (cv->waiting > cv->signals) + if (cv->waiters > cv->release) { - cv->signals++; - ReleaseSemaphore(cv->wait_sem, 1, NULL); - LeaveCriticalSection(&cv->countlock); - WaitForSingleObject(cv->wait_done, INFINITE); + SetEvent(cv->evnt); + cv->release++; + cv->waitgeneration++; } - else - LeaveCriticalSection(&cv->countlock); + LeaveCriticalSection(&cv->countlock); - return true; + LeaveCriticalSection(&cv->mainlock); + + return true; } qboolean Sys_ConditionBroadcast(void *condv) { condvar_t *cv = (condvar_t *)condv; + EnterCriticalSection(&cv->mainlock); + // if there are non-signaled waiting threads, we signal all of them and wait on all the responses back EnterCriticalSection(&cv->countlock); - if (cv->waiting > cv->signals) + if (cv->waiters > 0) { - int i, num_waiting; - - num_waiting = (cv->waiting - cv->signals); - cv->signals = cv->waiting; - - ReleaseSemaphore(cv->wait_sem, num_waiting, NULL); - LeaveCriticalSection(&cv->countlock); - // there's no call to wait for the same object multiple times so we need to loop through - // and burn up the semaphore count - for (i = 0; i < num_waiting; i++) - WaitForSingleObject(cv->wait_done, INFINITE); + SetEvent(cv->evnt); + cv->release = cv->waiters; + cv->waitgeneration++; } - else - LeaveCriticalSection(&cv->countlock); + LeaveCriticalSection(&cv->countlock); + + LeaveCriticalSection(&cv->mainlock); return true; } @@ -312,8 +383,11 @@ void Sys_DestroyConditional(void *condv) { condvar_t *cv = (condvar_t *)condv; - CloseHandle(cv->wait_done); - CloseHandle(cv->wait_sem); + //make sure noone is still trying to poke it while shutting down +// Sys_LockConditional(condv); +// Sys_UnlockConditional(condv); + + CloseHandle(cv->evnt); DeleteCriticalSection(&cv->countlock); DeleteCriticalSection(&cv->mainlock); free(cv); diff --git a/engine/common/translate.c b/engine/common/translate.c index 8e0cd0be..16386ca8 100644 --- a/engine/common/translate.c +++ b/engine/common/translate.c @@ -171,7 +171,7 @@ void T_LoadString(void) //count new lines strings_loaded = true; strings_count = 0; - strings_list = FS_LoadMallocFile("strings.txt"); + strings_list = FS_LoadMallocFile("strings.txt", NULL); if (!strings_list) return; @@ -237,7 +237,7 @@ void T_LoadInfoString(void) //count new lines info_strings_loaded = true; info_strings_count = 0; - info_strings_list = FS_LoadMallocFile("infolist.txt"); + info_strings_list = FS_LoadMallocFile("infolist.txt", NULL); if (!info_strings_list) return; diff --git a/engine/common/world.h b/engine/common/world.h index e3933917..fe79254b 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -289,5 +289,16 @@ qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis qboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist); qboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3]); +// +// sv_phys.c +// void WPhys_Init(void); void World_Physics_Frame(world_t *w); +void SV_SetMoveVars(void); +void WPhys_RunNewmis (world_t *w); +qboolean SV_Physics (void); +void WPhys_CheckVelocity (world_t *w, wedict_t *ent); +trace_t WPhys_Trace_Toss (world_t *w, wedict_t *ent, wedict_t *ignore); +void SV_ProgStartFrame (void); +void WPhys_RunEntity (world_t *w, wedict_t *ent); +qboolean WPhys_RunThink (world_t *w, wedict_t *ent); diff --git a/engine/common/zone.c b/engine/common/zone.c index 9be47dd3..d68ca8d9 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -509,6 +509,8 @@ void Hunk_TempFree(void) { hnktemps_t *nt; + COM_AssertMainThread("Hunk_TempFree"); + while (hnktemps) { #if TEMPDEBUG>0 @@ -543,8 +545,10 @@ void Hunk_TempFree(void) void *Hunk_TempAllocMore (int size) { void *buf; + #if TEMPDEBUG>0 hnktemps_t *nt; + COM_AssertMainThread("Hunk_TempAllocMore"); nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t) + TEMPDEBUG*2); if (!nt) return NULL; @@ -559,6 +563,7 @@ void *Hunk_TempAllocMore (int size) return buf; #else hnktemps_t *nt; + COM_AssertMainThread("Hunk_TempAllocMore"); nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t)); if (!nt) return NULL; diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index 45b29a17..c45ba69f 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -204,10 +204,10 @@ typedef struct unsigned int curlmode; shader_t *shader_rtlight[LSHADER_MODES]; - texid_t curtex[MAX_TMUS]; + unsigned int texflags[MAX_TMUS]; unsigned int tmuflags[MAX_TMUS]; ID3D11SamplerState *cursamplerstate[MAX_TMUS]; - ID3D11SamplerState *sampstate[(SHADER_PASS_NEAREST|SHADER_PASS_CLAMP|SHADER_PASS_DEPTHCMP)+1]; + ID3D11SamplerState *sampstate[(SHADER_PASS_IMAGE_FLAGS|SHADER_PASS_DEPTHCMP)+1]; ID3D11DepthStencilState *depthstates[1u<<4]; //index, its fairly short. blendstates_t *blendstates; //list. this could get big. @@ -254,28 +254,38 @@ static d3d11backend_t shaderstate; extern int be_maxpasses; -static void BE_CreateSamplerStates(void) +void D3D11_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis) { D3D11_SAMPLER_DESC sampdesc; int flags; - for (flags = 0; flags <= (SHADER_PASS_CLAMP|SHADER_PASS_NEAREST|SHADER_PASS_DEPTHCMP); flags++) + for (flags = 0; flags <= (SHADER_PASS_IMAGE_FLAGS|SHADER_PASS_DEPTHCMP); flags++) { - if (flags & SHADER_PASS_DEPTHCMP) - { - if (flags & SHADER_PASS_NEAREST) - sampdesc.Filter = D3D11_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT; - else - sampdesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT; - sampdesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; - } + int *filter; + sampdesc.Filter = 0; + filter = (flags & SHADER_PASS_UIPIC)?filterpic:filtermip; + if ((filter[2] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR)) + sampdesc.Filter |= D3D11_FILTER_TYPE_LINEAR< 1) + sampdesc.Filter = D3D11_FILTER_ANISOTROPIC; + if (flags & SHADER_PASS_DEPTHCMP) + sampdesc.Filter |= D3D11_COMPARISON_FILTERING_BIT; + + if (flags & SHADER_PASS_DEPTHCMP) + sampdesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; else - { - if (flags & SHADER_PASS_NEAREST) - sampdesc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - else - sampdesc.Filter = /*D3D11_FILTER_MIN_MAG_MIP_POINT;*/D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;/*D3D11_FILTER_MIN_MAG_MIP_LINEAR*/; sampdesc.ComparisonFunc = D3D11_COMPARISON_NEVER; - } if (flags & SHADER_PASS_CLAMP) { sampdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; @@ -289,14 +299,27 @@ static void BE_CreateSamplerStates(void) sampdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; } sampdesc.MipLODBias = 0.0f; - sampdesc.MaxAnisotropy = 1; + sampdesc.MaxAnisotropy = bound(1, anis, 16); sampdesc.BorderColor[0] = 0; sampdesc.BorderColor[1] = 0; sampdesc.BorderColor[2] = 0; sampdesc.BorderColor[3] = 0; - sampdesc.MinLOD = 0; - sampdesc.MaxLOD = D3D11_FLOAT32_MAX; + if (flags & SHADER_PASS_NOMIPMAP) + { //only ever use the biggest mip if multiple are present + sampdesc.MinLOD = 0; + sampdesc.MaxLOD = 0; + } + else + { + sampdesc.MinLOD = mipcap[0]; + sampdesc.MaxLOD = mipcap[1]; + } + if (shaderstate.sampstate[flags]) + ID3D11SamplerState_Release(shaderstate.sampstate[flags]); + shaderstate.sampstate[flags] = NULL; + + //returns the same pointer for dupes, supposedly, so no need to check that ID3D11Device_CreateSamplerState(pD3DDev11, &sampdesc, &shaderstate.sampstate[flags]); } } @@ -313,7 +336,7 @@ static void BE_DestroyVariousStates(void) if (d3ddevctx && i) ID3D11DeviceContext_PSSetSamplers(d3ddevctx, 0, i, shaderstate.cursamplerstate); - for (flags = 0; flags <= (SHADER_PASS_CLAMP|SHADER_PASS_NEAREST|SHADER_PASS_DEPTHCMP); flags++) + for (flags = 0; flags <= (SHADER_PASS_IMAGE_FLAGS|SHADER_PASS_DEPTHCMP); flags++) { if (shaderstate.sampstate[flags]) ID3D11SamplerState_Release(shaderstate.sampstate[flags]); @@ -384,7 +407,8 @@ static void BE_ApplyTMUState(unsigned int tu, unsigned int flags) { ID3D11SamplerState *nstate; - flags = flags & (SHADER_PASS_CLAMP|SHADER_PASS_NEAREST|SHADER_PASS_DEPTHCMP); + flags = (flags & (SHADER_PASS_IMAGE_FLAGS|SHADER_PASS_DEPTHCMP)); + flags |= shaderstate.texflags[tu]; nstate = shaderstate.sampstate[flags]; if (nstate != shaderstate.cursamplerstate[tu]) { @@ -717,7 +741,7 @@ void D3D11BE_Init(void) for (i = 0; i < MAXRLIGHTMAPS; i++) shaderstate.dummybatch.lightmap[i] = -1; - BE_CreateSamplerStates(); +// BE_CreateSamplerStates(); // FTable_Init(); @@ -856,14 +880,16 @@ static unsigned int allocindexbuffer(void **dest, unsigned int entries) } #endif -ID3D11ShaderResourceView *D3D11_Image_View(const texid_t *id); -static void BindTexture(unsigned int tu, const texid_t *id) +ID3D11ShaderResourceView *D3D11_Image_View(const texid_t id); +static void BindTexture(unsigned int tu, const texid_t id) { ID3D11ShaderResourceView *view = D3D11_Image_View(id); if (shaderstate.pendingtextures[tu] != view) { shaderstate.textureschanged = true; shaderstate.pendingtextures[tu] = view; + if (id) + shaderstate.texflags[tu] = id->flags&SHADER_PASS_IMAGE_FLAGS; } } @@ -882,52 +908,51 @@ void D3D11BE_UnbindAllTextures(void) static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass) { extern texid_t r_whiteimage, missing_texture_gloss, missing_texture_normal; - texid_t foo; switch(pass->texgen) { default: case T_GEN_DIFFUSE: - BindTexture(tu, &shaderstate.curtexnums->base); + BindTexture(tu, shaderstate.curtexnums->base); break; case T_GEN_NORMALMAP: if (TEXVALID(shaderstate.curtexnums->bump)) - BindTexture(tu, &shaderstate.curtexnums->bump); + BindTexture(tu, shaderstate.curtexnums->bump); else - BindTexture(tu, &missing_texture_normal); + BindTexture(tu, missing_texture_normal); break; case T_GEN_SPECULAR: if (TEXVALID(shaderstate.curtexnums->specular)) - BindTexture(tu, &shaderstate.curtexnums->specular); + BindTexture(tu, shaderstate.curtexnums->specular); else - BindTexture(tu, &missing_texture_gloss); + BindTexture(tu, missing_texture_gloss); break; case T_GEN_UPPEROVERLAY: - BindTexture(tu, &shaderstate.curtexnums->upperoverlay); + BindTexture(tu, shaderstate.curtexnums->upperoverlay); break; case T_GEN_LOWEROVERLAY: - BindTexture(tu, &shaderstate.curtexnums->loweroverlay); + BindTexture(tu, shaderstate.curtexnums->loweroverlay); break; case T_GEN_FULLBRIGHT: - BindTexture(tu, &shaderstate.curtexnums->fullbright); + BindTexture(tu, shaderstate.curtexnums->fullbright); break; case T_GEN_ANIMMAP: - BindTexture(tu, &pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]); + BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]); break; case T_GEN_3DMAP: case T_GEN_CUBEMAP: case T_GEN_SINGLEMAP: - BindTexture(tu, &pass->anim_frames[0]); + BindTexture(tu, pass->anim_frames[0]); break; case T_GEN_DELUXMAP: { int lmi = shaderstate.curbatch->lightmap[0]; if (lmi < 0 || !lightmap[lmi]->hasdeluxe) - BindTexture(tu, &r_nulltex); + BindTexture(tu, r_nulltex); else { lmi+=1; - BindTexture(tu, &lightmap[lmi]->lightmap_texture); + BindTexture(tu, lightmap[lmi]->lightmap_texture); } } break; @@ -935,9 +960,9 @@ static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass) { int lmi = shaderstate.curbatch->lightmap[0]; if (lmi < 0) - BindTexture(tu, &r_whiteimage); + BindTexture(tu, r_whiteimage); else - BindTexture(tu, &lightmap[lmi]->lightmap_texture); + BindTexture(tu, lightmap[lmi]->lightmap_texture); } break; @@ -948,28 +973,26 @@ static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass) #ifndef NOMEDIA if (pass->cin) { - foo = Media_UpdateForShader(pass->cin); - BindTexture(tu, &foo); + BindTexture(tu, Media_UpdateForShader(pass->cin)); break; } #endif - BindTexture(tu, &r_nulltex); + BindTexture(tu, r_nulltex); break; case T_GEN_LIGHTCUBEMAP: //light's projected cubemap - BindTexture(tu, &shaderstate.curdlight->cubetexture); + BindTexture(tu, shaderstate.curdlight->cubetexture); break; case T_GEN_SHADOWMAP: //light's depth values. #ifdef RTLIGHTS if (shaderstate.curdlight) { - foo = D3D11_GetShadowMap(shaderstate.curdlight->fov>0); - BindTexture(tu, &foo); + BindTexture(tu, D3D11_GetShadowMap(shaderstate.curdlight->fov>0)); break; } #endif - BindTexture(tu, &r_nulltex); + BindTexture(tu, r_nulltex); break; case T_GEN_CURRENTRENDER://copy the current screen to a texture, and draw that @@ -983,7 +1006,7 @@ static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass) case T_GEN_RIPPLEMAP: //ripplemap image (water surface distortions-as-fbo) case T_GEN_SOURCECUBE: //used for render-to-texture targets - BindTexture(tu, &r_nulltex); + BindTexture(tu, r_nulltex); break; } @@ -1899,7 +1922,6 @@ static void BE_RenderMeshProgram(const shader_t *s, unsigned int vertcount, unsi BE_ApplyUniforms(p, perm); - D3D11BE_ApplyShaderBits(s->passes->shaderbits, &s->passes->becache); /*activate tmus*/ @@ -2156,7 +2178,7 @@ qboolean D3D11BE_GenerateRTLightShader(unsigned int lmode) return false; return true; } -qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) +qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode) { if (!D3D11BE_GenerateRTLightShader(lmode)) { diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index 558471ca..c5b0e57f 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -11,298 +11,31 @@ extern D3D_FEATURE_LEVEL d3dfeaturelevel; void D3D11BE_UnbindAllTextures(void); -//FIXME: need support for cubemaps. -typedef struct d3dtexture_s +ID3D11ShaderResourceView *D3D11_Image_View(const texid_t id) { - texcom_t com; - ID3D11ShaderResourceView *view; - struct d3dtexture_s *prev; - struct d3dtexture_s *next; - ID3D11Texture2D *tex2d; - char name[1]; -} d3d11texture_t; -static d3d11texture_t *d3d11textures; - -ID3D11ShaderResourceView *D3D11_Image_View(const texid_t *id) -{ - d3d11texture_t *info = (d3d11texture_t*)id->ref; - if (!info) + if (!id || !id->ptr) return NULL; - if (!info->view) - ID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)info->tex2d, NULL, &info->view); - return info->view; -} - -void D3D11_Image_Shutdown(void) -{ - //destroy all named textures - while(d3d11textures) - { - d3d11texture_t *t = d3d11textures; - d3d11textures = t->next; - - if (t->view) - ID3D11ShaderResourceView_Release(t->view); - if (t->tex2d) - ID3D11Texture2D_Release(t->tex2d); - - free(t); - } + if (!id->ptr2) + ID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)id->ptr, NULL, (ID3D11ShaderResourceView**)&id->ptr2); + return id->ptr2; } void D3D11_DestroyTexture (texid_t tex) { - d3d11texture_t *t = (d3d11texture_t*)tex.ref; - - ID3D11Texture2D *tx = tex.ptr; - - if (!t) + if (!tex) return; - if (t->view) - ID3D11ShaderResourceView_Release(t->view); - if (t->tex2d) - ID3D11Texture2D_Release(t->tex2d); - t->view = NULL; - t->tex2d = NULL; + if (tex->ptr2) + ID3D11ShaderResourceView_Release((ID3D11ShaderResourceView*)tex->ptr2); + tex->ptr2 = NULL; - if (t->prev) - t->prev->next = t->next; - else - d3d11textures = t->next; - if (t->next) - t->next->prev = t->prev; - t->prev = NULL; - t->next = NULL; - free(t); -} - -static d3d11texture_t *d3d_lookup_texture(const char *ident) -{ - d3d11texture_t *tex; - - if (*ident) - { - for (tex = d3d11textures; tex; tex = tex->next) - if (!strcmp(tex->name, ident)) - return tex; - } - - tex = calloc(1, sizeof(*tex)+strlen(ident)); - strcpy(tex->name, ident); - tex->view = NULL; - tex->tex2d = NULL; - tex->next = d3d11textures; - tex->prev = NULL; - d3d11textures = tex; - if (tex->next) - tex->next->prev = tex; - - return tex; -} - -extern cvar_t gl_picmip; -extern cvar_t gl_picmip2d; - -static texid_t ToTexID(d3d11texture_t *tex) -{ - texid_t tid; - tid.ref = &tex->com; - if (!tex->view) - ID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)tex->tex2d, NULL, &tex->view); - tid.ptr = NULL;//(void*)0xdeadbeef; - return tid; -} - -//outpitch = width*4 -static void D3D_Resize32(void *out, int outpitch, int outwidth, int outheight, const void *in, int inwidth, int inheight) -{ - int x, y; - int iny; - unsigned int *row; - const unsigned int *inrow; - - for (y = 0; y < outheight; y++) - { - row = (unsigned int*)((char*)out + outpitch*y); - iny = (y * inheight) / outheight; - inrow = (const unsigned int*)in + inwidth*iny; - for (x = 0; x < outwidth; x++) - { - row[x] = inrow[(x * inwidth)/outwidth]; - } - } -} -static void D3D_MipMap (qbyte *out, int outwidth, int outheight, const qbyte *in, int inwidth, int inheight) -{ - int i, j; - const qbyte *inrow; - - //with npot - int rowwidth = inwidth*4; //rowwidth is the byte width of the input - inrow = in; - - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; - out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; - out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; - } - } -} - -static void *D3D11_AllocNewTextureData(void *datargba, int datawidth, int dataheight, int width, int height, unsigned int flags) -{ - HRESULT hr; - ID3D11Texture2D *tx = NULL; - D3D11_TEXTURE2D_DESC tdesc = {0}; - D3D11_SUBRESOURCE_DATA subresdesc[64] = {0}; - int i; - int owidth, oheight; - qboolean fakenpot = false; - - tdesc.Width = width; - tdesc.Height = height; - tdesc.MipLevels = (!datargba || (flags & IF_NOMIPMAP))?1:0; //0 generates mipmaps automagically - tdesc.ArraySize = 1; - tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - tdesc.SampleDesc.Count = 1; - tdesc.SampleDesc.Quality = 0; - tdesc.Usage = datargba?D3D11_USAGE_IMMUTABLE:D3D11_USAGE_DYNAMIC; - tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - tdesc.CPUAccessFlags = (datargba)?0:D3D11_CPU_ACCESS_WRITE; - tdesc.MiscFlags = 0; - - //first mip level - subresdesc[0].SysMemPitch = width*4; - subresdesc[0].SysMemSlicePitch = width*height*4; - - if (!datargba) - { - subresdesc[0].pSysMem = NULL; - //one mip, but no data - i = 1; - } - else - { - if (datawidth != width || dataheight != height) - { - fakenpot = true; - subresdesc[0].pSysMem = malloc(width*height*4); - D3D_Resize32((void*)subresdesc[0].pSysMem, width*4, width, height, datargba, datawidth, dataheight); - } - else - subresdesc[0].pSysMem = datargba; - for (i = 1; i < 64 && width > 1 && height > 1 && !(flags & IF_NOMIPMAP); i++) - { - owidth = width; - oheight = height; - width /= 2; - height /= 2; - - subresdesc[i].pSysMem = malloc(width*height*4); - subresdesc[i].SysMemPitch = width*4; - subresdesc[i].SysMemSlicePitch = width*height*4; - - D3D_MipMap((void*)subresdesc[i].pSysMem, width, height, subresdesc[i-1].pSysMem, owidth, oheight); - } - } - - tdesc.MipLevels = i; //0 generates mipmaps automagically - - hr = ID3D11Device_CreateTexture2D(pD3DDev11, &tdesc, (subresdesc[0].pSysMem?subresdesc:NULL), &tx); - if (FAILED(hr)) - { - Con_Printf("Failed to create texture\n"); - tx = NULL; - } - - for (i = fakenpot?0:1; i < tdesc.MipLevels; i++) - { - free((void*)subresdesc[i].pSysMem); - } - return tx; -} -texid_t D3D11_AllocNewTexture(const char *ident, int width, int height, unsigned int flags) -{ - d3d11texture_t *t = d3d_lookup_texture(""); - texid_t id; - if (t->tex2d) - return ToTexID(t); - t->tex2d = D3D11_AllocNewTextureData(NULL, 0, 0, width, height, flags); - t->com.width = width; - t->com.height = height; - - id = ToTexID(t); - if (!t->tex2d) - { - D3D11_DestroyTexture(id); - return r_nulltex; - } - - return id; -} - -static void D3D11_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap, qboolean clamp) -{ - int maxsize; - if (D3D_HAVE_FULL_NPOT() || (!mipmap && clamp)) - { //NPOT is a simple extension that relaxes errors. - //featurelevel 9 supports npot but only if there's no mipmapping or texture coord wrapping. - TRACE(("dbg: D3D11_RoundDimensions: npot\n")); - } - else - { - int width = *scaled_width; - int height = *scaled_height; - for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1) - ; - for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1) - ; - } - - if (mipmap) - { - TRACE(("dbg: GL_RoundDimensions: %i\n", gl_picmip.ival)); - *scaled_width >>= gl_picmip.ival; - *scaled_height >>= gl_picmip.ival; - } - else - { - *scaled_width >>= gl_picmip2d.ival; - *scaled_height >>= gl_picmip2d.ival; - } - - if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_11_0) - maxsize = 16384; - else if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_10_0) - maxsize = 8192; - else if (d3dfeaturelevel>=D3D_FEATURE_LEVEL_9_3) - maxsize = 4096; - else - maxsize = 2048; - if (gl_max_size.ival && maxsize > gl_max_size.ival) - maxsize = gl_max_size.ival; - - TRACE(("dbg: GL_RoundDimensions: %i\n", maxsize)); - if (maxsize) - { - if (*scaled_width > maxsize) - *scaled_width = maxsize; - if (*scaled_height > maxsize) - *scaled_height = maxsize; - } - - if (*scaled_width < 1) - *scaled_width = 1; - if (*scaled_height < 1) - *scaled_height = 1; + if (tex->ptr) + ID3D11Texture2D_Release((ID3D11Texture2D*)tex->ptr); + tex->ptr = NULL; } +#if 0 static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int datawidth, int dataheight, unsigned int flags) { int x, y; @@ -373,377 +106,117 @@ static void Upload_Texture_32(ID3D11Texture2D *tex, unsigned int *data, int data ID3D11DeviceContext_Unmap(d3ddevctx, (ID3D11Resource*)tex, 0); #endif } +#endif -//create a basic shader from a 32bit image -static void D3D11_LoadTexture_32(d3d11texture_t *tex, unsigned int *data, int width, int height, int flags) +qboolean D3D11_LoadTextureMips(image_t *tex, struct pendingtextureinfo *mips) { - int nwidth, nheight; + HRESULT hr; + D3D11_TEXTURE2D_DESC tdesc = {0}; + D3D11_SUBRESOURCE_DATA subresdesc[sizeof(mips->mip) / sizeof(mips->mip[0])]; + int i; -/* - if (!(flags & TF_MANDATORY)) + tdesc.Width = mips->mip[0].width; + tdesc.Height = mips->mip[0].height; + tdesc.ArraySize = 1; + tdesc.SampleDesc.Count = 1; + tdesc.SampleDesc.Quality = 0; + tdesc.Usage = mips->mip[0].data?D3D11_USAGE_IMMUTABLE:D3D11_USAGE_DYNAMIC; + tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + tdesc.CPUAccessFlags = (mips->mip[0].data)?0:D3D11_CPU_ACCESS_WRITE; + tdesc.MiscFlags = 0; + + if (mips->type == PTI_CUBEMAP) { - Con_Printf("Texture upload missing flags\n"); - return NULL; + tdesc.ArraySize *= 6; + tdesc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; } -*/ + else if (mips->type == PTI_3D) + return false; //nyi - nwidth = width; - nheight = height; - D3D11_RoundDimensions(&nwidth, &nheight, !(flags & IF_NOMIPMAP), flags & IF_CLAMP); - - if (!tex->tex2d || tex->com.width != nwidth || tex->com.height != nheight) + switch(mips->encoding) { - tex->com.width = nwidth; - tex->com.height = nheight; - if (tex->tex2d) - ID3D11Texture2D_Release(tex->tex2d); + case PTI_RGBA8: + tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case PTI_RGBX8: + tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; //d3d11 has no alphaless format. be sure to proprly disable alpha in the shader. + break; + case PTI_BGRA8: + tdesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + break; + case PTI_BGRX8: + tdesc.Format = DXGI_FORMAT_B8G8R8X8_UNORM; + break; - tex->tex2d = D3D11_AllocNewTextureData(data, width, height, nwidth, nheight, flags); - return; + case PTI_S3RGB1: //d3d11 provides no way to disable alpha with dxt1. be sure to proprly disable alpha in the shader. + case PTI_S3RGBA1: + tdesc.Format = DXGI_FORMAT_BC1_UNORM; + break; + case PTI_S3RGBA3: + tdesc.Format = DXGI_FORMAT_BC2_UNORM; + break; + case PTI_S3RGBA5: + tdesc.Format = DXGI_FORMAT_BC3_UNORM; + break; } - else - Upload_Texture_32(tex->tex2d, data, width, height, flags); -} -static void D3D11_LoadTexture_8(d3d11texture_t *tex, unsigned char *data, unsigned int *pal32, int width, int height, int flags, enum uploadfmt fmt) -{ - static unsigned trans[1024*1024]; - int i, s; - qboolean noalpha; - int p; - - if (width*height > 1024*1024) - Sys_Error("GL_Upload8: image too big (%i*%i)", width, height); - - s = width*height; - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (fmt == TF_8PAL24) + if (!mips->mip[0].data) { - unsigned char *pal24 = (void*)pal32; - //strictly bgr little endian. - for (i=0 ; i 255-vid.fullbright) - trans[i] = pal32[p]; - else - { - noalpha = false; - trans[i] = 0; - } - } - } - else if ((fmt!=TF_SOLID8) && !(flags & IF_NOALPHA)) - { - noalpha = true; - for (i=0 ; i>4]] & 0x00ffffff; - trans[i] |= ( int )ColorPercent[p&15] << 24; - //trans[i] = 0x7fff0000; - } - break; -*/ - } + subresdesc[0].pSysMem = NULL; + //one mip, but no data. happens with rendertargets + tdesc.MipLevels = 1; } else { - for (i=(s&~3)-4 ; i>=0 ; i-=4) + for (i = 0; i < mips->mipcount; i++) { - trans[i] = pal32[data[i]]; - trans[i+1] = pal32[data[i+1]]; - trans[i+2] = pal32[data[i+2]]; - trans[i+3] = pal32[data[i+3]]; - } - for (i=s&~3 ; imip[i].data; + subresdesc[i].SysMemPitch = mips->mip[i].width*4; + subresdesc[i].SysMemSlicePitch = mips->mip[i].width*mips->mip[i].height*4; } + tdesc.MipLevels = i/tdesc.ArraySize; } - D3D11_LoadTexture_32(tex, trans, width, height, flags); -} -void D3D11_Upload (texid_t id, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) -{ - d3d11texture_t *tex = (d3d11texture_t *)id.ref; - switch (fmt) + D3D11_DestroyTexture(tex); + hr = ID3D11Device_CreateTexture2D(pD3DDev11, &tdesc, (mips->mip[0].data?subresdesc:NULL), (ID3D11Texture2D**)&tex->ptr); + + for (i = 0; i < mips->mipcount; i++) { - case TF_RGBX32: - flags |= IF_NOALPHA; - //fall through - case TF_RGBA32: - Upload_Texture_32(tex->tex2d, data, width, height, flags); - if (tex->view) - { - tex->view->lpVtbl->Release(tex->view); - tex->view = NULL; - } - ToTexID(tex); - break; - case TF_8PAL24: - D3D11_LoadTexture_8(tex, data, palette, width, height, flags, fmt); - if (tex->view) - { - tex->view->lpVtbl->Release(tex->view); - tex->view = NULL; - } - ToTexID(tex); - break; - case TF_TRANS8: - OutputDebugString(va("D3D11_LoadTextureFmt doesn't support fmt TF_TRANS8 (%s)\n", fmt, name)); - break; - default: - OutputDebugString(va("D3D11_LoadTextureFmt doesn't support fmt %i (%s)\n", fmt, name)); - break; + if (mips->mip[i].needfree) + BZ_Free(mips->mip[i].data); } -} + if (mips->extrafree) + BZ_Free(mips->extrafree); + return !FAILED(hr); +} void D3D11_UploadLightmap(lightmapinfo_t *lm) { - d3d11texture_t *tex; + extern cvar_t gl_lightmap_nearest; + struct pendingtextureinfo mips; + image_t *tex; lm->modified = false; if (!TEXVALID(lm->lightmap_texture)) { - lm->lightmap_texture = R_AllocNewTexture("***lightmap***", LMBLOCK_WIDTH, LMBLOCK_HEIGHT, 0); - if (!lm->lightmap_texture.ref) + lm->lightmap_texture = Image_CreateTexture("***lightmap***", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)); + if (!lm->lightmap_texture) return; } - tex = (d3d11texture_t*)lm->lightmap_texture.ref; + tex = lm->lightmap_texture; - if (!tex->tex2d) - tex->tex2d = D3D11_AllocNewTextureData(lm->lightmaps, lm->width, lm->height, lm->width, lm->height, 0); - else - { - if (tex->view) - { - ID3D11ShaderResourceView_Release(tex->view); - tex->view = NULL; - } - Upload_Texture_32(tex->tex2d, (void*)lm->lightmaps, lm->width, lm->height, 0); - } - tex->com.width = lm->width; - tex->com.height = lm->height; + mips.extrafree = NULL; + mips.type = PTI_2D; + mips.mip[0].data = lm->lightmaps; + mips.mip[0].needfree = false; + mips.mip[0].width = lm->width; + mips.mip[0].height = lm->height; + mips.encoding = PTI_RGBX8; + mips.mipcount = 1; + D3D11_LoadTextureMips(tex, &mips); + tex->width = lm->width; + tex->height = lm->height; - lm->lightmap_texture = ToTexID(tex); -} - -static void genNormalMap(unsigned int *nmap, qbyte *pixels, int w, int h, float scale) -{ - int i, j, wr, hr; - unsigned char r, g, b; - float sqlen, reciplen, nx, ny, nz; - - const float oneOver255 = 1.0f/255.0f; - - float c, cx, cy, dcx, dcy; - - wr = w; - hr = h; - - for (i=0; i Added support for big endian. - } - } -} - - -texid_t D3D11_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) -{ - d3d11texture_t *tex; - switch (fmt) - { - case TF_TRANS8_FULLBRIGHT: - { - qbyte *d = data; - unsigned int c = width * height; - while (c) - { - if (d[--c] > 255 - vid.fullbright) - break; - } - /*reject it if there's no fullbrights*/ - if (!c) - return r_nulltex; - } - break; - case TF_INVALID: - case TF_RGBA32: - case TF_BGRA32: - case TF_RGBX32: - case TF_RGB24: - case TF_BGR24_FLIP: - case TF_SOLID8: - case TF_TRANS8: - case TF_HEIGHT8: - case TF_HEIGHT8PAL: - case TF_H2_T7G1: - case TF_H2_TRANS8_0: - case TF_H2_T4A4: - case TF_PALETTES: - case TF_8PAL24: - case TF_8PAL32: - break; - - } - tex = d3d_lookup_texture(identifier); - if (tex->tex2d) //already loaded - return ToTexID(tex); - - switch (fmt) - { - case TF_HEIGHT8PAL: - { - extern cvar_t r_shadow_bumpscale_basetexture; - unsigned int *norm = malloc(width*height*4); - genNormalMap(norm, data, width, height, r_shadow_bumpscale_basetexture.value); - D3D11_LoadTexture_32(tex, norm, width, height, flags); - free(norm); - return ToTexID(tex); - } - case TF_HEIGHT8: - { - extern cvar_t r_shadow_bumpscale_basetexture; - unsigned int *norm = malloc(width*height*4); - genNormalMap(norm, data, width, height, r_shadow_bumpscale_basetexture.value); - D3D11_LoadTexture_32(tex, norm, width, height, flags); - free(norm); - return ToTexID(tex); - } - case TF_SOLID8: - case TF_TRANS8: - case TF_H2_T7G1: - case TF_H2_TRANS8_0: - case TF_H2_T4A4: - case TF_TRANS8_FULLBRIGHT: - D3D11_LoadTexture_8(tex, data, d_8to24rgbtable, width, height, flags, fmt); - return ToTexID(tex); - case TF_RGBX32: - flags |= IF_NOALPHA; - case TF_RGBA32: - D3D11_LoadTexture_32(tex, data, width, height, flags); - return ToTexID(tex); - default: - OutputDebugString(va("D3D11_LoadTexture doesn't support fmt %i (%s)\n", fmt, identifier)); - return r_nulltex; - } -} - -texid_t D3D11_LoadCompressed (const char *name) -{ - return r_nulltex; -} - -texid_t D3D11_FindTexture (const char *identifier, unsigned int flags) -{ - d3d11texture_t *tex = d3d_lookup_texture(identifier); - if (tex->tex2d) - return ToTexID(tex); - return r_nulltex; -} - -texid_t D3D11_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) -{ - d3d11texture_t *tex = d3d_lookup_texture(identifier); - D3D11_LoadTexture_8(tex, data, (unsigned int *)palette32, width, height, flags, TF_SOLID8); - return ToTexID(tex); -} -texid_t D3D11_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) -{ - unsigned int pal32[256]; - int i; - for (i = 0; i < 256; i++) - { - pal32[i] = (255<<24) | - (palette24[i*3+2]<<16) | - (palette24[i*3+1]<<8) | - (palette24[i*3+0]<<0); - } - return D3D11_LoadTexture8Pal32(identifier, width, height, data, (qbyte*)pal32, flags); + lm->lightmap_texture = tex; } #ifdef RTLIGHTS @@ -755,29 +228,26 @@ static const int shadowfmts[][3] = {DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM}, {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM} }; -d3d11texture_t shadowmap_texture[2]; +image_t shadowmap_texture[2]; ID3D11DepthStencilView *shadowmap_dsview[2]; ID3D11RenderTargetView *shadowmap_rtview[2]; texid_t D3D11_GetShadowMap(int id) { - d3d11texture_t *tex = &shadowmap_texture[id]; - texid_t tid; - if (!tex->tex2d) + texid_t tex = &shadowmap_texture[id]; + if (!tex->ptr) { return r_nulltex; } - tid.ref = &tex->com; - if (!tex->view) + if (!tex->ptr2) { D3D11_SHADER_RESOURCE_VIEW_DESC desc; desc.Format = shadowfmts[shadowfmt][0]; desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; desc.Texture2D.MostDetailedMip = 0; desc.Texture2D.MipLevels = -1; - ID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)tex->tex2d, &desc, &tex->view); + ID3D11Device_CreateShaderResourceView(pD3DDev11, (ID3D11Resource *)tex->ptr, &desc, (ID3D11ShaderResourceView**)&tex->ptr2); } - tid.ptr = NULL; - return tid; + return tex; } void D3D11_TerminateShadowMap(void) { @@ -787,9 +257,7 @@ void D3D11_TerminateShadowMap(void) if (shadowmap_dsview[i]) ID3D11DepthStencilView_Release(shadowmap_dsview[i]); shadowmap_dsview[i] = NULL; - if (shadowmap_texture[i].tex2d) - ID3D11DepthStencilView_Release(shadowmap_texture[i].tex2d); - shadowmap_texture[i].tex2d = NULL; + D3D11_DestroyTexture(&shadowmap_texture[i]); } } qboolean D3D11_BeginShadowMap(int id, int w, int h) @@ -817,9 +285,9 @@ qboolean D3D11_BeginShadowMap(int id, int w, int h) texdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; // Create the texture - if (!shadowmap_texture[id].tex2d) + if (!shadowmap_texture[id].ptr) { - hr = ID3D11Device_CreateTexture2D(pD3DDev11, &texdesc, NULL, &shadowmap_texture[id].tex2d); + hr = ID3D11Device_CreateTexture2D(pD3DDev11, &texdesc, NULL, (ID3D11Texture2D **)&shadowmap_texture[id].ptr); if (FAILED(hr)) return false; } @@ -827,7 +295,7 @@ qboolean D3D11_BeginShadowMap(int id, int w, int h) if (shadowfmt == 2) { - hr = ID3D11Device_CreateRenderTargetView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].tex2d, NULL, &shadowmap_rtview[id]); + hr = ID3D11Device_CreateRenderTargetView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].ptr, NULL, &shadowmap_rtview[id]); } else { @@ -836,7 +304,7 @@ qboolean D3D11_BeginShadowMap(int id, int w, int h) rtdesc.Flags = 0; rtdesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; rtdesc.Texture2D.MipSlice = 0; - hr = ID3D11Device_CreateDepthStencilView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].tex2d, &rtdesc, &shadowmap_dsview[id]); + hr = ID3D11Device_CreateDepthStencilView(pD3DDev11, (ID3D11Resource *)shadowmap_texture[id].ptr, &rtdesc, &shadowmap_dsview[id]); } if (FAILED(hr)) return false; diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index a616d810..460a01fd 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -172,7 +172,8 @@ typedef struct vbo_t *batchvbo; shader_t *shader_rtlight; - texid_t curtex[MAX_TMUS]; + IDirect3DTexture9 *curtex[MAX_TMUS]; + unsigned int curtexflags[MAX_TMUS]; unsigned int tmuflags[MAX_TMUS]; mesh_t **meshlist; @@ -205,6 +206,9 @@ typedef struct unsigned int wbatch; unsigned int maxwbatches; batch_t *wbatches; + + int mipfilter[3]; + int picfilter[3]; } d3dbackend_t; typedef struct @@ -253,9 +257,11 @@ static IDirect3DVertexDeclaration9 *vertexdecls[D3D_VDEC_MAX]; static void BE_ApplyTMUState(unsigned int tu, unsigned int flags) { - if ((flags ^ shaderstate.tmuflags[tu]) & SHADER_PASS_CLAMP) + unsigned int delta = shaderstate.tmuflags[tu] ^ flags; + if (!delta) + return; + if (delta & SHADER_PASS_CLAMP) { - shaderstate.tmuflags[tu] ^= SHADER_PASS_CLAMP; if (flags & SHADER_PASS_CLAMP) { IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); @@ -269,22 +275,44 @@ static void BE_ApplyTMUState(unsigned int tu, unsigned int flags) IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); } } - if ((flags ^ shaderstate.tmuflags[tu]) & SHADER_PASS_NOMIPMAP) + if (delta & (SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC)) { - shaderstate.tmuflags[tu] ^= SHADER_PASS_NOMIPMAP; - /*lightmaps don't use mipmaps*/ - if (flags & SHADER_PASS_NOMIPMAP) - { - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MIPFILTER, D3DTEXF_NONE); - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - } + int min, mip, mag; + int *filter = (flags & SHADER_PASS_UIPIC)?shaderstate.picfilter:shaderstate.mipfilter; + + if ((filter[2] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR)) + mag = D3DTEXF_LINEAR; else - { - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); - IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - } + mag = D3DTEXF_POINT; + if (filter[1] == -1 || (flags & IF_NOMIPMAP)) + mip = D3DTEXF_NONE; + else if ((filter[1] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR)) + mip = D3DTEXF_LINEAR; + else + mip = D3DTEXF_POINT; + if ((filter[0] && !(flags & SHADER_PASS_NEAREST)) || (flags & SHADER_PASS_LINEAR)) + min = D3DTEXF_LINEAR; + else + min = D3DTEXF_POINT; + + IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MINFILTER, min); + IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MIPFILTER, mip); + IDirect3DDevice9_SetSamplerState(pD3DDev9, tu, D3DSAMP_MAGFILTER, mag); + } + shaderstate.tmuflags[tu] = flags; +} + +//d3d9 is all sampler state +void D3D9_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis) +{ + int i; + memcpy(shaderstate.mipfilter, filtermip, sizeof(shaderstate.mipfilter)); + memcpy(shaderstate.picfilter, filterpic, sizeof(shaderstate.picfilter)); + + for (i = 0; i < MAX_TMUS; i++) + { + shaderstate.tmuflags[i] = ~shaderstate.tmuflags[i]; + BE_ApplyTMUState(i, ~shaderstate.tmuflags[i]); } } @@ -655,12 +683,20 @@ static unsigned int allocindexbuffer(void **dest, unsigned int entries) return offset/sizeof(index_t); } -static void BindTexture(unsigned int tu, void *id) +static void BindTexture(unsigned int tu, texid_t tex) { - if (shaderstate.curtex[tu].ptr != id) + IDirect3DTexture9 *dt; + if (tex) { - shaderstate.curtex[tu].ptr = id; - IDirect3DDevice9_SetTexture (pD3DDev9, tu, id); + dt = tex->ptr; + shaderstate.curtexflags[tu] = tex->flags & SHADER_PASS_IMAGE_FLAGS; + } + else + dt = NULL; + if (shaderstate.curtex[tu] != dt) + { + shaderstate.curtex[tu] = dt; + IDirect3DDevice9_SetTexture (pD3DDev9, tu, (void*)dt); } } @@ -673,48 +709,48 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass) { default: case T_GEN_DIFFUSE: - if (shaderstate.curtexnums->base.ptr) - BindTexture(tu, shaderstate.curtexnums->base.ptr); + if (shaderstate.curtexnums->base) + BindTexture(tu, shaderstate.curtexnums->base); else - BindTexture(tu, missing_texture.ptr); + BindTexture(tu, missing_texture); break; case T_GEN_NORMALMAP: - BindTexture( tu, shaderstate.curtexnums->bump.ptr); + BindTexture( tu, shaderstate.curtexnums->bump); break; case T_GEN_SPECULAR: - BindTexture(tu, shaderstate.curtexnums->specular.ptr); + BindTexture(tu, shaderstate.curtexnums->specular); break; case T_GEN_UPPEROVERLAY: - BindTexture(tu, shaderstate.curtexnums->upperoverlay.ptr); + BindTexture(tu, shaderstate.curtexnums->upperoverlay); break; case T_GEN_LOWEROVERLAY: - BindTexture(tu, shaderstate.curtexnums->loweroverlay.ptr); + BindTexture(tu, shaderstate.curtexnums->loweroverlay); break; case T_GEN_FULLBRIGHT: - BindTexture(tu, shaderstate.curtexnums->fullbright.ptr); + BindTexture(tu, shaderstate.curtexnums->fullbright); break; case T_GEN_ANIMMAP: - BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes].ptr); + BindTexture(tu, pass->anim_frames[(int)(pass->anim_fps * shaderstate.curtime) % pass->anim_numframes]); break; case T_GEN_SINGLEMAP: - BindTexture(tu, pass->anim_frames[0].ptr); + BindTexture(tu, pass->anim_frames[0]); break; case T_GEN_DELUXMAP: - BindTexture(tu, shaderstate.curdeluxmap.ptr); + BindTexture(tu, shaderstate.curdeluxmap); break; case T_GEN_LIGHTMAP: - BindTexture(tu, shaderstate.curlightmap.ptr); + BindTexture(tu, shaderstate.curlightmap); break; /*case T_GEN_CURRENTRENDER: FIXME: no code to grab the current screen and convert to a texture break;*/ case T_GEN_VIDEOMAP: - BindTexture(tu, Media_UpdateForShader(pass->cin).ptr); + BindTexture(tu, Media_UpdateForShader(pass->cin)); break; } - BE_ApplyTMUState(tu, pass->flags); + BE_ApplyTMUState(tu, shaderstate.curtexflags[tu]|pass->flags); if (tu == 0) { @@ -2158,7 +2194,7 @@ void D3D9BE_SelectMode(backendmode_t mode) BE_ApplyShaderBits(SBITS_MASK_BITS); } -qboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) +qboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode) { shaderstate.curdlight = dl; VectorCopy(colour, shaderstate.curdlight_colours); @@ -2525,17 +2561,22 @@ static void BE_UploadLightmaps(qboolean force) if (lightmap[i]->modified) { - IDirect3DTexture9 *tex = lm->lightmap_texture.ptr; + extern cvar_t gl_lightmap_nearest; + IDirect3DTexture9 *tex; D3DLOCKED_RECT lock; RECT rect; glRect_t *theRect = &lm->rectchange; int r; + + if (!lm->lightmap_texture) + lm->lightmap_texture = Image_CreateTexture("", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP); + tex = lm->lightmap_texture->ptr; if (!tex) { - lm->lightmap_texture = R_AllocNewTexture("***lightmap***", lm->width, lm->height, 0); - tex = lm->lightmap_texture.ptr; + IDirect3DDevice9_CreateTexture(pD3DDev9, lm->width, lm->height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL); if (!tex) continue; + lm->lightmap_texture->ptr = tex; } lm->modified = 0; @@ -2713,10 +2754,10 @@ void D3D9BE_SubmitBatch(batch_t *batch) shaderstate.curtexnums = batch->skin?batch->skin:&batch->shader->defaulttextures; shaderstate.curbatch = batch; shaderstate.flags = batch->flags; - if (batch->lightmap[0] < 0) - shaderstate.curlightmap = r_nulltex; - else + if ((unsigned)batch->lightmap[0] < (unsigned)numlightmaps) shaderstate.curlightmap = lightmap[batch->lightmap[0]]->lightmap_texture; + else + shaderstate.curlightmap = r_nulltex; BE_DrawMeshChain_Internal(); } diff --git a/engine/d3d/d3d_image.c b/engine/d3d/d3d_image.c index 12ce62fd..307aac8f 100644 --- a/engine/d3d/d3d_image.c +++ b/engine/d3d/d3d_image.c @@ -8,156 +8,6 @@ #include extern LPDIRECT3DDEVICE9 pD3DDev9; -typedef struct d3dtexture_s -{ - texcom_t com; - struct d3dtexture_s *next; - texid_t tex; - char name[1]; -} d3dtexture_t; -static d3dtexture_t *d3dtextures; - -void D3D9_Image_Shutdown(void) -{ - LPDIRECT3DTEXTURE9 tx; - while(d3dtextures) - { - d3dtexture_t *t = d3dtextures; - d3dtextures = t->next; - - tx = t->tex.ptr; - if (tx) - IDirect3DTexture9_Release(tx); - - free(t); - } -} - -static d3dtexture_t *d3d_lookup_texture(const char *ident) -{ - d3dtexture_t *tex; - - if (*ident) - { - for (tex = d3dtextures; tex; tex = tex->next) - if (!strcmp(tex->name, ident)) - return tex; - } - - tex = calloc(1, sizeof(*tex)+strlen(ident)); - strcpy(tex->name, ident); - tex->tex.ptr = NULL; - tex->tex.ref = &tex->com; - tex->next = d3dtextures; - d3dtextures = tex; - - return tex; -} - -extern cvar_t gl_picmip; -extern cvar_t gl_picmip2d; - -texid_t D3D9_AllocNewTexture(const char *ident, int width, int height, unsigned int flags) -{ - IDirect3DTexture9 *tx; - - /*unconditionally allocate a new texture*/ - d3dtexture_t *tex = calloc(1, sizeof(*tex)+strlen(ident)); - strcpy(tex->name, ident); - tex->tex.ptr = NULL; - tex->tex.ref = &tex->com; - tex->next = d3dtextures; - d3dtextures = tex; - - if (!tex->tex.ptr) - { - if (!FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, width, height, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tx, NULL))) - tex->tex.ptr = tx; - } - return tex->tex; -} - -void D3D9_DestroyTexture (texid_t tex) -{ - d3dtexture_t **link; - for (link = &d3dtextures; *link; link = &(*link)->next) - { - if (*link == (d3dtexture_t*)tex.ref) - { - *link = (*link)->next; - if (tex.ptr) - IDirect3DTexture9_Release((IDirect3DTexture9*)tex.ptr); - return; - } - } -} - -static void D3D9_RoundDimensions(int *scaled_width, int *scaled_height, qboolean mipmap) -{ -// if (gl_config.arb_texture_non_power_of_two) //NPOT is a simple extension that relaxes errors. -// { -// TRACE(("dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\n")); -// } -// else - { - int width = *scaled_width; - int height = *scaled_height; - for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1) - ; - for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1) - ; - } - - if (mipmap) - { - TRACE(("dbg: GL_RoundDimensions: %i\n", gl_picmip.ival)); - *scaled_width >>= gl_picmip.ival; - *scaled_height >>= gl_picmip.ival; - } - else - { - *scaled_width >>= gl_picmip2d.ival; - *scaled_height >>= gl_picmip2d.ival; - } - - TRACE(("dbg: GL_RoundDimensions: %i\n", gl_max_size.ival)); - if (gl_max_size.ival) - { - if (*scaled_width > gl_max_size.ival) - *scaled_width = gl_max_size.ival; - if (*scaled_height > gl_max_size.ival) - *scaled_height = gl_max_size.ival; - } - - if (*scaled_width < 1) - *scaled_width = 1; - if (*scaled_height < 1) - *scaled_height = 1; -} - -#if 0 -static void D3D_MipMap (qbyte *out, int outwidth, int outheight, qbyte *in, int inwidth, int inheight) -{ - int i, j; - qbyte *inrow; - - //with npot - int rowwidth = inwidth*4; //rowwidth is the byte width of the input - inrow = in; - - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; - out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; - out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; - } - } -} -#endif - static void Upload_Texture_32(LPDIRECT3DTEXTURE9 tex, unsigned int *data, int width, int height, unsigned int flags) { int x, y; @@ -228,332 +78,99 @@ static void Upload_Texture_32(LPDIRECT3DTEXTURE9 tex, unsigned int *data, int wi IDirect3DTexture9_UnlockRect(tex, 0); } -//create a basic shader from a 32bit image -static void D3D9_LoadTexture_32(d3dtexture_t *tex, unsigned int *data, int width, int height, int flags) + + +void D3D9_DestroyTexture (texid_t tex) { - int nwidth, nheight; + if (!tex) + return; -/* - if (!(flags & TF_MANDATORY)) - { - Con_Printf("Texture upload missing flags\n"); - return NULL; - } -*/ - - nwidth = width; - nheight = height; - D3D9_RoundDimensions(&nwidth, &nheight, !(flags & IF_NOMIPMAP)); - - if (!tex->tex.ptr) - { - LPDIRECT3DTEXTURE9 newsurf; - IDirect3DDevice9_CreateTexture(pD3DDev9, nwidth, nheight, (flags & IF_NOMIPMAP)?1:0, ((flags & IF_NOMIPMAP)?0:D3DUSAGE_AUTOGENMIPMAP), D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &newsurf, NULL); - if (!newsurf) - return; - tex->tex.ptr = newsurf; - } - - tex->com.width = width; - tex->com.height = height; - Upload_Texture_32(tex->tex.ptr, data, width, height, flags); + if (tex->ptr) + IDirect3DTexture9_Release((IDirect3DTexture9*)tex->ptr); + tex->ptr = NULL; } -static void D3D9_LoadTexture_8(d3dtexture_t *tex, unsigned char *data, unsigned int *pal32, int width, int height, int flags, enum uploadfmt fmt) +qboolean D3D9_LoadTextureMips(image_t *tex, struct pendingtextureinfo *mips) { - static unsigned trans[1024*1024]; - int i, s; - qboolean noalpha; - int p; + qbyte *fte_restrict out, *fte_restrict in; + int x, y, i; + D3DLOCKED_RECT lock; + D3DFORMAT fmt; + D3DSURFACE_DESC desc; + IDirect3DTexture9 *dt; + qboolean swap = false; - if (width*height > 1024*1024) - Sys_Error("GL_Upload8: image too big (%i*%i)", width, height); - - s = width*height; - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (fmt == TF_TRANS8_FULLBRIGHT) + switch(mips->encoding) { - for (i=0 ; i 255-vid.fullbright) - trans[i] = pal32[p]; - else - { - noalpha = false; - trans[i] = 0; - } - } + case PTI_RGBA8: + fmt = D3DFMT_A8R8G8B8; + swap = true; + break; + case PTI_RGBX8: + fmt = D3DFMT_X8R8G8B8; + swap = true; + break; + case PTI_BGRA8: + fmt = D3DFMT_A8R8G8B8; + break; + case PTI_BGRX8: + fmt = D3DFMT_X8R8G8B8; + break; + + //too lazy to support these for now + case PTI_S3RGB1: + case PTI_S3RGBA1: + case PTI_S3RGBA3: + case PTI_S3RGBA5: + return false; + + default: //no idea + return false; } - else if ((fmt!=TF_SOLID8) && !(flags & IF_NOALPHA)) + + if (FAILED(IDirect3DDevice9_CreateTexture(pD3DDev9, mips->mip[0].width, mips->mip[0].height, mips->mipcount, 0, fmt, D3DPOOL_MANAGED, &dt, NULL))) + return false; + + for (i = 0; i < mips->mipcount; i++) { - noalpha = true; - for (i=0 ; imip[i].height != desc.Height || mips->mip[i].width != desc.Width) { - p = data[i]; - if (p == 255) - { - noalpha = false; - trans[i] = 0; - } - else - trans[i] = pal32[p]; + IDirect3DTexture9_Release(dt); + return false; } - switch(fmt) + IDirect3DTexture9_LockRect(dt, i, &lock, NULL, D3DLOCK_NOSYSLOCK|D3DLOCK_DISCARD); + //can't do it in one go. pitch might contain padding or be upside down. + if (swap) { - default: - if (noalpha) - fmt = TF_SOLID8; - break; - case TF_H2_T7G1: - fmt = TF_TRANS8; - for (i=0 ; imip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*4) { - p = data[i]; - if (p == 0) - trans[i] &= 0x00ffffff; - else if( p & 1 ) + for (x = 0; x < mips->mip[i].width*4; x+=4) { - trans[i] &= 0x00ffffff; - trans[i] |= ( ( int )( 255 * 0.5 ) ) << 24; - } - else - { - trans[i] |= 0xff000000; + out[x+0] = in[x+2]; + out[x+1] = in[x+1]; + out[x+2] = in[x+0]; + out[x+3] = in[x+3]; } } - break; - case TF_H2_TRANS8_0: - fmt = TF_TRANS8; - for (i=0 ; i>4]] & 0x00ffffff; - trans[i] |= ( int )ColorPercent[p&15] << 24; - //trans[i] = 0x7fff0000; - } - break; -*/ } - } - else - { - for (i=(s&~3)-4 ; i>=0 ; i-=4) + else { - trans[i] = pal32[data[i]]; - trans[i+1] = pal32[data[i+1]]; - trans[i+2] = pal32[data[i+2]]; - trans[i+3] = pal32[data[i+3]]; - } - for (i=s&~3 ; imip[i].data; y < mips->mip[i].height; y++, out += lock.Pitch, in += mips->mip[i].width*4) + memcpy(out, in, mips->mip[i].width*4); } + IDirect3DTexture9_UnlockRect(dt, i); } - D3D9_LoadTexture_32(tex, trans, width, height, flags); + + D3D9_DestroyTexture(tex); + tex->ptr = dt; + + return true; } - -static void genNormalMap(unsigned int *nmap, qbyte *pixels, int w, int h, float scale) +void D3D9_UploadLightmap(lightmapinfo_t *lm) { - int i, j, wr, hr; - unsigned char r, g, b; - float sqlen, reciplen, nx, ny, nz; - - const float oneOver255 = 1.0f/255.0f; - - float c, cx, cy, dcx, dcy; - - wr = w; - hr = h; - - for (i=0; i Added support for big endian. - } - } -} - -void D3D9_Upload (texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) -{ - switch (fmt) - { - case TF_RGBX32: - flags |= IF_NOALPHA; - //fall through - case TF_RGBA32: - tex.ref->width = width; - tex.ref->height = height; - Upload_Texture_32(tex.ptr, data, width, height, flags); - break; - case TF_8PAL24: - { - qbyte *pal24 = palette; - unsigned int pal32[256]; - int i; - for (i = 0; i < 256; i++) - { - pal32[i] = 0x00000000 | - (pal24[i*3+2]<<24) | - (pal24[i*3+1]<<8) | - (pal24[i*3+0]<<0); - } - D3D9_LoadTexture_8((d3dtexture_t*)tex.ref, data, (unsigned int *)pal32, width, height, flags, fmt); - } - break; - default: - OutputDebugString(va("D3D9_Upload doesn't support fmt %i (%s)", fmt, name)); - break; - } -} - -texid_t D3D9_LoadTexture (const char *identifier, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) -{ - d3dtexture_t *tex; - switch (fmt) - { - case TF_TRANS8_FULLBRIGHT: - { - qbyte *d = data; - unsigned int c = width * height; - while (c) - { - if (d[--c] > 255 - vid.fullbright) - break; - } - /*reject it if there's no fullbrights*/ - if (!c) - return r_nulltex; - } - break; - case TF_INVALID: - case TF_RGBA32: - case TF_BGRA32: - case TF_RGBX32: - case TF_RGB24: - case TF_BGR24_FLIP: - case TF_SOLID8: - case TF_TRANS8: - case TF_HEIGHT8: - case TF_HEIGHT8PAL: - case TF_H2_T7G1: - case TF_H2_TRANS8_0: - case TF_H2_T4A4: - case TF_PALETTES: - case TF_8PAL24: - case TF_8PAL32: - break; - } - tex = d3d_lookup_texture(identifier); - - switch (fmt) - { - case TF_HEIGHT8PAL: - { - texid_t t; - extern cvar_t r_shadow_bumpscale_basetexture; - unsigned int *norm = malloc(width*height*4); - genNormalMap(norm, data, width, height, r_shadow_bumpscale_basetexture.value); - t = D3D9_LoadTexture(identifier, width, height, TF_RGBA32, data, flags); - free(norm); - return t; - } - case TF_HEIGHT8: - { - extern cvar_t r_shadow_bumpscale_basetexture; - unsigned int *norm = malloc(width*height*4); - genNormalMap(norm, data, width, height, r_shadow_bumpscale_basetexture.value); - D3D9_LoadTexture_32(tex, norm, width, height, flags); - free(norm); - return tex->tex; - } - case TF_SOLID8: - case TF_TRANS8: - case TF_H2_T7G1: - case TF_H2_TRANS8_0: - case TF_H2_T4A4: - case TF_TRANS8_FULLBRIGHT: - D3D9_LoadTexture_8(tex, data, d_8to24rgbtable, width, height, flags, fmt); - return tex->tex; - case TF_RGBX32: - flags |= IF_NOALPHA; - case TF_RGBA32: - D3D9_LoadTexture_32(tex, data, width, height, flags); - return tex->tex; - default: - OutputDebugString(va("D3D9_LoadTexture doesn't support fmt %i (%s)\n", fmt, identifier)); - return r_nulltex; - } -} - -texid_t D3D9_LoadCompressed (const char *name) -{ - return r_nulltex; -} - -texid_t D3D9_FindTexture (const char *identifier, unsigned int flags) -{ - d3dtexture_t *tex = d3d_lookup_texture(identifier); - if (tex->tex.ptr) - return tex->tex; - return r_nulltex; -} - -texid_t D3D9_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) -{ - d3dtexture_t *tex = d3d_lookup_texture(identifier); - D3D9_LoadTexture_8(tex, data, (unsigned int *)palette32, width, height, flags, TF_SOLID8); - return tex->tex; -} -texid_t D3D9_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) -{ - unsigned int pal32[256]; - int i; - for (i = 0; i < 256; i++) - { - pal32[i] = 0x00000000 | - (palette24[i*3+2]<<24) | - (palette24[i*3+1]<<8) | - (palette24[i*3+0]<<0); - } - return D3D9_LoadTexture8Pal32(identifier, width, height, data, (qbyte*)pal32, flags); } #endif diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 69cae858..bae566f8 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -734,43 +734,6 @@ static qboolean D3D9_VID_Init(rendererstate_t *info, unsigned char *palette) return true; } -/*a new model has been loaded*/ -static void (D3D9_R_NewMap) (void) -{ - r_worldentity.model = cl.worldmodel; - -#ifdef MAP_PROC - if (cl.worldmodel && cl.worldmodel->fromgame == fg_doom3) - D3_GenerateAreas(cl.worldmodel); -#endif - - /*wipe any lingering particles*/ - P_ClearParticles(); - CL_RegisterParticles(); - - R_AnimateLight(); - Surf_DeInit(); - Surf_WipeStains(); - Surf_BuildLightmaps(); - - TP_NewMap(); - R_SetSky(cl.skyname); - -#ifdef RTLIGHTS - Sh_PreGenerateLights(); -#endif -} - -extern mleaf_t *r_viewleaf, *r_oldviewleaf; -extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; -static void (D3D9_R_PreNewMap) (void) -{ - r_viewleaf = NULL; - r_oldviewleaf = NULL; - r_viewleaf2 = NULL; - r_oldviewleaf2 = NULL; -} - static void (D3D9_VID_DeInit) (void) { /*final shutdown, kill the video stuff*/ @@ -1111,7 +1074,6 @@ static void (D3D9_R_DeInit) (void) R_GAliasFlushSkinCache(true); Surf_DeInit(); Shader_Shutdown(); - D3D9_Image_Shutdown(); } @@ -1238,22 +1200,14 @@ rendererinfo_t d3d9rendererinfo = D3D9_Draw_Init, D3D9_Draw_Shutdown, - D3D9_LoadTexture, - D3D9_LoadTexture8Pal24, - D3D9_LoadTexture8Pal32, - D3D9_LoadCompressed, - D3D9_FindTexture, - D3D9_AllocNewTexture, - D3D9_Upload, + D3D9_UpdateFiltering, + D3D9_LoadTextureMips, D3D9_DestroyTexture, D3D9_R_Init, D3D9_R_DeInit, D3D9_R_RenderView, - D3D9_R_NewMap, - D3D9_R_PreNewMap, - D3D9_VID_Init, D3D9_VID_DeInit, D3D9_VID_SwapBuffers, diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 4feceb51..fce74770 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -740,6 +740,18 @@ static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREA { } + r_config.texture_non_power_of_two = flevel>=D3D_FEATURE_LEVEL_10_0; //npot MUST be supported on all d3d10+ cards. + r_config.texture_non_power_of_two_pic = true; //always supported in d3d11, supposedly. + r_config.npot_rounddown = false; + if (flevel>=D3D_FEATURE_LEVEL_11_0) + r_config.maxtexturesize = 16384; + else if (flevel>=D3D_FEATURE_LEVEL_10_0) + r_config.maxtexturesize = 8192; + else if (flevel>=D3D_FEATURE_LEVEL_9_3) + r_config.maxtexturesize = 4096; + else + r_config.maxtexturesize = 2048; + vid.numpages = scd.BufferCount; if (!D3D11Shader_Init(flevel)) { @@ -901,43 +913,6 @@ static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) } #endif -/*a new model has been loaded*/ -static void (D3D11_R_NewMap) (void) -{ - r_worldentity.model = cl.worldmodel; - -#ifdef MAP_PROC - if (cl.worldmodel && cl.worldmodel->fromgame == fg_doom3) - D3_GenerateAreas(cl.worldmodel); -#endif - - /*wipe any lingering particles*/ - P_ClearParticles(); - CL_RegisterParticles(); - - R_AnimateLight(); - Surf_DeInit(); - Surf_WipeStains(); - Surf_BuildLightmaps(); - - TP_NewMap(); - R_SetSky(cl.skyname); - -#ifdef RTLIGHTS - Sh_PreGenerateLights(); -#endif -} - -extern mleaf_t *r_viewleaf, *r_oldviewleaf; -extern mleaf_t *r_viewleaf2, *r_oldviewleaf2; -static void (D3D11_R_PreNewMap) (void) -{ - r_viewleaf = NULL; - r_oldviewleaf = NULL; - r_viewleaf2 = NULL; - r_oldviewleaf2 = NULL; -} - static void (D3D11_VID_DeInit) (void) { D3D11BE_Shutdown(); @@ -1282,24 +1257,23 @@ static void (D3D11_SCR_UpdateScreen) (void) -static void (D3D11_Draw_Init) (void) +static void D3D11_Draw_Init (void) { R2D_Init(); } -static void (D3D11_Draw_Shutdown) (void) +static void D3D11_Draw_Shutdown (void) { R2D_Shutdown(); } -static void (D3D11_R_Init) (void) +static void D3D11_R_Init (void) { } -static void (D3D11_R_DeInit) (void) +static void D3D11_R_DeInit (void) { R_GAliasFlushSkinCache(true); Surf_DeInit(); Shader_Shutdown(); - D3D11_Image_Shutdown(); } @@ -1389,7 +1363,7 @@ static void (D3D11_R_RenderView) (void) // } if (!(r_refdef.flags & RDF_NOWORLDMODEL)) - if (!r_worldentity.model || r_worldentity.model->needload || !cl.worldmodel) + if (!r_worldentity.model || r_worldentity.model->loadstate != MLS_LOADED || !cl.worldmodel) { D3D11_Set2D (); R2D_ImageColours(0, 0, 0, 1); @@ -1427,6 +1401,10 @@ rendererinfo_t d3d11rendererinfo = D3D11_Draw_Init, D3D11_Draw_Shutdown, + D3D11_UpdateFiltering, + D3D11_LoadTextureMips, + D3D11_DestroyTexture, +/* D3D11_LoadTexture, D3D11_LoadTexture8Pal24, D3D11_LoadTexture8Pal32, @@ -1435,14 +1413,11 @@ rendererinfo_t d3d11rendererinfo = D3D11_AllocNewTexture, D3D11_Upload, D3D11_DestroyTexture, - +*/ D3D11_R_Init, D3D11_R_DeInit, D3D11_R_RenderView, - D3D11_R_NewMap, - D3D11_R_PreNewMap, - D3D11_VID_Init, D3D11_VID_DeInit, D3D11_PresentOrCrash, diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index f9422887..8158bcdc 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -265,7 +265,6 @@ Global {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLDebug|Win32.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLDebug|x64.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLRelease|Win32.ActiveCfg = Release|Win32 - {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLRelease|Win32.Build.0 = Release|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.GLRelease|x64.ActiveCfg = Release|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.MDebug|Win32.ActiveCfg = Debug|Win32 {4877586B-E85B-4DF8-BCCE-59D31514D240}.MDebug|x64.ActiveCfg = Release|Win32 @@ -613,7 +612,6 @@ Global {74542CA7-48C1-4664-9007-66F751131EA3}.GLDebug|Win32.Build.0 = Debug|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.GLDebug|x64.ActiveCfg = Debug|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.GLRelease|Win32.ActiveCfg = Release|Win32 - {74542CA7-48C1-4664-9007-66F751131EA3}.GLRelease|Win32.Build.0 = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.GLRelease|x64.ActiveCfg = Release|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.MDebug|Win32.ActiveCfg = Debug|Win32 {74542CA7-48C1-4664-9007-66F751131EA3}.MDebug|Win32.Build.0 = Debug|Win32 @@ -638,10 +636,8 @@ Global {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.D3DRelease|Win32.Build.0 = Release|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.D3DRelease|x64.ActiveCfg = Release|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 - {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug|Win32.ActiveCfg = Debug|Win32 - {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug|Win32.Build.0 = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.Debug|x64.ActiveCfg = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.GLDebug|Win32.ActiveCfg = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.GLDebug|x64.ActiveCfg = Debug|Win32 @@ -652,7 +648,6 @@ Global {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MDebug|Win32.Build.0 = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MDebug|x64.ActiveCfg = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MinGLDebug|Win32.ActiveCfg = Debug|Win32 - {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MinGLDebug|Win32.Build.0 = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MinGLDebug|x64.ActiveCfg = Debug|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MinGLRelease|Win32.ActiveCfg = Release|Win32 {75D91BDE-CC30-4C53-BF33-5F69EF13A61B}.MinGLRelease|Win32.Build.0 = Release|Win32 @@ -671,6 +666,11 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {4735677B-6D5A-4BE6-A945-CB32DEADBEEF} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} + {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} + {4735677B-6D5A-4BE6-A945-CB32A7282F56} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} + {75D91BDE-CC30-4C53-BF33-5F69EF13A61B} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} {4877586B-E85B-4DF8-BCCE-59D31514D240} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} {32B12987-DF8C-4E40-B07C-B18586A4CA65} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} {873CCE24-3549-49D4-A4B4-653F91B1532A} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} @@ -678,11 +678,6 @@ Global {72269FEE-293D-40BC-A7AE-E429F4496869} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} {6ABD62A3-C5A0-43E8-BA4F-84606057774F} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} {74542CA7-48C1-4664-9007-66F751131EA3} = {8CED01C6-2C61-4EC5-90B6-574D9756D773} - {4735677B-6D5A-4BE6-A945-CB32DEADBEEF} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} - {4735677B-6D5A-4BE6-A945-CB32A7282F56} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} - {482A886A-5755-4DAE-AD5F-D7CD4A990F9E} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} - {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} - {75D91BDE-CC30-4C53-BF33-5F69EF13A61B} = {EB5DFF7C-C0A8-426C-BC66-524162350F1B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution AMDCaProjectFile = C:\Games\Quake\wip\engine\dotnet2005\CodeAnalyst\ftequake.caw diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 8d3692ed..83394ca8 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -737,7 +737,7 @@ width; - h = sourceimg->height; + w = 0; + h = 0; + sourceimg = NULL; } else - w = h = 0; + { + w = iw; + h = ih; + } while(*s) { @@ -267,6 +269,7 @@ skinid_t Mod_ReadSkinFile(const char *skinname, const char *skintext) //body if (com_tokentype != TTP_LINEENDING) { + //fixme: this blocks waiting for the textures to load. struct cctx_s cctx; memset(&cctx, 0, sizeof(cctx)); @@ -362,7 +365,7 @@ skinid_t Mod_RegisterSkinFile(const char *skinname) if (!strcmp(skinname, registeredskins[id]->skinname)) return id+1; } - f = FS_LoadMallocFile(skinname); + f = FS_LoadMallocFile(skinname, NULL); if (!f) return 0; id = Mod_ReadSkinFile(skinname, f); @@ -592,6 +595,8 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (!cl.players[e->playerindex].qwskin) Skin_Find(&cl.players[e->playerindex]); plskin = cl.players[e->playerindex].qwskin; +// if (plskin && plskin->failedload) +// plskin = NULL; } else plskin = NULL; @@ -649,7 +654,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (e->skinnum >= 0 && e->skinnum < inf->numskins) skins += e->skinnum; - if (!skins->numshaders) + if (!skins->numframes) { /*model has a skin, but has no framegroups*/ skins = NULL; @@ -659,15 +664,20 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e else { subframe = cl.time*skins->skinspeed; - subframe = subframe%skins->numshaders; + subframe = subframe%skins->numframes; - shader = skins->ofsshaders[subframe]; + shader = skins->frame[subframe].shader; } } if (shader) { - if (!plskin && (TEXVALID(shader->defaulttextures.loweroverlay) || TEXVALID(shader->defaulttextures.upperoverlay))) - return shader; //the shader can do its own colourmapping. + if (!plskin) + { + //do this for the loading case too, in the hope that it'll avoid generating a per-player skin at all + if ((shader->defaulttextures.loweroverlay && (shader->defaulttextures.loweroverlay->status == TEX_LOADING || shader->defaulttextures.loweroverlay->status == TEX_LOADED)) || + (shader->defaulttextures.upperoverlay && (shader->defaulttextures.upperoverlay->status == TEX_LOADING || shader->defaulttextures.upperoverlay->status == TEX_LOADED))) + return shader; + } if (shader->prog && shader->prog->permu[PERMUTATION_UPPERLOWER].handle.glsl && !h2playertranslations) { //this shader can do permutations. this means we can generate only a black image, with separate top+bottom textures. tc = 0xfe000000; @@ -743,7 +753,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e inwidth = plskin->width; inheight = plskin->height; - if (!original && TEXVALID(plskin->textures.base)) + if (!original && TEXLOADED(plskin->textures.base)) { cm->texnum.loweroverlay = plskin->textures.loweroverlay; cm->texnum.upperoverlay = plskin->textures.upperoverlay; @@ -761,9 +771,9 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e } if (!original) { - if (skins->ofstexels) + if (skins->numframes && skins->frame[subframe].texels) { - original = skins->ofstexels[subframe]; + original = skins->frame[subframe].texels; inwidth = skins->skinwidth; inheight = skins->skinheight; } @@ -1017,12 +1027,12 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e return NULL; } - if (!skins->numshaders) + if (!skins->numframes) return NULL; frame = cl.time*skins->skinspeed; - frame = frame%skins->numshaders; - return skins->ofsshaders[frame]; + frame = frame%skins->numframes; + return skins->frame[frame].shader; } #if defined(RTLIGHTS) @@ -1223,6 +1233,12 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel) dist); add = cl_dlights[i].radius - Length(dist); +#ifdef RTLIGHTS + //if world lighting is on, there may be no lightmap influence even if r_dynamic is on. + if (r_shadow_realtime_world.ival) + add *= r_shadow_realtime_world_lightmaps.value; +#endif + if (add > 0) { add*=5; @@ -1454,7 +1470,7 @@ void R_GAlias_GenerateBatches(entity_t *e, batch_t **batches) b->buildmeshes = R_GAlias_DrawBatch; b->ent = e; #ifdef Q3BSPS - b->fog = CM_FogForOrigin(e->origin); + b->fog = Mod_FogForOrigin(cl.worldmodel, e->origin); #endif b->mesh = NULL; b->firstmesh = 0; @@ -1946,173 +1962,253 @@ void GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, #ifdef Q3CLIENT -//q3 lightning gun -static void R_DB_LightningBeam(batch_t *batch) +//q3 lightning gun / q3 railgun / q2 beams +static void R_Beam_GenerateTrisoup(entity_t *e, int bemode) { - entity_t *e = batch->ent; - vec3_t v; - vec3_t dir, cr; - float scale = e->scale; - float length; + float lightmap; + unsigned int batchflags = 0; + vecV_t *xyz; + vec2_t *st; + vec4_t *rgba; + scenetris_t *t; + shader_t *shader = NULL; + float scale, length; + vec3_t dir, v, cr; - static vecV_t points[4]; - static vec2_t texcoords[4] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; - static index_t indexarray[6] = {0, 1, 2, 0, 2, 3}; - static vec4_t colors[4]; - - static mesh_t mesh; - static mesh_t *meshptr = &mesh; - - if (batch->ent == &r_worldentity) + if (e->forcedshader) { - mesh.numindexes = 0; - mesh.numindexes = 0; + shader = e->forcedshader; + if (!shader) + shader = R_RegisterShader("q2beam", SUF_NONE, + "{\n" + "{\n" + "map $whiteimage\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "blendfunc blend\n" + "}\n" + "}\n" + ); + } + else return; + + batchflags = 0; +// if (e->flags & RF_NOSHADOW) + batchflags |= BEF_NOSHADOWS; + if (e->flags & RF_ADDITIVE) + batchflags |= BEF_FORCEADDITIVE; + if (e->flags & RF_TRANSLUCENT) + batchflags |= BEF_FORCETRANSPARENT; + if (e->flags & RF_NODEPTHTEST) + batchflags |= BEF_FORCENODEPTH; + if (e->flags & RF_FORCECOLOURMOD) + batchflags |= BEF_FORCECOLOURMOD; + if (shader->flags & SHADER_NODLIGHT) + batchflags |= BEF_NODLIGHT; + + if ((batchflags & BEF_NODLIGHT) || (shader->flags & SHADER_NODLIGHT) || bemode != BEM_STANDARD) + { + //unlit sprites are just fullbright + lightmap = 1; + } + else + { + extern cvar_t r_shadow_realtime_world_lightmaps; + //lit sprites need to sample the world lighting. with rtlights that generally means they're 0. +#ifdef RTLIGHTS + if (r_shadow_realtime_world.ival) + lightmap = r_shadow_realtime_world_lightmaps.value; + else +#endif + lightmap = 1; } - scale *= -10; + if (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == batchflags) + t = &cl_stris[cl_numstris-1]; + else + { + if (cl_numstris == cl_maxstris) + { + cl_maxstris += 8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); + } + t = &cl_stris[cl_numstris++]; + t->shader = shader; + t->numidx = 0; + t->numvert = 0; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + t->flags = batchflags; + } + + if (cl_numstrisidx+6 > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+6 + 64; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + if (cl_numstrisvert+4 > cl_maxstrisvert) + { + cl_maxstrisvert+=64; + 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); + } + + xyz = &cl_strisvertv[cl_numstrisvert]; + st = &cl_strisvertt[cl_numstrisvert]; + rgba = &cl_strisvertc[cl_numstrisvert]; + + cl_strisidx[cl_numstrisidx++] = t->numvert+0; + cl_strisidx[cl_numstrisidx++] = t->numvert+1; + cl_strisidx[cl_numstrisidx++] = t->numvert+2; + cl_strisidx[cl_numstrisidx++] = t->numvert+0; + cl_strisidx[cl_numstrisidx++] = t->numvert+2; + cl_strisidx[cl_numstrisidx++] = t->numvert+3; + t->numidx += 6; + t->numvert += 4; + cl_numstrisvert += 4; + + scale = e->scale*10; if (!scale) scale = 10; - VectorSubtract(e->origin, e->oldorigin, dir); length = Length(dir); - //this seems to be about right. - texcoords[2][0] = length/128; - texcoords[3][0] = length/128; + Vector2Set(st[0], 0, 1); + Vector2Set(st[1], 0, 0); + Vector2Set(st[2], length/128, 0); + Vector2Set(st[3], length/128, 1); VectorSubtract(r_refdef.vieworg, e->origin, v); CrossProduct(v, dir, cr); VectorNormalize(cr); - VectorMA(e->origin, -scale/2, cr, points[0]); - VectorMA(e->origin, scale/2, cr, points[1]); + VectorMA(e->origin, -scale/2, cr, xyz[0]); + VectorMA(e->origin, scale/2, cr, xyz[1]); VectorSubtract(r_refdef.vieworg, e->oldorigin, v); CrossProduct(v, dir, cr); VectorNormalize(cr); - VectorMA(e->oldorigin, scale/2, cr, points[2]); - VectorMA(e->oldorigin, -scale/2, cr, points[3]); + VectorMA(e->oldorigin, scale/2, cr, xyz[2]); + VectorMA(e->oldorigin, -scale/2, cr, xyz[3]); - /*this is actually meant to be 4 separate quads at 45 degrees from each other*/ - - Vector4Copy(e->shaderRGBAf, colors[0]); - Vector4Copy(e->shaderRGBAf, colors[1]); - Vector4Copy(e->shaderRGBAf, colors[2]); - Vector4Copy(e->shaderRGBAf, colors[3]); - - batch->ent = &r_worldentity; - batch->mesh = &meshptr; - - memset(&mesh, 0, sizeof(mesh)); - mesh.vbofirstelement = 0; - mesh.vbofirstvert = 0; - mesh.xyz_array = points; - mesh.indexes = indexarray; - mesh.numindexes = sizeof(indexarray)/sizeof(indexarray[0]); - mesh.colors4f_array[0] = (vec4_t*)colors; - mesh.normals_array = NULL; - mesh.numvertexes = 4; - mesh.st_array = texcoords; -} -//q3 railgun beam -static void R_DB_RailgunBeam(batch_t *batch) -{ - entity_t *e = batch->ent; - vec3_t v; - vec3_t dir, cr; - float scale = e->scale; - float length; - - static mesh_t mesh; - static mesh_t *meshptr = &mesh; - static vecV_t points[4]; - static vec2_t texcoords[4] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; - static index_t indexarray[6] = {0, 1, 2, 0, 2, 3}; - static vec4_t colors[4]; - - if (batch->ent == &r_worldentity) + if (e->shaderRGBAf[0] != 0 || e->shaderRGBAf[1] != 0 || e->shaderRGBAf[2] != 0 || (batchflags & BEF_FORCECOLOURMOD)) { - mesh.numindexes = 0; - mesh.numindexes = 0; - return; + if (e->shaderRGBAf[0] > 1) + e->shaderRGBAf[0] = 1; + if (e->shaderRGBAf[1] > 1) + e->shaderRGBAf[1] = 1; + if (e->shaderRGBAf[2] > 1) + e->shaderRGBAf[2] = 1; + } + else + { + e->shaderRGBAf[0] = 1; + e->shaderRGBAf[1] = 1; + e->shaderRGBAf[2] = 1; } - if (!e->forcedshader) - return; - - if (!scale) - scale = 10; - - - VectorSubtract(e->origin, e->oldorigin, dir); - length = Length(dir); - - //this seems to be about right. - texcoords[2][0] = length/128; - texcoords[3][0] = length/128; - - VectorSubtract(r_refdef.vieworg, e->origin, v); - CrossProduct(v, dir, cr); - VectorNormalize(cr); - - VectorMA(e->origin, -scale/2, cr, points[0]); - VectorMA(e->origin, scale/2, cr, points[1]); - - VectorSubtract(r_refdef.vieworg, e->oldorigin, v); - CrossProduct(v, dir, cr); - VectorNormalize(cr); - - VectorMA(e->oldorigin, scale/2, cr, points[2]); - VectorMA(e->oldorigin, -scale/2, cr, points[3]); - - Vector4Copy(e->shaderRGBAf, colors[0]); - Vector4Copy(e->shaderRGBAf, colors[1]); - Vector4Copy(e->shaderRGBAf, colors[2]); - Vector4Copy(e->shaderRGBAf, colors[3]); - - batch->ent = &r_worldentity; - batch->mesh = &meshptr; - - memset(&mesh, 0, sizeof(mesh)); - mesh.vbofirstelement = 0; - mesh.vbofirstvert = 0; - mesh.xyz_array = points; - mesh.indexes = indexarray; - mesh.numindexes = sizeof(indexarray)/sizeof(indexarray[0]); - mesh.colors4f_array[0] = (vec4_t*)colors; - mesh.normals_array = NULL; - mesh.numvertexes = 4; - mesh.st_array = texcoords; + VectorScale(e->shaderRGBAf, lightmap, rgba[0]); + rgba[0][3] = e->shaderRGBAf[3]; + Vector4Copy(rgba[0], rgba[1]); + Vector4Copy(rgba[0], rgba[2]); + Vector4Copy(rgba[0], rgba[3]); } #endif -static void R_DB_Sprite(batch_t *batch) + +static void R_Sprite_GenerateTrisoup(entity_t *e, int bemode) { - entity_t *e = batch->ent; vec3_t point; - mspriteframe_t *frame, genframe; + mspriteframe_t genframe; vec3_t spraxis[3]; msprite_t *psprite; vec3_t sprorigin; unsigned int sprtype; + float lightmap; + unsigned int batchflags = 0; + vecV_t *xyz; + vec2_t *st; + vec4_t *rgba; + scenetris_t *t; - static vec2_t texcoords[4]={{0, 1},{0,0},{1,0},{1,1}}; - static index_t indexes[6] = {0, 1, 2, 0, 2, 3}; - static vecV_t vertcoords[4]; - static avec4_t colours[4]; - static mesh_t mesh; - static mesh_t *meshptr = &mesh; + extern cvar_t gl_blendsprites; + shader_t *shader = NULL; + mspriteframe_t *frame; - if (batch->ent == &r_worldentity) + if (!e->model || e->model->type != mod_sprite || e->forcedshader) { - mesh.numindexes = 0; - mesh.numindexes = 0; - return; + frame = NULL; + shader = e->forcedshader; + if (!shader) + shader = R_RegisterShader("q2beam", SUF_NONE, + "{\n" + "{\n" + "map $whiteimage\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "blendfunc blend\n" + "}\n" + "}\n" + ); + } + else + { + if (!(e->flags & RF_WEAPONMODEL)) + { + if (R_CullEntityBox (e, e->model->mins, e->model->maxs)) + return; + #ifdef RTLIGHTS + if (BE_LightCullModel(e->origin, e->model)) + return; + } + else + { + if (BE_LightCullModel(r_origin, e->model)) + return; + #endif + } + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame(e); + shader = frame->shader; } - if (e->flags & RF_WEAPONMODEL && r_refdef.playerview->viewentity > 0) + batchflags = 0; +// if (e->flags & RF_NOSHADOW) + batchflags |= BEF_NOSHADOWS; + if (e->flags & RF_ADDITIVE) + batchflags |= BEF_FORCEADDITIVE; + if (e->flags & RF_TRANSLUCENT) + batchflags |= BEF_FORCETRANSPARENT; + if (e->flags & RF_NODEPTHTEST) + batchflags |= BEF_FORCENODEPTH; + if (e->flags & RF_FORCECOLOURMOD) + batchflags |= BEF_FORCECOLOURMOD; + if (shader->flags & SHADER_NODLIGHT) + batchflags |= BEF_NODLIGHT; + + if ((batchflags & BEF_NODLIGHT) || (shader->flags & SHADER_NODLIGHT) || bemode != BEM_STANDARD) + { + //unlit sprites are just fullbright + lightmap = 1; + } + else + { + extern cvar_t r_shadow_realtime_world_lightmaps; + //lit sprites need to sample the world lighting. with rtlights that generally means they're 0. +#ifdef RTLIGHTS + if (r_shadow_realtime_world.ival) + lightmap = r_shadow_realtime_world_lightmaps.value; + else +#endif + lightmap = 1; + } + + if ((e->flags & RF_WEAPONMODEL) && r_refdef.playerview->viewentity > 0) { sprorigin[0] = r_refdef.playerview->vw_origin[0]; sprorigin[1] = r_refdef.playerview->vw_origin[1]; @@ -2122,14 +2218,59 @@ static void R_DB_Sprite(batch_t *batch) VectorMA(sprorigin, e->origin[2], r_refdef.playerview->vw_axis[2], sprorigin); VectorMA(sprorigin, 12, vpn, sprorigin); - batch->flags |= BEF_FORCENODEPTH; + batchflags |= BEF_FORCENODEPTH; } else VectorCopy(e->origin, sprorigin); - if (!e->model || e->forcedshader) + + if (cl_numstris && cl_stris[cl_numstris-1].shader == shader && cl_stris[cl_numstris-1].flags == batchflags) + t = &cl_stris[cl_numstris-1]; + else + { + if (cl_numstris == cl_maxstris) + { + cl_maxstris += 8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); + } + t = &cl_stris[cl_numstris++]; + t->shader = shader; + t->numidx = 0; + t->numvert = 0; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + t->flags = batchflags; + } + + if (cl_numstrisidx+6 > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+6 + 64; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + if (cl_numstrisvert+4 > cl_maxstrisvert) + { + cl_maxstrisvert+=64; + 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); + } + + xyz = &cl_strisvertv[cl_numstrisvert]; + st = &cl_strisvertt[cl_numstrisvert]; + rgba = &cl_strisvertc[cl_numstrisvert]; + + cl_strisidx[cl_numstrisidx++] = t->numvert+0; + cl_strisidx[cl_numstrisidx++] = t->numvert+1; + cl_strisidx[cl_numstrisidx++] = t->numvert+2; + cl_strisidx[cl_numstrisidx++] = t->numvert+0; + cl_strisidx[cl_numstrisidx++] = t->numvert+2; + cl_strisidx[cl_numstrisidx++] = t->numvert+3; + t->numidx += 6; + t->numvert += 4; + cl_numstrisvert += 4; + + if (!frame) { - genframe.shader = e->forcedshader; genframe.up = genframe.left = -1; genframe.down = genframe.right = 1; sprtype = SPR_VP_PARALLEL; @@ -2139,12 +2280,9 @@ static void R_DB_Sprite(batch_t *batch) { // don't even bother culling, because it's just a single // polygon without a surface cache - frame = R_GetSpriteFrame (e); psprite = e->model->meshinfo; sprtype = psprite->type; } - if (!frame->shader) - return; switch(sprtype) { @@ -2179,14 +2317,17 @@ static void R_DB_Sprite(batch_t *batch) VectorCopy(vright, spraxis[1]); break; } - spraxis[2][0]*=e->scale; - spraxis[2][1]*=e->scale; - spraxis[2][2]*=e->scale; - spraxis[1][0]*=e->scale; - spraxis[1][1]*=e->scale; - spraxis[1][2]*=e->scale; + if (e->scale) + { + spraxis[2][0]*=e->scale; + spraxis[2][1]*=e->scale; + spraxis[2][2]*=e->scale; + spraxis[1][0]*=e->scale; + spraxis[1][1]*=e->scale; + spraxis[1][2]*=e->scale; + } - if (e->shaderRGBAf[0] != 0 || e->shaderRGBAf[1] != 0 || e->shaderRGBAf[2] != 0 || (batch->flags & BEF_FORCECOLOURMOD)) + if (e->shaderRGBAf[0] != 0 || e->shaderRGBAf[1] != 0 || e->shaderRGBAf[2] != 0 || (batchflags & BEF_FORCECOLOURMOD)) { if (e->shaderRGBAf[0] > 1) e->shaderRGBAf[0] = 1; @@ -2202,120 +2343,28 @@ static void R_DB_Sprite(batch_t *batch) e->shaderRGBAf[2] = 1; } - Vector4Copy(e->shaderRGBAf, colours[0]); - Vector4Copy(e->shaderRGBAf, colours[1]); - Vector4Copy(e->shaderRGBAf, colours[2]); - Vector4Copy(e->shaderRGBAf, colours[3]); + VectorScale(e->shaderRGBAf, lightmap, rgba[0]); + rgba[0][3] = e->shaderRGBAf[3]; + Vector4Copy(rgba[0], rgba[1]); + Vector4Copy(rgba[0], rgba[2]); + Vector4Copy(rgba[0], rgba[3]); - VectorSubtract(sprorigin, e->origin, sprorigin); - if (!e->scale) - e->scale = 1; - VectorSet(e->axis[0], 1/e->scale, 0, 0); - VectorSet(e->axis[1], 0, 1/e->scale, 0); - VectorSet(e->axis[2], 0, 0, 1/e->scale); + Vector2Set(st[0], 0, 1); + Vector2Set(st[1], 0, 0); + Vector2Set(st[2], 1, 0); + Vector2Set(st[3], 1, 1); VectorMA (sprorigin, frame->down, spraxis[2], point); - VectorMA (point, frame->left, spraxis[1], vertcoords[0]); + VectorMA (point, frame->left, spraxis[1], xyz[0]); VectorMA (sprorigin, frame->up, spraxis[2], point); - VectorMA (point, frame->left, spraxis[1], vertcoords[1]); + VectorMA (point, frame->left, spraxis[1], xyz[1]); VectorMA (sprorigin, frame->up, spraxis[2], point); - VectorMA (point, frame->right, spraxis[1], vertcoords[2]); + VectorMA (point, frame->right, spraxis[1], xyz[2]); VectorMA (sprorigin, frame->down, spraxis[2], point); - VectorMA (point, frame->right, spraxis[1], vertcoords[3]); - - batch->mesh = &meshptr; - - memset(&mesh, 0, sizeof(mesh)); - mesh.vbofirstelement = 0; - mesh.vbofirstvert = 0; - mesh.xyz_array = vertcoords; - mesh.indexes = indexes; - mesh.numindexes = sizeof(indexes)/sizeof(indexes[0]); - mesh.colors4f_array[0] = colours; - mesh.normals_array = NULL; - mesh.numvertexes = 4; - mesh.st_array = texcoords; - mesh.istrifan = true; -} -static void R_Sprite_GenerateBatch(entity_t *e, batch_t **batches, void (*drawfunc)(batch_t *batch)) -{ - extern cvar_t gl_blendsprites; - shader_t *shader = NULL; - batch_t *b; - shadersort_t sort; - int j; - - if (!e->model || e->model->type != mod_sprite || e->forcedshader) - { - shader = e->forcedshader; - if (!shader) - shader = R_RegisterShader("q2beam", SUF_NONE, - "{\n" - "{\n" - "map $whiteimage\n" - "rgbgen vertex\n" - "alphagen vertex\n" - "blendfunc blend\n" - "}\n" - "}\n" - ); - } - else - { - // don't even bother culling, because it's just a single - // polygon without a surface cache - shader = R_GetSpriteFrame(e)->shader; - } - - if (!shader) - return; - - b = BE_GetTempBatch(); - if (!b) - return; - - b->flags = 0; - sort = shader->sort; - if (e->flags & RF_ADDITIVE) - { - b->flags |= BEF_FORCEADDITIVE; - if (sort < SHADER_SORT_ADDITIVE) - sort = SHADER_SORT_ADDITIVE; - } - if (e->flags & RF_TRANSLUCENT || (gl_blendsprites.ival && drawfunc == R_DB_Sprite)) - { - b->flags |= BEF_FORCETRANSPARENT; - if (SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND) - sort = SHADER_SORT_BLEND; - } - if (e->flags & RF_NODEPTHTEST) - { - b->flags |= BEF_FORCENODEPTH; - if (sort < SHADER_SORT_BANNER) - sort = SHADER_SORT_BANNER; - } - - b->buildmeshes = drawfunc; - b->ent = e; -#ifdef Q3BSPS - b->fog = CM_FogForOrigin(e->origin); -#endif - b->mesh = NULL; - b->firstmesh = 0; - b->meshes = 1; - b->skin = &shader->defaulttextures; - b->texture = NULL; - b->shader = shader; - for (j = 0; j < MAXRLIGHTMAPS; j++) - b->lightmap[j] = -1; - b->surf_first = 0; - b->flags |= BEF_NODLIGHT|BEF_NOSHADOWS; - b->vbo = NULL; - b->next = batches[sort]; - batches[sort] = b; + VectorMA (point, frame->right, spraxis[1], xyz[3]); } static void R_DB_Poly(batch_t *batch) @@ -2333,11 +2382,12 @@ static void R_DB_Poly(batch_t *batch) mesh.numindexes = cl_stris[i].numidx; mesh.numvertexes = cl_stris[i].numvert; } -void BE_GenPolyBatches(batch_t **batches) +static void BE_GenPolyBatches(batch_t **batches) { shader_t *shader = NULL; batch_t *b; unsigned int i = cl_numstris, j; + unsigned int sort; while (i-- > 0) { @@ -2363,10 +2413,19 @@ void BE_GenPolyBatches(batch_t **batches) for (j = 0; j < MAXRLIGHTMAPS; j++) b->lightmap[j] = -1; b->surf_first = i; - b->flags = BEF_NODLIGHT|BEF_NOSHADOWS | cl_stris[i].flags; + b->flags = cl_stris[i].flags; b->vbo = 0; - b->next = batches[shader->sort]; - batches[shader->sort] = b; + + sort = shader->sort; + if ((b->flags & BEF_FORCEADDITIVE) && sort < SHADER_SORT_ADDITIVE) + sort = SHADER_SORT_ADDITIVE; + if ((b->flags & BEF_FORCETRANSPARENT) && SHADER_SORT_PORTAL < sort && sort < SHADER_SORT_BLEND) + sort = SHADER_SORT_BLEND; + if ((b->flags & BEF_FORCENODEPTH) && sort < SHADER_SORT_BANNER) + sort = SHADER_SORT_BANNER; + + b->next = batches[sort]; + batches[sort] = b; } } void R_HalfLife_GenerateBatches(entity_t *e, batch_t **batches); @@ -2376,6 +2435,8 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo entity_t *ent; unsigned int orig_numstris = cl_numstris; unsigned int orig_numvisedicts = cl_numvisedicts; + unsigned int orig_numstrisidx = cl_numstrisidx; + unsigned int orig_numstrisvert = cl_numstrisvert; /*clear the batch list*/ for (i = 0; i < SHADER_SORT_COUNT; i++) @@ -2424,11 +2485,13 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo default: if (!ent->model) continue; - if (ent->model->needload) + if (ent->model->loadstate == MLS_NOTLOADED) { if (!Mod_LoadModel(ent->model, MLV_WARN)) continue; } + if (ent->model->loadstate != MLS_LOADED) + continue; if (cl.lerpents && (cls.allow_anyparticles)) //allowed or static { @@ -2459,7 +2522,7 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo R_GAlias_GenerateBatches(ent, batches); break; case mod_sprite: - R_Sprite_GenerateBatch(ent, batches, R_DB_Sprite); + R_Sprite_GenerateTrisoup(ent, bemode); break; case mod_halflife: #ifdef HALFLIFEMODELS @@ -2473,17 +2536,15 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo } break; case RT_SPRITE: - R_Sprite_GenerateBatch(ent, batches, R_DB_Sprite); + R_Sprite_GenerateTrisoup(ent, bemode); break; #ifdef Q3CLIENT case RT_BEAM: case RT_RAIL_RINGS: case RT_LIGHTNING: - R_Sprite_GenerateBatch(ent, batches, R_DB_LightningBeam); - continue; case RT_RAIL_CORE: - R_Sprite_GenerateBatch(ent, batches, R_DB_RailgunBeam); + R_Beam_GenerateTrisoup(ent, bemode); continue; #endif @@ -2499,8 +2560,17 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo if (cl_numstris) BE_GenPolyBatches(batches); + while(orig_numstris < cl_numstris) + cl_stris[orig_numstris++].shader = NULL; cl_numstris = orig_numstris; - cl_numvisedicts = orig_numvisedicts; +/* cl_numstrisidx = orig_numstrisidx; + cl_numstrisvert = orig_numstrisvert; + if (cl_numstris) + { //fix this up, in case they got merged. + cl_stris[cl_numstris-1].numvert = cl_numstrisvert - cl_stris[cl_numstris-1].firstvert; + cl_stris[cl_numstris-1].numidx = cl_numstrisidx - cl_stris[cl_numstris-1].firstidx; + } +*/ cl_numvisedicts = orig_numvisedicts; } #endif // defined(GLQUAKE) diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 20a5739f..2ed87d0e 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -378,13 +378,13 @@ void GL_MTBind(int tmu, int target, texid_t texnum) GL_SelectTexture(tmu); #ifndef FORCESTATE - if (shaderstate.currenttextures[tmu] == texnum.num) + if (shaderstate.currenttextures[tmu] == texnum->num) return; #endif - shaderstate.currenttextures[tmu] = texnum.num; + shaderstate.currenttextures[tmu] = texnum->num; if (target) - qglBindTexture (target, texnum.num); + qglBindTexture (target, texnum->num); if ( #ifndef FORCESTATE @@ -403,13 +403,14 @@ void GL_MTBind(int tmu, int target, texid_t texnum) void GL_LazyBind(int tmu, int target, texid_t texnum) { + int glnum = texnum?texnum->num:0; #ifndef FORCESTATE - if (shaderstate.currenttextures[tmu] != texnum.num) + if (shaderstate.currenttextures[tmu] != glnum) #endif { GL_SelectTexture(tmu); - shaderstate.currenttextures[shaderstate.currenttmu] = texnum.num; + shaderstate.currenttextures[shaderstate.currenttmu] = glnum; #ifndef FORCESTATE if (shaderstate.curtexturetype[tmu] != target) @@ -432,7 +433,7 @@ void GL_LazyBind(int tmu, int target, texid_t texnum) } if (target) - qglBindTexture (target, texnum.num); + qglBindTexture (target, glnum); } } @@ -1029,7 +1030,10 @@ static void T_Gen_CurrentRender(int tmu) } // copy the scene to texture if (!TEXVALID(shaderstate.temptexture)) - TEXASSIGN(shaderstate.temptexture, GL_AllocNewTexture("***$currentrender***", vwidth, vheight, 0)); + { + TEXASSIGN(shaderstate.temptexture, Image_CreateTexture("***$currentrender***", NULL, 0)); + qglGenTextures(1, &shaderstate.temptexture->num); + } GL_MTBind(tmu, GL_TEXTURE_2D, shaderstate.temptexture); qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, vwidth, vheight, 0); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -1072,28 +1076,28 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass) } break; case T_GEN_DIFFUSE: - if (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->base)) + if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->base)) t = shaderstate.curtexnums->base; else t = missing_texture; break; case T_GEN_NORMALMAP: - t = (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->bump))?shaderstate.curtexnums->bump:missing_texture_normal; + t = (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->bump))?shaderstate.curtexnums->bump:missing_texture_normal; break; case T_GEN_SPECULAR: - if (TEXVALID(shaderstate.curtexnums->specular)) + if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->specular)) t = shaderstate.curtexnums->specular; else t = missing_texture_gloss; break; case T_GEN_UPPEROVERLAY: - if (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->upperoverlay)) + if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->upperoverlay)) t = shaderstate.curtexnums->upperoverlay; else t = r_nulltex; break; case T_GEN_LOWEROVERLAY: - if (shaderstate.curtexnums && TEXVALID(shaderstate.curtexnums->loweroverlay)) + if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->loweroverlay)) t = shaderstate.curtexnums->loweroverlay; else t = r_nulltex; @@ -1275,8 +1279,8 @@ void GenerateFogTexture(texid_t *tex, float density, float zscale) } if (!TEXVALID(*tex)) - *tex = R_AllocNewTexture("***fog***", FOGS, FOGT, 0); - R_Upload(*tex, "fog", TF_RGBA32, fogdata, NULL, FOGS, FOGT, IF_CLAMP|IF_NOMIPMAP); + *tex = Image_CreateTexture("***fog***", NULL, IF_CLAMP|IF_NOMIPMAP); + Image_Upload(*tex, TF_RGBA32, fogdata, NULL, FOGS, FOGT, IF_CLAMP|IF_NOMIPMAP); } void GLBE_DestroyFBOs(void) @@ -1285,22 +1289,22 @@ void GLBE_DestroyFBOs(void) GLBE_FBO_Destroy(&shaderstate.fbo_reflectrefrac); GLBE_FBO_Destroy(&shaderstate.fbo_lprepass); - if (shaderstate.tex_reflection.num) + if (shaderstate.tex_reflection) { R_DestroyTexture(shaderstate.tex_reflection); shaderstate.tex_reflection = r_nulltex; } - if (shaderstate.tex_refraction.num) + if (shaderstate.tex_refraction) { R_DestroyTexture(shaderstate.tex_refraction); shaderstate.tex_refraction = r_nulltex; } - if (shaderstate.tex_refractiondepth.num) + if (shaderstate.tex_refractiondepth) { R_DestroyTexture(shaderstate.tex_refractiondepth); shaderstate.tex_refractiondepth = r_nulltex; } - if (shaderstate.temptexture.num) + if (shaderstate.temptexture) { R_DestroyTexture(shaderstate.temptexture); shaderstate.temptexture = r_nulltex; @@ -1488,7 +1492,6 @@ static void tcgen_fog(float *st, unsigned int numverts, float *xyz, mfog_t *fog) { z = DotProduct(xyz, zmat) + zmat[3]; st[0] = z; - st[1] = realtime - (int)realtime; if (fog->visibleplane) point = (DotProduct(xyz, fog->visibleplane->normal) - fog->visibleplane->dist); @@ -2737,11 +2740,11 @@ static void DrawPass(const shaderpass_t *pass) for (i = 0; i < lastpass; i++) { - if (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXVALID(shaderstate.curtexnums->upperoverlay)) + if (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay)) continue; - if (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXVALID(shaderstate.curtexnums->loweroverlay)) + if (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay)) continue; - if (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXVALID(shaderstate.curtexnums->fullbright)) + if (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright)) continue; break; } @@ -2752,11 +2755,11 @@ static void DrawPass(const shaderpass_t *pass) tmu = 0; for (; i < lastpass; i++) { - if (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXVALID(shaderstate.curtexnums->upperoverlay)) + if (pass[i].texgen == T_GEN_UPPEROVERLAY && !TEXLOADED(shaderstate.curtexnums->upperoverlay)) continue; - if (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXVALID(shaderstate.curtexnums->loweroverlay)) + if (pass[i].texgen == T_GEN_LOWEROVERLAY && !TEXLOADED(shaderstate.curtexnums->loweroverlay)) continue; - if (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXVALID(shaderstate.curtexnums->fullbright)) + if (pass[i].texgen == T_GEN_FULLBRIGHT && !TEXLOADED(shaderstate.curtexnums->fullbright)) continue; Shader_BindTextureForPass(tmu, pass+i); attr |= (1u<<(VATTR_LEG_TMU0+tmu)); @@ -3193,15 +3196,15 @@ static void BE_RenderMeshProgram(const shader_t *shader, const shaderpass_t *pas } if (p->permu[perm|PERMUTATION_FRAMEBLEND].handle.glsl && shaderstate.sourcevbo->coord2.gl.addr) perm |= PERMUTATION_FRAMEBLEND; - if (TEXVALID(shaderstate.curtexnums->bump) && p->permu[perm|PERMUTATION_BUMPMAP].handle.glsl) + if (TEXLOADED(shaderstate.curtexnums->bump) && p->permu[perm|PERMUTATION_BUMPMAP].handle.glsl) perm |= PERMUTATION_BUMPMAP; - if (TEXVALID(shaderstate.curtexnums->fullbright) && p->permu[perm|PERMUTATION_FULLBRIGHT].handle.glsl) + if (TEXLOADED(shaderstate.curtexnums->fullbright) && p->permu[perm|PERMUTATION_FULLBRIGHT].handle.glsl) perm |= PERMUTATION_FULLBRIGHT; - if ((TEXVALID(shaderstate.curtexnums->loweroverlay) || TEXVALID(shaderstate.curtexnums->upperoverlay)) && p->permu[perm|PERMUTATION_UPPERLOWER].handle.glsl) + if ((TEXLOADED(shaderstate.curtexnums->loweroverlay) || TEXLOADED(shaderstate.curtexnums->upperoverlay)) && p->permu[perm|PERMUTATION_UPPERLOWER].handle.glsl) perm |= PERMUTATION_UPPERLOWER; if (r_refdef.globalfog.density && p->permu[perm|PERMUTATION_FOG].handle.glsl) perm |= PERMUTATION_FOG; - if (p->permu[perm|PERMUTATION_DELUXE].handle.glsl && TEXVALID(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe) + if (p->permu[perm|PERMUTATION_DELUXE].handle.glsl && TEXLOADED(shaderstate.curtexnums->bump) && shaderstate.curbatch->lightmap[0] >= 0 && lightmap[shaderstate.curbatch->lightmap[0]]->hasdeluxe) perm |= PERMUTATION_DELUXE; #if MAXRLIGHTMAPS > 1 if (shaderstate.curbatch->lightmap[1] >= 0 && p->permu[perm|PERMUTATION_LIGHTSTYLES].handle.glsl) @@ -3496,7 +3499,7 @@ static qboolean GLBE_RegisterLightShader(int mode) } #endif -qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) +qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode) { extern cvar_t gl_specular; @@ -3507,16 +3510,16 @@ qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) /*simple info*/ shaderstate.lightradius = dl->radius; VectorCopy(dl->origin, shaderstate.lightorg); - shaderstate.lightcolourscale[2] *= gl_specular.value; VectorCopy(colour, shaderstate.lightcolours); #ifdef RTLIGHTS VectorCopy(dl->lightcolourscales, shaderstate.lightcolourscale); + shaderstate.lightcolourscale[2] *= gl_specular.value; if (lmode & LSHADER_SPOT) shaderstate.lightcubemap = r_nulltex; else shaderstate.lightcubemap = dl->cubetexture; - if (TEXVALID(shaderstate.lightcubemap) && GLBE_RegisterLightShader(shaderstate.lightmode | LSHADER_CUBE)) + if (TEXLOADED(shaderstate.lightcubemap) && GLBE_RegisterLightShader(shaderstate.lightmode | LSHADER_CUBE)) shaderstate.lightmode |= LSHADER_CUBE; if (!GLBE_RegisterLightShader(shaderstate.lightmode)) return false; @@ -3528,19 +3531,19 @@ qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode) float proj[16]; extern cvar_t r_shadow_shadowmapping_nearclip; Matrix4x4_CM_Projection_Far(proj, dl->fov, dl->fov, r_shadow_shadowmapping_nearclip.value, dl->radius); - Matrix4x4_CM_ModelViewMatrixFromAxis(view, dl->axis[0], dl->axis[1], dl->axis[2], dl->origin); + Matrix4x4_CM_ModelViewMatrixFromAxis(view, axis[0], axis[1], axis[2], dl->origin); Matrix4_Multiply(proj, view, shaderstate.lightprojmatrix); } else if (shaderstate.lightmode & (LSHADER_SMAP|LSHADER_CUBE)) { - Matrix4x4_CM_LightMatrixFromAxis(shaderstate.lightprojmatrix, dl->axis[0], dl->axis[1], dl->axis[2], dl->origin); + Matrix4x4_CM_LightMatrixFromAxis(shaderstate.lightprojmatrix, axis[0], axis[1], axis[2], dl->origin); /* vec3_t down; vec3_t back; vec3_t right; - VectorScale(dl->axis[2], -1, down); - VectorScale(dl->axis[1], 1, right); - VectorScale(dl->axis[0], 1, back); + VectorScale(axis[2], -1, down); + VectorScale(axis[1], 1, right); + VectorScale(axis[0], 1, back); Matrix4x4_CM_ModelViewMatrixFromAxis(shaderstate.lightprojmatrix, down, back, right, dl->origin); */ } @@ -3643,6 +3646,8 @@ static void BE_LegacyLighting(void) //vbo-only mesh. if (!mesh->xyz_array) return; + if (!mesh->normals_array) + return; col = coloursarray[0] + mesh->vbofirstvert*4; ldir = texcoordarray[0] + mesh->vbofirstvert*3; @@ -3659,10 +3664,10 @@ static void BE_LegacyLighting(void) } } - if (shaderstate.curtexnums->bump.num && gl_config.arb_texture_cube_map && gl_config.arb_texture_env_dot3 && gl_config.arb_texture_env_combine && be_maxpasses >= 4) + if (TEXVALID(shaderstate.curtexnums->bump) && gl_config.arb_texture_cube_map && gl_config.arb_texture_env_dot3 && gl_config.arb_texture_env_combine && be_maxpasses >= 4) { //we could get this down to 2 tmus by arranging for the dot3 result to be written the alpha buffer. But then we'd need to have an alpha buffer too. - if (!shaderstate.normalisationcubemap.num) + if (!shaderstate.normalisationcubemap) shaderstate.normalisationcubemap = GenerateNormalisationCubeMap(); //tmu0: normalmap+replace+regular tex coords @@ -3973,7 +3978,7 @@ static void DrawMeshes(void) GenerateFogTexture(&shaderstate.fogtexture, shaderstate.curbatch->fog->shader->fog_dist, 2048); shaderstate.fogfar = 1.0f/2048; /*scaler for z coords*/ - while(shaderstate.lastpasstmus>0) + while(shaderstate.lastpasstmus>1) { GL_LazyBind(--shaderstate.lastpasstmus, 0, r_nulltex); } @@ -4300,7 +4305,7 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) if (shaderstate.mode == BEM_LIGHT) continue; if (batch->shader->flags & SHADER_NOSHADOWS) - if (shaderstate.mode == BEM_DEPTHONLY) + if (shaderstate.mode == BEM_STENCIL || shaderstate.mode == BEM_DEPTHONLY) //fixme: depthonly is not just shadows. continue; if (batch->shader->flags & SHADER_SKY) { @@ -4333,9 +4338,10 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; - if (!shaderstate.tex_reflection.num) + if (!shaderstate.tex_reflection) { - shaderstate.tex_reflection = GL_AllocNewTexture("***tex_reflection***", vid.pixelwidth/2, vid.pixelheight/2, 0); + shaderstate.tex_reflection = Image_CreateTexture("***tex_reflection***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_reflection->num); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_reflection); 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); @@ -4370,9 +4376,10 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) vrect_t ovrect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; - if (!shaderstate.tex_refraction.num) + if (!shaderstate.tex_refraction) { - shaderstate.tex_refraction = GL_AllocNewTexture("***tex_refraction***", vid.pixelwidth/2, vid.pixelheight/2, 0); + shaderstate.tex_refraction = Image_CreateTexture("***tex_refraction***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_refraction->num); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refraction); 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); @@ -4382,9 +4389,10 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) } if (batch->shader->flags & SHADER_HASREFRACTDEPTH) { - if (!shaderstate.tex_refractiondepth.num) + if (!shaderstate.tex_refractiondepth) { - shaderstate.tex_refractiondepth = GL_AllocNewTexture("***tex_refractiondepth***", vid.pixelwidth/2, vid.pixelheight/2, 0); + shaderstate.tex_refractiondepth = Image_CreateTexture("***tex_refractiondepth***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_refractiondepth->num); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_refractiondepth); qglTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, vid.pixelwidth/2, vid.pixelheight/2, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -4427,10 +4435,11 @@ static void GLBE_SubmitMeshesSortList(batch_t *sortlist) { vrect_t orect = r_refdef.vrect; pxrect_t oprect = r_refdef.pxrect; - if (!shaderstate.tex_ripplemap.num) + if (!shaderstate.tex_ripplemap) { //FIXME: can we use RGB8 instead? - shaderstate.tex_ripplemap = GL_AllocNewTexture("***tex_ripplemap***", vid.pixelwidth/2, vid.pixelheight/2, 0); + shaderstate.tex_ripplemap = Image_CreateTexture("***tex_ripplemap***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_ripplemap->num); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_ripplemap); qglTexImage2D(GL_TEXTURE_2D, 0, /*(gl_config.glversion>3.1)?GL_RGBA8_SNORM:*/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); @@ -4526,7 +4535,9 @@ static void BE_UpdateLightmaps(void) lm->modified = false; if (!TEXVALID(lm->lightmap_texture)) { - TEXASSIGN(lm->lightmap_texture, R_AllocNewTexture("***lightmap***", lm->width, lm->height, IF_LINEAR|IF_NOMIPMAP)); + extern cvar_t gl_lightmap_nearest; + TEXASSIGN(lm->lightmap_texture, Image_CreateTexture("***lightmap***", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP)); + qglGenTextures(1, &lm->lightmap_texture->num); GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -4570,7 +4581,7 @@ void GLBE_BaseEntTextures(void) batch_t **ob = shaderstate.mbatches; shaderstate.mbatches = batches; BE_GenModelBatches(batches, shaderstate.curdlight, shaderstate.mode); - GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_DECAL); + GLBE_SubmitMeshes(false, SHADER_SORT_PORTAL, SHADER_SORT_SEETHROUGH+1); GLBE_SelectEntity(&r_worldentity); shaderstate.mbatches = ob; } @@ -4714,7 +4725,7 @@ int GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, i } if (enables & FBO_TEX_DEPTH) { - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, destdepth.num, 0); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, destdepth->num, 0); //fixme: no stencil } else if (enables & FBO_RB_DEPTH) @@ -4748,7 +4759,7 @@ int GLBE_FBO_Update(fbostate_t *state, unsigned int enables, texid_t *destcol, i } for (i = 0; i < mrt; i++) - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D, destcol[i].num, 0); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D, destcol[i]->num, 0); i = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if (GL_FRAMEBUFFER_COMPLETE_EXT != i) @@ -4921,7 +4932,8 @@ void GLBE_DrawLightPrePass(qbyte *vis) if (!TEXVALID(shaderstate.tex_normals)) { - shaderstate.tex_normals = GL_AllocNewTexture("***prepass normals***", vid.pixelwidth, vid.pixelheight, 0); + shaderstate.tex_normals = Image_CreateTexture("***prepass normals***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_normals->num); r_lightprepass.modified = true; } if (r_lightprepass.modified) @@ -4935,7 +4947,8 @@ void GLBE_DrawLightPrePass(qbyte *vis) if (!TEXVALID(shaderstate.tex_diffuse)) { - shaderstate.tex_diffuse = GL_AllocNewTexture("***prepass diffuse***", vid.pixelwidth, vid.pixelheight, 0); + shaderstate.tex_diffuse = Image_CreateTexture("***prepass diffuse***", NULL, 0); + qglGenTextures(1, &shaderstate.tex_diffuse->num); GL_MTBind(0, GL_TEXTURE_2D, shaderstate.tex_diffuse); 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); @@ -5006,6 +5019,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) TRACE(("GLBE_DrawWorld: %i %p\n", drawworld, vis)); + //reset batches if we needed more mem, to avoid allocations mid-frame. if (!r_refdef.recurse) { if (shaderstate.wbatch + 50 > shaderstate.maxwbatches) @@ -5018,6 +5032,7 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) shaderstate.wbatch = 0; } + //if the video mode changed, update any fbos (hopefully this won't happen on mirrors) if (shaderstate.oldwidth != vid.pixelwidth || shaderstate.oldheight != vid.pixelheight) { GLBE_DestroyFBOs(); //will be recreated on demand @@ -5148,6 +5163,8 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) void GLBE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize) { + COM_AssertMainThread("GLBE_VBO_Begin"); + ctx->maxsize = maxsize; ctx->pos = 0; ctx->fallback = NULL; diff --git a/engine/gl/gl_bloom.c b/engine/gl/gl_bloom.c index 51c0e699..35c54546 100644 --- a/engine/gl/gl_bloom.c +++ b/engine/gl/gl_bloom.c @@ -114,7 +114,8 @@ static void R_SetupBloomTextures(int w, int h) if (!TEXVALID(pingtex[i][j])) { sprintf(name, "***bloom*%c*%i***", 'a'+i, j); - TEXASSIGN(pingtex[i][j], GL_AllocNewTexture(name, texwidth[j], texheight[j], IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); + TEXASSIGN(pingtex[i][j], Image_CreateTexture(name, NULL, IF_CLAMP|IF_NOMIPMAP|IF_NOPICMIP|IF_LINEAR)); + qglGenTextures(1, &pingtex[i][j]->num); } GL_MTBind(0, GL_TEXTURE_2D, pingtex[i][j]); qglTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, texwidth[j], texheight[j], 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 80580b11..c30521b1 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -27,137 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "shader.h" #include "gl_draw.h" -#include // is this needed for atoi? -#include // is this needed for atoi? -//#define GL_USE8BITTEX - -static void GL_Upload32 (const char *name, unsigned *data, int width, int height, unsigned int flags); -static void GL_Upload32_BGRA (const char *name, unsigned *data, int width, int height, unsigned int flags); -static void GL_Upload24BGR_Flip (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags); -static void GL_Upload8 (const char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha); -static void GL_Upload8Pal32 (qbyte *data, qbyte *pal, int width, int height, unsigned int flags); -static void GL_Upload32_Int (const char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode); - -void GL_UploadFmt(texid_t tex, const char *name, enum uploadfmt fmt, void *data, void *palette, int width, int height, unsigned int flags) -{ - GL_MTBind(0, GL_TEXTURE_2D, tex); - switch(fmt) - { - case TF_INVALID: - break; - - case TF_RGBX32: - flags |= IF_NOALPHA; - case TF_RGBA32: - GL_Upload32(name, data, width, height, flags); - break; - - case TF_BGRX32: - flags |= IF_NOALPHA; - case TF_BGRA32: - GL_Upload32_BGRA(name, data, width, height, flags); - break; - -// case TF_BGRA24: -// GL_Upload24BGR(name, data, width, height, flags); -// break; - - case TF_BGR24_FLIP: - GL_Upload24BGR_Flip(name, data, width, height, flags); - break; - - case TF_SOLID8: - GL_Upload8(name, data, width, height, flags, 0); - break; - - case TF_TRANS8: - GL_Upload8(name, data, width, height, flags, 1); - break; - - case TF_8PAL24: - GL_Upload8Pal24(data, palette, width, height, flags); - break; - case TF_8PAL32: - GL_Upload8Pal32(data, palette, width, height, flags); - break; - - //render target formats, data is not supported. this means we can just use the 32bit upload. - case TF_DEPTH16: - GL_Upload32_Int(name, NULL, width, height, flags|IF_NOMIPMAP, GL_DEPTH_COMPONENT16_ARB); - break; - case TF_DEPTH24: - GL_Upload32_Int(name, NULL, width, height, flags|IF_NOMIPMAP, GL_DEPTH_COMPONENT24_ARB); - break; - case TF_DEPTH32: - GL_Upload32_Int(name, NULL, width, height, flags|IF_NOMIPMAP, GL_DEPTH_COMPONENT32_ARB); - break; - case TF_RGBA16F: - GL_Upload32_Int(name, NULL, width, height, flags|IF_NOMIPMAP|IF_CLAMP, GL_RGBA16F_ARB); - break; - case TF_RGBA32F: - GL_Upload32_Int(name, NULL, width, height, flags|IF_NOMIPMAP|IF_CLAMP, GL_RGBA32F_ARB); - break; - - default: - Sys_Error("Unsupported image format type\n"); - break; - } -} - -texid_t GL_LoadTextureFmt (const char *name, int width, int height, enum uploadfmt fmt, void *data, unsigned int flags) -{ - extern cvar_t r_shadow_bumpscale_basetexture, r_shadow_bumpscale_bumpmap; - switch(fmt) - { - case TF_INVALID: - return r_nulltex; - - case TF_RGBX32: - flags |= IF_NOALPHA; - case TF_RGBA32: - return GL_LoadTexture32(name, width, height, data, flags); - - case TF_TRANS8: - return GL_LoadTexture(name, width, height, data, flags, 1); - - case TF_TRANS8_FULLBRIGHT: - return GL_LoadTextureFB(name, width, height, data, flags); - - case TF_SOLID8: - return GL_LoadTexture(name, width, height, data, flags, 0); - - case TF_H2_T7G1: - return GL_LoadTexture(name, width, height, data, flags, 2); - case TF_H2_TRANS8_0: - return GL_LoadTexture(name, width, height, data, flags, 3); - case TF_H2_T4A4: - return GL_LoadTexture(name, width, height, data, flags, 4); - - case TF_HEIGHT8PAL: - return GL_LoadTexture8Bump(name, width, height, data, flags, r_shadow_bumpscale_basetexture.value); - case TF_HEIGHT8: - return GL_LoadTexture8Bump(name, width, height, data, flags, r_shadow_bumpscale_bumpmap.value); - - default: - Sys_Error("Unsupported image format type\n"); - return r_nulltex; - } -} - -qbyte *uploadmemorybuffer; -int sizeofuploadmemorybuffer; -qbyte *uploadmemorybufferintermediate; -int sizeofuploadmemorybufferintermediate; - -extern qbyte gammatable[256]; - -#ifdef GL_USE8BITTEX -unsigned char *d_15to8table; -qboolean inited15to8; -#endif - -int maxtexsize; //max_size for 2d images. extern cvar_t gl_max_size; extern cvar_t gl_picmip; extern cvar_t gl_lerpimages; @@ -166,299 +36,20 @@ extern cvar_t gl_compress; extern cvar_t gl_smoothcrosshair; extern cvar_t gl_texturemode, gl_texture_anisotropic_filtering; -extern cvar_t gl_savecompressedtex; +float gl_anisotropy_factor; -int gl_anisotropy_factor; - -mpic_t *conback; - -#include "hash.h" -hashtable_t gltexturetable; -bucket_t *gltexturetablebuckets[256]; - -int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; -int gl_filter_max = GL_LINEAR; -int gl_filter_max_2d = GL_LINEAR; +static int gl_filter_pic[3]; //ui elements +static int gl_filter_mip[3]; //everything else int gl_mipcap_min = 0; int gl_mipcap_max = 1000; -typedef struct gltexture_s -{ - texcom_t com; - texid_t texnum; - char identifier[MAX_QPATH]; - int bpp; - unsigned int flags; - struct gltexture_s *next; -} gltexture_t; - -static gltexture_t *gltextures; - -static gltexture_t *GL_AllocNewGLTexture(const char *ident, int w, int h, unsigned int flags) -{ - gltexture_t *glt; - glt = BZ_Malloc(sizeof(*glt) + sizeof(bucket_t)); - glt->next = gltextures; - gltextures = glt; - - glt->texnum.ref = &glt->com; - Q_strncpyz (glt->identifier, ident, sizeof(glt->identifier)); - glt->flags = flags; - glt->com.width = w; - glt->com.height = h; - glt->bpp = 0; - glt->com.regsequence = r_regsequence; - - qglGenTextures(1, &glt->texnum.num); - - if (*glt->identifier) - Hash_Add(&gltexturetable, glt->identifier, glt, (bucket_t*)(glt+1)); - return glt; -} - -texid_t GL_AllocNewTexture(const char *name, int w, int h, unsigned int flags) -{ - gltexture_t *glt = GL_AllocNewGLTexture(name, w, h, flags); - return glt->texnum; -} - void GL_DestroyTexture(texid_t tex) { - gltexture_t **link; - if (!tex.ref) + if (!tex) return; - for (link = &gltextures; *link; link = &(*link)->next) - { - if (*link == (gltexture_t*)tex.ref) - { - Hash_RemoveData(&gltexturetable, (*link)->identifier, *link); - *link = (*link)->next; - qglDeleteTextures(1, &tex.num); - BZ_Free(tex.ref); - return; - } - } -} - -//============================================================================= -/* Support Routines */ - -typedef struct -{ - char *name; - char *altname; - int minimize, maximize; -} glmode_t; - -glmode_t modes[] = { - {"GL_NEAREST", "n", GL_NEAREST, GL_NEAREST}, - {"GL_LINEAR", "l", GL_LINEAR, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_NEAREST", "nn", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_NEAREST", "ln", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_LINEAR", "nl", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_LINEAR", "ll", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} -}; - -void GL_Texture_Anisotropic_Filtering_Callback (struct cvar_s *var, char *oldvalue) -{ - gltexture_t *glt; - int anfactor; - - if (qrenderer != QR_OPENGL) - return; - - gl_anisotropy_factor = 0; - - if (gl_config.ext_texture_filter_anisotropic < 2) - return; - - anfactor = bound(1, var->value, gl_config.ext_texture_filter_anisotropic); - - /* change all the existing max anisotropy settings */ - for (glt = gltextures; glt ; glt = glt->next) //redo anisotropic filtering when map is changed - { - if (!(glt->flags & IF_NOMIPMAP)) - { - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anfactor); - } - } - - if (anfactor >= 2) - gl_anisotropy_factor = anfactor; - else - gl_anisotropy_factor = 0; -} - -void GL_Mipcap_Callback (struct cvar_s *var, char *oldvalue) -{ - gltexture_t *glt; - char *s = var->string; - - if (gl_config.gles) - return; - - s = COM_Parse(s); - gl_mipcap_min = *com_token?atoi(com_token):0; - if (gl_mipcap_min > 3) /*cap it to 3, so no 16*16 textures get bugged*/ - gl_mipcap_min = 3; - s = COM_Parse(s); - gl_mipcap_max = *com_token?atoi(com_token):1000; - if (gl_mipcap_max < gl_mipcap_min) - gl_mipcap_max = gl_mipcap_min; - - for (glt=gltextures ; glt ; glt=glt->next) - { - if (!(glt->flags & IF_NOMIPMAP)) - if (glt->flags & IF_MIPCAP) - { - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, gl_mipcap_min); - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, gl_mipcap_max); - } - } -} -void GL_Texturemode_Apply(GLenum targ, unsigned int flags) -{ - int mag; - if (flags & IF_UIPIC) - { - qglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, gl_filter_max_2d); - if (flags & IF_NEAREST) - qglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else if (flags & IF_LINEAR) - qglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - else - qglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, gl_filter_max_2d); - } - else - { - if (flags & IF_NEAREST) - mag = GL_NEAREST; - else if (flags & IF_LINEAR) - mag = GL_LINEAR; - else - mag = gl_filter_max; - - if (flags & IF_NOMIPMAP) - qglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, mag); - else - qglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, mag); - } -} - -/* -=============== -Draw_TextureMode_f -=============== -*/ -void GL_Texturemode_Callback (struct cvar_s *var, char *oldvalue) -{ - int i; - gltexture_t *glt; - int targ; - - if (qrenderer != QR_OPENGL) - return; - - for (i=0 ; i< sizeof(modes)/sizeof(modes[0]) ; i++) - { - if (!Q_strcasecmp (modes[i].name, var->string ) ) - break; - if (!Q_strcasecmp (modes[i].altname, var->string ) ) - break; - } - if (i == sizeof(modes)/sizeof(modes[0])) - { - Con_Printf ("bad gl_texturemode name - %s\n", var->string); - return; - } - - gl_filter_min = modes[i].minimize; - gl_filter_max = modes[i].maximize; - - // change all the existing mipmap texture objects - for (glt=gltextures ; glt ; glt=glt->next) - { - if (!(glt->flags & IF_UIPIC)) - { - if (glt->flags & IF_CUBEMAP) - targ = GL_TEXTURE_CUBE_MAP_ARB; - else - targ = GL_TEXTURE_2D; - - GL_MTBind(0, targ, glt->texnum); - GL_Texturemode_Apply(targ, glt->flags); - } - } -} -void GL_Texturemode2d_Callback (struct cvar_s *var, char *oldvalue) -{ - int i; - gltexture_t *glt; - - if (qrenderer != QR_OPENGL) - return; - - for (i=0 ; i< sizeof(modes)/sizeof(modes[0]) ; i++) - { - if (!Q_strcasecmp (modes[i].name, var->string ) ) - break; - if (!Q_strcasecmp (modes[i].altname, var->string ) ) - break; - } - if (i == sizeof(modes)/sizeof(modes[0])) - { - Con_Printf ("bad gl_texturemode name\n"); - return; - } - -// gl_filter_min = modes[i].minimize; - gl_filter_max_2d = modes[i].maximize; - - // change all the existing mipmap texture objects - for (glt=gltextures ; glt ; glt=glt->next) - { - if (glt->flags & IF_UIPIC) - { - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - GL_Texturemode_Apply(GL_TEXTURE_2D, glt->flags); - } - } -} - -void GLDraw_ImageList_f(void) -{ - int count = 0; - unsigned int mem = 0; - gltexture_t *glt; - for (glt=gltextures ; glt ; glt=glt->next) - { - count++; - mem += glt->com.width * glt->com.height * 4; - Con_Printf("%s (%i*%i, seq=%i)\n", glt->identifier, glt->com.width, glt->com.height, glt->com.regsequence); - } - Con_Printf("%i images, %i bytes\n", count, mem); -} - -void GLDraw_FlushOldTextures(void) -{ - gltexture_t **link = &gltextures, *t; - while (*link) - { - t = *link; - if (t->com.regsequence != r_regsequence) - { - //make sure the hash table can't still find it... - Hash_RemoveData(&gltexturetable, t->identifier, t); - qglDeleteTextures(1, &t->texnum.num); - (*link)->next = t->next; - BZ_Free(t); - } - else - link = &(*link)->next; - } + if (tex->num) + qglDeleteTextures(1, &tex->num); + tex->num = 0; } /* @@ -468,32 +59,8 @@ Draw_Init */ void GLDraw_Init (void) { - char ver[40]; - - if (gltextures) - gltextures = NULL; - - memset(gltexturetablebuckets, 0, sizeof(gltexturetablebuckets)); - Hash_InitTable(&gltexturetable, sizeof(gltexturetablebuckets)/sizeof(gltexturetablebuckets[0]), gltexturetablebuckets); - - maxtexsize = 256; - qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize); - if (gl_max_size.value > maxtexsize) - { - sprintf(ver, "%i", maxtexsize); - Cvar_ForceSet (&gl_max_size, ver); - } - - if (uploadmemorybuffer) - BZ_Free(uploadmemorybuffer); - if (uploadmemorybufferintermediate) - BZ_Free(uploadmemorybufferintermediate); - //required to hold the image after scaling has occured - sizeofuploadmemorybuffer = 0; - sizeofuploadmemorybufferintermediate = 0; -TRACE(("dbg: GLDraw_ReInit: Allocating upload buffers\n")); - uploadmemorybuffer = NULL; - uploadmemorybufferintermediate = NULL; + r_config.maxtexturesize = 256; + qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &r_config.maxtexturesize); R2D_Init(); @@ -534,7 +101,7 @@ TRACE(("dbg: GLDraw_ReInit: Allocating upload buffers\n")); TRACE(("dbg: GLDraw_ReInit: PPL_LoadSpecularFragmentProgram\n")); GL_InitSceneProcessingShaders(); - Cmd_AddCommandD ("r_imagelist", GLDraw_ImageList_f, "Debug command. Reveals current list of loaded images."); +// Cmd_AddCommandD ("r_imagelist", GLDraw_ImageList_f, "Debug command. Reveals current list of loaded images."); } void GLDraw_DeInit (void) @@ -548,29 +115,12 @@ void GLDraw_DeInit (void) draw_disc = NULL; GL_ShutdownPostProcessing(); - if (uploadmemorybuffer) - BZ_Free(uploadmemorybuffer); //free the mem - if (uploadmemorybufferintermediate) - BZ_Free(uploadmemorybufferintermediate); - uploadmemorybuffer = NULL; //make sure we know it's free - uploadmemorybufferintermediate = NULL; - sizeofuploadmemorybuffer = 0; //and give a nice safe sys_error if we try using it. - sizeofuploadmemorybufferintermediate = 0; + Image_Shutdown(); #ifdef RTLIGHTS Sh_Shutdown(); #endif Shader_Shutdown(); - - while(gltextures) - { - gltexture_t *glt; - glt = gltextures; - gltextures = gltextures->next; - - BZ_Free(glt); - } - } @@ -655,639 +205,98 @@ void GL_Set2D (qboolean flipped) //==================================================================== -/* -================ -GL_FindTexture -================ -*/ -texid_t GL_FindTexture (const char *identifier, unsigned int flags) -{ - gltexture_t *glt; - glt = Hash_Get(&gltexturetable, identifier); - while(glt) +#ifndef GL_COMPRESSED_RGB_S3TC_DXT1_EXT +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +//note: needs to be bound first, so the 'targ' argument shouldn't be a problem. +static void GL_Texturemode_Apply(GLenum targ, unsigned int flags) +{ + int min, mag; + int *filter = (flags & IF_UIPIC)?gl_filter_pic:gl_filter_mip; + + if (targ == GL_TEXTURE_CUBE_MAP_ARB) + flags |= IF_NOMIPMAP; + + if ((filter[2] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR)) + mag = GL_LINEAR; + else + mag = GL_NEAREST; + if (filter[1] == -1 || (flags & IF_NOMIPMAP)) { - if ((glt->flags ^ flags) & IF_CLAMP) - { - glt = Hash_GetNext(&gltexturetable, identifier, glt); - continue; - } - return glt->texnum; + if ((filter[0] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR)) + min = GL_LINEAR; + else + min = GL_NEAREST; } - - return r_nulltex; -} - -gltexture_t *GL_MatchTexture (const char *identifier, unsigned int flags, int bits, int width, int height) -{ - gltexture_t *glt; - - glt = Hash_Get(&gltexturetable, identifier); - while(glt) + else { - if (glt->bpp == bits && width == glt->com.width && height == glt->com.height && !((glt->flags ^ flags) & IF_CLAMP)) - return glt; - - glt = Hash_GetNext(&gltexturetable, identifier, glt); - } - - return NULL; -} - - - -static void Image_Resample32LerpLine (const qbyte *in, qbyte *out, int inwidth, int outwidth) -{ - int j, xi, oldx = 0, f, fstep, endx, lerp; - fstep = (int) (inwidth*65536.0f/outwidth); - endx = (inwidth-1); - for (j = 0,f = 0;j < outwidth;j++, f += fstep) - { - xi = f >> 16; - if (xi != oldx) + if ((filter[1] && !(flags & IF_NEAREST)) || (flags & IF_LINEAR)) { - in += (xi - oldx) * 4; - oldx = xi; - } - if (xi < endx) - { - lerp = f & 0xFFFF; - *out++ = (qbyte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]); - *out++ = (qbyte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]); - *out++ = (qbyte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]); - *out++ = (qbyte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]); - } - else // last pixel of the line has no pixel to lerp to - { - *out++ = in[0]; - *out++ = in[1]; - *out++ = in[2]; - *out++ = in[3]; - } - } -} - -//yes, this is lordhavok's code. -//superblur away! -#define LERPBYTE(i) r = row1[i];out[i] = (qbyte) ((((row2[i] - r) * lerp) >> 16) + r) -static void Image_Resample32Lerp(const void *indata, int inwidth, int inheight, void *outdata, int outwidth, int outheight) -{ - int i, j, r, yi, oldy, f, fstep, lerp, endy = (inheight-1), inwidth4 = inwidth*4, outwidth4 = outwidth*4; - qbyte *out; - const qbyte *inrow; - qbyte *tmem, *row1, *row2; - - tmem = row1 = BZ_Malloc(2*(outwidth*4)); - row2 = row1 + (outwidth * 4); - - out = outdata; - fstep = (int) (inheight*65536.0f/outheight); - - inrow = indata; - oldy = 0; - Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); - Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); - for (i = 0, f = 0;i < outheight;i++,f += fstep) - { - yi = f >> 16; - if (yi < endy) - { - lerp = f & 0xFFFF; - if (yi != oldy) - { - inrow = (qbyte *)indata + inwidth4*yi; - if (yi == oldy+1) - memcpy(row1, row2, outwidth4); - else - Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); - Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); - oldy = yi; - } - j = outwidth - 4; - while(j >= 0) - { - LERPBYTE( 0); - LERPBYTE( 1); - LERPBYTE( 2); - LERPBYTE( 3); - LERPBYTE( 4); - LERPBYTE( 5); - LERPBYTE( 6); - LERPBYTE( 7); - LERPBYTE( 8); - LERPBYTE( 9); - LERPBYTE(10); - LERPBYTE(11); - LERPBYTE(12); - LERPBYTE(13); - LERPBYTE(14); - LERPBYTE(15); - out += 16; - row1 += 16; - row2 += 16; - j -= 4; - } - if (j & 2) - { - LERPBYTE( 0); - LERPBYTE( 1); - LERPBYTE( 2); - LERPBYTE( 3); - LERPBYTE( 4); - LERPBYTE( 5); - LERPBYTE( 6); - LERPBYTE( 7); - out += 8; - row1 += 8; - row2 += 8; - } - if (j & 1) - { - LERPBYTE( 0); - LERPBYTE( 1); - LERPBYTE( 2); - LERPBYTE( 3); - out += 4; - row1 += 4; - row2 += 4; - } - row1 -= outwidth4; - row2 -= outwidth4; + if (filter[0]) + min = GL_LINEAR_MIPMAP_LINEAR; + else + min = GL_NEAREST_MIPMAP_LINEAR; } else { - if (yi != oldy) - { - inrow = (qbyte *)indata + inwidth4*yi; - if (yi == oldy+1) - memcpy(row1, row2, outwidth4); - else - Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); - oldy = yi; - } - memcpy(out, row1, outwidth4); + if (filter[0]) + min = GL_LINEAR_MIPMAP_NEAREST; + else + min = GL_NEAREST_MIPMAP_NEAREST; } } - BZ_Free(tmem); -} - -/* -================ -GL_ResampleTexture -================ -*/ -static void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) -{ - int i, j; - unsigned *inrow; - unsigned frac, fracstep; - - if (gl_lerpimages.ival) + qglTexParameteri(targ, GL_TEXTURE_MIN_FILTER, min); + qglTexParameteri(targ, GL_TEXTURE_MAG_FILTER, mag); + if (gl_anisotropy_factor) //0 means driver doesn't support { - Image_Resample32Lerp(in, inwidth, inheight, out, outwidth, outheight); - return; - } - - fracstep = inwidth*0x10000/outwidth; - for (i=0 ; i>16]; - frac -= fracstep; - j--; - } - for ( ; j>=0 ; j-=4) - { - out[j+3] = inrow[frac>>16]; - frac -= fracstep; - out[j+2] = inrow[frac>>16]; - frac -= fracstep; - out[j+1] = inrow[frac>>16]; - frac -= fracstep; - out[j+0] = inrow[frac>>16]; - frac -= fracstep; - } + //only use anisotrophy when using linear any linear, because of drivers that forces linear sampling when anis is active (annoyingly this is allowed by the spec). + if ((min == GL_LINEAR || min == GL_LINEAR_MIPMAP_LINEAR || min == GL_LINEAR_MIPMAP_NEAREST) && mag == GL_LINEAR) + qglTexParameterf(targ, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropy_factor); + else + qglTexParameterf(targ, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); } } -/* -================ -GL_Resample8BitTexture -- JACK -================ -*/ -static void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) +qboolean GL_LoadTextureMips(texid_t tex, struct pendingtextureinfo *mips) { - int i, j; - unsigned char *inrow; - unsigned frac, fracstep; - - fracstep = inwidth*0x10000/outwidth; - for (i=0 ; i> 1; - for (j=0 ; j>16]; - frac += fracstep; - out[j+1] = inrow[frac>>16]; - frac += fracstep; - out[j+2] = inrow[frac>>16]; - frac += fracstep; - out[j+3] = inrow[frac>>16]; - frac += fracstep; - } - } -} + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB + }; + int targ, targface; + int i, j; -/* -================ -GL_MipMap + if (!tex->num) + qglGenTextures(1, &tex->num); -Operates in place, quartering the size of the texture -================ -*/ -static void GL_MipMap (qbyte *in, int width, int height) -{ - int i, j; - qbyte *out; - qbyte *inrow; - - //with npot - int rowwidth = width*4; //rowwidth is the byte width of the input - inrow = in; - - width >>= 1; //ensure its truncated, so don't merge with the *8 - height >>= 1; - out = in; - - - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; - out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; - out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; - } - } -} - -#ifdef GL_USE8BITTEX -#ifdef GL_EXT_paletted_texture -void GLDraw_Init15to8(void) -{ - int i, r, g, b, v, k; - int r1, g1, b1; - qbyte *pal; - float dist, bestdist; - vfsfile_t *f; - - qboolean savetable; - - // JACK: 3D distance calcs - k is last closest, l is the distance. - if (inited15to8) - return; - if (!d_15to8table) - d_15to8table = BZ_Malloc(sizeof(qbyte) * 32768); - inited15to8 = true; - - savetable = COM_CheckParm("-save15to8"); - - if (savetable) - f = FS_OpenVFS("glquake/15to8.pal"); - else - f = NULL; - if (f) - { - VFS_READ(f, d_15to8table, 1<<15); - VFS_CLOSE(f); - } - else - { - for (i=0; i < (1<<15); i++) - { - /* Maps - 000000000000000 - 000000000011111 = Red = 0x1F - 000001111100000 = Blue = 0x03E0 - 111110000000000 = Grn = 0x7C00 - */ - r = ((i & 0x1F) << 3)+4; - g = ((i & 0x03E0) >> 2)+4; - b = ((i & 0x7C00) >> 7)+4; - pal = (unsigned char *)d_8to24rgbtable; - for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { - r1 = (int)r - (int)pal[0]; - g1 = (int)g - (int)pal[1]; - b1 = (int)b - (int)pal[2]; - dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); - if (dist < bestdist) { - k=v; - bestdist = dist; - } - } - d_15to8table[i]=k; - } - if (savetable) - { - FS_WriteFile("glquake/15to8.pal", d_15to8table, 1<<15, FS_GAME); - } - } -} - -/* -================ -GL_MipMap8Bit - -Mipping for 8 bit textures -================ -*/ -static void GL_MipMap8Bit (qbyte *in, int width, int height) -{ - int i, j; - qbyte *out; - unsigned short r,g,b; - qbyte *at1, *at2, *at3, *at4; - - height >>= 1; - out = in; - for (i=0 ; i>=5; - g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; - b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; - - out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; - } -} -#endif -#endif - -static qboolean GL_UploadCompressed (qbyte *file, int *out_width, int *out_height, unsigned int *out_flags) -{ - int miplevel; - int width; - int height; - int compressed_size; - int internalformat; - int nummips; -#define GETVAR(var) memcpy(var, file, sizeof(*var));file+=sizeof(*var); - - if (!gl_config.arb_texture_compression || !gl_compress.value) - return false; - - GETVAR(&nummips) - GETVAR(out_width) - GETVAR(out_height) - GETVAR(out_flags) - for (miplevel = 0; miplevel < nummips; miplevel++) - { - GETVAR(&width); - GETVAR(&height); - GETVAR(&compressed_size); - GETVAR(&internalformat); - width = LittleLong(width); - height = LittleLong(height); - compressed_size = LittleLong(compressed_size); - internalformat = LittleLong(internalformat); - - qglCompressedTexImage2DARB(GL_TEXTURE_2D, miplevel, internalformat, width, height, 0, compressed_size, file); - file += compressed_size; - } - - GL_Texturemode_Apply(GL_TEXTURE_2D, *out_flags); - return true; -} - - -void GL_RoundDimensions(int *scaled_width, int *scaled_height, unsigned int flags) -{ - if (gl_config.texture_non_power_of_two) //NPOT is a simple extension that relaxes errors. - { - TRACE(("dbg: GL_RoundDimensions: GL_ARB_texture_non_power_of_two\n")); - } - else if (gl_config.texture_non_power_of_two_limited && (flags&IF_NOMIPMAP) && (flags&IF_CLAMP)) - { - //clamped mipless textures will work as-is in gles2/webgl - } - else - { - int width = *scaled_width; - int height = *scaled_height; - for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1) - ; - for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1) - ; - - /*round npot textures down if we're running on an embedded system*/ - /* - if (gl_config.gles) - { - if (*scaled_width != width) - *scaled_width >>= 1; - if (*scaled_height != height) - *scaled_height >>= 1; - } - */ - } - - if (flags & IF_NOMIPMAP) - { - *scaled_width >>= gl_picmip2d.ival; - *scaled_height >>= gl_picmip2d.ival; - } - else - { - TRACE(("dbg: GL_RoundDimensions: %f\n", gl_picmip.value)); - *scaled_width >>= gl_picmip.ival; - *scaled_height >>= gl_picmip.ival; - } - - TRACE(("dbg: GL_RoundDimensions: %f\n", gl_max_size.value)); - - if (maxtexsize) - { - if (*scaled_width > maxtexsize) - *scaled_width = maxtexsize; - if (*scaled_height > maxtexsize) - *scaled_height = maxtexsize; - } - if (!(flags & IF_UIPIC)) - { - if (gl_max_size.value) - { - if (*scaled_width > gl_max_size.value) - *scaled_width = gl_max_size.value; - if (*scaled_height > gl_max_size.value) - *scaled_height = gl_max_size.value; - } - } - - if (*scaled_width < 1) - *scaled_width = 1; - if (*scaled_height < 1) - *scaled_height = 1; -} - -void GL_8888to565(int targ, unsigned char *in, unsigned short *out, unsigned int mip, unsigned int w, unsigned int h) -{ - unsigned int p = w*h; - unsigned short tmp; - void *iout = out; - - while(p-->0) - { - tmp = ((*in++>>3) << 11); - tmp |= ((*in++>>2) << 5); - tmp |= ((*in++>>3) << 0); - in++; - *out++ = tmp; - } - qglTexImage2D (targ, mip, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, iout); -} - -void GL_8888to4444(int targ, unsigned char *in, unsigned short *out, unsigned int mip, unsigned int w, unsigned int h) -{ - unsigned int p = w*h; - unsigned short tmp; - void *iout = out; - - while(p-->0) - { - tmp = ((*in++>>4) << 12); - tmp |= ((*in++>>4) << 8); - tmp |= ((*in++>>4) << 4); - tmp |= ((*in++>>4) << 0); - *out++ = tmp; - } - qglTexImage2D (targ, mip, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, iout); -} - -/* -=============== -GL_Upload32 -=============== -*/ -static void GL_Upload32_Int (const char *name, unsigned *data, int width, int height, unsigned int flags, GLenum glcolormode) -{ - int miplevel=0; - int samples; - unsigned *scaled; - int scaled_width, scaled_height; - int type; - int targ, targface; - - TRACE(("dbg: GL_Upload32: %s %i %i\n", name, width, height)); - - scaled_width = width; - scaled_height = height; - if (data) - { - GL_RoundDimensions(&scaled_width, &scaled_height, flags); - - if (!(flags & IF_NOALPHA)) - { //make sure it does actually have those alpha pixels (q3 compat) - int i; - flags |= IF_NOALPHA; - for (i = 3; i < width*height*4; i+=4) - { - if (((unsigned char*)data)[i] < 255) - { - flags &= ~IF_NOALPHA; - break; - } - } - } - } - - switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) + switch((tex->flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) { + default: case 0: - targface = targ = GL_TEXTURE_2D; + targ = GL_TEXTURE_2D; break; case 1: - targface = targ = GL_TEXTURE_3D; + targ = GL_TEXTURE_3D; break; - default: - targface = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + (((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) - 2); + case 2: targ = GL_TEXTURE_CUBE_MAP_ARB; break; } - TRACE(("dbg: GL_Upload32: %i %i\n", scaled_width, scaled_height)); + GL_MTBind(0, targ, tex); - if (!data) - scaled = NULL; - else - { - if (scaled_width * scaled_height*4 > sizeofuploadmemorybuffer) - { - sizeofuploadmemorybuffer = scaled_width * scaled_height * 4; - uploadmemorybuffer = BZ_Realloc(uploadmemorybuffer, sizeofuploadmemorybuffer); - } - scaled = (unsigned *)uploadmemorybuffer; - } - - if (glcolormode == GL_DEPTH_COMPONENT || glcolormode == GL_DEPTH_COMPONENT16_ARB || glcolormode == GL_DEPTH_COMPONENT24_ARB || glcolormode == GL_DEPTH_COMPONENT32_ARB) - { - samples = glcolormode; - glcolormode = GL_DEPTH_COMPONENT; - type = GL_UNSIGNED_BYTE; - } - else if (glcolormode == GL_RGBA16F_ARB || glcolormode == GL_RGBA32F_ARB) - { - samples = glcolormode; - glcolormode = GL_RGBA; - type = GL_FLOAT; - } - else if (gl_config.gles) - { - glcolormode = GL_RGBA; /*our input is RGBA or RGBX, with the internal format restriction, we must therefore always have an alpha value*/ - - if (1)//gl_config.webgl_ie) - { //fixme: I think my npot mips don't work properly. - type = GL_UNSIGNED_BYTE; - glcolormode = GL_RGBA; //I hope alpha is 1. note that samples matching colormode means we can't use packed formats, and I'm too lazy to strip it - } - else if (flags & IF_NOALPHA) - { - /*no alpha there, yay*/ - type = GL_UNSIGNED_SHORT_5_6_5; - glcolormode = GL_RGB; - } - else - { - /*we need an alpha channel, sorry for any banding*/ - type = GL_UNSIGNED_SHORT_4_4_4_4; - glcolormode = GL_RGBA; - } - - /*GLES requires that the internal format match the format*/ - samples = glcolormode; - } - else - { - samples = (flags&IF_NOALPHA) ? GL_RGB : GL_RGBA; - type = GL_UNSIGNED_BYTE; - } - - if (gl_config.arb_texture_compression && gl_compress.value && name && !(flags&IF_NOMIPMAP)) - samples = (flags&IF_NOALPHA) ? GL_COMPRESSED_RGB_ARB : GL_COMPRESSED_RGBA_ARB; - - if (flags&IF_CLAMP) + if (tex->flags&IF_CLAMP) { qglTexParameteri(targ, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(targ, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -1302,1231 +311,146 @@ static void GL_Upload32_Int (const char *name, unsigned *data, int width, int he qglTexParameteri(targ, GL_TEXTURE_WRAP_R, GL_REPEAT); } + tex->width = mips->mip[0].width; + tex->height = mips->mip[0].height; + GL_Texturemode_Apply(targ, tex->flags); if (targ == GL_TEXTURE_3D) { - int r,d; - if (scaled_height * scaled_height != scaled_width) - return; - - qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - if (flags & IF_NEAREST) - qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else - qglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - for (d = 0; d < scaled_height; d++) + targface = targ; + for (i = 0; i < mips->mipcount; i++) { - /*each 'layer' is sequential, which means we need to de-interlace the layers*/ - for (r = 0; r < scaled_height; r++) + int size = mips->mip[i].height; + switch(mips->encoding) { - memcpy(scaled + (r + d*scaled_height) * scaled_height, data + (r*scaled_height + d) * scaled_height, scaled_height*sizeof(*data)); + case PTI_RGBX8: + qglTexImage3D(targface, i, GL_RGB, size, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, mips->mip[i].data); + break; + case PTI_RGBA8: + qglTexImage3D(targface, i, GL_RGBA, size, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, mips->mip[i].data); + break; + case PTI_BGRX8: + qglTexImage3D(targface, i, GL_RGB, size, size, size, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, mips->mip[i].data); + break; + case PTI_BGRA8: + default: + qglTexImage3D(targface, i, GL_RGBA, size, size, size, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, mips->mip[i].data); + break; } + + if (mips->mip[i].needfree) + Z_Free(mips->mip[i].data); } - qglTexImage3D (GL_TEXTURE_3D, 0, samples, scaled_height, scaled_height, scaled_height, 0, glcolormode, GL_UNSIGNED_BYTE, scaled); - return; - } - - if (gl_config.sgis_generate_mipmap && !(flags&IF_NOMIPMAP)) - { - TRACE(("dbg: GL_Upload32: GL_SGIS_generate_mipmap\n")); - qglTexParameteri(targ, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); - } - - GL_Texturemode_Apply(targ, flags); - - if (!data) - scaled = NULL; -// qglTexImage2D (targface, 0, samples, scaled_width, scaled_height, 0, glcolormode, GL_UNSIGNED_BYTE, data); - else if (scaled_width == width && scaled_height == height) - { - if (((flags&IF_NOMIPMAP)||gl_config.sgis_generate_mipmap) && !(flags & IF_PREMULTIPLYALPHA)) //gotta love this with NPOT textures... :) - { - TRACE(("dbg: GL_Upload32: non-mipmapped/unscaled\n")); - if (type == GL_UNSIGNED_SHORT_5_6_5) - GL_8888to565(targface, (unsigned char *)data, (unsigned short*)scaled, 0, scaled_width, scaled_height); - else if (type == GL_UNSIGNED_SHORT_4_4_4_4) - GL_8888to4444(targface, (unsigned char *)data, (unsigned short*)scaled, 0, scaled_width, scaled_height); - else - qglTexImage2D (targface, 0, samples, scaled_width, scaled_height, 0, glcolormode, type, data); - goto done; - } - memcpy (scaled, data, width*height*4); - } - else - GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); - - if (flags & IF_PREMULTIPLYALPHA) - { - //works for rgba or bgra - int i; - unsigned char *premul = (unsigned char*)scaled; - for (i = 0; i < scaled_width*scaled_height; i++, premul+=4) - { - premul[0] = (premul[0] * premul[3])>>8; - premul[1] = (premul[1] * premul[3])>>8; - premul[2] = (premul[2] * premul[3])>>8; - } - } - if (scaled_width * scaled_height*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = scaled_width * scaled_height * 4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - - if (scaled_width*scaled_height*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = scaled_width*scaled_height*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - TRACE(("dbg: GL_Upload32: rescaled\n")); - if (type == GL_UNSIGNED_SHORT_5_6_5) - GL_8888to565(targface, (unsigned char *)scaled, (unsigned short*)uploadmemorybufferintermediate, 0, scaled_width, scaled_height); - else if (type == GL_UNSIGNED_SHORT_4_4_4_4) - GL_8888to4444(targface, (unsigned char *)scaled, (unsigned short*)uploadmemorybufferintermediate, 0, scaled_width, scaled_height); - else - qglTexImage2D (targface, 0, samples, scaled_width, scaled_height, 0, glcolormode, type, scaled); - if (!(flags&IF_NOMIPMAP) && !gl_config.sgis_generate_mipmap) - { - miplevel = 0; - TRACE(("dbg: GL_Upload32: mips\n")); - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap ((qbyte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - if (type == GL_UNSIGNED_SHORT_5_6_5) - GL_8888to565(targface, (unsigned char *)scaled, (unsigned short*)uploadmemorybufferintermediate, miplevel, scaled_width, scaled_height); - else if (type == GL_UNSIGNED_SHORT_4_4_4_4) - GL_8888to4444(targface, (unsigned char *)scaled, (unsigned short*)uploadmemorybufferintermediate, miplevel, scaled_width, scaled_height); - else - qglTexImage2D (targface, miplevel, samples, scaled_width, scaled_height, 0, glcolormode, GL_UNSIGNED_BYTE, scaled); - } - } - - if (targ == GL_TEXTURE_2D && gl_config.arb_texture_compression && gl_compress.value && gl_savecompressedtex.value && name && !(flags&IF_NOMIPMAP)) - { - vfsfile_t *out; - int miplevels; - GLint compressed; - GLint compressed_size; - GLint internalformat; - unsigned char *img; - char outname[MAX_OSPATH]; - int i; - miplevels = miplevel+1; - qglGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed); - if (compressed == GL_TRUE && !strstr(name, "..")) //is there any point in bothering with the whole endian thing? - { - Q_snprintfz(outname, sizeof(outname), "tex/%s.tex", name); - FS_CreatePath(outname, FS_GAMEONLY); - out = FS_OpenVFS(outname, "wb", FS_GAMEONLY); - if (out) - { - i = LittleLong(miplevels); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(width); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(height); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(flags); - VFS_WRITE(out, &i, sizeof(i)); - for (miplevel = 0; miplevel < miplevels; miplevel++) - { - qglGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_COMPRESSED_ARB, &compressed); - qglGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); - qglGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size); - qglGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_WIDTH, &width); - qglGetTexLevelParameteriv(GL_TEXTURE_2D, miplevel, GL_TEXTURE_HEIGHT, &height); - img = (unsigned char *)BZ_Malloc(compressed_size * sizeof(unsigned char)); - qglGetCompressedTexImageARB(GL_TEXTURE_2D, miplevel, img); - - i = LittleLong(width); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(height); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(compressed_size); - VFS_WRITE(out, &i, sizeof(i)); - i = LittleLong(internalformat); - VFS_WRITE(out, &i, sizeof(i)); - VFS_WRITE(out, img, compressed_size); - BZ_Free(img); - } - VFS_CLOSE(out); - } - } - } -done: - - if (gl_anisotropy_factor) - qglTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropy_factor); // without this, you could loose anisotropy on mapchange - - if (gl_config.sgis_generate_mipmap && !(flags&IF_NOMIPMAP)) - qglTexParameteri(targ, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); - - /*apply this flag after, so that we can safely change the base (to avoid drivers just not uploading lower mips)*/ - if (!gl_config.gles && (flags & IF_MIPCAP)) - { - qglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, gl_mipcap_min); - qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, gl_mipcap_max); - } -} - -void GL_Upload32 (const char *name, unsigned *data, int width, int height, unsigned int flags) -{ - GL_Upload32_Int(name, data, width, height, flags, GL_RGBA); -} -void GL_Upload32_BGRA (const char *name, unsigned *data, int width, int height, unsigned int flags) -{ - GL_Upload32_Int(name, data, width, height, flags, GL_BGRA_EXT); -} - -void GL_Upload24BGR (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) -{ - int outwidth, outheight; - int y, x; - - int v; - unsigned int f, fstep; - qbyte *src, *dest; - //change from bgr bottomup to rgba topdown - - outwidth = inwidth; - outheight = inheight; - GL_RoundDimensions(&outwidth, &outheight, flags); - - if (outwidth*outheight*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = outwidth*outheight*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - dest = uploadmemorybufferintermediate; - - for (y=0 ; y>16)*3+2]; - *dest++ = src[(f>>16)*3+1]; - *dest++ = src[(f>>16)*3+0]; - *dest++ = 255; - f += fstep; - } - for ( ; x ; x-=4) //loop through the remaining chunks. - { - dest[0] = src[(f>>16)*3+2]; - dest[1] = src[(f>>16)*3+1]; - dest[2] = src[(f>>16)*3+0]; - dest[3] = 255; - f += fstep; - - dest[4] = src[(f>>16)*3+2]; - dest[5] = src[(f>>16)*3+1]; - dest[6] = src[(f>>16)*3+0]; - dest[7] = 255; - f += fstep; - - dest[8] = src[(f>>16)*3+2]; - dest[9] = src[(f>>16)*3+1]; - dest[10] = src[(f>>16)*3+0]; - dest[11] = 255; - f += fstep; - - dest[12] = src[(f>>16)*3+2]; - dest[13] = src[(f>>16)*3+1]; - dest[14] = src[(f>>16)*3+0]; - dest[15] = 255; - f += fstep; - - dest += 16; - } - } - } - - GL_Upload32 (name, (unsigned int*)uploadmemorybufferintermediate, outwidth, outheight, flags); -} -void GL_Upload24BGR_Flip (const char *name, qbyte *framedata, int inwidth, int inheight, unsigned int flags) -{ - int outwidth, outheight; - int y, x; - - int v; - unsigned int f, fstep; - qbyte *src, *dest; - //change from bgr bottomup to rgba topdown - - outwidth = inwidth; - outheight = inheight; - GL_RoundDimensions(&outwidth, &outheight, flags); - - if (outwidth*outheight*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = outwidth*outheight*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - dest = uploadmemorybufferintermediate; - - for (y=1 ; y<=outheight ; y++) - { - v = ((outheight - y)*(float)inheight/outheight); - src = framedata + v*(inwidth*3); - { - f = 0; - fstep = ((inwidth)*0x10000)/outwidth; - - for (x=outwidth ; x&3 ; x--) //do the odd ones first. (bigger condition) - { - *dest++ = src[(f>>16)*3+2]; - *dest++ = src[(f>>16)*3+1]; - *dest++ = src[(f>>16)*3+0]; - *dest++ = 255; - f += fstep; - } - for ( ; x ; x-=4) //loop through the remaining chunks. - { - dest[0] = src[(f>>16)*3+2]; - dest[1] = src[(f>>16)*3+1]; - dest[2] = src[(f>>16)*3+0]; - dest[3] = 255; - f += fstep; - - dest[4] = src[(f>>16)*3+2]; - dest[5] = src[(f>>16)*3+1]; - dest[6] = src[(f>>16)*3+0]; - dest[7] = 255; - f += fstep; - - dest[8] = src[(f>>16)*3+2]; - dest[9] = src[(f>>16)*3+1]; - dest[10] = src[(f>>16)*3+0]; - dest[11] = 255; - f += fstep; - - dest[12] = src[(f>>16)*3+2]; - dest[13] = src[(f>>16)*3+1]; - dest[14] = src[(f>>16)*3+0]; - dest[15] = 255; - f += fstep; - - dest += 16; - } - } - } - - GL_Upload32 (name, (unsigned int*)uploadmemorybufferintermediate, outwidth, outheight, flags); -} - - -void GL_Upload8Grey (unsigned char*data, int width, int height, unsigned int flags) -{ - int samples; - unsigned char *scaled; - int scaled_width, scaled_height; - - scaled_width = width; - scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, flags); - - if (scaled_width * scaled_height*4 > sizeofuploadmemorybuffer) - { - sizeofuploadmemorybuffer = scaled_width * scaled_height * 4; - uploadmemorybuffer = BZ_Realloc(uploadmemorybuffer, sizeofuploadmemorybuffer); - } - scaled = uploadmemorybuffer; - - samples = 1;//alpha ? gl_alpha_format : gl_solid_format; - - if (scaled_width == width && scaled_height == height) - { - if (flags&IF_NOMIPMAP) - { - qglTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); - goto done; - } - memcpy (scaled, data, width*height); - } - else - GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); - - qglTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, scaled); - if (!(flags&IF_NOMIPMAP)) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap ((qbyte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - qglTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, scaled); - } - } -done: ; - - GL_Texturemode_Apply(GL_TEXTURE_2D, flags); -} - - - - - - - - - - - -void GL_MipMapNormal (qbyte *in, int width, int height) -{ - int i, j; - qbyte *out; - float inv255 = 1.0f/255.0f; - float inv127 = 1.0f/127.0f; - float x,y,z,l,mag00,mag01,mag10,mag11; - - - width <<=2; - height >>= 1; - out = in; - for (i=0 ; i 1.0) { - out[3] = 255; - } else { - out[3] = (qbyte)(255.0*l); - } - } - } -} - -//PENTA - -//sizeofuploadmemorybufferintermediate is guarenteed to be bigger or equal to the normal uploadbuffer size -static unsigned int * genNormalMap(qbyte *pixels, int w, int h, float scale) -{ - int i, j, wr, hr; - unsigned char r, g, b; - unsigned *nmap; - float sqlen, reciplen, nx, ny, nz; - - const float oneOver255 = 1.0f/255.0f; - - float c, cx, cy, dcx, dcy; - - if (w*h*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = w*h*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - nmap = (unsigned *)uploadmemorybufferintermediate; - - wr = w; - hr = h; - - for (i=0; i Added support for big endian. - } - } - - return &nmap[0]; -} - -//PENTA -void GL_UploadBump(qbyte *data, int width, int height, unsigned int flags, float bumpscale) -{ - unsigned char *scaled; - int scaled_width, scaled_height; - qbyte *nmap; - - TRACE(("dbg: GL_UploadBump entered: %i %i\n", width, height)); - - scaled_width = width; - scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, flags); - - if (scaled_width*scaled_height*4 > sizeofuploadmemorybuffer) - { - sizeofuploadmemorybuffer = scaled_width*scaled_height*4; - uploadmemorybuffer = BZ_Realloc(uploadmemorybuffer, sizeofuploadmemorybuffer); - } - scaled = uploadmemorybuffer; - - //To resize or not to resize - if (scaled_width == width && scaled_height == height) - { - memcpy (scaled, data, width*height); - scaled_width = width; - scaled_height = height; - } - else { - //Just picks pixels so grayscale is equivalent with 8 bit. - GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); - } - - nmap = (qbyte *)genNormalMap(scaled,scaled_width,scaled_height,bumpscale); - - qglTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA - , scaled_width, scaled_height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nmap); - - //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if (!(flags & IF_NOMIPMAP)) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap(nmap,scaled_width,scaled_height); - //GL_MipMapGray((qbyte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - - qglTexImage2D (GL_TEXTURE_2D, miplevel, GL_RGBA, scaled_width, scaled_height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, nmap); - //glTexImage2D (GL_TEXTURE_2D, miplevel, GL_RGBA, scaled_width, scaled_height, 0, GL_RGBA, - // GL_UNSIGNED_BYTE, genNormalMap(scaled,scaled_width,scaled_height,4.0f)); - } - } - - if (!(flags & IF_NOMIPMAP)) - { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - if (0 & IF_NEAREST) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max_2d); - if (0 & IF_NEAREST) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max_2d); - } - -// if (gl_texturefilteranisotropic) -// glTexParameterfv (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &gl_texureanisotropylevel); - - TRACE(("dbg: GL_UploadBump: escaped %i %i\n", width, height)); -} - - - - -#ifdef GL_USE8BITTEX -#ifdef GL_EXT_paletted_texture -void GL_Upload8_EXT (qbyte *data, int width, int height, unsigned int flags, qboolean alpha) -{ - int i, s; - qboolean noalpha; - int samples; - unsigned char *scaled; - int scaled_width, scaled_height; - - GLDraw_Init15to8(); - - s = width*height; - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (alpha) - { - noalpha = true; - for (i=0 ; imipcount; i++) { - if (data[i] == 255) - noalpha = false; - } - - if (alpha && noalpha) - alpha = false; - } - - scaled_width = width; - scaled_height = height; - GL_RoundDimensions(&scaled_width, &scaled_height, flags); - - if (scaled_width*scaled_height*4 > sizeofuploadmemorybuffer) - { - sizeofuploadmemorybuffer = scaled_width*scaled_height*4; - uploadmemorybuffer = BZ_Realloc(uploadmemorybuffer, sizeofuploadmemorybuffer); - } - scaled = uploadmemorybuffer; - - samples = 1; // alpha ? gl_alpha_format : gl_solid_format; - - if (scaled_width == width && scaled_height == height) - { - if (!mipmap) - { - glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); - goto done; - } - memcpy (scaled, data, width*height); - } - else - GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); - - glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); - if (mipmap) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - GL_MipMap8Bit ((qbyte *)scaled, scaled_width, scaled_height); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); - } - } -done: ; - - if (mipmap) - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - if (flags & IF_NEAREST) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max_2d); - if (flags & IF_NEAREST) - qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - else - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max_2d); - } -} -#endif -#endif - -/* -=============== -GL_Upload8 -=============== -*/ -int ColorIndex[16] = -{ - 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 -}; - -unsigned ColorPercent[16] = -{ - 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 -}; - -void GL_Upload8 (const char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alpha) -{ - unsigned *trans; - int i, s; - qboolean noalpha; - int p; - - if (width*height*4 > sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = width*height*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - trans = (unsigned *)uploadmemorybufferintermediate; - - s = width*height; - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (alpha && !(flags & IF_NOALPHA)) - { - noalpha = true; - for (i=0 ; iflags & IF_TEXTYPE) { - noalpha = false; - trans[i] = 0; + targface = cubeface[i]; + j = 0; } else - trans[i] = d_8to24rgbtable[p]; + { + targface = targ; + j = i; + } + switch(mips->encoding) + { + case PTI_RGBX8: + qglTexImage2D(targface, j, GL_RGB, mips->mip[i].width, mips->mip[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, mips->mip[i].data); + break; + case PTI_RGBA8: + qglTexImage2D(targface, j, GL_RGBA, mips->mip[i].width, mips->mip[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, mips->mip[i].data); + break; + case PTI_BGRX8: + qglTexImage2D(targface, j, GL_RGB, mips->mip[i].width, mips->mip[i].height, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, mips->mip[i].data); + break; + case PTI_BGRA8: + default: + qglTexImage2D(targface, j, GL_RGBA, mips->mip[i].width, mips->mip[i].height, 0, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, mips->mip[i].data); + break; + case PTI_S3RGB1: + qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data); + break; + case PTI_S3RGBA1: + qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data); + break; + case PTI_S3RGBA3: + qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data); + break; + case PTI_S3RGBA5: + qglCompressedTexImage2DARB(targface, j, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, mips->mip[i].width, mips->mip[i].height, 0, mips->mip[i].datasize, mips->mip[i].data); + break; + } + if (mips->mip[i].needfree) + Z_Free(mips->mip[i].data); } - - switch( alpha ) + } + if (!gl_config.gles) //make sure the texture is complete even if the mips are not. + { + if (targ != GL_TEXTURE_CUBE_MAP_ARB && (tex->flags & IF_MIPCAP)) { + qglTexParameteri(targ, GL_TEXTURE_BASE_LEVEL, min(mips->mipcount-1, gl_mipcap_min)); + qglTexParameteri(targ, GL_TEXTURE_MAX_LEVEL, min(mips->mipcount, gl_mipcap_max)); + } + } + + if (mips->extrafree) + Z_Free(mips->extrafree); + return true; +} + +void GL_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis) +{ + int targ; + image_t *img; + + gl_mipcap_min = mipcap[0]; + gl_mipcap_max = mipcap[1]; + + VectorCopy(filterpic, gl_filter_pic); + VectorCopy(filtermip, gl_filter_mip); + + //bound carefully, so that we get 0 if anisrophy is not supported at all (1 is fine). we can then test against 0 (which is an otherwise-invalid value) avoiding gl errors. + if (anis > gl_config.ext_texture_filter_anisotropic) + gl_anisotropy_factor = gl_config.ext_texture_filter_anisotropic; + else if (anis < 1) + gl_anisotropy_factor = 1; + else + gl_anisotropy_factor = anis; + + // change all the existing mipmap texture objects + for (img=imagelist ; img ; img=img->next) + { + switch((img->flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) + { + case 0: + targ = GL_TEXTURE_2D; + break; + case 1: + targ = GL_TEXTURE_3D; + break; default: - if (alpha && noalpha) - alpha = false; - break; - case 2: - alpha = true; - for (i=0 ; i>4]] & 0x00ffffff; - trans[i] |= ( int )ColorPercent[p&15] << 24; - //trans[i] = 0x7fff0000; - } + targ = GL_TEXTURE_CUBE_MAP_ARB; break; } - //2:H2_T7G1 - //3:H2_TRANS8_0 - //4:H2_T4A4 - } - else - { - for (i=(s&~3)-4 ; i>=0 ; i-=4) + if (img->status != TEX_LOADED) + continue; + + GL_MTBind(0, targ, img); + GL_Texturemode_Apply(targ, img->flags); + + //should we do dynamic mipcap settings? this bugs out ATI. + /* + if (!gl_config.gles && (tex->flags & IF_MIPCAP)) { - trans[i] = d_8to24rgbtable[data[i]]|0xff000000; - trans[i+1] = d_8to24rgbtable[data[i+1]]|0xff000000; - trans[i+2] = d_8to24rgbtable[data[i+2]]|0xff000000; - trans[i+3] = d_8to24rgbtable[data[i+3]]|0xff000000; - } - for (i=s&~3 ; i sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = s*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - trans = (unsigned *)uploadmemorybufferintermediate; - - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - for (i=0 ; i sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = s*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - trans = uploadmemorybufferintermediate; - - // if there are no transparent pixels, make it a 3 component - // texture even if it was specified as otherwise - if (gammaworks) - { - if (!(flags & IF_NOALPHA)) - { - noalpha = true; - for (i=0 ; i sizeofuploadmemorybufferintermediate) - { - sizeofuploadmemorybufferintermediate = s*4; - uploadmemorybufferintermediate = BZ_Realloc(uploadmemorybufferintermediate, sizeofuploadmemorybufferintermediate); - } - trans = uploadmemorybufferintermediate; - - if (s&3) - Sys_Error ("GL_Upload8: s&3"); - for (i=0 ; itexnum; - } - if (!glt) - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - -TRACE(("dbg: GL_LoadTexture: new %s\n", identifier)); - - glt->bpp = 8; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload8 ("8bit", data, width, height, flags, transtype); - return glt->texnum; -} - -texid_t GL_LoadTextureFB (const char *identifier, int width, int height, qbyte *data, unsigned int flags) -{ - int i; - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 8, width, height); - if (glt) - return glt->texnum; - } - - for (i = 0; i < width*height; i++) - if (data[i] > 255-vid.fullbright) - break; - - if (i == width*height) - return r_nulltex; //none found, don't bother uploading. - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 8; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload8FB (data, width, height, flags); - - return glt->texnum; -} - -texid_t GL_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) -{ - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 24, width, height); - if (glt) - return glt->texnum; - } - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 24; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload8Pal24 (data, palette24, width, height, flags); - - return glt->texnum; -} -texid_t GL_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) -{ - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 32, width, height); - if (glt) - return glt->texnum; - } - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 32; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload8Pal32 (data, palette32, width, height, flags); - - return glt->texnum; -} - -texid_t GL_LoadTexture32 (const char *identifier, int width, int height, void *data, unsigned int flags) -{ -// qboolean noalpha; -// int p, s; - gltexture_t *glt = NULL; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 32, width, height); - if (glt && !(flags & IF_REPLACE)) - return glt->texnum; - } - if (!glt) - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 32; - glt->flags = flags; - - switch((flags & IF_TEXTYPE) >> IF_TEXTYPESHIFT) - { - case 0: - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - break; - case 1: - GL_MTBind(0, GL_TEXTURE_3D, glt->texnum); - break; - default: - GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, glt->texnum); - break; - } - - GL_Upload32 (identifier, data, width, height, flags); - - return glt->texnum; -} - -texid_t GL_LoadTexture32_BGRA (char *identifier, int width, int height, unsigned *data, unsigned int flags) -{ -// qboolean noalpha; -// int p, s; - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 32, width, height); - if (glt) - return glt->texnum; - } - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 32; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload32_BGRA (identifier, data, width, height, flags); - - return glt->texnum; -} - -texid_t GL_LoadCompressed(const char *name) -{ - unsigned char *file; - gltexture_t *glt; - char inname[MAX_OSPATH]; - - if (!gl_config.arb_texture_compression || !gl_compress.ival) - return r_nulltex; - - - // see if the texture is already present - if (name[0]) - { - texid_t num = GL_FindTexture(name, 0); - if (TEXVALID(num)) - return num; - } - else - return r_nulltex; - - - snprintf(inname, sizeof(inname)-1, "tex/%s.tex", name); - file = COM_LoadFile(inname, 5); - if (!file) - return r_nulltex; - - glt = GL_AllocNewGLTexture(name, 0, 0, 0); - glt->bpp = 32; - glt->flags = 0; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - if (!GL_UploadCompressed(file, &glt->com.width, &glt->com.height, (unsigned int *)&glt->flags)) - return r_nulltex; - - return glt->texnum; -} - -texid_t GL_LoadTexture8Grey (char *identifier, int width, int height, unsigned char *data, unsigned int flags) -{ -// qboolean noalpha; -// int p, s; - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 8, width, height); - if (glt) - return glt->texnum; - } - - flags |= IF_NOALPHA; - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 8; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_Upload8Grey (data, width, height, flags); - - return glt->texnum; -} - -texid_t GL_LoadTexture8Bump (const char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale) -{ -// qboolean noalpha; - // int p, s; - gltexture_t *glt; - - // see if the texture is already present - if (identifier[0]) - { - glt = GL_MatchTexture(identifier, flags, 8, width, height); - if (glt) - { - TRACE(("dbg: GL_LoadTexture8Bump: duplicated %s\n", identifier)); - return glt->texnum; - } - } - - TRACE(("dbg: GL_LoadTexture8Bump: new %s\n", identifier)); - - glt = GL_AllocNewGLTexture(identifier, width, height, flags); - glt->bpp = 8; - glt->flags = flags; - - GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); - - GL_UploadBump (data, width, height, flags, bumpscale); - - return glt->texnum; -} - -/* -================ -GL_LoadPicTexture -================ -*/ -texid_t GL_LoadPicTexture (qpic_t *pic) -{ - return GL_LoadTexture ("", pic->width, pic->height, pic->data, IF_NOMIPMAP, 1); } #endif diff --git a/engine/gl/gl_font.c b/engine/gl/gl_font.c index 49af08e7..ae6cb69e 100644 --- a/engine/gl/gl_font.c +++ b/engine/gl/gl_font.c @@ -287,7 +287,7 @@ void Font_Init(void) for (i = 0; i < FONTPLANES; i++) { - TEXASSIGN(fontplanes.texnum[i], R_AllocNewTexture("***fontplane***", PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA)); + TEXASSIGN(fontplanes.texnum[i], Image_CreateTexture("***fontplane***", NULL, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA)); } fontplanes.shader = R_RegisterShader("ftefont", SUF_NONE, @@ -327,7 +327,7 @@ static void Font_Flush(void) return; if (fontplanes.planechanged) { - R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); + Image_Upload(fontplanes.texnum[fontplanes.activeplane], TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); fontplanes.planechanged = false; } @@ -350,7 +350,7 @@ static int Font_BeginChar(texid_t tex) { int fvert; - if (font_foremesh.numindexes == FONT_CHAR_BUFFER*6 || font_texture.ref != tex.ref) + if (font_foremesh.numindexes == FONT_CHAR_BUFFER*6 || font_texture != tex) { Font_Flush(); TEXASSIGNF(font_texture, tex); @@ -393,7 +393,7 @@ void Font_FlushPlane(void) if (fontplanes.planechanged) { - R_Upload(fontplanes.texnum[fontplanes.activeplane], NULL, TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); + Image_Upload(fontplanes.texnum[fontplanes.activeplane], TF_RGBA32, (void*)fontplanes.plane, NULL, PLANEWIDTH, PLANEHEIGHT, IF_UIPIC|IF_NEAREST|IF_NOPICMIP|IF_NOMIPMAP|IF_NOGAMMA); fontplanes.planechanged = false; } @@ -857,16 +857,19 @@ static texid_t Font_LoadReplacementConchars(void) { texid_t tex; //q1 replacement - tex = R_LoadReplacementTexture("gfx/conchars.lmp", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); - if (TEXVALID(tex)) + tex = R_LoadReplacementTexture("gfx/conchars.lmp", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA, NULL, 0, 0, TF_INVALID); + TEXDOWAIT(tex); + if (TEXLOADED(tex)) return tex; //q2 tex = R_LoadHiResTexture("pics/conchars.pcx", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); - if (TEXVALID(tex)) + TEXDOWAIT(tex); + if (TEXLOADED(tex)) return tex; //q3 tex = R_LoadHiResTexture("gfx/2d/bigchars.tga", NULL, IF_UIPIC|IF_NOMIPMAP|IF_NOGAMMA); - if (TEXVALID(tex)) + TEXDOWAIT(tex); + if (TEXLOADED(tex)) return tex; return r_nulltex; } @@ -1010,16 +1013,22 @@ static texid_t Font_LoadDefaultConchars(void) { texid_t tex; tex = Font_LoadReplacementConchars(); - if (TEXVALID(tex)) + if (TEXLOADED(tex)) return tex; tex = Font_LoadQuakeConchars(); - if (TEXVALID(tex)) + if (tex && tex->status == TEX_LOADING) + COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); + if (TEXLOADED(tex)) return tex; tex = Font_LoadHexen2Conchars(true); - if (TEXVALID(tex)) + if (tex && tex->status == TEX_LOADING) + COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); + if (TEXLOADED(tex)) return tex; tex = Font_LoadFallbackConchars(); - if (TEXVALID(tex)) + if (tex && tex->status == TEX_LOADING) + COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); + if (TEXLOADED(tex)) return tex; Sys_Error("Unable to load any conchars\n"); } @@ -1207,12 +1216,14 @@ struct font_s *Font_LoadFont(int vheight, const char *fontfilename) for (x = 0; x < 128; x++) img[x + y*PLANEWIDTH] = w[x + y*128]?d_8to24rgbtable[w[x + y*128]]:0; - f->singletexture = R_LoadTexture(fontfilename,PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP); + f->singletexture = R_LoadTexture("tinyfont",PLANEWIDTH,PLANEWIDTH,TF_RGBA32,img,IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP); + if (f->singletexture->status == TEX_LOADING) + COM_WorkerPartialSync(f->singletexture, &f->singletexture->status, TEX_LOADING); Z_Free(img); for (i = 0x00; i <= 0xff; i++) { - f->chars[i].advance = height; + f->chars[i].advance = (height*3)/4; f->chars[i].left = 0; f->chars[i].top = 0; f->chars[i].nextchar = 0; //these chars are not linked in @@ -1298,7 +1309,11 @@ struct font_s *Font_LoadFont(int vheight, const char *fontfilename) { //default to only map the ascii-compatible chars from the quake font. if (*fontfilename) + { f->singletexture = R_LoadHiResTexture(fontfilename, "fonts", IF_UIPIC|IF_NOMIPMAP); + if (f->singletexture->status == TEX_LOADING) + COM_WorkerPartialSync(f->singletexture, &f->singletexture->status, TEX_LOADING); + } for ( ; i < 32; i++) { @@ -1321,9 +1336,9 @@ struct font_s *Font_LoadFont(int vheight, const char *fontfilename) } defaultplane = BITMAPPLANE;/*assume the bitmap plane - don't use the fallback as people don't think to use com_parseutf8*/ - if (!TEXVALID(f->singletexture)) + if (!TEXLOADED(f->singletexture)) { - if (!TEXVALID(f->singletexture) && !TEXVALID(fontplanes.defaultfont)) + if (!TEXLOADED(fontplanes.defaultfont)) fontplanes.defaultfont = Font_LoadDefaultConchars(); if (!strcmp(fontfilename, "gfx/hexen2")) @@ -1331,7 +1346,7 @@ struct font_s *Font_LoadFont(int vheight, const char *fontfilename) f->singletexture = Font_LoadHexen2Conchars(false); defaultplane = DEFAULTPLANE; } - if (!TEXVALID(f->singletexture)) + if (!TEXLOADED(f->singletexture)) f->singletexture = fontplanes.defaultfont; } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 1d9507ae..032eb745 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -70,12 +70,13 @@ ver=2 */ #define TGS_NOLOAD 0 -#define TGS_LOAD 1 //always try to load it. -#define TGS_FORCELOAD 2 //load it even if it doesn't exist (for editing). will otherwise be unusable -#define TGS_LAZYLOAD 4 //only try to load it if its the only one we loaded this frame. -#define TGS_NODOWNLOAD 8 //don't queue it for download -#define TGS_NORENDER 16 //don't upload any textures or whatever -#define TGS_IGNOREFAILED 32 //grab the section even if its invalid, used for loading/overwriting +#define TGS_LAZYLOAD 1 //see if its available, if not, queue it. don't create too much work at once. peace man +#define TGS_TRYLOAD 2 //try and get it, but don't stress if its not available yet +#define TGS_WAITLOAD 4 //load it, wait for it if needed. +#define TGS_ANYSTATE 8 //returns the section regardless of its current state, even if its loading. +#define TGS_NODOWNLOAD 16 //don't queue it for download +#define TGS_NORENDER 32 //don't upload any textures or whatever +#define TGS_DEFAULTONFAIL 64 //if it failed to load, generate a default anyway enum { @@ -89,13 +90,12 @@ enum TSF_COMPRESSED = 1u<<31, //these flags should not be found on disk - TSF_FAILEDLOAD = 1u<<27, //placeholder to avoid excess disk access in border regions, means it still has default settings (unless edited). not saved unless its edited. TSF_NOTIFY = 1u<<28, //modified on server, waiting for clients to be told about the change. TSF_RELIGHT = 1u<<29, //height edited, needs relighting. TSF_DIRTY = 1u<<30, //its heightmap has changed, the mesh needs rebuilding TSF_EDITED = 1u<<31 //says it needs to be written if saved -#define TSF_INTERNAL (TSF_RELIGHT|TSF_DIRTY|TSF_EDITED|TSF_NOTIFY|TSF_FAILEDLOAD) +#define TSF_INTERNAL (TSF_RELIGHT|TSF_DIRTY|TSF_EDITED|TSF_NOTIFY) }; enum { @@ -166,15 +166,26 @@ struct hmwater_s qboolean simple; //no holes, one height float minheight; float maxheight; + char shadername[MAX_QPATH]; shader_t *shader; qbyte holes[8]; float heights[9*9]; }; +enum +{ + TSLS_NOTLOADED, + TSLS_LOADING1, //section is queued to the worker (and may be loaded as part of another section) + TSLS_LOADING2, //waiting for main thread to finish, worker will ignore + TSLS_LOADED, + TSLS_FAILED +}; typedef struct { link_t recycle; int sx, sy; + int loadstate; + float heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE]; unsigned char holes[8]; unsigned int flags; @@ -211,6 +222,8 @@ typedef struct typedef struct heightmap_s { char path[MAX_QPATH]; + char skyname[MAX_QPATH]; + char groundshadername[MAX_QPATH]; char defaultwatershader[MAX_QPATH]; //typically the name of the ocean or whatever. float defaultwaterheight; float defaultgroundheight; @@ -224,7 +237,7 @@ typedef struct heightmap_s mesh_t skymesh; mesh_t *askymesh; unsigned int exteriorcontents; - qboolean beinglazy; //only load one section per frame, if its the renderer doing the loading. this helps avoid nasty stalls, in theory. + unsigned int loadingsections; //number of sections currently being loaded. avoid loading extras while non-zero. size_t traceseq; size_t drawnframe; enum @@ -254,6 +267,7 @@ typedef struct heightmap_s struct hmentity_s *next; //used for freeing/allocating an entity } *entities; + void *entitylock; //lock this if you're going to read/write entities of any kind. #ifndef SERVERONLY unsigned int numusedlmsects; //to track leaks and stats @@ -279,6 +293,10 @@ static void ted_dorelight(heightmap_t *hm); #endif static qboolean Terr_Collect(heightmap_t *hm); static hmsection_t *Terr_GetSection(heightmap_t *hm, int x, int y, unsigned int flags); +static void Terr_LoadSectionWorker(void *ctx, void *data, size_t a, size_t b); +static void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b); +static void Terr_WorkerLoadedSection(void *ctx, void *data, size_t a, size_t b); +static void Terr_WorkerFailedSection(void *ctx, void *data, size_t a, size_t b); #ifndef SERVERONLY static texid_t Terr_LoadTexture(char *name) @@ -304,6 +322,7 @@ static void Terr_LoadSectionTextures(hmsection_t *s) { #ifndef SERVERONLY extern texid_t missing_texture; + struct hmwater_s *w; if (isDedicated) return; //CL_CheckOrEnqueDownloadFile(s->texname[0], NULL, 0); @@ -329,6 +348,11 @@ static void Terr_LoadSectionTextures(hmsection_t *s) s->textures.specular = *s->texname[0]?R_LoadHiResTexture(va("%s_spec", s->texname[0]), NULL, 0):r_nulltex; break; } + for (w = s->water; w; w = w->next) + { + w->shader = R_RegisterCustom (w->shadername, SUF_NONE, Shader_DefaultWaterShader, NULL); + R_BuildDefaultTexnums(NULL, w->shader); //this might get expensive. hideously so. + } #endif } @@ -387,12 +411,6 @@ static qboolean Terr_InitLightmap(hmsection_t *s, qboolean initialise) } lm += (HMLMSTRIDE)*lightmap_bytes; } - - lightmap[s->lightmap]->modified = true; - lightmap[s->lightmap]->rectchange.l = 0; - lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; } if (s->lightmap >= 0) @@ -408,7 +426,7 @@ static qboolean Terr_InitLightmap(hmsection_t *s, qboolean initialise) } #endif -char *genextendedhex(int n, char *buf) +static char *genextendedhex(int n, char *buf) { char *ret; static char nibble[16] = "0123456789abcdef"; @@ -426,7 +444,7 @@ char *genextendedhex(int n, char *buf) *buf++ = 0; return ret; } -static char *Terr_DiskBlockName(heightmap_t *hm, int sx, int sy) +static char *Terr_DiskBlockName(heightmap_t *hm, int sx, int sy, char *out, size_t outsize) { char xpart[9]; char ypart[9]; @@ -442,16 +460,18 @@ static char *Terr_DiskBlockName(heightmap_t *hm, int sx, int sy) sx |= 0xffffff00; if (sy >= CHUNKBIAS/SECTIONSPERBLOCK) sy |= 0xffffff00; - return va("maps/%s/block_%s_%s.hms", hm->path, genextendedhex(sx, xpart), genextendedhex(sy, ypart)); + Q_snprintfz(out, outsize, "maps/%s/block_%s_%s.hms", hm->path, genextendedhex(sx, xpart), genextendedhex(sy, ypart)); + return out; } -static char *Terr_DiskSectionName(heightmap_t *hm, int sx, int sy) +static char *Terr_DiskSectionName(heightmap_t *hm, int sx, int sy, char *out, size_t outsize) { sx -= CHUNKBIAS; sy -= CHUNKBIAS; //wrap cleanly sx &= CHUNKLIMIT-1; sy &= CHUNKLIMIT-1; - return va("maps/%s/sect_%03x_%03x.hms", hm->path, sx, sy); + Q_snprintfz(out, outsize, "maps/%s/sect_%03x_%03x.hms", hm->path, sx, sy); + return out; } #ifndef SERVERONLY static char *Terr_TempDiskSectionName(heightmap_t *hm, int sx, int sy) @@ -528,45 +548,55 @@ static qboolean Terr_IsSectionFName(heightmap_t *hm, char *fname, int *sx, int * return true; } -static hmsection_t *Terr_GenerateSection(heightmap_t *hm, int sx, int sy) +static hmsection_t *Terr_GenerateSection(heightmap_t *hm, int sx, int sy, qboolean scheduleload) { hmsection_t *s; hmcluster_t *cluster; int clusternum = (sx/MAXSECTIONS) + (sy/MAXSECTIONS)*MAXCLUSTERS; + +#ifdef LOADERTHREAD + Sys_LockMutex(com_resourcemutex); +#endif cluster = hm->cluster[clusternum]; if (!cluster) cluster = hm->cluster[clusternum] = Z_Malloc(sizeof(*cluster)); s = cluster->section[(sx%MAXSECTIONS) + (sy%MAXSECTIONS)*MAXSECTIONS]; if (!s) { - /*link_t *l; - l = hm->collected.next; - if (l != &hm->collected) + s = Z_Malloc(sizeof(*s)); + if (!s) { - s = (hmsection_t*)l; - RemoveLink(&s->recycle); - } - else*/ - { - s = Z_Malloc(sizeof(*s)); - if (!s) - return NULL; -#ifndef SERVERONLY - s->lightmap = -1; +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); #endif + return NULL; } +#ifndef SERVERONLY + s->lightmap = -1; +#endif + +#ifndef SERVERONLY + s->numents = 0; +#endif - InsertLinkBefore(&s->recycle, &hm->recycle); s->sx = sx; s->sy = sy; cluster->section[(sx%MAXSECTIONS) + (sy%MAXSECTIONS)*MAXSECTIONS] = s; hm->activesections++; s->hmmod = hm; + + s->flags = TSF_DIRTY; + + hm->loadingsections+=1; + + s->loadstate = TSLS_LOADING1; + + if (scheduleload) + COM_AddWork(1, Terr_LoadSectionWorker, s, hm, sx, sy); } -#ifndef SERVERONLY - s->numents = 0; +#ifdef LOADERTHREAD + Sys_UnlockMutex(com_resourcemutex); #endif - s->flags = TSF_DIRTY; return s; } @@ -578,13 +608,7 @@ static void *Terr_GenerateWater(hmsection_t *s, float maxheight) w = Z_Malloc(sizeof(*s->water)); w->next = s->water; s->water = w; -#ifndef SERVERONLY - if (!isDedicated) - { - w->shader = R_RegisterCustom (s->hmmod->defaultwatershader, SUF_NONE, Shader_DefaultWaterShader, NULL); - R_BuildDefaultTexnums(NULL, w->shader); //this might get expensive. hideously so. - } -#endif + Q_strncpyz(w->shadername, s->hmmod->defaultwatershader, sizeof(w->shadername)); w->simple = true; w->contentmask = FTECONTENTS_WATER; memset(w->holes, 0, sizeof(w->holes)); @@ -637,6 +661,7 @@ static void Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, vec3_t ep max[1] = bound(hm->firstsegy, max[1], hm->maxsegy-1); } + Sys_LockMutex(hm->entitylock); //try to find the ent if it already exists (don't do dupes) for (e = hm->entities; e; e = e->next) { @@ -681,7 +706,7 @@ static void Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, vec3_t ep { for (coord[1] = min[1]; coord[1] <= max[1]; coord[1]++) { - s = Terr_GetSection(hm, coord[0], coord[1], loadflags); + s = Terr_GetSection(hm, coord[0], coord[1], loadflags|TGS_ANYSTATE); if (!s) continue; @@ -694,8 +719,11 @@ static void Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, vec3_t ep if (i < s->numents) continue; + //FIXME: while technically correct, this causes issues with the v1 format. s->flags |= TSF_EDITED; + //FIXME: race condition - main thread might be walking the entity list. + //FIXME: even worse: the editor might be running through this routine adding/removing entities at the same time as the loader. if (s->maxents == s->numents) { s->maxents++; @@ -705,6 +733,7 @@ static void Terr_AddMesh(heightmap_t *hm, int loadflags, model_t *mod, vec3_t ep e->refs++; } } + Sys_UnlockMutex(hm->entitylock); } static void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len) @@ -712,6 +741,7 @@ static void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len) #ifndef SERVERONLY dsmesh_v1_t *dm; float *colours; + qbyte *lmstart; #endif dsection_v1_t *ds = ptr; int i; @@ -746,33 +776,10 @@ static void *Terr_ReadV1(heightmap_t *hm, hmsection_t *s, void *ptr, int len) Q_strncpyz(s->texname[2], ds->texname[2], sizeof(s->texname[2])); Q_strncpyz(s->texname[3], ds->texname[3], sizeof(s->texname[3])); - if (*s->texname[0]) - CL_CheckOrEnqueDownloadFile(s->texname[0], NULL, 0); - if (*s->texname[1]) - CL_CheckOrEnqueDownloadFile(s->texname[1], NULL, 0); - if (*s->texname[2]) - CL_CheckOrEnqueDownloadFile(s->texname[2], NULL, 0); - if (*s->texname[3]) - CL_CheckOrEnqueDownloadFile(s->texname[3], NULL, 0); - /*load in the mixture/lighting*/ - if (s->lightmap >= 0) - { - qbyte *lm; - - lm = lightmap[s->lightmap]->lightmaps; - lm += (s->lmy * HMLMSTRIDE + s->lmx) * lightmap_bytes; - for (i = 0; i < SECTTEXSIZE; i++) - { - memcpy(lm, ds->texmap + i, sizeof(ds->texmap[0])); - lm += (HMLMSTRIDE)*lightmap_bytes; - } - lightmap[s->lightmap]->modified = true; - lightmap[s->lightmap]->rectchange.l = 0; - lightmap[s->lightmap]->rectchange.t = 0; - lightmap[s->lightmap]->rectchange.w = HMLMSTRIDE; - lightmap[s->lightmap]->rectchange.h = HMLMSTRIDE; - } + lmstart = BZ_Malloc(SECTTEXSIZE*SECTTEXSIZE*4); + memcpy(lmstart, ds->texmap, SECTTEXSIZE*SECTTEXSIZE*4); + COM_AddWork(0, Terr_WorkerLoadedSectionLightmap, hm, lmstart, s->sx, s->sy); s->mesh.colors4f_array[0] = s->colours; if (flags & TSF_HASCOLOURS) @@ -1044,6 +1051,7 @@ static void Terr_SaveV2(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, i } } + Sys_LockMutex(hm->entitylock); Terr_Write_SInt(&strm, s->numents); for (i = 0; i < s->numents; i++) { @@ -1079,12 +1087,36 @@ static void Terr_SaveV2(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, i if (mf & TMF_SCALE) Terr_Write_Float(&strm, s->ents[i]->ent.scale); } + Sys_UnlockMutex(hm->entitylock); //reset it in case the buffer is getting a little full strm.pos = (strm.pos + sizeof(int)-1) & ~(sizeof(int)-1); VFS_WRITE(f, strm.buffer, strm.pos); strm.pos = 0; } +static void Terr_WorkerLoadedSectionLightmap(void *ctx, void *data, size_t a, size_t b) +{ + heightmap_t *hm = ctx; + hmsection_t *s = Terr_GetSection(hm, a, b, TGS_NOLOAD|TGS_ANYSTATE); + qbyte *inlm = data; + qbyte *outlm; + int y; + + if (s) + if (lightmap_bytes == 4 && Terr_InitLightmap(s, false)) + { + outlm = lightmap[s->lightmap]->lightmaps; + outlm += (s->lmy * HMLMSTRIDE + s->lmx) * lightmap_bytes; + for (y = 0; y < SECTTEXSIZE; y++) + { + memcpy(outlm, inlm, SECTTEXSIZE*4); + inlm += SECTTEXSIZE*4; + outlm += (HMLMSTRIDE)*lightmap_bytes; + } + } + + BZ_Free(data); +} #endif static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) { @@ -1093,7 +1125,7 @@ static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) qbyte last; int y; qboolean present; - qbyte *lm, delta; + qbyte *lmstart = NULL, *lm, delta; #endif struct terrstream_s strm = {ptr, len, 0}; float f; @@ -1128,20 +1160,15 @@ static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) for (i = 0; i < j; i++) { struct hmwater_s *w = Z_Malloc(sizeof(*w)); - char shadername[MAX_QPATH]; int fl = Terr_Read_SInt(&strm); w->next = s->water; s->water = w; w->simple = true; w->contentmask = Terr_Read_SInt(&strm); if (fl & 1) - Terr_Read_String(&strm, shadername, sizeof(shadername)); + Terr_Read_String(&strm, w->shadername, sizeof(w->shadername)); else - Q_strncpyz(shadername, hm->defaultwatershader, sizeof(shadername)); -#ifndef SERVERONLY -// CL_CheckOrEnqueDownloadFile(shadername, NULL, 0); - w->shader = R_RegisterCustom (shadername, SUF_NONE, Shader_DefaultWaterShader, NULL); -#endif + Q_strncpyz(w->shadername, hm->defaultwatershader, sizeof(w->shadername)); if (fl & 2) { for (x = 0; x < 8; x++) @@ -1197,44 +1224,42 @@ static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) else present = !!(*s->texname[2-j]); + //should be able to skip this if no shadows or textures + if (!lmstart) + lmstart = BZ_Malloc(SECTTEXSIZE*SECTTEXSIZE*4); + if (present) { //read the channel - if (s->lightmap >= 0) + last = 0; + lm = lmstart; + for (y = 0; y < SECTTEXSIZE; y++) { - last = 0; - lm = lightmap[s->lightmap]->lightmaps; - lm += (s->lmy * HMLMSTRIDE + s->lmx) * lightmap_bytes; - for (y = 0; y < SECTTEXSIZE; y++) + for (x = 0; x < SECTTEXSIZE; x++) { - for (x = 0; x < SECTTEXSIZE; x++) - { - delta = Terr_Read_Byte(&strm); - last = (last+delta)&0xff; - lm[x*4+j] = last; - } - lm += (HMLMSTRIDE)*lightmap_bytes; + delta = Terr_Read_Byte(&strm); + last = (last+delta)&0xff; + lm[x*4+j] = last; } - } - else - { - strm.pos += SECTTEXSIZE*SECTTEXSIZE; + lm += x*4; } } - else if (s->lightmap >= 0) + else { last = ((j==3)?255:0); - lm = lightmap[s->lightmap]->lightmaps; - lm += (s->lmy * HMLMSTRIDE + s->lmx) * lightmap_bytes; + lm = lmstart; for (y = 0; y < SECTTEXSIZE; y++) { for (x = 0; x < SECTTEXSIZE; x++) lm[x*4+j] = last; - lm += (HMLMSTRIDE)*lightmap_bytes; + lm += x*4; } } } + if (lmstart) + COM_AddWork(0, Terr_WorkerLoadedSectionLightmap, hm, lmstart, s->sx, s->sy); + /*load any static ents*/ j = Terr_Read_SInt(&strm); for (i = 0; i < j; i++) @@ -1272,11 +1297,28 @@ static void *Terr_ReadV2(heightmap_t *hm, hmsection_t *s, void *ptr, int len) //#include "gl_adt.inc" +static void Terr_ClearSection(hmsection_t *s) +{ + struct hmwater_s *w; + int i; + Sys_LockMutex(s->hmmod->entitylock); + for (i = 0; i < s->numents; i++) + s->ents[i]->refs-=1; + s->numents = 0; + Sys_UnlockMutex(s->hmmod->entitylock); + + while(s->water) + { + w = s->water; + s->water = w->next; + Z_Free(w); + } +} + static void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s) { int i; - s->flags |= TSF_FAILEDLOAD; memset(s->holes, 0, sizeof(s->holes)); #ifndef SERVERONLY @@ -1410,42 +1452,49 @@ static void Terr_GenerateDefault(heightmap_t *hm, hmsection_t *s) } #endif } -static hmsection_t *Terr_ReadSection(heightmap_t *hm, int ver, int sx, int sy, void *filebase, unsigned int filelen) + +static void Terr_WorkerLoadedSection(void *ctx, void *data, size_t a, size_t b) { + hmsection_t *s = ctx; + Terr_LoadSectionTextures(s); + COM_AssertMainThread("foo"); + InsertLinkBefore(&s->recycle, &s->hmmod->recycle); + s->hmmod->loadingsections-=1; + s->flags &= ~TSF_EDITED; + s->loadstate = TSLS_LOADED; +} +static void Terr_WorkerFailedSection(void *ctx, void *data, size_t a, size_t b) +{ + hmsection_t *s = ctx; + Terr_WorkerLoadedSection(ctx, data, a, b); + s->flags &= ~TSF_EDITED; + s->loadstate = TSLS_FAILED; +} + +static hmsection_t *Terr_ReadSection(heightmap_t *hm, hmsection_t *s, int ver, void *filebase, unsigned int filelen) +{ + qboolean failed = false; void *ptr = filebase; - hmsection_t *s; - hmcluster_t *cluster; - int clusternum = (sx/MAXSECTIONS) + (sy/MAXSECTIONS)*MAXCLUSTERS; - cluster = hm->cluster[clusternum]; - if (!cluster) - cluster = hm->cluster[clusternum] = Z_Malloc(sizeof(*cluster)); - s = cluster->section[(sx%MAXSECTIONS) + (sy%MAXSECTIONS)*MAXSECTIONS]; - - if (!s) - { - s = Terr_GenerateSection(hm, sx, sy); - if (!s) - return NULL; - } - -#ifndef SERVERONLY - Terr_InitLightmap(s, false); -#endif - - s->flags &= ~TSF_FAILEDLOAD; if (ptr && ver == 1) Terr_ReadV1(hm, s, ptr, filelen); else if (ptr && ver == 2) Terr_ReadV2(hm, s, ptr, filelen); else { - s->flags |= TSF_FAILEDLOAD; // s->flags |= TSF_RELIGHT; Terr_GenerateDefault(hm, s); + + failed = true; } - Terr_LoadSectionTextures(s); + s->flags &= ~TSF_EDITED; //its just been loaded (and was probably edited by the loader), make sure it doesn't get saved or whatever + + s->loadstate = TSLS_LOADING2; + if (failed) + COM_AddWork(0, Terr_WorkerFailedSection, s, NULL, s->sx, s->sy); + else + COM_AddWork(0, Terr_WorkerLoadedSection, s, NULL, s->sx, s->sy); return s; } @@ -1453,6 +1502,7 @@ static hmsection_t *Terr_ReadSection(heightmap_t *hm, int ver, int sx, int sy, v #ifndef SERVERONLY qboolean Terr_DownloadedSection(char *fname) { +/* qofs_t len; dsection_t *fileptr; int x, y; @@ -1478,31 +1528,50 @@ qboolean Terr_DownloadedSection(char *fname) FS_FreeFile(fileptr); return true; } - +*/ return false; } #endif static void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, unsigned int flags) { - void *diskimage; - qofs_t len; #ifndef SERVERONLY //when using networked terrain, the client will never load a section from disk, but will only load it from the server //one section at a time. if (mod_terrain_networked.ival && !sv.state) { + char fname[MAX_QPATH]; if (flags & TGS_NODOWNLOAD) return; //try to download it now... if (!cl.downloadlist) - CL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, sx, sy), Terr_TempDiskSectionName(hm, sx, sy), DLLF_OVERWRITE|DLLF_TEMPORARY); + CL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, sx, sy, fname, sizeof(fname)), Terr_TempDiskSectionName(hm, sx, sy), DLLF_OVERWRITE|DLLF_TEMPORARY); return; } #endif + if (!s) + { + Terr_GenerateSection(hm, sx, sy, true); + } +} +static void Terr_LoadSectionWorker(void *ctx, void *data, size_t a, size_t b) +{ + heightmap_t *hm = data; + hmsection_t *s = ctx; + int sx = a; + int sy = b; + int flags = 0; + void *diskimage; + qofs_t len; + char fname[MAX_QPATH]; + + //already processed, or not otherwise valid + if (s->loadstate != TSLS_LOADING1) + return; + #if SECTIONSPERBLOCK > 1 - len = FS_LoadFile(Terr_DiskBlockName(hm, sx, sy), (void**)&diskimage); + len = FS_LoadFile(Terr_DiskBlockName(hm, sx, sy, fname, sizeof(fname)), (void**)&diskimage); if (!qofs_Error(len)) { int offset; @@ -1512,7 +1581,7 @@ static void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, un if (block->magic != SECTION_MAGIC || !(block->ver & 0x80000000)) { //give it a dummy so we don't constantly hit the disk - Terr_ReadSection(hm, 0, sx, sy, NULL, 0); + Terr_ReadSection(hm, s, 0, NULL, 0); } else { @@ -1524,18 +1593,14 @@ static void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, un for (x = 0; x < SECTIONSPERBLOCK; x++) { //noload avoids recursion. - s = Terr_GetSection(hm, sx+x, sy+y, TGS_NOLOAD|TGS_NODOWNLOAD|TGS_IGNOREFAILED); - if (!s || s->flags & TSF_FAILEDLOAD -#ifndef SERVERONLY - || s->lightmap < 0 -#endif - ) + s = Terr_GenerateSection(hm, sx+x, sy+y, false); + if (s->loadstate == TSLS_LOADING1) { offset = block->offset[x + y*SECTIONSPERBLOCK]; if (!offset) - Terr_ReadSection(hm, ver, sx+x, sy+y, NULL, 0); //no data in the file for this section + Terr_ReadSection(hm, s, ver, NULL, 0); //no data in the file for this section else - Terr_ReadSection(hm, ver, sx+x, sy+y, (char*)diskimage + offset, len - offset); + Terr_ReadSection(hm, s, ver, (char*)diskimage + offset, len - offset); } } } @@ -1545,13 +1610,13 @@ static void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, un #endif //legacy one-section-per-file format. - len = FS_LoadFile(Terr_DiskSectionName(hm, sx, sy), (void**)&diskimage); + len = FS_LoadFile(Terr_DiskSectionName(hm, sx, sy, fname, sizeof(fname)), (void**)&diskimage); if (!qofs_Error(len)) { dsection_t *h = diskimage; if (len >= sizeof(*h) && h->magic == SECTION_MAGIC) { - Terr_ReadSection(hm, h->ver, sx, sy, h+1, len-sizeof(*h)); + Terr_ReadSection(hm, s, h->ver, h+1, len-sizeof(*h)); FS_FreeFile(diskimage); return; } @@ -1565,13 +1630,7 @@ static void Terr_LoadSection(heightmap_t *hm, hmsection_t *s, int sx, int sy, un #endif //generate a dummy one - Terr_ReadSection(hm, 0, sx, sy, NULL, 0); - - //download it if it couldn't be loaded. -#ifndef SERVERONLY - if (!cl.downloadlist && !(flags & TGS_NODOWNLOAD)) - CL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, sx, sy), NULL, 0); -#endif + Terr_ReadSection(hm, s, 0, NULL, 0); } #ifndef SERVERONLY @@ -1650,6 +1709,7 @@ static void Terr_SaveV1(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, i ds.waterheight = w?w->heights[4*8+4]:s->minh; ds.minh = s->minh; ds.maxh = s->maxh; + Sys_LockMutex(hm->entitylock); ds.ents_num = s->numents; VFS_WRITE(f, &ds, sizeof(ds)); @@ -1676,6 +1736,7 @@ static void Terr_SaveV1(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, i if (pad) VFS_WRITE(f, ¬hing, pad); } + Sys_UnlockMutex(hm->entitylock); } static void Terr_Save(heightmap_t *hm, hmsection_t *s, vfsfile_t *f, int sx, int sy, int ver) @@ -1694,7 +1755,7 @@ static qboolean Terr_SaveSection(heightmap_t *hm, hmsection_t *s, int sx, int sy return true; #else vfsfile_t *f; - char *fname; + char fname[MAX_QPATH]; int x, y; int writever = mod_terrain_savever.ival; if (!writever) @@ -1715,13 +1776,16 @@ static qboolean Terr_SaveSection(heightmap_t *hm, hmsection_t *s, int sx, int sy { for (x = 0; x < SECTIONSPERBLOCK; x++) { - s = Terr_GetSection(hm, sx+x, sy+y, TGS_LOAD); + s = Terr_GetSection(hm, sx+x, sy+y, TGS_WAITLOAD|TGS_NODOWNLOAD); if (s) s->flags |= TSF_EDITED; //stop them from getting reused for something else. } } - fname = Terr_DiskBlockName(hm, sx, sy); + //make sure all lightmap info was loaded. + COM_WorkerFullSync(); + + Terr_DiskBlockName(hm, sx, sy, fname, sizeof(fname)); FS_CreatePath(fname, FS_GAMEONLY); f = FS_OpenVFS(fname, "wb", FS_GAMEONLY); if (!f) @@ -1738,8 +1802,8 @@ static qboolean Terr_SaveSection(heightmap_t *hm, hmsection_t *s, int sx, int sy { for (x = 0; x < SECTIONSPERBLOCK; x++) { - s = Terr_GetSection(hm, sx+x, sy+y, TGS_LOAD); - if (s && (s->flags & (TSF_EDITED|TSF_FAILEDLOAD)) != TSF_FAILEDLOAD) + s = Terr_GetSection(hm, sx+x, sy+y, TGS_WAITLOAD|TGS_NODOWNLOAD); + if (s && s->loadstate == TSLS_LOADED) { dbh.offset[y*SECTIONSPERBLOCK + x] = VFS_TELL(f); Terr_Save(hm, s, f, sx+x, sy+y, writever); @@ -1758,10 +1822,13 @@ static qboolean Terr_SaveSection(heightmap_t *hm, hmsection_t *s, int sx, int sy #endif { dsection_t dsh; - fname = Terr_DiskSectionName(hm, sx, sy); + Terr_DiskSectionName(hm, sx, sy, fname, sizeof(fname)); - if (s && (s->flags & (TSF_EDITED|TSF_FAILEDLOAD)) != TSF_FAILEDLOAD) - return FS_Remove(fname, FS_GAMEONLY); //delete the file if the section got reverted to default, and wasn't later modified. +// if (s && (s->flags & (TSF_EDITED|TSF_FAILEDLOAD)) != TSF_FAILEDLOAD) +// return FS_Remove(fname, FS_GAMEONLY); //delete the file if the section got reverted to default, and wasn't later modified. + + //make sure all lightmap info was loaded. + COM_WorkerFullSync(); FS_CreatePath(fname, FS_GAMEONLY); f = FS_OpenVFS(fname, "wb", FS_GAMEONLY); @@ -1793,51 +1860,56 @@ static hmsection_t *Terr_GetSection(heightmap_t *hm, int x, int y, unsigned int int sy = y & (MAXSECTIONS-1); cluster = hm->cluster[cx + cy*MAXCLUSTERS]; if (!cluster) - { - if (flags & (TGS_LOAD|TGS_FORCELOAD|TGS_LAZYLOAD)) - { - cluster = Z_Malloc(sizeof(*cluster)); - if (!cluster) - return NULL; - hm->cluster[cx + cy*MAXCLUSTERS] = cluster; - } - else - return NULL; - } - section = cluster->section[sx + sy*MAXSECTIONS]; + section = NULL; + else + section = cluster->section[sx + sy*MAXSECTIONS]; if (!section) { - if (flags & (TGS_LOAD|TGS_FORCELOAD|TGS_LAZYLOAD)) + if (flags & (TGS_LAZYLOAD|TGS_TRYLOAD|TGS_WAITLOAD)) { - if ((flags & TGS_LAZYLOAD) && hm->beinglazy) + if ((flags & TGS_LAZYLOAD) && hm->loadingsections) return NULL; - hm->beinglazy = true; -// while (hm->activesections > TERRAINACTIVESECTIONS) -// Terr_Collect(hm); - Terr_LoadSection(hm, section, x, y, flags); - section = cluster->section[sx + sy*MAXSECTIONS]; + section = Terr_GenerateSection(hm, x, y, true); } } #ifndef SERVERONLY //when using networked terrain, the client will never load a section from disk, but only loading it from the server + //this means we need to send a new request to download the section if it was flagged as modified. if (!(flags & TGS_NODOWNLOAD)) if (section && (section->flags & TSF_NOTIFY) && mod_terrain_networked.ival && !sv.state) { //try to download it now... if (!cl.downloadlist) { - CL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, x, y), Terr_TempDiskSectionName(hm, x, y), DLLF_OVERWRITE|DLLF_TEMPORARY); + char fname[MAX_QPATH]; + CL_CheckOrEnqueDownloadFile(Terr_DiskSectionName(hm, x, y, fname, sizeof(fname)), Terr_TempDiskSectionName(hm, x, y), DLLF_OVERWRITE|DLLF_TEMPORARY); section->flags &= ~TSF_NOTIFY; } } #endif - if (section && (section->flags & TSF_FAILEDLOAD)) + if (section) { - if (flags & TGS_FORCELOAD) - section->flags &= ~TSF_FAILEDLOAD; - else + //wait for it to load if we're meant to be doing that. + if (section->loadstate == TSLS_LOADING1 && (flags & TGS_WAITLOAD)) + { + //process the load + COM_WorkerPartialSync(section, §ion->loadstate, TSLS_LOADING1); + } + if (section->loadstate == TSLS_LOADING2 && (flags & TGS_WAITLOAD)) + COM_MainThreadFlush(); //make sure any associated lightmaps also got read+handled + + //if it failed, generate a default (for editing) + if (section->loadstate == TSLS_FAILED && (flags & TGS_DEFAULTONFAIL)) + { + section->flags = (section->flags & ~TSF_EDITED); + Terr_ClearSection(section); + Terr_GenerateDefault(hm, section); + } + + + if ((section->loadstate != TSLS_LOADED) && !(flags & TGS_ANYSTATE)) section = NULL; } @@ -1847,8 +1919,8 @@ static hmsection_t *Terr_GetSection(heightmap_t *hm, int x, int y, unsigned int /*save all currently loaded sections*/ int Heightmap_Save(heightmap_t *hm) { - hmsection_t *s, *os; - int x, y, sx, sy; + hmsection_t *s; + int x, y; int sectionssaved = 0; for (x = hm->firstsegx; x < hm->maxsegx; x++) { @@ -1859,17 +1931,17 @@ int Heightmap_Save(heightmap_t *hm) continue; if (s->flags & TSF_EDITED) { - //make sure all the parts are loaded before trying to write them, so we don't try reading partial files, which would be bad, mmkay? +/* //make sure all the parts are loaded before trying to write them, so we don't try reading partial files, which would be bad, mmkay? for (sy = y&~(SECTIONSPERBLOCK-1); sy < y+SECTIONSPERBLOCK && sy < hm->maxsegy; sy++) { for (sx = x&~(SECTIONSPERBLOCK-1); sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++) { - os = Terr_GetSection(hm, sx, sy, TGS_LOAD|TGS_NODOWNLOAD|TGS_NORENDER); + os = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER); if (os) os->flags |= TSF_EDITED; } } - +*/ if (Terr_SaveSection(hm, s, x, y, true)) { @@ -1890,6 +1962,7 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc) heightmap_t *hm; hmsection_t *s; int x, y; + char fname[MAX_QPATH]; //reject if its not in maps if (strncmp(name, "maps/", 5)) @@ -1902,7 +1975,7 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc) return false; //verify that its valid - if (strcmp(name, Terr_DiskSectionName(hm, x, y))) + if (strcmp(name, Terr_DiskSectionName(hm, x, y, fname, sizeof(fname)))) return false; s = Terr_GetSection(hm, x, y, TGS_NOLOAD); @@ -1916,24 +1989,13 @@ qboolean Terrain_LocateSection(char *name, flocation_t *loc) } #endif -void Terr_ClearSection(hmsection_t *s) -{ - struct hmwater_s *w; - int i; - for (i = 0; i < s->numents; i++) - s->ents[i]->refs-=1; - s->numents = 0; - - while(s->water) - { - w = s->water; - s->water = w->next; - Z_Free(w); - } -} - void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusable) { + if (s && s->loadstate == TSLS_LOADING1) + COM_WorkerPartialSync(s, &s->loadstate, TSLS_LOADING1); + if (s && s->loadstate == TSLS_LOADING2) + COM_MainThreadFlush(); //make sure any associated lightmaps also got read+handled + RemoveLink(&s->recycle); Terr_ClearSection(s); @@ -1979,6 +2041,9 @@ void Terr_DestroySection(heightmap_t *hm, hmsection_t *s, qboolean lightmapreusa #ifndef SERVERONLY //dedicated servers do not support editing. no lightmap info causes problems. + +//when a terrain section has the notify flag set on the server, the server needs to go through and set out notifications to replicate it to the various clients +//so the clients know to re-download the section. static void Terr_DoEditNotify(heightmap_t *hm) { int i; @@ -2029,7 +2094,7 @@ static qboolean Terr_Collect(heightmap_t *hm) for (ln = &hm->recycle; ln->next != &hm->recycle; ) { s = (hmsection_t*)ln->next; - if (s->flags & TSF_EDITED) + if ((s->flags & TSF_EDITED) || s->loadstate == TSLS_LOADING1 || s->loadstate == TSLS_LOADING2) ln = &s->recycle; else { @@ -2586,6 +2651,8 @@ static void Terr_RebuildMesh(model_t *model, hmsection_t *s, int x, int y) return; } + if (s->maxh_cull < s->maxh) + s->maxh_cull = s->maxh; { vec3_t mins, maxs; mins[0] = (x-CHUNKBIAS) * hm->sectionsize; @@ -2721,56 +2788,63 @@ void Terr_DrawInBounds(struct tdibctx *ctx, int x, int y, int w, int h) if (ctx->pvs && !ctx->wmodel->funcs.EdictInFatPVS(ctx->wmodel, &s->pvscache, ctx->pvs)) return; //this section isn't in any visible bsp leafs - //chuck out any batches for models in this section - for (i = 0; i < s->numents; i++) + if (s->numents) { - vec3_t dist; - float a; - model_t *model; - //skip the entity if its already been added to some batch this frame. - if (s->ents[i]->drawnframe == hm->drawnframe) - continue; - s->ents[i]->drawnframe = hm->drawnframe; - - model = s->ents[i]->ent.model; - if (!model) - continue; - - if (model->needload == 1) + Sys_LockMutex(hm->entitylock); + //chuck out any batches for models in this section + for (i = 0; i < s->numents; i++) { - if (hm->beinglazy) + vec3_t dist; + float a, dmin, dmax; + model_t *model; + //skip the entity if its already been added to some batch this frame. + if (s->ents[i]->drawnframe == hm->drawnframe) continue; - hm->beinglazy = true; - Mod_LoadModel(model, MLV_WARN); - } - if (model->needload) - continue; + s->ents[i]->drawnframe = hm->drawnframe; - VectorSubtract(s->ents[i]->ent.origin, r_origin, dist); - a = VectorLength(dist); - a = 1024 - a + model->radius*16; - a /= model->radius; - if (a < 0) - continue; - if (a >= 1) - { - a = 1; - s->ents[i]->ent.flags &= ~RF_TRANSLUCENT; - } - else - s->ents[i]->ent.flags |= RF_TRANSLUCENT; - s->ents[i]->ent.shaderRGBAf[3] = a; - switch(model->type) - { - case mod_alias: - R_GAlias_GenerateBatches(&s->ents[i]->ent, ctx->batches); - break; - case mod_brush: - Surf_GenBrushBatches(ctx->batches, &s->ents[i]->ent); - break; - default: //FIXME: no sprites! oh noes! - break; + model = s->ents[i]->ent.model; + if (!model) + continue; + + if (model->loadstate == MLS_NOTLOADED) + { + // if (hm->beinglazy) + // continue; + // hm->beinglazy = true; + Mod_LoadModel(model, MLV_WARN); + } + if (model->loadstate != MLS_LOADED) + continue; + + VectorSubtract(s->ents[i]->ent.origin, r_origin, dist); + a = VectorLength(dist); + dmin = 1024 + model->radius*160; + dmax = dmin + 1024; + a = (a - dmin) / (dmax - dmin); + a = 1-a; + if (a < 0) + continue; + if (a >= 1) + { + a = 1; + s->ents[i]->ent.flags &= ~RF_TRANSLUCENT; + } + else + s->ents[i]->ent.flags |= RF_TRANSLUCENT; + s->ents[i]->ent.shaderRGBAf[3] = a; + switch(model->type) + { + case mod_alias: + R_GAlias_GenerateBatches(&s->ents[i]->ent, ctx->batches); + break; + case mod_brush: + Surf_GenBrushBatches(ctx->batches, &s->ents[i]->ent); + break; + default: //FIXME: no sprites! oh noes! + break; + } } + Sys_UnlockMutex(hm->entitylock); } for (wa = s->water; wa; wa = wa->next) @@ -2862,11 +2936,14 @@ void Terr_DrawTerrainModel (batch_t **batches, entity_t *e) // if (!Terr_Collect(hm)) // break; while (hm->activesections > TERRAINACTIVESECTIONS) + { if (!Terr_Collect(hm)) break; + break; + } } - hm->beinglazy = false; +// hm->beinglazy = false; if (hm->relight) ted_dorelight(hm); @@ -2965,7 +3042,7 @@ void Terrain_ClipDecal(fragmentdecal_t *dec, float *center, float radius, model_ { for (x = min[0]; x < max[0]; x++) { - s = Terr_GetSection(hm, x, y, TGS_LOAD); + s = Terr_GetSection(hm, x, y, TGS_WAITLOAD); if (!s) continue; @@ -3051,7 +3128,7 @@ unsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, vec3_t return hm->exteriorcontents; if (sx >= hm->maxsegx || sy >= hm->maxsegy) return hm->exteriorcontents; - s = Terr_GetSection(hm, sx, sy, TGS_LOAD); + s = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD); if (!s) { return FTECONTENTS_SOLID; @@ -3148,7 +3225,7 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) return; if (sx >= hm->maxsegx || sy >= hm->maxsegy) return; - s = Terr_GetSection(hm, sx, sy, TGS_LOAD); + s = Terr_GetSection(hm, sx, sy, TGS_TRYLOAD); if (!s) return; @@ -3305,7 +3382,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) else if (sy < tr->hm->firstsegy || sy >= tr->hm->maxsegy) s = NULL; else - s = Terr_GetSection(tr->hm, sx, sy, TGS_LOAD); + s = Terr_GetSection(tr->hm, sx, sy, TGS_TRYLOAD|TGS_WAITLOAD); if (!s) { @@ -3319,9 +3396,10 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) return; } - if (s->traceseq != tr->hm->traceseq) + if (s->traceseq != tr->hm->traceseq && s->numents) { s->traceseq = tr->hm->traceseq; + Sys_LockMutex(tr->hm->entitylock); for (i = 0; i < s->numents; i++) { vec3_t start_l, end_l; @@ -3333,7 +3411,8 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) s->ents[i]->traceseq = tr->hm->traceseq; model = s->ents[i]->ent.model; frame = s->ents[i]->ent.framestate.g[FS_REG].frame[0]; - if (!model || model->needload || !model->funcs.NativeTrace) + //FIXME: IGNORE the entity if it isn't loaded yet? surely that's bad? + if (!model || model->loadstate != MLS_LOADED || !model->funcs.NativeTrace) continue; //figure out where on the submodel the trace is. VectorSubtract (tr->start, s->ents[i]->ent.origin, start_l); @@ -3369,6 +3448,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) tr->plane[2] = etr.plane.normal[2]; } } + Sys_UnlockMutex(tr->hm->entitylock); } sx = tx - CHUNKBIAS*(SECTHEIGHTSIZE-1); @@ -3796,7 +3876,7 @@ static void ted_dorelight(heightmap_t *hm) // lm[0] = norm[0]*127 + 128; // lm[1] = norm[1]*127 + 128; // lm[2] = norm[2]*127 + 128; - lm[3] = d*255; + lm[3] = 127 + d*128; } lightmap[s->lightmap]->modified = true; @@ -4070,6 +4150,7 @@ enum tid_exponential, tid_square_linear, tid_square_exponential, + tid_flat }; //calls 'func' for each tile upon the terrain. the 'tile' can be either height or texel static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float radius, float strength, int steps, void(*func)(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength), void *ctx) @@ -4105,7 +4186,7 @@ static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float ra { for (sx = min[0]; sx < max[0]; sx++) { - s = Terr_GetSection(hm, sx, sy, TGS_FORCELOAD); + s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_DEFAULTONFAIL); if (!s) continue; @@ -4143,6 +4224,12 @@ static void ted_itterate(heightmap_t *hm, int distribution, float *pos, float ra if (w > 0) func(ctx, s, tx+ty*steps, wx, wy, w*strength/(radius)); break; + case tid_flat: + w = max(fabs(xd), fabs(yd)); + w = radius - w; + if (w > 0) + func(ctx, s, tx+ty*steps, wx, wy, strength); + break; } } } @@ -4185,7 +4272,7 @@ void ted_texkill(hmsection_t *s, const char *killtex) else { if (to != 3) - lm[to] += lm[t]; + lm[to] = (lm[to]+lm[t])&0xff; lm[t] = 0; } } @@ -4330,7 +4417,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g x = bound(hm->firstsegx, x, hm->maxsegx-1); y = bound(hm->firstsegy, y, hm->maxsegy-1); - s = Terr_GetSection(hm, x, y, TGS_LOAD); + s = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL); if (!s) return; x = bound(0, quant, 3); @@ -4345,7 +4432,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g x = bound(hm->firstsegx, x, hm->maxsegx-1); y = bound(hm->firstsegy, y, hm->maxsegy-1); - ted_texkill(Terr_GetSection(hm, x, y, TGS_FORCELOAD), PR_GetStringOfs(prinst, OFS_PARM4)); + ted_texkill(Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL), PR_GetStringOfs(prinst, OFS_PARM4)); } break; case ter_reset: @@ -4356,10 +4443,10 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g y = pos[1] / hm->sectionsize; x = bound(hm->firstsegx, x, hm->maxsegx-1); y = bound(hm->firstsegy, y, hm->maxsegy-1); - s = Terr_GetSection(hm, x, y, TGS_LOAD); + s = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL); if (s) { - s->flags = (s->flags & ~TSF_EDITED) | TSF_FAILEDLOAD; + s->flags = (s->flags & ~TSF_EDITED); Terr_ClearSection(s); Terr_GenerateDefault(hm, s); } @@ -4371,7 +4458,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g wedict_t *ed = G_WEDICT(prinst, OFS_PARM1); //FIXME: modeltype pitch inversion AngleVectorsFLU(ed->v->angles, axis[0], axis[1], axis[2]); - Terr_AddMesh(hm, TGS_FORCELOAD, vmw->Get_CModel(vmw, ed->v->modelindex), ed->v->origin, axis, ed->xv->scale); + Terr_AddMesh(hm, TGS_WAITLOAD|TGS_DEFAULTONFAIL, vmw->Get_CModel(vmw, ed->v->modelindex), ed->v->origin, axis, ed->xv->scale); } break; case ter_mesh_kill: @@ -4386,10 +4473,12 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g x = bound(hm->firstsegx, x, hm->maxsegx-1); y = bound(hm->firstsegy, y, hm->maxsegy-1); - s = Terr_GetSection(hm, x, y, TGS_FORCELOAD); + s = Terr_GetSection(hm, x, y, TGS_WAITLOAD|TGS_DEFAULTONFAIL); if (!s) return; + Sys_LockMutex(hm->entitylock); + //FIXME: this doesn't work properly. if (s->numents) { for (i = 0; i < s->numents; i++) @@ -4397,11 +4486,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g s->flags |= TSF_EDITED; s->numents = 0; } - - /*for (i = 0; i < s->numents; i++) - { - - }*/ + Sys_UnlockMutex(hm->entitylock); } break; } @@ -4416,53 +4501,52 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g void Terr_ParseEntityLump(char *data, heightmap_t *heightmap) { char key[128]; + char value[2048]; heightmap->sectionsize = 1024; heightmap->mode = HMM_TERRAIN; heightmap->defaultgroundheight = 0; heightmap->defaultwaterheight = 0; - Q_strncpyz(heightmap->defaultwatershader, va("water/%s", heightmap->path), sizeof(heightmap->defaultwatershader)); + Q_snprintfz(heightmap->defaultwatershader, sizeof(heightmap->defaultwatershader), "water/%s", heightmap->path); Q_strncpyz(heightmap->defaultgroundtexture, "", sizeof(heightmap->defaultgroundtexture)); if (data) - if ((data=COM_Parse(data))) //read the map info. - if (com_token[0] == '{') + if ((data=COM_ParseOut(data, key, sizeof(key)))) //read the map info. + if (key[0] == '{') while (1) { - if (!(data=COM_Parse(data))) + if (!(data=COM_ParseOut(data, key, sizeof(key)))) break; // error - if (com_token[0] == '}') + if (key[0] == '}') break; // end of worldspawn - if (com_token[0] == '_') - strcpy(key, com_token + 1); //_ vars are for comments/utility stuff that arn't visible to progs and for compat. We want to support these stealth things. - else - strcpy(key, com_token); - if (!((data=COM_Parse(data)))) + if (key[0] == '_') + memmove(key, key+1, strlen(key)); //_ vars are for comments/utility stuff that arn't visible to progs and for compat. We want to support these stealth things. + if (!((data=COM_ParseOut(data, value, sizeof(value))))) break; // error if (!strcmp("segmentsize", key)) - heightmap->sectionsize = atof(com_token); + heightmap->sectionsize = atof(value); else if (!strcmp("minxsegment", key)) - heightmap->firstsegx = atoi(com_token); + heightmap->firstsegx = atoi(value); else if (!strcmp("minysegment", key)) - heightmap->firstsegy = atoi(com_token); + heightmap->firstsegy = atoi(value); else if (!strcmp("maxxsegment", key)) - heightmap->maxsegx = atoi(com_token); + heightmap->maxsegx = atoi(value); else if (!strcmp("maxysegment", key)) - heightmap->maxsegy = atoi(com_token); + heightmap->maxsegy = atoi(value); else if (!strcmp("defaultwaterheight", key)) - heightmap->defaultwaterheight = atof(com_token); + heightmap->defaultwaterheight = atof(value); else if (!strcmp("defaultgroundheight", key)) - heightmap->defaultgroundheight = atof(com_token); + heightmap->defaultgroundheight = atof(value); else if (!strcmp("defaultgroundtexture", key)) - Q_strncpyz(heightmap->defaultgroundtexture, com_token, sizeof(heightmap->defaultgroundtexture)); + Q_strncpyz(heightmap->defaultgroundtexture, value, sizeof(heightmap->defaultgroundtexture)); else if (!strcmp("defaultwatertexture", key)) - Q_strncpyz(heightmap->defaultwatershader, com_token, sizeof(heightmap->defaultwatershader)); + Q_strncpyz(heightmap->defaultwatershader, value, sizeof(heightmap->defaultwatershader)); else if (!strcmp("tiles", key)) { char *d; heightmap->mode = HMM_BLOCKS; - d = com_token; + d = value; d = COM_ParseOut(d, key, sizeof(key)); heightmap->tilepixcount[0] = atoi(key); d = COM_ParseOut(d, key, sizeof(key)); @@ -4489,13 +4573,14 @@ void Terr_ParseEntityLump(char *data, heightmap_t *heightmap) heightmap->maxsegy = CHUNKLIMIT; } -void Terr_FinishTerrain(heightmap_t *hm, char *shadername, char *skyname) +void Terr_FinishTerrain(model_t *mod) { + heightmap_t *hm = mod->terrain; #ifndef SERVERONLY if (qrenderer != QR_NONE) { - if (skyname) - hm->skyshader = R_RegisterCustom(va("skybox_%s", skyname), SUF_NONE, Shader_DefaultSkybox, NULL); + if (*hm->skyname) + hm->skyshader = R_RegisterCustom(va("skybox_%s", hm->skyname), SUF_NONE, Shader_DefaultSkybox, NULL); else hm->skyshader = NULL; @@ -4511,7 +4596,7 @@ void Terr_FinishTerrain(heightmap_t *hm, char *shadername, char *skyname) ); break; case HMM_TERRAIN: - hm->shader = R_RegisterShader(shadername, SUF_LIGHTMAP, + hm->shader = R_RegisterShader(hm->groundshadername, SUF_LIGHTMAP, "{\n" "bemode rtlight\n" "{\n" @@ -4587,16 +4672,11 @@ qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize { heightmap_t *hm; - char shadername[MAX_QPATH]; - char skyname[MAX_QPATH]; + char token[MAX_QPATH]; int sectsize = 0; - COM_FileBase(mod->name, shadername, sizeof(shadername)); - strcpy(shadername, "terrainshader"); - strcpy(skyname, "sky1"); - - buffer = COM_Parse(buffer); - if (strcmp(com_token, "terrain")) + buffer = COM_ParseOut(buffer, token, sizeof(token)); + if (strcmp(token, "terrain")) { Con_Printf(CON_ERROR "%s wasn't terrain map\n", mod->name); //shouldn't happen return false; @@ -4612,6 +4692,10 @@ qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize mod->entities = ZG_Malloc(&mod->memgroup, strlen(buffer)+1); strcpy(mod->entities, buffer); + strcpy(hm->groundshadername, "terrainshader"); + strcpy(hm->skyname, "sky1"); + + hm->entitylock = Sys_CreateMutex(); hm->sectionsize = sectsize; hm->firstsegx = -1; hm->firstsegy = -1; @@ -4653,8 +4737,6 @@ qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize mod->terrain = hm; - Terr_FinishTerrain(hm, shadername, skyname); - return true; } @@ -4682,20 +4764,27 @@ void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force) potential.maxsegy = bound(CHUNKBIAS+1, potential.maxsegy, CHUNKLIMIT); if (!force) - if (!COM_FCheckExists(va("maps/%s/sect_%03x_%03x.hms", loadname, potential.firstsegx + (potential.maxsegx-potential.firstsegx)/2, potential.firstsegy + (potential.maxsegy-potential.firstsegy)/2))) - if (!COM_FCheckExists(va("maps/%s/block_00_00.hms", loadname))) + { + char sect[MAX_QPATH]; + Q_snprintfz(sect, sizeof(sect), "maps/%s/sect_%03x_%03x.hms", loadname, potential.firstsegx + (potential.maxsegx-potential.firstsegx)/2, potential.firstsegy + (potential.maxsegy-potential.firstsegy)/2); + if (!COM_FCheckExists(sect)) + { + Q_snprintfz(sect, sizeof(sect), "maps/%s/block_00_00.hms", loadname); + if (!COM_FCheckExists(sect)) return NULL; + } + } } hm = Z_Malloc(sizeof(*hm)); *hm = potential; + hm->entitylock = Sys_CreateMutex(); ClearLink(&hm->recycle); Q_strncpyz(hm->path, loadname, sizeof(hm->path)); + Q_strncpyz(hm->groundshadername, "terrainshader", sizeof(hm->groundshadername)); hm->exteriorcontents = FTECONTENTS_EMPTY; //bsp geometry outside the heightmap - Terr_FinishTerrain(hm, "terrainshader", NULL); - return hm; } @@ -4771,7 +4860,7 @@ void Mod_Terrain_Convert_f(void) { for (sx = x; sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++) { - s = Terr_GetSection(hm, sx, sy, TGS_LOAD|TGS_NODOWNLOAD|TGS_NORENDER); + s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER); if (s) { if (*texkill) @@ -4784,7 +4873,7 @@ void Mod_Terrain_Convert_f(void) { for (sx = x; sx < x+SECTIONSPERBLOCK && sx < hm->maxsegx; sx++) { - s = Terr_GetSection(hm, sx, sy, TGS_LOAD|TGS_NODOWNLOAD|TGS_NORENDER); + s = Terr_GetSection(hm, sx, sy, TGS_WAITLOAD|TGS_NODOWNLOAD|TGS_NORENDER); if (s) { if (s->flags & TSF_EDITED) diff --git a/engine/gl/gl_hlmdl.c b/engine/gl/gl_hlmdl.c index b42d6b31..e8dad2d7 100644 --- a/engine/gl/gl_hlmdl.c +++ b/engine/gl/gl_hlmdl.c @@ -71,7 +71,6 @@ void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float Mod_LoadHLModel - read in the model's constituent parts ======================================================================================================================= */ -extern char loadname[]; qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize) { /*~~*/ diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index fa5bb31a..a76d5c28 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -32,7 +32,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. extern cvar_t r_shadow_bumpscale_basetexture; extern cvar_t r_replacemodels; extern cvar_t gl_lightmap_average; +cvar_t mod_loadentfiles = CVAR("sv_loadentfiles", "1"); cvar_t mod_external_vis = CVARD("mod_external_vis", "1", "Attempt to load .vis patches for quake maps, allowing transparent water to work properly."); +cvar_t mod_warnmodels = CVARD("mod_warnmodels", "1", "Warn if any models failed to load. Set to 0 if your mod is likely to lack optional models (like its in development)."); //set to 0 for hexen2 and its otherwise-spammy-as-heck demo. +cvar_t mod_litsprites = CVARD("mod_litsprites", "0", "If set to 1, sprites will be lit according to world lighting (including rtlights), like Tenebrae. Use EF_ADDITIVE or EF_FULLBRIGHT to make emissive sprites instead."); #ifdef SERVERONLY cvar_t gl_overbright, gl_specular, gl_load24bit, r_replacemodels, gl_miptexLevel, r_fb_bmodels; //all of these can/should default to 0 cvar_t r_noframegrouplerp = CVARF ("r_noframegrouplerp", "0", CVAR_ARCHIVE); @@ -43,12 +46,10 @@ texture_t *r_notexture_mip = &r_notexture_mip_real; qboolean isnotmap = true; //used to not warp ammo models. -model_t *loadmodel; -char loadname[32]; // for hunk tags - void CM_Init(void); void CM_Shutdown(void); +void Mod_LoadSpriteShaders(model_t *spr); qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize); qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize); qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); @@ -56,6 +57,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer, size_t fsize); #endif model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose); +static void Mod_PrintFormats_f(void); #ifdef MAP_DOOM qboolean Mod_LoadDoomLevel(model_t *mod); @@ -75,7 +77,8 @@ extern cvar_t gl_specular; #endif extern cvar_t r_fb_bmodels; mesh_t nullmesh; -void Mod_SortShaders(void); +void Mod_SortShaders(model_t *mod); +void Mod_LoadAliasShaders(model_t *mod); #ifdef RUNTIMELIGHTING model_t *lightmodel; @@ -126,7 +129,7 @@ static void Mod_BatchList_f(void) unsigned int count; for (m=0 , mod=mod_known ; mtype == mod_brush && !mod->needload) + if (mod->type == mod_brush && mod->loadstate == MLS_LOADED) { Con_Printf("%s:\n", mod->name); count = 0; @@ -168,7 +171,7 @@ static void Mod_TextureList_f(void) Con_Printf("%u\n", count); shownmodelname = false; - if (mod->type == mod_brush && !mod->needload) + if (mod->type == mod_brush && mod->loadstate == MLS_LOADED) { if (*mod->name == '*') continue;// inlines don't count @@ -221,14 +224,15 @@ static void Mod_BlockTextureColour_f (void) if (!s) { s = R_RegisterCustom (texname, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); - } - for (i = 0; i < sizeof(colour)/sizeof(colour[0]); i++) - colour[i] = rgba; + for (i = 0; i < sizeof(colour)/sizeof(colour[0]); i++) + colour[i] = rgba; +// s->defaulttextures.base = GL_LoadTexture32(texname, 8, 8, colour, IF_NOMIPMAP); + } for (m=0 , mod=mod_known ; mtype == mod_brush && !mod->needload) + if (mod->type == mod_brush && mod->loadstate == MLS_LOADED) { for (i = 0; i < mod->numtextures; i++) { @@ -382,7 +386,7 @@ void Mod_RebuildLightmaps (void) for (i=0 , mod=mod_known ; ineedload) + if (mod->loadstate != MLS_LOADED) continue; if (mod->type == mod_brush) @@ -403,7 +407,7 @@ void Mod_ResortShaders(void) model_t *mod; for (i=0, mod=mod_known ; ineedload) + if (mod->loadstate != MLS_LOADED) continue; memcpy(oldlists, mod->batches, sizeof(oldlists)); @@ -461,14 +465,14 @@ void Mod_Purge(enum mod_purge_e ptype) for (i=0 , mod=mod_known ; ineedload) - continue; - unused = mod->datasequence != mod_datasequence; //this model isn't active any more. if (unused || ptype != MP_MAPCHANGED) { + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + if (unused) Con_DPrintf("model \"%s\" no longer needed\n", mod->name); @@ -498,11 +502,16 @@ void Mod_Purge(enum mod_purge_e ptype) Terr_FreeModel(mod); } #endif + if (mod->type == mod_alias) + { + Mod_DestroyMesh(mod->meshinfo); + mod->meshinfo = NULL; + } //and obliterate anything else remaining in memory. ZG_FreeGroup(&mod->memgroup); mod->meshinfo = NULL; - mod->needload = true; + mod->loadstate = MLS_NOTLOADED; } } } @@ -530,7 +539,13 @@ void Mod_Init (qboolean initial) #endif } else + { Cvar_Register(&mod_external_vis, "Graphical Nicaties"); + Cvar_Register(&mod_warnmodels, "Graphical Nicaties"); + Cvar_Register(&mod_litsprites, "Graphical Nicaties"); + Cvar_Register(&mod_loadentfiles, NULL); + Cmd_AddCommand("version_modelformats", Mod_PrintFormats_f); + } if (initial) { @@ -665,6 +680,18 @@ mleaf_t *Mod_PointInLeaf (model_t *model, vec3_t p) return NULL; // never reached } +const char *Mod_FixName(const char *modname, const char *worldname) +{ + if (*modname == '*' && worldname && *worldname) + { + //make sure that the value is an inline value with no existing extra postfix or anything. + char *e; + if (strtoul(modname+1, &e, 10) != 0) + if (!*e) + return va("%s:%s", modname, worldname); + } + return modname; +} /* ================== Mod_FindName @@ -688,20 +715,32 @@ model_t *Mod_FindName (const char *name) if (i == mod_numknown) { - if (mod_numknown == MAX_MOD_KNOWN) - Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); - if (strlen(name) >= sizeof(mod->name)) - Sys_Error ("model name is too long: %s", name); - memset(mod, 0, sizeof(model_t)); //clear the old model as the renderers use the same globals - strcpy (mod->name, name); - mod->needload = true; - mod_numknown++; - mod->particleeffect = -1; - mod->particletrail = -1; +#ifdef LOADERTHREAD + Sys_LockMutex(com_resourcemutex); + for (i=0 , mod=mod_known ; iname, name) ) + break; + if (i == mod_numknown) + { +#endif + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + if (strlen(name) >= sizeof(mod->name)) + Sys_Error ("model name is too long: %s", name); + memset(mod, 0, sizeof(model_t)); //clear the old model as the renderers use the same globals + Q_strncpyz (mod->name, name, sizeof(mod->name)); + mod->loadstate = MLS_NOTLOADED; + mod_numknown++; + mod->particleeffect = -1; + mod->particletrail = -1; +#ifdef LOADERTHREAD + } + Sys_UnlockMutex(com_resourcemutex); +#endif } - if (mod->needload == 2) - mod->needload = true; + if (mod->loadstate == MLS_FAILED) + mod->loadstate = MLS_NOTLOADED; //mark it as active, so it doesn't get flushed prematurely mod->datasequence = mod_datasequence; @@ -716,17 +755,8 @@ Mod_TouchModel */ void Mod_TouchModel (const char *name) { - model_t *mod; - - mod = Mod_FindName (name); - - if (!mod->needload) - { -// if (mod->type == mod_alias -// || mod->type == mod_halflife -// ) -// Cache_Check (&mod->cache); - } + //findname does this anyway. + Mod_FindName (name); } static struct @@ -738,6 +768,16 @@ static struct qboolean (QDECL *load) (model_t *mod, void *buffer, size_t buffersize); } modelloaders[64]; +static void Mod_PrintFormats_f(void) +{ + int i; + for (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++) + { + if (modelloaders[i].load && modelloaders[i].formatname) + Con_Printf("%s\n", modelloaders[i].formatname); + } +} + int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (model_t *mod, void *buffer, size_t fsize)) { int i, free = -1; @@ -791,12 +831,14 @@ int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned void Mod_UnRegisterModelFormat(void *module, int idx) { + idx--; if ((unsigned int)(idx) >= sizeof(modelloaders)/sizeof(modelloaders[0])) return; if (modelloaders[idx].module != module) return; + COM_WorkerFullSync(); Z_Free(modelloaders[idx].ident); modelloaders[idx].ident = NULL; Z_Free(modelloaders[idx].formatname); @@ -811,6 +853,7 @@ void Mod_UnRegisterModelFormat(void *module, int idx) void Mod_UnRegisterAllModelFormats(void *module) { int i; + COM_WorkerFullSync(); for (i = 0; i < sizeof(modelloaders)/sizeof(modelloaders[0]); i++) { if (modelloaders[i].module == module) @@ -818,6 +861,70 @@ void Mod_UnRegisterAllModelFormats(void *module) } } +void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b) +{ + model_t *mod = ctx; + enum mlverbosity_e verbose = b; +#ifndef SERVERONLY + P_LoadedModel(mod); +#endif + +#ifdef TERRAIN + if (mod->terrain) + Terr_FinishTerrain(mod); +#endif +#ifndef SERVERONLY + if (mod->type == mod_brush) + { + Surf_BuildModelLightmaps(mod); + } + if (mod->type == mod_sprite) + { + Mod_LoadSpriteShaders(mod); + } + if (mod->type == mod_alias) + { + if (qrenderer != QR_NONE) + Mod_LoadAliasShaders(mod); + + +#ifdef RAGDOLL + { + int numbones = Mod_GetNumBones(mod, false); + if (numbones) + { + size_t filesize; + char *buf; + char dollname[MAX_QPATH]; + Q_snprintfz(dollname, sizeof(dollname), "%s.doll", mod->name); + buf = COM_LoadFile(dollname, 5, &filesize); + if (buf) + { + mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf); + BZ_Free(buf); + } + } + } +#endif + } +#endif + + mod->loadstate = a; + + switch(verbose) + { + default: + case MLV_ERROR: + Host_EndGame ("Mod_NumForName: %s not found or couldn't load", mod->name); + break; + case MLV_WARN: + if (*mod->name != '*' && strcmp(mod->name, "null") && mod_warnmodels.ival) + Con_Printf(CON_ERROR "Unable to load %s\n", mod->name); + break; + case MLV_SILENT: + break; + } +} /* ================== Mod_LoadModel @@ -825,31 +932,17 @@ Mod_LoadModel Loads a model into the cache ================== */ -model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) +void Mod_LoadModelWorker (void *ctx, void *data, size_t a, size_t b) { + model_t *mod = ctx; + enum mlverbosity_e verbose = a; unsigned *buf = NULL; - qbyte stackbuf[1024]; // avoid dirtying the cache heap char mdlbase[MAX_QPATH]; char *replstr; - qboolean doomsprite = false, tryreplace; + qboolean doomsprite = false; unsigned int magic, i; - - char *ext; - - if (!mod->needload && mod->type != mod_dummy) - { - if (mod->type == mod_alias - || mod->type == mod_halflife - ) - { - if (mod->meshinfo) - return mod; - } - else - return mod; // not cached at all - } - - loadmodel = mod; + size_t filesize; + char ext[8]; if (!*mod->name) { @@ -860,12 +953,9 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) mod->maxs[0] = 16; mod->maxs[1] = 16; mod->maxs[2] = 16; - mod->needload = false; mod->engineflags = 0; -#ifndef SERVERONLY - P_LoadedModel(mod); -#endif - return mod; + COM_AddWork(0, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0); + return; } #ifdef RAGDOLL @@ -876,8 +966,8 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) } #endif - if (mod->needload == 2) - return mod; + if (mod->loadstate == MLS_FAILED) + return; // // load the file @@ -916,11 +1006,8 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) !strncmp(mod->name, "progs/v_", 8)) mod->engineflags &= ~MDLF_EZQUAKEFBCHEAT; - // call the apropriate loader - mod->needload = false; - // get string used for replacement tokens - ext = COM_FileExtension(mod->name); + COM_FileExtension(mod->name, ext, sizeof(ext)); if (!Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "sp2")) replstr = ""; // sprite else if (!Q_strcasecmp(ext, "dsp")) // doom sprite @@ -935,32 +1022,34 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) if (!gl_load24bit.value) replstr = ""; - tryreplace = !!*replstr; - COM_StripExtension(mod->name, mdlbase, sizeof(mdlbase)); while (replstr) { - replstr = COM_ParseStringSet(replstr); + char token[256]; + replstr = COM_ParseStringSet(replstr, token, sizeof(token)); if (replstr) { - TRACE(("Mod_LoadModel: Trying to load (replacement) model \"%s.%s\"\n", mdlbase, com_token)); - buf = (unsigned *)COM_LoadStackFile (va("%s.%s", mdlbase, com_token), stackbuf, sizeof(stackbuf)); + char altname[MAX_QPATH]; + Q_snprintfz(altname, sizeof(altname), "%s.%s", mdlbase, token); + TRACE(("Mod_LoadModel: Trying to load (replacement) model \"%s\"\n", altname)); + buf = (unsigned *)COM_LoadFile (altname, 5, &filesize); } else { TRACE(("Mod_LoadModel: Trying to load model \"%s\"\n", mod->name)); - buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + buf = (unsigned *)COM_LoadFile (mod->name, 5, &filesize); if (!buf) { #ifdef DSPMODELS if (doomsprite) // special case needed for doom sprites { - mod->needload = false; TRACE(("Mod_LoadModel: doomsprite: \"%s\"\n", mod->name)); Mod_LoadDoomSprite(mod); - return mod; + BZ_Free(buf); + COM_AddWork(0, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0); + return; } #endif break; // failed to load unreplaced file and nothing left @@ -969,15 +1058,10 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) if (!buf) continue; -// -// allocate a new model -// - COM_FileBase (mod->name, loadname, sizeof(loadname)); - // // fill it in // - Mod_DoCRC(mod, (char*)buf, com_filesize); + Mod_DoCRC(mod, (char*)buf, filesize); magic = LittleLong(*(unsigned *)buf); for(i = 0; i < sizeof(modelloaders) / sizeof(modelloaders[0]); i++) @@ -987,29 +1071,32 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) } if (i < sizeof(modelloaders) / sizeof(modelloaders[0])) { - if (!modelloaders[i].load(mod, buf, com_filesize)) + if (!modelloaders[i].load(mod, buf, filesize)) + { + BZ_Free(buf); continue; -#ifndef SERVERONLY - if (mod->type == mod_brush) - Surf_BuildModelLightmaps(mod); -#endif + } } else { - COM_Parse((char*)buf); + COM_ParseOut((char*)buf, token, sizeof(token)); for(i = 0; i < sizeof(modelloaders) / sizeof(modelloaders[0]); i++) { - if (modelloaders[i].load && modelloaders[i].ident && !strcmp(modelloaders[i].ident, com_token)) + if (modelloaders[i].load && modelloaders[i].ident && !strcmp(modelloaders[i].ident, token)) break; } if (i < sizeof(modelloaders) / sizeof(modelloaders[0])) { - if (!modelloaders[i].load(mod, buf, com_filesize)) + if (!modelloaders[i].load(mod, buf, filesize)) + { + BZ_Free(buf); continue; + } } else { Con_Printf(CON_WARNING "Unrecognised model format 0x%x (%c%c%c%c)\n", LittleLong(*(unsigned *)buf), ((char*)buf)[0], ((char*)buf)[1], ((char*)buf)[2], ((char*)buf)[3]); + BZ_Free(buf); continue; } } @@ -1026,47 +1113,14 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) #endif */ -#ifndef SERVERONLY - P_LoadedModel(mod); - Validation_IncludeFile(mod->name, (char *)buf, com_filesize); -#endif - TRACE(("Mod_LoadModel: Loaded\n")); -#ifdef RAGDOLL - { - int numbones = Mod_GetNumBones(mod, false); - if (numbones) - { - char *dollname = va("%s.doll", mod->name); - buf = (unsigned *)COM_LoadStackFile (dollname, stackbuf, sizeof(stackbuf)); - if (buf) - mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, (char*)buf); - } - } -#endif + BZ_Free(buf); - return mod; + COM_AddWork(0, Mod_ModelLoaded, mod, NULL, MLS_LOADED, 0); + return; } - switch(verbose) - { - default: - case MLV_ERROR: - Host_EndGame ("Mod_NumForName: %s not found or couldn't load", mod->name); - break; - case MLV_WARN: - if (*mod->name != '*' && strcmp(mod->name, "null")) - { - if (tryreplace) - Con_Printf(CON_ERROR "Unable to load or replace %s\n", mod->name); - else - Con_Printf(CON_ERROR "Unable to load %s\n", mod->name); - } - break; - case MLV_SILENT: - break; - } mod->type = mod_dummy; mod->mins[0] = -16; mod->mins[1] = -16; @@ -1074,11 +1128,32 @@ model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) mod->maxs[0] = 16; mod->maxs[1] = 16; mod->maxs[2] = 16; - mod->needload = 2; mod->engineflags = 0; -#ifndef SERVERONLY - P_LoadedModel(mod); -#endif + COM_AddWork(0, Mod_ModelLoaded, mod, NULL, MLS_FAILED, verbose); +} + + +model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) +{ + if (mod->loadstate == MLS_NOTLOADED && *mod->name != '*') + { + mod->loadstate = MLS_LOADING; +// if (verbose == MLV_ERROR) //if its fatal on failure (ie: world), do it on the main thread and block to wait for it. +// Mod_LoadModelWorker(mod, MLV_WARN, 0); +// else + COM_AddWork(1, Mod_LoadModelWorker, mod, NULL, verbose, 0); + } + + if (verbose == MLV_ERROR) + { + //someone already tried to load it without caring if it failed or not. make sure its loaded. + //fixme: this is a spinloop. + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + + if (mod->loadstate != MLS_LOADED) + Host_EndGame ("Mod_NumForName: %s not found or couldn't load", mod->name); + } return mod; } @@ -1107,8 +1182,6 @@ model_t *Mod_ForName (const char *name, enum mlverbosity_e verbosity) =============================================================================== */ -qbyte *mod_base; - #if 0 char *advtexturedesc; char *mapsection; @@ -1295,7 +1368,7 @@ void Mod_LoadAdvancedTexture(char *name, int *base, int *norm, int *luma, int *g } #endif -void Mod_FinishTexture(texture_t *tx, texnums_t tn) +void Mod_FinishTexture(texture_t *tx) { #ifndef SERVERONLY extern cvar_t gl_shadeq1_name; @@ -1318,7 +1391,7 @@ void Mod_FinishTexture(texture_t *tx, texnums_t tn) tx->shader = R_RegisterCustom (altname, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL); } - R_BuildDefaultTexnums(&tn, tx->shader); + R_BuildDefaultTexnums(&tx->texnums, tx->shader); #endif } @@ -1326,20 +1399,21 @@ void Mod_FinishTexture(texture_t *tx, texnums_t tn) #define LMT_FULLBRIGHT 2 #define LMT_BUMP 4 #define LMT_SPEC 8 -static void Mod_LoadMiptex(texture_t *tx, miptex_t *mt, texnums_t *tn, int maps) +static void Mod_LoadMiptex(model_t *loadmodel, char *loadname, texture_t *tx, miptex_t *mt, int maps) { #ifndef SERVERONLY char altname[256]; qbyte *base; qboolean alphaed; - int j; int pixels = mt->width*mt->height/64*85; - qboolean using8bit = false; + + if (!strncmp(loadname, "b_", 2)) + loadname = "bmodels"; if (!Q_strncmp(mt->name,"sky",3)) { if (maps & LMT_DIFFUSE) - R_InitSky (tn, tx, (char *)mt + mt->offsets[0]); + R_InitSky (&tx->texnums, tx, (char *)mt + mt->offsets[0]); } else { @@ -1357,16 +1431,7 @@ static void Mod_LoadMiptex(texture_t *tx, miptex_t *mt, texnums_t *tn, int maps) { base = W_ConvertWAD3Texture(mt, &mt->width, &mt->height, &alphaed); //convert texture to 32 bit. tx->alphaed = alphaed; - tn->base = R_LoadReplacementTexture(mt->name, loadname, alphaed?0:IF_NOALPHA); - if (!TEXVALID(tn->base)) - { - tn->base = R_LoadReplacementTexture(mt->name, "bmodels", alphaed?0:IF_NOALPHA); - if (base && !TEXVALID(tn->base)) - { - tn->base = R_LoadTexture32 (mt->name, tx->width, tx->height, (unsigned int *)base, (alphaed?0:IF_NOALPHA)); - using8bit = true; - } - } + tx->texnums.base = R_LoadReplacementTexture(mt->name, loadname, alphaed?0:IF_NOALPHA, base, tx->width, tx->height, alphaed?TF_RGBA32:TF_RGBX32); BZ_Free(base); } @@ -1392,70 +1457,34 @@ static void Mod_LoadMiptex(texture_t *tx, miptex_t *mt, texnums_t *tn, int maps) if (maps & LMT_DIFFUSE) { - tn->base = R_LoadReplacementTexture(mt->name, loadname, ((*mt->name == '{')?0:IF_NOALPHA)|IF_SUBDIRONLY|IF_MIPCAP); - if (!TEXVALID(tn->base)) - { - tn->base = R_LoadReplacementTexture(mt->name, "bmodels", ((*mt->name == '{')?0:IF_NOALPHA)|IF_MIPCAP); - if (!TEXVALID(tn->base)) - { - tn->base = R_LoadTexture8 (mt->name, mipwidth, mipheight, mipbase, ((*mt->name == '{')?0:IF_NOALPHA)|IF_MIPCAP, 1); - using8bit = true; - } - } + tx->texnums.base = R_LoadReplacementTexture(mt->name, loadname, ((*mt->name == '{')?0:IF_NOALPHA)|IF_MIPCAP, mipbase, mipwidth, mipheight, (*mt->name == '{')?TF_TRANS8:TF_SOLID8); } if (maps & LMT_FULLBRIGHT) { snprintf(altname, sizeof(altname)-1, "%s_luma", mt->name); - if (gl_load24bit.value) //allows fullbright replacement without diffuse - { - tn->fullbright = R_LoadReplacementTexture(altname, loadname, IF_NOGAMMA|IF_SUBDIRONLY|IF_MIPCAP); - if (!TEXVALID(tn->fullbright)) - tn->fullbright = R_LoadReplacementTexture(altname, "bmodels", IF_NOGAMMA|IF_MIPCAP); - } - //only use 8bit luma if 8bit diffuse was also used. - if (using8bit && (*mt->name != '{') && !TEXVALID(tn->fullbright)) //generate one (if possible). - tn->fullbright = R_LoadTextureFB(altname, mipwidth, mipheight, mipbase, IF_NOGAMMA|IF_MIPCAP); + tx->texnums.fullbright = R_LoadReplacementTexture(altname, loadname, IF_MIPCAP, mipbase, mipwidth, mipheight, TF_TRANS8_FULLBRIGHT); + } + + if ((maps & LMT_BUMP) && (r_shadow_bumpscale_basetexture.value != 0 || *mt->name == '*')) + { + maps &= ~LMT_BUMP; + snprintf(altname, sizeof(altname)-1, "%s_norm", mt->name); + tx->texnums.bump = R_LoadReplacementTexture(altname, loadname, IF_NOGAMMA|IF_MIPCAP|IF_TRYBUMP, mipbase, mipwidth, mipheight, TF_HEIGHT8PAL); } } if (maps & LMT_BUMP) { snprintf(altname, sizeof(altname)-1, "%s_norm", mt->name); - tn->bump = R_LoadReplacementTexture(altname, loadname, IF_NOGAMMA|IF_SUBDIRONLY|IF_MIPCAP); - if (!TEXVALID(tn->bump)) - tn->bump = R_LoadReplacementTexture(altname, "bmodels", IF_NOGAMMA|IF_MIPCAP); - if (!TEXVALID(tn->bump)) - { - if (gl_load24bit.value) //allows bumpmaps replacement without diffuse - { - snprintf(altname, sizeof(altname)-1, "%s_bump", mt->name); - tn->bump = R_LoadBumpmapTexture(altname, loadname); - if (!TEXVALID(tn->bump)) - tn->bump = R_LoadBumpmapTexture(altname, "bmodels"); - } - } - - //only use 8bit bumps if the 8bit diffuse was used. - if (using8bit && !TEXVALID(tn->bump) && loadmodel->fromgame != fg_halflife) - { - snprintf(altname, sizeof(altname)-1, "%s_bump", mt->name); - //no mip levels here, would be absurd. - base = (qbyte *)(mt+1); //convert to greyscale. - for (j = 0; j < pixels; j++) - base[j] = (host_basepal[base[j]*3] + host_basepal[base[j]*3+1] + host_basepal[base[j]*3+2]) / 3; - - tn->bump = R_LoadTexture8BumpPal(altname, tx->width, tx->height, base, true); //normalise it and then bump it. - } + tx->texnums.bump = R_LoadReplacementTexture(altname, loadname, IF_NOGAMMA|IF_MIPCAP|IF_TRYBUMP, NULL, 0, 0, TF_INVALID); } //don't do any complex quake 8bit -> glossmap. It would likly look a little ugly... - if ((maps & LMT_SPEC) && gl_load24bit.value) //allows bumpmaps replacement without diffuse + if (maps & LMT_SPEC) //allows bumpmaps replacement without diffuse { snprintf(altname, sizeof(altname)-1, "%s_gloss", mt->name); - tn->specular = R_LoadHiResTexture(altname, loadname, IF_NOGAMMA|IF_SUBDIRONLY|IF_MIPCAP); - if (!TEXVALID(tn->specular)) - tn->specular = R_LoadHiResTexture(altname, "bmodels", IF_NOGAMMA|IF_MIPCAP); + tx->texnums.specular = R_LoadReplacementTexture(altname, loadname, IF_NOGAMMA|IF_MIPCAP, NULL, 0, 0, TF_INVALID); } } #endif @@ -1466,7 +1495,7 @@ static void Mod_LoadMiptex(texture_t *tx, miptex_t *mt, texnums_t *tn, int maps) Mod_LoadTextures ================= */ -qboolean Mod_LoadTextures (lump_t *l) +qboolean Mod_LoadTextures (model_t *loadmodel, qbyte *mod_base, lump_t *l, char *loadname) { int i, j, num, max, altmax; miptex_t *mt; @@ -1497,9 +1526,9 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); return true; } m = (dmiptexlump_t *)(mod_base + l->fileofs); - + m->nummiptex = LittleLong (m->nummiptex); - + loadmodel->numtextures = m->nummiptex; loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, m->nummiptex * sizeof(*loadmodel->textures)); @@ -1528,7 +1557,7 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); mt->height = LittleLong (mt->height); for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); - + if ( (mt->width & 15) || (mt->height & 15) ) Con_Printf (CON_WARNING "Warning: Texture %s is not 16 aligned", mt->name); if (mt->width < 1 || mt->height < 1) @@ -1547,20 +1576,21 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); memset(&tn, 0, sizeof(tn)); - maps = LMT_DIFFUSE; - if (r_fb_bmodels.ival) - maps |= LMT_FULLBRIGHT; - if (r_loadbumpmapping) - maps |= LMT_BUMP; - if (gl_specular.ival) - maps |= LMT_SPEC; + maps = 0; +#ifndef SERVERONLY + if (qrenderer != QR_NONE) + { + maps |= LMT_DIFFUSE; + if (r_fb_bmodels.ival) + maps |= LMT_FULLBRIGHT; + if (r_loadbumpmapping || (r_waterstyle.ival > 1 && *tx->name == '*')) + maps |= LMT_BUMP; + if (gl_specular.ival) + maps |= LMT_SPEC; + } +#endif - Mod_LoadMiptex(tx, mt, &tn, maps); - - Mod_FinishTexture(tx, tn); - - if ((tx->shader->flags & SHADER_HASNORMALMAP) && !(maps & LMT_BUMP)) - Mod_LoadMiptex(tx, mt, &tx->shader->defaulttextures, LMT_BUMP); + Mod_LoadMiptex(loadmodel, loadname, tx, mt, maps); } // // sequence the animations @@ -1632,7 +1662,7 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n")); return false; } } - + #define ANIM_CYCLE 2 // link them all together for (j=0 ; jname, loadname, sizeof(loadname)); for (i=0 ; inumtextures ; i++) { @@ -1687,9 +1718,7 @@ void Mod_NowLoadExternal(void) if (tx->shader) continue; - memset (&tn, 0, sizeof(tn)); - - if (!TEXVALID(tn.base)) + if (!TEXVALID(tx->texnums.base)) { qbyte * data; @@ -1699,21 +1728,21 @@ void Mod_NowLoadExternal(void) tx->alphaed = alphaed; } - tn.base = R_LoadHiResTexture(tx->name, loadname, IF_NOALPHA|IF_MIPCAP); - if (!TEXVALID(tn.base)) + tx->texnums.base = R_LoadHiResTexture(tx->name, loadname, IF_NOALPHA|IF_MIPCAP); + if (!TEXVALID(tx->texnums.base)) { - tn.base = R_LoadHiResTexture(tx->name, "bmodels", IF_NOALPHA|IF_MIPCAP); + tx->texnums.base = R_LoadHiResTexture(tx->name, "bmodels", IF_NOALPHA|IF_MIPCAP); // if (!TEXVALID(tn.base)) // tn.base = R_LoadReplacementTexture("light1_4", NULL, IF_NOALPHA|IF_MIPCAP); //a fallback. :/ } BZ_Free(data); } - if (!TEXVALID(tn.bump) && *tx->name != '{' && r_loadbumpmapping) + if (!TEXVALID(tx->texnums.bump) && *tx->name != '{' && r_loadbumpmapping) { - tn.bump = R_LoadBumpmapTexture(va("%s_bump", tx->name), loadname); +/* tx->texnums.bump = R_LoadBumpmapTexture(va("%s_bump", tx->name), loadname); + if (!TEXVALID(tx->texnums.bump)) + tx->texnums.bump = R_LoadBumpmapTexture(va("%s_bump", tx->name), "bmodels"); if (!TEXVALID(tn.bump)) - tn.bump = R_LoadBumpmapTexture(va("%s_bump", tx->name), "bmodels"); -/* if (!TEXVALID(tn.bump)) { qbyte *data; qbyte *heightmap; @@ -1733,11 +1762,11 @@ void Mod_NowLoadExternal(void) } } */ } - if (!TEXVALID(tn.base)) + if (!TEXVALID(tx->texnums.base)) { - tn.base = R_LoadTexture8("notexture", 16, 16, r_notexture_mip+1, IF_NOALPHA, 0); + tx->texnums.base = R_LoadTexture8("notexture", 16, 16, r_notexture_mip+1, IF_NOALPHA, 0); } - Mod_FinishTexture(tx, tn); + Mod_FinishTexture(tx); } #endif } @@ -1773,7 +1802,7 @@ void BuildLightMapGammaTable (float g, float c) Mod_LoadLighting ================= */ -void Mod_LoadLighting (lump_t *l) +void Mod_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l) { qboolean luxtmp = true; qboolean littmp = true; @@ -1818,29 +1847,30 @@ void Mod_LoadLighting (lump_t *l) #ifndef SERVERONLY if (!luxdata && r_loadlits.ival && r_deluxemapping.ival) { //the map util has a '-scalecos X' parameter. use 0 if you're going to use only just lux. without lux scalecos 0 is hideous. - char luxname[MAX_QPATH]; + char luxname[MAX_QPATH]; + size_t luxsz = 0; if (!luxdata) { - strcpy(luxname, loadmodel->name); + Q_strncpyz(luxname, loadmodel->name, sizeof(luxname)); COM_StripExtension(loadmodel->name, luxname, sizeof(luxname)); COM_DefaultExtension(luxname, ".lux", sizeof(luxname)); - luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname); + luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz); luxtmp = false; } if (!luxdata) { - strcpy(luxname, "luxs/"); + Q_strncpyz(luxname, "luxs/", sizeof(luxname)); COM_StripExtension(COM_SkipPath(loadmodel->name), luxname+5, sizeof(luxname)-5); - strcat(luxname, ".lux"); + Q_strncatz(luxname, ".lux", sizeof(luxname)); - luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname); + luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz); luxtmp = false; } if (!luxdata) //dp... { COM_StripExtension(loadmodel->name, luxname, sizeof(luxname)); COM_DefaultExtension(luxname, ".dlit", sizeof(luxname)); - luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname); + luxdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, luxname, &luxsz); luxtmp = false; } if (!luxdata) @@ -1853,7 +1883,7 @@ void Mod_LoadLighting (lump_t *l) } else if (luxdata) { - if (l->filelen && l->filelen != (com_filesize-8)/3) + if (l->filelen && l->filelen != (luxsz-8)/3) { Con_Printf("deluxmap \"%s\" doesn't match level. Ignored.\n", luxname); luxdata=NULL; @@ -1886,17 +1916,18 @@ void Mod_LoadLighting (lump_t *l) char litnamelits[MAX_QPATH]; int depthmaps; int depthlits; + size_t litsize; { - strcpy(litnamemaps, loadmodel->name); + Q_strncpyz(litnamemaps, loadmodel->name, sizeof(litnamelits)); COM_StripExtension(loadmodel->name, litnamemaps, sizeof(litnamemaps)); COM_DefaultExtension(litnamemaps, ".lit", sizeof(litnamemaps)); depthmaps = COM_FDepthFile(litnamemaps, false); } { - strcpy(litnamelits, "lits/"); + Q_strncpyz(litnamelits, "lits/", sizeof(litnamelits)); COM_StripExtension(COM_SkipPath(loadmodel->name), litnamelits+5, sizeof(litnamelits) - 5); - strcat(litnamelits, ".lit"); + Q_strncatz(litnamelits, ".lit", sizeof(litnamelits)); depthlits = COM_FDepthFile(litnamelits, false); } @@ -1907,7 +1938,7 @@ void Mod_LoadLighting (lump_t *l) litname = litnamelits; } - litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname); + litdata = FS_LoadMallocGroupFile(&loadmodel->memgroup, litname, &litsize); littmp = false; if (!litdata) { @@ -1919,7 +1950,7 @@ void Mod_LoadLighting (lump_t *l) } else if (litdata[0] == 'Q' && litdata[1] == 'L' && litdata[2] == 'I' && litdata[3] == 'T') { - if (LittleLong(*(int *)&litdata[4]) == 1 && l->filelen && samples*3 != (com_filesize-8)) + if (LittleLong(*(int *)&litdata[4]) == 1 && l->filelen && samples*3 != (litsize-8)) { litdata = NULL; Con_Printf("lit \"%s\" doesn't match level. Ignored.\n", litname); @@ -2067,7 +2098,7 @@ void Mod_LoadLighting (lump_t *l) Mod_LoadVisibility ================= */ -void Mod_LoadVisibility (lump_t *l, qbyte *ptr, size_t len) +void Mod_LoadVisibility (model_t *loadmodel, qbyte *mod_base, lump_t *l, qbyte *ptr, size_t len) { if (!ptr) { @@ -2089,16 +2120,35 @@ void Mod_LoadVisibility (lump_t *l, qbyte *ptr, size_t len) Mod_LoadEntities ================= */ -void Mod_LoadEntities (lump_t *l) +void Mod_LoadEntities (model_t *loadmodel, qbyte *mod_base, lump_t *l) { + char fname[MAX_QPATH]; + size_t sz; + loadmodel->entitiescrc = 0; + loadmodel->entities = NULL; if (!l->filelen) - { - loadmodel->entities = NULL; return; + + if (mod_loadentfiles.value && !loadmodel->entities) + { + COM_StripExtension(loadmodel->name, fname, sizeof(fname)); + Q_strncatz(fname, ".ent", sizeof(fname)); + loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz); } - loadmodel->entities = ZG_Malloc(&loadmodel->memgroup, l->filelen + 1); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); - loadmodel->entities[l->filelen] = 0; + if (mod_loadentfiles.value && !loadmodel->entities) + { //tenebrae compat + COM_StripExtension(loadmodel->name, fname, sizeof(fname)); + Q_strncatz(fname, ".edo", sizeof(fname)); + loadmodel->entities = FS_LoadMallocGroupFile(&loadmodel->memgroup, fname, &sz); + } + if (!loadmodel->entities) + { + loadmodel->entities = ZG_Malloc(&loadmodel->memgroup, l->filelen + 1); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); + loadmodel->entities[l->filelen] = 0; + } + else + loadmodel->entitiescrc = QCRC_Block(loadmodel->entities, strlen(loadmodel->entities)); } @@ -2107,7 +2157,7 @@ void Mod_LoadEntities (lump_t *l) Mod_LoadVertexes ================= */ -qboolean Mod_LoadVertexes (lump_t *l) +qboolean Mod_LoadVertexes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { dvertex_t *in; mvertex_t *out; @@ -2141,7 +2191,7 @@ Mod_LoadSubmodels ================= */ static qboolean hexen2map; -qboolean Mod_LoadSubmodels (lump_t *l) +qboolean Mod_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, lump_t *l) { dq1model_t *inq; dh2model_t *inh; @@ -2236,7 +2286,7 @@ qboolean Mod_LoadSubmodels (lump_t *l) Mod_LoadEdges ================= */ -qboolean Mod_LoadEdges (lump_t *l, qboolean lm) +qboolean Mod_LoadEdges (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) { medge_t *out; int i, count; @@ -2290,7 +2340,7 @@ qboolean Mod_LoadEdges (lump_t *l, qboolean lm) Mod_LoadTexinfo ================= */ -qboolean Mod_LoadTexinfo (lump_t *l) +qboolean Mod_LoadTexinfo (model_t *loadmodel, qbyte *mod_base, lump_t *l) { texinfo_t *in; mtexinfo_t *out; @@ -2360,7 +2410,7 @@ Fills in s->texturemins[] and s->extents[] ================ */ -void CalcSurfaceExtents (msurface_t *s); +void CalcSurfaceExtents (model_t *mod, msurface_t *s); /* { float mins[2], maxs[2], val; @@ -2414,7 +2464,7 @@ void CalcSurfaceExtents (msurface_t *s); Mod_LoadFaces ================= */ -qboolean Mod_LoadFaces (lump_t *l, qboolean lm, mesh_t **meshlist) +qboolean Mod_LoadFaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) { dsface_t *ins; dlface_t *inl; @@ -2447,7 +2497,7 @@ qboolean Mod_LoadFaces (lump_t *l, qboolean lm, mesh_t **meshlist) } out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - *meshlist = ZG_Malloc(&loadmodel->memgroup, count*sizeof(**meshlist)); +// *meshlist = ZG_Malloc(&loadmodel->memgroup, count*sizeof(**meshlist)); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for ( surfnum=0 ; surfnumplane = loadmodel->planes + planenum; if (tn < 0 || tn >= loadmodel->numtexinfo) - Host_EndGame("Hey! That map has texinfos out of bounds!\n"); + { + Con_Printf("texinfo 0 <= %i < %i\n", tn, loadmodel->numtexinfo); + return false; + } out->texinfo = loadmodel->texinfo + tn; - CalcSurfaceExtents (out); + CalcSurfaceExtents (loadmodel, out); if (lofs == -1) out->samples = NULL; else if ((loadmodel->engineflags & MDLF_RGBLIGHTING) && loadmodel->fromgame != fg_halflife) @@ -2624,7 +2677,7 @@ void ModQ1_Batches_BuildQ1Q2Poly(model_t *mod, msurface_t *surf, void *cookie) } } -static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindicies, void (*build)(model_t *mod, msurface_t *surf, void *cookie), void *buildcookie) +static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindicies, void (*build)(model_t *mod, msurface_t *surf, builddata_t *bd), builddata_t *bd) { batch_t *batch; msurface_t *surf; @@ -2639,32 +2692,33 @@ static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindi char *ptr; memset(&vbo, 0, sizeof(vbo)); - vbo.indicies.dummy = ZG_Malloc(&loadmodel->memgroup, sizeof(index_t) * maxindicies); - ptr = ZG_Malloc(&loadmodel->memgroup, (sizeof(vecV_t)+sizeof(vec2_t)*(1+styles)+sizeof(vec3_t)*3+sizeof(vec4_t)*styles)* maxverts); - vbo.coord.dummy = ptr; + vbo.indicies.sysptr = ZG_Malloc(&mod->memgroup, sizeof(index_t) * maxindicies); + ptr = ZG_Malloc(&mod->memgroup, (sizeof(vecV_t)+sizeof(vec2_t)*(1+styles)+sizeof(vec3_t)*3+sizeof(vec4_t)*styles)* maxverts); + + vbo.coord.sysptr = ptr; ptr += sizeof(vecV_t)*maxverts; for (sty = 0; sty < styles; sty++) { - vbo.colours[sty].dummy = ptr; + vbo.colours[sty].sysptr = ptr; ptr += sizeof(vec4_t)*maxverts; } for (; sty < MAXRLIGHTMAPS; sty++) - vbo.colours[sty].dummy = NULL; - vbo.texcoord.dummy = ptr; + vbo.colours[sty].sysptr = NULL; + vbo.texcoord.sysptr = ptr; ptr += sizeof(vec2_t)*maxverts; sty = 0; for (; sty < styles; sty++) { - vbo.lmcoord[sty].dummy = ptr; + vbo.lmcoord[sty].sysptr = ptr; ptr += sizeof(vec2_t)*maxverts; } for (; sty < MAXRLIGHTMAPS; sty++) - vbo.lmcoord[sty].dummy = NULL; - vbo.normals.dummy = ptr; + vbo.lmcoord[sty].sysptr = NULL; + vbo.normals.sysptr = ptr; ptr += sizeof(vec3_t)*maxverts; - vbo.svector.dummy = ptr; + vbo.svector.sysptr = ptr; ptr += sizeof(vec3_t)*maxverts; - vbo.tvector.dummy = ptr; + vbo.tvector.sysptr = ptr; ptr += sizeof(vec3_t)*maxverts; numindicies = 0; @@ -2687,28 +2741,28 @@ static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindi numindicies += mesh->numindexes; //set up the arrays. the arrangement is required for the backend to optimise vbos - mesh->xyz_array = (vecV_t*)vbo.coord.dummy + mesh->vbofirstvert; - mesh->st_array = (vec2_t*)vbo.texcoord.dummy + mesh->vbofirstvert; + mesh->xyz_array = (vecV_t*)vbo.coord.sysptr + mesh->vbofirstvert; + mesh->st_array = (vec2_t*)vbo.texcoord.sysptr + mesh->vbofirstvert; for (sty = 0; sty < MAXRLIGHTMAPS; sty++) { - if (vbo.lmcoord[sty].dummy) - mesh->lmst_array[sty] = (vec2_t*)vbo.lmcoord[sty].dummy + mesh->vbofirstvert; + if (vbo.lmcoord[sty].sysptr) + mesh->lmst_array[sty] = (vec2_t*)vbo.lmcoord[sty].sysptr + mesh->vbofirstvert; else mesh->lmst_array[sty] = NULL; - if (vbo.colours[sty].dummy) - mesh->colors4f_array[sty] = (vec4_t*)vbo.colours[sty].dummy + mesh->vbofirstvert; + if (vbo.colours[sty].sysptr) + mesh->colors4f_array[sty] = (vec4_t*)vbo.colours[sty].sysptr + mesh->vbofirstvert; else mesh->colors4f_array[sty] = NULL; } - mesh->normals_array = (vec3_t*)vbo.normals.dummy + mesh->vbofirstvert; - mesh->snormals_array = (vec3_t*)vbo.svector.dummy + mesh->vbofirstvert; - mesh->tnormals_array = (vec3_t*)vbo.tvector.dummy + mesh->vbofirstvert; - mesh->indexes = (index_t*)vbo.indicies.dummy + mesh->vbofirstelement; + mesh->normals_array = (vec3_t*)vbo.normals.sysptr + mesh->vbofirstvert; + mesh->snormals_array = (vec3_t*)vbo.svector.sysptr + mesh->vbofirstvert; + mesh->tnormals_array = (vec3_t*)vbo.tvector.sysptr + mesh->vbofirstvert; + mesh->indexes = (index_t*)vbo.indicies.sysptr + mesh->vbofirstelement; mesh->vbofirstvert = 0; mesh->vbofirstelement = 0; - build(mod, surf, buildcookie); + build(mod, surf, bd); } batch->meshes = 0; batch->firstmesh = 0; @@ -2852,7 +2906,7 @@ static void Mod_Batches_Generate(model_t *mod) } if (!batch) { - batch = ZG_Malloc(&loadmodel->memgroup, sizeof(*batch)); + batch = ZG_Malloc(&mod->memgroup, sizeof(*batch)); batch->lightmap[0] = surf->lightmaptexturenums[0]; #if MAXRLIGHTMAPS > 1 batch->lightmap[1] = surf->lightmaptexturenums[1]; @@ -3008,7 +3062,7 @@ static void Mod_Batches_SplitLightmaps(model_t *mod) } if (sty < MAXRLIGHTMAPS) { - nb = ZG_Malloc(&loadmodel->memgroup, sizeof(*batch)); + nb = ZG_Malloc(&mod->memgroup, sizeof(*batch)); *nb = *batch; batch->next = nb; @@ -3073,7 +3127,7 @@ static void Mod_Batches_AllocLightmaps(model_t *mod) surf->lightmaptexturenums[sty] = -1; if (surf->lightmaptexturenums[0] != batch->lightmap[0]) { - nb = ZG_Malloc(&loadmodel->memgroup, sizeof(*batch)); + nb = ZG_Malloc(&mod->memgroup, sizeof(*batch)); *nb = *batch; batch->next = nb; @@ -3102,7 +3156,7 @@ static void Mod_Batches_AllocLightmaps(model_t *mod) extern void Surf_CreateSurfaceLightmap (msurface_t *surf, int shift); //if build is NULL, uses q1/q2 surf generation, and allocates lightmaps -void Mod_Batches_Build(mesh_t *meshlist, model_t *mod, void (*build)(model_t *mod, msurface_t *surf, void *cookie), void *buildcookie) +void Mod_Batches_Build(model_t *mod, builddata_t *bd) { int i; int numverts = 0, numindicies=0; @@ -3111,16 +3165,17 @@ void Mod_Batches_Build(mesh_t *meshlist, model_t *mod, void (*build)(model_t *mo mesh_t **bmeshes; int sortid; batch_t *batch; + mesh_t *meshlist; currentmodel = mod; if (!mod->textures) return; - if (meshlist) - meshlist += mod->firstmodelsurface; - else if (!build) - meshlist = ZG_Malloc(&loadmodel->memgroup, sizeof(mesh_t) * mod->nummodelsurfaces); + if (bd) + meshlist = NULL; + else + meshlist = ZG_Malloc(&mod->memgroup, sizeof(mesh_t) * mod->nummodelsurfaces); for (i=0; inummodelsurfaces; i++) { @@ -3142,7 +3197,7 @@ void Mod_Batches_Build(mesh_t *meshlist, model_t *mod, void (*build)(model_t *mo /*assign each mesh to a batch, generating as needed*/ Mod_Batches_Generate(mod); - bmeshes = ZG_Malloc(&loadmodel->memgroup, sizeof(*bmeshes)*mod->nummodelsurfaces*R_MAX_RECURSE); + bmeshes = ZG_Malloc(&mod->memgroup, sizeof(*bmeshes)*mod->nummodelsurfaces*R_MAX_RECURSE); //we now know which batch each surface is in, and how many meshes there are in each batch. //allocate the mesh-pointer-lists for each batch. *2 for recursion. @@ -3158,17 +3213,18 @@ void Mod_Batches_Build(mesh_t *meshlist, model_t *mod, void (*build)(model_t *mo surf = mod->surfaces + mod->firstmodelsurface + i; surf->sbatch->mesh[surf->sbatch->meshes++] = (mesh_t*)surf; } - if (build) + if (bd) //q3 Mod_Batches_SplitLightmaps(mod); else Mod_Batches_AllocLightmaps(mod); - if (!build) + if (!bd) { - build = ModQ1_Batches_BuildQ1Q2Poly; mod->lightmaps.surfstyles = 1; + Mod_Batches_BuildModelMeshes(mod, numverts, numindicies, ModQ1_Batches_BuildQ1Q2Poly, bd); } - Mod_Batches_BuildModelMeshes(mod, numverts, numindicies, build, buildcookie); + else + Mod_Batches_BuildModelMeshes(mod, numverts, numindicies, bd->buildfunc, bd); if (BE_GenBrushModelVBO) BE_GenBrushModelVBO(mod); @@ -3196,7 +3252,7 @@ void Mod_SetParent (mnode_t *node, mnode_t *parent) Mod_LoadNodes ================= */ -qboolean Mod_LoadNodes (lump_t *l, int lm) +static qboolean Mod_LoadNodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm) { int i, j, count, p; mnode_t *out; @@ -3328,7 +3384,7 @@ qboolean Mod_LoadNodes (lump_t *l, int lm) Mod_LoadLeafs ================= */ -qboolean Mod_LoadLeafs (lump_t *l, int lm, qbyte *ptr, size_t len) +static qboolean Mod_LoadLeafs (model_t *loadmodel, qbyte *mod_base, lump_t *l, int lm, qbyte *ptr, size_t len) { mleaf_t *out; int i, j, count, p; @@ -3542,7 +3598,7 @@ void *suplementryclipnodes; void *suplementryplanes; void *crouchhullfile; -void Mod_LoadCrouchHull(void) +void Mod_LoadCrouchHull(model_t *loadmodel) { int i, h; int numsm; @@ -3613,7 +3669,7 @@ void Mod_LoadCrouchHull(void) Mod_LoadClipnodes ================= */ -qboolean Mod_LoadClipnodes (lump_t *l, qboolean lm) +qboolean Mod_LoadClipnodes (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) { dsclipnode_t *ins; dlclipnode_t *inl; @@ -3862,7 +3918,7 @@ Mod_MakeHull0 Deplicate the drawing hull structure as a clipping hull ================= */ -void Mod_MakeHull0 (void) +void Mod_MakeHull0 (model_t *loadmodel) { mnode_t *in, *child; mclipnode_t *out; @@ -3899,7 +3955,7 @@ void Mod_MakeHull0 (void) Mod_LoadMarksurfaces ================= */ -qboolean Mod_LoadMarksurfaces (lump_t *l, qboolean lm) +qboolean Mod_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, lump_t *l, qboolean lm) { int i, j, count; msurface_t **out; @@ -3965,7 +4021,7 @@ qboolean Mod_LoadMarksurfaces (lump_t *l, qboolean lm) Mod_LoadSurfedges ================= */ -qboolean Mod_LoadSurfedges (lump_t *l) +qboolean Mod_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, count; int *in, *out; @@ -3994,7 +4050,7 @@ qboolean Mod_LoadSurfedges (lump_t *l) Mod_LoadPlanes ================= */ -qboolean Mod_LoadPlanes (lump_t *l) +qboolean Mod_LoadPlanes (model_t *loadmodel, qbyte *mod_base, lump_t *l) { int i, j; mplane_t *out; @@ -4122,7 +4178,7 @@ void Mod_FixupNodeMinsMaxs (mnode_t *node, mnode_t *parent) } -static void Mod_FixupMinsMaxs(void) +static void Mod_FixupMinsMaxs(model_t *loadmodel) { //q1 bsps are capped to +/- 32767 by the nodes/leafs //verts arn't though @@ -4208,6 +4264,57 @@ static void Mod_FixupMinsMaxs(void) Mod_FixupNodeMinsMaxs (loadmodel->nodes, NULL); // sets nodes and leafs } +void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b) +{ +#ifndef SERVERONLY + model_t *mod = ctx; + + if (!a) + { //submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world). + for (a = 0; a < mod->numfogs; a++) + { + mod->fogs[a].shader = R_RegisterShader_Lightmap(mod->fogs[a].shadername); + R_BuildDefaultTexnums(&mod->fogs[a].shader->defaulttextures, mod->fogs[a].shader); + if (!mod->fogs[a].shader->fog_dist) + { + //invalid fog shader, don't use. + mod->fogs[a].shader = NULL; + mod->fogs[a].numplanes = 0; + } + } + + if (mod->fromgame == fg_quake3) + { + for(a = 0; a < mod->numtexinfo; a++) + { + mod->textures[a]->shader = R_RegisterShader_Lightmap(mod->textures[a]->name); + R_BuildDefaultTexnums(&mod->textures[a]->texnums, mod->textures[a]->shader); + + mod->textures[a+mod->numtexinfo]->shader = R_RegisterShader_Vertex (mod->textures[a+mod->numtexinfo]->name); + R_BuildDefaultTexnums(&mod->textures[a+mod->numtexinfo]->texnums, mod->textures[a+mod->numtexinfo]->shader); + } + mod->textures[2*mod->numtexinfo]->shader = R_RegisterShader_Flare("noshader"); + } + else if (mod->fromgame == fg_quake2) + { + for(a = 0; a < mod->numtextures; a++) + { + mod->textures[a]->shader = R_RegisterCustom (mod->textures[a]->name, SUF_LIGHTMAP, Shader_DefaultBSPQ2, NULL); + R_BuildDefaultTexnums(&mod->textures[a]->texnums, mod->textures[a]->shader); + } + } + else + { + for(a = 0; a < mod->numtextures; a++) + Mod_FinishTexture(mod->textures[a]); + } + } + Mod_Batches_Build(mod, data); + if (data) + BZ_Free(data); +#endif +} + struct vispatch_s { void *fileptr; @@ -4220,7 +4327,7 @@ struct vispatch_s int leaflen; }; -void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaflumpsize) +static void Mod_FindVisPatch(struct vispatch_s *patch, model_t *mod, size_t leaflumpsize) { char patchname[MAX_QPATH]; int *lenptr, len; @@ -4292,18 +4399,20 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) int i, j; dheader_t *header; mmodel_t *bm; - model_t *lm=mod; + model_t *submod; unsigned int chksum; qboolean noerrors; int longm = false; - mesh_t *meshlist = NULL; + char loadname[32]; + qbyte *mod_base = buffer; #if (defined(ODE_STATIC) || defined(ODE_DYNAMIC)) qboolean ode = true; #else #define ode true #endif - loadmodel->type = mod_brush; + COM_FileBase (mod->name, loadname, sizeof(loadname)); + mod->type = mod_brush; header = (dheader_t *)buffer; @@ -4312,7 +4421,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) #else if ((!cl.worldmodel && cls.state>=ca_connected) #ifndef CLIENTONLY - || (!sv.world.worldmodel && sv.active) + || (!sv.world.worldmodel && sv.state) #endif ) isnotmap = false; @@ -4324,23 +4433,23 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) if (i == BSPVERSION || i == BSPVERSIONPREREL) { - loadmodel->fromgame = fg_quake; - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->fromgame = fg_quake; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; } else if (i == BSPVERSION_LONG1) { longm = true; - loadmodel->fromgame = fg_quake; - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->fromgame = fg_quake; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; } else if (i == BSPVERSION_LONG2) { longm = 2; - loadmodel->fromgame = fg_quake; - loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT; + mod->fromgame = fg_quake; + mod->engineflags |= MDLF_NEEDOVERBRIGHT; } else if (i == BSPVERSIONHL) //halflife support - loadmodel->fromgame = fg_halflife; + mod->fromgame = fg_halflife; else { Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)\n", mod->name, i, BSPVERSION); @@ -4356,7 +4465,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) for (i=0 ; ilumps, HEADER_LUMPS); + Q1BSPX_Setup(mod, mod_base, fsize, header->lumps, HEADER_LUMPS); // checksum all of the map, except for entities mod->checksum = 0; @@ -4364,7 +4473,7 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) for (i = 0; i < HEADER_LUMPS; i++) { - if ((unsigned)header->lumps[i].fileofs + (unsigned)header->lumps[i].filelen > com_filesize) + if ((unsigned)header->lumps[i].fileofs + (unsigned)header->lumps[i].filelen > fsize) { Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s appears truncated\n", mod->name); return false; @@ -4393,74 +4502,74 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) crouchhullfile = NULL; - Mod_FindVisPatch(&vispatch, loadmodel, header->lumps[LUMP_LEAFS].filelen); + Mod_FindVisPatch(&vispatch, mod, header->lumps[LUMP_LEAFS].filelen); TRACE(("Loading info\n")); #ifndef SERVERONLY if (!isnotmap) - Mod_ParseInfoFromEntityLump(loadmodel, mod_base + header->lumps[LUMP_ENTITIES].fileofs, loadname); + Mod_ParseInfoFromEntityLump(mod, mod_base + header->lumps[LUMP_ENTITIES].fileofs, loadname); #endif // load into heap if (!isDedicated || ode) { TRACE(("Loading verts\n")); - noerrors = noerrors && Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + noerrors = noerrors && Mod_LoadVertexes (mod, mod_base, &header->lumps[LUMP_VERTEXES]); TRACE(("Loading edges\n")); - noerrors = noerrors && Mod_LoadEdges (&header->lumps[LUMP_EDGES], longm); + noerrors = noerrors && Mod_LoadEdges (mod, mod_base, &header->lumps[LUMP_EDGES], longm); TRACE(("Loading Surfedges\n")); - noerrors = noerrors && Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + noerrors = noerrors && Mod_LoadSurfedges (mod, mod_base, &header->lumps[LUMP_SURFEDGES]); } if (!isDedicated) { TRACE(("Loading Textures\n")); - noerrors = noerrors && Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + noerrors = noerrors && Mod_LoadTextures (mod, mod_base, &header->lumps[LUMP_TEXTURES], loadname); TRACE(("Loading Lighting\n")); if (noerrors) - Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + Mod_LoadLighting (mod, mod_base, &header->lumps[LUMP_LIGHTING]); } TRACE(("Loading Submodels\n")); - noerrors = noerrors && Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + noerrors = noerrors && Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); if (noerrors) { TRACE(("Loading CH\n")); - Mod_LoadCrouchHull(); + Mod_LoadCrouchHull(mod); } TRACE(("Loading Planes\n")); - noerrors = noerrors && Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); + noerrors = noerrors && Mod_LoadPlanes (mod, mod_base, &header->lumps[LUMP_PLANES]); if (!isDedicated || ode) { TRACE(("Loading Texinfo\n")); - noerrors = noerrors && Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + noerrors = noerrors && Mod_LoadTexinfo (mod, mod_base, &header->lumps[LUMP_TEXINFO]); TRACE(("Loading Faces\n")); - noerrors = noerrors && Mod_LoadFaces (&header->lumps[LUMP_FACES], longm, &meshlist); + noerrors = noerrors && Mod_LoadFaces (mod, mod_base, &header->lumps[LUMP_FACES], longm); } if (!isDedicated) { TRACE(("Loading MarkSurfaces\n")); - noerrors = noerrors && Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], longm); + noerrors = noerrors && Mod_LoadMarksurfaces (mod, mod_base, &header->lumps[LUMP_MARKSURFACES], longm); } if (noerrors) { TRACE(("Loading Vis\n")); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen); + Mod_LoadVisibility (mod, mod_base, &header->lumps[LUMP_VISIBILITY], vispatch.visptr, vispatch.vislen); } - noerrors = noerrors && Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], longm, vispatch.leafptr, vispatch.leaflen); + noerrors = noerrors && Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS], longm, vispatch.leafptr, vispatch.leaflen); TRACE(("Loading Nodes\n")); - noerrors = noerrors && Mod_LoadNodes (&header->lumps[LUMP_NODES], longm); + noerrors = noerrors && Mod_LoadNodes (mod, mod_base, &header->lumps[LUMP_NODES], longm); TRACE(("Loading Clipnodes\n")); - noerrors = noerrors && Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], longm); + noerrors = noerrors && Mod_LoadClipnodes (mod, mod_base, &header->lumps[LUMP_CLIPNODES], longm); if (noerrors) { TRACE(("Loading Entities\n")); - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadEntities (mod, mod_base, &header->lumps[LUMP_ENTITIES]); TRACE(("Loading hull 0\n")); - Mod_MakeHull0 (); + Mod_MakeHull0 (mod); } TRACE(("sorting shaders\n")); if (!isDedicated && noerrors) - Mod_SortShaders(); + Mod_SortShaders(mod); if (crouchhullfile) { @@ -4492,75 +4601,79 @@ qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize) // // set up the submodels (FIXME: this is confusing) // - for (i=0 ; inumsubmodels ; i++) + for (i=0, submod = mod; inumsubmodels ; i++) { bm = &mod->submodels[i]; - mod->rootnode = mod->nodes + bm->headnode[0]; - mod->hulls[0].firstclipnode = bm->headnode[0]; - mod->hulls[0].available = true; - Q1BSP_CheckHullNodes(&mod->hulls[0]); + submod->rootnode = submod->nodes + bm->headnode[0]; + submod->hulls[0].firstclipnode = bm->headnode[0]; + submod->hulls[0].available = true; + Q1BSP_CheckHullNodes(&submod->hulls[0]); TRACE(("LoadBrushModel %i\n", __LINE__)); for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; + submod->hulls[j].firstclipnode = bm->headnode[j]; + submod->hulls[j].lastclipnode = submod->numclipnodes-1; - mod->hulls[j].available &= bm->hullavailable[j]; - if (mod->hulls[j].firstclipnode > mod->hulls[j].lastclipnode) - mod->hulls[j].available = false; + submod->hulls[j].available &= bm->hullavailable[j]; + if (submod->hulls[j].firstclipnode > submod->hulls[j].lastclipnode) + submod->hulls[j].available = false; - if (mod->hulls[j].available) - Q1BSP_CheckHullNodes(&mod->hulls[j]); + if (submod->hulls[j].available) + Q1BSP_CheckHullNodes(&submod->hulls[j]); } - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; + submod->firstmodelsurface = bm->firstface; + submod->nummodelsurfaces = bm->numfaces; - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); + VectorCopy (bm->maxs, submod->maxs); + VectorCopy (bm->mins, submod->mins); - mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + submod->radius = RadiusFromBounds (submod->mins, submod->maxs); - mod->numclusters = bm->visleafs; + submod->numclusters = bm->visleafs; - memset(&mod->batches, 0, sizeof(mod->batches)); - mod->vbos = NULL; + memset(&submod->batches, 0, sizeof(submod->batches)); + submod->vbos = NULL; TRACE(("LoadBrushModel %i\n", __LINE__)); - if (meshlist) + if (!isDedicated || ode) { -#ifndef SERVERONLY - Mod_Batches_Build(meshlist, mod, NULL, NULL); -#endif + COM_AddWork(0, ModBrush_LoadGLStuff, submod, NULL, i, 0); } TRACE(("LoadBrushModel %i\n", __LINE__)); - if (i < mod->numsubmodels-1) + if (i) + COM_AddWork(0, Mod_ModelLoaded, submod, NULL, MLS_LOADED, 0); + if (i < submod->numsubmodels-1) { // duplicate the basic information - char name[10]; + char name[MAX_QPATH]; + model_t *nextmod; - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - memset(&mod->memgroup, 0, sizeof(mod->memgroup)); +// if (isnotmap) + Q_snprintfz (name, sizeof(name), "*%i:%s", i+1, mod->name); +// else//FIXME: this can bug out if we've still got one of these queued from a previous map change +// Q_snprintfz (name, sizeof(name), "*%i", i+1); + nextmod = Mod_FindName (name); + *nextmod = *submod; + Q_strncpyz(nextmod->name, name, sizeof(nextmod->name)); + submod = nextmod; + memset(&submod->memgroup, 0, sizeof(submod->memgroup)); } TRACE(("LoadBrushModel %i\n", __LINE__)); } #ifdef RUNTIMELIGHTING TRACE(("LoadBrushModel %i\n", __LINE__)); - if (lightmodel == lm) + if (lightmodel == mod) LightLoadEntities(lightmodel->entities); #endif TRACE(("LoadBrushModel %i\n", __LINE__)); if (!isDedicated) - Mod_FixupMinsMaxs(); + Mod_FixupMinsMaxs(mod); TRACE(("LoadBrushModel %i\n", __LINE__)); #ifdef TERRAIN - lm->terrain = Mod_LoadTerrainInfo(lm, loadname, false); + mod->terrain = Mod_LoadTerrainInfo(mod, loadname, false); #endif return true; } @@ -4568,26 +4681,151 @@ TRACE(("LoadBrushModel %i\n", __LINE__)); /* ============================================================================== -ALIAS MODELS +SPRITES ============================================================================== */ //========================================================= +//we need to override the rtlight shader for sprites so they get lit properly ignoring n+s+t dirs +//so lets split the shader into parts to avoid too many dupes +#define SPRITE_SHADER_MAIN \ + "{\n" \ + "if gl_blendsprites\n" \ + "program defaultsprite\n" \ + "else\n" \ + "program defaultsprite#MASK=1\n" \ + "endif\n" \ + "{\n" \ + "map $diffuse\n" \ + "if gl_blendsprites\n" \ + "blendfunc GL_SRC_ALPHA GL_ONE\n" \ + "else\n" \ + "alphafunc ge128\n" \ + "depthwrite\n" \ + "endif\n" \ + "rgbgen vertex\n" \ + "alphagen vertex\n" \ + "}\n" \ + "surfaceparm noshadows\n" +#define SPRITE_SHADER_UNLIT "surfaceparm nodlight\n" +#define SPRITE_SHADER_LIT \ + "sort seethrough\n" \ + "bemode rtlight\n" \ + "{\n" \ + "program rtlight#NOBUMP\n" \ + "{\n" \ + "map $diffuse\n" \ + "blendfunc add\n" \ + "}\n" \ + "{\n" \ + "map $null\n" /*normalmap*/ \ + "}\n" \ + "{\n" \ + "map $null\n" /*specular*/ \ + "}\n" \ + "{\n" \ + "map $lightcubemap\n" \ + "}\n" \ + "{\n" \ + "map $shadowmap\n" \ + "}\n" \ + "{\n" \ + "map $null\n" /*lower*/ \ + "}\n" \ + "{\n" \ + "map $null\n" /*upper*/ \ + "}\n" \ + "}\n" +#define SPRITE_SHADER_FOOTER "}\n" + +void Mod_LoadSpriteFrameShader(model_t *spr, int frame, int subframe, mspriteframe_t *frameinfo) +{ +#ifndef SERVERONLY + /* + A quick note on tenebrae and sprites: In tenebrae, sprites are always lit, unless the light_lev field is set (which makes it fullbright). + While its generally preferable and more consistent to assume lit sprites, this is incompatible with vanilla quake and thus unacceptable to us, but you can set the mod_assumelitsprites cvar if you want it. + So for better compatibility, we have a whitelist of 'well-known' sprites that tenebrae uses in this way, which we do lighting on. + You should still be able to use EF_FULLBRIGHT on these, but light_lev is an imprecise setting and will result in issues. Just be specific about fullbright or additive. + DP on the other hand, supports lit sprites only when the sprite contains a ! in its name. We support that too. + */ + char *forcelitsprites[] = + { + "progs/smokepuff.spr", + NULL + }; + int i; + char *shadertext; + char name[MAX_QPATH]; + + if (qrenderer == QR_NONE) + return; + + if (subframe == -1) + Q_snprintfz(name, sizeof(name), "%s_%i.tga", spr->name, frame); + else + Q_snprintfz(name, sizeof(name), "%s_%i_%i.tga", spr->name, frame, subframe); + + if (mod_litsprites.ival || strchr(spr->name, '!')) + i = -1; + else + { + for (i = 0; forcelitsprites[i]; i++) + if (!strcmp(spr->name, forcelitsprites[i])) + { + i = -1; + break; + } + } + + if (i == -1) // a ! in the filename makes it non-fullbright (and can also be lit by rtlights too). + shadertext = SPRITE_SHADER_MAIN SPRITE_SHADER_LIT SPRITE_SHADER_FOOTER; + else + shadertext = SPRITE_SHADER_MAIN SPRITE_SHADER_UNLIT SPRITE_SHADER_FOOTER; + frameinfo->shader = R_RegisterShader(name, SUF_NONE, shadertext); + frameinfo->shader->defaulttextures.base = frameinfo->image; + frameinfo->shader->width = frameinfo->right-frameinfo->left; + frameinfo->shader->height = frameinfo->up-frameinfo->down; +#endif +} +void Mod_LoadSpriteShaders(model_t *spr) +{ + msprite_t *psprite = spr->meshinfo; + int i, j; + mspritegroup_t *group; + + for (i = 0; i < psprite->numframes; i++) + { + switch (psprite->frames[i].type) + { + case SPR_SINGLE: + Mod_LoadSpriteFrameShader(spr, i, -1, psprite->frames[i].frameptr); + break; + case SPR_ANGLED: + case SPR_GROUP: + group = (mspritegroup_t *)psprite->frames[i].frameptr; + for (j = 0; j < group->numframes; j++) + Mod_LoadSpriteFrameShader(spr, i, j, group->frames[j]); + break; + } + } +} + #ifdef SPRMODELS /* ================= Mod_LoadSpriteFrame ================= */ -static void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, int version, unsigned char *palette) +static void * Mod_LoadSpriteFrame (model_t *mod, void *pin, void *pend, mspriteframe_t **ppframe, int framenum, int version, unsigned char *palette) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int width, height, size, origin[2]; char name[64]; - texid_t texnum; + uploadfmt_t lowresfmt; + void *dataptr; pinframe = (dspriteframe_t *)pin; @@ -4595,7 +4833,7 @@ static void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int fra height = LittleLong (pinframe->height); size = width * height; - pspriteframe = ZG_Malloc(&loadmodel->memgroup, sizeof (mspriteframe_t)); + pspriteframe = ZG_Malloc(&mod->memgroup, sizeof (mspriteframe_t)); Q_memset (pspriteframe, 0, sizeof (mspriteframe_t)); @@ -4609,65 +4847,27 @@ static void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int fra pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; - texnum = r_nulltex; - - if (!TEXVALID(texnum)) - { //the dp way - Q_strncpyz(name, loadmodel->name, sizeof(name)); - Q_strncatz(name, va("_%i.tga", framenum), sizeof(name)); - texnum = R_LoadReplacementTexture(name, "sprites", 0); - } - if (!TEXVALID(texnum)) - { //the older fte way. - COM_StripExtension(loadmodel->name, name, sizeof(name)); - Q_strncatz(name, va("_%i.tga", framenum), sizeof(name)); - texnum = R_LoadReplacementTexture(name, "sprites", 0); - } - if (!TEXVALID(texnum)) - { //the fuhquake way - COM_StripExtension(COM_SkipPath(loadmodel->name), name, sizeof(name)); - Q_strncatz(name, va("_%i.tga", framenum), sizeof(name)); - texnum = R_LoadReplacementTexture(name, "sprites", 0); - } + dataptr = (pinframe + 1); if (version == SPRITE32_VERSION) { size *= 4; - if (!TEXVALID(texnum)) - texnum = R_LoadTexture32 (name, width, height, (unsigned *)(pinframe + 1), IF_NOGAMMA|IF_CLAMP); + lowresfmt = TF_RGBA32; } else if (version == SPRITEHL_VERSION) - { - if (!TEXVALID(texnum)) - texnum = R_LoadTexture8Pal32 (name, width, height, (qbyte *)(pinframe + 1), (qbyte*)palette, IF_NOGAMMA|IF_CLAMP); - } + lowresfmt = TF_8PAL32; else + lowresfmt = TF_TRANS8; + + if ((qbyte*)dataptr + size > (qbyte*)pend) { - if (!TEXVALID(texnum)) - texnum = R_LoadTexture8 (name, width, height, (qbyte *)(pinframe + 1), IF_NOMIPMAP|IF_NOGAMMA|IF_CLAMP, 1); + //tenebrae has a couple of dodgy truncated sprites. yay for replacement textures. + dataptr = NULL; + lowresfmt = TF_INVALID; } - Q_strncpyz(name, loadmodel->name, sizeof(name)); - Q_strncatz(name, va("_%i.tga", framenum), sizeof(name)); - pspriteframe->shader = R_RegisterShader(name, SUF_NONE, - "{\n" - "if gl_blendsprites\n" - "program defaultsprite\n" - "else\n" - "program defaultsprite#MASK=1\n" - "endif\n" - "{\n" - "map $diffuse\n" - "alphafunc ge128\n" - "depthwrite\n" - "rgbgen vertex\n" - "alphagen vertex\n" - "}\n" - "}\n" - ); - pspriteframe->shader->defaulttextures.base = texnum; - pspriteframe->shader->width = width; - pspriteframe->shader->height = height; + Q_snprintfz(name, sizeof(name), "%s_%i.tga", mod->name, framenum); + pspriteframe->image = Image_GetTexture(name, "sprites", IF_NOMIPMAP|IF_NOGAMMA|IF_CLAMP, dataptr, palette, width, height, lowresfmt); return (void *)((qbyte *)(pinframe+1) + size); } @@ -4678,7 +4878,7 @@ static void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int fra Mod_LoadSpriteGroup ================= */ -static void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, int version, unsigned char *palette) +static void * Mod_LoadSpriteGroup (model_t *mod, void * pin, void *pend, mspriteframe_t **ppframe, int framenum, int version, unsigned char *palette) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; @@ -4691,7 +4891,7 @@ static void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int fra numframes = LittleLong (pingroup->numframes); - pspritegroup = ZG_Malloc(&loadmodel->memgroup, sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0])); + pspritegroup = ZG_Malloc(&mod->memgroup, sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0])); pspritegroup->numframes = numframes; @@ -4699,7 +4899,7 @@ static void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int fra pin_intervals = (dspriteinterval_t *)(pingroup + 1); - poutintervals = ZG_Malloc(&loadmodel->memgroup, numframes * sizeof (float)); + poutintervals = ZG_Malloc(&mod->memgroup, numframes * sizeof (float)); pspritegroup->intervals = poutintervals; @@ -4720,7 +4920,7 @@ static void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int fra for (i=0 ; iframes[i], framenum * 100 + i, version, palette); + ptemp = Mod_LoadSpriteFrame (mod, ptemp, pend, &pspritegroup->frames[i], framenum * 100 + i, version, palette); } return ptemp; @@ -4768,7 +4968,7 @@ qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize) size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); - psprite = ZG_Malloc(&loadmodel->memgroup, size); + psprite = ZG_Malloc(&mod->memgroup, size); mod->meshinfo = psprite; switch(sptype) @@ -4865,13 +5065,13 @@ qboolean QDECL Mod_LoadSpriteModel (model_t *mod, void *buffer, size_t fsize) if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) - Mod_LoadSpriteFrame (pframetype + 1, + Mod_LoadSpriteFrame (mod, pframetype + 1, (qbyte*)buffer + fsize, &psprite->frames[i].frameptr, i, version, pal); } else { pframetype = (dspriteframetype_t *) - Mod_LoadSpriteGroup (pframetype + 1, + Mod_LoadSpriteGroup (mod, pframetype + 1, (qbyte*)buffer + fsize, &psprite->frames[i].frameptr, i, version, pal); } if (pframetype == NULL) @@ -4899,6 +5099,7 @@ qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize) mspriteframe_t *frame; int w, h; float origin[2]; + pin = (dmd2sprite_t *)buffer; @@ -4914,7 +5115,7 @@ qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize) size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); - psprite = ZG_Malloc(&loadmodel->memgroup, size); + psprite = ZG_Malloc(&mod->memgroup, size); mod->meshinfo = psprite; @@ -4950,9 +5151,9 @@ qboolean QDECL Mod_LoadSprite2Model (model_t *mod, void *buffer, size_t fsize) frametype = SPR_SINGLE; psprite->frames[i].type = frametype; - frame = psprite->frames[i].frameptr = ZG_Malloc(&loadmodel->memgroup, sizeof(mspriteframe_t)); + frame = psprite->frames[i].frameptr = ZG_Malloc(&mod->memgroup, sizeof(mspriteframe_t)); - frame->shader = R_RegisterPic(pframetype->name); + frame->image = Image_GetTexture(pframetype->name, NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_CLAMP, NULL, NULL, 0, 0, TF_INVALID); w = LittleLong(pframetype->width); h = LittleLong(pframetype->height); @@ -4993,7 +5194,7 @@ static int QDECL FindDoomSprites(const char *name, qofs_t size, void *param, sea } -static void LoadDoomSpriteFrame(char *imagename, mspriteframedesc_t *pdesc, int anglenum, qboolean xmirrored) +static void LoadDoomSpriteFrame(model_t *mod, char *imagename, mspriteframedesc_t *pdesc, int anglenum, qboolean xmirrored) { int c; int fr; @@ -5010,7 +5211,7 @@ static void LoadDoomSpriteFrame(char *imagename, mspriteframedesc_t *pdesc, int if (!anglenum) { pdesc->type = SPR_SINGLE; - pdesc->frameptr = pframe = ZG_Malloc(&loadmodel->memgroup, sizeof(*pframe)); + pdesc->frameptr = pframe = ZG_Malloc(&mod->memgroup, sizeof(*pframe)); } else { @@ -5019,14 +5220,14 @@ static void LoadDoomSpriteFrame(char *imagename, mspriteframedesc_t *pdesc, int if (!pdesc->frameptr || pdesc->type != SPR_ANGLED) { pdesc->type = SPR_ANGLED; - group = ZG_Malloc(&loadmodel->memgroup, sizeof(*group)+sizeof(mspriteframe_t *)*(8-1)); + group = ZG_Malloc(&mod->memgroup, sizeof(*group)+sizeof(mspriteframe_t *)*(8-1)); pdesc->frameptr = (mspriteframe_t *)group; group->numframes = 8; } else group = (mspritegroup_t *)pdesc->frameptr; - pframe = ZG_Malloc(&loadmodel->memgroup, sizeof(*pframe)); + pframe = ZG_Malloc(&mod->memgroup, sizeof(*pframe)); group->frames[anglenum-1] = pframe; } @@ -5152,7 +5353,7 @@ void Mod_LoadDoomSprite (model_t *mod) Host_Error("Doom sprite componant has no frames"); size = sizeof (msprite_t) + (elements - 1) * sizeof (psprite->frames); - psprite = ZG_Malloc(&loadmodel->memgroup, size); + psprite = ZG_Malloc(&mod->memgroup, size); psprite->numframes = numframes; @@ -5163,14 +5364,14 @@ void Mod_LoadDoomSprite (model_t *mod) framenum = name[baselen+0] - 'a'; anglenum = name[baselen+1] - '0'; - LoadDoomSpriteFrame(name, &psprite->frames[framenum], anglenum, false); + LoadDoomSpriteFrame(mod, name, &psprite->frames[framenum], anglenum, false); if (name[baselen+2]) //is there a second element? { framenum = name[baselen+2] - 'a'; anglenum = name[baselen+3] - '0'; - LoadDoomSpriteFrame(name, &psprite->frames[framenum], anglenum, true); + LoadDoomSpriteFrame(mod, name, &psprite->frames[framenum], anglenum, true); } } diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index fc780835..5622a4d3 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -285,6 +285,7 @@ typedef struct texture_s qbyte alphaed; //gl_blend needed on this surface. struct shader_s *shader; + texnums_t texnums; int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max @@ -344,6 +345,7 @@ typedef struct mtexinfo_s typedef struct mfog_s { + char shadername[MAX_QPATH]; struct shader_s *shader; mplane_t *visibleplane; @@ -516,6 +518,7 @@ typedef struct mspriteframe_s { float up, down, left, right; shader_t *shader; + image_t *image; } mspriteframe_t; mspriteframe_t *R_GetSpriteFrame (entity_t *currententity); @@ -779,32 +782,33 @@ typedef struct { typedef enum {mod_brush, mod_sprite, mod_alias, mod_dummy, mod_halflife, mod_heightmap} modtype_t; typedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom, fg_doom3} fromgame_t; //useful when we have very similar model types. (eg quake/halflife bsps) -#define MF_ROCKET 1 // leave a trail -#define MF_GRENADE 2 // leave a trail -#define MF_GIB 4 // leave a trail -#define MF_ROTATE 8 // rotate (bonus items) -#define MF_TRACER 16 // green split trail -#define MF_ZOMGIB 32 // small blood trail -#define MF_TRACER2 64 // orange split trail + rotate -#define MF_TRACER3 128 // purple trail +#define MF_ROCKET (1u<<0) // leave a trail +#define MF_GRENADE (1u<<1) // leave a trail +#define MF_GIB (1u<<2) // leave a trail +#define MF_ROTATE (1u<<3) // rotate (bonus items) +#define MF_TRACER (1u<<4) // green split trail +#define MF_ZOMGIB (1u<<5) // small blood trail +#define MF_TRACER2 (1u<<6) // orange split trail + rotate +#define MF_TRACER3 (1u<<7) // purple trail //hexen2 support. -#define MFH2_FIREBALL 256 // Yellow transparent trail in all directions -#define MFH2_ICE 512 // Blue-white transparent trail, with gravity -#define MFH2_MIP_MAP 1024 // This model has mip-maps -#define MFH2_SPIT 2048 // Black transparent trail with negative light -#define MFH2_TRANSPARENT 4096 // Transparent sprite -#define MFH2_SPELL 8192 // Vertical spray of particles -#define MFH2_HOLEY 16384 // Solid model with color 0 -#define MFH2_SPECIAL_TRANS 32768 // Translucency through the particle table -#define MFH2_FACE_VIEW 65536 // Poly Model always faces you -#define MFH2_VORP_MISSILE 131072 // leave a trail at top and bottom of model -#define MFH2_SET_STAFF 262144 // slowly move up and left/right -#define MFH2_MAGICMISSILE 524288 // a trickle of blue/white particles with gravity -#define MFH2_BONESHARD 1048576 // a trickle of brown particles with gravity -#define MFH2_SCARAB 2097152 // white transparent particles with little gravity -#define MFH2_ACIDBALL 4194304 // Green drippy acid shit -#define MFH2_BLOODSHOT 8388608 // Blood rain shot trail +#define MFH2_FIREBALL (1u<<8) // Yellow transparent trail in all directions +#define MFH2_ICE (1u<<9) // Blue-white transparent trail, with gravity +#define MFH2_MIP_MAP (1u<<10) // This model has mip-maps +#define MFH2_SPIT (1u<<11) // Black transparent trail with negative light +#define MFH2_TRANSPARENT (1u<<12) // Transparent sprite +#define MFH2_SPELL (1u<<13) // Vertical spray of particles +#define MFH2_HOLEY (1u<<14) // Solid model with color 0 +#define MFH2_SPECIAL_TRANS (1u<<15) // Translucency through the particle table +#define MFH2_FACE_VIEW (1u<<16) // Poly Model always faces you +#define MFH2_VORP_MISSILE (1u<<17) // leave a trail at top and bottom of model +#define MFH2_SET_STAFF (1u<<18) // slowly move up and left/right +#define MFH2_MAGICMISSILE (1u<<19) // a trickle of blue/white particles with gravity +#define MFH2_BONESHARD (1u<<20) // a trickle of brown particles with gravity +#define MFH2_SCARAB (1u<<21) // white transparent particles with little gravity +#define MFH2_ACIDBALL (1u<<22) // Green drippy acid shit +#define MFH2_BLOODSHOT (1u<<23) // Blood rain shot trail +#define MFH2_ROCKET (1u<<31) // spider blood (remapped from MF_ROCKET, to avoid dlight issues) typedef union { struct { @@ -826,11 +830,18 @@ typedef struct vec4_t *points; } portal_t; +enum +{ + MLS_NOTLOADED, + MLS_LOADING, + MLS_LOADED, + MLS_FAILED +}; typedef struct model_s { char name[MAX_QPATH]; int datasequence; - int needload; // if needload is set, the model is not currently valid. 0=false, 1=true, 2 means it was never valid (and please don't spam about it still being invalid). most code should treat this as a simple boolean. + int loadstate;//MLS_ qboolean tainted; qboolean pushdepth; // bsp submodels have this flag set so you don't get z fighting on co-planar surfaces. @@ -910,7 +921,10 @@ typedef struct model_s qbyte *lightdata; qbyte *deluxdata; q3lightgridinfo_t *lightgrid; + mfog_t *fogs; + int numfogs; char *entities; + int entitiescrc; struct doll_s *dollinfo; @@ -972,6 +986,7 @@ typedef struct model_s void Terr_Init(void); void Terr_DrawTerrainModel (batch_t **batch, entity_t *e); void Terr_FreeModel(model_t *mod); +void Terr_FinishTerrain(model_t *model); void Terr_PurgeTerrainModel(model_t *hm, qboolean lightmapsonly, qboolean lightmapreusable); void *Mod_LoadTerrainInfo(model_t *mod, char *loadname, qboolean force); //call this after loading a bsp qboolean Terrain_LocateSection(char *name, flocation_t *loc); //used on servers to generate sections for download. diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c index c0fe61bb..0aeac295 100644 --- a/engine/gl/gl_ngraph.c +++ b/engine/gl/gl_ngraph.c @@ -139,7 +139,7 @@ void R_NetGraph (void) Draw_FunString(8, y, st); y += 8; - R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); + Image_Upload(netgraphtexture, TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); x=8; R2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader); } @@ -172,14 +172,14 @@ void R_FrameTimeGraph (int frametime) y += 8; - R_Upload(netgraphtexture, "***netgraph***", TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); + Image_Upload(netgraphtexture, TF_RGBA32, ngraph_pixels, NULL, NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP|IF_NOPICMIP); x=8; R2D_Image(x, y, NET_TIMINGS, NET_GRAPHHEIGHT, 0, 0, 1, 1, netgraphshader); } void R_NetgraphInit(void) { - TEXASSIGN(netgraphtexture, R_AllocNewTexture("***netgraph***", NET_TIMINGS, NET_GRAPHHEIGHT, IF_UIPIC|IF_NOMIPMAP)); + TEXASSIGN(netgraphtexture, Image_CreateTexture("***netgraph***", NULL, IF_UIPIC|IF_NOMIPMAP)); netgraphshader = R_RegisterShader("netgraph", SUF_NONE, "{\n" "program default2d\n" diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 496ee6e8..e1a35b08 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -75,6 +75,12 @@ void R_AnimateLight (void) if (!cl_lightstyle[j].length) continue; + if (cl_lightstyle[j].map[0] == '=') + { + d_lightstylevalue[j] = atof(cl_lightstyle[j].map+1)*256; + continue; + } + v1 = i % cl_lightstyle[j].length; v1 = cl_lightstyle[j].map[v1] - 'a'; @@ -164,7 +170,7 @@ void R_GenerateFlashblendTexture(void) pixels[y][x][3] = 255; } } - R_LoadTexture32("***flashblend***", 32, 32, pixels, 0); + R_LoadReplacementTexture("***flashblend***", NULL, 0, pixels, 32, 32, TF_RGBA32); } void R_InitFlashblends(void) { @@ -368,7 +374,7 @@ void R_GenDlightMesh(struct batch_s *batch) static mesh_t *meshptr; dlight_t *l = cl_dlights + batch->surf_first; - BE_SelectDLight(l, l->color, 0); + BE_SelectDLight(l, l->color, l->axis, 0); if (!R_BuildDlightMesh (l, 2, 1, 2)) { @@ -686,6 +692,12 @@ void R_ImportRTLights(char *entlump) light[2] = 1; light[3] = atof(value); } + else if (entnum == 0 && !strcmp("noautolight", key)) + { + //tenebrae compat. don't generate rtlights automagically if the world entity specifies this. + if (atoi(value)) + return; + } } if (!islight) continue; @@ -743,7 +755,7 @@ void R_ImportRTLights(char *entlump) dl->lightcolourscales[1] = r_editlights_import_diffuse.value; dl->lightcolourscales[2] = r_editlights_import_specular.value; if (skin >= 16) - snprintf(dl->cubemapname, sizeof(dl->cubemapname), "cubemaps/%i", skin); + R_LoadNumberedLightTexture(dl, skin); } } } @@ -760,6 +772,8 @@ void R_LoadRTLights(void) vec3_t org; float radius; vec3_t rgb; + vec3_t avel; + float fov; unsigned int flags; float coronascale; @@ -774,7 +788,7 @@ void R_LoadRTLights(void) COM_StripExtension(cl.worldmodel->name, fname, sizeof(fname)); strncat(fname, ".rtlights", MAX_QPATH-1); - file = COM_LoadTempFile(fname); + file = COM_LoadTempFile(fname, NULL); if (file) while(1) { @@ -831,7 +845,7 @@ void R_LoadRTLights(void) angles[2] = file?atof(com_token):0; file = COM_Parse(file); - //corrona scale + //corona scale coronascale = file?atof(com_token):0.25; file = COM_Parse(file); @@ -849,6 +863,20 @@ void R_LoadRTLights(void) file = COM_Parse(file); flags |= file?atoi(com_token):LFLAG_REALTIMEMODE; + fov = avel[0] = avel[1] = avel[2] = 0; + while(file) + { + file = COM_Parse(file); + if (!strncmp(com_token, "rotx=", 5)) + avel[0] = file?atof(com_token+5):0; + else if (!strncmp(com_token, "roty=", 5)) + avel[1] = file?atof(com_token+5):0; + else if (!strncmp(com_token, "rotz=", 5)) + avel[2] = file?atof(com_token+5):0; + else if (!strncmp(com_token, "fov=", 4)) + fov = file?atof(com_token+4):0; + } + if (radius) { dl = CL_AllocSlight(); @@ -862,15 +890,16 @@ void R_LoadRTLights(void) dl->coronascale = coronascale; dl->die = 0; dl->flags = flags; + dl->fov = fov; dl->lightcolourscales[0] = ambientscale; dl->lightcolourscales[1] = diffusescale; dl->lightcolourscales[2] = specularscale; - AngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]); - VectorInverse(dl->axis[1]); + AngleVectorsFLU(angles, dl->axis[0], dl->axis[1], dl->axis[2]); + VectorCopy(avel, dl->rotation); Q_strncpyz(dl->cubemapname, cubename, sizeof(dl->cubemapname)); if (*dl->cubemapname) - dl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, "", IF_CUBEMAP); + dl->cubetexture = R_LoadReplacementTexture(dl->cubemapname, "", IF_CUBEMAP, NULL, 0, 0, TF_INVALID); else dl->cubetexture = r_nulltex; @@ -912,6 +941,7 @@ void R_SaveRTLights_f(void) "\"%s\" %f " "%f %f %f " "%f %f %f %f %i " + "rotx=%g roty=%g rotz=%g fov=%g " "\n" , (light->flags & LFLAG_NOSHADOWS)?"!":"", light->origin[0], light->origin[1], light->origin[2], @@ -919,7 +949,8 @@ void R_SaveRTLights_f(void) light->style-1, light->cubemapname, light->corona, ang[0], ang[1], ang[2], - light->coronascale, light->lightcolourscales[0], light->lightcolourscales[1], light->lightcolourscales[2], light->flags&(LFLAG_NORMALMODE|LFLAG_REALTIMEMODE|LFLAG_CREPUSCULAR) + light->coronascale, light->lightcolourscales[0], light->lightcolourscales[1], light->lightcolourscales[2], light->flags&(LFLAG_NORMALMODE|LFLAG_REALTIMEMODE|LFLAG_CREPUSCULAR), + light->rotation[0],light->rotation[1],light->rotation[2],light->fov )); } VFS_CLOSE(f); @@ -928,8 +959,50 @@ void R_SaveRTLights_f(void) Con_Printf("rtlights saved to %s\n", sysname); } +void R_StaticEntityToRTLight(int i) +{ + entity_state_t *state = &cl_static_entities[i].state; + dlight_t *dl; + if (!(state->lightpflags&(PFLAGS_FULLDYNAMIC|PFLAGS_CORONA))) + return; + dl = CL_AllocSlight(); + if (!dl) + return; + VectorCopy(state->origin, dl->origin); + AngleVectors(state->angles, dl->axis[0], dl->axis[1], dl->axis[2]); + VectorInverse(dl->axis[1]); + dl->radius = state->light[3]; + if (!dl->radius) + dl->radius = 350; + VectorScale(state->light, 1.0/1024, dl->color); + if (!state->light[0] && !state->light[1] && !state->light[2]) + VectorSet(dl->color, 1, 1, 1); + dl->flags = 0; + dl->flags |= LFLAG_REALTIMEMODE; + dl->flags |= (state->lightpflags & PFLAGS_NOSHADOW)?LFLAG_NOSHADOWS:0; + if (state->lightpflags & PFLAGS_CORONA) + dl->corona = 1; + dl->style = state->lightstyle+1; + if (state->lightpflags & PFLAGS_FULLDYNAMIC) + { + dl->lightcolourscales[0] = r_editlights_import_ambient.value; + dl->lightcolourscales[1] = r_editlights_import_diffuse.value; + dl->lightcolourscales[2] = r_editlights_import_specular.value; + } + else + { //corona-only light + dl->lightcolourscales[0] = 0; + dl->lightcolourscales[1] = 0; + dl->lightcolourscales[2] = 0; + } + if (state->skinnum >= 16) + R_LoadNumberedLightTexture(dl, state->skinnum); +} + void R_ReloadRTLights_f(void) { + int i; + if (!cl.worldmodel) { Con_Printf("Cannot reload lights at this time\n"); @@ -947,6 +1020,11 @@ void R_ReloadRTLights_f(void) if (rtlights_first == rtlights_max) R_ImportRTLights(cl.worldmodel->entities); } + + for (i = 0; i < cl.num_statics; i++) + { + R_StaticEntityToRTLight(i); + } } #endif diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index f3ca89a8..a4e32756 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -153,18 +153,18 @@ void GL_InitSceneProcessingShaders (void) void GL_SetupSceneProcessingTextures (void) { int i, x, y; - unsigned char pp_warp_tex[PP_WARP_TEX_SIZE*PP_WARP_TEX_SIZE*3]; - unsigned char pp_edge_tex[PP_AMP_TEX_SIZE*PP_AMP_TEX_SIZE*3]; + unsigned char pp_warp_tex[PP_WARP_TEX_SIZE*PP_WARP_TEX_SIZE*4]; + unsigned char pp_edge_tex[PP_AMP_TEX_SIZE*PP_AMP_TEX_SIZE*4]; scenepp_postproc_cube = r_nulltex; - TEXASSIGN(sceneblur_texture, GL_AllocNewTexture("***postprocess_blur***", 0, 0, 0)); + TEXASSIGN(sceneblur_texture, Image_CreateTexture("***postprocess_blur***", NULL, 0)); if (!gl_config.arb_shader_objects) return; - TEXASSIGN(scenepp_texture_warp, GL_AllocNewTexture("***postprocess_warp***", PP_WARP_TEX_SIZE, PP_WARP_TEX_SIZE, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR)); - TEXASSIGN(scenepp_texture_edge, GL_AllocNewTexture("***postprocess_edge***", PP_WARP_TEX_SIZE, PP_WARP_TEX_SIZE, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR)); + TEXASSIGN(scenepp_texture_warp, Image_CreateTexture("***postprocess_warp***", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR)); + TEXASSIGN(scenepp_texture_edge, Image_CreateTexture("***postprocess_edge***", NULL, IF_NOMIPMAP|IF_NOGAMMA|IF_LINEAR)); // init warp texture - this specifies offset in for (y=0; ynum); + } GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube); for (i = 0; i < 6; i++) @@ -1579,7 +1575,7 @@ void GLR_RenderView (void) if (!(r_refdef.flags & RDF_NOWORLDMODEL)) { //FIXME: fbo stuff - if (!r_worldentity.model || r_worldentity.model->needload || !cl.worldmodel) + if (!r_worldentity.model || r_worldentity.model->loadstate != MLS_LOADED || !cl.worldmodel) { GL_Set2D (false); R2D_ImageColours(0, 0, 0, 1); diff --git a/engine/gl/gl_rmisc.c b/engine/gl/gl_rmisc.c index efae8bf4..3f735e8f 100644 --- a/engine/gl/gl_rmisc.c +++ b/engine/gl/gl_rmisc.c @@ -66,7 +66,7 @@ void GLR_InitTextures (void) -#if 1 +#ifdef RTLIGHTS texid_t GenerateNormalisationCubeMap(void) { texid_t normalisationCubeMap; @@ -81,7 +81,8 @@ texid_t GenerateNormalisationCubeMap(void) int i, j; - normalisationCubeMap = R_AllocNewTexture("normalisationcubemap", 32, 32, 0); + normalisationCubeMap = Image_CreateTexture("normalisationcubemap", NULL, IF_CUBEMAP); + qglGenTextures(1, &normalisationCubeMap->num); GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, normalisationCubeMap); //positive x @@ -232,7 +233,6 @@ texid_t GenerateNormalisationCubeMap(void) return normalisationCubeMap; } - texid_t normalisationCubeMap; #endif @@ -246,7 +246,7 @@ void GLR_ReInit (void) R_NetgraphInit(); } -#if 1 +#if 0 typedef struct { long offset; // Position of the entry in WAD @@ -454,7 +454,7 @@ void GLR_DeInit (void) { Cmd_RemoveCommand ("timerefresh"); - Cmd_RemoveCommand ("makewad"); +// Cmd_RemoveCommand ("makewad"); Cvar_Unhook(&r_skyboxname); Cvar_Unhook(&vid_conautoscale); @@ -476,7 +476,7 @@ void GLR_Init (void) { Cmd_AddCommand ("timerefresh", GLR_TimeRefresh_f); - Cmd_AddCommand ("makewad", R_MakeTexWad_f); +// Cmd_AddCommand ("makewad", R_MakeTexWad_f); // Cvar_Hook(&r_floortexture, GLR_Floortexture_Callback); // Cvar_Hook(&r_walltexture, GLR_Walltexture_Callback); @@ -485,83 +485,6 @@ void GLR_Init (void) GLR_ReInit(); } -/* -=============== -R_NewMap -=============== -*/ -void GLR_NewMap (void) -{ - char namebuf[MAX_QPATH]; - extern cvar_t host_mapname; - 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); - VectorSet(r_worldentity.light_avg, 1, 1, 1); - - - COM_StripExtension(COM_SkipPath(cl.worldmodel->name), namebuf, sizeof(namebuf)); - Cvar_Set(&host_mapname, namebuf); - - Surf_DeInit(); - - r_viewleaf = NULL; - r_oldviewleaf = NULL; - r_viewcluster = -1; - r_oldviewcluster = 0; - r_viewcluster2 = -1; - - Mod_ParseInfoFromEntityLump(cl.worldmodel, cl.worldmodel->entities, cl.worldmodel->name); - - if (!pe) - Cvar_ForceCallback(&r_particlesystem); -TRACE(("dbg: GLR_NewMap: clear particles\n")); - P_ClearParticles (); -TRACE(("dbg: GLR_NewMap: wiping them stains (getting the cloth out)\n")); - Surf_WipeStains(); - CL_RegisterParticles(); -TRACE(("dbg: GLR_NewMap: building lightmaps\n")); - Surf_BuildLightmaps (); - - -TRACE(("dbg: GLR_NewMap: ui\n")); -#ifdef VM_UI - UI_Reset(); -#endif -TRACE(("dbg: GLR_NewMap: tp\n")); - TP_NewMap(); - R_SetSky(cl.skyname); - -#ifdef MAP_PROC - if (cl.worldmodel->fromgame == fg_doom3) - D3_GenerateAreas(cl.worldmodel); -#endif - -#ifdef RTLIGHTS - Sh_PreGenerateLights(); -#endif -} - -void GLR_PreNewMap(void) -{ - r_loadbumpmapping = r_deluxemapping.ival || r_glsl_offsetmapping.ival; -#ifdef RTLIGHTS - r_loadbumpmapping |= r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival; -#endif - r_viewleaf = NULL; - r_oldviewleaf = NULL; - r_viewleaf2 = NULL; - r_oldviewleaf2 = NULL; -} - - /* ==================== R_TimeRefresh_f diff --git a/engine/gl/gl_rsurf.c b/engine/gl/gl_rsurf.c index dfe3c311..8127524a 100644 --- a/engine/gl/gl_rsurf.c +++ b/engine/gl/gl_rsurf.c @@ -530,9 +530,9 @@ void GLBE_UploadAllLightmaps(void) continue; lm->modified = false; if (!TEXVALID(lm->lightmap_texture)) - { - TEXASSIGN(lm->lightmap_texture, R_AllocNewTexture("***lightmap***", lm->width, lm->height, IF_LINEAR|IF_NOMIPMAP)); - } + TEXASSIGN(lm->lightmap_texture, Image_CreateTexture("***lightmap***", NULL, (gl_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP)); + if (!lm->lightmap_texture->num) + qglGenTextures(1, &lm->lightmap_texture->num); GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture); if (gl_lightmap_nearest.ival) { diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index f0f209bc..f3660342 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -40,8 +40,10 @@ extern texid_t missing_texture; texid_t r_whiteimage; static qboolean shader_reload_needed; static qboolean shader_rescan_needed; +static char **saveshaderbody; sh_config_t sh_config; +r_config_t r_config; //cvars that affect shader generation cvar_t r_vertexlight = CVARFD("r_vertexlight", "0", CVAR_SHADERSYSTEM, "Hack loaded shaders to remove detail pass and lightmap sampling for faster rendering."); @@ -553,11 +555,13 @@ qboolean Shader_ParseSkySides (char *shadername, char *texturename, texid_t *ima for (ss = 0; ss < sizeof(skyname_suffix)/sizeof(skyname_suffix[0]); ss++) { Q_snprintfz ( path, sizeof(path), skyname_pattern[sp], texturename, skyname_suffix[ss][i] ); - images[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP); - if (TEXVALID(images[i])) + images[i] = R_LoadHiResTexture ( path, NULL, IF_NOALPHA|IF_CLAMP|IF_NOWORKER); + if (images[i]->status == TEX_LOADING) //FIXME: unsafe, as it can recurse through shader loading and mess up internal parse state. + COM_WorkerPartialSync(images[i], &images[i]->status, TEX_LOADING); + if (TEXLOADED(images[i])) break; } - if (TEXVALID(images[i])) + if (TEXLOADED(images[i])) break; } if (!TEXVALID(images[i])) @@ -663,12 +667,7 @@ static texid_t Shader_FindImage ( char *name, int flags ) return r_whiteimage; } if (flags & IF_RENDERTARGET) - { - texid_t tid = R_FindTexture(name, (flags&~IF_RENDERTARGET)|IF_NOMIPMAP); - if (!TEXVALID(tid)) - tid = R_AllocNewTexture(name, 0, 0, (flags&~IF_RENDERTARGET)|IF_NOMIPMAP); - return tid; - } + return R2D_RT_Configure(name, 0, 0, 0); return R_LoadHiResTexture(name, NULL, flags); } @@ -827,6 +826,8 @@ static void Shader_SurfaceParm ( shader_t *shader, shaderpass_t *pass, char **pt shader->flags |= SHADER_NODRAW; else if ( !Q_stricmp( token, "nodlight" ) ) shader->flags |= SHADER_NODLIGHT; + else if ( !Q_stricmp( token, "noshadows" ) ) + shader->flags |= SHADER_NOSHADOWS; } static void Shader_Sort ( shader_t *shader, shaderpass_t *pass, char **ptr ) @@ -926,6 +927,8 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip { return false; } + if (!sh_config.pCreateProgram && !sh_config.pLoadBlob) + return false; cvarnames[cvarcount] = NULL; @@ -1077,9 +1080,11 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip if (blobfile) { + blobheaderoffset = 0; VFS_SEEK(blobfile, 0); magic = *(unsigned int*)"FBLB"; //magic VFS_WRITE(blobfile, &magic, sizeof(magic)); + VFS_WRITE(blobfile, &blobheaderoffset, sizeof(blobheaderoffset)); memset(ever, 0, sizeof(ever)); //make sure we don't leak stuff. Q_strncpyz(ever, thisever, sizeof(ever)); VFS_WRITE(blobfile, ever, sizeof(ever)); @@ -1225,16 +1230,44 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip continue; //blob was loaded from disk, yay. //otherwise fall through. } + if (blobfile && !sh_config.pValidateProgram) + { + initoffset = VFS_GETLEN(blobfile); + VFS_SEEK(blobfile, initoffset); + } + if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, sh_config.pValidateProgram?NULL:blobfile)) + { + if (!(p & PERMUTATION_SKELETAL)) + onefailed = true; //don't flag it if skeletal failed. + if (!p) //give up if permutation 0 failed. that one failing is fatal. + break; + } + if (!sh_config.pValidateProgram && blobfile && initoffset != VFS_GETLEN(blobfile)) + { + permuoffsets[p] = initoffset; + blobadded = true; + } + } + while(nummodifiers) + Z_Free((char*)permutationdefines[--nummodifiers]); + + //extra loop to validate the programs actually linked properly. + //delaying it like this gives certain threaded drivers a chance to compile them all while we're messing around with other junk + if (sh_config.pValidateProgram) + for (p = 0; p < PERMUTATIONS; p++) + { + if (nopermutation & p) + continue; if (blobfile) { initoffset = VFS_GETLEN(blobfile); VFS_SEEK(blobfile, initoffset); } - if (!sh_config.pCreateProgram(prog, name, p, ver, permutationdefines, script, tess?script:NULL, tess?script:NULL, script, (p & PERMUTATION_SKELETAL)?true:onefailed, blobfile)) + if (!sh_config.pValidateProgram(prog, name, p, (p & PERMUTATION_SKELETAL)?true:onefailed, blobfile)) { if (!(p & PERMUTATION_SKELETAL)) onefailed = true; //don't flag it if skeletal failed. - if (!p) //give up if permutation 0 failed. that one failing is fatal. + if (!p) break; } if (blobfile && initoffset != VFS_GETLEN(blobfile)) @@ -1243,8 +1276,6 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip blobadded = true; } } - while(nummodifiers) - Z_Free((char*)permutationdefines[--nummodifiers]); if (sh_config.pProgAutoFields) sh_config.pProgAutoFields(prog, cvarnames, cvartypes); @@ -1373,14 +1404,6 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype) } } } -static void Shader_ReloadGenerics(void) -{ - sgeneric_t *g; - for (g = sgenerics; g; g = g->next) - { - Shader_LoadGeneric(g, qrenderer); - } -} static program_t *Shader_FindGeneric(char *name, int qrtype) { sgeneric_t *g; @@ -1415,7 +1438,23 @@ static program_t *Shader_FindGeneric(char *name, int qrtype) g->prog.refs++; return &g->prog; } +static void Shader_ReloadGenerics(void) +{ + sgeneric_t *g; + for (g = sgenerics; g; g = g->next) + { + Shader_LoadGeneric(g, qrenderer); + } + //this shader can take a while to load due to its number of permutations. + //because this all happens on the main thread, try to avoid random stalls by pre-loading it. + if (sh_config.progs_supported) + { + program_t *p = Shader_FindGeneric("defaultskin", qrenderer); + if (p) //generics get held on to in order to avoid so much churn. so we can just release the reference we just created and it'll be held until shutdown anyway. + p->refs--; + } +} void Shader_WriteOutGenerics_f(void) { int i; @@ -1873,11 +1912,17 @@ static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) { if ((mode & LSHADER_CUBE) && (mode & LSHADER_SPOT)) continue; - shader->bemoverrides[mode] = R_RegisterCustom(va("%s%s%s%s", + shader->bemoverrides[mode] = R_RegisterCustom(va("%s%s%s%s%s", tokencopy, (mode & LSHADER_SMAP)?"#PCF":"", (mode & LSHADER_SPOT)?"#SPOT":"", - (mode & LSHADER_CUBE)?"#CUBE":"") + (mode & LSHADER_CUBE)?"#CUBE":"", +#ifdef GLQUAKE + (qrenderer == QR_OPENGL && gl_config.arb_shadow && (mode & (LSHADER_SMAP|LSHADER_SPOT)))?"#USE_ARB_SHADOW":"" +#else + "" +#endif + ) , shader->usageflags, embed?Shader_DefaultScript:NULL, embed); } } @@ -1965,7 +2010,6 @@ static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *t { pass->texgen = T_GEN_DIFFUSE; pass->tcgen = TC_GEN_BASE; - shader->flags |= SHADER_NOIMAGE; } else if (!Q_stricmp (tname, "$normalmap")) { @@ -2052,6 +2096,12 @@ static qboolean Shaderpass_MapGen (shader_t *shader, shaderpass_t *pass, char *t pass->texgen = T_GEN_RIPPLEMAP; pass->tcgen = TC_GEN_BASE; //FIXME: moo! } + else if (!Q_stricmp (tname, "$null")) + { + pass->tcgen = TC_GEN_BASE; + pass->flags |= SHADER_PASS_NOMIPMAP|SHADER_PASS_DETAIL; + pass->texgen = T_GEN_SINGLEMAP; + } else return false; return true; @@ -3021,6 +3071,8 @@ void Shader_Reset(shader_t *s) Shader_Free(s); memset(s, 0, sizeof(*s)); + s->flags |= SHADER_IMAGEPENDING; + s->remapto = s; s->id = id; s->width = w; @@ -3527,9 +3579,6 @@ void Shader_Finish (shader_t *s) } } - if (TEXVALID(s->defaulttextures.base)) - s->flags &= ~SHADER_NOIMAGE; - if (!s->numpasses && s->sort != SHADER_SORT_PORTAL && !(s->flags & (SHADER_NODRAW|SHADER_SKY)) && !s->fog_dist && !s->prog) { pass = &s->passes[s->numpasses++]; @@ -3889,15 +3938,21 @@ void Shader_UpdateRegistration (void) } */ +void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args); void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) { char *h; char imagename[MAX_QPATH]; + char *subpath = NULL; strcpy(imagename, shader->name); h = strchr(imagename, '#'); if (h) *h = 0; + //skins can use an alternative path in certain cases, to work around dodgy models. + if (shader->generator == Shader_DefaultSkin) + subpath = shader->genargs; + if (!tn) tn = &shader->defaulttextures; if (!TEXVALID(shader->defaulttextures.base)) @@ -3905,10 +3960,8 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) /*dlights/realtime lighting needs some stuff*/ if (!TEXVALID(tn->base)) { - tn->base = R_LoadHiResTexture(imagename, NULL, IF_NOALPHA); + tn->base = R_LoadHiResTexture(imagename, subpath, IF_NOALPHA); } - if (TEXVALID(tn->base)) - shader->flags &= ~SHADER_NOIMAGE; TEXASSIGN(shader->defaulttextures.base, tn->base); } @@ -3920,11 +3973,11 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) if (r_loadbumpmapping) { if (!TEXVALID(tn->bump)) - tn->bump = R_LoadHiResTexture(va("%s_norm", imagename), NULL, IF_NOALPHA); + tn->bump = R_LoadHiResTexture(va("%s_norm", imagename), subpath, IF_NOALPHA); if (!TEXVALID(tn->bump)) - tn->bump = R_LoadHiResTexture(va("%s_bump", imagename), NULL, IF_NOALPHA); + tn->bump = R_LoadHiResTexture(va("%s_bump", imagename), subpath, IF_NOALPHA); if (!TEXVALID(tn->bump)) - tn->bump = R_LoadHiResTexture(va("normalmaps/%s", imagename), NULL, IF_NOALPHA); + tn->bump = R_LoadHiResTexture(va("normalmaps/%s", imagename), subpath, IF_NOALPHA); } TEXASSIGN(shader->defaulttextures.bump, tn->bump); } @@ -3934,7 +3987,7 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) if (shader->flags & SHADER_HASTOPBOTTOM) { if (!TEXVALID(tn->loweroverlay)) - tn->loweroverlay = R_LoadHiResTexture(va("%s_pants", imagename), NULL, 0); /*how rude*/ + tn->loweroverlay = R_LoadHiResTexture(va("%s_pants", imagename), subpath, 0); /*how rude*/ } TEXASSIGN(shader->defaulttextures.loweroverlay, tn->loweroverlay); } @@ -3944,7 +3997,7 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) if (shader->flags & SHADER_HASTOPBOTTOM) { if (!TEXVALID(tn->upperoverlay)) - tn->upperoverlay = R_LoadHiResTexture(va("%s_shirt", imagename), NULL, 0); + tn->upperoverlay = R_LoadHiResTexture(va("%s_shirt", imagename), subpath, 0); } TEXASSIGN(shader->defaulttextures.upperoverlay, tn->upperoverlay); } @@ -3955,7 +4008,7 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) if ((shader->flags & SHADER_HASGLOSS) && gl_specular.value && gl_load24bit.value) { if (!TEXVALID(tn->specular)) - tn->specular = R_LoadHiResTexture(va("%s_gloss", imagename), NULL, 0); + tn->specular = R_LoadHiResTexture(va("%s_gloss", imagename), subpath, 0); } TEXASSIGN(shader->defaulttextures.specular, tn->specular); } @@ -3966,7 +4019,7 @@ void QDECL R_BuildDefaultTexnums(texnums_t *tn, shader_t *shader) if ((shader->flags & SHADER_HASFULLBRIGHT) && r_fb_bmodels.value && gl_load24bit.value) { if (!TEXVALID(tn->fullbright)) - tn->specular = R_LoadHiResTexture(va("%s_luma", imagename), NULL, 0); + tn->specular = R_LoadHiResTexture(va("%s_luma", imagename), subpath, 0); } TEXASSIGN(shader->defaulttextures.fullbright, tn->fullbright); } @@ -4541,9 +4594,9 @@ void Shader_DefaultBSPVertex(const char *shortname, shader_t *s, const void *arg { shaderpass_t *pass; - s->defaulttextures.base = R_LoadHiResTexture(va("%s_d.tga", shortname), NULL, 0); +// s->defaulttextures.base = R_LoadHiResTexture(va("%s_d.tga", shortname), NULL, 0); - if (Shader_ParseShader("defaultflare", s)) + if (Shader_ParseShader("defaultvertexlit", s)) return; pass = &s->passes[0]; @@ -4554,23 +4607,21 @@ void Shader_DefaultBSPVertex(const char *shortname, shader_t *s, const void *arg pass->numMergedPasses = 1; Shader_SetBlendmode(pass); - if (TEXVALID(s->defaulttextures.base)) +/* if (TEXVALID(s->defaulttextures.base)) { pass->texgen = T_GEN_DIFFUSE; } - else + else*/ { - pass->anim_frames[0] = R_LoadHiResTexture(shortname, NULL, 0); - if (!TEXVALID(pass->anim_frames[0])) - { + s->defaulttextures.base = R_LoadHiResTexture(shortname, NULL, 0); + pass->texgen = T_GEN_DIFFUSE; + if (!TEXVALID(s->defaulttextures.base)) Con_DPrintf (CON_WARNING "Shader %s has a stage with no image: %s.\n", s->name, shortname ); - pass->anim_frames[0] = missing_texture; - } } s->numpasses = 1; s->numdeforms = 0; - s->flags = SHADER_DEPTHWRITE|SHADER_CULL_FRONT; + s->flags = SHADER_IMAGEPENDING|SHADER_DEPTHWRITE|SHADER_CULL_FRONT; s->sort = SHADER_SORT_OPAQUE; s->uses = 1; } @@ -4581,7 +4632,7 @@ void Shader_DefaultBSPFlare(const char *shortname, shader_t *s, const void *args return; pass = &s->passes[0]; - pass->flags = SHADER_PASS_NOCOLORARRAY; + pass->flags = SHADER_IMAGEPENDING|SHADER_PASS_NOCOLORARRAY; pass->shaderbits |= SBITS_SRCBLEND_ONE|SBITS_DSTBLEND_ONE; pass->anim_frames[0] = R_LoadHiResTexture(shortname, NULL, 0); pass->rgbgen = RGB_GEN_VERTEX_LIGHTING; @@ -4599,7 +4650,7 @@ void Shader_DefaultBSPFlare(const char *shortname, shader_t *s, const void *args s->numpasses = 1; s->numdeforms = 0; - s->flags = SHADER_FLARE; + s->flags = SHADER_IMAGEPENDING|SHADER_FLARE; s->sort = SHADER_SORT_ADDITIVE; s->uses = 1; @@ -4648,8 +4699,6 @@ void Shader_DefaultSkin(const char *shortname, shader_t *s, const void *args) "endif\n" "}\n" ); - - s->flags |= SHADER_NOIMAGE; } void Shader_DefaultSkinShell(const char *shortname, shader_t *s, const void *args) { @@ -4697,19 +4746,7 @@ void Shader_Default2D(const char *shortname, shader_t *s, const void *genargs) "}\n" ); - TEXASSIGN(s->defaulttextures.base, R_LoadHiResTexture(shortname, NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); - if (!TEXVALID(s->defaulttextures.base)) - { - unsigned char data[4*4] = {0}; - TEXASSIGN(s->defaulttextures.base, R_LoadTexture8("black", 4, 4, data, 0, 0)); - s->flags |= SHADER_NOIMAGE; - } - else - { - s->flags &= ~SHADER_NOIMAGE; - s->width = image_width; - s->height = image_height; - } + TEXASSIGN(s->defaulttextures.base, R_LoadHiResTexture(s->name, NULL, IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP)); } qboolean Shader_ReadShaderTerms(shader_t *s, char **shadersource, int parsemode, int *conddepth, int maxconddepth, int *cond) @@ -4817,11 +4854,19 @@ static void Shader_ReadShader(shader_t *s, char *shadersource, int parsemode) int cond[8]; cond[0] = 0; + //querying the shader body often requires generating the shader, which then gets parsed. + if (saveshaderbody) + { + Z_Free(*saveshaderbody); + *saveshaderbody = Z_StrDup(shadersource); + saveshaderbody = NULL; + } + memset(&parsestate, 0, sizeof(parsestate)); parsestate.mode = parsemode; // set defaults - s->flags = SHADER_CULL_FRONT; + s->flags = SHADER_IMAGEPENDING|SHADER_CULL_FRONT; s->uses = 1; while (Shader_ReadShaderTerms(s, &shadersource, parsemode, &conddepth, sizeof(cond)/sizeof(cond[0]), cond)) @@ -4884,6 +4929,8 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader if (!*name) name = "gfx/unspecified"; + COM_AssertMainThread("R_LoadShader"); + Q_strncpyz(cleanname, name, sizeof(cleanname)); COM_CleanUpPath(cleanname); @@ -4968,15 +5015,15 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader { if (sh_config.shadernamefmt) { - if (Shader_ParseShader(va(sh_config.shadernamefmt, shortname), s)) - { + char drivername[MAX_QPATH]; + Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); + if (Shader_ParseShader(drivername, s)) return s; - } } - if (Shader_ParseShader(shortname, s)) - { + if (Shader_ParseShader(cleanname, s)) + return s; + if (Shader_ParseShader(shortname, s)) return s; - } } // make a default shader @@ -5002,11 +5049,59 @@ static shader_t *R_LoadShader (const char *name, unsigned int usageflags, shader return NULL; } +char *Shader_GetShaderBody(shader_t *s) +{ + char *adr; + char cleanname[MAX_QPATH]; + char shortname[MAX_QPATH]; + int oldsort; + qboolean resort = false; + if (!s || !s->uses) + return NULL; + + adr = Z_StrDup("UNKNOWN BODY"); + saveshaderbody = &adr; + + strcpy(cleanname, s->name); + COM_StripExtension (cleanname, shortname, sizeof(shortname)); + if (ruleset_allow_shaders.ival) + { + if (sh_config.shadernamefmt) + { + char drivername[MAX_QPATH]; + Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); + if (Shader_ParseShader(drivername, s)) + return adr; + } + if (Shader_ParseShader(cleanname, s)) + return adr; + if (Shader_ParseShader(shortname, s)) + return adr; + } + if (s->generator) + { + oldsort = s->sort; + Shader_Reset(s); + + s->generator(shortname, s, s->genargs); + + if (s->sort != oldsort) + resort = true; + } + + if (resort) + { + Mod_ResortShaders(); + } + return adr; +} + void Shader_DoReload(void) { shader_t *s; unsigned int i; char shortname[MAX_QPATH]; + char cleanname[MAX_QPATH]; int oldsort; qboolean resort = false; @@ -5035,10 +5130,18 @@ void Shader_DoReload(void) if (!s || !s->uses) continue; - strcpy(shortname, s->name); + strcpy(cleanname, s->name); + COM_StripExtension (cleanname, shortname, sizeof(shortname)); if (ruleset_allow_shaders.ival) { - if (sh_config.shadernamefmt && Shader_ParseShader(va(sh_config.shadernamefmt, shortname), s)) + if (sh_config.shadernamefmt) + { + char drivername[MAX_QPATH]; + Q_snprintfz(drivername, sizeof(drivername), sh_config.shadernamefmt, cleanname); + if (Shader_ParseShader(drivername, s)) + continue; + } + if (Shader_ParseShader(cleanname, s)) continue; if (Shader_ParseShader(shortname, s)) continue; @@ -5188,27 +5291,79 @@ void Shader_RemapShader_f(void) R_RemapShader(sourcename, destname, timeoffset); } +//blocks +int R_GetShaderSizes(shader_t *shader, int *width, int *height, qboolean blocktillloaded) +{ + if (!shader) + return false; + if (shader->flags & SHADER_IMAGEPENDING) + { + int i; + if (width) + *width = 0; + if (height) + *height = 0; + for (i = 0; i < shader->numpasses; i++) + { + if (shader->passes[i].texgen == T_GEN_SINGLEMAP && shader->passes[i].anim_frames[0] && shader->passes[i].anim_frames[0]->status == TEX_LOADING) + { + if (!blocktillloaded) + return -1; + COM_WorkerPartialSync(shader->passes[i].anim_frames[0], &shader->passes[i].anim_frames[0]->status, TEX_LOADING); + } + if (shader->passes[i].texgen == T_GEN_DIFFUSE && (shader->defaulttextures.base && shader->defaulttextures.base->status == TEX_LOADING)) + { + if (!blocktillloaded) + return -1; + COM_WorkerPartialSync(shader->defaulttextures.base, &shader->defaulttextures.base->status, TEX_LOADING); + } + } + + shader->flags &= ~SHADER_IMAGEPENDING; + for (i = 0; i < shader->numpasses; i++) + { + if (shader->passes[i].texgen == T_GEN_SINGLEMAP) + { + if (shader->passes[i].anim_frames[0] && shader->passes[i].anim_frames[0]->status == TEX_LOADED) + { + shader->width = shader->passes[i].anim_frames[0]->width; + shader->height = shader->passes[i].anim_frames[0]->height; + } + break; + } + if (shader->passes[i].texgen == T_GEN_DIFFUSE && shader->defaulttextures.base) + { + if (shader->defaulttextures.base->status == TEX_LOADED) + { + shader->width = shader->defaulttextures.base->width; + shader->height = shader->defaulttextures.base->height; + } + break; + } + } + } + if (shader->width && shader->height) + { + if (width) + *width = shader->width; + if (height) + *height = shader->height; + return true; //final size + } + else + { + //fill with dummy values + if (width) + *width = 64; + if (height) + *height = 64; + return false; + } +} shader_t *R_RegisterPic (const char *name) { shader_t *shader; - - /*don't get confused by other shaders*/ - image_width = 64; - image_height = 64; - shader = R_LoadShader (name, SUF_2D, Shader_Default2D, NULL); - - /*worth a try*/ - if (shader->width <= 0) - shader->width = image_width; - if (shader->height <= 0) - shader->height = image_height; - - /*last ditch attempt*/ - if (shader->width <= 0) - shader->width = 64; - if (shader->height <= 0) - shader->height = 64; return shader; } @@ -5234,31 +5389,32 @@ shader_t *R_RegisterShader_Flare (const char *name) shader_t *QDECL R_RegisterSkin (const char *shadername, const char *modname) { + char newsname[MAX_QPATH]; shader_t *shader; #ifdef _DEBUG if (shadername == com_token) Con_Printf("R_RegisterSkin was passed com_token. that will bug out.\n"); #endif + newsname[0] = 0; if (modname && !strchr(shadername, '/') && *shadername) { - char newsname[MAX_QPATH]; char *b = COM_SkipPath(modname); if (b != modname && b-modname + strlen(shadername)+1 < sizeof(newsname)) { + b--; //no trailing / memcpy(newsname, modname, b - modname); - memcpy(newsname + (b-modname), shadername, strlen(shadername)+1); - /*if the specified shader does not contain a path, try and load one relative to the name of the model*/ - shader = R_LoadShader (newsname, 0, Shader_DefaultSkin, NULL); - - R_BuildDefaultTexnums(&shader->defaulttextures, shader); - - /*if its a valid shader with valid textures, use it*/ - if (!(shader->flags & SHADER_NOIMAGE)) - return shader; + newsname[b-modname] = 0; } } - shader = R_LoadShader (shadername, 0, Shader_DefaultSkin, NULL); + if (*newsname) + { + int l = strlen(newsname); + Q_strncpyz(newsname+l, ":models", sizeof(newsname)-l); + } + else + Q_strncpyz(newsname, "models", sizeof(newsname)); + shader = R_LoadShader (shadername, 0, Shader_DefaultSkin, newsname); return shader; } shader_t *R_RegisterCustom (const char *name, unsigned int usageflags, shader_gen_t *defaultgen, const void *args) diff --git a/engine/gl/gl_shadow.c b/engine/gl/gl_shadow.c index 00ec0832..8ea8dcda 100644 --- a/engine/gl/gl_shadow.c +++ b/engine/gl/gl_shadow.c @@ -92,17 +92,17 @@ void Sh_Reset(void) shadow_fbo_id = 0; shadow_fbo_depth_num = 0; } - if (shadowmap[0].num) + if (shadowmap[0]) { R_DestroyTexture(shadowmap[0]); shadowmap[0] = r_nulltex; } - if (shadowmap[1].num) + if (shadowmap[1]) { R_DestroyTexture(shadowmap[1]); shadowmap[1] = r_nulltex; } - if (crepuscular_texture_id.num) + if (crepuscular_texture_id) { R_DestroyTexture(crepuscular_texture_id); crepuscular_texture_id = r_nulltex; @@ -1408,8 +1408,8 @@ static struct shadowmesh_s *SHM_BuildShadowMesh(dlight_t *dl, unsigned char *lvi if (maxedge < cl.worldmodel->numedges) { maxedge = cl.worldmodel->numedges; - free(edge); - edge = malloc(sizeof(*edge) * maxedge); + Z_Free(edge); + edge = Z_Malloc(sizeof(*edge) * maxedge); } if (cl.worldmodel->type == mod_brush) @@ -2056,11 +2056,11 @@ void GL_BeginRenderBuffer_DepthOnly(texid_t depthtexture) else qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadow_fbo_id); - if (shadow_fbo_depth_num != depthtexture.num) + if (shadow_fbo_depth_num != depthtexture->num) { - shadow_fbo_depth_num = depthtexture.num; + shadow_fbo_depth_num = depthtexture->num; if (TEXVALID(depthtexture)) - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthtexture.num, 0); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depthtexture->num, 0); } } } @@ -2083,7 +2083,7 @@ void D3D11BE_BeginShadowmapFace(void); //determine the 5 bounding points of a shadowmap light projection side //needs to match Sh_GenShadowFace -static void Sh_LightFrustumPlanes(dlight_t *l, vec4_t *planes, int face) +static void Sh_LightFrustumPlanes(dlight_t *l, vec3_t axis[3], vec4_t *planes, int face) { vec3_t tmp; int axis0, axis1, axis2; @@ -2097,15 +2097,15 @@ static void Sh_LightFrustumPlanes(dlight_t *l, vec4_t *planes, int face) //center point is always the same VectorCopy(l->origin, planes[4]); - VectorScale(l->axis[axis0], dir, planes[4]); + VectorScale(axis[axis0], dir, planes[4]); VectorNormalize(planes[4]); planes[4][3] = r_shadow_shadowmapping_nearclip.value + DotProduct(planes[4], l->origin); for (i = 0; i < 4; i++) { - VectorScale(l->axis[axis0], dir, tmp); - VectorMA(tmp, ((i&1)?1:-1), l->axis[axis1], tmp); - VectorMA(tmp, ((i&2)?1:-1), l->axis[axis2], planes[i]); + VectorScale(axis[axis0], dir, tmp); + VectorMA(tmp, ((i&1)?1:-1), axis[axis1], tmp); + VectorMA(tmp, ((i&2)?1:-1), axis[axis2], planes[i]); VectorNormalize(planes[i]); planes[i][3] = DotProduct(planes[i], l->origin); } @@ -2113,7 +2113,7 @@ static void Sh_LightFrustumPlanes(dlight_t *l, vec4_t *planes, int face) //culling for the face happens in the caller. //these faces should thus match Sh_LightFrustumPlanes -static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face, int smsize, float proj[16]) +static void Sh_GenShadowFace(dlight_t *l, vec3_t axis[3], shadowmesh_t *smesh, int face, int smsize, float proj[16]) { vec3_t t1,t2,t3; texture_t *tex; @@ -2128,51 +2128,51 @@ static void Sh_GenShadowFace(dlight_t *l, shadowmesh_t *smesh, int face, int sms { case 0: //down - VectorCopy(l->axis[0], t1); - VectorCopy(l->axis[1], t2); - VectorCopy(l->axis[2], t3); + VectorCopy(axis[0], t1); + VectorCopy(axis[1], t2); + VectorCopy(axis[2], t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = 0; break; case 1: //back - VectorCopy(l->axis[2], t1); - VectorCopy(l->axis[1], t2); - VectorCopy(l->axis[0], t3); + VectorCopy(axis[2], t1); + VectorCopy(axis[1], t2); + VectorCopy(axis[0], t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = SHADER_CULL_FLIP; break; case 2: //right - VectorCopy(l->axis[0], t1); - VectorCopy(l->axis[2], t2); - VectorCopy(l->axis[1], t3); + VectorCopy(axis[0], t1); + VectorCopy(axis[2], t2); + VectorCopy(axis[1], t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = SHADER_CULL_FLIP; break; case 3: //up - VectorCopy(l->axis[0], t1); - VectorCopy(l->axis[1], t2); - VectorCopy(l->axis[2], t3); + VectorCopy(axis[0], t1); + VectorCopy(axis[1], t2); + VectorCopy(axis[2], t3); VectorNegate(t3, t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = SHADER_CULL_FLIP; break; case 4: //forward - VectorCopy(l->axis[2], t1); - VectorCopy(l->axis[1], t2); - VectorCopy(l->axis[0], t3); + VectorCopy(axis[2], t1); + VectorCopy(axis[1], t2); + VectorCopy(axis[0], t3); VectorNegate(t3, t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = 0; break; case 5: //left - VectorCopy(l->axis[0], t1); - VectorCopy(l->axis[2], t2); - VectorCopy(l->axis[1], t3); + VectorCopy(axis[0], t1); + VectorCopy(axis[2], t2); + VectorCopy(axis[1], t3); VectorNegate(t3, t3); Matrix4x4_CM_LightMatrixFromAxis(r_refdef.m_view, t1, t2, t3, l->origin); r_refdef.flipcull = 0; @@ -2291,7 +2291,7 @@ void D3D11_EndShadowMap(void); void D3D11BE_SetupForShadowMap(dlight_t *dl, qboolean isspot, int texwidth, int texheight, float shadowscale); -qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) +qboolean Sh_GenShadowMap (dlight_t *l, vec3_t axis[3], qbyte *lvis, int smsize) { int restorefbo = 0; int f; @@ -2314,7 +2314,7 @@ qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) vec4_t planes[5]; float dist; int fp,lp; - Sh_LightFrustumPlanes(l, planes, f); + Sh_LightFrustumPlanes(l, axis, planes, f); for (fp = 0; fp < r_refdef.frustum_numplanes; fp++) { vec3_t nearest; @@ -2361,7 +2361,8 @@ qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) { if (isspot) { - shadowmap[isspot] = GL_AllocNewTexture("***shadowmap2dspot***", SHADOWMAP_SIZE, SHADOWMAP_SIZE, 0); + shadowmap[isspot] = Image_CreateTexture("***shadowmap2dspot***", NULL, 0); + qglGenTextures(1, &shadowmap[isspot]->num); GL_MTBind(0, GL_TEXTURE_2D, shadowmap[isspot]); #ifdef DBG_COLOURNOTDEPTH qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SHADOWMAP_SIZE, SHADOWMAP_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); @@ -2371,7 +2372,8 @@ qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) } else { - shadowmap[isspot] = GL_AllocNewTexture("***shadowmap2dcube***", SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2, 0); + shadowmap[isspot] = Image_CreateTexture("***shadowmap2dcube***", NULL, 0); + qglGenTextures(1, &shadowmap[isspot]->num); GL_MTBind(0, GL_TEXTURE_2D, shadowmap[isspot]); #ifdef DBG_COLOURNOTDEPTH qglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SHADOWMAP_SIZE*3, SHADOWMAP_SIZE*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); @@ -2438,7 +2440,7 @@ qboolean Sh_GenShadowMap (dlight_t *l, qbyte *lvis, int smsize) if (sidevisible & (1u<num); GL_MTBind(0, GL_TEXTURE_2D, crepuscular_texture_id); 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); @@ -3212,7 +3218,7 @@ void Sh_DrawCrepuscularLight(dlight_t *dl, float *colours) oldfbo = GLBE_FBO_Update(&crepuscular_fbo, 0, &crepuscular_texture_id, 1, r_nulltex, vid.pixelwidth, vid.pixelheight); BE_SelectMode(BEM_CREPUSCULAR); - BE_SelectDLight(dl, colours, LSHADER_STANDARD); + BE_SelectDLight(dl, colours, dl->axis, LSHADER_STANDARD); GLBE_SubmitMeshes(true, SHADER_SORT_PORTAL, SHADER_SORT_BLEND); GLBE_FBO_Pop(oldfbo); @@ -3238,7 +3244,7 @@ void Sh_PurgeShadowMeshes(void) dl->rebuildcache = true; } } - free(edge); + Z_Free(edge); edge = NULL; maxedge = 0; } @@ -3388,8 +3394,11 @@ void Sh_CheckSettings(void) } } +int drawdlightnum; void Sh_DrawLights(qbyte *vis) { + vec3_t rotated[3]; + vec3_t *axis; vec3_t colour; dlight_t *dl; int i; @@ -3441,19 +3450,32 @@ void Sh_DrawLights(qbyte *vis) if (colour[0] < 0.001 && colour[1] < 0.001 && colour[2] < 0.001) continue; //just switch these off. + if (dl->rotation[0] || dl->rotation[1] || dl->rotation[2]) + { + vec3_t rot; + vec3_t rotationaxis[3]; + VectorScale(dl->rotation, cl.time, rot); + AngleVectorsFLU(rot, rotationaxis[0], rotationaxis[1], rotationaxis[2]); + Matrix3_Multiply(dl->axis, rotationaxis, rotated); + axis = rotated; + } + else + axis = dl->axis; + + drawdlightnum++; if (dl->flags & LFLAG_CREPUSCULAR) 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); + Sh_DrawShadowlessLight(dl, colour, axis, vis); } else if ((dl->flags & LFLAG_SHADOWMAP) || r_shadow_shadowmapping.ival) { - Sh_DrawShadowMapLight(dl, colour, vis); + Sh_DrawShadowMapLight(dl, colour, axis, vis); } else { - Sh_DrawStencilLight(dl, colour, vis); + Sh_DrawStencilLight(dl, colour, axis, vis); } } @@ -3499,6 +3521,8 @@ void Sh_DrawLights(qbyte *vis) // if (developer.value) // Con_Printf("%i lights drawn, %i frustum culled, %i pvs culled, %i scissor culled\n", bench.numlights, bench.numfrustumculled, bench.numpvsculled, bench.numscissorculled); // memset(&bench, 0, sizeof(bench)); + + drawdlightnum = -1; } #endif diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index c393f6e3..be1aac6c 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -599,11 +599,27 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name)) Con_DPrintf("Anisotropic filter extension found (%dx max).\n",gl_config.ext_texture_filter_anisotropic); } - if (GL_CheckExtension("GL_ARB_texture_non_power_of_two") || GL_CheckExtension("GL_OES_texture_npot")) + //FIXME: GL_ARB_texture_non_power_of_two is supposed to be mandatory in gl2+ and thus checking for it is redundant and not forwards-compatible + //geforcefx apparently software emulates it, so gl<3 is bad. + if (GL_CheckExtension("GL_ARB_texture_non_power_of_two")) + { gl_config.texture_non_power_of_two = true; //gles2 has limited npot as standard, which is sufficient to make the hud not look like poo. lets use it. if ((gl_config.gles && gl_config.glversion >= 2) || gl_config.texture_non_power_of_two) gl_config.texture_non_power_of_two_limited = true; + r_config.texture_non_power_of_two_pic = true; + } + else if (GL_CheckExtension("GL_OES_texture_npot")) + { + r_config.texture_non_power_of_two = false; + r_config.texture_non_power_of_two_pic = true; + } + else + { + r_config.texture_non_power_of_two = false; + r_config.texture_non_power_of_two_pic = false; + } + // if (GL_CheckExtension("GL_SGIS_generate_mipmap")) //a suprising number of implementations have this broken. // gl_config.sgis_generate_mipmap = true; @@ -1180,7 +1196,7 @@ static const char *glsl_hdrs[] = "{\n" "#if defined(RELIEFMAPPING) && !defined(GL_ES)\n" "float i, f;\n" - "vec3 OffsetVector = vec3(normalize(eyevector.xyz).xy * cvar_r_glsl_offsetmapping_scale * vec2(-1.0, -1.0), -1.0);\n" + "vec3 OffsetVector = vec3(normalize(eyevector.xyz).xy * cvar_r_glsl_offsetmapping_scale * vec2(-1.0, 1.0), -1.0);\n" "vec3 RT = vec3(vec2(base.xy"/* - OffsetVector.xy*OffsetMapping_Bias*/"), 1.0);\n" "OffsetVector /= 10.0;\n" "for(i = 1.0; i < 10.0; ++i)\n" @@ -1189,7 +1205,7 @@ static const char *glsl_hdrs[] = "RT += OffsetVector * (step(texture2D(normtex, RT.xy).a, RT.z) * f - 0.5 * f);\n" "return RT.xy;\n" "#elif defined(OFFSETMAPPING)\n" - "vec2 OffsetVector = normalize(eyevector).xy * cvar_r_glsl_offsetmapping_scale * vec2(-1.0, -1.0);\n" + "vec2 OffsetVector = normalize(eyevector).xy * cvar_r_glsl_offsetmapping_scale * vec2(-1.0, 1.0);\n" "vec2 tc = base;\n" "tc += OffsetVector;\n" "OffsetVector *= 0.333;\n" @@ -1607,8 +1623,6 @@ static GLhandleARB GLSlang_FinishShader(GLhandleARB shader, const char *name, GL GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLhandleARB frag, qboolean silent) { GLhandleARB program; - GLint linked; - char str[2048]; program = qglCreateProgramObjectARB(); qglAttachObjectARB(program, vert); @@ -1636,6 +1650,14 @@ GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLh qglBindAttribLocationARB(program, VATTR_VERTEX2, "v_position2"); qglLinkProgramARB(program); + return program; +} + +GLhandleARB GLSlang_ValidateProgram(GLhandleARB program, const char *name, qboolean silent, vfsfile_t *blobfile) +{ + char str[2048]; + char *nullconstants = NULL; + GLint linked; qglGetProgramParameteriv_(program, GL_OBJECT_LINK_STATUS_ARB, &linked); @@ -1651,6 +1673,27 @@ GLhandleARB GLSlang_CreateProgramObject (const char *name, GLhandleARB vert, GLh return (GLhandleARB)0; } + + if (program && blobfile && qglGetProgramBinary) + { + GLuint ui; + GLenum e; + unsigned int len, fmt; + void *blobdata; + + qglGetProgramParameteriv_(program, GL_PROGRAM_BINARY_LENGTH, &ui); + len = ui; + + blobdata = BZ_Malloc(len); + qglGetProgramBinary(program, len, NULL, &e, blobdata); + fmt = e; + + VFS_WRITE(blobfile, &fmt, sizeof(fmt)); + VFS_WRITE(blobfile, &len, sizeof(len)); + VFS_WRITE(blobfile, blobdata, len); + BZ_Free(blobdata); + } + return program; } @@ -1706,6 +1749,11 @@ GLhandleARB GLSlang_CreateProgram(const char *name, int ver, const char **precom return handle; } +qboolean GLSlang_ValidateProgramPermu(program_t *prog, const char *name, unsigned int permu, qboolean noerrors, vfsfile_t *blobfile) +{ + prog->permu[permu].handle.glsl = GLSlang_ValidateProgram(prog->permu[permu].handle.glsl, name, noerrors, blobfile); + return !!prog->permu[permu].handle.glsl; +} qboolean GLSlang_CreateProgramPermu(program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile) { if (!ver) @@ -2100,10 +2148,14 @@ void GL_Init(void *(*getglfunction) (char *name)) sh_config.progs_supported = gl_config.arb_shader_objects; sh_config.progs_required = gl_config_nofixedfunc; - sh_config.pDeleteProg = GLSlang_DeleteProg; - sh_config.pLoadBlob = qglProgramBinary?GLSlang_LoadBlob:NULL; - sh_config.pCreateProgram = GLSlang_CreateProgramPermu; - sh_config.pProgAutoFields = GLSlang_ProgAutoFields; + if (gl_config.arb_shader_objects) + { + sh_config.pDeleteProg = GLSlang_DeleteProg; + sh_config.pLoadBlob = qglProgramBinary?GLSlang_LoadBlob:NULL; + sh_config.pCreateProgram = GLSlang_CreateProgramPermu; + sh_config.pValidateProgram = GLSlang_ValidateProgramPermu; + sh_config.pProgAutoFields = GLSlang_ProgAutoFields; + } if (gl_config.nofixedfunc) { @@ -2314,23 +2366,14 @@ rendererinfo_t openglrendererinfo = { GLDraw_Init, GLDraw_DeInit, - GL_LoadTextureFmt, - GL_LoadTexture8Pal24, - GL_LoadTexture8Pal32, - GL_LoadCompressed, - GL_FindTexture, - GL_AllocNewTexture, - GL_UploadFmt, + GL_UpdateFiltering, + GL_LoadTextureMips, GL_DestroyTexture, GLR_Init, GLR_DeInit, GLR_RenderView, - - GLR_NewMap, - GLR_PreNewMap, - GLVID_Init, GLVID_DeInit, GLVID_SwapBuffers, diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index cdf2a8da..47180858 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -2024,22 +2024,14 @@ rendererinfo_t eglrendererinfo = GLDraw_Init, GLDraw_DeInit, - GL_LoadTextureFmt, - GL_LoadTexture8Pal24, - GL_LoadTexture8Pal32, - GL_LoadCompressed, - GL_FindTexture, - GL_AllocNewTexture, - GL_UploadFmt, + GL_UpdateFiltering, + GL_LoadTextureMips, GL_DestroyTexture, GLR_Init, GLR_DeInit, GLR_RenderView, - GLR_NewMap, - GLR_PreNewMap, - EGLVID_Init, GLVID_DeInit, GLVID_SwapBuffers, diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index db832bd4..06b99e35 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -553,7 +553,7 @@ RECT centerrect(unsigned int parentleft, unsigned int parenttop, unsigned int pa return r; } - +void Image_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight); void *WIN_CreateCursor(char *filename, float hotx, float hoty, float scale) { int width, height; @@ -568,8 +568,6 @@ void *WIN_CreateCursor(char *filename, float hotx, float hoty, float scale) int filelen; if (!filename || !*filename) return NULL; - if (scale != 1) //rescaling cursors is not supported at this time. - return NULL; filelen = FS_LoadFile(filename, &filedata); if (!filedata) return NULL; @@ -579,6 +577,22 @@ void *WIN_CreateCursor(char *filename, float hotx, float hoty, float scale) if (!rgbadata_start) return NULL; + if (scale != 1) + { + int nw,nh; + qbyte *nd; + nw = width * scale; + nh = height * scale; + if (nw <= 0 || nh <= 0 || nw > 128 || nh > 128) //don't go crazy. + return NULL; + nd = BZ_Malloc(nw*nh*4); + Image_ResampleTexture((unsigned int*)rgbadata_start, width, height, (unsigned int*)nd, nw, nh); + width = nw; + height = nh; + BZ_Free(rgbadata_start); + rgbadata_start = nd; + } + memset(&bi,0, sizeof(BITMAPV5HEADER)); bi.bV5Size = sizeof(BITMAPV5HEADER); bi.bV5Width = width; @@ -588,6 +602,7 @@ void *WIN_CreateCursor(char *filename, float hotx, float hoty, float scale) bi.bV5Compression = BI_BITFIELDS; // The following mask specification specifies a supported 32 BPP // alpha format for Windows XP. + //FIXME: can we not just specify it as RGBA? meh. bi.bV5RedMask = 0x00FF0000; bi.bV5GreenMask = 0x0000FF00; bi.bV5BlueMask = 0x000000FF; @@ -2115,7 +2130,7 @@ LONG WINAPI GLMainWndProc ( case WM_MOUSEWHEEL: if (!vid_initializing) { - if ((short) HIWORD(wParam) > 0) + if ((short) HIWORD(wParam&0xffffffff) > 0) { Key_Event(0, K_MWHEELUP, 0, true); Key_Event(0, K_MWHEELUP, 0, false); diff --git a/engine/gl/gl_vidrpi.c b/engine/gl/gl_vidrpi.c index b1ee9265..357a35ed 100644 --- a/engine/gl/gl_vidrpi.c +++ b/engine/gl/gl_vidrpi.c @@ -91,22 +91,14 @@ rendererinfo_t rpirendererinfo = GLDraw_Init, GLDraw_DeInit, - GL_LoadTextureFmt, - GL_LoadTexture8Pal24, - GL_LoadTexture8Pal32, - GL_LoadCompressed, - GL_FindTexture, - GL_AllocNewTexture, - GL_UploadFmt, - GL_DestroyTexture, + GL_UpdateFiltering, + GL_LoadTextureMips, + GL_DestroyTexture, GLR_Init, GLR_DeInit, GLR_RenderView, - GLR_NewMap, - GLR_PreNewMap, - RPI_Init, RPI_DeInit, RPI_ApplyGammaRamps, diff --git a/engine/gl/gl_vidwayland.c b/engine/gl/gl_vidwayland.c index bb8b928f..7a69e414 100644 --- a/engine/gl/gl_vidwayland.c +++ b/engine/gl/gl_vidwayland.c @@ -418,22 +418,14 @@ rendererinfo_t waylandrendererinfo = GLDraw_Init, GLDraw_DeInit, - GL_LoadTextureFmt, - GL_LoadTexture8Pal24, - GL_LoadTexture8Pal32, - GL_LoadCompressed, - GL_FindTexture, - GL_AllocNewTexture, - GL_UploadFmt, - GL_DestroyTexture, + GL_UpdateFiltering, + GL_LoadTextureMips, + GL_DestroyTexture, GLR_Init, GLR_DeInit, GLR_RenderView, - GLR_NewMap, - GLR_PreNewMap, - WL_Init, WL_DeInit, WL_SwapBuffers, diff --git a/engine/gl/gl_warp.c b/engine/gl/gl_warp.c index 88148805..078d6ded 100644 --- a/engine/gl/gl_warp.c +++ b/engine/gl/gl_warp.c @@ -768,17 +768,14 @@ void R_InitSky (struct texnums_s *tn, texture_t *mt, qbyte *src) b += ((qbyte *)rgba)[2]; } + Q_snprintfz(name, sizeof(name), "%s_solid", mt->name); + Q_strlwr(name); + tn->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA, trans, 128, 128, TF_RGBX32); + ((qbyte *)&transpix)[0] = r/(128*128); ((qbyte *)&transpix)[1] = g/(128*128); ((qbyte *)&transpix)[2] = b/(128*128); ((qbyte *)&transpix)[3] = 0; - - Q_snprintfz(name, sizeof(name), "%s_solid", mt->name); - Q_strlwr(name); - tn->base = R_LoadReplacementTexture(name, NULL, IF_NOALPHA); - if (!TEXVALID(tn->base)) - tn->base = R_LoadTexture32(name, 128, 128, trans, IF_NOALPHA|IF_NOGAMMA); - alphamask = LittleLong(0x7fffffff); for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) @@ -790,16 +787,9 @@ void R_InitSky (struct texnums_s *tn, texture_t *mt, qbyte *src) trans[(i*128) + j] = d_8to24rgbtable[p] & alphamask; } - Q_snprintfz(name, sizeof(name), "%s_trans", mt->name); + //FIXME: support _trans + Q_snprintfz(name, sizeof(name), "%s_alpha", mt->name); Q_strlwr(name); - tn->fullbright = R_LoadReplacementTexture(name, NULL, 0); - if (!TEXVALID(tn->fullbright)) - { - Q_snprintfz(name, sizeof(name), "%s_alpha", mt->name); - Q_strlwr(name); - tn->fullbright = R_LoadReplacementTexture(name, NULL, 0); - } - if (!TEXVALID(tn->fullbright)) - tn->fullbright = R_LoadTexture32(name, 128, 128, trans, IF_NOGAMMA); + tn->fullbright = R_LoadReplacementTexture(name, NULL, 0, trans, 128, 128, TF_RGBA32); } #endif diff --git a/engine/gl/glmod_doom.c b/engine/gl/glmod_doom.c index 85daba62..9c775895 100644 --- a/engine/gl/glmod_doom.c +++ b/engine/gl/glmod_doom.c @@ -205,9 +205,6 @@ unsigned int sidedefsc; unsigned int vertexesc; unsigned int vertexsglbase; -extern model_t *loadmodel; -extern char loadname[]; - typedef struct { diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 7989e9a3..1c1564b7 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -52,6 +52,23 @@ void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2); void ClearBounds (vec3_t mins, vec3_t maxs); +typedef struct builddata_s +{ + void (*buildfunc)(model_t *mod, msurface_t *surf, struct builddata_s *bd); + void *facedata; +} builddata_t; +void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b); //data === builddata_t + + +//optional features common to all renderers, so I don't have to check to see which one it is all the time. +typedef struct { + qboolean texture_non_power_of_two; //all npot is okay + qboolean texture_non_power_of_two_pic; //npot only works with clamp-to-edge mipless images. + qboolean npot_rounddown; //memory limited systems can say that they want to use less ram. + int maxtexturesize; //biggest image size supported +} r_config_t; +extern r_config_t r_config; + #ifdef GLQUAKE #if defined(ANDROID) /*FIXME: actually just to use standard GLES headers instead of full GL*/ #if 1 @@ -228,26 +245,10 @@ extern gl_config_t gl_config; extern float gldepthmin, gldepthmax; -/* -void GL_Upload32 (char *name, unsigned *data, int width, int height, unsigned int flags); //name was added for texture compression output -void GL_Upload32_BGRA (char *name, unsigned *data, int width, int height, unsigned int flags); //name was added for texture compression output -void GL_Upload8 (char *name, qbyte *data, int width, int height, unsigned int flags, unsigned int alphatype); -void GL_Upload24BGR_Flip (char *name, qbyte *data, int width, int height, unsigned int flags); -void GL_Upload24BGR (char *name, qbyte *data, int width, int height, unsigned int flags); -#ifdef GL_EXT_paletted_texture -void GL_Upload8_EXT (qbyte *data, int width, int height, qboolean mipmap, qboolean alpha); -#endif -*/ -texid_tf GL_LoadTexture (const char *identifier, int width, int height, qbyte *data, unsigned int flags, unsigned int transtype); -texid_tf GL_LoadTexture8Bump (const char *identifier, int width, int height, unsigned char *data, unsigned int flags, float bumpscale); -texid_tf GL_LoadTexture8Pal24 (const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_tf GL_LoadTexture8Pal32 (const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_tf GL_LoadTexture32 (const char *identifier, int width, int height, void *data, unsigned int flags); -texid_tf GL_LoadCompressed(const char *name); -texid_tf GL_FindTexture (const char *identifier, unsigned int flags); +void GL_UpdateFiltering(image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); +qboolean GL_LoadTextureMips(texid_t tex, struct pendingtextureinfo *mips); +void GL_DestroyTexture(texid_t tex); -texid_tf GL_LoadTextureFB (const char *identifier, int width, int height, qbyte *data, unsigned int flags); -void GL_Upload8Pal24 (qbyte *data, qbyte *pal, int width, int height, unsigned int flags); /* typedef struct { diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 01787833..830cc106 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -656,7 +656,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec4 lc = texture2D(s_t1, tc);\n" "col.rgb += lc.rgb*e_lowercolour*lc.a;\n" "#endif\n" -"col.rgb *= light;\n" "#if defined(BUMP) && defined(SPECULAR)\n" "vec3 bumps = normalize(vec3(texture2D(s_t4, tc)) - 0.5);\n" @@ -667,6 +666,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "col.rgb += cvar_gl_specular * spec * specs.rgb;\n" "#endif\n" +"col.rgb *= light;\n" + "#ifdef FULLBRIGHT\n" "vec4 fb = texture2D(s_t3, tc);\n" // col.rgb = mix(col.rgb, fb.rgb, fb.a); @@ -1726,6 +1727,14 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#define LOWER\n" "#endif\n" +//if there's no vertex normals known, disable some stuff. +//FIXME: this results in dupe permutations. +"#ifdef NOBUMP\n" +"#undef SPECULAR\n" +"#undef BUMP\n" +"#undef OFFSETMAPPING\n" +"#endif\n" + "varying vec2 tcbase;\n" "varying vec3 lightvector;\n" @@ -1753,9 +1762,15 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "gl_Position = skeletaltransform_wnst(w,n,s,t);\n" "tcbase = v_texcoord; //pass the texture coords straight through\n" "vec3 lightminusvertex = l_lightposition - w.xyz;\n" +"#ifdef NOBUMP\n" +//the only important thing is distance +"lightvector = lightminusvertex;\n" +"#else\n" +//the light direction relative to the surface normal, for bumpmapping. "lightvector.x = dot(lightminusvertex, s.xyz);\n" "lightvector.y = dot(lightminusvertex, t.xyz);\n" "lightvector.z = dot(lightminusvertex, n.xyz);\n" +"#endif\n" "#if defined(SPECULAR)||defined(OFFSETMAPPING)\n" "vec3 eyeminusvertex = e_eyepos - w.xyz;\n" "eyevector.x = dot(eyeminusvertex, s.xyz);\n" @@ -1817,8 +1832,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#ifdef SPOT\n" //bias it. don't bother figuring out which side or anything, its not needed //l_projmatrix contains the light's projection matrix so no other magic needed -"vtexprojcoord.z -= 0.015;\n" -"return (vtexprojcoord.xyz/vtexprojcoord.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5);\n" +"return ((vtexprojcoord.xyz-vec3(0.0,0.0,0.015))/vtexprojcoord.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5);\n" //#elif defined(CUBESHADOW) // vec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w; // #define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r @@ -1941,15 +1955,20 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "vec4 specs = texture2D(s_t2, tcbase);\n" "#endif\n" -"vec3 nl = normalize(lightvector);\n" "float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);\n" "vec3 diff;\n" +"#ifdef NOBUMP\n" +//surface can only support ambient lighting, even for lights that try to avoid it. +"diff = bases * (l_lightcolourscale.x+l_lightcolourscale.y);\n" +"#else\n" +"vec3 nl = normalize(lightvector);\n" "#ifdef BUMP\n" "diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0));\n" "#else\n" //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. "diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));\n" "#endif\n" +"#endif\n" "#ifdef SPECULAR\n" diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 3f68350f..f69a8f0e 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -279,18 +279,22 @@ typedef struct shaderpass_s { } stagetype; enum { - SHADER_PASS_CLAMP = 1<<0, //needed for d3d's sampler states, infects image flags - SHADER_PASS_NEAREST = 1<<1, //needed for d3d's sampler states, infects image flags - SHADER_PASS_DEPTHCMP = 1<<2, //needed for d3d's sampler states - SHADER_PASS_NOMIPMAP = 1<<3, //infects image flags - SHADER_PASS_NOCOLORARRAY = 1<< 4, + SHADER_PASS_CLAMP = 1<<0, //needed for d3d's sampler states, MUST MATCH IMAGE FLAGS + SHADER_PASS_NOMIPMAP = 1<<1, //needed for d3d's sampler states, MUST MATCH IMAGE FLAGS + SHADER_PASS_NEAREST = 1<<2, //needed for d3d's sampler states, MUST MATCH IMAGE FLAGS + SHADER_PASS_LINEAR = 1<<3, //needed for d3d's sampler states, MUST MATCH IMAGE FLAGS + SHADER_PASS_UIPIC = 1<<4, // MUST MATCH IMAGE FLAGS +#define SHADER_PASS_IMAGE_FLAGS (SHADER_PASS_CLAMP|SHADER_PASS_NOMIPMAP|SHADER_PASS_NEAREST|SHADER_PASS_LINEAR|SHADER_PASS_UIPIC) + + SHADER_PASS_DEPTHCMP = 1<<5, //needed for d3d's sampler states + SHADER_PASS_NOCOLORARRAY = 1<<6, //FIXME: remove these - SHADER_PASS_VIDEOMAP = 1 << 5, - SHADER_PASS_DETAIL = 1 << 6, - SHADER_PASS_LIGHTMAP = 1 << 7, - SHADER_PASS_DELUXMAP = 1 << 8, - SHADER_PASS_ANIMMAP = 1 << 9 + SHADER_PASS_VIDEOMAP = 1 << 6, + SHADER_PASS_DETAIL = 1 << 7, + SHADER_PASS_LIGHTMAP = 1 << 8, + SHADER_PASS_DELUXMAP = 1 << 9, + SHADER_PASS_ANIMMAP = 1 << 10 } flags; #ifdef D3D11QUAKE @@ -481,7 +485,7 @@ struct shader_s SUF_2D = 1<<1 //any loaded textures will obey 2d picmips rather than 3d picmips } usageflags; // int uses; //released when the uses drops to 0 - int width; //when used as an image, this is the logical 'width' of the image + int width; //when used as an image, this is the logical 'width' of the image. FIXME. int height; int numpasses; texnums_t defaulttextures; @@ -512,7 +516,7 @@ struct shader_s SHADER_DEFORMV_BULGE = 1 << 5, SHADER_AUTOSPRITE = 1 << 6, SHADER_FLARE = 1 << 7, - SHADER_NOIMAGE = 1 << 8, + SHADER_IMAGEPENDING = 1 << 8, //FIXME SHADER_ENTITY_MERGABLE = 1 << 9, SHADER_VIDEOMAP = 1 << 10, SHADER_DEPTHWRITE = 1 << 11, //some pass already wrote depth. not used by the renderer. @@ -554,7 +558,9 @@ extern shader_t **r_shaders; extern int be_maxpasses; +char *Shader_GetShaderBody(shader_t *s); void R_UnloadShader(shader_t *shader); +int R_GetShaderSizes(shader_t *shader, int *width, int *height, qboolean blocktillloaded); shader_t *R_RegisterPic (const char *name); shader_t *QDECL R_RegisterShader (const char *name, unsigned int usageflags, const char *shaderscript); shader_t *R_RegisterShader_Lightmap (const char *name); @@ -585,7 +591,7 @@ void Shader_NeedReload(qboolean rescanfs); void Shader_WriteOutGenerics_f(void); void Shader_RemapShader_f(void); -mfog_t *CM_FogForOrigin(vec3_t org); +mfog_t *Mod_FogForOrigin(model_t *wmodel, vec3_t org); #define BEF_FORCEDEPTHWRITE 1 #define BEF_FORCEDEPTHTEST 2 @@ -634,6 +640,7 @@ typedef struct void (*pDeleteProg) (program_t *prog, unsigned int permu); qboolean (*pLoadBlob) (program_t *prog, const char *name, unsigned int permu, vfsfile_t *blobfile); qboolean (*pCreateProgram) (program_t *prog, const char *name, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean noerrors, vfsfile_t *blobfile); + qboolean (*pValidateProgram)(program_t *prog, const char *name, unsigned int permu, qboolean noerrors, vfsfile_t *blobfile); void (*pProgAutoFields) (program_t *prog, char **cvarnames, int *cvartypes); } sh_config_t; extern sh_config_t sh_config; @@ -663,7 +670,7 @@ void GLBE_UploadAllLightmaps(void); void GLBE_DrawWorld (qboolean drawworld, qbyte *vis); qboolean GLBE_LightCullModel(vec3_t org, model_t *model); void GLBE_SelectEntity(entity_t *ent); -qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode); +qboolean GLBE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); void GLBE_Scissor(srect_t *rect); void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); //void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth); @@ -693,7 +700,7 @@ void D3D9BE_UploadAllLightmaps(void); void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis); qboolean D3D9BE_LightCullModel(vec3_t org, model_t *model); void D3D9BE_SelectEntity(entity_t *ent); -qboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode); +qboolean D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); void D3D9BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); void D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); void D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); @@ -717,7 +724,7 @@ void D3D11BE_UploadAllLightmaps(void); void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis); qboolean D3D11BE_LightCullModel(vec3_t org, model_t *model); void D3D11BE_SelectEntity(entity_t *ent); -qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, unsigned int lmode); +qboolean D3D11BE_SelectDLight(dlight_t *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); qboolean D3D11Shader_Init(unsigned int featurelevel); void D3D11BE_Reset(qboolean before); diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 51d4302d..356ea1e7 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -416,13 +416,13 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) else if (!strnicmp(msg, "Content-Type:", 13)) { *nl = '\0'; - Q_strncpyz(mimetype, COM_TrimString(msg+13), sizeof(mimetype)); + COM_TrimString(msg+13, mimetype, sizeof(mimetype)); *nl = '\n'; } else if (!strnicmp(msg, "Location: ", 10)) { *nl = '\0'; - Q_strncpyz(Location, COM_TrimString(msg+10), sizeof(Location)); + COM_TrimString(msg+10, Location, sizeof(Location)); *nl = '\n'; } else if (!strnicmp(msg, "Content-Encoding: ", 18)) @@ -458,10 +458,11 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) if (!stricmp(buffer, "301") || !stricmp(buffer, "302") || !stricmp(buffer, "303")) { + char trimmed[256]; nl = strchr(msg, '\n'); if (nl) *nl = '\0'; - Con_Printf("HTTP: %s %s\n", buffer, COM_TrimString(msg)); + Con_Printf("HTTP: %s %s\n", buffer, COM_TrimString(msg, trimmed, sizeof(trimmed))); if (!*Location) Con_Printf("Server redirected to null location\n"); else diff --git a/engine/partcfgs/generatebuiltin.c b/engine/partcfgs/generatebuiltin.c index 7b6e7b23..17d19593 100644 --- a/engine/partcfgs/generatebuiltin.c +++ b/engine/partcfgs/generatebuiltin.c @@ -1,23 +1,28 @@ +//simple tool to read the particle configs and generate a header file for inclusion in the engine. #include #include -char effects[][64] = +struct { - "spikeset.cfg", - "faithful.cfg", - "highfps.cfg", - "high.cfg", - "minimal.cfg", - "h2part.cfg", - "q2part.cfg", - "tsshaft.cfg", - "" + char filename[64]; + char *ifdef; +} effects[] = +{ + {"spikeset.cfg"}, + {"faithful.cfg"}, + {"highfps.cfg"}, + {"high.cfg"}, + {"minimal.cfg"}, + {"h2part.cfg", "#ifdef HEXEN2\n"}, + {"q2part.cfg", "#ifdef Q2CLIENT\n"}, + {"tsshaft.cfg"}, + {""} }; int main(void) { FILE *c, *h, *s; - char line[1024]; + char line[1024], *f; int i, j; c = fopen("../client/r_partset.c", "wt"); h = fopen("../client/r_partset.h", "wt"); @@ -28,22 +33,38 @@ int main(void) return; } + fprintf(h, "/*\nWARNING: THIS FILE IS GENERATED BY '"__FILE__"'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n"); fprintf(c, "/*\nWARNING: THIS FILE IS GENERATED BY '"__FILE__"'.\nYOU SHOULD NOT EDIT THIS FILE BY HAND\n*/\n\n"); + fprintf(c, "#include \"bothdefs.h\"\n"); fprintf(c, "#include \"r_partset.h\"\n\n\n"); - for (i = 0; *effects[i]; i++) + for (i = 0; *effects[i].filename; i++) { - s = fopen(effects[i], "rt"); + s = fopen(effects[i].filename, "rt"); if (!s) { - printf("unable to open %s\n", effects[i]); + printf("unable to open %s\n", effects[i].filename); return; } - *strchr(effects[i], '.') = 0; - fprintf(h, "extern char *particle_set_%s;\n", effects[i]); + *strchr(effects[i].filename, '.') = 0; + + if (effects[i].ifdef) + fprintf(h, "%s", effects[i].ifdef); + + fprintf(h, "extern char *particle_set_%s;\n", effects[i].filename); + fprintf(h, "#define R_PARTSET_BUILTINS_%s {\"%s\", &particle_set_%s},\n", effects[i].filename, effects[i].filename, effects[i].filename); + if (effects[i].ifdef) + { + fputs("#else\n", h); + fprintf(h, "#define R_PARTSET_BUILTINS_%s\n", effects[i].filename); + fputs("#endif\n", h); + } + if (i) fprintf(c, "\n\n\n//////////////////////////////////////////////////////\n\n\n"); - fprintf(c, "char *particle_set_%s =\n", effects[i]); + if (effects[i].ifdef) + fprintf(c, "%s", effects[i].ifdef); + fprintf(c, "char *particle_set_%s =\n", effects[i].filename); while(fgets(line, sizeof(line), s)) { j = 0; @@ -74,13 +95,15 @@ int main(void) } } fputs(";\n", c); + if (effects[i].ifdef) + fputs("#endif\n", c); fclose(s); } fputs("#define R_PARTSET_BUILTINS ", h); - for (i = 0; *effects[i]; i++) + for (i = 0; *effects[i].filename; i++) { - fprintf(h, "{\"%s\", &particle_set_%s},", effects[i], effects[i]); + fprintf(h, "R_PARTSET_BUILTINS_%s ", effects[i].filename); } fputs("\n", h); diff --git a/engine/partcfgs/h2part.cfg b/engine/partcfgs/h2part.cfg index e0f8ded5..bfc37f1a 100644 --- a/engine/partcfgs/h2part.cfg +++ b/engine/partcfgs/h2part.cfg @@ -254,6 +254,10 @@ r_part tr_acidball rgbrand 0 0 0 gravity 200 scalefactor 0.4 + lighttime 0 + lightshadows 1 + lightradius 100 120 + lightrgb 0.50 1.00 0.25 } //fixme:test @@ -295,6 +299,11 @@ r_part tr_spit rgbrand 0 0 0 gravity 0 scalefactor 0.3 + + lighttime 0 + lightshadows 1 + lightradius 100 120 + lightrgb -2.00 -1.00 -0.25 } //famine missiles r_part tr_spell @@ -387,6 +396,10 @@ r_part tr_fireball blend add scalefactor 1 scaledelta -15 + lighttime 0 + lightshadows 1 + lightradius 100 120 + lightrgb 2.00 1.00 0.25 } r_part +tr_fireball { diff --git a/engine/partcfgs/high.cfg b/engine/partcfgs/high.cfg index 75e011ab..facd0a4c 100644 --- a/engine/partcfgs/high.cfg +++ b/engine/partcfgs/high.cfg @@ -335,6 +335,8 @@ r_part te_teleport shader { + surfaceparm noshadows + surfaceparm nodlight glslprogram { varying vec2 tcoord; diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index c7adec89..fd32d8ff 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -1158,7 +1158,7 @@ pubprogfuncs_t deffuncs = { PR_ToggleBreakpoint, 0, //numprogs NULL, //parms -#if defined(MINIMAL) || defined(OMIT_QCC) +#if 1//defined(MINIMAL) || defined(OMIT_QCC) NULL, //decompile #else QC_Decompile, @@ -1235,7 +1235,7 @@ progexterns_t defexterns = { qclib_null_printf, //void (*printf) (char *, ...); (void*)exit, //void (*Sys_Error) (char *, ...); NULL, //void (*Abort) (char *, ...); - sizeof(edictrun_t), //int edictsize; //size of edict_t + NULL, NULL, //void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set NULL, //bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed @@ -1252,6 +1252,8 @@ progexterns_t defexterns = { qclib_malloc, //void *(*memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use memalloc if you want) qclib_free, //void (*memfree) (void * mem); + NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms); + NULL, //relocated NULL, //builtin_t *globalbuiltins; //these are available to all progs 0, //int numglobalbuiltins; @@ -1262,8 +1264,7 @@ progexterns_t defexterns = { &safesv_edicts, //struct edict_s **sv_edicts; &safesv_num_edicts, //int *sv_num_edicts; - - NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms); + sizeof(edictrun_t), //int edictsize; //size of edict_t }; //progfuncs_t *progfuncs = NULL; diff --git a/engine/qclib/pr_edict.c b/engine/qclib/pr_edict.c index bacd2023..9525e06b 100644 --- a/engine/qclib/pr_edict.c +++ b/engine/qclib/pr_edict.c @@ -1750,12 +1750,11 @@ char *PDECL PR_SaveEnts(pubprogfuncs_t *ppf, char *buf, int *bufofs, int bufmax, { if (!pr_progstate[a].progs) continue; - PR_SwitchProgs(progfuncs, a); { AddS (qcva("progs %i {\n", a)); AddS (qcva("\"filename\" \"%s\"\n", pr_progstate[a].filename)); - AddS (qcva("\"crc\" \"%i\"\n", pr_progs->crc)); - AddS (qcva("\"numbuiltins\" \"%i\"\n", current_progstate->numbuiltins)); + AddS (qcva("\"crc\" \"%i\"\n", pr_progstate[a].progs->crc)); + AddS (qcva("\"numbuiltins\" \"%i\"\n", pr_progstate[a].numbuiltins)); AddS ("}\n"); } } @@ -1959,7 +1958,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, float killonspawnfl Sys_Error("Bad key \"%s\" in progs block", qcc_token); } - PR_ReallyLoadProgs(progfuncs, filename, header_crc, &pr_progstate[num], true); + PR_ReallyLoadProgs(progfuncs, filename, &pr_progstate[num], true); if (!externs->builtinsfor) { // Sys_Error("Couldn't reset the builtin functions"); @@ -2500,7 +2499,7 @@ char *decode(int complen, int len, int method, char *info, char *buffer); PR_LoadProgs =============== */ -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, int headercrc, progstate_t *progstate, pbool complain) +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain) { unsigned int i, j, type; // extensionbuiltin_t *eb; @@ -2523,6 +2522,7 @@ int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, int header dfunction_t *fnc; mfunction_t *fnc2; dstatement16_t *st16; + size_t fsz; int hmark=0xffffffff; @@ -2577,7 +2577,7 @@ retry: hmark = PRHunkMark(progfuncs); pr_progs = PRHunkAlloc(progfuncs, len+1, "proginfo"); - if (!externs->ReadFile(filename, pr_progs, len+1)) + if (!externs->ReadFile(filename, pr_progs, len+1, &fsz)) { if (!complain) return false; @@ -2660,8 +2660,7 @@ retry: if (!trysleft) //the progs exists, let's just be happy about it. printf("Progs is out of date and uncompilable\n"); - if (headercrc != -1 && headercrc != 0) - if (pr_progs->crc != headercrc && pr_progs->crc != 0) //This shouldn't affect us. However, it does adjust expectations and usage of builtins. + if (externs->CheckHeaderCrc && !externs->CheckHeaderCrc(&progfuncs->funcs, pr_typecurrent, pr_progs->crc)) { // printf ("%s system vars have been modified, progdefs.h is out of date\n", filename); PRHunkFree(progfuncs, hmark); @@ -2822,7 +2821,7 @@ retry: if ((len=externs->FileSize(lnoname))>0) { file = PRHunkAlloc(progfuncs, len+1, "line numbers"); - if (externs->ReadFile(lnoname, file, len+1)) + if (externs->ReadFile(lnoname, file, len+1, &fsz)) { if ( file[0] != lnotype || file[1] != version @@ -2929,9 +2928,6 @@ retry: } } - if (reorg) - reorg = (headercrc != -1); - QC_FlushProgsOffsets(progfuncs); switch(current_progstate->structtype) { @@ -3154,7 +3150,7 @@ retry: break; } - +/* if (headercrc == -1) { isfriked = true; @@ -3162,7 +3158,7 @@ retry: Sys_Error("Decompiling a bigprogs"); return true; } - +*/ progstype = current_progstate-pr_progstate; // QC_StartShares(progfuncs); diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index b1693c0d..c2dab69a 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -658,14 +658,13 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, char *name, eval_t **val) return &def; } -static char *COM_TrimString(char *str) +static char *COM_TrimString(char *str, char *buffer, int buffersize) { int i; - static char buffer[256]; while (*str <= ' ' && *str>'\0') str++; - for (i = 0; i < 255; i++) + for (i = 0; i < buffersize-1; i++) { if (*str <= ' ') break; @@ -736,7 +735,8 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, char *key, eval_t **result, etype_ fdef = ED_FindField(progfuncs, c2); if (!fdef) { - c2 = COM_TrimString(c2); + char trimmed[256]; + c2 = COM_TrimString(c2, trimmed, sizeof(trimmed)); def = ED_FindLocalOrGlobal(progfuncs, c2, &fval); if (def && def->type == ev_field) { diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index 172f049a..4d7ab3cc 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -94,7 +94,7 @@ pbool PR_SwitchProgsParms(progfuncs_t *progfuncs, progsnum_t newpr) //from 2 to return PR_SwitchProgs(progfuncs, newpr); } -progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s, int headercrc, builtin_t *builtins, int numbuiltins) +progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s, builtin_t *builtins, int numbuiltins) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; unsigned int a; @@ -106,7 +106,7 @@ progsnum_t PDECL PR_LoadProgs(pubprogfuncs_t *ppf, const char *s, int headercrc, { pr_typecurrent = a; current_progstate = &pr_progstate[a]; - if (PR_ReallyLoadProgs(progfuncs, s, headercrc, &pr_progstate[a], false)) //try and load it + if (PR_ReallyLoadProgs(progfuncs, s, &pr_progstate[a], false)) //try and load it { current_progstate->builtins = builtins; current_progstate->numbuiltins = numbuiltins; diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 6358b6d5..92095c3a 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -363,8 +363,8 @@ typedef struct extensionbuiltin_s { void PR_Init (void); void PDECL PR_ExecuteProgram (pubprogfuncs_t *progfuncs, func_t fnum); -int PDECL PR_LoadProgs(pubprogfuncs_t *progfncs, const char *s, int headercrc, builtin_t *builtins, int numbuiltins); -int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, int headercrc, progstate_t *progstate, pbool complain); +int PDECL PR_LoadProgs(pubprogfuncs_t *progfncs, const char *s, builtin_t *builtins, int numbuiltins); +int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain); void *PRHunkAlloc(progfuncs_t *progfuncs, int ammount, char *name); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 0beb2ba5..4aa7c39d 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -69,7 +69,7 @@ struct pubprogfuncs_s void (PDECL *CloseProgs) (pubprogfuncs_t *inst); void (PDECL *Configure) (pubprogfuncs_t *prinst, size_t addressablesize, int max_progs, pbool enableprofiling); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM. - progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, const char *s, int headercrc, builtin_t *builtins, int numbuiltins); //load a progs + progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, const char *s, builtin_t *builtins, int numbuiltins); //load a progs int (PDECL *InitEnts) (pubprogfuncs_t *prinst, int max_ents); //returns size of edicts for use with nextedict macro void (PDECL *ExecuteProgram) (pubprogfuncs_t *prinst, func_t fnum); //start execution struct globalvars_s *(PDECL *globals) (pubprogfuncs_t *prinst, progsnum_t num); //get the globals of a progs @@ -95,8 +95,8 @@ struct pubprogfuncs_s int (PDECL *StartCompile) (pubprogfuncs_t *prinst, int argv, char **argc); //1 if can compile, 0 if failed to compile int (PDECL *ContinueCompile) (pubprogfuncs_t *prinst); //2 if finished, 1 if more to go, 0 if failed - char *(PDECL *filefromprogs) (pubprogfuncs_t *prinst, progsnum_t prnum, char *fname, int *size, char *buffer); //reveals encoded/added files from already loaded progs - char *(PDECL *filefromnewprogs) (pubprogfuncs_t *prinst, char *prname, char *fname, int *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere + char *(PDECL *filefromprogs) (pubprogfuncs_t *prinst, progsnum_t prnum, char *fname, size_t *size, char *buffer); //reveals encoded/added files from already loaded progs + char *(PDECL *filefromnewprogs) (pubprogfuncs_t *prinst, char *prname, char *fname, size_t *size, char *buffer); //reveals encoded/added files from a progs on the disk somewhere void (PDECL *ED_Print) (pubprogfuncs_t *prinst, struct edict_s *ed); char *(PDECL *save_ents) (pubprogfuncs_t *prinst, char *buf, int *size, int maxsize, int mode); //dump the entire progs info into one big self allocated string @@ -177,30 +177,32 @@ struct pubprogfuncs_s typedef struct progexterns_s { int progsversion; //PROGSTRUCT_VERSION - unsigned char *(PDECL *ReadFile) (const char *fname, void *buffer, int len); - int (PDECL *FileSize) (const char *fname); //-1 if file does not exist - pbool (PDECL *WriteFile) (const char *name, void *data, int len); - int (VARGS *Printf) (const char *, ...) LIKEPRINTF(1); - void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1); - void (VARGS *Abort) (char *, ...) LIKEPRINTF(1); - int edictsize; //size of edict_t + unsigned char *(PDECL *ReadFile) (const char *fname, void *buffer, int len, size_t *sz); + int (PDECL *FileSize) (const char *fname); //-1 if file does not exist + pbool (PDECL *WriteFile) (const char *name, void *data, int len); + int (VARGS *Printf) (const char *, ...) LIKEPRINTF(1); + void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1); + void (VARGS *Abort) (char *, ...) LIKEPRINTF(1); + pbool (PDECL *CheckHeaderCrc) (pubprogfuncs_t *inst, progsnum_t idx, int crc); - void (PDECL *entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set - pbool (PDECL *entcanfree) (struct edict_s *ent); //return true to stop ent from being freed - void (ASMCALL *stateop) (pubprogfuncs_t *prinst, float var, func_t func); //what to do on qc's state opcode. - void (ASMCALL *cstateop) (pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. - void (ASMCALL *cwstateop) (pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. - void (ASMCALL *thinktimeop) (pubprogfuncs_t *prinst, struct edict_s *ent, float varb); //a hexen2 opcode. + void (PDECL *entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set + pbool (PDECL *entcanfree) (struct edict_s *ent); //return true to stop ent from being freed + void (ASMCALL *stateop) (pubprogfuncs_t *prinst, float var, func_t func); //what to do on qc's state opcode. + void (ASMCALL *cstateop) (pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. + void (ASMCALL *cwstateop) (pubprogfuncs_t *prinst, float vara, float varb, func_t currentfunc); //a hexen2 opcode. + void (ASMCALL *thinktimeop) (pubprogfuncs_t *prinst, struct edict_s *ent, float varb); //a hexen2 opcode. //used when loading a game - builtin_t *(PDECL *builtinsfor) (int num, int headercrc); //must return a pointer to the builtins that were used before the state was saved. - void (PDECL *loadcompleate) (int edictsize); //notification to reset any pointers. - pbool (PDECL *badfield)(pubprogfuncs_t *prinst, struct edict_s *ent, const char *keyname, const char *value); //called for any fields that are not registered + builtin_t *(PDECL *builtinsfor) (int num, int headercrc); //must return a pointer to the builtins that were used before the state was saved. + void (PDECL *loadcompleate) (int edictsize); //notification to reset any pointers. + pbool (PDECL *badfield) (pubprogfuncs_t *prinst, struct edict_s *ent, const char *keyname, const char *value); //called for any fields that are not registered - void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) - void (VARGS *memfree) (void * mem); + void *(VARGS *memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use malloc if you want) + void (VARGS *memfree) (void * mem); + int (PDECL *useeditor) (pubprogfuncs_t *prinst, char *filename, int line, int statement, int nump, char **parms); //called on syntax errors or step-by-step debugging. + void (PDECL *addressablerelocated) (pubprogfuncs_t *progfuncs, char *oldb, char *newb, int oldlen); //called when the progs memory was resized. you must fix up all pointers to globals, strings, fields, addressable blocks. builtin_t *globalbuiltins; //these are available to all progs int numglobalbuiltins; @@ -211,9 +213,7 @@ typedef struct progexterns_s { struct edict_s **sv_edicts; //pointer to the engine's reference to world. unsigned int *sv_num_edicts; //pointer to the engine's edict count. - - int (PDECL *useeditor) (pubprogfuncs_t *prinst, char *filename, int line, int statement, int nump, char **parms); //called on syntax errors or step-by-step debugging. - void (PDECL *addressablerelocated) (pubprogfuncs_t *progfuncs, char *oldb, char *newb, int oldlen); //called when the progs memory was resized. you must fix up all pointers to globals, strings, fields, addressable blocks. + int edictsize; //size of edict_t void *user; /*contains the owner's world reference in FTE*/ } progparms_t, progexterns_t; @@ -237,12 +237,12 @@ typedef union eval_s #define PR_CURRENT -1 #define PR_ANY -2 //not always valid. Use for finding funcs #define PR_ANYBACK -3 -#define PROGSTRUCT_VERSION 2 +#define PROGSTRUCT_VERSION 3 #ifndef DLL_PROG #define PR_Configure(pf, memsize, max_progs, profiling) (*pf->Configure) (pf, memsize, max_progs, profiling) -#define PR_LoadProgs(pf, s, headercrc, builtins, numb) (*pf->LoadProgs) (pf, s, headercrc, builtins, numb) +#define PR_LoadProgs(pf, s, builtins, numb) (*pf->LoadProgs) (pf, s, builtins, numb) #define PR_InitEnts(pf, maxents) (*pf->InitEnts) (pf, maxents) #define PR_ExecuteProgram(pf, fnum) (*pf->ExecuteProgram) (pf, fnum) #define PR_globals(pf, num) (*pf->globals) (pf, num) diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 0b55d684..23c9e175 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -307,8 +307,8 @@ struct accessor_s struct accessor_s *next; struct QCC_type_s *type; struct QCC_type_s *indexertype; //null if not indexer - struct QCC_def_s *get; - struct QCC_def_s *set; + struct QCC_def_s *getset_func[2]; + pbool *getset_isref[2]; char *fieldname; }; diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index d909f4c8..b7168780 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -1103,7 +1103,7 @@ long QCC_LoadFile (char *filename, void **bufferptr) qcc_sourcefile = (qcc_cachedsourcefile_t*)mem; mem += sizeof(qcc_cachedsourcefile_t); - externs->ReadFile(filename, mem, len+2); + externs->ReadFile(filename, mem, len+2, NULL); if (len >= 4 && mem[0] == '\xff' && mem[1] == '\xfe' && mem[2] == '\x00' && mem[3] == '\x00') mem = decodeUTF(UTF32LE, (unsigned char*)mem+4, len-4, &len, false); @@ -1152,7 +1152,7 @@ void QCC_AddFile (char *filename) qcc_sourcefile->file = mem; qcc_sourcefile->type = FT_DATA; - externs->ReadFile(filename, mem, len+1); + externs->ReadFile(filename, mem, len+1, NULL); mem[len] = '\0'; } void *FS_ReadToMem(char *filename, void *mem, int *len) @@ -1162,7 +1162,7 @@ void *FS_ReadToMem(char *filename, void *mem, int *len) *len = externs->FileSize(filename); mem = externs->memalloc(*len); } - return externs->ReadFile(filename, mem, *len); + return externs->ReadFile(filename, mem, *len, NULL); } void FS_CloseFromMem(void *mem) diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 19d77b21..06776654 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -6653,14 +6653,22 @@ QCC_def_t *QCC_RefToDef(QCC_ref_t *ref, pbool freetemps) case REF_STRING: return QCC_PR_StatementFlags(&pr_opcodes[OP_LOADP_C], ref->base, ref->index, NULL, freetemps?0:(STFL_PRESERVEA|STFL_PRESERVEB)); case REF_ACCESSOR: - if (ref->accessor && ref->accessor->get) + if (ref->accessor && ref->accessor->getset_func[0]) { int args = 0; QCC_def_t *arg[2] = {NULL, NULL}; - arg[args++] = ref->base; + if (ref->accessor->getset_isref[0]) + { + if (ref->base->temp) + QCC_PR_ParseErrorPrintDef(ERR_NOFUNC, ref->base, "Accessor %s(get) cannot be used on a temporary accessor reference", ref->accessor?ref->accessor->fieldname:""); + //there shouldn't really be any need for this, but its problematic if the accessor is a field. + arg[args++] = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], ref->base, NULL, NULL); + } + else + arg[args++] = ref->base; if (ref->accessor->indexertype) arg[args++] = ref->index?QCC_SupplyConversion(ref->index, ref->accessor->indexertype->type, true):QCC_MakeIntConst(0); - return QCC_PR_GenerateFunctionCall(NULL, ref->accessor->get, arg, NULL, args); + return QCC_PR_GenerateFunctionCall(NULL, ref->accessor->getset_func[0], arg, NULL, args); } else QCC_PR_ParseErrorPrintDef(ERR_NOFUNC, ref->base, "Accessor %s has no get function", ref->accessor?ref->accessor->fieldname:""); @@ -6805,16 +6813,24 @@ QCC_def_t *QCC_StoreToRef(QCC_ref_t *dest, QCC_def_t *source, pbool readable, pb } break; case REF_ACCESSOR: - if (dest->accessor && dest->accessor->set) + if (dest->accessor && dest->accessor->getset_func[1]) { int args = 0; QCC_def_t *arg[3] = {NULL, NULL, NULL}; - arg[args++] = dest->base; + if (dest->accessor->getset_isref[1]) + { + if (dest->base->temp) + QCC_PR_ParseErrorPrintDef(ERR_NOFUNC, dest->base, "Accessor %s(set) cannot be used on a temporary accessor reference", dest->accessor?dest->accessor->fieldname:""); + //there shouldn't really be any need for this, but its problematic if the accessor is a field. + arg[args++] = QCC_PR_Statement(&pr_opcodes[OP_GLOBALADDRESS], dest->base, NULL, NULL); + } + else + arg[args++] = dest->base; if (dest->accessor->indexertype) arg[args++] = dest->index?QCC_SupplyConversion(dest->index, dest->accessor->indexertype->type, true):QCC_MakeIntConst(0); arg[args++] = source; - QCC_FreeTemp(QCC_PR_GenerateFunctionCall(NULL, dest->accessor->set, arg, NULL/*argt*/, args)); + QCC_FreeTemp(QCC_PR_GenerateFunctionCall(NULL, dest->accessor->getset_func[1], arg, NULL/*argt*/, args)); } else QCC_PR_ParseErrorPrintDef(ERR_NOFUNC, dest->base, "Accessor has no set function"); diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 023ca506..d7182f8c 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -4383,6 +4383,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) QCC_type_t *arg[3]; int args; char *indexname; + pbool isref; do @@ -4393,6 +4394,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) setnotget = false; else break; + isref = QCC_PR_CheckToken("*"); fieldtypename = QCC_PR_ParseName(); fieldtype = QCC_TypeForName(fieldtypename); @@ -4427,7 +4429,10 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) args = 0; strcpy (pr_parm_names[args], "this"); argname[args] = "this"; - arg[args++] = newt; + if (isref) + arg[args++] = QCC_PointerTypeTo(newt); + else + arg[args++] = newt; if (indextype) { strcpy (pr_parm_names[args], indexname); @@ -4507,18 +4512,9 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) newt->accessors = acc; } - if (setnotget) - { - if (acc->set) - QCC_Error(ERR_TOOMANYINITIALISERS, "%s::set_%s already declared", newt->name, accessorname); - acc->set = def; - } - else - { - if (acc->get) - QCC_Error(ERR_TOOMANYINITIALISERS, "%s::get_%s already declared", newt->name, accessorname); - acc->get = def; - } + if (acc->getset_func[setnotget]) + QCC_Error(ERR_TOOMANYINITIALISERS, "%s::%s_%s already declared", newt->name, setnotget?"set":"get", accessorname); + acc->getset_func[setnotget] = def; } while (QCC_PR_CheckToken(",") || QCC_PR_CheckToken(";")); QCC_PR_Expect("}"); } diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 51e40ec5..3aaf7c8d 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -3120,12 +3120,12 @@ pbool QCC_main (int argc, char **argv) //as part of the quake engine compressoutput = 0; p = externs->FileSize("qcc.cfg"); - if (p < 0) - p = externs->FileSize("src/qcc.cfg"); +// if (p < 0) +// p = externs->FileSize("src/qcc.cfg"); if (p>0) { s = qccHunkAlloc(p+1); - s = externs->ReadFile("qcc.cfg", s, p); + s = externs->ReadFile("qcc.cfg", s, p, NULL); while(1) { diff --git a/engine/qclib/qcd.h b/engine/qclib/qcd.h index 1c755a2e..1c10138c 100644 --- a/engine/qclib/qcd.h +++ b/engine/qclib/qcd.h @@ -2,6 +2,6 @@ pbool QC_decodeMethodSupported(int method); char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, char *info, char *buffer); int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle); -char *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, char *fname, int *size, char *buffer); -char *filefromnewprogs(pubprogfuncs_t *progfuncs, char *prname, char *fname, int *size, char *buffer);//fixme - remove parm 1 +char *PDECL filefromprogs(pubprogfuncs_t *progfuncs, progsnum_t prnum, char *fname, size_t *size, char *buffer); +char *filefromnewprogs(pubprogfuncs_t *progfuncs, char *prname, char *fname, size_t *size, char *buffer);//fixme - remove parm 1 diff --git a/engine/qclib/qcd_main.c b/engine/qclib/qcd_main.c index 76b7a293..d4e8b95f 100644 --- a/engine/qclib/qcd_main.c +++ b/engine/qclib/qcd_main.c @@ -156,11 +156,13 @@ int QC_encode(progfuncs_t *progfuncs, int len, int method, char *in, int handle) } #endif -char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, char *fname, int *size, char *buffer) +char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, char *fname, size_t *size, char *buffer) { progfuncs_t *progfuncs = (progfuncs_t*)ppf; int num; includeddatafile_t *s; + if (size) + *size = 0; if (!pr_progstate[prnum].progs) return NULL; if (pr_progstate[prnum].progs->version != PROG_EXTENDEDVERSION) @@ -178,7 +180,7 @@ char *PDECL filefromprogs(pubprogfuncs_t *ppf, progsnum_t prnum, char *fname, in if (size) *size = s->size; if (!buffer) - return (char *)0xffffffff; + return NULL; return QC_decode(progfuncs, s->compsize, s->size, s->compmethod, (char *)pr_progstate[prnum].progs+s->ofs, buffer); } diff --git a/engine/qclib/qcdecomp.c b/engine/qclib/qcdecomp.c index f1d35341..f68d1f89 100644 --- a/engine/qclib/qcdecomp.c +++ b/engine/qclib/qcdecomp.c @@ -62,11 +62,6 @@ QCC_type_t *QCC_PR_NewType (char *name, int basictype, pbool typedefed); jmp_buf decompilestatementfailure; #if 0 -pbool QC_Decompile(progfuncs_t *progfuncs, char *fname) -{ - return false; -} -#else QCC_type_t **ofstype; qbyte *ofsflags; @@ -859,7 +854,7 @@ pbool PDECL QC_Decompile(pubprogfuncs_t *ppf, char *fname) qccprogfuncs = progfuncs; op=current_progstate; - if (!PR_ReallyLoadProgs(progfuncs, fname, -1, &progs, false)) + if (!PR_ReallyLoadProgs(progfuncs, fname, &progs, false)) { return false; } diff --git a/engine/server/net_preparse.c b/engine/server/net_preparse.c index c1410b6d..a9e60e03 100644 --- a/engine/server/net_preparse.c +++ b/engine/server/net_preparse.c @@ -735,10 +735,20 @@ void NPP_NQFlush(void) // bufferlen = 0; break; case svcdp_hidelmp: + if (progstype == PROG_UNKNOWN) + { + bufferlen = 0; + break; + } requireextension = PEXT_SHOWPIC; buffer[0] = svcfte_hidepic; break; case svcdp_showlmp: + if (progstype == PROG_UNKNOWN) + { + bufferlen = 0; + break; + } requireextension = PEXT_SHOWPIC; memmove(buffer+2, buffer+1, bufferlen-1); bufferlen++; @@ -1233,16 +1243,37 @@ void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw) case svc_updatename: if (bufferlen < 2) break; //don't truncate the name if the mod is sending the slot number - case svcdp_hidelmp: case svc_stufftext: case svc_centerprint: case svc_cutscene: if (!data) protocollen = bufferlen; break; - case svcdp_showlmp: // [string] slotname [string] lmpfilename [byte] x [byte] y - //note: nehara uses bytes! - //and the rest of dp uses shorts. how nasty is that? + case svcdp_hidelmp: + //tenebrae compat: + if (progstype == PROG_UNKNOWN) + { + //svc, coord6, byte, long, long, effectname + if (bufferlen >= sizeof(qbyte)*2+destprim->coordsize*6+sizeof(int)*2 && !data) + protocollen = bufferlen; + break; + } + //nehahra/dp compat + if (!data) + protocollen = bufferlen; + break; + case svcdp_showlmp: + //tenebrae compat: + if (progstype == PROG_UNKNOWN) + { + //svc, coord3, byte, effectname + if (bufferlen >= sizeof(qbyte)*2+destprim->coordsize*3 && !data) + protocollen = bufferlen; + break; + } + // [string] slotname [string] lmpfilename [byte] x [byte] y + //note: nehara uses bytes! + //and the rest of dp uses shorts. how nasty is that? if (!data) { //second string, plus 2 bytes. int i; @@ -3100,7 +3131,7 @@ void NPP_MVDWriteByte(qbyte data, client_t *to, int broadcast) //replacement wri void NPP_Flush(void) { - if (progstype == PROG_NQ) + if (progstype != PROG_QW) NPP_NQFlush(); #ifdef NQPROT else diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 7b8e3884..182f242a 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -58,6 +58,7 @@ cvar_t saved3 = CVARF("saved3", "0", CVAR_ARCHIVE); cvar_t saved4 = CVARF("saved4", "0", CVAR_ARCHIVE); cvar_t temp1 = CVARF("temp1", "0", CVAR_ARCHIVE); cvar_t noexit = CVAR("noexit", "0"); + cvar_t pr_ssqc_memsize = CVARD("pr_ssqc_memsize", "-1", "The ammount of memory available to the QC vm. This has a theoretical maximum of 1gb, but that value can only really be used in 64bit builds. -1 will attempt to use some conservative default, but you may need to increase it. Consider also clearing pr_fixbrokenqccarrays if you need to change this cvar."); /*cvars purely for compat with others*/ @@ -72,10 +73,9 @@ cvar_t pr_maxedicts = CVARAFD("pr_maxedicts", "32768", "max_edicts", CVAR_LATCH, cvar_t pr_no_playerphysics = CVARFD("pr_no_playerphysics", "0", CVAR_LATCH, "Prevents support of the 'SV_PlayerPhysics' QC function. This allows servers to prevent needless breakage of player prediction."); cvar_t pr_no_parsecommand = CVARFD("pr_no_parsecommand", "0", 0, "Provides a way around invalid mod usage of SV_ParseClientCommand, eg xonotic."); -cvar_t pr_sourcedir = CVARD("pr_sourcedir", "src", "Subdirectory where your qc source is located. Used by the internal compiler and qc debugging functionality."); +extern cvar_t pr_sourcedir; cvar_t pr_ssqc_progs = CVARAF("progs", "", "sv_progs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOTFROMSERVER); -cvar_t qc_nonetaccess = CVAR("qc_nonetaccess", "0"); //prevent write_... builtins from doing anything. This means we can run any mod, specific to any engine, on the condition that it also has a qw or nq crc. -cvar_t qc_netpreparse = CVAR("qc_netpreparse", "1"); //server-side writebyte protocol translation +cvar_t pr_nonetaccess = CVARD("pr_nonetaccess", "0", "Block all direct access to network buffers (the writebyte builtin and friends will ignore the call)."); //prevent write_... builtins from doing anything. This means we can run any mod, specific to any engine, on the condition that it also has a qw or nq crc. cvar_t pr_overridebuiltins = CVAR("pr_overridebuiltins", "1"); @@ -466,7 +466,7 @@ model_t *SVPR_GetCModel(world_t *w, int modelindex) if ((unsigned int)modelindex < MAX_PRECACHE_MODELS) { if (!sv.models[modelindex] && sv.strings.model_precache[modelindex]) - sv.models[modelindex] = Mod_ForName(sv.strings.model_precache[modelindex], MLV_WARN); + sv.models[modelindex] = Mod_ForName(Mod_FixName(sv.strings.model_precache[modelindex], sv.modelname), MLV_WARN); return sv.models[modelindex]; } else @@ -517,6 +517,37 @@ static qboolean SVPR_Event_ContentsTransition(world_t *w, wedict_t *ent, int old return false; //do legacy behaviour } +#define QW_PROGHEADER_CRC 54730 +#define NQ_PROGHEADER_CRC 5927 +#define PREREL_PROGHEADER_CRC 26940 //prerelease +#define H2_PROGHEADER_CRC 38488 //basic hexen2 +#define H2MP_PROGHEADER_CRC 26905 //hexen2 mission pack uses slightly different defs... *sigh*... +#define H2DEMO_PROGHEADER_CRC 14046 //I'm guessing this is from the original release or something + +pbool PDECL PR_SSQC_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc) +{ + progstype_t modtype; + if (crc == QW_PROGHEADER_CRC) + modtype = PROG_QW; + else if (crc == NQ_PROGHEADER_CRC) + modtype = PROG_NQ; +#ifdef HEXEN2 + else if (crc == H2_PROGHEADER_CRC || crc == H2MP_PROGHEADER_CRC || crc == H2DEMO_PROGHEADER_CRC) + modtype = PROG_H2; +#endif + else if (crc == PREREL_PROGHEADER_CRC) + modtype = PROG_PREREL; + else + modtype = PROG_UNKNOWN; + + //if we didn't see one yet, use the one that just got loaded. + if (progstype == PROG_NONE) + progstype = modtype; + //if the new one differs from the main module, reject it, unless it has crc 0, which we'll allow as a universal mutator (good luck guessing the correct arguments, but hey). + if (progstype != modtype && crc != 0) + return false; + return true; +} void Q_SetProgsParms(qboolean forcompiler) { progstype = PROG_NONE; @@ -525,6 +556,7 @@ void Q_SetProgsParms(qboolean forcompiler) svprogparms.FileSize = COM_FileSize;//int (*FileSize) (char *fname); //-1 if file does not exist svprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len); svprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...); + svprogparms.CheckHeaderCrc = PR_SSQC_CheckHeaderCrc; svprogparms.Sys_Error = Sys_Error; svprogparms.Abort = SV_Error; svprogparms.edictsize = sizeof(edict_t); @@ -630,13 +662,6 @@ void PR_Shutdown(void) #endif } -#define QW_PROGHEADER_CRC 54730 -#define NQ_PROGHEADER_CRC 5927 -#define PREREL_PROGHEADER_CRC 26940 //prerelease -#define H2_PROGHEADER_CRC 38488 //basic hexen2 -#define H2MP_PROGHEADER_CRC 26905 //hexen2 mission pack uses slightly different defs... *sigh*... -#define H2DEMO_PROGHEADER_CRC 14046 //I'm guessing this is from the original release or something - void PR_LoadGlabalStruct(void) { static float svphysicsmode = 2; @@ -895,6 +920,9 @@ progsnum_t AddProgs(const char *name) globalvars_t *pr_globals; progsnum_t num; int i; + if (strlen(name) >= sizeof(svs.progsnames[0])) + return -1; + for (i = 0; i < svs.numprogs; i++) { if (!strcmp(svs.progsnames[i], name)) @@ -908,68 +936,27 @@ progsnum_t AddProgs(const char *name) svprogparms.autocompile = PR_COMPILECHANGED; - if (progstype == PROG_QW) - num = PR_LoadProgs (svprogfuncs, name, QW_PROGHEADER_CRC, NULL, 0); - else if (progstype == PROG_NQ) - num = PR_LoadProgs (svprogfuncs, name, NQ_PROGHEADER_CRC, NULL, 0); - else if (progstype == PROG_UNKNOWN) - num = PR_LoadProgs (svprogfuncs, name, 0, NULL, 0); - else //if (progstype == PROG_NONE) + num = PR_LoadProgs (svprogfuncs, name, NULL, 0); + sv.world.usesolidcorpse = (progstype != PROG_H2); + if (!i && num != -1) { - progstype = PROG_QW; - num = PR_LoadProgs (svprogfuncs, name, QW_PROGHEADER_CRC, NULL, 0); - if (num == -1) + switch(progstype) { - svprogparms.autocompile = PR_NOCOMPILE; - progstype = PROG_NQ; - num = PR_LoadProgs (svprogfuncs, name, NQ_PROGHEADER_CRC, NULL, 0); - if (num == -1) - { - progstype = PROG_H2; - num = PR_LoadProgs (svprogfuncs, name, H2_PROGHEADER_CRC, NULL, 0); - if (num == -1) //don't commit if bad. - num = PR_LoadProgs (svprogfuncs, name, H2MP_PROGHEADER_CRC, NULL, 0); - if (num == -1) - num = PR_LoadProgs (svprogfuncs, name, H2DEMO_PROGHEADER_CRC, NULL, 0); - if (num == -1) //don't commit if bad. - { - progstype = PROG_PREREL; - num = PR_LoadProgs (svprogfuncs, name, PREREL_PROGHEADER_CRC, NULL, 0); - if (num == -1) - { - progstype = PROG_UNKNOWN; - num = PR_LoadProgs (svprogfuncs, name, 0, NULL, 0); - if (num == -1) //don't commit if bad. - { - progstype = PROG_NONE; - } - else - Cvar_Set(&qc_nonetaccess, "1"); //just in case - } - } - } - } - sv.world.usesolidcorpse = (progstype != PROG_H2); - if (num != -1) - { - switch(progstype) - { - case PROG_QW: - Con_DPrintf("Using QW progs\n"); - break; - case PROG_NQ: - Con_DPrintf("Using NQ progs\n"); - break; - case PROG_H2: - Con_DPrintf("Using H2 progs\n"); - break; - case PROG_PREREL: - Con_DPrintf("Using prerelease progs\n"); - break; - default: - Con_DPrintf("Using unknown progs, assuming NQ\n"); - break; - } + case PROG_QW: + Con_DPrintf("Using QW progs\n"); + break; + case PROG_NQ: + Con_DPrintf("Using NQ progs\n"); + break; + case PROG_H2: + Con_DPrintf("Using H2 progs\n"); + break; + case PROG_PREREL: + Con_DPrintf("Using prerelease progs\n"); + break; + default: + Con_DPrintf("Using unknown progs\n"); + break; } } svprogparms.autocompile = PR_COMPILECHANGED; @@ -1276,7 +1263,6 @@ void PR_Init(void) Cvar_Register(&pr_imitatemvdsv, cvargroup_progs); Cvar_Register(&pr_fixbrokenqccarrays, cvargroup_progs); - Cvar_Register(&pr_sourcedir, cvargroup_progs); Cvar_Register(&pr_maxedicts, cvargroup_progs); Cvar_Register(&pr_no_playerphysics, cvargroup_progs); Cvar_Register(&pr_no_parsecommand, cvargroup_progs); @@ -1307,7 +1293,7 @@ void PR_Init(void) Cvar_Register (&pr_ssqc_progs, cvargroup_progs); Cvar_Register (&pr_compatabilitytest, cvargroup_progs); - Cvar_Register (&qc_nonetaccess, cvargroup_progs); + Cvar_Register (&pr_nonetaccess, cvargroup_progs); Cvar_Register (&pr_overridebuiltins, cvargroup_progs); Cvar_Register (&pr_ssqc_coreonerror, cvargroup_progs); @@ -1387,7 +1373,7 @@ void Q_InitProgs(void) int j, maps; char *f; - f = COM_LoadTempFile("maplist.txt"); + f = COM_LoadTempFile("maplist.txt", NULL); f = COM_Parse(f); maps = atoi(com_token); for (j = 0; j < maps; j++) @@ -1470,7 +1456,7 @@ void Q_InitProgs(void) */ //additional (always) progs as = NULL; - a = COM_LoadStackFile("mod.gam", addons, 2048); + a = COM_LoadStackFile("mod.gam", addons, 2048, NULL); if (a) { @@ -1589,7 +1575,7 @@ void Q_InitProgs(void) } //progs depended on by maps. - a = as = COM_LoadStackFile(va("maps/%s.inf", sv.name), addons, 2048); + a = as = COM_LoadStackFile(va("maps/%s.inf", sv.name), addons, sizeof(addons), NULL); if (a) { as = strstr(a, "qwprogs="); @@ -1984,15 +1970,15 @@ void QC_Clear(void) int prnumforfile; int PR_SizeOfFile(char *filename) { - extern int com_filesize; + size_t sz; // int size; if (!svprogfuncs) return -1; prnumforfile=svs.numprogs-1; while(prnumforfile>=0) { - if ((qbyte *)svprogfuncs->filefromprogs(svprogfuncs, prnumforfile, filename, &com_filesize, NULL)==(qbyte *)-1) - return com_filesize; + if ((qbyte *)svprogfuncs->filefromprogs(svprogfuncs, prnumforfile, filename, &sz, NULL)==(qbyte *)-1) + return sz; prnumforfile--; } return -1; @@ -2380,7 +2366,7 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m) #endif m = sv.strings.model_precache[i] = PR_AddString(prinst, m, 0, false); if (!strcmp(m + strlen(m) - 4, ".bsp")) //always precache bsps - sv.models[i] = Mod_FindName(m); + sv.models[i] = Mod_FindName(Mod_FixName(m, sv.strings.model_precache[1])); Con_Printf("WARNING: SV_ModelIndex: model %s not precached\n", m); if (sv.state != ss_loading) @@ -2410,9 +2396,12 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m) // if it is an inline model, get the size information for it if (m && (m[0] == '*' || (*m&&progstype == PROG_H2))) { - mod = Mod_ForName (m, MLV_WARN); + mod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARN); if (mod) { + while(mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + VectorCopy (mod->mins, e->v->mins); VectorCopy (mod->maxs, e->v->maxs); VectorSubtract (mod->maxs, mod->mins, e->v->size); @@ -2448,9 +2437,12 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m) //qw dedicated servers only load bsps (better) if (mod) { - mod = Mod_ForName (m, MLV_WARN); + mod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARN); if (mod) { + while(mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + VectorCopy (mod->mins, e->v->mins); VectorCopy (mod->maxs, e->v->maxs); VectorSubtract (mod->maxs, mod->mins, e->v->size); @@ -2477,6 +2469,9 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m) { if (mod && mod->type != mod_alias) { + while(mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); + VectorCopy (mod->mins, e->v->mins); VectorCopy (mod->maxs, e->v->maxs); VectorSubtract (mod->maxs, mod->mins, e->v->size); @@ -3826,7 +3821,7 @@ int PF_precache_model_Internal (pubprogfuncs_t *prinst, const char *s, qboolean sv.strings.model_precache[i] = PR_AddString(prinst, s, 0, false); s = sv.strings.model_precache[i]; if (!strcmp(s + strlen(s) - 4, ".bsp") || sv_gameplayfix_setmodelrealbox.ival) - sv.models[i] = Mod_ForName(s, MLV_WARN); + sv.models[i] = Mod_ForName(Mod_FixName(s, sv.modelname), MLV_WARN); else { /*touch the file, so any packs will be referenced*/ @@ -4387,7 +4382,7 @@ void QCBUILTIN PF_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK @@ -4395,7 +4390,7 @@ void QCBUILTIN PF_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteByte(dest, val); return; @@ -4430,14 +4425,14 @@ void QCBUILTIN PF_WriteChar (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteChar(dest, val); return; @@ -4472,14 +4467,14 @@ void QCBUILTIN PF_WriteShort (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteShort(dest, val); return; @@ -4513,14 +4508,14 @@ void QCBUILTIN PF_WriteLong (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteLong(dest, G_FLOAT(OFS_PARM1)); return; @@ -4554,14 +4549,14 @@ void QCBUILTIN PF_WriteAngle (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteAngle(dest, G_FLOAT(OFS_PARM1)); return; @@ -4595,14 +4590,14 @@ void QCBUILTIN PF_WriteCoord (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteCoord(dest, G_FLOAT(OFS_PARM1)); return; @@ -4636,14 +4631,14 @@ void QCBUILTIN PF_WriteFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl return; } - if (qc_nonetaccess.value) + if (pr_nonetaccess.value) return; #ifdef SERVER_DEMO_PLAYBACK if (sv.demofile) return; #endif - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteFloat(dest, G_FLOAT(OFS_PARM1)); return; @@ -4676,14 +4671,14 @@ void PF_WriteString_Internal (int target, const char *str) return; } - if (qc_nonetaccess.value + if (pr_nonetaccess.value #ifdef SERVER_DEMO_PLAYBACK || sv.demofile #endif ) return; - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteString(target, str); return; @@ -4724,14 +4719,14 @@ void QCBUILTIN PF_WriteEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; } - if (qc_nonetaccess.value + if (pr_nonetaccess.value #ifdef SERVER_DEMO_PLAYBACK || sv.demofile #endif ) return; - if (progstype == PROG_NQ || progstype == PROG_H2) + if (progstype != PROG_QW) { NPP_NQWriteEntity(dest, (short)G_EDICTNUM(prinst, OFS_PARM1)); return; @@ -4763,7 +4758,7 @@ static void QCBUILTIN PF_WriteString2 (pubprogfuncs_t *prinst, struct globalvars int old; char *str; - if (G_FLOAT(OFS_PARM0) != MSG_CSQC && (qc_nonetaccess.value + if (G_FLOAT(OFS_PARM0) != MSG_CSQC && (pr_nonetaccess.value #ifdef SERVER_DEMO_PLAYBACK || sv.demofile #endif @@ -4986,6 +4981,7 @@ void PF_tempentity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //============================================================================= int SV_ModelIndex (const char *name); +void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *client); void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -5005,26 +5001,9 @@ void QCBUILTIN PF_makestatic (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl state = &sv_staticentities[sv.num_static_entities++]; memset(state, 0, sizeof(*state)); - state->number = sv.num_static_entities; - VectorCopy (ent->v->origin, state->origin); - VectorCopy (ent->v->angles, state->angles); - state->modelindex = mdlindex;//ent->v->modelindex; - state->frame = ent->v->frame; - state->colormap = ent->v->colormap; - state->skinnum = ent->v->skin; - state->effects = ent->v->effects; - state->hexen2flags = ent->xv->drawflags; - state->abslight = (int)(ent->xv->abslight*255) & 255; - state->trans = ent->xv->alpha*255; - if (!ent->xv->alpha) - state->trans = 255; - state->fatness = ent->xv->fatness; - state->scale = ent->xv->scale*16.0; - if (!ent->xv->scale) - state->scale = 1*16; - if (progstype != PROG_QW) //don't send extra nq effects to a qw client. - state->effects &= EF_BRIGHTLIGHT | EF_DIMLIGHT; + SV_Snapshot_BuildStateQ1(state, ent, NULL); + state->number = sv.num_static_entities; // throw the entity away now ED_Free (svprogfuncs, ent); @@ -5074,6 +5053,7 @@ void QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_g return; sv.mapchangelocked = true; +#ifdef HEXEN2 if (progstype == PROG_H2) { COM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM1), startspot, sizeof(startspot)); @@ -5092,6 +5072,7 @@ void QCBUILTIN PF_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_g } } else +#endif { COM_QuotedString(PR_GetStringOfs(prinst, OFS_PARM0), newmap, sizeof(newmap)); if (svprogfuncs->callargc == 2) @@ -8963,6 +8944,67 @@ static void QCBUILTIN PF_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s NET_SendPacket(NS_SERVER, strlen(contents), contents, &to); } +//be careful to not touch the resource unless we're meant to, to avoid stalling +static void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + int restype = G_FLOAT(OFS_PARM0); + int doload = G_FLOAT(OFS_PARM1); + const char *resname = PR_GetStringOfs(prinst, OFS_PARM2); + int idx; + G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN; + switch(restype) + { + case RESTYPE_MODEL: + for (idx=1 ; idxloadstate == MLS_NOTLOADED) + Mod_LoadModel (mod, MLV_SILENT); //should avoid blocking. + switch(mod->loadstate) + { + default: + case MLS_NOTLOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_NOTLOADED; + break; + case MLS_LOADING: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADING; + break; + case MLS_LOADED: + G_FLOAT(OFS_RETURN) = RESSTATE_LOADED; + break; + case MLS_FAILED: + G_FLOAT(OFS_RETURN) = RESSTATE_FAILED; + break; + } + } + return; + } + } + break; + case RESTYPE_SOUND: + for (idx=1 ; idx>24}, - {"MF_GRENADE", "const float", QW|NQ, NULL, EF_MF_GRENADE>>24}, - {"MF_GIB", "const float", QW|NQ, NULL, EF_MF_GIB>>24}, - {"MF_ROTATE", "const float", QW|NQ, NULL, EF_MF_ROTATE>>24}, - {"MF_TRACER", "const float", QW|NQ, NULL, EF_MF_TRACER>>24}, - {"MF_ZOMGIB", "const float", QW|NQ, NULL, EF_MF_ZOMGIB>>24}, - {"MF_TRACER2", "const float", QW|NQ, NULL, EF_MF_TRACER2>>24}, - {"MF_TRACER3", "const float", QW|NQ, NULL, EF_MF_TRACER3>>24}, + {"MF_ROCKET", "const float", QW|NQ|CS, NULL, EF_MF_ROCKET>>24}, + {"MF_GRENADE", "const float", QW|NQ|CS, NULL, EF_MF_GRENADE>>24}, + {"MF_GIB", "const float", QW|NQ|CS, NULL, EF_MF_GIB>>24}, + {"MF_ROTATE", "const float", QW|NQ|CS, NULL, EF_MF_ROTATE>>24}, + {"MF_TRACER", "const float", QW|NQ|CS, NULL, EF_MF_TRACER>>24}, + {"MF_ZOMGIB", "const float", QW|NQ|CS, NULL, EF_MF_ZOMGIB>>24}, + {"MF_TRACER2", "const float", QW|NQ|CS, NULL, EF_MF_TRACER2>>24}, + {"MF_TRACER3", "const float", QW|NQ|CS, NULL, EF_MF_TRACER3>>24}, {"PFLAGS_NOSHADOW", "const float", QW|NQ|CS, "Associated RT lights attached will not cast shadows, making them significantly faster to draw.", PFLAGS_NOSHADOW}, {"PFLAGS_CORONA", "const float", QW|NQ|CS, "Enables support of coronas on the associated rtlights.", PFLAGS_CORONA}, @@ -10780,6 +10838,7 @@ void PR_DumpPlatform_f(void) {"LFIELD_AMBIENTSCALE", "const float", CS, NULL, lfield_ambientscale}, {"LFIELD_DIFFUSESCALE", "const float", CS, NULL, lfield_diffusescale}, {"LFIELD_SPECULARSCALE","const float", CS, NULL, lfield_specularscale}, + {"LFIELD_ROTATION", "const float", CS, NULL, lfield_rotation}, {"LFLAG_NORMALMODE", "const float", CS, NULL, LFLAG_NORMALMODE}, {"LFLAG_REALTIMEMODE", "const float", CS, NULL, LFLAG_REALTIMEMODE}, @@ -10833,31 +10892,52 @@ void PR_DumpPlatform_f(void) {NULL} }; +#define DUMPHELP \ + "Available options:\n" \ + "-Ffte - target only FTE (optimations and additional extensions)\n" \ + "-Tnq - dump specifically NQ fields\n" \ + "-Tqw - dump specifically QW fields\n" \ + "-Tcs - dump specifically CSQC fields\n" \ + "-Tmenu - dump specifically menuqc fields\n" \ + "-Fdefines - generate #defines instead of constants\n" \ + "-Faccessors - use accessors instead of basic types via defines\n" \ + "-O - write to a different qc file\n" targ = 0; for (i = 1; i < Cmd_Argc(); i++) { - if (!stricmp(Cmd_Argv(i), "-Ffte")) + char *arg = Cmd_Argv(i); + if (!stricmp(arg, "-Ffte")) targ |= FTE; - if (!stricmp(Cmd_Argv(i), "-Tnq")) + else if (!stricmp(arg, "-Tnq")) targ |= NQ; - if (!stricmp(Cmd_Argv(i), "-Tcs")) + else if (!stricmp(arg, "-Tcs")) targ |= CS; - if (!stricmp(Cmd_Argv(i), "-Tqw")) + else if (!stricmp(arg, "-Tqw")) targ |= QW; - if (!stricmp(Cmd_Argv(i), "-Tmenu")) + else if (!stricmp(arg, "-Tmenu")) targ |= MENU; - if (!stricmp(Cmd_Argv(i), "-Th2")) + else if (!stricmp(arg, "-Th2")) targ |= H2; - if (!stricmp(Cmd_Argv(i), "-Fdefines")) + else if (!stricmp(arg, "-Fdefines")) defines = true; - if (!stricmp(Cmd_Argv(i), "-Fnodefines")) + else if (!stricmp(arg, "-Fnodefines")) defines = false; - if (!stricmp(Cmd_Argv(i), "-Faccessors")) + else if (!stricmp(arg, "-Faccessors")) accessors = true; - if (!stricmp(Cmd_Argv(i), "-Fnoaccessors")) + else if (!stricmp(arg, "-Fnoaccessors")) accessors = false; - if (!stricmp(Cmd_Argv(i), "-O")) - fname = Cmd_Argv(++i); + else if (!Q_strncasecmp(arg, "-O", 2)) + { + if (arg[2]) + fname = arg+2; + else + fname = Cmd_Argv(++i); + } + else + { + Con_Printf("Unknown argument \"%s\"\n" DUMPHELP, arg); + return; + } } if (!(targ & ALL)) targ |= (QW|NQ|CS|MENU); diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index e20d5713..e1765197 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -200,6 +200,8 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(scale,"Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16.")/*DP_ENT_SCALE*/\ comfieldfloat(fatness,"How many QuakeUnits to push the entity's verticies along their normals by.")/*FTE_PEXT_FATNESS*/\ comfieldfloat(alpha,"The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility.")/*DP_ENT_ALPHA*/\ + comfieldfloat(modelflags,"Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile.")\ + comfieldfunction(customphysics,".void()", "Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate.")\ comfieldentity(tag_entity,NULL)\ comfieldfloat(tag_index,NULL)\ comfieldfloat(skeletonindex,"This object serves as a container for the skeletal bone states used to override the animation data.") /*FTE_CSQC_SKELETONOBJECTS*/\ @@ -257,7 +259,6 @@ and the extension fields are added on the end and can have extra vm-specific stu comfieldfloat(SendFlags,"Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method.")/*EXT_CSQC_1 (one of the DP guys came up with it)*/\ comfieldfloat(Version,"Obsolete, set a SendFlags bit instead.")/*EXT_CSQC (obsolete)*/\ comfieldfloat(pvsflags,"Reconfigures when the entity is visible to clients")/*EXT_CSQC_1*/\ - comfieldfloat(modelflags,"Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile.")\ comfieldfloat(uniquespawnid,"Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient.")/*FTE_ENT_UNIQUESPAWNID*/\ comfieldfunction(customizeentityforclient, ".float()","Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see.") diff --git a/engine/server/savegame.c b/engine/server/savegame.c index c28c57e9..48967eaa 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -879,7 +879,7 @@ void SV_SaveLevelCache(char *savedir, qboolean dontharmgame) if (!FS_NativePath(name, FS_GAMEONLY, syspath, sizeof(syspath))) return; ge->WriteLevel(syspath); - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); return; } #endif @@ -1107,11 +1107,14 @@ void SV_Savegame (char *savename) if (!FS_NativePath(va("saves/%s/game.gsv", savename), FS_GAMEONLY, syspath, sizeof(syspath))) return; ge->WriteGame(syspath, false); - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); } + else #endif - - FS_FlushFSHashReally(); + { + //fixme + FS_FlushFSHashReally(true); + } } void SV_Savegame_f (void) diff --git a/engine/server/server.h b/engine/server/server.h index 2e554d85..1edc5cd3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -819,7 +819,7 @@ typedef struct bannedips_t *bannedips; - char progsnames[MAX_PROGS][32]; + char progsnames[MAX_PROGS][MAX_QPATH]; progsnum_t progsnum[MAX_PROGS]; int numprogs; @@ -1083,18 +1083,6 @@ qboolean SVQ3_Command(void); #endif -// -// sv_phys.c -// -void SV_SetMoveVars(void); -void WPhys_RunNewmis (world_t *w); -qboolean SV_Physics (void); -void WPhys_CheckVelocity (world_t *w, wedict_t *ent); -trace_t WPhys_Trace_Toss (world_t *w, wedict_t *ent, wedict_t *ignore); -void SV_ProgStartFrame (void); -void WPhys_RunEntity (world_t *w, wedict_t *ent); -qboolean WPhys_RunThink (world_t *w, wedict_t *ent); - // // sv_send.c // @@ -1431,3 +1419,4 @@ qboolean HLSV_ClientCommand(client_t *client); void SVHL_DropClient(client_t *drop); void SVHL_ShutdownGame(void); #endif + diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index a3df9e33..9e503f79 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -527,7 +527,7 @@ void SV_Map_f (void) #define SCR_SetLoadingFile(s) #endif - COM_FlushFSCache(); + COM_FlushFSCache(false, true); if (strlen(level) > 4 && (!strcmp(level + strlen(level)-4, ".cin") || @@ -1730,7 +1730,7 @@ static void SV_Status_f (void) if (svs.gametype == GT_PROGS) { int count = 0; - Con_Printf("entities : %i/%i (mem: %i/%i)\n", sv.world.num_edicts, sv.world.max_edicts, sv.world.progs->stringtablesize, sv.world.progs->stringtablemaxsize); + Con_Printf("entities : %i/%i (mem: %u/%u)\n", sv.world.num_edicts, sv.world.max_edicts, sv.world.progs->stringtablesize, sv.world.progs->stringtablemaxsize); for (count = 1; count < MAX_PRECACHE_MODELS; count++) if (!sv.strings.model_precache[count]) break; diff --git a/engine/server/sv_chat.c b/engine/server/sv_chat.c index 29105fc3..0eb7eed1 100644 --- a/engine/server/sv_chat.c +++ b/engine/server/sv_chat.c @@ -73,7 +73,7 @@ float SV_ChatFunc(const char *func); void Chat_GetTag(const char *filename, float tag, char **text, char **condition, char **options) { char *file; char *s; - file = COM_LoadTempFile(va("dialog/%s.dlg", filename)); + file = COM_LoadTempFile(va("dialog/%s.dlg", filename), NULL); if (!file) { *text = "ERROR: File not found"; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index b3fabca6..87cf7eb3 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -1243,6 +1243,8 @@ void SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizebuf_t SV_EmitDeltaEntIndex(msg, 0, true, true); resendbits[outno] = UF_REMOVE; resendnum[outno++] = 0; + + client->pendingentbits[0] &= ~UF_REMOVE; } for(j = 1; j < client->sentents.num_entities; j++) { @@ -2695,7 +2697,7 @@ void SV_GibFilterInit(void) if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) return; - file = COM_LoadStackFile("gibfiltr.cfg", buffer, sizeof(buffer)); + file = COM_LoadStackFile("gibfiltr.cfg", buffer, sizeof(buffer), NULL); if (!file) { Con_DPrintf("gibfiltr.cfg file was not found. Using defaults\n"); @@ -3010,6 +3012,14 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli state->hexen2flags = 0; } } + else if (progstype == PROG_UNKNOWN) + { + if (state->effects & 16) + { + state->effects &= ~16; + state->lightpflags |= PFLAGS_FULLDYNAMIC; + } + } else { if (state->effects & NQEF_NODRAW) @@ -3018,6 +3028,20 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli if (state->number <= sv.allocated_client_slots) // clear only client ents state->effects &= ~ (QWEF_FLAG1|QWEF_FLAG2); + + if ((state->effects & EF_DIMLIGHT) && !(state->effects & (EF_RED|EF_BLUE))) + { + int it = ent->v->items; + state->effects &= ~EF_DIMLIGHT; + if ((it & (IT_INVULNERABILITY|IT_QUAD)) == (IT_INVULNERABILITY|IT_QUAD)) + state->effects |= EF_RED|EF_BLUE; + else if (it & IT_INVULNERABILITY) + state->effects |= EF_RED; + else if (it & IT_QUAD) + state->effects |= EF_BLUE; + else + state->effects |= EF_DIMLIGHT; + } } if (!ent->xv->colormod[0] && !ent->xv->colormod[1] && !ent->xv->colormod[2]) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index afbacd28..7b0aede3 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -33,7 +33,7 @@ int sv_max_staticentities; char localinfo[MAX_LOCALINFO_STRING+1]; // local game info -extern cvar_t skill, sv_loadentfiles; +extern cvar_t skill; extern cvar_t sv_cheats; extern cvar_t sv_bigcoords; extern cvar_t sv_gamespeed; @@ -69,7 +69,7 @@ int SV_ModelIndex (const char *name) #endif sv.strings.model_precache[i] = PR_AddString(svprogfuncs, name, 0, false); if (!strcmp(name + strlen(name) - 4, ".bsp")) - sv.models[i] = Mod_FindName(sv.strings.model_precache[i]); + sv.models[i] = Mod_FindName(Mod_FixName(sv.strings.model_precache[i], sv.strings.model_precache[1])); Con_DPrintf("WARNING: SV_ModelIndex: model %s not precached\n", name); @@ -89,6 +89,7 @@ int SV_ModelIndex (const char *name) return i; } +//looks up a name->index without caching it int SV_SafeModelIndex (char *name) { int i; @@ -549,18 +550,20 @@ void SV_CalcPHS (void) unsigned SV_CheckModel(char *mdl) { - qbyte stackbuf[1024]; // avoid dirtying the cache heap + size_t fsize; qbyte *buf; unsigned short crc; // int len; - buf = (qbyte *)COM_LoadStackFile (mdl, stackbuf, sizeof(stackbuf)); + buf = (qbyte *)COM_LoadFile (mdl, 5, &fsize); if (!buf) return 0; - crc = QCRC_Block(buf, com_filesize); + crc = QCRC_Block(buf, fsize); // for (len = com_filesize; len; len--, buf++) // CRC_ProcessByte(&crc, *buf); + BZ_Free(buf); + return crc; } @@ -765,6 +768,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us int i, j; int spawnflagmask; extern int sv_allow_cheats; + size_t fsz; #ifndef SERVERONLY if (!isDedicated && qrenderer == QR_NONE) @@ -840,8 +844,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us r_worldentity.model = NULL; if (0) cls.state = ca_connected; - if (R_PreNewMap) - R_PreNewMap(); + Surf_PreNewMap(); #ifdef VM_CG CG_Stop(); #endif @@ -914,13 +917,15 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (usecinematic) { qboolean QDECL Mod_LoadQ2BrushModel (model_t *mod, void *buffer); - extern model_t *loadmodel; Q_strncpyz (sv.name, server, sizeof(sv.name)); Q_strncpyz (sv.modelname, "", sizeof(sv.modelname)); - loadmodel = sv.world.worldmodel = Mod_FindName (sv.modelname); - loadmodel->needload = !Mod_LoadQ2BrushModel (sv.world.worldmodel, NULL); + sv.world.worldmodel = Mod_FindName (sv.modelname); + if (Mod_LoadQ2BrushModel (sv.world.worldmodel, NULL)) + sv.world.worldmodel->loadstate = MLS_LOADED; + else + sv.world.worldmodel->loadstate = MLS_FAILED; } else #endif @@ -942,7 +947,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us COM_FCheckExists(sv.modelname); sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR); } - if (!sv.world.worldmodel || sv.world.worldmodel->needload) + if (!sv.world.worldmodel || sv.world.worldmodel->loadstate != MLS_LOADED) Sys_Error("\"%s\" is missing or corrupt\n", sv.modelname); if (sv.world.worldmodel->type != mod_brush && sv.world.worldmodel->type != mod_heightmap) Sys_Error("\"%s\" is not a bsp model\n", sv.modelname); @@ -981,17 +986,18 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us //do we allow csprogs? #ifdef PEXT_CSQC + fsz = 0; if (*sv_csqc_progname.string) - file = COM_LoadTempFile(sv_csqc_progname.string); + file = COM_LoadTempFile(sv_csqc_progname.string, &fsz); else file = NULL; if (file) { char text[64]; - sv.csqcchecksum = Com_BlockChecksum(file, com_filesize); + sv.csqcchecksum = Com_BlockChecksum(file, fsz); sprintf(text, "0x%x", sv.csqcchecksum); Info_SetValueForStarKey(svs.info, "*csprogs", text, MAX_SERVERINFO_STRING); - sprintf(text, "0x%x", com_filesize); + sprintf(text, "0x%x", fsz); Info_SetValueForStarKey(svs.info, "*csprogssize", text, MAX_SERVERINFO_STRING); if (strcmp(sv_csqc_progname.string, "csprogs.dat")) Info_SetValueForStarKey(svs.info, "*csprogsname", sv_csqc_progname.string, MAX_SERVERINFO_STRING); @@ -1111,7 +1117,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us z = Z_TagMalloc(strlen(s)+1, VMFSID_Q1QVM); strcpy(z, s); sv.strings.model_precache[1+i] = z; - sv.models[i+1] = Mod_ForName (z, MLV_WARN); + sv.models[i+1] = Mod_ForName (Mod_FixName(z, sv.modelname), MLV_WARN); } //check player/eyes models for hacks @@ -1141,7 +1147,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us for (i=1 ; inumsubmodels; i++) { Q_snprintfz(sv.strings.configstring[Q2CS_MODELS+1+i], sizeof(sv.strings.configstring[Q2CS_MODELS+1+i]), "*%u", i); - sv.models[i+1] = Mod_ForName (sv.strings.configstring[Q2CS_MODELS+1+i], MLV_WARN); + sv.models[i+1] = Mod_ForName (Mod_FixName(sv.strings.configstring[Q2CS_MODELS+1+i], sv.modelname), MLV_WARN); } } #endif @@ -1355,6 +1361,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us pr_global_struct->serverflags = svs.serverflags; pr_global_struct->time = 0.1; //HACK!!!! A few QuakeC mods expect time to be non-zero in spawn funcs - like prydon gate... +#ifdef HEXEN2 if (progstype == PROG_H2) { cvar_t *cv; @@ -1377,6 +1384,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us if (eval && cv) eval->_float = cv->value; } else +#endif { if (pr_global_ptrs->coop && coop.value) pr_global_struct->coop = coop.value; @@ -1404,6 +1412,7 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us SCR_SetLoadingFile("entities"); if (!deathmatch.value && !*skill.string) //skill was left blank so it doesn't polute serverinfo on deathmatch servers. in single player, we ensure that it gets a proper value. Cvar_Set(&skill, "1"); +#ifdef HEXEN2 if (progstype == PROG_H2) { extern cvar_t coop; @@ -1437,7 +1446,9 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us //don't filter based on player class. we're lame and don't have any real concept of player classes. } - else if (!deathmatch.value) //decide if we are to inhibit single player game ents instead + else +#endif + if (!deathmatch.value) //decide if we are to inhibit single player game ents instead { if (skill.value < 0.5) spawnflagmask = SPAWNFLAG_NOT_EASY; @@ -1449,23 +1460,18 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us else spawnflagmask = SPAWNFLAG_NOT_DEATHMATCH; //do this and get the precaches/start up the game - if (sv_loadentfiles.value) - file = FS_LoadMallocFile(va("maps/%s.ent", server)); - else - file = NULL; - if (file) + if (sv.world.worldmodel->entitiescrc) { char crc[12]; - sprintf(crc, "%i", QCRC_Block(file, com_filesize)); + sprintf(crc, "%i", sv.world.worldmodel->entitiescrc); Info_SetValueForStarKey(svs.info, "*entfile", crc, MAX_SERVERINFO_STRING); } else Info_SetValueForStarKey(svs.info, "*entfile", "", MAX_SERVERINFO_STRING); + file = sv.world.worldmodel->entities; if (!file) - file = sv.world.worldmodel->entities; - if (!file) - file = Z_StrDup(""); + file = ""; switch(svs.gametype) { @@ -1489,9 +1495,6 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us #endif } - if (file != sv.world.worldmodel->entities) - Z_Free(file); - #ifndef SERVERONLY current_loading_size+=10; //SCR_BeginLoadingPlaque(); @@ -1627,7 +1630,11 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us extent = ne; } } - if (extent > (1u<<15)/8) + if (extent > (1u<<15)/8 +#ifdef TERRAIN + || sv.world.worldmodel->terrain +#endif + ) { if (sv.num_signon_buffers > 1 || sv.signon.cursize) Con_Printf("Cannot auto-enable extended coords as the init buffer was used\n"); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index e06f61b9..4f1d25eb 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -135,7 +135,6 @@ cvar_t sv_listen_dp = CVAR("sv_listen_dp", "0"); /*kinda fucked right now*/ cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); cvar_t sv_highchars = CVAR("sv_highchars", "1"); -cvar_t sv_loadentfiles = CVAR("sv_loadentfiles", "1"); cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); cvar_t sv_maxdrate = CVARAF("sv_maxdrate", "100000", "sv_maxdownloadrate", 0); @@ -283,6 +282,9 @@ void SV_Shutdown (void) SV_GibFilterPurge(); NET_Shutdown (); + + COM_DestroyWorkerThread(); + #ifdef WEBSERVER IWebShutdown(); #endif @@ -1416,7 +1418,7 @@ void SVC_GetChallenge (void) int oldest; int oldestTime; - if (!sv_listen_qw.value && !sv_listen_dp.value && !sv_listen_q3.value) + if (!sv_listen_qw.value && !sv_listen_dp.value && !sv_listen_q3.ival) return; oldest = 0; @@ -1531,7 +1533,7 @@ void SVC_GetChallenge (void) #ifdef Q3SERVER if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) { - if (sv_listen_q3.value) + if (sv_listen_q3.ival) { buf = va("challengeResponse %i", svs.challenges[i].challenge); Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf); @@ -1922,7 +1924,7 @@ client_t *SVC_DirectConnect(void) { //connect "\key\val" //this is used by q3 (note, we already decrypted the huffman connection packet in a hack) - if (!sv_listen_q3.value) + if (!sv_listen_q3.ival) { if (!sv_listen_nq.value) SV_RejectMessage (SCP_DARKPLACES6, "Server is not accepting quake3 clients at this time.\n", version_string()); @@ -3181,7 +3183,7 @@ qboolean SV_ConnectionlessPacket (void) return true; } - if (sv_listen_q3.value) + if (sv_listen_q3.ival) { if (!strstr(s, "\\name\\")) { //if name isn't in the string, assume they're q3 @@ -3198,7 +3200,7 @@ qboolean SV_ConnectionlessPacket (void) #endif if (secure.value) //FIXME: possible problem for nq clients when enabled { - Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, svs.language, "%c\nThis server requires client validation.\nPlease use the "DISTRIBUTION" validation program\n", A2C_PRINT); + Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, svs.language, "%c\nThis server requires client validation.\nPlease use the "FULLENGINENAME" validation program\n", A2C_PRINT); } else { @@ -4135,6 +4137,8 @@ float SV_Frame (void) float timedelta; float delay; + COM_MainThreadWork(); + start = Sys_DoubleTime (); svs.stats.idle += start - end; end = start; @@ -4449,7 +4453,6 @@ void SV_InitLocal (void) Cvar_Register (&zombietime, cvargroup_servercontrol); Cvar_Register (&sv_pupglow, cvargroup_serverinfo); - Cvar_Register (&sv_loadentfiles, cvargroup_servercontrol); Cvar_Register (&sv_bigcoords, cvargroup_serverphysics); @@ -5142,7 +5145,7 @@ void SV_Init (quakeparms_t *parms) Sys_Init(); - COM_ParsePlusSets(); + COM_ParsePlusSets(false); Cbuf_Init (); Cmd_Init (); @@ -5190,10 +5193,6 @@ void SV_Init (quakeparms_t *parms) #ifdef SVRANKING Rank_RegisterCommands(); #endif - Cbuf_AddText( - "alias restart \"changelevel .\"\n" - "alias startmap_sp \"map start\"\n", - RESTRICT_LOCAL); #ifndef SERVERONLY if (isDedicated) diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index f0288f61..f150b8ff 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -290,16 +290,19 @@ static void WPhys_PortalTransform(world_t *w, wedict_t *ent, wedict_t *portal, v //transform the angles too VectorCopy(org, G_VECTOR(OFS_PARM0)); - if (ent->entnum <= svs.allocated_client_slots) +#ifndef CLIENTONLY + if (w == &sv.world && ent->entnum <= svs.allocated_client_slots) { VectorCopy(ent->v->v_angle, ent->v->angles); } else +#endif ent->v->angles[0] *= -1; VectorCopy(ent->v->angles, G_VECTOR(OFS_PARM1)); AngleVectors(ent->v->angles, w->g.v_forward, w->g.v_right, w->g.v_up); PR_ExecuteProgram (w->progs, portal->xv->camera_transform); VectorAngles(w->g.v_forward, w->g.v_up, ent->v->angles); +#ifndef CLIENTONLY if (ent->entnum > 0 && ent->entnum <= svs.allocated_client_slots) { client_t *cl = &svs.clients[ent->entnum-1]; @@ -330,6 +333,7 @@ static void WPhys_PortalTransform(world_t *w, wedict_t *ent, wedict_t *portal, v VectorCopy(ent->v->angles, ent->v->v_angle); ent->v->angles[0] *= -1; } +#endif /* avelocity is horribly dependant upon eular angles. trying to treat it as a matrix is folly. @@ -682,6 +686,7 @@ static qboolean WPhys_PushAngles (world_t *w, wedict_t *pusher, vec3_t move, vec World_LinkEdict (w, pusher, false); // see if any solid entities are inside the final position + if (pusher->v->movetype != MOVETYPE_H2PUSHPULL) for (e = 1; e < w->num_edicts; e++) { check = WEDICT_NUM(w->progs, e); @@ -1330,7 +1335,9 @@ static void WPhys_Physics_Toss (world_t *w, wedict_t *ent) if (trace.allsolid) { +#ifndef CLIENTONLY if (progstype != PROG_H2) +#endif trace.fraction = 0; //traces that start in solid report a fraction of 0. this is to prevent things from dropping out of the world completely. at least this way they ought to still be shootable etc #pragma warningmsg("The following line might help boost framerates a lot in rmq, not sure if they violate expected behaviour in other mods though - check that they're safe.") @@ -2084,7 +2091,10 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent) } #endif - switch ( (int)ent->v->movetype) + + if (ent->xv->customphysics) + PR_ExecuteProgram (w->progs, ent->xv->customphysics); + else switch ( (int)ent->v->movetype) { case MOVETYPE_PUSH: WPhys_Physics_Pusher (w, ent); @@ -2097,8 +2107,15 @@ void WPhys_RunEntity (world_t *w, wedict_t *ent) case MOVETYPE_ANGLENOCLIP: WPhys_Physics_Noclip (w, ent); break; - case MOVETYPE_STEP: case MOVETYPE_H2PUSHPULL: +#if defined(HEXEN2) && !defined(CLIENTONLY) + if (w == &sv.world && progstype == PROG_H2) + WPhys_Physics_Step (w, ent); //hexen2 pushable object (basically exactly movetype_step) + else +#endif + WPhys_Physics_Pusher (w, ent); //non-solid pusher, for tenebrae compat + break; + case MOVETYPE_STEP: WPhys_Physics_Step (w, ent); break; case MOVETYPE_FOLLOW: diff --git a/engine/server/sv_rankin.c b/engine/server/sv_rankin.c index cb03c956..4f2a6ed3 100644 --- a/engine/server/sv_rankin.c +++ b/engine/server/sv_rankin.c @@ -168,7 +168,7 @@ qboolean Rank_OpenRankings(void) if (!created && (rankfileheader.version != RANKFILE_VERSION || rankfileheader.ident != RANKFILE_IDENT)) { - Con_Printf("Rank file is version %i not %i\nEither delete the file or use an equivelent version of " DISTRIBUTION "\n", rankfileheader.version, RANKFILE_VERSION); + Con_Printf("Rank file is version %i not %i\nEither delete the file or use an equivelent version of " FULLENGINENAME "\n", rankfileheader.version, RANKFILE_VERSION); VFS_CLOSE(rankfile); rankfile = NULL; diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index e1ded4fb..657ceebe 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -564,7 +564,7 @@ void SV_MulticastProtExt(vec3_t origin, multicast_t to, int dimension_mask, int // to = MULTICAST_ALL; //don't let things crash if the world model went away. can happen in broadcasts when reloading video with the map no longer available causing the server to die with the resulting broadcast messages about players dropping or gib effects appearing - if (sv.world.worldmodel->needload) + if (sv.world.worldmodel->loadstate != MLS_LOADED) { switch(to) { diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index d6ff9594..4762d2e5 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -931,6 +931,7 @@ void Sys_DestroyMutex(void *mutex) /* Conditional wait calls */ typedef struct condvar_s { + //FIXME: these should not be pointers. pthread_mutex_t *mutex; pthread_cond_t *cond; } condvar_t; diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index abc3f696..09c3684c 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -298,7 +298,7 @@ DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exception } } else - MessageBox(NULL, "Kaboom! Sorry. No MiniDumpWriteDump function.", DISTRIBUTION " Sucks", 0); + MessageBox(NULL, "Kaboom! Sorry. No MiniDumpWriteDump function.", FULLENGINENAME " Sucks", 0); return EXCEPTION_EXECUTE_HANDLER; } diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index a3bd5522..2eab4504 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -543,6 +543,7 @@ void SVNQ_New_f (void) if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7) { + size_t sz; extern cvar_t allow_download; char *f; @@ -552,15 +553,15 @@ void SVNQ_New_f (void) MSG_WriteString (&host_client->netchan.message, "cl_serverextension_download 1\n"); } - f = COM_LoadTempFile("csprogs.dat"); + f = COM_LoadTempFile("csprogs.dat", &sz); if (f) { MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("csqc_progname %s\n", "csprogs.dat")); MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, va("csqc_progsize %i\n", com_filesize)); + MSG_WriteString (&host_client->netchan.message, va("csqc_progsize %i\n", sz)); MSG_WriteByte (&host_client->netchan.message, svc_stufftext); - MSG_WriteString (&host_client->netchan.message, va("csqc_progcrc %i\n", QCRC_Block(f, com_filesize))); + MSG_WriteString (&host_client->netchan.message, va("csqc_progcrc %i\n", QCRC_Block(f, sz))); MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, "cmd enablecsqc\n"); @@ -1042,6 +1043,7 @@ void SV_SendClientPrespawnInfo(client_t *client) if (client->zquake_extensions & Z_EXT_VWEP) { char mname[MAX_QPATH]; + char ext[8]; char vweaplist[2048] = "//vwep"; for (i = 0; sv.strings.vw_model_precache[i]; i++) @@ -1053,7 +1055,8 @@ void SV_SendClientPrespawnInfo(client_t *client) Q_strncpy(mname, sv.strings.vw_model_precache[i], sizeof(mname)); //strip .mdl extensions, for compat with ezquake - if (!strcmp(COM_FileExtension(mname), "mdl")) + COM_FileExtension(mname, ext, sizeof(ext)); + if (!strcmp(ext, "mdl")) COM_StripExtension(mname, mname, sizeof(mname)); //add it to the vweap command, taking care of any remaining spaces in names. @@ -2610,6 +2613,7 @@ qboolean SV_AllowDownload (const char *name) extern cvar_t allow_download_copyrighted; extern cvar_t allow_download_other; char cleanname[MAX_QPATH]; + char ext[8]; int i=0; if (strlen(name) >= MAX_QPATH) return false; @@ -2633,12 +2637,15 @@ qboolean SV_AllowDownload (const char *name) if (strchr(name, '\\')) //no windows paths - grow up lame windows users. return false; - if (!Q_strcasecmp("log", COM_FileExtension(name))) + COM_FileExtension(name, ext, sizeof(ext)); + + //block attempts to download logs + if (!Q_strcasecmp("log", ext)) return false; if (!strncmp(name, "package/", 8)) { - if (!strcmp("pk4", COM_FileExtension(name)) || !strcmp("pk3", COM_FileExtension(name)) || !strcmp("pak", COM_FileExtension(name))) + if (!strcmp("pk4", ext) || !strcmp("pk3", ext) || !strcmp("pak", ext)) { if (!allow_download_packages.ival) return false; @@ -2675,11 +2682,11 @@ qboolean SV_AllowDownload (const char *name) //wads if (strncmp(name, "wads/", 5) == 0) return !!allow_download_wads.value; - if (!strcmp("wad", COM_FileExtension(name))) + if (!strcmp("wad", ext)) return !!allow_download_wads.value; //pak/pk3s. - if (!strcmp("pk4", COM_FileExtension(name)) || !strcmp("pk3", COM_FileExtension(name)) || !strcmp("pak", COM_FileExtension(name))) + if (!strcmp("pk4", ext) || !strcmp("pk3", ext) || !strcmp("pak", ext)) { if (strnicmp(name, "pak", 3)) //don't give out core pak/pk3 files. This matches q3 logic. return !!allow_download_packages.value; @@ -2687,7 +2694,7 @@ qboolean SV_AllowDownload (const char *name) return !!allow_download_copyrighted.value; } - if (!strcmp("cfg", COM_FileExtension(name))) + if (!strcmp("cfg", ext)) return !!allow_download_configs.value; //root of gamedir @@ -2761,17 +2768,20 @@ static int SV_LocateDownload(char *name, flocation_t *loc, char **replacementnam else return -1; //not found/unable to open } +#ifdef TERRAIN else if (Terrain_LocateSection(name, loc)) { found = true; } +#endif else found = FS_FLocateFile(name, FSLFRT_IFFOUND, loc); //nexuiz names certain files as .wav but they're really .ogg on disk. if (!found && replacementname) { - if (!strcmp(COM_FileExtension(name), "wav")) + char ext[8]; + if (!strcmp(COM_FileExtension(name, ext, sizeof(ext)), "wav")) { char tryogg[MAX_QPATH]; COM_StripExtension(name, tryogg, sizeof(tryogg)); @@ -5469,7 +5479,7 @@ void SV_FilterImpulseInit(void) int imp; memset(implevels, 0, sizeof(implevels)); - s = COM_LoadStackFile("impfiltr.cfg", buffer, sizeof(buffer)); + s = COM_LoadStackFile("impfiltr.cfg", buffer, sizeof(buffer), NULL); if (!s) Con_DPrintf("impfiltr.cfg not found. Impulse filters are disabled\n"); @@ -5976,8 +5986,20 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) sv_player->v->v_angle[2] = SHORT2ANGLE(ucmd->angles[2]); } +#ifdef HEXEN2 if (progstype == PROG_H2) - sv_player->xv->light_level = 128; //hmm... HACK!!! + { + //fixme: should probably support rtlights, but this is a server, so urgh. + if (sv.world.worldmodel && sv.world.worldmodel->funcs.LightPointValues) + { + vec3_t diffuse, ambient, dir; + sv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, sv_player->v->origin, diffuse, ambient, dir); + sv_player->xv->light_level = (ambient[0]+ambient[1]+ambient[2])/3.0+(diffuse[0]+diffuse[1]+diffuse[2])/6.0; + } + else + sv_player->xv->light_level = 128; //don't know, some dummy value. + } +#endif sv_player->v->button0 = ucmd->buttons & 1; sv_player->v->button2 = (ucmd->buttons >> 1) & 1; diff --git a/engine/server/svhl_game.c b/engine/server/svhl_game.c index 2d673b42..7825394c 100644 --- a/engine/server/svhl_game.c +++ b/engine/server/svhl_game.c @@ -1475,7 +1475,7 @@ void SVHL_SpawnEntities(char *entstring) for (i=1 ; inumsubmodels ; i++) { sv.strings.model_precache[1+i] = localmodels[i]; - sv.models[i+1] = Mod_ForName (localmodels[i], false); + sv.models[i+1] = Mod_ForName (Mod_FixName(localmodels[i], sv.modelname), false); } while (entstring) diff --git a/engine/server/svmodel.c b/engine/server/svmodel.c deleted file mode 100644 index d1a10b80..00000000 --- a/engine/server/svmodel.c +++ /dev/null @@ -1,1944 +0,0 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -// models.c -- model loading and caching - -#include "quakedef.h" - -#ifdef SERVERONLY -model_t *loadmodel; -char loadname[32]; // for hunk tags -static int mod_datasequence; - -qboolean Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadQ2BrushModel (model_t *mod, void *buffer); -qboolean D3_LoadMap_CollisionMap(model_t *mod, char *buf, size_t fsize); - -qboolean Mod_LoadQ1Model (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadQ2Model (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadQ3Model (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadZymoticModel (model_t *mod, void *buffer, size_t fsize); -qboolean Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t fsize); - -qbyte mod_novis[(MAX_MAP_LEAFS+7)/8]; - -#define MAX_MOD_KNOWN 2048 -model_t mod_known[MAX_MOD_KNOWN]; -int mod_numknown; - -texture_t r_notexture_mip_real; -texture_t *r_notexture_mip = &r_notexture_mip_real; - -cvar_t sv_nogetlight = SCVAR("sv_nogetlight", "0"); -cvar_t dpcompat_psa_ungroup = CVAR ("dpcompat_psa_ungroup", "0"); -cvar_t r_noframegrouplerp = CVARF ("r_noframegrouplerp", "0", CVAR_ARCHIVE); - -unsigned *model_checksum; - - -int SVQ1_RecursiveLightPoint3C (model_t *model, mnode_t *node, vec3_t start, vec3_t end) -{ - int r; - float front, back, frac; - int side; - mplane_t *plane; - vec3_t mid; - msurface_t *surf; - int s, t, ds, dt; - int i; - mtexinfo_t *tex; - qbyte *lightmap; - unsigned scale; - int maps; - - - if (model->fromgame == fg_quake2) - { - if (node->contents != -1) - return -1; // solid - } - else - { - if (node->contents < 0) - return -1; // didn't hit anything - } - -// calculate mid point - -// FIXME: optimize for axial - plane = node->plane; - front = DotProduct (start, plane->normal) - plane->dist; - back = DotProduct (end, plane->normal) - plane->dist; - side = front < 0; - - if ( (back < 0) == side) - return SVQ1_RecursiveLightPoint3C (model, node->children[side], start, end); - - frac = front / (front-back); - mid[0] = start[0] + (end[0] - start[0])*frac; - mid[1] = start[1] + (end[1] - start[1])*frac; - mid[2] = start[2] + (end[2] - start[2])*frac; - -// go down front side - r = SVQ1_RecursiveLightPoint3C (model, node->children[side], start, mid); - if (r >= 0) - return r; // hit something - - if ( (back < 0) == side ) - return -1; // didn't hit anuthing - -// check for impact on this node - - surf = model->surfaces + node->firstsurface; - for (i=0 ; inumsurfaces ; i++, surf++) - { - if (surf->flags & SURF_DRAWTILED) - continue; // no lightmaps - - tex = surf->texinfo; - - s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; - t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3]; - - if (s < surf->texturemins[0] || - t < surf->texturemins[1]) - continue; - - ds = s - surf->texturemins[0]; - dt = t - surf->texturemins[1]; - - if ( ds > surf->extents[0] || dt > surf->extents[1] ) - continue; - - if (!surf->samples) - return 0; - - lightmap = surf->samples; - r = 0; - if (lightmap) - { - - lightmap += (dt * ((surf->extents[0])+1) + ds)*3; - - for (maps = 0 ; maps < MAXQ1LIGHTMAPS && surf->styles[maps] != 255 ; - maps++) - { - scale = sv.strings.lightstyles[surf->styles[maps]][0]; - r += (lightmap[0]+lightmap[1]+lightmap[2])/3 * scale; - lightmap += ((surf->extents[0])+1) * - ((surf->extents[1])+1)*3; - } - - r >>= 8; - } - - return r; - } - -// go down back side - return SVQ1_RecursiveLightPoint3C (model, node->children[!side], mid, end); -} - -void SVQ1_LightPointValues(model_t *model, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) -{ - vec3_t end; - float r; - - res_dir[0] = 0; //software doesn't load luxes - res_dir[1] = 1; - res_dir[2] = 1; - - end[0] = point[0]; - end[1] = point[1]; - end[2] = point[2] - 2048; - - r = SVQ1_RecursiveLightPoint3C (model, model->nodes, point, end); - if (r < 0) - { - res_diffuse[0] = 0; - res_diffuse[1] = 0; - res_diffuse[2] = 0; - - res_ambient[0] = 0; - res_ambient[1] = 0; - res_ambient[2] = 0; - } - else - { - res_diffuse[0] = r; - res_diffuse[1] = r; - res_diffuse[2] = r; - - res_ambient[0] = r; - res_ambient[1] = r; - res_ambient[2] = r; - } -} - - - -/* -=============== -Mod_Init -=============== -*/ -void Mod_Init (qboolean initial) -{ - if (initial) - { - memset (mod_novis, 0xff, sizeof(mod_novis)); - Cvar_Register(&sv_nogetlight, "Memory preservation"); - Cvar_Register (&dpcompat_psa_ungroup, "Darkplaces compatibility"); - Cvar_Register (&r_noframegrouplerp, "Graphical Nicaties"); - } -} - - -int Mod_RegisterModelFormatText(void *module, const char *formatname, char *magictext, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)) -{ - return 0; -} -int Mod_RegisterModelFormatMagic(void *module, const char *formatname, unsigned int magic, qboolean (QDECL *load) (struct model_s *mod, void *buffer, size_t fsize)) -{ - return 0; -} -void Mod_UnRegisterModelFormat(void *module, int idx) -{ -} -void Mod_UnRegisterAllModelFormats(void *module) -{ -} - -/* -=============== -Mod_LeafForPoint -=============== -*/ -int Mod_LeafForPoint (model_t *model, vec3_t p) -{ - mnode_t *node; - float d; - mplane_t *plane; -#ifdef Q2BSPS - if (model->fromgame == fg_quake2 || model->fromgame == fg_quake3) - { - return CM_PointLeafnum(model, p); - } -#endif - if (!model || !model->nodes) - Sys_Error ("Mod_PointInLeaf: bad model"); - - node = model->nodes; - while (1) - { - if (node->contents < 0) - return (mleaf_t *)node - model->leafs; - plane = node->plane; - d = DotProduct (p,plane->normal) - plane->dist; - if (d > 0) - node = node->children[0]; - else - node = node->children[1]; - } - - return 0; // never reached -} - -/* -=============== -Mod_PointInLeaf -=============== -*/ -mleaf_t *Mod_PointInLeaf (model_t *model, vec3_t p) -{ - return model->leafs + Mod_LeafForPoint(model, p); -} - - - -/* -=================== -Mod_DecompressVis -=================== -*/ -qbyte *Mod_DecompressVis (qbyte *in, model_t *model, qbyte *decompressed) -{ - int c; - qbyte *out; - int row; - - row = (model->numvisleafs+7)>>3; - out = decompressed; - -#if 0 - memcpy (out, in, row); -#else - if (!in) - { // no vis info, so make all visible - while (row) - { - *out++ = 0xff; - row--; - } - return decompressed; - } - - do - { - if (*in) - { - *out++ = *in++; - continue; - } - - c = in[1]; - in += 2; - while (c) - { - *out++ = 0; - c--; - } - } while (out - decompressed < row); -#endif - - return decompressed; -} - -qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model, qbyte *buffer) -{ - static qbyte decompressed[(MAX_MAP_LEAFS+7)/8]; - - if (leaf == model->leafs) - return mod_novis; - - if (!buffer) - buffer = decompressed; - return Mod_DecompressVis (leaf->compressed_vis, model, buffer); -} - -qbyte *Mod_LeafnumPVS (int ln, model_t *model, qbyte *buffer) -{ - return Mod_LeafPVS(model->leafs + ln, model, buffer); -} - - -/* -=================== -Mod_Purge -=================== -*/ -void Mod_Purge(enum mod_purge_e type) -{ - int i; - model_t *mod; - - for (i=0 , mod=mod_known ; idatasequence != mod_datasequence || type != MP_MAPCHANGED) - { - //and obliterate anything else remaining in memory. - ZG_FreeGroup(&mod->memgroup); - mod->meshinfo = NULL; - mod->needload = true; - } - } -} -/* -=================== -Mod_ClearAll -=================== -*/ -void Mod_ClearAll (void) -{ - mod_datasequence++; -} - -/* -================== -Mod_FindName - -================== -*/ -model_t *Mod_FindName (const char *name) -{ - int i; - model_t *mod; - - if (!name[0]) - SV_Error ("Mod_ForName: NULL name"); - -// -// search the currently loaded models -// - for (i=0 , mod=mod_known ; iname, name) ) - break; - - if (i == mod_numknown) - { - if (mod_numknown == MAX_MOD_KNOWN) - SV_Error ("mod_numknown == MAX_MOD_KNOWN"); - if (strlen(name) >= sizeof(mod->name)) - Sys_Error ("model name is too long: %s", name); - strcpy (mod->name, name); - mod->needload = true; - mod_numknown++; - } - - //mark it as active, so it doesn't get flushed prematurely - mod->datasequence = mod_datasequence; - return mod; -} - - -/* -================== -Mod_LoadModel - -Loads a model into the cache -================== -*/ -model_t *Mod_LoadModel (model_t *mod, enum mlverbosity_e verbose) -{ - unsigned *buf; - qbyte stackbuf[1024]; // avoid dirtying the cache heap - - if (!mod->needload) - { - if (mod->type == mod_alias) - { - if (mod->meshinfo) - return mod; - } - else - return mod; // not cached at all - } - -// -// load the file -// - buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); - if (!buf) - { - if (verbose >= MLV_ERROR) - SV_Error ("Mod_NumForName: %s not found", mod->name); - return NULL; - } - -// -// allocate a new model -// - COM_FileBase (mod->name, loadname, sizeof(loadname)); - - loadmodel = mod; - -// -// fill it in -// - -// call the apropriate loader - mod->needload = false; - - // set necessary engine flags for loading purposes - if (!strcmp(mod->name, "progs/player.mdl")) - { - mod->engineflags |= MDLF_PLAYER | MDLF_DOCRC; - } - else if (!strcmp(mod->name, "progs/eyes.mdl")) - mod->engineflags |= MDLF_DOCRC; - - switch (LittleLong(*(unsigned *)buf)) - { -#if defined(Q2BSPS) - case IDBSPHEADER: //looks like id switched to have proper ids - if (!Mod_LoadQ2BrushModel (mod, buf)) - goto couldntload; - break; -#endif - - case BSPVERSIONPREREL: - case BSPVERSION: - case BSPVERSIONHL: - if (!Mod_LoadBrushModel (mod, buf, com_filesize)) - goto couldntload; - break; - - case RAPOLYHEADER: - case IDPOLYHEADER: - if (!Mod_LoadQ1Model(mod, buf, com_filesize)) - goto couldntload; - break; -#ifdef MD2MODELS - case MD2IDALIASHEADER: - if (!Mod_LoadQ2Model(mod, buf, com_filesize)) - goto couldntload; - break; -#endif -#ifdef MD3MODELS - case MD3_IDENT: - if (!Mod_LoadQ3Model (mod, buf, com_filesize)) - goto couldntload; - break; -#endif -#ifdef ZYMOTICMODELS - case (('O'<<24)+('M'<<16)+('Y'<<8)+'Z'): - if (!Mod_LoadZymoticModel(mod, buf, com_filesize)) - goto couldntload; - break; -#endif -#ifdef ZYMOTICMODELS - case (('K'<<24)+('R'<<16)+('A'<<8)+'D'): - if (!Mod_LoadDarkPlacesModel(mod, buf, com_filesize)) - goto couldntload; - break; -#endif - - default: - COM_Parse((char*)buf); -#ifdef MAP_PROC - if (!strcmp(com_token, "CM")) //doom3 map. - { - if (!D3_LoadMap_CollisionMap (mod, (char*)buf)) - goto couldntload; - break; - } -#endif -#ifdef TERRAIN - if (!strcmp(com_token, "terrain")) //custom format, text based. - { - if (!Terr_LoadTerrainModel(mod, buf, com_filesize)) - goto couldntload; - break; - } -#endif - - if (verbose >= MLV_WARN) - Con_Printf (CON_ERROR "Mod_NumForName: %s: format not recognised\n", mod->name); -couldntload: - if (verbose >= MLV_ERROR) - SV_Error ("Load failed on critical model %s", mod->name); - return NULL; - } - - return mod; -} - -/* -================== -Mod_ForName - -Loads in a model for the given name -================== -*/ -model_t *Mod_ForName (const char *name, enum mlverbosity_e verbose) -{ - model_t *mod; - - mod = Mod_FindName (name); - - return Mod_LoadModel (mod, verbose); -} - - -/* -=============================================================================== - - BRUSHMODEL LOADING - -=============================================================================== -*/ - -qbyte *mod_base; - - -/* -================= -Mod_LoadTextures -================= -*/ -void Mod_LoadTextures (lump_t *l) -{ - int i, j, pixels, num, max, altmax; - miptex_t *mt; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - dmiptexlump_t *m; - - if (loadmodel->fromgame != fg_quake) - return; - - if (!l->filelen) - { - loadmodel->textures = NULL; - return; - } - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); - - loadmodel->numtextures = m->nummiptex; - loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, m->nummiptex * sizeof(*loadmodel->textures)); - - for (i=0 ; inummiptex ; i++) - { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) - continue; - mt = (miptex_t *)((qbyte *)m + m->dataofs[i]); - mt->width = LittleLong (mt->width); - mt->height = LittleLong (mt->height); - for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); - - if ( (mt->width & 15) || (mt->height & 15) ) - SV_Error ("Texture %s is not 16 aligned", mt->name); - pixels = mt->width*mt->height/64*85; - tx = ZG_Malloc(&loadmodel->memgroup, sizeof(texture_t) +pixels); - loadmodel->textures[i] = tx; - - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - memcpy ( tx+1, mt+1, pixels); - } - -// -// sequence the animations -// - for (i=0 ; inummiptex ; i++) - { - tx = loadmodel->textures[i]; - if (!tx || tx->name[0] != '+') - continue; - if (tx->anim_next) - continue; // already sequenced - - // find the number of frames in the animation - memset (anims, 0, sizeof(anims)); - memset (altanims, 0, sizeof(altanims)); - - max = tx->name[1]; - altmax = 0; - if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; - if (max >= '0' && max <= '9') - { - max -= '0'; - altmax = 0; - anims[max] = tx; - max++; - } - else if (max >= 'A' && max <= 'J') - { - altmax = max - 'A'; - max = 0; - altanims[altmax] = tx; - altmax++; - } - else - SV_Error ("Bad animating texture %s", tx->name); - - for (j=i+1 ; jnummiptex ; j++) - { - tx2 = loadmodel->textures[j]; - if (!tx2 || tx2->name[0] != '+') - continue; - if (strcmp (tx2->name+2, tx->name+2)) - continue; - - num = tx2->name[1]; - if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; - if (num >= '0' && num <= '9') - { - num -= '0'; - anims[num] = tx2; - if (num+1 > max) - max = num + 1; - } - else if (num >= 'A' && num <= 'J') - { - num = num - 'A'; - altanims[num] = tx2; - if (num+1 > altmax) - altmax = num+1; - } - else - SV_Error ("Bad animating texture %s", tx->name); - } - -#define ANIM_CYCLE 2 - // link them all together - for (j=0 ; jname); - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = anims[ (j+1)%max ]; - if (altmax) - tx2->alternate_anims = altanims[0]; - } - for (j=0 ; jname); - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[ (j+1)%altmax ]; - if (max) - tx2->alternate_anims = anims[0]; - } - } -} - -/* -================= -Mod_LoadLighting -================= -*/ -qboolean Mod_LoadLighting (lump_t *l) -{ - int i; - char *in; - char *out; - if (!l->filelen) - { - loadmodel->lightdata = NULL; - return true; - } - - if (loadmodel->fromgame == fg_halflife) - { - loadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, l->filelen); - memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); - } - else - { - loadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, l->filelen*3); - - in = mod_base + l->fileofs; - out = loadmodel->lightdata; - - for (i = 0; i < l->filelen; i++) - { - *out++ = *in; - *out++ = *in; - *out++ = *in++; - } - } - - return true; -} - - -/* -================= -Mod_LoadVisibility -================= -*/ -void Mod_LoadVisibility (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->visdata = NULL; - return; - } - loadmodel->visdata = ZG_Malloc(&loadmodel->memgroup, l->filelen); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); -} - - -/* -================= -Mod_LoadEntities -================= -*/ -void Mod_LoadEntities (lump_t *l) -{ - if (!l->filelen) - { - loadmodel->entities = NULL; - return; - } - loadmodel->entities = ZG_Malloc(&loadmodel->memgroup, l->filelen + 1); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); - loadmodel->entities[l->filelen] = 0; -} - - -/* -================= -Mod_LoadVertexes -================= -*/ -qboolean Mod_LoadVertexes (lump_t *l) -{ - dvertex_t *in; - mvertex_t *out; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->vertexes = out; - loadmodel->numvertexes = count; - - for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); - out->position[1] = LittleFloat (in->point[1]); - out->position[2] = LittleFloat (in->point[2]); - } - return true; -} - -/* -================= -Mod_LoadSubmodels -================= -*/ -static qboolean hexen2map; -qboolean Mod_LoadSubmodels (lump_t *l) -{ - dq1model_t *inq; - dh2model_t *inh; - mmodel_t *out; - int i, j, count; - - //this is crazy! - - inq = (void *)(mod_base + l->fileofs); - inh = (void *)(mod_base + l->fileofs); - if (!inq->numfaces) - { - hexen2map = true; - if (l->filelen % sizeof(*inh)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*inh); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->submodels = out; - loadmodel->numsubmodels = count; - - for ( i=0 ; imins[j] = LittleFloat (inh->mins[j]) - 1; - out->maxs[j] = LittleFloat (inh->maxs[j]) + 1; - out->origin[j] = LittleFloat (inh->origin[j]); - } - for (j=0 ; jheadnode[j] = LittleLong (inh->headnode[j]); - } - for ( ; jheadnode[j] = 0; - for (j=0 ; jhullavailable[j] = true; - for ( ; jhullavailable[j] = false; - out->visleafs = LittleLong (inh->visleafs); - out->firstface = LittleLong (inh->firstface); - out->numfaces = LittleLong (inh->numfaces); - } - - } - else - { - hexen2map = false; - if (l->filelen % sizeof(*inq)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*inq); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->submodels = out; - loadmodel->numsubmodels = count; - - for ( i=0 ; imins[j] = LittleFloat (inq->mins[j]) - 1; - out->maxs[j] = LittleFloat (inq->maxs[j]) + 1; - out->origin[j] = LittleFloat (inq->origin[j]); - } - for (j=0 ; jheadnode[j] = LittleLong (inq->headnode[j]); - } - for ( ; jheadnode[j] = 0; - for (j=0 ; j<3 ; j++) - out->hullavailable[j] = true; - for ( ; jhullavailable[j] = false; - out->visleafs = LittleLong (inq->visleafs); - out->firstface = LittleLong (inq->firstface); - out->numfaces = LittleLong (inq->numfaces); - } - } - - return true; -} - -/* -================= -Mod_LoadEdges -================= -*/ -qboolean Mod_LoadEdges (lump_t *l, qboolean lm) -{ - medge_t *out; - int i, count; - - if (lm) - { - dledge_t *in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, (count + 1) * sizeof(*out)); - - loadmodel->edges = out; - loadmodel->numedges = count; - - for ( i=0 ; iv[0] = (unsigned int)LittleLong(in->v[0]); - out->v[1] = (unsigned int)LittleLong(in->v[1]); - } - } - else - { - dsedge_t *in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, (count + 1) * sizeof(*out)); - - loadmodel->edges = out; - loadmodel->numedges = count; - - for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); - out->v[1] = (unsigned short)LittleShort(in->v[1]); - } - } - return true; -} - -/* -================= -Mod_LoadTexinfo -================= -*/ -qboolean Mod_LoadTexinfo (lump_t *l) -{ - texinfo_t *in; - mtexinfo_t *out; - int i, j, count; - int miptex; - float len1, len2; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); - len1 = Length (in->vecs[0]); - len2 = Length (in->vecs[1]); -#else - for (j=0 ; j<4 ; j++) { - out->vecs[0][j] = LittleFloat (in->vecs[0][j]); - out->vecs[1][j] = LittleFloat (in->vecs[1][j]); - } - len1 = Length (out->vecs[0]); - len2 = Length (out->vecs[1]); -#endif - if (len1 + len2 < 2 /*0.001*/) - out->mipadjust = 1; - else - out->mipadjust = 1 / floor( (len1+len2)/2 ); - - miptex = LittleLong (in->miptex); - out->flags = LittleLong (in->flags); - - if (!loadmodel->textures) - { - out->texture = r_notexture_mip; // checkerboard texture - out->flags = 0; - } - else - { - if (miptex >= loadmodel->numtextures) - SV_Error ("miptex >= loadmodel->numtextures"); - out->texture = loadmodel->textures[miptex]; - if (!out->texture) - { - out->texture = r_notexture_mip; // texture not found - out->flags = 0; - } - } - } - return true; -} - -/* -================ -CalcSurfaceExtents - -Fills in s->texturemins[] and s->extents[] -================ -*/ - -void CalcSurfaceExtents (msurface_t *s); -/* -{ - float mins[2], maxs[2], val; - int i,j, e; - mvertex_t *v; - mtexinfo_t *tex; - int bmins[2], bmaxs[2]; - - mins[0] = mins[1] = 999999; - maxs[0] = maxs[1] = -99999; - - tex = s->texinfo; - - for (i=0 ; inumedges ; i++) - { - e = loadmodel->surfedges[s->firstedge+i]; - if (e >= 0) - v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; - else - v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; - - for (j=0 ; j<2 ; j++) - { - val = v->position[0] * tex->vecs[j][0] + - v->position[1] * tex->vecs[j][1] + - v->position[2] * tex->vecs[j][2] + - tex->vecs[j][3]; - if (val < mins[j]) - mins[j] = val; - if (val > maxs[j]) - maxs[j] = val; - } - } - - for (i=0 ; i<2 ; i++) - { - bmins[i] = floor(mins[i]/16); - bmaxs[i] = ceil(maxs[i]/16); - - s->texturemins[i] = bmins[i]; - s->extents[i] = (bmaxs[i] - bmins[i]); -// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) -// SV_Error ("Bad surface extents"); - } -} -*/ - -/* -================= -Mod_LoadFaces -================= -*/ -qboolean Mod_LoadFaces (lump_t *l, qboolean lm) -{ - dsface_t *ins; - dlface_t *inl; - msurface_t *out; - int i, count, surfnum; - int planenum, side; - int tn, lofs; - - if (lm) - { - ins = NULL; - inl = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*inl)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*inl); - } - else - { - inl = NULL; - ins = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*ins)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*ins); - } - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; - - for ( surfnum=0 ; surfnumplanenum); - side = LittleLong(inl->side); - out->firstedge = LittleLong(inl->firstedge); - out->numedges = LittleLong(inl->numedges); - tn = LittleLong (inl->texinfo); - for (i=0 ; istyles[i] = inl->styles[i]; - lofs = LittleLong(inl->lightofs); - inl++; - } - else - { - planenum = LittleShort(ins->planenum); - side = LittleShort(ins->side); - out->firstedge = LittleLong(ins->firstedge); - out->numedges = LittleShort(ins->numedges); - tn = LittleShort (ins->texinfo); - for (i=0 ; istyles[i] = ins->styles[i]; - lofs = LittleLong(ins->lightofs); - ins++; - } - - out->flags = 0; - - if (side) - out->flags |= SURF_PLANEBACK; - - out->plane = loadmodel->planes + planenum; - - out->texinfo = loadmodel->texinfo + tn; - - CalcSurfaceExtents (out); - - // lighting info - if (lofs == -1) - out->samples = NULL; - else if (loadmodel->fromgame == fg_halflife) - out->samples = loadmodel->lightdata + lofs; - else - out->samples = loadmodel->lightdata + lofs*3; - - // set the drawing flags flag - - if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) // sky - { - out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); - continue; - } - - if (!Q_strncmp(out->texinfo->texture->name,"*",1)) // turbulent - { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); - for (i=0 ; i<2 ; i++) - { - out->extents[i] = 16384; - out->texturemins[i] = -8192; - } - continue; - } - } - - return true; -} - - -/* -================= -Mod_SetParent -================= -*/ -void Mod_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents < 0) - return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); -} - -/* -================= -Mod_LoadNodes -================= -*/ -qboolean Mod_LoadNodes (lump_t *l, qboolean lm) -{ - int i, j, count, p; - mnode_t *out; - - if (lm) - { - dl1node_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->nodes = out; - loadmodel->numnodes = count; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->planenum); - out->plane = loadmodel->planes + p; - - out->firstsurface = LittleLong (in->firstface); - out->numsurfaces = LittleLong (in->numfaces); - - for (j=0 ; j<2 ; j++) - { - p = LittleLong (in->children[j]); - if (p >= 0) - out->children[j] = loadmodel->nodes + p; - else - out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); - } - } - } - else - { - dsnode_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->nodes = out; - loadmodel->numnodes = count; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->planenum); - out->plane = loadmodel->planes + p; - - out->firstsurface = LittleShort (in->firstface); - out->numsurfaces = LittleShort (in->numfaces); - - for (j=0 ; j<2 ; j++) - { - p = LittleShort (in->children[j]); - if (p >= 0) - out->children[j] = loadmodel->nodes + p; - else - out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); - } - } - } - Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs - - return true; -} - -/* -================= -Mod_LoadLeafs -================= -*/ -qboolean Mod_LoadLeafs (lump_t *l, qboolean lm) -{ - mleaf_t *out; - int i, j, count, p; - - if (lm) - { - dl1leaf_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - (unsigned int)LittleLong(in->firstmarksurface); - out->nummarksurfaces = (unsigned int)LittleLong(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - } - } - else - { - dsleaf_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - loadmodel->numvisleafs = count-1; - - for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - (unsigned short)LittleShort(in->firstmarksurface); - out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - } - } - - return true; -} - -/* -================= -Mod_LoadClipnodes -================= -*/ -qboolean Mod_LoadClipnodes (lump_t *l, qboolean lm) -{ - mclipnode_t *out; - int i, count; - hull_t *hull; - - if (lm) - { - dlclipnode_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - for (i=0 ; iplanenum); - out[i].children[0] = LittleLong(in->children[0]); - out[i].children[1] = LittleLong(in->children[1]); - } - } - else - { - dsclipnode_t *in; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - for (i=0 ; iplanenum); - out[i].children[0] = LittleShort(in->children[0]); - out[i].children[1] = LittleShort(in->children[1]); - } - } - - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; - - if (hexen2map) - { //hexen2. - hexen2map=false; - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - hull->available = true; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -24; - hull->clip_mins[1] = -24; - hull->clip_mins[2] = -20; - hull->clip_maxs[0] = 24; - hull->clip_maxs[1] = 24; - hull->clip_maxs[2] = 20; - hull->available = true; - - hull = &loadmodel->hulls[3]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -12; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 16; - hull->available = true; - - /* - There is some mission-pack weirdness here - in the missionpack, hull 4 is meant to be '-8 -8 -8' '8 8 8' - in the original game, hull 4 is '-40 -40 -42' '40 40 42' - */ - hull = &loadmodel->hulls[4]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -8; - hull->clip_mins[1] = -8; - hull->clip_mins[2] = -8; - hull->clip_maxs[0] = 8; - hull->clip_maxs[1] = 8; - hull->clip_maxs[2] = 8; - hull->available = true; - - hull = &loadmodel->hulls[5]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -48; - hull->clip_mins[1] = -48; - hull->clip_mins[2] = -50; - hull->clip_maxs[0] = 48; - hull->clip_maxs[1] = 48; - hull->clip_maxs[2] = 50; - hull->available = true; - - //6 isn't used. - //7 isn't used. - } - else if (loadmodel->fromgame == fg_halflife) - { - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -32;//-36 is correct here, but we'll just copy mvdsv instead. - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = hull->clip_mins[2]+72; - hull->available = true; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -32; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = hull->clip_mins[2]+64; - hull->available = true; - - hull = &loadmodel->hulls[3]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -18; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = hull->clip_mins[2]+36; - hull->available = true; - } - else - { - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - hull->available = true; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; - hull->available = true; - - hull = &loadmodel->hulls[3]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -6; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 30; - hull->available = false; - } - - return true; -} - -/* -================= -Mod_MakeHull0 - -Deplicate the drawing hull structure as a clipping hull -================= -*/ -void Mod_MakeHull0 (void) -{ - mnode_t *in, *child; - mclipnode_t *out; - int i, j, count; - hull_t *hull; - - hull = &loadmodel->hulls[0]; - - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - - for (i=0 ; iplanenum = in->plane - loadmodel->planes; - for (j=0 ; j<2 ; j++) - { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; - } - } -} - -/* -================= -Mod_LoadMarksurfaces -================= -*/ -void Mod_LoadMarksurfaces (lump_t *l) -{ - int i, j, count; - short *in; - msurface_t **out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - SV_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->marksurfaces = out; - loadmodel->nummarksurfaces = count; - - for ( i=0 ; i= loadmodel->numsurfaces) - SV_Error ("Mod_ParseMarksurfaces: bad surface number"); - out[i] = loadmodel->surfaces + j; - } -} - -/* -================= -Mod_LoadSurfedges -================= -*/ -qboolean Mod_LoadSurfedges (lump_t *l) -{ - int i, count; - int *in, *out; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*sizeof(*out)); - - loadmodel->surfedges = out; - loadmodel->numsurfedges = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - { - Con_Printf (CON_ERROR "MOD_LoadBmodel: funny lump size in %s\n",loadmodel->name); - return false; - } - count = l->filelen / sizeof(*in); - out = ZG_Malloc(&loadmodel->memgroup, count*2*sizeof(*out)); - - loadmodel->planes = out; - loadmodel->numplanes = count; - - for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); - if (out->normal[j] < 0) - bits |= 1<dist = LittleFloat (in->dist); - out->type = LittleLong (in->type); - out->signbits = bits; - } - - return true; -} - -/* -================= -Mod_LoadBrushModel -================= -*/ -qboolean Mod_LoadBrushModel (model_t *mod, void *buffer, size_t buffersize) -{ - int i, j; - dheader_t *header; - mmodel_t *bm; - unsigned int chksum; - qboolean noerrors; - qboolean longm = false; -#ifdef TERRAIN - model_t *lm = loadmodel; -#endif - - loadmodel->type = mod_brush; - - header = (dheader_t *)buffer; - - i = LittleLong (header->version); - - - if (i == BSPVERSION_LONG1) - { - loadmodel->fromgame = fg_quake; - longm = true; - } - else if (i == BSPVERSION || i == BSPVERSIONPREREL) - loadmodel->fromgame = fg_quake; - else if (i == BSPVERSIONHL) - loadmodel->fromgame = fg_halflife; - else - { - Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)\n", mod->name, i, BSPVERSION); - return false; - } - -// swap all the lumps - mod_base = (qbyte *)header; - - for (i=0 ; ichecksum = 0; - mod->checksum2 = 0; - - // checksum all of the map, except for entities - for (i = 0; i < HEADER_LUMPS; i++) - { - if ((unsigned)header->lumps[i].fileofs + (unsigned)header->lumps[i].filelen > com_filesize) - { - Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s appears truncated\n", mod->name); - return false; - } - - if (i == LUMP_ENTITIES) - continue; - chksum = Com_BlockChecksum(mod_base + header->lumps[i].fileofs, - header->lumps[i].filelen); - mod->checksum ^= chksum; - - if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) - continue; - mod->checksum2 ^= chksum; - } - - noerrors = true; - if (!sv_nogetlight.value) - { - noerrors = noerrors && Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); - noerrors = noerrors && Mod_LoadEdges (&header->lumps[LUMP_EDGES], longm); - noerrors = noerrors && Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); -///*/on server?*/ Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); - noerrors = noerrors && Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); - } - noerrors = noerrors && Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); - noerrors = noerrors && Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); - if (!sv_nogetlight.value) - { - noerrors = noerrors && Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); - noerrors = noerrors && Mod_LoadFaces (&header->lumps[LUMP_FACES], longm); - } -// Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); - if (noerrors) - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); - noerrors = noerrors && Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], longm); - noerrors = noerrors && Mod_LoadNodes (&header->lumps[LUMP_NODES], longm); - noerrors = noerrors && Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], longm); - if (noerrors) - { - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); - Mod_MakeHull0 (); - } - - if (!noerrors) - { - ZG_FreeGroup(&mod->memgroup); - return false; - } - - Q1BSP_LoadBrushes(mod); - Q1BSP_SetModelFuncs(mod); - - if (mod->surfaces && mod->lightdata) - mod->funcs.LightPointValues = SVQ1_LightPointValues; - - - mod->numframes = 2; // regular and alternate animation -// -// set up the submodels (FIXME: this is confusing) -// - for (i=0 ; inumsubmodels ; i++) - { - bm = &mod->submodels[i]; - - mod->hulls[0].firstclipnode = bm->headnode[0]; - Q1BSP_CheckHullNodes(&mod->hulls[0]); - for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; - if (mod->hulls[j].available) - Q1BSP_CheckHullNodes(&mod->hulls[j]); - } - - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; - - VectorCopy (bm->maxs, mod->maxs); - VectorCopy (bm->mins, mod->mins); - - mod->numvisleafs = bm->visleafs; - - if (i < mod->numsubmodels-1) - { // duplicate the basic information - char name[10]; - - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); - mod = loadmodel; - memset(&mod->memgroup, 0, sizeof(mod->memgroup)); - } - } - -#ifdef TERRAIN - lm->terrain = Mod_LoadTerrainInfo(lm, loadname, false); -#endif - - return true; -} - - - -void *Mod_Extradata (model_t *mod) -{ - if (mod->meshinfo) - return mod->meshinfo; - - Mod_LoadModel (mod, true); - - if (!mod->meshinfo) - Sys_Error ("Mod_Extradata: caching failed"); - return mod->meshinfo; -} - - - - - - -#endif - diff --git a/engine/server/svq2_game.c b/engine/server/svq2_game.c index ad80f4b5..2fa8dace 100644 --- a/engine/server/svq2_game.c +++ b/engine/server/svq2_game.c @@ -377,7 +377,9 @@ static void VARGS PFQ2_setmodel (q2edict_t *ent, char *name) // if it is an inline model, get the size information for it if (name[0] == '*') { - mod = Mod_FindName (name); + mod = Mod_FindName (Mod_FixName(name, sv.modelname)); + if (mod->loadstate == MLS_LOADING) + COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); //wait for it if needed VectorCopy (mod->mins, ent->mins); VectorCopy (mod->maxs, ent->maxs); WorldQ2_LinkEdict (&sv.world, ent); diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 5acb50ed..19d00766 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -163,10 +163,12 @@ static model_t *Q3G_GetCModel(unsigned int modelindex) if (modelindex == 0) sv.models[modelindex] = sv.world.worldmodel; else - sv.models[modelindex] = Mod_ForName(va("*%i", modelindex), MLV_WARN); + sv.models[modelindex] = Mod_ForName(Mod_FixName(va("*%i", modelindex), sv.modelname), MLV_WARN); } - if (!sv.models[modelindex]->needload) + if (sv.models[modelindex]->loadstate == MLS_LOADING) + COM_WorkerPartialSync(sv.models[modelindex], &sv.models[modelindex]->loadstate, MLS_LOADING); + if (sv.models[modelindex]->loadstate == MLS_LOADED) return sv.models[modelindex]; } return NULL; @@ -445,7 +447,7 @@ static void SVQ3_Trace(q3trace_t *result, vec3_t start, vec3_t mins, vec3_t maxs if (entnum == -1) ourowner = -1; - else if ( entnum != ENTITYNUM_WORLD ) + else if (entnum != ENTITYNUM_WORLD) { ourowner = GENTITY_FOR_NUM(entnum)->r.ownerNum; if (ourowner == ENTITYNUM_NONE) @@ -604,8 +606,8 @@ static void SVQ3_SetBrushModel(q3sharedEntity_t *ent, char *modelname) } else { - VectorCopy(mod->mins, ent->r.mins); - VectorCopy(mod->maxs, ent->r.maxs); + VectorCopy(vec3_origin, ent->r.mins); + VectorCopy(vec3_origin, ent->r.maxs); } ent->r.bmodel = true; ent->r.contents = -1; @@ -2945,7 +2947,7 @@ void SVQ3_ParseUsercmd(client_t *client, qboolean delta) memcpy(&client->lastcmd, to, sizeof(client->lastcmd)); if (svs.gametype == GT_QUAKE3) SVQ3_ClientThink(client); - else + else if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) { usercmd_t temp; temp = client->lastcmd; diff --git a/engine/server/world.c b/engine/server/world.c index 3370f3f5..425c9692 100644 --- a/engine/server/world.c +++ b/engine/server/world.c @@ -552,7 +552,7 @@ void World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers) } // link to PVS leafs - if (w->worldmodel && !w->worldmodel->needload) + if (w->worldmodel && w->worldmodel->loadstate == MLS_LOADED && w->worldmodel->funcs.FindTouchedLeafs) { w->worldmodel->funcs.FindTouchedLeafs(w->worldmodel, &ent->pvsinfo, ent->v->absmin, ent->v->absmax); } @@ -1011,7 +1011,7 @@ qboolean World_TransformedTrace (struct model_s *model, int hulloverride, int fr } // don't rotate non bsp ents. Too small to bother. - if (model) + if (model && model->loadstate == MLS_LOADED) { VectorSubtract (start, origin, start_l); VectorSubtract (end, origin, end_l); @@ -1074,7 +1074,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v else model = NULL; - if (!model || model->needload) + if (!model || model->loadstate != MLS_LOADED) { vec3_t boxmins, boxmaxs; model = NULL; @@ -1112,7 +1112,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v //okay, we hit the bbox model = w->Get_CModel(w, mdlidx); - if (model && model->funcs.NativeTrace && !model->needload) + if (model && model->funcs.NativeTrace && model->loadstate == MLS_LOADED) { //do the second trace, using the actual mesh. World_TransformedTrace(model, hullnum, ent->v->frame, start, end, mins, maxs, capsule, &trace, eorg, ent->v->angles, hitcontentsmask); @@ -1135,7 +1135,7 @@ static trace_t WorldQ2_ClipMoveToEntity (world_t *w, q2edict_t *ent, vec3_t star if (ent->s.solid == Q2SOLID_BSP) model = w->Get_CModel(w, ent->s.modelindex); - if (!model || model->type != mod_brush || model->needload) + if (!model || model->type != mod_brush || model->loadstate != MLS_LOADED) { vec3_t boxmins, boxmaxs; VectorSubtract (ent->mins, maxs, boxmins); @@ -1331,7 +1331,8 @@ static model_t *WorldQ2_ModelForEntity (world_t *w, q2edict_t *ent) if (!model) SV_Error ("Q2SOLID_BSP with a non bsp model"); - return model; + if (model->loadstate == MLS_LOADED) + return model; } // create a temp hull from bounding box sizes diff --git a/engine/shaders/glsl/defaultskin.glsl b/engine/shaders/glsl/defaultskin.glsl index 3b4b331e..7b77d5e3 100644 --- a/engine/shaders/glsl/defaultskin.glsl +++ b/engine/shaders/glsl/defaultskin.glsl @@ -99,7 +99,6 @@ void main () vec4 lc = texture2D(s_t1, tc); col.rgb += lc.rgb*e_lowercolour*lc.a; #endif - col.rgb *= light; #if defined(BUMP) && defined(SPECULAR) vec3 bumps = normalize(vec3(texture2D(s_t4, tc)) - 0.5); @@ -110,6 +109,8 @@ void main () col.rgb += cvar_gl_specular * spec * specs.rgb; #endif + col.rgb *= light; + #ifdef FULLBRIGHT vec4 fb = texture2D(s_t3, tc); // col.rgb = mix(col.rgb, fb.rgb, fb.a); diff --git a/engine/shaders/glsl/rtlight.glsl b/engine/shaders/glsl/rtlight.glsl index 688a3b73..15ff57c3 100644 --- a/engine/shaders/glsl/rtlight.glsl +++ b/engine/shaders/glsl/rtlight.glsl @@ -39,6 +39,14 @@ #define LOWER #endif +//if there's no vertex normals known, disable some stuff. +//FIXME: this results in dupe permutations. +#ifdef NOBUMP +#undef SPECULAR +#undef BUMP +#undef OFFSETMAPPING +#endif + varying vec2 tcbase; varying vec3 lightvector; @@ -66,9 +74,15 @@ void main () gl_Position = skeletaltransform_wnst(w,n,s,t); tcbase = v_texcoord; //pass the texture coords straight through vec3 lightminusvertex = l_lightposition - w.xyz; +#ifdef NOBUMP + //the only important thing is distance + lightvector = lightminusvertex; +#else + //the light direction relative to the surface normal, for bumpmapping. lightvector.x = dot(lightminusvertex, s.xyz); lightvector.y = dot(lightminusvertex, t.xyz); lightvector.z = dot(lightminusvertex, n.xyz); +#endif #if defined(SPECULAR)||defined(OFFSETMAPPING) vec3 eyeminusvertex = e_eyepos - w.xyz; eyevector.x = dot(eyeminusvertex, s.xyz); @@ -130,8 +144,7 @@ vec3 ShadowmapCoord(void) #ifdef SPOT //bias it. don't bother figuring out which side or anything, its not needed //l_projmatrix contains the light's projection matrix so no other magic needed - vtexprojcoord.z -= 0.015; - return (vtexprojcoord.xyz/vtexprojcoord.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5); + return ((vtexprojcoord.xyz-vec3(0.0,0.0,0.015))/vtexprojcoord.w + vec3(1.0, 1.0, 1.0)) * vec3(0.5, 0.5, 0.5); //#elif defined(CUBESHADOW) // vec3 shadowcoord = vshadowcoord.xyz / vshadowcoord.w; // #define dosamp(x,y) shadowCube(s_t4, shadowcoord + vec2(x,y)*texscale.xy).r @@ -254,14 +267,19 @@ void main () vec4 specs = texture2D(s_t2, tcbase); #endif - vec3 nl = normalize(lightvector); float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0); vec3 diff; -#ifdef BUMP - diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); +#ifdef NOBUMP + //surface can only support ambient lighting, even for lights that try to avoid it. + diff = bases * (l_lightcolourscale.x+l_lightcolourscale.y); #else - //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. - diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); + vec3 nl = normalize(lightvector); + #ifdef BUMP + diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(bumps, nl), 0.0)); + #else + //we still do bumpmapping even without bumps to ensure colours are always sane. light.exe does it too. + diff = bases * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0)); + #endif #endif diff --git a/engine/sw/sw.h b/engine/sw/sw.h index cf13f647..b86a97f4 100644 --- a/engine/sw/sw.h +++ b/engine/sw/sw.h @@ -1,9 +1,6 @@ typedef struct { - texcom_t com; - - char name[64]; int pwidth; int pheight; int pwidthmask; @@ -42,6 +39,7 @@ typedef struct typedef struct { + int scoord[2]; vec4_t vcoord; vec2_t tccoord; vec2_t lmcoord; @@ -158,14 +156,9 @@ void SW_VID_UpdateViewport(wqcom_t *com); -texid_tf SW_LoadTexture(const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags); -texid_tf SW_LoadTexture8Pal24(const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags); -texid_tf SW_LoadTexture8Pal32(const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags); -texid_tf SW_LoadCompressed(const char *name); -texid_tf SW_FindTexture(const char *identifier, unsigned int flags); -texid_tf SW_AllocNewTexture(const char *identifier, int w, int h, unsigned int flags); -void SW_Upload(texid_t tex, const char *name, uploadfmt_t fmt, void *data, void *palette, int width, int height, unsigned int flags); -void SW_DestroyTexture(texid_t tex); +void SW_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis); +qboolean SW_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips); +void SW_DestroyTexture (texid_t tex); void SWBE_SelectMode(backendmode_t mode); @@ -179,7 +172,7 @@ 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); -qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, unsigned int lmode); +qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode); qboolean SWBE_LightCullModel(vec3_t org, struct model_s *model); void SWBE_RenderToTextureUpdate2d(qboolean destchanged); void SWBE_Set2D(void); diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c index b80658db..d2b8f6fe 100644 --- a/engine/sw/sw_backend.c +++ b/engine/sw/sw_backend.c @@ -431,7 +431,7 @@ void SWBE_DrawMesh_Single(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, str { com = SWRast_BeginCommand(&commandqueue, WTC_TRIFAN, mesh->numvertexes*sizeof(swvert_t) + sizeof(com->trifan) - sizeof(com->trifan.verts)); - com->trifan.texture = texnums->base.ptr; + com->trifan.texture = texnums->base->ptr; com->trifan.numverts = mesh->numvertexes; SWBE_TransformVerticies(com->trifan.verts, mesh); @@ -442,7 +442,7 @@ void SWBE_DrawMesh_Single(shader_t *shader, mesh_t *mesh, struct vbo_s *vbo, str { com = SWRast_BeginCommand(&commandqueue, 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.texture = texnums->base->ptr; com->trisoup.numverts = mesh->numvertexes; com->trisoup.numidx = mesh->numindexes; @@ -616,6 +616,8 @@ void SWBE_DrawWorld(qboolean drawworld, qbyte *vis) } void SWBE_Init(void) { + memset(&r_config, 0, sizeof(r_config)); + r_config.maxtexturesize = 512; BE_InitTables(); } void SWBE_GenBrushModelVBO(struct model_s *mod) @@ -652,7 +654,7 @@ void SWBE_SelectEntity(struct entity_s *ent) SWBE_UpdateUniforms(); } -qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, unsigned int lmode) +qboolean SWBE_SelectDLight(struct dlight_s *dl, vec3_t colour, vec3_t axis[3], unsigned int lmode) { return false; } diff --git a/engine/sw/sw_image.c b/engine/sw/sw_image.c index cda4d4e3..e6dd30b7 100644 --- a/engine/sw/sw_image.c +++ b/engine/sw/sw_image.c @@ -2,55 +2,7 @@ #ifdef SWQUAKE #include "sw.h" -void SW_RoundDimensions(int width, int height, int *scaled_width, int *scaled_height, qboolean mipmap) -{ - for (*scaled_width = 1 ; *scaled_width < width ; *scaled_width<<=1) - ; - for (*scaled_height = 1 ; *scaled_height < height ; *scaled_height<<=1) - ; - - if (*scaled_width != width) - *scaled_width >>= 1; - if (*scaled_height != height) - *scaled_height >>= 1; - - if (*scaled_width > 256) - *scaled_width = 256; - if (*scaled_height > 256) - *scaled_height = 256; -} - -texid_tf SW_AllocNewTexture(const char *identifier, int w, int h, unsigned int flags) -{ - int nw, nh; - texid_t n; - swimage_t *img; - if (w & 3) - return r_nulltex; - - SW_RoundDimensions(w, h, &nw, &nh, false); - img = BZ_Malloc(sizeof(*img) - sizeof(img->data) + (nw * nh * 4)); - - Q_strncpy(img->name, identifier, sizeof(img->name)); - img->com.width = w; - img->com.height = h; - img->pwidth = nw; - img->pheight = nh; - img->pitch = nw; - - img->pwidthmask = nw-1; - img->pheightmask = nh-1; - - n.ptr = img; - n.ref = &img->com; - return n; -} -texid_tf SW_FindTexture(const char *identifier, unsigned int flags) -{ - return r_nulltex; -} - -void SW_RGBToBGR(swimage_t *img) +void SW_NukeAlpha(swimage_t *img) { int x, y; unsigned int *d = img->data; @@ -58,105 +10,16 @@ void SW_RGBToBGR(swimage_t *img) { for (x = 0; x < img->pwidth; x++) { - d[x] = (d[x]&0xff00ff00) | ((d[x]&0xff)<<16) | ((d[x]&0xff0000)>>16); + d[x] |= 0xff000000; } d += img->pitch; } } -void SW_Upload32(swimage_t *img, int iw, int ih, unsigned int *data) -{ - //rescale the input to the output. - //just use nearest-sample, cos we're lazy. - int x, y; - unsigned int *out = img->data; - unsigned int *in; - int sx, sy, stx, sty; - int ow = img->pwidth, oh = img->pheight; - stx = (iw<<16) / ow; - sty = (ih<<16) / oh; - for (y = 0, sy = 0; y < oh; y++, sy += sty) - { - in = data + iw*(sy>>16); - for (x = 0, sx = 0; x < ow; x++, sx += stx) - { - out[x] = in[sx>>16]; - } - out += img->pitch; - } - SW_RGBToBGR(img); -} -void SW_Upload8(swimage_t *img, int iw, int ih, unsigned char *data) -{ - //rescale the input to the output. - //just use nearest-sample, cos we're lazy. - int x, y; - unsigned int *out = img->data; - unsigned char *in; - int sx, sy, stx, sty; - int ow = img->pwidth, oh = img->pheight; - stx = (iw<<16) / ow; - sty = (ih<<16) / oh; - - for (y = 0, sy = 0; y < oh; y++, sy += sty) - { - in = data + iw*(sy>>16); - for (x = 0, sx = 0; x < ow; x++, sx += stx) - { - out[x] = d_8to24rgbtable[in[sx>>16]]; - } - out += img->pitch; - } - - SW_RGBToBGR(img); -} - -texid_tf SW_LoadTexture(const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags) -{ - texid_t img = SW_FindTexture(identifier, flags); - if (!img.ptr) - img = SW_AllocNewTexture(identifier, width, height, flags); - if (!img.ptr) - return r_nulltex; - 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(const char *identifier, int width, int height, qbyte *data, qbyte *palette24, unsigned int flags) -{ - return r_nulltex; -} -texid_tf SW_LoadTexture8Pal32(const char *identifier, int width, int height, qbyte *data, qbyte *palette32, unsigned int flags) -{ - return r_nulltex; -} -texid_tf SW_LoadCompressed(const char *name) -{ - return r_nulltex; -} -void SW_Upload(texid_t tex, const 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; + swimage_t *img = tex->ptr; + tex->ptr = NULL; /*make sure its not in use by the renderer*/ SWRast_Sync(&commandqueue); @@ -164,4 +27,88 @@ void SW_DestroyTexture(texid_t tex) /*okay, it can be killed*/ BZ_Free(img); } + + + +void SW_UpdateFiltering (image_t *imagelist, int filtermip[3], int filterpic[3], int mipcap[2], float anis) +{ + //always nearest... +} + +qboolean SW_LoadTextureMips (texid_t tex, struct pendingtextureinfo *mips) +{ + swimage_t *img; + int i; + int nw = mips->mip[0].width; + int nh = mips->mip[0].height; + qbyte *indata = mips->mip[0].data; + qbyte *imgdata; + + + if (mips->type != PTI_2D) + return false; + + //ensure we reject any s3tc encodings + switch(mips->encoding) + { + case PTI_RGBA8: + case PTI_RGBX8: + case PTI_BGRA8: + case PTI_BGRX8: + break; + default: + return false; + } + + img = BZ_Malloc(sizeof(*img) - sizeof(img->data) + (nw * nh * 4)); + imgdata = (qbyte*)(img+1) - sizeof(img->data); + tex->ptr = img; + + img->pwidth = nw; + img->pheight = nh; + img->pitch = nw; + + //precalculated + img->pwidthmask = nw-1; + img->pheightmask = nh-1; + + if (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_RGBX8) + { //assuming PC hardware is bgr + if (mips->encoding == PTI_RGBX8) + { + for (i = 0; i < nw*nh*4; i+=4) + { + imgdata[i+0] = indata[i+2]; + imgdata[i+1] = indata[i+1]; + imgdata[i+2] = indata[i+0]; + imgdata[i+3] = 255; + } + } + else + { + for (i = 0; i < nw*nh*4; i+=4) + { + imgdata[i+0] = indata[i+2]; + imgdata[i+1] = indata[i+1]; + imgdata[i+2] = indata[i+0]; + imgdata[i+3] = indata[i+3]; + } + } + } + else + { + memcpy(imgdata, indata, (nw * nh * 4)); + if (mips->encoding == PTI_BGRX8) + for (i = 0; i < nw*nh*4; i+=4) + imgdata[i+3] = 255; + } + + for (i = 0; i < mips->mipcount; i++) + if (mips->mip[i].needfree) + Z_Free(mips->mip[i].data); + if (mips->extrafree) + Z_Free(mips->extrafree); + + return true; +} #endif diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index ed0f257b..93fcdeeb 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -103,19 +103,19 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' */ /*reorder the verticies for height*/ - if (v1->vcoord[1] > v2->vcoord[1]) + if (v1->scoord[1] > v2->scoord[1]) { vt = v1; v1 = v2; v2 = vt; } - if (v1->vcoord[1] > v3->vcoord[1]) + if (v1->scoord[1] > v3->scoord[1]) { vt = v1; v1 = v3; v3 = vt; } - if (v2->vcoord[1] > v3->vcoord[1]) + if (v2->scoord[1] > v3->scoord[1]) { vt = v3; v3 = v2; @@ -132,26 +132,26 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' //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) + if (v[i]->scoord[0] < 0 || v[i]->scoord[0] > th->vpwidth) return; - if (v[i]->vcoord[1] < 0 || v[i]->vcoord[1] >= th->vpheight) + if (v[i]->scoord[1] < 0 || v[i]->scoord[1] > th->vpheight) return; -// if (v[i]->vcoord[3] < 0) +// if (v[i]->scoord[3] < 0) // return; } for (i = 0; i < 2; i++) { - if (v[i]->vcoord[1] > v[i+1]->vcoord[1]) + if (v[i]->scoord[1] > v[i+1]->scoord[1]) return; } } - fdx1 = v2->vcoord[0] - v1->vcoord[0]; - fdy1 = v2->vcoord[1] - v1->vcoord[1]; + fdx1 = v2->scoord[0] - v1->scoord[0]; + fdy1 = v2->scoord[1] - v1->scoord[1]; - fdx2 = v3->vcoord[0] - v1->vcoord[0]; - fdy2 = v3->vcoord[1] - v1->vcoord[1]; + fdx2 = v3->scoord[0] - v1->scoord[0]; + fdy2 = v3->scoord[1] - v1->scoord[1]; fz = fdx1*fdy2 - fdx2*fdy1; @@ -187,12 +187,13 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' ti = img->data; - y = v1->vcoord[1]; + y = v1->scoord[1]; for (secondhalf = 0; secondhalf <= 1; secondhalf++) { if (secondhalf) { + // return; if (numspans < 0) { interlace = -numspans; @@ -241,7 +242,7 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' } //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) - numspans = v3->vcoord[1] - y; + numspans = v3->scoord[1] - y; } else { @@ -261,24 +262,24 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' recalcside = 3; //flip the triangle to keep it facing the screen (we swapped the verts almost randomly) - numspans = v2->vcoord[1] - y; + numspans = v2->scoord[1] - y; } if (recalcside & 1) { - dx = (vlb->vcoord[0] - vlt->vcoord[0]); - dy = (vlb->vcoord[1] - vlt->vcoord[1]); + dx = (vlb->scoord[0] - vlt->scoord[0]); + dy = (vlb->scoord[1] - vlt->scoord[1]); if (dy > 0) xld = (dx<<16) / dy; else xld = 0; - xl = (int)vlt->vcoord[0]<<16; + xl = (int)vlt->scoord[0]<<16; #ifdef SPAN_ST sl = vlt->tccoord[0] * (img->pwidth<<16); - sld = sld + (((long long)sd*xld)>>16); + sld = sld + (((long long)sd*xld+32767)>>16); tl = vlt->tccoord[1] * (img->pheight<<16); - tld = tld + (((long long)td*xld)>>16); + tld = tld + (((long long)td*xld+32767)>>16); #endif #ifdef SPAN_Z zl = vlt->vcoord[3] * (1<<16); @@ -288,19 +289,17 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' if (recalcside & 2) { - dx = (vrb->vcoord[0] - vrt->vcoord[0]); - dy = (vrb->vcoord[1] - vrt->vcoord[1]); + dx = (vrb->scoord[0] - vrt->scoord[0]); + dy = (vrb->scoord[1] - vrt->scoord[1]); if (dy) xrd = (dx<<16) / dy; else xrd = 0; - xr = (int)vrt->vcoord[0]<<16; + xr = (int)vrt->scoord[0]<<16; } - - - if (y + numspans >= th->vpheight) - numspans = th->vpheight - y - 1; + if (y + numspans > th->vpheight) + numspans = th->vpheight - y; if (numspans <= 0) continue; @@ -389,18 +388,20 @@ SPAN_ST - interpolates S+T across the span. access with 'sc' and 'tc' static void WT_Clip_Top(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result) { float frac; - frac = (1 - in->vcoord[1]) / - (out->vcoord[1] - in->vcoord[1]); - Vector4Interpolate(in->vcoord, frac, out->vcoord, result->vcoord); + frac = (1 - in->scoord[1]) / + (out->scoord[1] - in->scoord[1]); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); // result->vcoord[1] = 0; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } static void WT_Clip_Bottom(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result) { float frac; - frac = ((th->vpheight-2) - in->vcoord[1]) / + frac = ((th->vpheight) - in->vcoord[1]) / (out->vcoord[1] - in->vcoord[1]); - Vector4Interpolate(in->vcoord, frac, out->vcoord, result->vcoord); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); // result->vcoord[1] = vid.pixelheight-1; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } @@ -409,16 +410,18 @@ static void WT_Clip_Left(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t * float frac; frac = (1 - in->vcoord[0]) / (out->vcoord[0] - in->vcoord[0]); - Vector4Interpolate(in->vcoord, frac, out->vcoord, result->vcoord); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); // result->vcoord[0] = 0; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } static void WT_Clip_Right(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *result) { float frac; - frac = ((th->vpwidth-2) - in->vcoord[0]) / + frac = ((th->vpwidth) - in->vcoord[0]) / (out->vcoord[0] - in->vcoord[0]); - Vector4Interpolate(in->vcoord, frac, out->vcoord, result->vcoord); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); // result->vcoord[0] = vid.pixelwidth-1; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } @@ -428,7 +431,8 @@ static void WT_Clip_Near(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t * double frac; frac = (nearclip - in->vcoord[3]) / (out->vcoord[3] - in->vcoord[3]); - VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); result->vcoord[3] = nearclip; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } @@ -439,7 +443,8 @@ static void WT_Clip_Far(swthread_t *th, swvert_t *out, swvert_t *in, swvert_t *r double frac; frac = (farclip - in->vcoord[3]) / (out->vcoord[3] - in->vcoord[3]); - VectorInterpolate(in->vcoord, frac, out->vcoord, result->vcoord); + Vector2Interpolate(in->scoord, frac, out->scoord, result->scoord); + Vector2Interpolate(in->vcoord+2, frac, out->vcoord+2, result->vcoord+2); result->vcoord[3] = farclip; Vector2Interpolate(in->tccoord, frac, out->tccoord, result->tccoord); } @@ -468,18 +473,18 @@ static int WT_ClipPoly(swthread_t *th, int incount, swvert_t *inv, swvert_t *out clip(th, &inv[p], &inv[c], &outv[result]); outv[result].clipflags = 0; - if (outv[result].vcoord[0] < 0) + if (outv[result].scoord[0] < 0) outv[result].clipflags |= CLIP_LEFT_FLAG; - if (outv[result].vcoord[0] >= th->vpwidth) + if (outv[result].scoord[0] > th->vpwidth) outv[result].clipflags |= CLIP_RIGHT_FLAG; - if (outv[result].vcoord[1] < 0) + if (outv[result].scoord[1] < 0) outv[result].clipflags |= CLIP_TOP_FLAG; - if (outv[result].vcoord[1] >= th->vpheight) + if (outv[result].scoord[1] > th->vpheight) outv[result].clipflags |= CLIP_BOTTOM_FLAG; - if (outv[result].vcoord[3] < 0) + if (outv[result].scoord[3] < 0) outv[result].clipflags |= CLIP_NEAR_FLAG; - if (outv[result].vcoord[3] > 1) + if (outv[result].scoord[3] > 1) outv[result].clipflags |= CLIP_FAR_FLAG; result++; @@ -506,16 +511,16 @@ static int WT_TransformVertXY(swthread_t *th, swvert_t *v) tr[2] /= tr[3]; } - v->vcoord[0] = (tr[0]+1)/2 * th->vpwidth; - if (v->vcoord[0] < 0) + v->scoord[0] = (tr[0]+1)/2 * th->vpwidth; + if (v->scoord[0] < 0) result |= CLIP_LEFT_FLAG; - if (v->vcoord[0] >= th->vpwidth) + if (v->scoord[0] > th->vpwidth) result |= CLIP_RIGHT_FLAG; - v->vcoord[1] = (tr[1]+1)/2 * th->vpheight; - if (v->vcoord[1] < 0) + v->scoord[1] = (tr[1]+1)/2 * th->vpheight; + if (v->scoord[1] < 0) result |= CLIP_TOP_FLAG; - if (v->vcoord[1] >= th->vpheight) + if (v->scoord[1] > th->vpheight) result |= CLIP_BOTTOM_FLAG; v->clipflags = result; @@ -1023,22 +1028,14 @@ rendererinfo_t swrendererinfo = SW_Draw_Init, SW_Draw_Shutdown, - SW_LoadTexture, - SW_LoadTexture8Pal24, - SW_LoadTexture8Pal32, - SW_LoadCompressed, - SW_FindTexture, - SW_AllocNewTexture, - SW_Upload, + SW_UpdateFiltering, + SW_LoadTextureMips, SW_DestroyTexture, SW_R_Init, SW_R_DeInit, SW_R_RenderView, - SW_R_NewMap, - SW_R_PreNewMap, - SW_VID_Init, SW_VID_DeInit, SW_VID_SwapBuffers, diff --git a/engine/web/fs_web.c b/engine/web/fs_web.c index 2c18d614..e7419ea1 100644 --- a/engine/web/fs_web.c +++ b/engine/web/fs_web.c @@ -155,7 +155,7 @@ vfsfile_t *VFSOS_Open(const char *osname, const char *mode) qboolean needsflush; f = VFSWEB_Open(osname, mode, &needsflush); if (needsflush) - FS_FlushFSHashReally(); + FS_FlushFSHashReally(true); return f; } diff --git a/plugins/plugin.c b/plugins/plugin.c index e15637e3..14e7ebf2 100644 --- a/plugins/plugin.c +++ b/plugins/plugin.c @@ -194,6 +194,11 @@ BUILTIN(void, Menu_Control, (int mnum)); BUILTINR(int, Key_GetKeyCode, (char *keyname)); #undef ARGNAMES +#if !defined(Q3_VM) && defined(FTEPLUGIN) +#define ARGNAMES ,name,handle,mode +BUILTINR(qboolean, VFS_Open, (char *name, vfsfile_t **handle, char *mode));//opens a direct vfs file. no access checks, and so can be used in threaded plugins +#undef ARGNAMES +#endif #define ARGNAMES ,name,handle,mode BUILTINR(int, FS_Open, (char *name, qhandle_t *handle, int mode)); #undef ARGNAMES @@ -350,6 +355,9 @@ void Plug_InitStandardBuiltins(void) CHECKBUILTIN(Cvar_Update); //file system +#if !defined(Q3_VM) && defined(FTEPLUGIN) + CHECKBUILTIN(VFS_Open); +#endif CHECKBUILTIN(FS_Open); CHECKBUILTIN(FS_Read); CHECKBUILTIN(FS_Write); diff --git a/plugins/plugin.h b/plugins/plugin.h index 621ecd5e..c2468618 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -251,6 +251,9 @@ EBUILTIN(void, S_RawAudio, (int sourceid, void *data, int speed, int samples, in EBUILTIN(int, ReadInputBuffer, (void *inputbuffer, int buffersize)); EBUILTIN(int, UpdateInputBuffer, (void *inputbuffer, int bytes)); +#if !defined(Q3_VM) && defined(FTEPLUGIN) +EBUILTIN(qboolean, VFS_Open, (char *name, vfsfile_t **handle, char *mode));//opens a direct vfs file. no access checks, and so can be used in threaded plugins +#endif EBUILTIN(int, FS_Open, (char *name, qhandle_t *handle, int mode)); EBUILTIN(void, FS_Close, (qhandle_t handle)); EBUILTIN(int, FS_Write, (qhandle_t handle, void *data, int len));