X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=clvm_cmds.c;h=fc0ad58546b7577db59a85e59cb0674d8f31f2e8;hp=9e1f1d61161a617bac97f673db42876605346faa;hb=36db1374c6782cadb439e5d642e0ffed73236034;hpb=30ea4b2e8a18a0290bffeae4a5802260e9255bf9 diff --git a/clvm_cmds.c b/clvm_cmds.c index 9e1f1d61..fc0ad585 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -4,6 +4,8 @@ #include "csprogs.h" #include "cl_collision.h" #include "r_shadow.h" +#include "jpeg.h" +#include "image.h" //============================================================================ // Client @@ -18,8 +20,11 @@ //4 feature darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh) //4 feature darkplaces csqc: add builtins to clientside qc for gl calls +extern cvar_t v_flipped; +extern cvar_t r_equalize_entities_fullbright; + sfx_t *S_FindName(const char *name); -int Sbar_GetPlayer (int index); +int Sbar_GetSortedPlayerIndex (int index); void Sbar_SortFrags (void); void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius); void CSQC_RelinkAllEntities (int drawmask); @@ -77,45 +82,50 @@ void VM_CL_setmodel (void) { prvm_edict_t *e; const char *m; - model_t *mod; + dp_model_t *mod; int i; VM_SAFEPARMCOUNT(2, VM_CL_setmodel); e = PRVM_G_EDICT(OFS_PARM0); + e->fields.client->modelindex = 0; + e->fields.client->model = 0; + m = PRVM_G_STRING(OFS_PARM1); + mod = NULL; for (i = 0;i < MAX_MODELS && cl.csqc_model_precache[i];i++) { if (!strcmp(cl.csqc_model_precache[i]->name, m)) { - e->fields.client->model = PRVM_SetEngineString(cl.csqc_model_precache[i]->name); + mod = cl.csqc_model_precache[i]; + e->fields.client->model = PRVM_SetEngineString(mod->name); e->fields.client->modelindex = -(i+1); - return; + break; } } - for (i = 0;i < MAX_MODELS;i++) - { - mod = cl.model_precache[i]; - if (mod && !strcmp(mod->name, m)) + if( !mod ) { + for (i = 0;i < MAX_MODELS;i++) { - e->fields.client->model = PRVM_SetEngineString(mod->name); - e->fields.client->modelindex = i; - return; + mod = cl.model_precache[i]; + if (mod && !strcmp(mod->name, m)) + { + e->fields.client->model = PRVM_SetEngineString(mod->name); + e->fields.client->modelindex = i; + break; + } } } - e->fields.client->modelindex = 0; - e->fields.client->model = 0; - VM_Warning ("setmodel: model '%s' not precached\n", m); - - // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black] - if (mod) - { - SetMinMaxSize (e, mod->normalmins, mod->normalmaxs); + if( mod ) { + // TODO: check if this breaks needed consistency and maybe add a cvar for it too?? [1/10/2008 Black] + //SetMinMaxSize (e, mod->normalmins, mod->normalmaxs); } else + { SetMinMaxSize (e, vec3_origin, vec3_origin); + VM_Warning ("setmodel: model '%s' not precached\n", m); + } } // #4 void(entity e, vector min, vector max) setsize @@ -221,12 +231,23 @@ static void VM_CL_spawn (void) VM_RETURN_EDICT(ed); } -// #16 float(vector v1, vector v2, float movetype, entity ignore) traceline +void CL_VM_SetTraceGlobals(const trace_t *trace, int svent) +{ + prvm_eval_t *val; + VM_SetTraceGlobals(trace); + if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_networkentity))) + val->_float = svent; +} + +#define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY) +#define CL_HitNetworkPlayers(move) !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS) + +// #16 void(vector v1, vector v2, float movetype, entity ignore) traceline static void VM_CL_traceline (void) { float *v1, *v2; trace_t trace; - int move; + int move, svent; prvm_edict_t *ent; VM_SAFEPARMCOUNTRANGE(4, 4, VM_CL_traceline); @@ -238,12 +259,12 @@ static void VM_CL_traceline (void) move = (int)PRVM_G_FLOAT(OFS_PARM2); ent = PRVM_G_EDICT(OFS_PARM3); - if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2])) PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); - trace = CL_Move(v1, vec3_origin, vec3_origin, v2, move, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceLine(v1, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true); - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); } /* @@ -262,7 +283,7 @@ static void VM_CL_tracebox (void) { float *v1, *v2, *m1, *m2; trace_t trace; - int move; + int move, svent; prvm_edict_t *ent; VM_SAFEPARMCOUNTRANGE(6, 8, VM_CL_tracebox); // allow more parameters for future expansion @@ -276,15 +297,15 @@ static void VM_CL_tracebox (void) move = (int)PRVM_G_FLOAT(OFS_PARM4); ent = PRVM_G_EDICT(OFS_PARM5); - if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || IS_NAN(v2[2])) PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); - trace = CL_Move(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceBox(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true); - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); } -trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore) +trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore, int *svent) { int i; float gravity; @@ -314,7 +335,7 @@ trace_t CL_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore) VectorMA (tossent->fields.client->angles, 0.05, tossent->fields.client->avelocity, tossent->fields.client->angles); VectorScale (tossent->fields.client->velocity, 0.05, move); VectorAdd (tossent->fields.client->origin, move, end); - trace = CL_Move (tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true); + trace = CL_TraceBox(tossent->fields.client->origin, tossent->fields.client->mins, tossent->fields.client->maxs, end, MOVE_NORMAL, tossent, CL_GenericHitSuperContentsMask(tossent), true, true, NULL, true); VectorCopy (trace.endpos, tossent->fields.client->origin); if (trace.fraction < 1) @@ -334,6 +355,7 @@ static void VM_CL_tracetoss (void) trace_t trace; prvm_edict_t *ent; prvm_edict_t *ignore; + int svent; prog->xfunction->builtinsprofile += 600; @@ -347,9 +369,9 @@ static void VM_CL_tracetoss (void) } ignore = PRVM_G_EDICT(OFS_PARM1); - trace = CL_Trace_Toss (ent, ignore); + trace = CL_Trace_Toss (ent, ignore, &svent); - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); } @@ -358,7 +380,7 @@ void VM_CL_precache_model (void) { const char *name; int i; - model_t *m; + dp_model_t *m; VM_SAFEPARMCOUNT(1, VM_CL_precache_model); @@ -372,14 +394,14 @@ void VM_CL_precache_model (void) } } PRVM_G_FLOAT(OFS_RETURN) = 0; - m = Mod_ForName(name, false, false, false); + m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL); if(m && m->loaded) { for (i = 0;i < MAX_MODELS;i++) { if (!cl.csqc_model_precache[i]) { - cl.csqc_model_precache[i] = (model_t*)m; + cl.csqc_model_precache[i] = (dp_model_t*)m; PRVM_G_FLOAT(OFS_RETURN) = -(i+1); return; } @@ -414,8 +436,16 @@ static void VM_CL_findradius (void) vec3_t org, eorg, mins, maxs; int i, numtouchedicts; prvm_edict_t *touchedicts[MAX_EDICTS]; + int chainfield; - VM_SAFEPARMCOUNT(2, VM_CL_findradius); + VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius); + + if(prog->argc == 3) + chainfield = PRVM_G_INT(OFS_PARM2); + else + chainfield = prog->fieldoffsets.chain; + if(chainfield < 0) + PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME); chain = (prvm_edict_t *)prog->edicts; @@ -457,7 +487,7 @@ static void VM_CL_findradius (void) VectorMAMAM(1, eorg, -0.5f, ent->fields.client->mins, -0.5f, ent->fields.client->maxs, eorg); if (DotProduct(eorg, eorg) < radius2) { - ent->fields.client->chain = PRVM_EDICT_TO_PROG(chain); + PRVM_EDICTFIELDVALUE(ent, chainfield)->edict = PRVM_EDICT_TO_PROG(chain); chain = ent; } } @@ -493,7 +523,7 @@ static void VM_CL_droptofloor (void) VectorCopy (ent->fields.client->origin, end); end[2] -= 256; - trace = CL_Move(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); if (trace.fraction != 1) { @@ -522,7 +552,7 @@ static void VM_CL_lightstyle (void) VM_Warning("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n"); return; } - strlcpy (cl.lightstyle[i].map, MSG_ReadString(), sizeof (cl.lightstyle[i].map)); + strlcpy (cl.lightstyle[i].map, c, sizeof (cl.lightstyle[i].map)); cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0; cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map); } @@ -572,7 +602,7 @@ realcheck: start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*sv_stepheight.value; - trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); if (trace.fraction == 1.0) return; @@ -586,7 +616,7 @@ realcheck: start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; - trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceLine(start, stop, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; @@ -683,6 +713,8 @@ void VM_CL_R_ClearScene (void) r_refdef.view.frustum_y *= r_refdef.frustumscale_y; r_refdef.view.ortho_x = scr_fov.value * (3.0 / 4.0) * (float)r_refdef.view.width / (float)r_refdef.view.height / vid_pixelheight.value; r_refdef.view.ortho_y = scr_fov.value * (3.0 / 4.0); + r_refdef.view.clear = true; + r_refdef.view.isoverlay = false; // FIXME: restore cl.csqc_origin // FIXME: restore cl.csqc_angles cl.csqc_vidvars.drawworld = true; @@ -695,6 +727,7 @@ extern void CSQC_Predraw (prvm_edict_t *ed);//csprogs.c extern void CSQC_Think (prvm_edict_t *ed);//csprogs.c void VM_CL_R_AddEntities (void) { + double t = Sys_DoubleTime(); int i, drawmask; prvm_edict_t *ed; VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntities); @@ -719,13 +752,18 @@ void VM_CL_R_AddEntities (void) continue; CSQC_AddRenderEdict(ed); } + + // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView + prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t; } //#302 void(entity ent) addentity (EXT_CSQC) void VM_CL_R_AddEntity (void) { + double t = Sys_DoubleTime(); VM_SAFEPARMCOUNT(1, VM_CL_R_AddEntity); CSQC_AddRenderEdict(PRVM_G_EDICT(OFS_PARM0)); + prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t; } //#303 float(float property, ...) setproperty (EXT_CSQC) @@ -744,31 +782,31 @@ void VM_CL_R_SetView (void) switch(c) { case VF_MIN: - r_refdef.view.x = (int)(f[0] * vid.width / vid_conwidth.value); - r_refdef.view.y = (int)(f[1] * vid.height / vid_conheight.value); + r_refdef.view.x = (int)(f[0]); + r_refdef.view.y = (int)(f[1]); break; case VF_MIN_X: - r_refdef.view.x = (int)(k * vid.width / vid_conwidth.value); + r_refdef.view.x = (int)(k); break; case VF_MIN_Y: - r_refdef.view.y = (int)(k * vid.height / vid_conheight.value); + r_refdef.view.y = (int)(k); break; case VF_SIZE: - r_refdef.view.width = (int)(f[0] * vid.width / vid_conwidth.value); - r_refdef.view.height = (int)(f[1] * vid.height / vid_conheight.value); - break; - case VF_SIZE_Y: - r_refdef.view.width = (int)(k * vid.width / vid_conwidth.value); + r_refdef.view.width = (int)(f[0]); + r_refdef.view.height = (int)(f[1]); break; case VF_SIZE_X: - r_refdef.view.height = (int)(k * vid.height / vid_conheight.value); + r_refdef.view.width = (int)(k); + break; + case VF_SIZE_Y: + r_refdef.view.height = (int)(k); break; case VF_VIEWPORT: - r_refdef.view.x = (int)(f[0] * vid.width / vid_conwidth.value); - r_refdef.view.y = (int)(f[1] * vid.height / vid_conheight.value); + r_refdef.view.x = (int)(f[0]); + r_refdef.view.y = (int)(f[1]); f = PRVM_G_VECTOR(OFS_PARM2); - r_refdef.view.width = (int)(f[0] * vid.width / vid_conwidth.value); - r_refdef.view.height = (int)(f[1] * vid.height / vid_conheight.value); + r_refdef.view.width = (int)(f[0]); + r_refdef.view.height = (int)(f[1]); break; case VF_FOV: r_refdef.view.frustum_x = tan(f[0] * M_PI / 360.0);r_refdef.view.ortho_x = f[0]; @@ -813,13 +851,13 @@ void VM_CL_R_SetView (void) CSQC_R_RecalcView(); break; case VF_DRAWWORLD: - cl.csqc_vidvars.drawworld = k; + cl.csqc_vidvars.drawworld = k != 0; break; case VF_DRAWENGINESBAR: - cl.csqc_vidvars.drawenginesbar = k; + cl.csqc_vidvars.drawenginesbar = k != 0; break; case VF_DRAWCROSSHAIR: - cl.csqc_vidvars.drawcrosshair = k; + cl.csqc_vidvars.drawcrosshair = k != 0; break; case VF_CL_VIEWANGLES: VectorCopy(f, cl.viewangles); @@ -836,6 +874,9 @@ void VM_CL_R_SetView (void) case VF_PERSPECTIVE: r_refdef.view.useperspective = k != 0; break; + case VF_CLEARSCREEN: + r_refdef.view.isoverlay = !k; + break; default: PRVM_G_FLOAT(OFS_RETURN) = 0; VM_Warning("VM_CL_R_SetView : unknown parm %i\n", c); @@ -844,32 +885,57 @@ void VM_CL_R_SetView (void) PRVM_G_FLOAT(OFS_RETURN) = 1; } -//#304 void() renderscene (EXT_CSQC) -void VM_CL_R_RenderScene (void) -{ - VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene); - // we need to update any RENDER_VIEWMODEL entities at this point because - // csqc supplies its own view matrix - CL_UpdateViewEntities(); - // now draw stuff! - R_RenderView(); -} - -//#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC) +//#305 void(vector org, float radius, vector lightcolours[, float style, string cubemapname, float pflags]) adddynamiclight (EXT_CSQC) void VM_CL_R_AddDynamicLight (void) { - float *pos, *col; - matrix4x4_t matrix; - VM_SAFEPARMCOUNTRANGE(3, 3, VM_CL_R_AddDynamicLight); + double t = Sys_DoubleTime(); + vec_t *org; + float radius = 300; + vec_t *col; + int style = -1; + const char *cubemapname = NULL; + int pflags = PFLAGS_CORONA | PFLAGS_FULLDYNAMIC; + float coronaintensity = 1; + float coronasizescale = 0.25; + qboolean castshadow = true; + float ambientscale = 0; + float diffusescale = 1; + float specularscale = 1; + matrix4x4_t matrix; + vec3_t forward, left, up; + VM_SAFEPARMCOUNTRANGE(3, 8, VM_CL_R_AddDynamicLight); // if we've run out of dlights, just return if (r_refdef.scene.numlights >= MAX_DLIGHTS) return; - pos = PRVM_G_VECTOR(OFS_PARM0); + org = PRVM_G_VECTOR(OFS_PARM0); + radius = PRVM_G_FLOAT(OFS_PARM1); col = PRVM_G_VECTOR(OFS_PARM2); - Matrix4x4_CreateFromQuakeEntity(&matrix, pos[0], pos[1], pos[2], 0, 0, 0, PRVM_G_FLOAT(OFS_PARM1)); - R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &matrix, col, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + if (prog->argc >= 4) + { + style = (int)PRVM_G_FLOAT(OFS_PARM3); + if (style >= MAX_LIGHTSTYLES) + { + Con_DPrintf("VM_CL_R_AddDynamicLight: out of bounds lightstyle index %i\n", style); + style = -1; + } + } + if (prog->argc >= 5) + cubemapname = PRVM_G_STRING(OFS_PARM4); + if (prog->argc >= 6) + pflags = (int)PRVM_G_FLOAT(OFS_PARM5); + coronaintensity = (pflags & PFLAGS_CORONA) != 0; + castshadow = (pflags & PFLAGS_NOSHADOW) == 0; + + VectorScale(prog->globals.client->v_forward, radius, forward); + VectorScale(prog->globals.client->v_right, -radius, left); + VectorScale(prog->globals.client->v_up, radius, up); + Matrix4x4_FromVectors(&matrix, forward, left, up, org); + + R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &matrix, col, style, cubemapname, castshadow, coronaintensity, coronasizescale, ambientscale, diffusescale, specularscale, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights++]; + prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t; } //============================================================================ @@ -882,7 +948,12 @@ static void VM_CL_unproject (void) VM_SAFEPARMCOUNT(1, VM_CL_unproject); f = PRVM_G_VECTOR(OFS_PARM0); - VectorSet(temp, f[2], f[0] * f[2] * -r_refdef.view.frustum_x * 2.0 / r_refdef.view.width, f[1] * f[2] * -r_refdef.view.frustum_y * 2.0 / r_refdef.view.height); + if(v_flipped.integer) + f[0] = (2 * r_refdef.view.x + r_refdef.view.width) * (vid_conwidth.integer / (float) vid.width) - f[0]; + VectorSet(temp, + f[2], + (-1.0 + 2.0 * (f[0] / (vid_conwidth.integer / (float) vid.width) - r_refdef.view.x) / r_refdef.view.width) * f[2] * -r_refdef.view.frustum_x, + (-1.0 + 2.0 * (f[1] / (vid_conheight.integer / (float) vid.height) - r_refdef.view.y) / r_refdef.view.height) * f[2] * -r_refdef.view.frustum_y); Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN)); } @@ -897,7 +968,12 @@ static void VM_CL_project (void) f = PRVM_G_VECTOR(OFS_PARM0); Matrix4x4_Invert_Simple(&m, &r_refdef.view.matrix); Matrix4x4_Transform(&m, f, v); - VectorSet(PRVM_G_VECTOR(OFS_RETURN), v[1]/v[0]/-r_refdef.view.frustum_x*0.5*r_refdef.view.width, v[2]/v[0]/-r_refdef.view.frustum_y*r_refdef.view.height*0.5, v[0]); + if(v_flipped.integer) + v[1] = -v[1]; + VectorSet(PRVM_G_VECTOR(OFS_RETURN), + (vid_conwidth.integer / (float) vid.width) * (r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)), + (vid_conheight.integer / (float) vid.height) * (r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)), + v[0]); } //#330 float(float stnum) getstatf (EXT_CSQC) @@ -1011,7 +1087,7 @@ static void VM_CL_setmodelindex (void) //#334 string(float mdlindex) modelnameforindex (EXT_CSQC) static void VM_CL_modelnameforindex (void) { - model_t *model; + dp_model_t *model; VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex); @@ -1044,6 +1120,8 @@ static void VM_CL_trailparticles (void) start = PRVM_G_VECTOR(OFS_PARM2); end = PRVM_G_VECTOR(OFS_PARM3); + if (i < 0) + return; CL_ParticleEffect(i, VectorDistance(start, end), start, end, t->fields.client->velocity, t->fields.client->velocity, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0); } @@ -1057,6 +1135,8 @@ static void VM_CL_pointparticles (void) f = PRVM_G_VECTOR(OFS_PARM1); v = PRVM_G_VECTOR(OFS_PARM2); n = (int)PRVM_G_FLOAT(OFS_PARM3); + if (i < 0) + return; CL_ParticleEffect(i, n, f, f, v, v, NULL, prog->argc >= 5 ? (int)PRVM_G_FLOAT(OFS_PARM4) : 0); } @@ -1071,24 +1151,41 @@ static void VM_CL_getkeybind (void) static void VM_CL_setcursormode (void) { VM_SAFEPARMCOUNT(1, VM_CL_setcursormode); - cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0); + cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0) != 0; cl_ignoremousemoves = 2; } +//#344 vector() getmousepos (EXT_CSQC) +static void VM_CL_getmousepos(void) +{ + VM_SAFEPARMCOUNT(0,VM_CL_getmousepos); + + if (key_consoleactive || key_dest != key_game) + VectorSet(PRVM_G_VECTOR(OFS_RETURN), 0, 0, 0); + else if (cl.csqc_wantsmousemove) + VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_windowmouse_x * vid_conwidth.integer / vid.width, in_windowmouse_y * vid_conheight.integer / vid.height, 0); + else + VectorSet(PRVM_G_VECTOR(OFS_RETURN), in_mouse_x * vid_conwidth.integer / vid.width, in_mouse_y * vid_conheight.integer / vid.height, 0); +} + //#345 float(float framenum) getinputstate (EXT_CSQC) static void VM_CL_getinputstate (void) { int i, frame; VM_SAFEPARMCOUNT(1, VM_CL_getinputstate); frame = (int)PRVM_G_FLOAT(OFS_PARM0); - for (i = 0;i < cl.movement_numqueue;i++) - if (cl.movement_queue[i].sequence == frame) + PRVM_G_FLOAT(OFS_RETURN) = false; + for (i = 0;i < CL_MAX_USERCMDS;i++) + { + if (cl.movecmd[i].sequence == frame) { - VectorCopy(cl.movement_queue[i].viewangles, prog->globals.client->input_angles); - //prog->globals.client->input_buttons = cl.movement_queue[i].//FIXME - VectorCopy(cl.movement_queue[i].move, prog->globals.client->input_movevalues); - prog->globals.client->input_timelength = cl.movement_queue[i].frametime; - if(cl.movement_queue[i].crouch) + VectorCopy(cl.movecmd[i].viewangles, prog->globals.client->input_angles); + prog->globals.client->input_buttons = cl.movecmd[i].buttons; // FIXME: this should not be directly exposed to csqc (translation layer needed?) + prog->globals.client->input_movevalues[0] = cl.movecmd[i].forwardmove; + prog->globals.client->input_movevalues[1] = cl.movecmd[i].sidemove; + prog->globals.client->input_movevalues[2] = cl.movecmd[i].upmove; + prog->globals.client->input_timelength = cl.movecmd[i].frametime; + if(cl.movecmd[i].crouch) { VectorCopy(cl.playercrouchmins, prog->globals.client->pmove_mins); VectorCopy(cl.playercrouchmaxs, prog->globals.client->pmove_maxs); @@ -1098,7 +1195,9 @@ static void VM_CL_getinputstate (void) VectorCopy(cl.playerstandmins, prog->globals.client->pmove_mins); VectorCopy(cl.playerstandmaxs, prog->globals.client->pmove_maxs); } + PRVM_G_FLOAT(OFS_RETURN) = true; } + } } //#346 void(float sens) setsensitivityscaler (EXT_CSQC) @@ -1127,8 +1226,9 @@ static void VM_CL_getplayerkey (void) PRVM_G_INT(OFS_RETURN) = OFS_NULL; Sbar_SortFrags(); - i = Sbar_GetPlayer(i); - if(i < 0) + if (i < 0) + i = Sbar_GetSortedPlayerIndex(-1-i); + if(i < 0 || i >= cl.maxclients) return; t[0] = 0; @@ -1137,28 +1237,28 @@ static void VM_CL_getplayerkey (void) strlcpy(t, cl.scores[i].name, sizeof(t)); else if(!strcasecmp(c, "frags")) - sprintf(t, "%i", cl.scores[i].frags); + dpsnprintf(t, sizeof(t), "%i", cl.scores[i].frags); else if(!strcasecmp(c, "ping")) - sprintf(t, "%i", cl.scores[i].qw_ping); + dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_ping); else if(!strcasecmp(c, "pl")) - sprintf(t, "%i", cl.scores[i].qw_packetloss); + dpsnprintf(t, sizeof(t), "%i", cl.scores[i].qw_packetloss); else if(!strcasecmp(c, "entertime")) - sprintf(t, "%f", cl.scores[i].qw_entertime); + dpsnprintf(t, sizeof(t), "%f", cl.scores[i].qw_entertime); else if(!strcasecmp(c, "colors")) - sprintf(t, "%i", cl.scores[i].colors); + dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors); else if(!strcasecmp(c, "topcolor")) - sprintf(t, "%i", cl.scores[i].colors & 0xf0); + dpsnprintf(t, sizeof(t), "%i", cl.scores[i].colors & 0xf0); else if(!strcasecmp(c, "bottomcolor")) - sprintf(t, "%i", (cl.scores[i].colors &15)<<4); + dpsnprintf(t, sizeof(t), "%i", (cl.scores[i].colors &15)<<4); else if(!strcasecmp(c, "viewentity")) - sprintf(t, "%i", i+1); + dpsnprintf(t, sizeof(t), "%i", i+1); if(!t[0]) return; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t); @@ -1254,6 +1354,55 @@ static void VM_CL_ReadFloat (void) PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat(); } +//#501 string() readpicture (DP_CSQC_READWRITEPICTURE) +extern cvar_t cl_readpicture_force; +static void VM_CL_ReadPicture (void) +{ + const char *name; + unsigned char *data; + unsigned char *buf; + int size; + int i; + cachepic_t *pic; + + VM_SAFEPARMCOUNT(0, VM_CL_ReadPicture); + + name = MSG_ReadString(); + size = MSG_ReadShort(); + + // check if a texture of that name exists + // if yes, it is used and the data is discarded + // if not, the (low quality) data is used to build a new texture, whose name will get returned + + pic = Draw_CachePic_Flags (name, CACHEPICFLAG_NOTPERSISTENT); + + if(size) + { + if(pic->tex == r_texture_notexture) + pic->tex = NULL; // don't overwrite the notexture by Draw_NewPic + if(pic->tex && !cl_readpicture_force.integer) + { + // texture found and loaded + // skip over the jpeg as we don't need it + for(i = 0; i < size; ++i) + MSG_ReadByte(); + } + else + { + // texture not found + // use the attached jpeg as texture + buf = (unsigned char *) Mem_Alloc(tempmempool, size); + MSG_ReadBytes(size, buf); + data = JPEG_LoadImage_BGRA(buf, size); + Mem_Free(buf); + Draw_NewPic(name, image_width, image_height, false, data); + Mem_Free(data); + } + } + + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(name); +} + ////////////////////////////////////////////////////////// static void VM_CL_makestatic (void) @@ -1283,10 +1432,10 @@ static void VM_CL_makestatic (void) // copy it to the current state memset(staticent, 0, sizeof(*staticent)); staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex); - staticent->render.frame1 = staticent->render.frame2 = (int)ent->fields.client->frame; - staticent->render.framelerp = 0; + staticent->render.framegroupblend[0].frame = (int)ent->fields.client->frame; + staticent->render.framegroupblend[0].lerp = 1; // make torchs play out of sync - staticent->render.frame1time = staticent->render.frame2time = lhrandom(-10, -1); + staticent->render.framegroupblend[0].start = lhrandom(-10, -1); staticent->render.skinnum = (int)ent->fields.client->skin; staticent->render.effects = (int)ent->fields.client->effects; staticent->render.alpha = 1; @@ -1294,6 +1443,11 @@ static void VM_CL_makestatic (void) staticent->render.scale = 1; if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)) && val->_float) staticent->render.scale = val->_float; if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.colormod); + if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod)) && VectorLength2(val->vector)) VectorCopy(val->vector, staticent->render.glowmod); + if (!VectorLength2(staticent->render.colormod)) + VectorSet(staticent->render.colormod, 1, 1, 1); + if (!VectorLength2(staticent->render.glowmod)) + VectorSet(staticent->render.glowmod, 1, 1, 1); renderflags = 0; if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && val->_float) renderflags = (int)val->_float; @@ -1308,11 +1462,22 @@ static void VM_CL_makestatic (void) Matrix4x4_CreateFromQuakeEntity(&staticent->render.matrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], staticent->render.scale); // either fullbright or lit - if (!(staticent->render.effects & EF_FULLBRIGHT) && !r_fullbright.integer) - staticent->render.flags |= RENDER_LIGHT; + if(!r_fullbright.integer) + { + if (!(staticent->render.effects & EF_FULLBRIGHT)) + staticent->render.flags |= RENDER_LIGHT; + else if(r_equalize_entities_fullbright.integer) + staticent->render.flags |= RENDER_LIGHT | RENDER_EQUALIZE; + } // turn off shadows from transparent objects if (!(staticent->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST)) && (staticent->render.alpha >= 1)) staticent->render.flags |= RENDER_SHADOW; + if (staticent->render.effects & EF_NODEPTHTEST) + staticent->render.flags |= RENDER_NODEPTHTEST; + if (staticent->render.effects & EF_ADDITIVE) + staticent->render.flags |= RENDER_ADDITIVE; + if (staticent->render.effects & EF_DOUBLESIDED) + staticent->render.flags |= RENDER_DOUBLESIDED; CL_UpdateRenderEntity(&staticent->render); } @@ -1771,9 +1936,9 @@ static void VM_CL_te_flamejet (void) //==================================================================== //DP_QC_GETSURFACE -extern void clippointtosurface(model_t *model, msurface_t *surface, vec3_t p, vec3_t out); +extern void clippointtosurface(dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out); -static msurface_t *cl_getsurface(model_t *model, int surfacenum) +static msurface_t *cl_getsurface(dp_model_t *model, int surfacenum) { if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces) return NULL; @@ -1783,7 +1948,7 @@ static msurface_t *cl_getsurface(model_t *model, int surfacenum) // #434 float(entity e, float s) getsurfacenumpoints static void VM_CL_getsurfacenumpoints(void) { - model_t *model; + dp_model_t *model; msurface_t *surface; VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenumpoints); // return 0 if no such surface @@ -1801,7 +1966,7 @@ static void VM_CL_getsurfacenumpoints(void) static void VM_CL_getsurfacepoint(void) { prvm_edict_t *ed; - model_t *model; + dp_model_t *model; msurface_t *surface; int pointnum; VM_SAFEPARMCOUNT(3, VM_CL_getsurfacenumpoints); @@ -1828,7 +1993,7 @@ static void VM_CL_getsurfacepoint(void) static void VM_CL_getsurfacepointattribute(void) { prvm_edict_t *ed; - model_t *model; + dp_model_t *model; msurface_t *surface; int pointnum; int attributetype; @@ -1849,7 +2014,7 @@ static void VM_CL_getsurfacepointattribute(void) switch( attributetype ) { // float SPA_POSITION = 0; case 0: - VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN)); + VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN)); break; // float SPA_S_AXIS = 1; case 1: @@ -1894,7 +2059,7 @@ static void VM_CL_getsurfacepointattribute(void) // #436 vector(entity e, float s) getsurfacenormal static void VM_CL_getsurfacenormal(void) { - model_t *model; + dp_model_t *model; msurface_t *surface; vec3_t normal; VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenormal); @@ -1913,7 +2078,7 @@ static void VM_CL_getsurfacenormal(void) // #437 string(entity e, float s) getsurfacetexture static void VM_CL_getsurfacetexture(void) { - model_t *model; + dp_model_t *model; msurface_t *surface; VM_SAFEPARMCOUNT(2, VM_CL_getsurfacetexture); PRVM_G_INT(OFS_RETURN) = OFS_NULL; @@ -1929,7 +2094,7 @@ static void VM_CL_getsurfacenearpoint(void) vec3_t clipped, p; vec_t dist, bestdist; prvm_edict_t *ed; - model_t *model = NULL; + dp_model_t *model = NULL; msurface_t *surface; vec_t *point; VM_SAFEPARMCOUNT(2, VM_CL_getsurfacenearpoint); @@ -1972,7 +2137,7 @@ static void VM_CL_getsurfacenearpoint(void) static void VM_CL_getsurfaceclippedpoint(void) { prvm_edict_t *ed; - model_t *model; + dp_model_t *model; msurface_t *surface; vec3_t p, out; VM_SAFEPARMCOUNT(3, VM_CL_getsurfaceclippedpoint); @@ -1988,14 +2153,14 @@ static void VM_CL_getsurfaceclippedpoint(void) } // #443 void(entity e, entity tagentity, string tagname) setattachment -static void VM_CL_setattachment (void) +void VM_CL_setattachment (void) { prvm_edict_t *e; prvm_edict_t *tagentity; const char *tagname; prvm_eval_t *v; int modelindex; - model_t *model; + dp_model_t *model; VM_SAFEPARMCOUNT(3, VM_CL_setattachment); e = PRVM_G_EDICT(OFS_PARM0); @@ -2043,12 +2208,98 @@ static void VM_CL_setattachment (void) int CL_GetTagIndex (prvm_edict_t *e, const char *tagname) { - model_t *model = CL_GetModelFromEdict(e); + dp_model_t *model = CL_GetModelFromEdict(e); if (model) return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname); else return -1; -}; +} + +int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix) +{ + int r; + dp_model_t *model; + int frame; + + *tagname = NULL; + *parentindex = 0; + Matrix4x4_CreateIdentity(tag_localmatrix); + + if (tagindex >= 0 + && (model = CL_GetModelFromEdict(e)) + && model->animscenes) + { + frame = (int)e->fields.client->frame; + if (frame < 0 || frame >= model->numframes) + frame = 0; + + r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, model->animscenes[frame].firstframe, tagindex - 1, parentindex, tagname, tag_localmatrix); + + if(!r) // success? + *parentindex += 1; + + return r; + } + + return 1; +} + +int CL_GetPitchSign(prvm_edict_t *ent) +{ + dp_model_t *model; + if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias) + return -1; + return 1; +} + +void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix) +{ + prvm_eval_t *val; + float scale; + float pitchsign = 1; + + scale = 1; + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); + if (val && val->_float != 0) + scale = val->_float; + + // TODO do we need the same weird angle inverting logic here as in the server side case? + if(viewmatrix) + Matrix4x4_CreateFromQuakeEntity(out, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale * cl_viewmodel_scale.value); + else + { + pitchsign = CL_GetPitchSign(ent); + Matrix4x4_CreateFromQuakeEntity(out, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], pitchsign * ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); + } +} + +int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out) +{ + int frame; + int ret; + dp_model_t *model; + entity_render_t cheatentity; + if (tagindex >= 0 + && (model = CL_GetModelFromEdict(ent)) + && model->animscenes) + { + // if model has wrong frame, engine automatically switches to model first frame + frame = (int)ent->fields.client->frame; + if (frame < 0 || frame >= model->numframes) + frame = 0; + // now we'll do some CHEATING + memset(&cheatentity, 0, sizeof(cheatentity)); + cheatentity.model = model; + CL_LoadFrameGroupBlend(ent, &cheatentity); + R_LerpAnimation(&cheatentity); + ret = CL_BlendTagMatrix(&cheatentity, tagindex, out); + if(ret) + *out = identitymatrix; + return ret; + } + *out = identitymatrix; + return 0; +} // Warnings/errors code: // 0 - normal (everything all-right) @@ -2062,12 +2313,11 @@ extern cvar_t cl_bobcycle; extern cvar_t cl_bobup; int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) { + int ret; prvm_eval_t *val; - int reqframe, attachloop; + int attachloop; matrix4x4_t entitymatrix, tagmatrix, attachmatrix; - prvm_edict_t *attachent; - model_t *model; - float scale; + dp_model_t *model; *out = identitymatrix; // warnings and errors return identical matrix @@ -2077,80 +2327,40 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) return 2; model = CL_GetModelFromEdict(ent); - if(!model) return 3; - if (ent->fields.client->frame >= 0 && ent->fields.client->frame < model->numframes && model->animscenes) - reqframe = model->animscenes[(int)ent->fields.client->frame].firstframe; - else - reqframe = 0; // if model has wrong frame, engine automatically switches to model first frame - - // get initial tag matrix - if (tagindex) + tagmatrix = identitymatrix; + attachloop = 0; + for(;;) { - int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix); - if (ret) + if(attachloop >= 256) + return 5; + // apply transformation by child's tagindex on parent entity and then + // by parent entity itself + ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix); + if(ret && attachloop == 0) return ret; - } - else - tagmatrix = identitymatrix; - - if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict) - { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity - attachloop = 0; - do + CL_GetEntityMatrix(ent, &entitymatrix, false); + Matrix4x4_Concat(&tagmatrix, &attachmatrix, out); + Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); + // next iteration we process the parent entity + if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict) { - attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index); - - model = CL_GetModelFromEdict(attachent); - - if (model && val->_float >= 1 && model->animscenes && attachent->fields.client->frame >= 0 && attachent->fields.client->frame < model->numframes) - Mod_Alias_GetTagMatrix(model, model->animscenes[(int)attachent->fields.client->frame].firstframe, (int)val->_float - 1, &attachmatrix); - else - attachmatrix = identitymatrix; - - // apply transformation by child entity matrix - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - Matrix4x4_Copy(&tagmatrix, out); - - // finally transformate by matrix of tag on parent entity - Matrix4x4_Concat(out, &attachmatrix, &tagmatrix); - Matrix4x4_Copy(&tagmatrix, out); - - ent = attachent; - attachloop += 1; - if (attachloop > 255) // prevent runaway looping - return 5; + tagindex = (int)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float; + ent = PRVM_EDICT_NUM(val->edict); } - while ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict); + else + break; + attachloop++; } - // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain) - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - // Alias models have inverse pitch, bmodels can't have tags, so don't check for modeltype... - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - + // RENDER_VIEWMODEL magic if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float)) - {// RENDER_VIEWMODEL magic + { Matrix4x4_Copy(&tagmatrix, out); - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale); + CL_GetEntityMatrix(prog->edicts, &entitymatrix, true); Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); /* @@ -2180,7 +2390,7 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) } // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) -static void VM_CL_gettagindex (void) +void VM_CL_gettagindex (void) { prvm_edict_t *ent; const char *tag_name; @@ -2215,19 +2425,40 @@ static void VM_CL_gettagindex (void) } // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO) -static void VM_CL_gettaginfo (void) +void VM_CL_gettaginfo (void) { prvm_edict_t *e; int tagindex; matrix4x4_t tag_matrix; + matrix4x4_t tag_localmatrix; + int parentindex; + const char *tagname; int returncode; + prvm_eval_t *val; + vec3_t fo, le, up, trans; VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo); e = PRVM_G_EDICT(OFS_PARM0); tagindex = (int)PRVM_G_FLOAT(OFS_PARM1); returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex); - Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN)); + Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN)); + VectorScale(le, -1, prog->globals.client->v_right); + CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix); + Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans); + + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_parent))) + val->_float = parentindex; + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_name))) + val->string = tagname ? PRVM_SetTempString(tagname) : 0; + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_offset))) + VectorCopy(trans, val->vector); + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_forward))) + VectorCopy(fo, val->vector); + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_right))) + VectorScale(le, -1, val->vector); + if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_up))) + VectorCopy(up, val->vector); switch(returncode) { @@ -2251,43 +2482,555 @@ static void VM_CL_gettaginfo (void) //============================================================================ +//==================== +// DP_CSQC_SPAWNPARTICLE +// a QC hook to engine's CL_NewParticle +//==================== + +// particle theme struct +typedef struct vmparticletheme_s +{ + unsigned short typeindex; + qboolean initialized; + pblend_t blendmode; + porientation_t orientation; + int color1; + int color2; + int tex; + float size; + float sizeincrease; + int alpha; + int alphafade; + float gravity; + float bounce; + float airfriction; + float liquidfriction; + float originjitter; + float velocityjitter; + qboolean qualityreduction; + float lifetime; + float stretch; + int staincolor1; + int staincolor2; + int staintex; + float delayspawn; + float delaycollision; +}vmparticletheme_t; + +// particle spawner +typedef struct vmparticlespawner_s +{ + mempool_t *pool; + qboolean initialized; + qboolean verified; + vmparticletheme_t *themes; + int max_themes; + // global addresses + float *particle_type; + float *particle_blendmode; + float *particle_orientation; + float *particle_color1; + float *particle_color2; + float *particle_tex; + float *particle_size; + float *particle_sizeincrease; + float *particle_alpha; + float *particle_alphafade; + float *particle_time; + float *particle_gravity; + float *particle_bounce; + float *particle_airfriction; + float *particle_liquidfriction; + float *particle_originjitter; + float *particle_velocityjitter; + float *particle_qualityreduction; + float *particle_stretch; + float *particle_staincolor1; + float *particle_staincolor2; + float *particle_staintex; + float *particle_delayspawn; + float *particle_delaycollision; +}vmparticlespawner_t; + +vmparticlespawner_t vmpartspawner; + +// TODO: automatic max_themes grow +static void VM_InitParticleSpawner (int maxthemes) +{ + prvm_eval_t *val; + + // bound max themes to not be an insane value + if (maxthemes < 4) + maxthemes = 4; + if (maxthemes > 2048) + maxthemes = 2048; + // allocate and set up structure + if (vmpartspawner.initialized) // reallocate + { + Mem_FreePool(&vmpartspawner.pool); + memset(&vmpartspawner, 0, sizeof(vmparticlespawner_t)); + } + vmpartspawner.pool = Mem_AllocPool("VMPARTICLESPAWNER", 0, NULL); + vmpartspawner.themes = (vmparticletheme_t *)Mem_Alloc(vmpartspawner.pool, sizeof(vmparticletheme_t)*maxthemes); + vmpartspawner.max_themes = maxthemes; + vmpartspawner.initialized = true; + vmpartspawner.verified = true; + // get field addresses for fast querying (we can do 1000 calls of spawnparticle in a frame) + #define getglobal(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = &val->_float; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; } + #define getglobalvector(v,s) val = PRVM_GLOBALFIELDVALUE(PRVM_ED_FindGlobalOffset(s)); if (val) { vmpartspawner.v = (float *)val->vector; } else { VM_Warning("VM_InitParticleSpawner: missing global '%s', spawner cannot work\n", s); vmpartspawner.verified = false; } + getglobal(particle_type, "particle_type"); + getglobal(particle_blendmode, "particle_blendmode"); + getglobal(particle_orientation, "particle_orientation"); + getglobalvector(particle_color1, "particle_color1"); + getglobalvector(particle_color2, "particle_color2"); + getglobal(particle_tex, "particle_tex"); + getglobal(particle_size, "particle_size"); + getglobal(particle_sizeincrease, "particle_sizeincrease"); + getglobal(particle_alpha, "particle_alpha"); + getglobal(particle_alphafade, "particle_alphafade"); + getglobal(particle_time, "particle_time"); + getglobal(particle_gravity, "particle_gravity"); + getglobal(particle_bounce, "particle_bounce"); + getglobal(particle_airfriction, "particle_airfriction"); + getglobal(particle_liquidfriction, "particle_liquidfriction"); + getglobal(particle_originjitter, "particle_originjitter"); + getglobal(particle_velocityjitter, "particle_velocityjitter"); + getglobal(particle_qualityreduction, "particle_qualityreduction"); + getglobal(particle_stretch, "particle_stretch"); + getglobalvector(particle_staincolor1, "particle_staincolor1"); + getglobalvector(particle_staincolor2, "particle_staincolor2"); + getglobal(particle_staintex, "particle_staintex"); + getglobal(particle_delayspawn, "particle_delayspawn"); + getglobal(particle_delaycollision, "particle_delaycollision"); + #undef getglobal + #undef getglobalvector +} + +// reset particle theme to default values +static void VM_ResetParticleTheme (vmparticletheme_t *theme) +{ + theme->initialized = true; + theme->typeindex = pt_static; + theme->blendmode = PBLEND_ADD; + theme->orientation = PARTICLE_BILLBOARD; + theme->color1 = 0x808080; + theme->color2 = 0xFFFFFF; + theme->tex = 63; + theme->size = 2; + theme->sizeincrease = 0; + theme->alpha = 256; + theme->alphafade = 512; + theme->gravity = 0.0f; + theme->bounce = 0.0f; + theme->airfriction = 1.0f; + theme->liquidfriction = 4.0f; + theme->originjitter = 0.0f; + theme->velocityjitter = 0.0f; + theme->qualityreduction = false; + theme->lifetime = 4; + theme->stretch = 1; + theme->staincolor1 = -1; + theme->staincolor2 = -1; + theme->staintex = -1; + theme->delayspawn = 0.0f; + theme->delaycollision = 0.0f; +} + +// particle theme -> QC globals +void VM_CL_ParticleThemeToGlobals(vmparticletheme_t *theme) +{ + *vmpartspawner.particle_type = theme->typeindex; + *vmpartspawner.particle_blendmode = theme->blendmode; + *vmpartspawner.particle_orientation = theme->orientation; + vmpartspawner.particle_color1[0] = (theme->color1 >> 16) & 0xFF; // VorteX: int only can store 0-255, not 0-256 which means 0 - 0,99609375... + vmpartspawner.particle_color1[1] = (theme->color1 >> 8) & 0xFF; + vmpartspawner.particle_color1[2] = (theme->color1 >> 0) & 0xFF; + vmpartspawner.particle_color2[0] = (theme->color2 >> 16) & 0xFF; + vmpartspawner.particle_color2[1] = (theme->color2 >> 8) & 0xFF; + vmpartspawner.particle_color2[2] = (theme->color2 >> 0) & 0xFF; + *vmpartspawner.particle_tex = (float)theme->tex; + *vmpartspawner.particle_size = theme->size; + *vmpartspawner.particle_sizeincrease = theme->sizeincrease; + *vmpartspawner.particle_alpha = (float)theme->alpha/256; + *vmpartspawner.particle_alphafade = (float)theme->alphafade/256; + *vmpartspawner.particle_time = theme->lifetime; + *vmpartspawner.particle_gravity = theme->gravity; + *vmpartspawner.particle_bounce = theme->bounce; + *vmpartspawner.particle_airfriction = theme->airfriction; + *vmpartspawner.particle_liquidfriction = theme->liquidfriction; + *vmpartspawner.particle_originjitter = theme->originjitter; + *vmpartspawner.particle_velocityjitter = theme->velocityjitter; + *vmpartspawner.particle_qualityreduction = theme->qualityreduction; + *vmpartspawner.particle_stretch = theme->stretch; + vmpartspawner.particle_staincolor1[0] = (theme->staincolor1 >> 16) & 0xFF; + vmpartspawner.particle_staincolor1[1] = (theme->staincolor1 >> 8) & 0xFF; + vmpartspawner.particle_staincolor1[2] = (theme->staincolor1 >> 0) & 0xFF; + vmpartspawner.particle_staincolor2[0] = (theme->staincolor2 >> 16) & 0xFF; + vmpartspawner.particle_staincolor2[1] = (theme->staincolor2 >> 8) & 0xFF; + vmpartspawner.particle_staincolor2[2] = (theme->staincolor2 >> 0) & 0xFF; + *vmpartspawner.particle_staintex = (float)theme->staintex; + *vmpartspawner.particle_delayspawn = theme->delayspawn; + *vmpartspawner.particle_delaycollision = theme->delaycollision; +} + +// QC globals -> particle theme +void VM_CL_ParticleThemeFromGlobals(vmparticletheme_t *theme) +{ + theme->typeindex = (unsigned short)*vmpartspawner.particle_type; + theme->blendmode = (pblend_t)*vmpartspawner.particle_blendmode; + theme->orientation = (porientation_t)*vmpartspawner.particle_orientation; + theme->color1 = ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]); + theme->color2 = ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]); + theme->tex = (int)*vmpartspawner.particle_tex; + theme->size = *vmpartspawner.particle_size; + theme->sizeincrease = *vmpartspawner.particle_sizeincrease; + theme->alpha = (int)(*vmpartspawner.particle_alpha*256); + theme->alphafade = (int)(*vmpartspawner.particle_alphafade*256); + theme->lifetime = *vmpartspawner.particle_time; + theme->gravity = *vmpartspawner.particle_gravity; + theme->bounce = *vmpartspawner.particle_bounce; + theme->airfriction = *vmpartspawner.particle_airfriction; + theme->liquidfriction = *vmpartspawner.particle_liquidfriction; + theme->originjitter = *vmpartspawner.particle_originjitter; + theme->velocityjitter = *vmpartspawner.particle_velocityjitter; + theme->qualityreduction = (*vmpartspawner.particle_qualityreduction) ? true : false; + theme->stretch = *vmpartspawner.particle_stretch; + theme->staincolor1 = vmpartspawner.particle_staincolor1[0]*65536 + vmpartspawner.particle_staincolor1[1]*256 + vmpartspawner.particle_staincolor1[2]; + theme->staincolor2 = vmpartspawner.particle_staincolor2[0]*65536 + vmpartspawner.particle_staincolor2[1]*256 + vmpartspawner.particle_staincolor2[2]; + theme->staintex =(int)*vmpartspawner.particle_staintex; + theme->delayspawn = *vmpartspawner.particle_delayspawn; + theme->delaycollision = *vmpartspawner.particle_delaycollision; +} + +// init particle spawner interface +// # float(float max_themes) initparticlespawner +void VM_CL_InitParticleSpawner (void) +{ + VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_InitParticleSpawner); + VM_InitParticleSpawner((int)PRVM_G_FLOAT(OFS_PARM0)); + vmpartspawner.themes[0].initialized = true; + VM_ResetParticleTheme(&vmpartspawner.themes[0]); + PRVM_G_FLOAT(OFS_RETURN) = (vmpartspawner.verified == true) ? 1 : 0; +} + +// void() resetparticle +void VM_CL_ResetParticle (void) +{ + VM_SAFEPARMCOUNT(0, VM_CL_ResetParticle); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_ResetParticle: particle spawner not initialized\n"); + return; + } + VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]); +} + +// void(float themenum) particletheme +void VM_CL_ParticleTheme (void) +{ + int themenum; + + VM_SAFEPARMCOUNT(1, VM_CL_ParticleTheme); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_ParticleTheme: particle spawner not initialized\n"); + return; + } + themenum = (int)PRVM_G_FLOAT(OFS_PARM0); + if (themenum < 0 || themenum >= vmpartspawner.max_themes) + { + VM_Warning("VM_CL_ParticleTheme: bad theme number %i\n", themenum); + VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]); + return; + } + if (vmpartspawner.themes[themenum].initialized == false) + { + VM_Warning("VM_CL_ParticleTheme: theme #%i not exists\n", themenum); + VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]); + return; + } + // load particle theme into globals + VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[themenum]); +} + +// float() saveparticletheme +// void(float themenum) updateparticletheme +void VM_CL_ParticleThemeSave (void) +{ + int themenum; + + VM_SAFEPARMCOUNTRANGE(0, 1, VM_CL_ParticleThemeSave); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_ParticleThemeSave: particle spawner not initialized\n"); + return; + } + // allocate new theme, save it and return + if (prog->argc < 1) + { + for (themenum = 0; themenum < vmpartspawner.max_themes; themenum++) + if (vmpartspawner.themes[themenum].initialized == false) + break; + if (themenum >= vmpartspawner.max_themes) + { + if (vmpartspawner.max_themes == 2048) + VM_Warning("VM_CL_ParticleThemeSave: no free theme slots\n"); + else + VM_Warning("VM_CL_ParticleThemeSave: no free theme slots, try initparticlespawner() with highter max_themes\n"); + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; + } + vmpartspawner.themes[themenum].initialized = true; + VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]); + PRVM_G_FLOAT(OFS_RETURN) = themenum; + return; + } + // update existing theme + themenum = (int)PRVM_G_FLOAT(OFS_PARM0); + if (themenum < 0 || themenum >= vmpartspawner.max_themes) + { + VM_Warning("VM_CL_ParticleThemeSave: bad theme number %i\n", themenum); + return; + } + vmpartspawner.themes[themenum].initialized = true; + VM_CL_ParticleThemeFromGlobals(&vmpartspawner.themes[themenum]); +} + +// void(float themenum) freeparticletheme +void VM_CL_ParticleThemeFree (void) +{ + int themenum; + + VM_SAFEPARMCOUNT(1, VM_CL_ParticleThemeFree); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_ParticleThemeFree: particle spawner not initialized\n"); + return; + } + themenum = (int)PRVM_G_FLOAT(OFS_PARM0); + // check parms + if (themenum <= 0 || themenum >= vmpartspawner.max_themes) + { + VM_Warning("VM_CL_ParticleThemeFree: bad theme number %i\n", themenum); + return; + } + if (vmpartspawner.themes[themenum].initialized == false) + { + VM_Warning("VM_CL_ParticleThemeFree: theme #%i already freed\n", themenum); + VM_CL_ParticleThemeToGlobals(&vmpartspawner.themes[0]); + return; + } + // free theme + VM_ResetParticleTheme(&vmpartspawner.themes[themenum]); + vmpartspawner.themes[themenum].initialized = false; +} + +// float(vector org, vector dir, [float theme]) particle +// returns 0 if failed, 1 if succesful +void VM_CL_SpawnParticle (void) +{ + float *org, *dir; + vmparticletheme_t *theme; + particle_t *part; + int themenum; + + VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_SpawnParticle2); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n"); + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + org = PRVM_G_VECTOR(OFS_PARM0); + dir = PRVM_G_VECTOR(OFS_PARM1); + + if (prog->argc < 3) // global-set particle + { + part = CL_NewParticle((unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, (int)(*vmpartspawner.particle_alpha*256), (int)(*vmpartspawner.particle_alphafade*256), *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex); + if (!part) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + if (*vmpartspawner.particle_delayspawn) + part->delayedspawn = cl.time + *vmpartspawner.particle_delayspawn; + if (*vmpartspawner.particle_delaycollision) + part->delayedcollisions = cl.time + *vmpartspawner.particle_delaycollision; + } + else // quick themed particle + { + themenum = (int)PRVM_G_FLOAT(OFS_PARM2); + if (themenum <= 0 || themenum >= vmpartspawner.max_themes) + { + VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum); + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + theme = &vmpartspawner.themes[themenum]; + part = CL_NewParticle(theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex); + if (!part) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + if (theme->delayspawn) + part->delayedspawn = cl.time + theme->delayspawn; + if (theme->delaycollision) + part->delayedcollisions = cl.time + theme->delaycollision; + } + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +// float(vector org, vector dir, float spawndelay, float collisiondelay, [float theme]) delayedparticle +// returns 0 if failed, 1 if success +void VM_CL_SpawnParticleDelayed (void) +{ + float *org, *dir; + vmparticletheme_t *theme; + particle_t *part; + int themenum; + + VM_SAFEPARMCOUNTRANGE(4, 5, VM_CL_SpawnParticle2); + if (vmpartspawner.verified == false) + { + VM_Warning("VM_CL_SpawnParticle: particle spawner not initialized\n"); + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + org = PRVM_G_VECTOR(OFS_PARM0); + dir = PRVM_G_VECTOR(OFS_PARM1); + if (prog->argc < 5) // global-set particle + part = CL_NewParticle((unsigned short)*vmpartspawner.particle_type, ((int)vmpartspawner.particle_color1[0] << 16) + ((int)vmpartspawner.particle_color1[1] << 8) + ((int)vmpartspawner.particle_color1[2]), ((int)vmpartspawner.particle_color2[0] << 16) + ((int)vmpartspawner.particle_color2[1] << 8) + ((int)vmpartspawner.particle_color2[2]), (int)*vmpartspawner.particle_tex, *vmpartspawner.particle_size, *vmpartspawner.particle_sizeincrease, (int)(*vmpartspawner.particle_alpha*256), (int)(*vmpartspawner.particle_alphafade*256), *vmpartspawner.particle_gravity, *vmpartspawner.particle_bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], *vmpartspawner.particle_airfriction, *vmpartspawner.particle_liquidfriction, *vmpartspawner.particle_originjitter, *vmpartspawner.particle_velocityjitter, (*vmpartspawner.particle_qualityreduction) ? true : false, *vmpartspawner.particle_time, *vmpartspawner.particle_stretch, (pblend_t)*vmpartspawner.particle_blendmode, (porientation_t)*vmpartspawner.particle_orientation, ((int)vmpartspawner.particle_staincolor1[0] << 16) + ((int)vmpartspawner.particle_staincolor1[1] << 8) + ((int)vmpartspawner.particle_staincolor1[2]), ((int)vmpartspawner.particle_staincolor2[0] << 16) + ((int)vmpartspawner.particle_staincolor2[1] << 8) + ((int)vmpartspawner.particle_staincolor2[2]), (int)*vmpartspawner.particle_staintex); + else // themed particle + { + themenum = (int)PRVM_G_FLOAT(OFS_PARM4); + if (themenum <= 0 || themenum >= vmpartspawner.max_themes) + { + VM_Warning("VM_CL_SpawnParticle: bad theme number %i\n", themenum); + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + theme = &vmpartspawner.themes[themenum]; + part = CL_NewParticle(theme->typeindex, theme->color1, theme->color2, theme->tex, theme->size, theme->sizeincrease, theme->alpha, theme->alphafade, theme->gravity, theme->bounce, org[0], org[1], org[2], dir[0], dir[1], dir[2], theme->airfriction, theme->liquidfriction, theme->originjitter, theme->velocityjitter, theme->qualityreduction, theme->lifetime, theme->stretch, theme->blendmode, theme->orientation, theme->staincolor1, theme->staincolor2, theme->staintex); + } + if (!part) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + part->delayedspawn = cl.time + PRVM_G_FLOAT(OFS_PARM2); + part->delayedcollisions = cl.time + PRVM_G_FLOAT(OFS_PARM3); + PRVM_G_FLOAT(OFS_RETURN) = 0; +} + +// //==================== //QC POLYGON functions //==================== -typedef struct +#define VMPOLYGONS_MAXPOINTS 64 + +typedef struct vmpolygons_triangle_s { - rtexture_t *tex; - float data[36]; //[515]: enough for polygons - unsigned char flags; //[515]: + VM_POLYGON_2D and VM_POLYGON_FL4V flags -}vm_polygon_t; + rtexture_t *texture; + int drawflag; + unsigned short elements[3]; +}vmpolygons_triangle_t; typedef struct vmpolygons_s { - //static float vm_polygon_linewidth = 1; mempool_t *pool; - unsigned char current_vertices; qboolean initialized; - vm_polygon_t *polygons; - unsigned long polygons_num, drawpolygons_num; //[515]: ok long on 64bit ? - qboolean polygonbegin; //[515]: for "no-crap-on-the-screen" check + double progstarttime; + + int max_vertices; + int num_vertices; + float *data_vertex3f; + float *data_color4f; + float *data_texcoord2f; + + int max_triangles; + int num_triangles; + vmpolygons_triangle_t *data_triangles; + unsigned short *data_sortedelement3s; + + qboolean begin_active; + rtexture_t *begin_texture; + int begin_drawflag; + int begin_vertices; + float begin_vertex[VMPOLYGONS_MAXPOINTS][3]; + float begin_color[VMPOLYGONS_MAXPOINTS][4]; + float begin_texcoord[VMPOLYGONS_MAXPOINTS][2]; } vmpolygons_t; + +// FIXME: make VM_CL_R_Polygon functions use Debug_Polygon functions? vmpolygons_t vmpolygons[PRVM_MAXPROGS]; -#define VM_DEFPOLYNUM 64 //[515]: enough for default ? -#define VM_POLYGON_FL3V 16 //more than 2 vertices (used only for lines) -#define VM_POLYGON_FLLINES 32 -#define VM_POLYGON_FL2D 64 -#define VM_POLYGON_FL4V 128 //4 vertices +//#304 void() renderscene (EXT_CSQC) +// moved that here to reset the polygons, +// resetting them earlier causes R_Mesh_Draw to be called with numvertices = 0 +// --blub +void VM_CL_R_RenderScene (void) +{ + double t = Sys_DoubleTime(); + vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); + VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene); + + // we need to update any RENDER_VIEWMODEL entities at this point because + // csqc supplies its own view matrix + CL_UpdateViewEntities(); + // now draw stuff! + R_RenderView(); + + polys->num_vertices = polys->num_triangles = 0; + polys->progstarttime = prog->starttime; + + // callprofile fixing hack: do not include this time in what is counted for CSQC_UpdateView + prog->functions[prog->funcoffsets.CSQC_UpdateView].totaltime -= Sys_DoubleTime() - t; +} + +static void VM_ResizePolygons(vmpolygons_t *polys) +{ + float *oldvertex3f = polys->data_vertex3f; + float *oldcolor4f = polys->data_color4f; + float *oldtexcoord2f = polys->data_texcoord2f; + vmpolygons_triangle_t *oldtriangles = polys->data_triangles; + unsigned short *oldsortedelement3s = polys->data_sortedelement3s; + polys->max_vertices = min(polys->max_triangles*3, 65536); + polys->data_vertex3f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[3])); + polys->data_color4f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[4])); + polys->data_texcoord2f = (float *)Mem_Alloc(polys->pool, polys->max_vertices*sizeof(float[2])); + polys->data_triangles = (vmpolygons_triangle_t *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(vmpolygons_triangle_t)); + polys->data_sortedelement3s = (unsigned short *)Mem_Alloc(polys->pool, polys->max_triangles*sizeof(unsigned short[3])); + if (polys->num_vertices) + { + memcpy(polys->data_vertex3f, oldvertex3f, polys->num_vertices*sizeof(float[3])); + memcpy(polys->data_color4f, oldcolor4f, polys->num_vertices*sizeof(float[4])); + memcpy(polys->data_texcoord2f, oldtexcoord2f, polys->num_vertices*sizeof(float[2])); + } + if (polys->num_triangles) + { + memcpy(polys->data_triangles, oldtriangles, polys->num_triangles*sizeof(vmpolygons_triangle_t)); + memcpy(polys->data_sortedelement3s, oldsortedelement3s, polys->num_triangles*sizeof(unsigned short[3])); + } + if (oldvertex3f) + Mem_Free(oldvertex3f); + if (oldcolor4f) + Mem_Free(oldcolor4f); + if (oldtexcoord2f) + Mem_Free(oldtexcoord2f); + if (oldtriangles) + Mem_Free(oldtriangles); + if (oldsortedelement3s) + Mem_Free(oldsortedelement3s); +} static void VM_InitPolygons (vmpolygons_t* polys) { + memset(polys, 0, sizeof(*polys)); polys->pool = Mem_AllocPool("VMPOLY", 0, NULL); - polys->polygons = (vm_polygon_t *)Mem_Alloc(polys->pool, VM_DEFPOLYNUM*sizeof(vm_polygon_t)); - memset(polys->polygons, 0, VM_DEFPOLYNUM*sizeof(vm_polygon_t)); - polys->polygons_num = VM_DEFPOLYNUM; - polys->drawpolygons_num = 0; - polys->polygonbegin = false; + polys->max_triangles = 1024; + VM_ResizePolygons(polys); polys->initialized = true; } @@ -2295,216 +3038,204 @@ static void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t { int surfacelistindex; vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); + if(polys->progstarttime != prog->starttime) // from other progs? won't draw these (this can cause crashes!) + return; + R_Mesh_ResetTextureState(); + R_Mesh_Matrix(&identitymatrix); + GL_CullFace(GL_NONE); + R_Mesh_VertexPointer(polys->data_vertex3f, 0, 0); + R_Mesh_ColorPointer(polys->data_color4f, 0, 0); + R_Mesh_TexCoordPointer(0, 2, polys->data_texcoord2f, 0, 0); + R_SetupGenericShader(true); - // LordHavoc: FIXME: this is stupid code - for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++) + for (surfacelistindex = 0;surfacelistindex < numsurfaces;) { - const vm_polygon_t *p = &polys->polygons[surfacelist[surfacelistindex]]; - int flags = p->flags & 0x0f; - - if(flags == DRAWFLAG_ADDITIVE) + int numtriangles = 0; + rtexture_t *tex = polys->data_triangles[surfacelist[surfacelistindex]].texture; + int drawflag = polys->data_triangles[surfacelist[surfacelistindex]].drawflag; + // this can't call _DrawQ_ProcessDrawFlag, but should be in sync with it + // FIXME factor this out + if(drawflag == DRAWFLAG_ADDITIVE) GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - else if(flags == DRAWFLAG_MODULATE) + else if(drawflag == DRAWFLAG_MODULATE) GL_BlendFunc(GL_DST_COLOR, GL_ZERO); - else if(flags == DRAWFLAG_2XMODULATE) + else if(drawflag == DRAWFLAG_2XMODULATE) GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR); + else if(drawflag == DRAWFLAG_SCREEN) + GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE); else GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - R_Mesh_TexBind(0, R_GetTexture(p->tex)); - - CHECKGLERROR - //[515]: is speed is max ? - if(p->flags & VM_POLYGON_FLLINES) //[515]: lines - { - qglLineWidth(p->data[13]);CHECKGLERROR - qglBegin(GL_LINE_LOOP); - qglTexCoord1f (p->data[12]); - qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]); - qglVertex3f (p->data[0] , p->data[1], p->data[2]); - - qglTexCoord1f (p->data[14]); - qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]); - qglVertex3f (p->data[3] , p->data[4], p->data[5]); - - if(p->flags & VM_POLYGON_FL3V) - { - qglTexCoord1f (p->data[16]); - qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]); - qglVertex3f (p->data[6] , p->data[7], p->data[8]); - - if(p->flags & VM_POLYGON_FL4V) - { - qglTexCoord1f (p->data[18]); - qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]); - qglVertex3f (p->data[9] , p->data[10], p->data[11]); - } - } - qglEnd(); - CHECKGLERROR - } - else + R_Mesh_TexBind(0, R_GetTexture(tex)); + numtriangles = 0; + for (;surfacelistindex < numsurfaces;surfacelistindex++) { - qglBegin(GL_POLYGON); - qglTexCoord2f (p->data[12], p->data[13]); - qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]); - qglVertex3f (p->data[0] , p->data[1], p->data[2]); - - qglTexCoord2f (p->data[14], p->data[15]); - qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]); - qglVertex3f (p->data[3] , p->data[4], p->data[5]); - - qglTexCoord2f (p->data[16], p->data[17]); - qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]); - qglVertex3f (p->data[6] , p->data[7], p->data[8]); - - if(p->flags & VM_POLYGON_FL4V) - { - qglTexCoord2f (p->data[18], p->data[19]); - qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]); - qglVertex3f (p->data[9] , p->data[10], p->data[11]); - } - qglEnd(); - CHECKGLERROR + if (polys->data_triangles[surfacelist[surfacelistindex]].texture != tex || polys->data_triangles[surfacelist[surfacelistindex]].drawflag != drawflag) + break; + VectorCopy(polys->data_triangles[surfacelist[surfacelistindex]].elements, polys->data_sortedelement3s + 3*numtriangles); + numtriangles++; } + R_Mesh_Draw(0, polys->num_vertices, 0, numtriangles, NULL, polys->data_sortedelement3s, 0, 0); } } -static void VM_CL_AddPolygonTo2DScene (vm_polygon_t *p) +void VMPolygons_Store(vmpolygons_t *polys) { - drawqueuemesh_t mesh; - static int picelements[6] = {0, 1, 2, 0, 2, 3}; - mesh.texture = p->tex; - mesh.data_element3i = picelements; - mesh.data_vertex3f = p->data; - mesh.data_texcoord2f = p->data + 12; - mesh.data_color4f = p->data + 20; - if(p->flags & VM_POLYGON_FL4V) + if (r_refdef.draw2dstage) { - mesh.num_vertices = 4; - mesh.num_triangles = 2; + // draw the polygon as 2D immediately + drawqueuemesh_t mesh; + mesh.texture = polys->begin_texture; + mesh.num_vertices = polys->begin_vertices; + mesh.num_triangles = polys->begin_vertices-2; + mesh.data_element3i = polygonelement3i; + mesh.data_element3s = polygonelement3s; + mesh.data_vertex3f = polys->begin_vertex[0]; + mesh.data_color4f = polys->begin_color[0]; + mesh.data_texcoord2f = polys->begin_texcoord[0]; + DrawQ_Mesh(&mesh, polys->begin_drawflag); } else { - mesh.num_vertices = 3; - mesh.num_triangles = 1; + // queue the polygon as 3D for sorted transparent rendering later + int i; + if (polys->max_triangles < polys->num_triangles + polys->begin_vertices-2) + { + polys->max_triangles *= 2; + VM_ResizePolygons(polys); + } + if (polys->num_vertices + polys->begin_vertices <= polys->max_vertices) + { + // needle in a haystack! + // polys->num_vertices was used for copying where we actually want to copy begin_vertices + // that also caused it to not render the first polygon that is added + // --blub + memcpy(polys->data_vertex3f + polys->num_vertices * 3, polys->begin_vertex[0], polys->begin_vertices * sizeof(float[3])); + memcpy(polys->data_color4f + polys->num_vertices * 4, polys->begin_color[0], polys->begin_vertices * sizeof(float[4])); + memcpy(polys->data_texcoord2f + polys->num_vertices * 2, polys->begin_texcoord[0], polys->begin_vertices * sizeof(float[2])); + for (i = 0;i < polys->begin_vertices-2;i++) + { + polys->data_triangles[polys->num_triangles].texture = polys->begin_texture; + polys->data_triangles[polys->num_triangles].drawflag = polys->begin_drawflag; + polys->data_triangles[polys->num_triangles].elements[0] = polys->num_vertices; + polys->data_triangles[polys->num_triangles].elements[1] = polys->num_vertices + i+1; + polys->data_triangles[polys->num_triangles].elements[2] = polys->num_vertices + i+2; + polys->num_triangles++; + } + polys->num_vertices += polys->begin_vertices; + } } - if(p->flags & VM_POLYGON_FLLINES) //[515]: lines - DrawQ_LineLoop (&mesh, (p->flags&0x0f)); - else - DrawQ_Mesh (&mesh, (p->flags&0x0f)); + polys->begin_active = false; } // TODO: move this into the client code and clean-up everything else, too! [1/6/2008 Black] +// LordHavoc: agreed, this is a mess void VM_CL_AddPolygonsToMeshQueue (void) { int i; vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); + vec3_t center; // only add polygons of the currently active prog to the queue - if there is none, we're done if( !prog ) return; - if(!polys->drawpolygons_num) + if (!polys->num_triangles) return; - R_Mesh_Matrix(&identitymatrix); - GL_CullFace(GL_NONE); - for(i = 0;i < (int)polys->drawpolygons_num;i++) - VM_DrawPolygonCallback(NULL, NULL, 1, &i); - polys->drawpolygons_num = 0; + + for (i = 0;i < polys->num_triangles;i++) + { + VectorMAMAM(1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[0], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[1], 1.0f / 3.0f, polys->data_vertex3f + 3*polys->data_triangles[i].elements[2], center); + R_MeshQueue_AddTransparent(center, VM_DrawPolygonCallback, NULL, i, NULL); + } + + /*polys->num_triangles = 0; // now done after rendering the scene, + polys->num_vertices = 0; // otherwise it's not rendered at all and prints an error message --blub */ } -//void(string texturename, float flag[, float 2d[, float lines]]) R_BeginPolygon +//void(string texturename, float flag) R_BeginPolygon void VM_CL_R_PolygonBegin (void) { - vm_polygon_t *p; const char *picname; + skinframe_t *sf; vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); + int tf; + + // TODO instead of using skinframes here (which provides the benefit of + // better management of flags, and is more suited for 3D rendering), what + // about supporting Q3 shaders? - VM_SAFEPARMCOUNTRANGE(2, 4, VM_CL_R_PolygonBegin); + VM_SAFEPARMCOUNT(2, VM_CL_R_PolygonBegin); - if(!polys->initialized) + if (!polys->initialized) VM_InitPolygons(polys); - if(polys->polygonbegin) + if(polys->progstarttime != prog->starttime) { - VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonEnd after first\n"); - return; + // from another progs? then reset the polys first (fixes crashes on map change, because that can make skinframe textures invalid) + polys->num_vertices = polys->num_triangles = 0; + polys->progstarttime = prog->starttime; } - if(polys->drawpolygons_num >= polys->polygons_num) + if (polys->begin_active) { - p = (vm_polygon_t *)Mem_Alloc(polys->pool, 2 * polys->polygons_num * sizeof(vm_polygon_t)); - memset(p, 0, 2 * polys->polygons_num * sizeof(vm_polygon_t)); - memcpy(p, polys->polygons, polys->polygons_num * sizeof(vm_polygon_t)); - Mem_Free(polys->polygons); - polys->polygons = p; - polys->polygons_num *= 2; + VM_Warning("VM_CL_R_PolygonBegin: called twice without VM_CL_R_PolygonBegin after first\n"); + return; } - p = &polys->polygons[polys->drawpolygons_num]; picname = PRVM_G_STRING(OFS_PARM0); - if(picname[0]) - p->tex = Draw_CachePic(picname, true)->tex; - else - p->tex = r_texture_white; - p->flags = (unsigned char)PRVM_G_FLOAT(OFS_PARM1); - polys->current_vertices = 0; - polys->polygonbegin = true; - if(prog->argc >= 3) - { - if(PRVM_G_FLOAT(OFS_PARM2)) - p->flags |= VM_POLYGON_FL2D; - if(prog->argc >= 4 && PRVM_G_FLOAT(OFS_PARM3)) + + sf = NULL; + if(*picname) + { + tf = TEXF_ALPHA; + if((int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MIPMAP) + tf |= TEXF_MIPMAP; + + do { - p->data[13] = PRVM_G_FLOAT(OFS_PARM3); //[515]: linewidth - p->flags |= VM_POLYGON_FLLINES; + sf = R_SkinFrame_FindNextByName(sf, picname); } + while(sf && sf->textureflags != tf); + + if(!sf || !sf->base) + sf = R_SkinFrame_LoadExternal(picname, tf, true); + + if(sf) + R_SkinFrame_MarkUsed(sf); } + + polys->begin_texture = (sf && sf->base) ? sf->base : r_texture_white; + polys->begin_drawflag = (int)PRVM_G_FLOAT(OFS_PARM1) & DRAWFLAG_MASK; + polys->begin_vertices = 0; + polys->begin_active = true; } //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex void VM_CL_R_PolygonVertex (void) { - float *coords, *tx, *rgb, alpha; - vm_polygon_t *p; vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); VM_SAFEPARMCOUNT(4, VM_CL_R_PolygonVertex); - if(!polys->polygonbegin) + if (!polys->begin_active) { VM_Warning("VM_CL_R_PolygonVertex: VM_CL_R_PolygonBegin wasn't called\n"); return; } - coords = PRVM_G_VECTOR(OFS_PARM0); - tx = PRVM_G_VECTOR(OFS_PARM1); - rgb = PRVM_G_VECTOR(OFS_PARM2); - alpha = PRVM_G_FLOAT(OFS_PARM3); - p = &polys->polygons[polys->drawpolygons_num]; - if(polys->current_vertices > 4) + if (polys->begin_vertices >= VMPOLYGONS_MAXPOINTS) { - VM_Warning("VM_CL_R_PolygonVertex: may have 4 vertices max\n"); + VM_Warning("VM_CL_R_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS); return; } - p->data[polys->current_vertices*3] = coords[0]; - p->data[1+polys->current_vertices*3] = coords[1]; - p->data[2+polys->current_vertices*3] = coords[2]; - - p->data[12+polys->current_vertices*2] = tx[0]; - if(!(p->flags & VM_POLYGON_FLLINES)) - p->data[13+polys->current_vertices*2] = tx[1]; - - p->data[20+polys->current_vertices*4] = rgb[0]; - p->data[21+polys->current_vertices*4] = rgb[1]; - p->data[22+polys->current_vertices*4] = rgb[2]; - p->data[23+polys->current_vertices*4] = alpha; - - polys->current_vertices++; - if(polys->current_vertices == 4) - p->flags |= VM_POLYGON_FL4V; - else - if(polys->current_vertices == 3) - p->flags |= VM_POLYGON_FL3V; + polys->begin_vertex[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM0)[0]; + polys->begin_vertex[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM0)[1]; + polys->begin_vertex[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM0)[2]; + polys->begin_texcoord[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM1)[0]; + polys->begin_texcoord[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM1)[1]; + polys->begin_color[polys->begin_vertices][0] = PRVM_G_VECTOR(OFS_PARM2)[0]; + polys->begin_color[polys->begin_vertices][1] = PRVM_G_VECTOR(OFS_PARM2)[1]; + polys->begin_color[polys->begin_vertices][2] = PRVM_G_VECTOR(OFS_PARM2)[2]; + polys->begin_color[polys->begin_vertices][3] = PRVM_G_FLOAT(OFS_PARM3); + polys->begin_vertices++; } //void() R_EndPolygon @@ -2513,120 +3244,73 @@ void VM_CL_R_PolygonEnd (void) vmpolygons_t* polys = vmpolygons + PRVM_GetProgNr(); VM_SAFEPARMCOUNT(0, VM_CL_R_PolygonEnd); - if(!polys->polygonbegin) + if (!polys->begin_active) { VM_Warning("VM_CL_R_PolygonEnd: VM_CL_R_PolygonBegin wasn't called\n"); return; } - polys->polygonbegin = false; - if(polys->current_vertices > 2 || (polys->current_vertices >= 2 && polys->polygons[polys->drawpolygons_num].flags & VM_POLYGON_FLLINES)) - { - if(polys->polygons[polys->drawpolygons_num].flags & VM_POLYGON_FL2D) //[515]: don't use qcpolygons memory if 2D - VM_CL_AddPolygonTo2DScene(&polys->polygons[polys->drawpolygons_num]); - else - polys->drawpolygons_num++; - } + polys->begin_active = false; + if (polys->begin_vertices >= 3) + VMPolygons_Store(polys); else - VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->current_vertices); + VM_Warning("VM_CL_R_PolygonEnd: %i vertices isn't a good choice\n", polys->begin_vertices); } static vmpolygons_t debugPolys; -void Debug_PolygonBegin(const char *picname, int flags, qboolean draw2d, float linewidth) +void Debug_PolygonBegin(const char *picname, int drawflag) { - vm_polygon_t *p; - if(!debugPolys.initialized) VM_InitPolygons(&debugPolys); - if(debugPolys.polygonbegin) + if(debugPolys.begin_active) { Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n"); return; } - // limit polygons to a vaguely sane amount, beyond this each one just - // replaces the last one - debugPolys.drawpolygons_num = min(debugPolys.drawpolygons_num, (1<<20)-1); - if(debugPolys.drawpolygons_num >= debugPolys.polygons_num) - { - p = (vm_polygon_t *)Mem_Alloc(debugPolys.pool, 2 * debugPolys.polygons_num * sizeof(vm_polygon_t)); - memset(p, 0, 2 * debugPolys.polygons_num * sizeof(vm_polygon_t)); - memcpy(p, debugPolys.polygons, debugPolys.polygons_num * sizeof(vm_polygon_t)); - Mem_Free(debugPolys.polygons); - debugPolys.polygons = p; - debugPolys.polygons_num *= 2; - } - p = &debugPolys.polygons[debugPolys.drawpolygons_num]; - if(picname && picname[0]) - p->tex = Draw_CachePic(picname, true)->tex; - else - p->tex = r_texture_white; - p->flags = flags; - debugPolys.current_vertices = 0; - debugPolys.polygonbegin = true; - if(draw2d) - p->flags |= VM_POLYGON_FL2D; - if(linewidth) - { - p->data[13] = linewidth; //[515]: linewidth - p->flags |= VM_POLYGON_FLLINES; - } + debugPolys.begin_texture = picname[0] ? Draw_CachePic (picname)->tex : r_texture_white; + debugPolys.begin_drawflag = drawflag; + debugPolys.begin_vertices = 0; + debugPolys.begin_active = true; } void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a) { - vm_polygon_t *p; - - if(!debugPolys.polygonbegin) + if(!debugPolys.begin_active) { Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n"); return; } - p = &debugPolys.polygons[debugPolys.drawpolygons_num]; - if(debugPolys.current_vertices > 4) + if(debugPolys.begin_vertices > VMPOLYGONS_MAXPOINTS) { - Con_Printf("Debug_PolygonVertex: may have 4 vertices max\n"); + Con_Printf("Debug_PolygonVertex: may have %i vertices max\n", VMPOLYGONS_MAXPOINTS); return; } - p->data[debugPolys.current_vertices*3] = x; - p->data[1+debugPolys.current_vertices*3] = y; - p->data[2+debugPolys.current_vertices*3] = z; - - p->data[12+debugPolys.current_vertices*2] = s; - if(!(p->flags & VM_POLYGON_FLLINES)) - p->data[13+debugPolys.current_vertices*2] = t; - - p->data[20+debugPolys.current_vertices*4] = r; - p->data[21+debugPolys.current_vertices*4] = g; - p->data[22+debugPolys.current_vertices*4] = b; - p->data[23+debugPolys.current_vertices*4] = a; - - debugPolys.current_vertices++; - if(debugPolys.current_vertices == 4) - p->flags |= VM_POLYGON_FL4V; - else - if(debugPolys.current_vertices == 3) - p->flags |= VM_POLYGON_FL3V; + debugPolys.begin_vertex[debugPolys.begin_vertices][0] = x; + debugPolys.begin_vertex[debugPolys.begin_vertices][1] = y; + debugPolys.begin_vertex[debugPolys.begin_vertices][2] = z; + debugPolys.begin_texcoord[debugPolys.begin_vertices][0] = s; + debugPolys.begin_texcoord[debugPolys.begin_vertices][1] = t; + debugPolys.begin_color[debugPolys.begin_vertices][0] = r; + debugPolys.begin_color[debugPolys.begin_vertices][1] = g; + debugPolys.begin_color[debugPolys.begin_vertices][2] = b; + debugPolys.begin_color[debugPolys.begin_vertices][3] = a; + debugPolys.begin_vertices++; } void Debug_PolygonEnd(void) { - if(!debugPolys.polygonbegin) + if (!debugPolys.begin_active) { Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n"); return; } - debugPolys.polygonbegin = false; - if(debugPolys.current_vertices > 2 || (debugPolys.current_vertices >= 2 && debugPolys.polygons[debugPolys.drawpolygons_num].flags & VM_POLYGON_FLLINES)) - { - if(debugPolys.polygons[debugPolys.drawpolygons_num].flags & VM_POLYGON_FL2D) //[515]: don't use qcpolygons memory if 2D - VM_CL_AddPolygonTo2DScene(&debugPolys.polygons[debugPolys.drawpolygons_num]); - else - debugPolys.drawpolygons_num++; - } + debugPolys.begin_active = false; + if (debugPolys.begin_vertices >= 3) + VMPolygons_Store(&debugPolys); else - Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.current_vertices); + Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", debugPolys.begin_vertices); } /* @@ -2673,7 +3357,7 @@ realcheck: start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*sv_stepheight.value; - trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true); if (trace.fraction == 1.0) return false; @@ -2686,7 +3370,7 @@ realcheck: start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; - trace = CL_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceLine(start, stop, MOVE_NOMONSTERS, ent, CL_GenericHitSuperContentsMask(ent), true, false, NULL, true); if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; @@ -2711,7 +3395,7 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean float dz; vec3_t oldorg, neworg, end, traceendpos; trace_t trace; - int i; + int i, svent; prvm_edict_t *enemy; prvm_eval_t *val; @@ -2735,9 +3419,9 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean if (dz < 30) neworg[2] += 8; } - trace = CL_Move (ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, neworg, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true); if (settrace) - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); if (trace.fraction == 1) { @@ -2763,16 +3447,16 @@ qboolean CL_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean VectorCopy (neworg, end); end[2] -= sv_stepheight.value*2; - trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true); if (settrace) - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); if (trace.startsolid) { neworg[2] -= sv_stepheight.value; - trace = CL_Move (neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, NULL, true); + trace = CL_TraceBox(neworg, ent->fields.client->mins, ent->fields.client->maxs, end, MOVE_NORMAL, ent, CL_GenericHitSuperContentsMask(ent), true, true, &svent, true); if (settrace) - VM_SetTraceGlobals(&trace); + CL_VM_SetTraceGlobals(&trace, svent); if (trace.startsolid) return false; } @@ -2891,6 +3575,75 @@ void VM_CL_serverkey(void) PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string); } +/* +================= +VM_CL_checkpvs + +Checks if an entity is in a point's PVS. +Should be fast but can be inexact. + +float checkpvs(vector viewpos, entity viewee) = #240; +================= +*/ +static void VM_CL_checkpvs (void) +{ + vec3_t viewpos; + prvm_edict_t *viewee; + vec3_t mi, ma; +#if 1 + unsigned char *pvs; +#else + static int fatpvsbytes; + static unsigned char fatpvs[MAX_MAP_LEAFS/8]; +#endif + + VM_SAFEPARMCOUNT(2, VM_SV_checkpvs); + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos); + viewee = PRVM_G_EDICT(OFS_PARM1); + + if(viewee->priv.required->free) + { + VM_Warning("checkpvs: can not check free entity\n"); + PRVM_G_FLOAT(OFS_RETURN) = 4; + return; + } + + VectorAdd(viewee->fields.server->origin, viewee->fields.server->mins, mi); + VectorAdd(viewee->fields.server->origin, viewee->fields.server->maxs, ma); + +#if 1 + if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS) + { + // no PVS support on this worldmodel... darn + PRVM_G_FLOAT(OFS_RETURN) = 3; + return; + } + pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos); + if(!pvs) + { + // viewpos isn't in any PVS... darn + PRVM_G_FLOAT(OFS_RETURN) = 2; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma); +#else + // using fat PVS like FTEQW does (slow) + if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS) + { + // no PVS support on this worldmodel... darn + PRVM_G_FLOAT(OFS_RETURN) = 3; + return; + } + fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false); + if(!fatpvsbytes) + { + // viewpos isn't in any PVS... darn + PRVM_G_FLOAT(OFS_RETURN) = 2; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma); +#endif +} //============================================================================ // To create a almost working builtin file from this replace: @@ -2917,7 +3670,7 @@ VM_vlen, // #12 float(vector v) vlen (QUAKE) VM_vectoyaw, // #13 float(vector v) vectoyaw (QUAKE) VM_CL_spawn, // #14 entity() spawn (QUAKE) VM_remove, // #15 void(entity e) remove (QUAKE) -VM_CL_traceline, // #16 float(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE) +VM_CL_traceline, // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE) NULL, // #17 entity() checkclient (QUAKE) VM_find, // #18 entity(entity start, .string fld, string match) find (QUAKE) VM_precache_sound, // #19 void(string s) precache_sound (QUAKE) @@ -3143,7 +3896,7 @@ NULL, // #236 NULL, // #237 NULL, // #238 NULL, // #239 -NULL, // #240 +VM_CL_checkpvs, // #240 NULL, // #241 NULL, // #242 NULL, // #243 @@ -3231,9 +3984,9 @@ VM_drawfill, // #323 float(vector position, vector size, vector rgb, float a VM_drawsetcliparea, // #324 void(float x, float y, float width, float height) drawsetcliparea VM_drawresetcliparea, // #325 void(void) drawresetcliparea VM_drawcolorcodedstring, // #326 float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) (EXT_CSQC) -NULL, // #327 // FIXME add stringwidth() here? -NULL, // #328 // FIXME add drawsubpic() here? -NULL, // #329 +VM_stringwidth, // #327 // FIXME is this okay? +VM_drawsubpic, // #328 // FIXME is this okay? +VM_drawrotpic, // #329 // FIXME is this okay? VM_CL_getstatf, // #330 float(float stnum) getstatf (EXT_CSQC) VM_CL_getstati, // #331 float(float stnum) getstati (EXT_CSQC) VM_CL_getstats, // #332 string(float firststnum) getstats (EXT_CSQC) @@ -3248,7 +4001,7 @@ VM_keynumtostring, // #340 string(float keynum) keynumtostring (EXT_CSQC) VM_stringtokeynum, // #341 float(string keyname) stringtokeynum (EXT_CSQC) VM_CL_getkeybind, // #342 string(float keynum) getkeybind (EXT_CSQC) VM_CL_setcursormode, // #343 void(float usecursor) setcursormode (EXT_CSQC) -VM_getmousepos, // #344 vector() getmousepos (EXT_CSQC) +VM_CL_getmousepos, // #344 vector() getmousepos (EXT_CSQC) VM_CL_getinputstate, // #345 float(float framenum) getinputstate (EXT_CSQC) VM_CL_setsensitivityscale, // #346 void(float sens) setsensitivityscale (EXT_CSQC) VM_CL_runplayerphysics, // #347 void() runstandardplayerphysics (EXT_CSQC) @@ -3392,7 +4145,6 @@ VM_CL_pointsound, // #483 void(vector origin, string sample, float volume, fl VM_strreplace, // #484 string(string search, string replace, string subject) strreplace (DP_QC_STRREPLACE) VM_strireplace, // #485 string(string search, string replace, string subject) strireplace (DP_QC_STRREPLACE) VM_CL_getsurfacepointattribute,// #486 vector(entity e, float s, float n, float a) getsurfacepointattribute -#ifdef SUPPORT_GECKO VM_gecko_create, // #487 float gecko_create( string name ) VM_gecko_destroy, // #488 void gecko_destroy( string name ) VM_gecko_navigate, // #489 void gecko_navigate( string name, string URI ) @@ -3400,21 +4152,138 @@ VM_gecko_keyevent, // #490 float gecko_keyevent( string name, float key, floa VM_gecko_movemouse, // #491 void gecko_mousemove( string name, float x, float y ) VM_gecko_resize, // #492 void gecko_resize( string name, float w, float h ) VM_gecko_get_texture_extent, // #493 vector gecko_get_texture_extent( string name ) -#else -NULL, // #487 -NULL, // #488 -NULL, // #489 -NULL, // #490 -NULL, // #491 -NULL, // #492 -NULL, // #493 -#endif -NULL, // #494 -NULL, // #495 -NULL, // #496 -NULL, // #497 -NULL, // #498 -NULL, // #499 +VM_crc16, // #494 float(float caseinsensitive, string s, ...) crc16 = #494 (DP_QC_CRC16) +VM_cvar_type, // #495 float(string name) cvar_type = #495; (DP_QC_CVAR_TYPE) +VM_numentityfields, // #496 float() numentityfields = #496; (QP_QC_ENTITYDATA) +VM_entityfieldname, // #497 string(float fieldnum) entityfieldname = #497; (DP_QC_ENTITYDATA) +VM_entityfieldtype, // #498 float(float fieldnum) entityfieldtype = #498; (DP_QC_ENTITYDATA) +VM_getentityfieldstring, // #499 string(float fieldnum, entity ent) getentityfieldstring = #499; (DP_QC_ENTITYDATA) +VM_putentityfieldstring, // #500 float(float fieldnum, entity ent, string s) putentityfieldstring = #500; (DP_QC_ENTITYDATA) +VM_CL_ReadPicture, // #501 string() ReadPicture = #501; +NULL, // #502 +VM_whichpack, // #503 string(string) whichpack = #503; +NULL, // #504 +NULL, // #505 +NULL, // #506 +NULL, // #507 +NULL, // #508 +NULL, // #509 +VM_uri_escape, // #510 string(string in) uri_escape = #510; +VM_uri_unescape, // #511 string(string in) uri_unescape = #511; +VM_etof, // #512 float(entity ent) num_for_edict = #512 (DP_QC_NUM_FOR_EDICT) +VM_uri_get, // #513 float(string uril, float id) uri_get = #512; (DP_QC_URI_GET) +VM_tokenize_console, // #514 float(string str) tokenize_console = #514; (DP_QC_TOKENIZE_CONSOLE) +VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_QC_TOKENIZE_CONSOLE) +VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE) +VM_buf_cvarlist, // #517 void(float buf, string prefix, string antiprefix) buf_cvarlist = #517; (DP_QC_STRINGBUFFERS_CVARLIST) +VM_cvar_description, // #518 float(string name) cvar_description = #518; (DP_QC_CVAR_DESCRIPTION) +VM_gettime, // #519 float(float timer) gettime = #519; (DP_QC_GETTIME) +VM_keynumtostring, // #520 string keynumtostring(float keynum) +VM_findkeysforcommand, // #521 string findkeysforcommand(string command) +VM_CL_InitParticleSpawner, // #522 void(float max_themes) initparticlespawner (DP_CSQC_SPAWNPARTICLE) +VM_CL_ResetParticle, // #523 void() resetparticle (DP_CSQC_SPAWNPARTICLE) +VM_CL_ParticleTheme, // #524 void(float theme) particletheme (DP_CSQC_SPAWNPARTICLE) +VM_CL_ParticleThemeSave, // #525 void() particlethemesave, void(float theme) particlethemeupdate (DP_CSQC_SPAWNPARTICLE) +VM_CL_ParticleThemeFree, // #526 void() particlethemefree (DP_CSQC_SPAWNPARTICLE) +VM_CL_SpawnParticle, // #527 float(vector org, vector vel, [float theme]) particle (DP_CSQC_SPAWNPARTICLE) +VM_CL_SpawnParticleDelayed, // #528 float(vector org, vector vel, float delay, float collisiondelay, [float theme]) delayedparticle (DP_CSQC_SPAWNPARTICLE) +VM_loadfromdata, // #529 +VM_loadfromfile, // #530 +NULL, // #531 +NULL, // #532 +NULL, // #533 +NULL, // #534 +NULL, // #535 +NULL, // #536 +NULL, // #537 +NULL, // #538 +NULL, // #539 +NULL, // #540 +NULL, // #541 +NULL, // #542 +NULL, // #543 +NULL, // #544 +NULL, // #545 +NULL, // #546 +NULL, // #547 +NULL, // #548 +NULL, // #549 +NULL, // #550 +NULL, // #551 +NULL, // #552 +NULL, // #553 +NULL, // #554 +NULL, // #555 +NULL, // #556 +NULL, // #557 +NULL, // #558 +NULL, // #559 +NULL, // #560 +NULL, // #561 +NULL, // #562 +NULL, // #563 +NULL, // #564 +NULL, // #565 +NULL, // #566 +NULL, // #567 +NULL, // #568 +NULL, // #569 +NULL, // #570 +NULL, // #571 +NULL, // #572 +NULL, // #573 +NULL, // #574 +NULL, // #575 +NULL, // #576 +NULL, // #577 +NULL, // #578 +NULL, // #579 +NULL, // #580 +NULL, // #581 +NULL, // #582 +NULL, // #583 +NULL, // #584 +NULL, // #585 +NULL, // #586 +NULL, // #587 +NULL, // #588 +NULL, // #589 +NULL, // #590 +NULL, // #591 +NULL, // #592 +NULL, // #593 +NULL, // #594 +NULL, // #595 +NULL, // #596 +NULL, // #597 +NULL, // #598 +NULL, // #599 +NULL, // #600 +NULL, // #601 +NULL, // #602 +NULL, // #603 +NULL, // #604 +VM_callfunction, // #605 +VM_writetofile, // #606 +VM_isfunction, // #607 +NULL, // #608 +NULL, // #609 +NULL, // #610 +NULL, // #611 +NULL, // #612 +VM_parseentitydata, // #613 +NULL, // #614 +NULL, // #615 +NULL, // #616 +NULL, // #617 +NULL, // #618 +NULL, // #619 +NULL, // #620 +NULL, // #621 +NULL, // #622 +NULL, // #623 +VM_getextresponse, // #624 string getextresponse(void) +NULL, // #625 }; const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t); @@ -3439,6 +4308,7 @@ void VM_CL_Cmd_Init(void) void VM_CL_Cmd_Reset(void) { + World_End(&cl.world); VM_Cmd_Reset(); VM_Polygons_Reset(); }