diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 8e743fdd2..cb91ca7fb 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -4748,7 +4748,7 @@ double Host_Frame (double time) realtime = newrealtime; if (oldrealtime > realtime) - oldrealtime = 0; + oldrealtime = realtime; if (cl.gamespeed<0.1) cl.gamespeed = 1; @@ -4831,7 +4831,10 @@ double Host_Frame (double time) #ifndef CLIENTONLY if (sv.state && cls.state != ca_active) + { + maxfpsignoreserver = false; maxfps = 0; + } else #endif if ((cl_netfps.value>0 || cls.demoplayback || cl_threadedphysics.ival)) diff --git a/engine/client/m_script.c b/engine/client/m_script.c index b2a5c12fe..749a7e1a4 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -13,6 +13,7 @@ void M_Script_Option (menu_t *menu, char *optionvalue) menuoption_t *mo; char buf[8192]; + int level; Cbuf_AddText("wait\n", RESTRICT_LOCAL); @@ -32,7 +33,8 @@ void M_Script_Option (menu_t *menu, char *optionvalue) } } Cmd_TokenizeString(buf, false, false); - Cmd_ExpandString(menu->data, buf, sizeof(buf), RESTRICT_SERVER, true, true); + level = RESTRICT_SERVER; + Cmd_ExpandString(menu->data, buf, sizeof(buf), &level, true, true); //and execute it as-is Cbuf_AddText(buf, RESTRICT_LOCAL); diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 7fb7cedd1..d6f6e58d7 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -463,11 +463,15 @@ typedef struct { char *ext[64]; int numext; + int dragscroll; + int mousedownpos; + demoitem_t *items; } demomenu_t; static void M_DemoDraw(int x, int y, menucustom_t *control, menu_t *menu) { + extern qboolean keydown[K_MAX]; char *text; demomenu_t *info = menu->data; demoitem_t *item, *lostit; @@ -499,20 +503,50 @@ static void M_DemoDraw(int x, int y, menucustom_t *control, menu_t *menu) if (!item) info->firstitem = info->items; + if (!info->dragscroll && keydown[K_MOUSE1]) + { + info->dragscroll = 1; + info->mousedownpos = mousecursor_y; + } + if (info->dragscroll && keydown[K_MOUSE1]) + { + if (info->mousedownpos >= mousecursor_y+8) + { + info->dragscroll = 2; + info->mousedownpos -= 8; + if (info->firstitem->next) + { + if (info->firstitem == info->selected) + info->selected = info->firstitem->next; + info->firstitem = info->firstitem->next; + } + } + if (info->mousedownpos+8 <= mousecursor_y) + { + info->dragscroll = 2; + info->mousedownpos += 8; + if (info->firstitem->prev) + { + if (ty <= 24) + info->selected = info->selected->prev; + info->firstitem = info->firstitem->prev; + } + } + } item = info->firstitem; while(item) { - if (y+8 >= vid.height) + if (y >= vid.height) return; if (!item->isdir) text = va("%-32.32s%6iKB", item->name+info->pathlen, item->size/1024); else text = item->name+info->pathlen; if (item == info->selected) - Draw_AltFunString(x, y+8, text); + Draw_AltFunString(x, y, text); else - Draw_FunString(x, y+8, text); + Draw_FunString(x, y, text); y+=8; item = item->next; } @@ -521,6 +555,7 @@ static void ShowDemoMenu (menu_t *menu, const char *path); static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned int unicode) { demomenu_t *info = menu->data; + demoitem_t *it; int i; switch (key) @@ -557,6 +592,26 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned info->selected = info->selected->next; } break; + case K_MOUSE1: + if (info->dragscroll == 2) + { + info->dragscroll = 0; + break; + } + it = info->firstitem; + i = (mousecursor_y - control->common.posy) / 8; + while(i > 0 && it && it->next) + { + it = it->next; + i--; + } + if (info->selected != it) + { + info->selected = it; + info->dragscroll = 0; + break; + } + //fallthrough case K_ENTER: case K_KP_ENTER: if (info->selected) @@ -574,6 +629,9 @@ static qboolean M_DemoKey(menucustom_t *control, menu_t *menu, int key, unsigned if (extnum == info->numext) //wasn't on our list of extensions. extnum = 0; + if (!info->command[extnum]) + return true; //FIXME: archives + Cbuf_AddText(va("%s \"%s%s\"\n", info->command[extnum], (info->fs->fsroot==FS_SYSTEM)?"#":"", info->selected->name), RESTRICT_LOCAL); if (!shift_down) M_RemoveMenu(menu); diff --git a/engine/client/render.h b/engine/client/render.h index 38ef843f0..3f8cd49d1 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -470,7 +470,7 @@ struct llightinfo_s; void LightFace (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, int surfnum); //version that is aware of bsp trees void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *threadctx, qbyte surf_styles[4], qbyte *surf_rgbsamples, qbyte *surf_deluxesamples, vec4_t surf_plane, vec4_t surf_texplanes[2], vec2_t exactmins, vec2_t exactmaxs, int texmins[2], int texsize[2], float lmscale); //special version that doesn't know what a face is or anything. struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, struct model_s *model, qboolean shadows); -void LightReloadEntities(struct relight_ctx_s *ctx, char *entstring); +void LightReloadEntities(struct relight_ctx_s *ctx, char *entstring, qboolean ignorestyles); void LightShutdown(struct relight_ctx_s *ctx, struct model_s *mod); extern const size_t lightthreadctxsize; #endif diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index bce2ee798..adca763f3 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -73,22 +73,18 @@ void Sys_Printf (char *fmt, ...) unsigned int Sys_Milliseconds(void) { static int first = true; - static unsigned long oldtime = 0, curtime = 0; - unsigned long newtime; + static unsigned long starttime = 0; + unsigned long now; - newtime = SDL_GetTicks(); + now = SDL_GetTicks(); if (first) { first = false; - oldtime = newtime; + starttime = now; } - if (newtime < oldtime) - Con_Printf("Sys_Milliseconds stepped backwards!\n"); - else - curtime += newtime - oldtime; - oldtime = newtime; - return curtime; + + return now - starttime; } //return the current time, in the form of a double diff --git a/engine/client/zqtp.c b/engine/client/zqtp.c index fe6d4d97d..88faccb76 100644 --- a/engine/client/zqtp.c +++ b/engine/client/zqtp.c @@ -1639,6 +1639,7 @@ char *TP_LocationName (vec3_t location) vec3_t vec; static qbool recursive; static char buf[MAX_LOC_NAME]; + int level; if (!loc_numentries || (cls.state != ca_active)) return tp_name_someplace.string; @@ -1668,7 +1669,8 @@ char *TP_LocationName (vec3_t location) } recursive = true; - Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), Cmd_ExecLevel, true, false); + level = Cmd_ExecLevel; + Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), &level, true, false); recursive = false; return buf; @@ -3376,12 +3378,14 @@ void TP_UpdateAutoStatus(void) { char newstatusbuf[sizeof(vars.autoteamstatus)]; char *newstatus; + int level; if (vars.autoteamstatus_time > realtime || !*tp_autostatus.string) return; vars.autoteamstatus_time = realtime + 3; - newstatus = Cmd_ExpandString(tp_autostatus.string, newstatusbuf, sizeof(newstatusbuf), tp_autostatus.restriction, true, true); + level = tp_autostatus.restriction; + newstatus = Cmd_ExpandString(tp_autostatus.string, newstatusbuf, sizeof(newstatusbuf), &level, true, true); newstatus = TP_ParseMacroString(newstatus); if (!strcmp(newstatus, vars.autoteamstatus)) @@ -3737,7 +3741,8 @@ void CL_Say (qboolean team, char *extra) !strchr(s, '\x0d') /* explicit $\ in message overrides cl_fakename */) { char buf[1024]; - Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), Cmd_ExecLevel, true, true); + int level = Cmd_ExecLevel; + Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), &level, true, true); strcpy (buf, TP_ParseMacroString (buf)); Q_snprintfz (sendtext, sizeof(sendtext), "\x0d%s: ", TP_ParseFunChars(buf)); } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 2c947aa64..c0e8501e7 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -96,9 +96,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define AVAIL_DINPUT #define AVAIL_DDRAW #define AVAIL_DSOUND -// #define AVAIL_XAUDIO2 #define AVAIL_D3D #endif +#ifdef WINRT + #define AVAIL_XAUDIO2 +#endif #define AVAIL_XZDEC #if !defined(MINIMAL) && !defined(NPFTE) && !defined(NPQTV) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 96981596f..b461ff94e 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -98,7 +98,7 @@ void Cmd_AddMacro(char *s, char *(*f)(void), int disputableintentions) macro_count++; } -char *TP_MacroString (char *s, int *len) +char *TP_MacroString (char *s, int *newaccesslevel, int *len) { int i; macro_command_t *macro; @@ -110,7 +110,7 @@ char *TP_MacroString (char *s, int *len) { if (macro->disputableintentions) if (!tp_disputablemacros.ival) - continue; + *newaccesslevel = 0; if (len) *len = strlen(macro->name); return macro->func(); @@ -1244,7 +1244,7 @@ void Cmd_ShiftArgs (int ammount, qboolean expandstring) } } -char *Cmd_ExpandCvar(char *cvarname, int maxaccesslevel, int *len) +char *Cmd_ExpandCvar(char *cvarname, int maxaccesslevel, int *newaccesslevel, int *len) { char *ret = NULL, *end, *namestart; char *fixup = NULL, fixval=0; @@ -1291,6 +1291,9 @@ char *Cmd_ExpandCvar(char *cvarname, int maxaccesslevel, int *len) if (var->restriction <= maxaccesslevel && !((var->flags & CVAR_NOUNSAFEEXPAND) && Cmd_IsInsecure())) { ret = var->string; + + if (var->flags & CVAR_TEAMPLAYTAINT) //if we're only allowed to expand this for teamplay, then switch access levels + *newaccesslevel = 0; } } *fixup = fixval; @@ -1312,7 +1315,7 @@ If not SERVERONLY, also expands $macro expressions Note: dest must point to a 1024 byte buffer ================ */ -char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandcvars, qboolean expandmacros) +char *Cmd_ExpandString (char *data, char *dest, int destlen, int *accesslevel, qboolean expandcvars, qboolean expandmacros) { unsigned int c; char buf[255]; @@ -1322,6 +1325,7 @@ char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, char *bestvar; int name_length, var_length; qboolean striptrailing; + int maxaccesslevel = *accesslevel; len = 0; @@ -1349,9 +1353,9 @@ char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, data++; buf[i++] = c; buf[i] = 0; - if (expandcvars && (str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, &var_length))) + if (expandcvars && (str = Cmd_ExpandCvar(buf+striptrailing, maxaccesslevel, accesslevel, &var_length))) bestvar = str; - if (expandmacros && (str = TP_MacroString (buf+striptrailing, &var_length))) + if (expandmacros && (str = TP_MacroString (buf+striptrailing, accesslevel, &var_length))) bestvar = str; } @@ -2122,7 +2126,7 @@ void Cmd_ExecuteString (char *text, int level) char dest[8192]; - text = Cmd_ExpandString(text, dest, sizeof(dest), level, !Cmd_IsInsecure()?true:false, true); + text = Cmd_ExpandString(text, dest, sizeof(dest), &level, !Cmd_IsInsecure()?true:false, true); Cmd_TokenizeString (text, level == RESTRICT_LOCAL?true:false, false); // execute the command line @@ -2137,6 +2141,9 @@ void Cmd_ExecuteString (char *text, int level) if (strcmp (cmd_argv[0],cmd->name)) break; //yes, I know we found it... (but it's the wrong case, go for an alias or cvar instead FIRST) + if (!level) + break; + if ((cmd->restriction?cmd->restriction:rcon_level.ival) > level) Con_TPrintf("cmd '%s' was restricted.\n", cmd_argv[0]); else if (!cmd->function) @@ -2186,15 +2193,20 @@ void Cmd_ExecuteString (char *text, int level) return; #endif - if ((a->restriction?a->restriction:rcon_level.ival) > level) - { - Con_TPrintf("alias '%s' was restricted.\n", cmd_argv[0]); - return; - } - if (a->execlevel) - execlevel = a->execlevel; - else + if (!level) execlevel = level; + else + { + if ((a->restriction?a->restriction:rcon_level.ival) > level) + { + Con_TPrintf("alias '%s' was restricted.\n", cmd_argv[0]); + return; + } + if (a->execlevel) + execlevel = a->execlevel; + else + execlevel = level; + } Cbuf_InsertText ("\n", execlevel, false); @@ -2220,7 +2232,35 @@ void Cmd_ExecuteString (char *text, int level) if (Cvar_Command (level)) return; - + if (!level) + { + char *tpcmds[] = + { + "if", "wait", /*would be nice to include alias in here*/ + "say", "say_team", "echo", /*display stuff, because it would be useless otherwise*/ + "set_tp", "set", "set_calc", "inc", /*because scripting variables is fun. not.*/ + "tp_point", "tp_pickup", "tp_took" /*updates what the $took etc macros are allowed to generate*/ + }; + if (cmd) + { + for (level = 0; level < countof(tpcmds); level++) + { + if (!strcmp(cmd_argv[0], tpcmds[level])) + { + int olev = Cmd_ExecLevel; + Cmd_ExecLevel = 0; + if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function(); + Cmd_ExecLevel = olev; + return; + } + } + } + Con_TPrintf("'%s' is not permitted in combination with teamplay macros.\n", cmd_argv[0]); + return; + } if (cmd) //go for skipped ones { @@ -2843,7 +2883,7 @@ elseif: } skipws: - while(*end <= ' ' && *end) //skip leading whitespace. + while(*end == ' ' || *end == '\t') //skip leading whitespace. end++; for (ws = end + strlen(end)-1; ws >= end && *ws <= ' '; ws--) //skip trailing @@ -2855,9 +2895,12 @@ skipws: goto skipws; } + while (*end == ' ' || *end == '\t') + end++; + if (!*end) { - if (ret && *ret) //equation was true. + if (is_true(ret)) //equation was true. { trueblock = true; Cbuf_ExecBlock(level); @@ -3028,7 +3071,7 @@ void Cmd_set_f(void) forceflags = 0; } - var = Cvar_Get (name, text, 0, "Custom variables"); + var = Cvar_Get (name, text, CVAR_TEAMPLAYTAINT, "Custom variables"); mark = If_Token_GetMark(); @@ -3061,6 +3104,9 @@ void Cmd_set_f(void) Cvar_Set(var, text); var->flags |= CVAR_USERCREATED | forceflags; + if (Cmd_ExecLevel == RESTRICT_TEAMPLAY) + var->flags |= CVAR_TEAMPLAYTAINT; + if (!strncmp(Cmd_Argv(0), "seta", 4)) var->flags |= CVAR_ARCHIVE; } @@ -3114,7 +3160,13 @@ void Cvar_Inc_f (void) delta = (c == 3) ? atof (Cmd_Argv(2)) : 1; - Cvar_SetValue (var, var->value + delta); + if (Cmd_ExecLevel == RESTRICT_TEAMPLAY || (var->flags & CVAR_TEAMPLAYTAINT)) + { + Cvar_SetValue (var, var->value + delta); + var->flags |= CVAR_TEAMPLAYTAINT; + } + else + Cvar_SetValue (var, var->value + delta); } void Cmd_WriteConfig_f(void) diff --git a/engine/common/cmd.h b/engine/common/cmd.h index d6957bed5..282392b81 100644 --- a/engine/common/cmd.h +++ b/engine/common/cmd.h @@ -133,9 +133,10 @@ void Cmd_ExecuteString (char *text, int restrictionlevel); void Cmd_Args_Set(const char *newargs); #define RESTRICT_MAX_TOTAL 31 -#define RESTRICT_MAX_USER 29 //1-64 it's all about bit size. This is max settable. servers are +1 or +2 -#define RESTRICT_DEFAULT 20 //rcon get's 63, local always gets 64 -#define RESTRICT_MIN 1 //rcon get's 63, local always gets 64 +#define RESTRICT_MAX_USER 29 +#define RESTRICT_DEFAULT 20 +#define RESTRICT_MIN 1 +#define RESTRICT_TEAMPLAY 0 #define RESTRICT_MAX RESTRICT_MAX_USER @@ -161,7 +162,7 @@ void Cmd_MessageTrigger (char *message, int type); void Cmd_ShiftArgs (int ammount, qboolean expandstring); -char *Cmd_ExpandString (char *data, char *dest, int destlen, int maxaccesslevel, qboolean expandcvars, qboolean expandmacros); +char *Cmd_ExpandString (char *data, char *dest, int destlen, int *accesslevel, qboolean expandcvars, qboolean expandmacros); qboolean If_EvaluateBoolean(const char *text, int restriction); extern cvar_t rcon_level; diff --git a/engine/common/cvar.c b/engine/common/cvar.c index f90db4c14..bab4d6f34 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -812,6 +812,8 @@ cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force) var->value = Q_atof (var->string); var->ival = Q_atoi (var->string); + var->flags &= ~CVAR_TEAMPLAYTAINT; + { char *str = COM_Parse(var->string); var->vec4[0] = atof(com_token); @@ -1016,9 +1018,9 @@ void Cvar_SetValue (cvar_t *var, float value) { char val[32]; - if (value == (int)value) - sprintf (val, "%i",(int)value); //make it look nicer. - else +// if (value == (int)value) +// sprintf (val, "%i",(int)value); //make it look nicer. +// else sprintf (val, "%g",value); Cvar_Set (var, val); } @@ -1238,7 +1240,7 @@ qboolean Cvar_Command (int level) if (!v) return false; - if ((v->restriction?v->restriction:rcon_level.ival) > level) + if (!level || (v->restriction?v->restriction:rcon_level.ival) > level) { Con_Printf ("You do not have the priveledges for %s\n", v->name); return true; diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 9d5e98e42..fd8d4398e 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -141,6 +141,7 @@ typedef struct cvar_group_s #define CVAR_CONFIGDEFAULT (1<<18) //this cvar's default value has been changed to match a config. #define CVAR_NOSAVE (1<<19) //this cvar should never be saved. ever. #define CVAR_NORESET (1<<20) //cvar is not reset by various things. +#define CVAR_TEAMPLAYTAINT (1<<21) //current value contains the evaluation of a teamplay macro. #define CVAR_LASTFLAG CVAR_SHADERSYSTEM diff --git a/engine/common/q1bsp.c b/engine/common/q1bsp.c index 74a3112e3..6517aa698 100644 --- a/engine/common/q1bsp.c +++ b/engine/common/q1bsp.c @@ -80,8 +80,13 @@ enum rht_empty, rht_impact }; -vec3_t rht_start, rht_end; -static int Q1BSP_RecursiveHullTrace (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +struct rhtctx_s +{ + vec3_t start, end; + mclipnode_t *clipnodes; + mplane_t *planes; +}; +static int Q1BSP_RecursiveHullTrace (struct rhtctx_s *ctx, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) { mclipnode_t *node; mplane_t *plane; @@ -116,8 +121,8 @@ reenter: /*its a node*/ /*get the node info*/ - node = hull->clipnodes + num; - plane = hull->planes + node->planenum; + node = ctx->clipnodes + num; + plane = ctx->planes + node->planenum; if (plane->type < 3) { @@ -146,13 +151,13 @@ reenter: if (plane->type < 3) { - t1 = rht_start[plane->type] - plane->dist; - t2 = rht_end[plane->type] - plane->dist; + t1 = ctx->start[plane->type] - plane->dist; + t2 = ctx->end[plane->type] - plane->dist; } else { - t1 = DotProduct (plane->normal, rht_start) - plane->dist; - t2 = DotProduct (plane->normal, rht_end) - plane->dist; + t1 = DotProduct (plane->normal, ctx->start) - plane->dist; + t2 = DotProduct (plane->normal, ctx->end) - plane->dist; } side = t1 < 0; @@ -160,12 +165,12 @@ reenter: midf = t1 / (t1 - t2); if (midf < p1f) midf = p1f; if (midf > p2f) midf = p2f; - VectorInterpolate(rht_start, midf, rht_end, mid); + VectorInterpolate(ctx->start, midf, ctx->end, mid); - rht = Q1BSP_RecursiveHullTrace(hull, node->children[side], p1f, midf, p1, mid, trace); + rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side], p1f, midf, p1, mid, trace); if (rht != rht_empty) return rht; - rht = Q1BSP_RecursiveHullTrace(hull, node->children[side^1], midf, p2f, mid, p2, trace); + rht = Q1BSP_RecursiveHullTrace(ctx, node->children[side^1], midf, p2f, mid, p2, trace); if (rht != rht_solid) return rht; @@ -184,14 +189,14 @@ reenter: midf = (t1 - DIST_EPSILON) / (t1 - t2); } - t1 = DotProduct (trace->plane.normal, rht_start) - trace->plane.dist; - t2 = DotProduct (trace->plane.normal, rht_end) - trace->plane.dist; + t1 = DotProduct (trace->plane.normal, ctx->start) - trace->plane.dist; + t2 = DotProduct (trace->plane.normal, ctx->end) - trace->plane.dist; midf = (t1 - DIST_EPSILON) / (t1 - t2); - + midf = bound(0, midf, 1); trace->fraction = midf; VectorCopy (mid, trace->endpos); - VectorInterpolate(rht_start, midf, rht_end, trace->endpos); + VectorInterpolate(ctx->start, midf, ctx->end, trace->endpos); return rht_impact; } @@ -219,9 +224,12 @@ qboolean Q1BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, } else { - VectorCopy(p1, rht_start); - VectorCopy(p2, rht_end); - return Q1BSP_RecursiveHullTrace(hull, num, p1f, p2f, p1, p2, trace) != rht_impact; + struct rhtctx_s ctx; + VectorCopy(p1, ctx.start); + VectorCopy(p2, ctx.end); + ctx.clipnodes = hull->clipnodes; + ctx.planes = hull->planes; + return Q1BSP_RecursiveHullTrace(&ctx, num, p1f, p2f, p1, p2, trace) != rht_impact; } } @@ -832,6 +840,7 @@ hull_t *Q1BSP_ChooseHull(model_t *model, int forcehullnum, vec3_t mins, vec3_t m hull = &model->hulls[forcehullnum-1]; else { +#ifdef HEXEN2 if (model->hulls[5].available) { //choose based on hexen2 sizes. @@ -847,6 +856,7 @@ hull_t *Q1BSP_ChooseHull(model_t *model, int forcehullnum, vec3_t mins, vec3_t m hull = &model->hulls[5]; } else +#endif { if (size[0] < 3 || !model->hulls[1].available) hull = &model->hulls[0]; @@ -931,8 +941,12 @@ qboolean Q1BSP_Trace(model_t *model, int forcehullnum, int frame, vec3_t axis[3] hull = Q1BSP_ChooseHull(model, forcehullnum, mins, maxs, offset); -// offset[0] = 0; -// offset[1] = 0; +//fix for hexen2 monsters half-walking into walls. +// if (forent.flags & FL_MONSTER) +// { +// offset[0] = 0; +// offset[1] = 0; +// } if (axis) { diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 6fbc3c4d7..4c8d14563 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -703,7 +703,7 @@ static qboolean D3D11_VID_Init(rendererstate_t *info, unsigned char *palette) scd.Format = info->srgb?DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:DXGI_FORMAT_B8G8R8A8_UNORM; scd.Stereo = info->stereo; scd.SampleDesc.Count = d3d11multisample_count = max(1,info->multisample); - scd.SampleDesc.Quality = d3d11multisample_quality = (d3d11multisample_count>1)?D3D11_STANDARD_MULTISAMPLE_PATTERN:0; + scd.SampleDesc.Quality = d3d11multisample_quality = (d3d11multisample_count>1)?~0:0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 2+info->triplebuffer; //rt only supports fullscreen, so the frontbuffer needs to be created by us. scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; @@ -782,7 +782,7 @@ static qboolean initD3D11Device(HWND hWnd, rendererstate_t *info, PFN_D3D11_CREA scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.OutputWindow = hWnd; scd.SampleDesc.Count = d3d11multisample_count = max(1, info->multisample); //as we're starting up windowed (and switching to fullscreen after), the frontbuffer is handled by windows. - scd.SampleDesc.Quality = d3d11multisample_quality = (d3d11multisample_count>1)?D3D11_STANDARD_MULTISAMPLE_PATTERN:0; + scd.SampleDesc.Quality = d3d11multisample_quality = 0; scd.Windowed = TRUE; scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;// | DXGI_SWAP_CHAIN_FLAG_NONPREROTATED; diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 08210c281..76030f236 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -270,6 +270,7 @@ typedef struct { unsigned int contents; unsigned int id; //networked/gamecode id. + unsigned int axialplanes; //+x,+y,+z,-x,-y,-z. used for bevel stuff. unsigned int numplanes; qboolean selected; //different shader stuff vec4_t *planes; @@ -362,6 +363,7 @@ typedef struct heightmap_s char *texmask; //for editing - visually masks off the areas which CANNOT accept this texture + qboolean entsdirty; //ents were edited, so we need to reload all lighting... struct relight_ctx_s *relightcontext; struct llightinfo_s *lightthreadmem; qboolean inheritedlightthreadmem; @@ -3511,7 +3513,7 @@ typedef struct { #endif } hmtrace_t; -static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes) +static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes, brushes_t *brushinfo) { qboolean startout; float *enterplane; @@ -3592,6 +3594,91 @@ static int Heightmap_Trace_Brush(hmtrace_t *tr, vec4_t *planes, int numplanes) } } + //non-point traces need to clip against the brush's edges + if (brushinfo && tr->shape != ispoint && brushinfo->axialplanes != 0x3f) + { + static vec3_t axis[] = {{1,0,0},{0,1,0},{0,0,1},{-1,0,0},{0,-1,0},{0,0,-1}}; + for (i = 0; i < 6; i++) + { +// if (brushinfo->axialplanes & (1u<= 3) + { + /*calculate the distance based upon the shape of the object we're tracing for*/ + switch(tr->shape) + { + default: + case isbox: + dist = -tr->maxs[i-3]; + dist = -brushinfo->mins[i-3] - dist; + break; + case iscapsule: + dist = -tr->up[i-3]; + dist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0]; + dist = -brushinfo->mins[i-3] - dist; + break; + case ispoint: + dist = -brushinfo->mins[i-3]; + break; + } + d1 = -tr->start[i-3] - dist; + d2 = -tr->end[i-3] - dist; + } + else + { + switch(tr->shape) + { + default: + case isbox: + dist = brushinfo->maxs[i] - tr->mins[i]; + break; + case iscapsule: + dist = tr->up[i]; + dist = dist*(tr->capsulesize[(dist<0)?1:2]) - tr->capsulesize[0]; + dist = brushinfo->maxs[i] - dist; + break; + case ispoint: + dist = brushinfo->maxs[i]; + break; + } + d1 = (tr->start[i]) - dist; + d2 = (tr->end[i]) - dist; + } + + //if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one + if (d1 > 0 && d2 >= d1) + return false; + + if (d1 > 0) + startout = true; + + //if we're fully inside the plane, then whatever is happening is not relevent for this plane + if (d1 <= 0 && d2 <= 0) + continue; + + f = (d1) / (d1-d2); + if (d1 > d2) + { + //entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes) + if (enterfrac < f) + { + enterfrac = f; + nearfrac = (d1 - (0.03125)) / (d1-d2); + enterplane = axis[i]; + enterdist = dist; + } + } + else + { + //left the brush, favour the nearest plane (smallest frac) + if (exitfrac > f) + { + exitfrac = f; + } + } + } + } + if (!startout) { @@ -3752,7 +3839,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) Vector4Set(n[1], -1, 0, 0, -(tx/(SECTHEIGHTSIZE-1) + 0 - CHUNKBIAS)*tr->hm->sectionsize); Vector4Set(n[2], 0, 1, 0, (ty/(SECTHEIGHTSIZE-1) + 1 - CHUNKBIAS)*tr->hm->sectionsize); Vector4Set(n[3], 0, -1, 0, -(ty/(SECTHEIGHTSIZE-1) + 0 - CHUNKBIAS)*tr->hm->sectionsize); - Heightmap_Trace_Brush(tr, n, 4); + Heightmap_Trace_Brush(tr, n, 4, NULL); } return; } @@ -3813,7 +3900,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) n[3][3] = DotProduct(n[3], s->ents[i]->ent.origin) + -model->mins[1]; n[4][3] = DotProduct(n[4], s->ents[i]->ent.origin) + model->maxs[2]; n[5][3] = DotProduct(n[5], s->ents[i]->ent.origin) + -model->mins[2]; - Heightmap_Trace_Brush(tr, n, 6); + Heightmap_Trace_Brush(tr, n, 6, NULL); } else { @@ -3858,7 +3945,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) //top Vector4Set(n[4], 0, 0, 1, s->heights[(tx+0)+(ty+0)*SECTHEIGHTSIZE]); - Heightmap_Trace_Brush(tr, n, 5); + Heightmap_Trace_Brush(tr, n, 5, NULL); return; case HMM_TERRAIN: VectorSet(p[0], tr->htilesize*(sx+0), tr->htilesize*(sy+0), s->heights[(tx+0)+(ty+0)*SECTHEIGHTSIZE]); @@ -3895,7 +3982,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) n[5][3] = max(p[0][2], p[2][2]); n[5][3] = max(n[5][3], p[3][2]); - Heightmap_Trace_Brush(tr, n, 6); + Heightmap_Trace_Brush(tr, n, 6, NULL); } { @@ -3921,7 +4008,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) n[5][3] = max(p[0][2], p[1][2]); n[5][3] = max(n[5][3], p[3][2]); - Heightmap_Trace_Brush(tr, n, 6); + Heightmap_Trace_Brush(tr, n, 6, NULL); } } else @@ -3950,7 +4037,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) n[5][3] = max(p[0][2], p[1][2]); n[5][3] = max(n[5][3], p[2][2]); - Heightmap_Trace_Brush(tr, n, 6); + Heightmap_Trace_Brush(tr, n, 6, NULL); } { VectorSubtract(p[3], p[2], d[0]); @@ -3975,7 +4062,7 @@ static void Heightmap_Trace_Square(hmtrace_t *tr, int tx, int ty) n[5][3] = max(p[1][2], p[2][2]); n[5][3] = max(n[5][3], p[3][2]); - Heightmap_Trace_Brush(tr, n, 6); + Heightmap_Trace_Brush(tr, n, 6, NULL); } } break; @@ -4168,7 +4255,7 @@ qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec hmtrace.absmins[1] > brushes->maxs[1] || hmtrace.absmins[2] > brushes->maxs[2]) continue; - face = Heightmap_Trace_Brush(&hmtrace, brushes->planes, brushes->numplanes); + face = Heightmap_Trace_Brush(&hmtrace, brushes->planes, brushes->numplanes, brushes); if (face) { trace->brush_id = brushes->id; @@ -4829,6 +4916,11 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g mod->entities[oldlen + newlen] = 0; Z_Free(olds); G_FLOAT(OFS_RETURN) = oldlen + newlen; + if (mod->terrain) + { + hm = mod->terrain; + hm->entsdirty = true; + } } return; case ter_ents_get: @@ -5258,12 +5350,19 @@ static size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *p size_t numverts; //generate some huge quad/poly aligned with the plane - vec3_t tmp = {0.1,0.04,0.96}; + vec3_t tmp; vec3_t right, forward; + double t; // if (face[2] != 1) // return 0; + t = fabs(face[2]); + if (t > fabs(face[0]) && t > fabs(face[1])) + VectorSet(tmp, 1, 0, 0); + else + VectorSet(tmp, 0, 0, 1); + CrossProduct(face, tmp, right); VectorNormalize(right); CrossProduct(face, right, forward); @@ -5318,11 +5417,13 @@ static size_t Terr_GenerateBrushFace(vecV_t *points, size_t maxpoints, vec4_t *p { for (a = 0; a < 3; a++) { - //if its within 1/1000th of a qu, just call it okay. - if ((int)points[p][a] * 1000 == (int)(points[p][a]*1000)) - points[p][a] = floor(cverts[p][a] + 0.5); + float f = cverts[p][a]; + int rounded = floor(f + 0.5); + //if its within 1/1000th of a qu, just round it. + if (fabs(f - rounded) < 0.001) + points[p][a] = rounded; else - points[p][a] = cverts[p][a]; + points[p][a] = f; } } @@ -5362,6 +5463,21 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e) //allocate lightmap space for all surfaces, and then rebuild all textures. //if a surface is modified, clear its lightmap to -1 and when its batches are rebuilt, it'll unlight naturally. + if (hm->entsdirty) + { + model_t *mod = e->model; + if (mod->submodelof) + mod = mod->submodelof; + hm->entsdirty = false; + LightReloadEntities(hm->relightcontext, mod->entities, true); + + //FIXME: figure out some way to hint this without having to relight the entire frigging world. + for (bt = hm->brushtextures; bt; bt = bt->next) + for (i = 0, br = hm->wbrushes; i < hm->numbrushes; i++, br++) + for (j = 0; j < br->numplanes; j++) + br->faces[j].relight = true; + } + if (hm->recalculatebrushlighting && !r_fullbright.ival) { unsigned int lmcount; @@ -5782,6 +5898,7 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t * out = &hm->wbrushes[hm->numbrushes]; out->selected = false; out->contents = brush->contents; + out->axialplanes = 0; out->planes = BZ_Malloc((sizeof(*out->planes)+sizeof(*out->faces)) * brush->numplanes); out->faces = (void*)(out->planes+brush->numplanes); @@ -5816,6 +5933,19 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t * Vector4Copy(brush->faces[iface].stdir[0], out->faces[oface].stdir[0]); Vector4Copy(brush->faces[iface].stdir[1], out->faces[oface].stdir[1]); + if (out->planes[oface][0] == 1) + out->axialplanes |= 1u<<0; + else if (out->planes[oface][1] == 1) + out->axialplanes |= 1u<<1; + else if (out->planes[oface][2] == 1) + out->axialplanes |= 1u<<2; + else if (out->planes[oface][0] == -1) + out->axialplanes |= 1u<<3; + else if (out->planes[oface][1] == -1) + out->axialplanes |= 1u<<4; + else if (out->planes[oface][2] == -1) + out->axialplanes |= 1u<<5; + //make sure this stuff is rebuilt properly. out->faces[oface].tex->rebuild = true; @@ -6675,6 +6805,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) model_t *submod = NULL; #ifdef RUNTIMELIGHTING + hm->entsdirty = true; hm->relightcontext = LightStartup(NULL, mod, false); hm->lightthreadmem = BZ_Malloc(lightthreadctxsize); hm->inheritedlightthreadmem = false; @@ -6791,6 +6922,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) { //parse a plane //Quake: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname 0 -32 rotation sscale tscale + //hexen2: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname 0 -32 rotation sscale tscale utterlypointless //Valve: ( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale //fte : ( px py pz pd ) texname [sx sy sz sd] [tx ty tz td] 0 1 1 brushtex_t *bt; @@ -6900,6 +7032,13 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) entities = COM_ParseOut(entities, token, sizeof(token)); scale[1] = atof(token); + //hexen2 has some extra junk that is useless - some 'light' value, but its never used and should normally be -1. + while (*entities == ' ' || *entities == '\t') + entities++; + if (*entities == '-' || (*entities >= '0' && *entities <= '9')) + entities = COM_ParseOut(entities, token, sizeof(token)); + + //okay, that's all the actual parsing, now try to make sense of this plane. if (p == 4) { //parsed an actual plane VectorCopy(points[0], planes[numplanes]); @@ -7073,7 +7212,10 @@ qboolean QDECL Terr_LoadTerrainModel (model_t *mod, void *buffer, size_t bufsize #ifdef RUNTIMELIGHTING if (hm->relightcontext) - LightReloadEntities(hm->relightcontext, mod->entities); + { + LightReloadEntities(hm->relightcontext, mod->entities, true); + hm->entsdirty = false; + } #endif return true; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 858c0f4c7..cfbb43d4d 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -3285,6 +3285,9 @@ static void Mod_Batches_Build(model_t *mod, builddata_t *bd) if (!mod->textures) return; + if (mod->firstmodelsurface + mod->nummodelsurfaces > mod->numsurfaces) + Sys_Error("submodel %s surface range is out of bounds\n", mod->name); + if (bd) meshlist = NULL; else @@ -4801,7 +4804,7 @@ TRACE(("LoadBrushModel %i\n", __LINE__)); if (lightmodel == mod) { lightcontext = LightStartup(NULL, lightmodel, true); - LightReloadEntities(lightcontext, lightmodel->entities); + LightReloadEntities(lightcontext, lightmodel->entities, false); } #endif TRACE(("LoadBrushModel %i\n", __LINE__)); diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 4c06ffcdb..f09c34719 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -4580,6 +4580,10 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons *imagename = 0; } + //for water texture replacements + while((h = strchr(imagename, '*'))) + *h = '#'; + loadflags &= shader->flags; //skins can use an alternative path in certain cases, to work around dodgy models. diff --git a/engine/gl/gl_vidsdl.c b/engine/gl/gl_vidsdl.c index 4a17a2d45..11b4b0c4e 100644 --- a/engine/gl/gl_vidsdl.c +++ b/engine/gl/gl_vidsdl.c @@ -46,6 +46,62 @@ static void *GLVID_getsdlglfunction(char *functionname) #endif } +#if SDL_MAJOR_VERSION >= 2 +void *GLVID_CreateCursor (char *filename, float hotx, float hoty, float scale) +{ + int width; + int height; + SDL_Cursor *curs; + SDL_Surface *surf; + qbyte *rgbadata_start; + qboolean hasalpha; + void *filedata; + int filelen; + if (!filename || !*filename) + return NULL; + filelen = FS_LoadFile(filename, &filedata); + if (!filedata) + return NULL; + + rgbadata_start = Read32BitImageFile(filedata, filelen, &width, &height, &hasalpha, "cursor"); + FS_FreeFile(filedata); + if (!rgbadata_start) + return NULL; + + if (scale != 1) + { + int nw,nh; + qbyte *nd; + nw = width * scale; + nh = height * scale; + if (nw <= 0 || nh <= 0 || nw > 128 || nh > 128) //don't go crazy. + return NULL; + nd = BZ_Malloc(nw*nh*4); + Image_ResampleTexture((unsigned int*)rgbadata_start, width, height, (unsigned int*)nd, nw, nh); + width = nw; + height = nh; + BZ_Free(rgbadata_start); + rgbadata_start = nd; + } + + surf = SDL_CreateRGBSurfaceFrom(rgbadata_start, width, height, 32, width*4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + curs = SDL_CreateColorCursor(surf, hotx, hoty); + SDL_FreeSurface(surf); + BZ_Free(rgbadata_start); + return curs; +} +qboolean GLVID_SetCursor (void *cursor) +{ + SDL_SetCursor(cursor); + return !!cursor; +} +void GLVID_DestroyCursor (void *cursor) +{ + SDL_FreeCursor(cursor); +} +#endif + + qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) { int flags = 0; @@ -133,6 +189,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) Con_Printf("Couldn't set video mode: %s\n", SDL_GetError()); return false; } + CL_UpdateWindowTitle(); #if SDL_PATCHLEVEL >= 1 SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size. #else @@ -207,6 +264,12 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) } #endif +#if SDL_MAJOR_VERSION >= 2 + rf->VID_CreateCursor = GLVID_CreateCursor; + rf->VID_DestroyCursor = GLVID_DestroyCursor; + rf->VID_SetCursor = GLVID_SetCursor; +#endif + return true; } diff --git a/engine/gl/ltface.c b/engine/gl/ltface.c index b490d702a..7ec08c73c 100644 --- a/engine/gl/ltface.c +++ b/engine/gl/ltface.c @@ -144,7 +144,7 @@ struct relight_ctx_s *LightStartup(struct relight_ctx_s *ctx, model_t *model, qb ctx->models[ctx->nummodels++] = model; return ctx; } -void LightReloadEntities(struct relight_ctx_s *ctx, char *entstring) +void LightReloadEntities(struct relight_ctx_s *ctx, char *entstring, qboolean ignorestyles) { #define DEFAULTLIGHTLEVEL 300 mentity_t *mapent; @@ -254,6 +254,8 @@ void LightReloadEntities(struct relight_ctx_s *ctx, char *entstring) mapent->style = switchedstyle++; } + if (ignorestyles) + mapent->style = 0; ctx->num_entities++; } @@ -771,7 +773,7 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s if (!surf_rgbsamples) return; - memset (l, 0, sizeof(*l)); +// memset (l, 0, sizeof(*l)); l->ctx = ctx; // @@ -796,7 +798,11 @@ void LightPlane (struct relight_ctx_s *ctx, struct llightinfo_s *l, qbyte surf_s i = 0; #ifndef UTILITY for (; surf_styles[i] != 255 && i < 4; i++) + { l->lightstyles[i] = surf_styles[i]; + memset(&l->lightmaps[i], 0, sizeof(l->lightmaps[i][0])*l->numsurfpt); + memset(&l->lightnorm[i], 0, sizeof(l->lightnorm[i][0])*l->numsurfpt); + } #endif l->numlightstyles = i; for ( ; i_int = (int)(OPA->_int >= OPB->_int); break; case OP_GE_IF: - OPC->_float = (float)(OPA->_int >= OPB->_float); + OPC->_int = (float)(OPA->_int >= OPB->_float); break; case OP_GE_FI: - OPC->_float = (float)(OPA->_float >= OPB->_int); + OPC->_int = (float)(OPA->_float >= OPB->_int); break; case OP_LE_F: @@ -185,10 +185,10 @@ reeval: OPC->_int = (int)(OPA->_int <= OPB->_int); break; case OP_LE_IF: - OPC->_float = (float)(OPA->_int <= OPB->_float); + OPC->_int = (float)(OPA->_int <= OPB->_float); break; case OP_LE_FI: - OPC->_float = (float)(OPA->_float <= OPB->_int); + OPC->_int = (float)(OPA->_float <= OPB->_int); break; case OP_GT_F: @@ -198,10 +198,10 @@ reeval: OPC->_int = (int)(OPA->_int > OPB->_int); break; case OP_GT_IF: - OPC->_float = (float)(OPA->_int > OPB->_float); + OPC->_int = (float)(OPA->_int > OPB->_float); break; case OP_GT_FI: - OPC->_float = (float)(OPA->_float > OPB->_int); + OPC->_int = (float)(OPA->_float > OPB->_int); break; case OP_LT_F: @@ -211,10 +211,10 @@ reeval: OPC->_int = (int)(OPA->_int < OPB->_int); break; case OP_LT_IF: - OPC->_float = (float)(OPA->_int < OPB->_float); + OPC->_int = (float)(OPA->_int < OPB->_float); break; case OP_LT_FI: - OPC->_float = (float)(OPA->_float < OPB->_int); + OPC->_int = (float)(OPA->_float < OPB->_int); break; case OP_AND_F: @@ -244,7 +244,7 @@ reeval: OPC->_float = (float)(OPA->_float == OPB->_float); break; case OP_EQ_IF: - OPC->_float = (float)(OPA->_int == OPB->_float); + OPC->_int = (float)(OPA->_int == OPB->_float); break; case OP_EQ_FI: OPC->_float = (float)(OPA->_float == OPB->_int); diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index 598c3e758..c8f49244c 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -900,7 +900,7 @@ void QCC_PR_NewLine (pbool incomment); #define GDF_STATIC 2 #define GDF_CONST 4 #define GDF_STRIP 8 //always stripped, regardless of optimisations. used for class member fields -#define GDF_SILENT 16 //used by the gui, to suppress ALL warnings. +#define GDF_SILENT 16 //used by the gui, to suppress ALL warnings associated with querying the def. #define GDF_INLINE 32 //attempt to inline calls to this function #define GDF_USED 64 //don't strip this, ever. QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *scope, pbool allocate, int arraysize, unsigned int flags); diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index a219da2ae..806c0e6c1 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -173,6 +173,7 @@ QCC_sref_t QCC_PR_GenerateFunctionCall (QCC_sref_t newself, QCC_sref_t func, QCC void QCC_Marshal_Locals(int firststatement, int laststatement); QCC_sref_t QCC_PR_ParseArrayPointer (QCC_sref_t d, pbool allowarrayassign, pbool makestructpointers); QCC_sref_t QCC_LoadFromArray(QCC_sref_t base, QCC_sref_t index, QCC_type_t *t, pbool preserve); +void QCC_PR_ParseInitializerDef(QCC_def_t *def); QCC_ref_t *QCC_DefToRef(QCC_ref_t *ref, QCC_sref_t def); //ref is a buffer to write into, to avoid excessive allocs QCC_sref_t QCC_RefToDef(QCC_ref_t *ref, pbool freetemps); @@ -8905,6 +8906,8 @@ void QCC_PR_ParseStatement (void) { int old_numstatements; int numtemp, i; + QCC_def_t *subscopestop; + QCC_def_t *subscopestart = pr.local_tail; QCC_statement_t temp[256]; @@ -8914,9 +8917,23 @@ void QCC_PR_ParseStatement (void) QCC_PR_Expect("("); if (!QCC_PR_CheckToken(";")) { - QCC_PR_DiscardExpression(TOP_PRIORITY, 0); + do + { + QCC_type_t *type = QCC_PR_ParseType (false, true); + if (type) + { + d = QCC_PR_GetDef (type, QCC_PR_ParseName(), pr_scope, true, 0, 0); + QCC_PR_Expect("="); + QCC_PR_ParseInitializerDef(d); + QCC_FreeDef(d); + QCC_FreeDef(d); + } + else + QCC_PR_DiscardExpression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); + } while(QCC_PR_CheckToken(",")); QCC_PR_Expect(";"); } + subscopestop = pr_subscopedlocals?NULL:pr.local_tail->nextlocal; QCC_ClobberDef(NULL); @@ -8991,6 +9008,18 @@ void QCC_PR_ParseStatement (void) num_continues = continues; } + + //remove any new locals from the hashtable. + //typically this is just the stuff inside the for(here;;) + for (d = subscopestart->nextlocal; d != subscopestop; d = d->nextlocal) + { + if (!d->subscoped_away) + { + pHash_RemoveData(&localstable, d->name, d); + d->subscoped_away = true; + } + } + return; } if (QCC_PR_CheckKeyword(keyword_do, "do")) diff --git a/quakec/csaddon/src/editor_brushes.qc b/quakec/csaddon/src/editor_brushes.qc index 63ec65b64..3bd33abce 100644 --- a/quakec/csaddon/src/editor_brushes.qc +++ b/quakec/csaddon/src/editor_brushes.qc @@ -163,6 +163,9 @@ typedef struct int numidx; vector *p; int *i; + + int selectedvert1; + int selectedvert2; } vertsoup_t; vertsoup_t vertedit; brushface_t tmp_faces[64]; @@ -184,6 +187,7 @@ enum int BT_MERGE, BT_PUSHFACE, BT_CREATE, + BT_CREATEDRAG, BT_CLONEDISPLACE, BT_SLICE, BT_MOVETEXTURE, @@ -229,7 +233,7 @@ void(brushface_t *fa) reset_texturecoords = static int(brushface_t *fa, int famax, vector *points, int numpoints, float height) BrushFromPoints = { - int count = 0, p; + int count = 0; fa->planenormal = normalize(cross(points[2] - points[0], points[1] - points[0])); fa->planedist = bt_point[0] * fa->planenormal + height; @@ -244,7 +248,7 @@ static int(brushface_t *fa, int famax, vector *points, int numpoints, float heig fa++; count = 2; - for (p = 0; p < numpoints; p++) + for (int p = 0; p < numpoints; p++) { int n = p + 1; if (n == numpoints) @@ -256,7 +260,7 @@ static int(brushface_t *fa, int famax, vector *points, int numpoints, float heig fa++; count++; } - + return count; } @@ -265,7 +269,7 @@ vector(vector guess) brush_snappoint = { if (nogrid) return guess; - int facenum, points, point; + int facenum, points; float bestdist = autocvar_ca_grid*autocvar_ca_grid; //worst case value so we don't snap to grid when there's a vertex 0.001qu away from the grid. vector best = guess * (1.0/autocvar_ca_grid); best_x = rint(best_x); //snap to grid @@ -276,9 +280,9 @@ vector(vector guess) brush_snappoint = //find surfaces within 32qu of the point (via plane volume). use a tetrahedron instead if you want something more circular for (facenum = 0; facenum < axis.length; facenum++) dists[facenum] = (guess * axis[facenum]) + autocvar_ca_grid; - int brushnum, numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length); + int numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length); - for (brushnum = 0; brushnum < numbrushes; brushnum++) + for (int brushnum = 0; brushnum < numbrushes; brushnum++) { for (facenum = 0; ; ) { @@ -287,7 +291,7 @@ vector(vector guess) brush_snappoint = break; //end of face list, I guess //walk the faces we found and use the point if its nearer than our previous guess. - for (point = 0; point < points; point++) + for (int point = 0; point < points; point++) { vector disp = facepoints[point] - guess; float dist = disp*disp; @@ -341,8 +345,7 @@ static void brushface_translate(brushface_t *fa, int numfaces, vector displaceme //translate before+after first in order to deal with pivots. static void brushface_rotate(brushface_t *fa, int numfaces) { - int i; - for (i = 0; i < numfaces; i++, fa++) + for (int i = 0; i < numfaces; i++, fa++) { vector orig = fa->planenormal; fa->planenormal[0] = orig * v_forward; @@ -412,14 +415,13 @@ vector(vector p1, vector p2, vector norm, float dist) planelinepoint = int(brushface_t *faces, int numfaces, vector *points, int numpoints) isconcave = { int result = 0; - int f, p; //if any of the points are outside the brush, then we know that one of the planes cut straight through in a concavey kind of way - for(f = 0; f < numfaces; f++) + for(int f = 0; f < numfaces; f++) { vector n = faces[f].planenormal; float d = faces[f].planedist + EPSILON; //epsilon to cover precision issues - for (p = 0; p < numpoints; p++) + for (int p = 0; p < numpoints; p++) { if (points[p] * n > d) { @@ -598,6 +600,7 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu { //this is unfortunate. the built in shaders expect to use lightmaps. we don't have those. //because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can. + //FIXME: we don't manage to pick up the size of the original wad image shaderforname(faces[f].shadername, sprintf("{" "{\n" @@ -616,65 +619,130 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu } }; -#if 0 -void(vertsoup_t *soup, brushface_t **trifaces, brushface_t **splits, int internalsplits, int drawit) Rebrushify_r = +void(vector *p, int points, string shader, vector col, float alpha) DrawAxisExtensions = { - //clip the soup by the internal splits. - //if any triangle is outside, remove that plane from the sub-brush - //add the splits to the volume. call it done. - - brushface_t faces[64]; - int numfaces; - int point; - - for(point = 0; point+2 < vertedit.numidx; point+=3) + R_BeginPolygon(shader); + for (int point = 0; point < points; point++) { - vector v1 = vertedit.p[vertedit.i[point+0]]; - vector v2 = vertedit.p[vertedit.i[point+1]]; - vector v3 = vertedit.p[vertedit.i[point+2]]; - - if ( v1 * s->planenormal < s->planedist && - v2 * s->planenormal < s->planedist && - v3 * s->planenormal < s->planedist) - continue; - } - - if (drawit) - { - //draw it wireframe without depth testing - DrawQCBrushWireframe(faces, numfaces, "chop", '1 0 0'); + R_PolygonVertex(p[point] + [ 64, 0, 0], '0 0', col, alpha); + R_PolygonVertex(p[point] + [-64, 0, 0], '0 0', col, alpha); + R_EndPolygon(); + R_PolygonVertex(p[point] + [0, 64, 0], '0 0', col, alpha); + R_PolygonVertex(p[point] + [0, -64, 0], '0 0', col, alpha); + R_EndPolygon(); + R_PolygonVertex(p[point] + [0, 0, 64], '0 0', col, alpha); + R_PolygonVertex(p[point] + [0, 0, -64], '0 0', col, alpha); + R_EndPolygon(); } }; -#endif + +static float(vertsoup_t *soup, int *idx, __inout vector norm, __inout float dist) planenormal = +{ + vector v1 = soup->p[idx[0]]; + vector v2 = soup->p[idx[1]]; + vector v3 = soup->p[idx[2]]; + vector d1 = v3 - v1; + vector d2 = v2 - v1; + vector d3 = v3 - v2; + norm = normalize(cross(d1, d2)); + dist = norm * v1; + + if (!d1 || !d2 || !d3 || !norm) + return FALSE; + return TRUE; +}; void(vertsoup_t *soup, int drawit) Rebrushify = { - brushface_t *internalsplit[64]; brushface_t faces[64]; - brushface_t *trifaces[128]; int numfaces, f, point; string shader = 0; + vector n; + float d; + + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + //if any triangle's neighbour opposes it, reform the edge across the quad to try to keep it convex. + for(point = 0; point+2 < soup->numidx; point+=3) + { + int p1 = soup->i[point+0]; + int p2 = soup->i[point+1]; + int p3 = soup->i[point+2]; + if (!planenormal(soup, &soup->i[point], n, d)) + continue; //degenerate + d += EPSILON; + for(f = point+3; f+2 < soup->numidx; f+=3) + { + int o1 = soup->i[f+0]; + int o2 = soup->i[f+1]; + int o3 = soup->i[f+2]; + int o; +p1p2edge: + if (o2 == p1 && o1 == p2) + o = o3; + else if (o3 == p1 && o2 == p2) + o = o1; + else if (o1 == p1 && o3 == p2) + o = o2; + else + goto p2p3edge; + if (soup->p[o] * n > d) + { + soup->i[f+0] = p3; + soup->i[f+1] = o; + soup->i[f+2] = p2; + p2 = o; + } + continue; +p2p3edge: + if (o2 == p2 && o1 == p3) + o = o3; + else if (o3 == p2 && o2 == p3) + o = o1; + else if (o1 == p2 && o3 == p3) + o = o2; + else + goto p3p1edge; + if (soup->p[o] * n > d) + { + soup->i[f+0] = p1; + soup->i[f+1] = o; + soup->i[f+2] = p3; + p3 = o; + } + continue; +p3p1edge: + if (o2 == p3 && o1 == p1) + o = o3; + else if (o3 == p3 && o2 == p1) + o = o1; + else if (o1 == p3 && o3 == p1) + o = o2; + else + continue; + if (soup->p[o] * n > d) + { + soup->i[f+0] = p2; + soup->i[f+1] = o; + soup->i[f+2] = p1; + p1 = o; + } + continue; + } + + soup->i[point+0] = p1; + soup->i[point+1] = p2; + soup->i[point+2] = p3; + } + //generate the plane info numfaces = 0; - for(point = 0; point+2 < vertedit.numidx; point+=3) + for(point = 0; point+2 < soup->numidx; point+=3) { - vector v1 = vertedit.p[vertedit.i[point+0]]; - vector v2 = vertedit.p[vertedit.i[point+1]]; - vector v3 = vertedit.p[vertedit.i[point+2]]; - vector d1 = v3 - v1; - vector d2 = v2 - v1; - vector d3 = v3 - v2; - vector n = normalize(cross(d1, d2)); - float d = n * v1; - - if (!d1 || !d2 || !d3 || !n) - { - trifaces[point/3] = 0; + if (!planenormal(soup, &soup->i[point], n, d)) continue; //a degenerate triangle is one that probably got merged or something - } for (f = 0; f < numfaces; f++) { @@ -682,10 +750,7 @@ void(vertsoup_t *soup, int drawit) Rebrushify = break; } if (f < numfaces) - { - trifaces[point/3] = &faces[f]; continue; //duplicate plane - } for (f = 0; f < tmp_numfaces; f++) { @@ -702,7 +767,6 @@ void(vertsoup_t *soup, int drawit) Rebrushify = faces[numfaces].sbias = tmp_faces[f].sbias; faces[numfaces].tdir = tmp_faces[f].tdir; faces[numfaces].tbias = tmp_faces[f].tbias; - trifaces[point/3] = &faces[numfaces]; numfaces++; break; } @@ -720,7 +784,6 @@ void(vertsoup_t *soup, int drawit) Rebrushify = faces[numfaces].planenormal = n; faces[numfaces].planedist = d; reset_texturecoords(&faces[numfaces]); - trifaces[point/3] = &faces[numfaces]; numfaces++; } @@ -740,20 +803,20 @@ void(vertsoup_t *soup, int drawit) Rebrushify = { n = faces[f].planenormal; d = faces[f].planedist; - for (point = 0; point < vertedit.numidx; point++) //would ideally use points, but I want to cover dead ones + for (point = 0; point < soup->numidx; point++) //would ideally use points, but I want to cover dead ones { - if (vertedit.p[vertedit.i[point]] * n > d + EPSILON) + if (soup->p[soup->i[point]] * n > d + EPSILON) { - internalsplit[internals++] = &faces[f]; + internals++; break; } } } cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces)); -// Rebrushify_r(soup, trifaces, internalsplit, internals, drawit); + if (numfaces <= 3) + return; //can't possibly be valid. - if (drawit) { //draw it wireframe WITH depth testing @@ -763,7 +826,7 @@ void(vertsoup_t *soup, int drawit) Rebrushify = DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1); //draw it wireframe without depth testing - DrawQCBrushWireframe(faces, numfaces, "chop", '0 0 1', 1); + DrawQCBrushWireframe(faces, numfaces, "chop", internals?'1 0 0':'0 0 1', 1); } else { @@ -772,8 +835,8 @@ void(vertsoup_t *soup, int drawit) Rebrushify = selectedbrush = brush_history_create(selectedbrushmodel, faces, numfaces, 1i); selectedbrushface = 0; - vertedit.numidx = 0; - vertedit.numverts = 0; + soup->numidx = 0; + soup->numverts = 0; } }; @@ -787,6 +850,8 @@ void(void) Debrushify = vector p; vector *np; static int fi[64]; + vertedit.numidx = 0; + vertedit.numverts = 0; for (i = 0; ; ) { points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++i, facepoints, facepoints.length); @@ -834,6 +899,48 @@ void(void) Debrushify = } }; +//determines only the various points of the brush, without duplicates. doesn't care about indexes. +void(brushface_t *faces, int numfaces) DebrushifyLite = +{ + int points, k; + vector p; + vector *np; + int fi[64]; + vertedit.numidx = 0; + vertedit.numverts = 0; + for (int i = 0; ; ) + { + points = brush_calcfacepoints(++i, faces, numfaces, facepoints, facepoints.length); + if (!points) + break; + + for (int j = 0; j < points; j++) + { + p = facepoints[j]; + p_x = floor(p_x + 0.5); //gah, bloomin inprecision. + p_y = floor(p_y + 0.5); + p_z = floor(p_z + 0.5); + for (k = 0; k < vertedit.numverts; k++) + { //try to be nice and re-use verts. + if (vertedit.p[k] == p) + break; + } + if (k == vertedit.numverts) + { + //if it wasn't found, we need to allocate a new one + np = memalloc(sizeof(*np) * (vertedit.numverts+1)); + memcpy(np, vertedit.p, sizeof(*np) * vertedit.numverts); + memfree(vertedit.p); + vertedit.p = np; + np += vertedit.numverts; + vertedit.numverts += 1; + *np = p; + } + fi[j] = k; + } + } +}; + void(vector mousepos) editor_brushes_add = { vector col = '0 0 0'; @@ -841,8 +948,10 @@ void(vector mousepos) editor_brushes_add = int facenum; float intensity = (sin(gettime(5)*4)+1)*0.05; vector displace, tmp; + float bestdist, dist; + vector mid; - if ((brushtool == BT_PUSHFACE || brushtool == BT_CLONEDISPLACE || brushtool == BT_MOVE || brushtool == BT_MOVETEXTURE) && bt_points) + if ((mousetool == BT_VERTEXEDIT || mousetool == BT_CREATEDRAG || brushtool == BT_PUSHFACE || brushtool == BT_CLONEDISPLACE || brushtool == BT_MOVE || brushtool == BT_MOVETEXTURE) && bt_points) { makevectors(input_angles); vector dir = v_forward; @@ -851,7 +960,8 @@ void(vector mousepos) editor_brushes_add = tmp = normalize(mousefar-mousenear) + mousenear; tmp = planelinepoint(mousenear, tmp, dir, bt_point[0] * dir); //find where the cursor impacts the screen grid (moved along the view vector to match where the drag was last frame) displace = tmp - bt_point[0]; - displace = brush_snappoint(displace); + if (mousetool != BT_VERTEXEDIT && mousetool != BT_CREATEDRAG) + displace = brush_snappoint(displace); if (altdown) //if alt is held, rotate the move by 90 degrees, and move ONLY in the axial dir instead of the screen's xy plane bt_displace_z = (displace * v_right + displace * v_up); else @@ -860,6 +970,9 @@ void(vector mousepos) editor_brushes_add = bt_displace_y = displace * axialize(v_up); } displace = bt_displace_x * axialize(v_right) + bt_displace_y * axialize(v_up) - bt_displace_z * axialize(v_forward); + + if (mousetool == BT_VERTEXEDIT) + displace = brush_snappoint(displace+bt_point[0]); } else if (brushtool == BT_ROTATE) { @@ -898,7 +1011,7 @@ void(vector mousepos) editor_brushes_add = "nodepthtest\n" "}\n" "}"); - + if (mousetool == BT_VERTEXEDIT && vertedit.numidx) { #if 0 @@ -913,6 +1026,7 @@ void(vector mousepos) editor_brushes_add = R_EndPolygon(); } #endif +#if 1 //draw the wires (no depth) R_BeginPolygon("chop"); for(point = 0; point+2 < vertedit.numidx; point+=3) @@ -928,6 +1042,21 @@ void(vector mousepos) editor_brushes_add = R_PolygonVertex(vertedit.p[vertedit.i[point+0]], '0 0', col, 1); R_EndPolygon(); } +#endif + + DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '1 0.3 0.3', 1); + + if (mousedown) + { + if (bt_points == 1 && vertedit.selectedvert1 != -1) + { + if (vertedit.selectedvert2 != -1) + vertedit.p[vertedit.selectedvert2] = displace + bt_point[2]; + vertedit.p[vertedit.selectedvert1] = displace + bt_point[1]; + } + } + else + bt_points = 0; Rebrushify(&vertedit, TRUE); } @@ -940,6 +1069,56 @@ void(vector mousepos) editor_brushes_add = DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "chop", '1 0 0', 1); } } + else if (mousetool == BT_CREATEDRAG) + { + if (bt_points) + { + displace -= axialize(v_forward)*autocvar_ca_grid; + + mid = bt_point[0]; + displace = brush_snappoint(mid + displace); + mid = brush_snappoint(mid); + displace = displace - mid; + + tmp_faces[0].planenormal = '1 0 0'; + tmp_faces[1].planenormal = '-1 0 0'; + tmp_faces[2].planenormal = '0 1 0'; + tmp_faces[3].planenormal = '0 -1 0'; + tmp_faces[4].planenormal = '0 0 1'; + tmp_faces[5].planenormal = '0 0 -1'; + tmp_faces[0].planedist = mid[0]; + tmp_faces[1].planedist = -mid[0]; + tmp_faces[2].planedist = mid[1]; + tmp_faces[3].planedist = -mid[1]; + tmp_faces[4].planedist = mid[2]; + tmp_faces[5].planedist = -mid[2]; + tmp_numfaces = 6; + tmp_contents = 1; + + tmp_faces[0 + (displace_x<0.0)].planedist += fabs(displace_x); + tmp_faces[2 + (displace_y<0.0)].planedist += fabs(displace_y); + tmp_faces[4 + (displace_z<0.0)].planedist += fabs(displace_z); + + for (facenum = 0; facenum < tmp_numfaces; facenum++) + { + tmp_faces[facenum].shadername = autocvar_ca_newbrushtexture; + reset_texturecoords(&tmp_faces[facenum]); + } + + DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "chop", '1 0 0', 1); + + if (!mousedown) + { + brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); + selectedbrush = brush_history_create(selectedbrushmodel, tmp_faces, tmp_numfaces, tmp_contents); + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + bt_points = 0; + mousetool = BT_NONE; + } + } + else + mousedown = FALSE; + } else if ((mousetool == BT_PUSHFACE || mousetool == BT_MOVETEXTURE || mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE || mousetool == BT_ROTATE) && bt_points) { tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); @@ -1022,6 +1201,9 @@ void(vector mousepos) editor_brushes_add = //draw it wireframe WITH depth testing DrawQCBrushWireframe(tmp_faces, tmp_numfaces, "terrainedit", '0 0 1', 1); + DebrushifyLite(tmp_faces, tmp_numfaces); + DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1); + if (!mousedown) { brush_selected(selectedbrushmodel, selectedbrush, -1, FALSE); @@ -1053,13 +1235,10 @@ void(vector mousepos) editor_brushes_add = //draw the other side wireframe DrawQCBrushWireframe(tmp_faces, tmp_numfaces+1, "chop", '0 1 0', 1); } - else + else if (selectedbrush) { if (brushtool == BT_PUSHFACE) { //selected face (not brush) follows cursor when we're not actively dragging a face - float bestdist = 0, dist; - vector mid; - for(facenum = 0;;) { points = brush_getfacepoints(selectedbrushmodel, selectedbrush, ++facenum, facepoints, facepoints.length); @@ -1115,6 +1294,10 @@ void(vector mousepos) editor_brushes_add = R_PolygonVertex(facepoints[0], '0 0', col, 1); R_EndPolygon(); } + + tmp_numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp_faces, tmp_faces.length, &tmp_contents); + DebrushifyLite(tmp_faces, tmp_numfaces); + DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1); } // editor_drawbbox(selectedbrush); @@ -1179,11 +1362,78 @@ void(vector mousepos) editor_brushes_add = void(vector mousepos) editor_brushes_overlay = { + int point; + vector mid; + float dist, bestdist; + if (vertedit.numidx) + { + if (!mousedown) + { + for(vertedit.selectedvert1 = -1, vertedit.selectedvert2 = -1, point = 0, bestdist = 16*16; point < vertedit.numverts; point++) + { + mid = project(vertedit.p[point]); + dist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y); + if (dist < bestdist && mid_z > 0) + { + bestdist = dist; + vertedit.selectedvert1 = point; + } + } + for(point = 0; point < vertedit.numidx; point+=3) + { + mid = project(0.5*(vertedit.p[vertedit.i[point+0]] + vertedit.p[vertedit.i[point+1]])); + dist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y); + if (dist < bestdist && mid_z > 0) + { + bestdist = dist; + vertedit.selectedvert1 = vertedit.i[point+0]; + vertedit.selectedvert2 = vertedit.i[point+1]; + } + + mid = project(0.5*(vertedit.p[vertedit.i[point+1]] + vertedit.p[vertedit.i[point+2]])); + dist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y); + if (dist < bestdist && mid_z > 0) + { + bestdist = dist; + vertedit.selectedvert1 = vertedit.i[point+1]; + vertedit.selectedvert2 = vertedit.i[point+2]; + } + + mid = project(0.5*(vertedit.p[vertedit.i[point+2]] + vertedit.p[vertedit.i[point+0]])); + dist = (mousepos_x - mid_x) * (mousepos_x - mid_x) + (mousepos_y - mid_y) * (mousepos_y - mid_y); + if (dist < bestdist && mid_z > 0) + { + bestdist = dist; + vertedit.selectedvert1 = vertedit.i[point+2]; + vertedit.selectedvert2 = vertedit.i[point+0]; + } + } + } + if (vertedit.selectedvert1 != -1) + { + mid = project(vertedit.p[vertedit.selectedvert1]); + if (mid_z >= 0) + drawfill([mid_x,mid_y] - '2 2', [4,4], [0.2,0.2,1], 1, 0); + } + if (vertedit.selectedvert2 != -1) + { + mid = project(vertedit.p[vertedit.selectedvert2]); + if (mid_z >= 0) + drawfill([mid_x,mid_y] - '2 2', [4,4], [0.2,0.2,1], 1, 0); + } + + drawrawstring('0 32 0', "Vertex Editor", '8 8 0', '1 1 1', 1); + return; + } + switch(brushtool) { case BT_CLONEDISPLACE: drawrawstring('0 32 0', "Clone", '8 8 0', '1 1 1', 1); break; + case BT_CREATEDRAG: + drawrawstring('0 32 0', "Drag+Create", '8 8 0', '1 1 1', 1); + break; case BT_CREATE: drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1); break; @@ -1204,12 +1454,16 @@ void(vector mousepos) editor_brushes_overlay = case BT_MOVETEXTURE: drawrawstring('0 32 0', "Move Texture", '8 8 0', '1 1 1', 1); break; + case BT_VERTEXEDIT: + drawrawstring('0 32 0', "Vertex Editor", '8 8 0', '1 1 1', 1); + break; } }; #define brusheditormodes //#append brusheditormodes brusheditormode("move", BT_MOVE) #append brusheditormodes brusheditormode("clone", BT_CLONEDISPLACE) +#append brusheditormodes brusheditormode("draw", BT_CREATEDRAG) #append brusheditormodes brusheditormode("rotate", BT_ROTATE) #append brusheditormodes brusheditormode("pushface", BT_PUSHFACE) #append brusheditormodes brusheditormode("scrolltex", BT_MOVETEXTURE) @@ -1306,7 +1560,7 @@ brusheditormodes localcmd("menubind 0 40 \"brushedit_redo\" \"Redo\"\n"); localcmd("menubind 0 48 \"brushedit_nogrid\" \"Disable Grid\"\n"); - float foo = 48; + float foo = 56; #define brusheditormode(n,v) localcmd(sprintf("menubind 0 %g \"+brushedit_"n"\" \""n"\"\n", foo)); foo+=8; brusheditormodes #undef brusheditormode @@ -1337,6 +1591,8 @@ float(float key, float unic, vector mousepos) editor_brushes_key = t = o + normalize(t)*8192; if (key == K_ESCAPE) { + vertedit.numidx = 0; + vertedit.numverts = 0; if (brushtool) brushtool = BT_NONE; else @@ -1349,16 +1605,37 @@ float(float key, float unic, vector mousepos) editor_brushes_key = } if (key == K_MOUSE1) { + if (vertedit.numidx) + { + if (vertedit.selectedvert1 != -1) + { + mousedown = TRUE; + mousetool = BT_VERTEXEDIT; + if (vertedit.selectedvert2 != -1) + { + bt_point[0] = 0.5 * (vertedit.p[vertedit.selectedvert1] + vertedit.p[vertedit.selectedvert2]); + bt_point[1] = vertedit.p[vertedit.selectedvert1] - bt_point[0]; + bt_point[2] = vertedit.p[vertedit.selectedvert2] - bt_point[0]; + } + else + bt_point[0] = vertedit.p[vertedit.selectedvert1]; + bt_point[1] = vertedit.p[vertedit.selectedvert1] - bt_point[0]; + bt_points = 1; + bt_displace = '0 0 0'; + } + return TRUE; + } + traceline(o, t, TRUE, world); float tracemodel = trace_ent.modelindex; - if (brushtool == BT_PUSHFACE && selectedbrushface) + if (brushtool == BT_CREATEDRAG || (brushtool == BT_PUSHFACE && selectedbrushface)) { trace_brush_faceid = selectedbrushface; trace_brush_id = selectedbrush; tracemodel = selectedbrushmodel; } - if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE) + else if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE) trace_brush_faceid = 0; if (brushtool == BT_SLICE || brushtool == BT_CREATE) @@ -1410,21 +1687,36 @@ float(float key, float unic, vector mousepos) editor_brushes_key = selectedbrush = trace_brush_id; selectedbrushface = trace_brush_faceid; selectedbrushmodel = tracemodel; - brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); + if (trace_brush_faceid) + brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); vertedit.numidx = 0; vertedit.numverts = 0; + + if (selectedbrushface && (brushtool == BT_PUSHFACE || brushtool == BT_MOVETEXTURE)) + { + mousedown = TRUE; + mousetool = brushtool; + vertedit.selectedvert1 = -1; + vertedit.selectedvert2 = -1; + bt_point[0] = brush_snappoint(trace_endpos); + bt_points = 1; + bt_displace = '0 0 0'; + } } else if (selectedbrush == trace_brush_id && selectedbrushface == trace_brush_faceid && selectedbrushmodel == tracemodel) { mousedown = TRUE; mousetool = brushtool; + vertedit.selectedvert1 = -1; + vertedit.selectedvert2 = -1; bt_point[0] = brush_snappoint(trace_endpos); bt_points = 1; bt_displace = '0 0 0'; } if (brushtool == BT_VERTEXEDIT && !vertedit.numidx && selectedbrush) { + mousedown = FALSE; Debrushify(); brush_selected(selectedbrushmodel, selectedbrush, selectedbrushface, TRUE); } @@ -1433,11 +1725,13 @@ float(float key, float unic, vector mousepos) editor_brushes_key = if (key == K_ENTER) { - if (mousetool == BT_VERTEXEDIT) + if (vertedit.numidx) { Rebrushify(&vertedit, FALSE); mousetool = BT_NONE; bt_points = 0; + vertedit.numidx = 0; + vertedit.numverts = 0; mousedown = FALSE; return TRUE; } @@ -1508,31 +1802,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key = } return TRUE; - } - - if (vertedit.numidx) - { - vector dir = '0 0 0'; - makevectors(input_angles); - if (key == K_KP_PLUS) - dir = axialize(v_up); - else if (key == K_KP_MINUS) - dir = -axialize(v_up); - else if (key == K_KP_UPARROW) - dir = axialize(v_forward); - else if (key == K_KP_DOWNARROW) - dir = -axialize(v_forward); - else if (key == K_KP_RIGHTARROW) - dir = axialize(v_right); - else if (key == K_KP_LEFTARROW) - dir = -axialize(v_right); - if (dir != '0 0 0') - { - vertedit.p[0] += dir; - return TRUE; - } - } - + } /* if (key == 's') { diff --git a/quakec/csaddon/src/editor_ents.qc b/quakec/csaddon/src/editor_ents.qc index b4f0c824c..484e96d4b 100644 --- a/quakec/csaddon/src/editor_ents.qc +++ b/quakec/csaddon/src/editor_ents.qc @@ -22,6 +22,8 @@ typedef struct static entedit_t *editents; static int numents; +static int entsdirty; +static float entsapplytime; struct { //FIXME: should probably parse quakeed comments or something. @@ -173,38 +175,43 @@ void(float num) editor_ents_delete = } }; +void() updatemodelents = +{ + entsdirty = FALSE; + self = world; + terrain_edit(TEREDIT_ENTS_WIPE); + for (int e = 0; e < numents; e++) + { + local entedit_t *ent = &editents[e]; + string n; + n = "{\n"; + for (int i = 0; ; i++) + { + string key, value; + key = hash_getkey(ent->fields, i); + if not (key) + break; + value = ent->fields[key]; + //inject markup into the value so that it doesn't get too corrupted + value = strreplace("\\", "\\\\", value); + value = strreplace("\"", "\\\"", value); + value = strreplace("\n", "\\n", value); + //these are more optional + value = strreplace("\t", "\\t", value); + value = strreplace("\r", "\\r", value); + n = strcat(n, key, " \"", value, "\"\n"); + } + n = strcat(n, "}\n"); + + terrain_edit(TEREDIT_ENTS_CONCAT, n); + } +}; + float(float mode) editor_ents_poll = { if (mode != MODE_ENTSEDIT) - { - int i, e; - self = world; - terrain_edit(TEREDIT_ENTS_WIPE); - for (e = 0; e < numents; e++) - { - local entedit_t *ent = &editents[e]; - string n; - n = "{\n"; - for (i = 0; ; i++) - { - string key, value; - key = hash_getkey(ent->fields, i); - if not (key) - break; - value = ent->fields[key]; - //inject markup into the value so that it doesn't get too corrupted - value = strreplace("\\", "\\\\", value); - value = strreplace("\"", "\\\"", value); - value = strreplace("\n", "\\n", value); - //these are more optional - value = strreplace("\t", "\\t", value); - value = strreplace("\r", "\\r", value); - n = strcat(n, key, " \"", value, "\"\n"); - } - n = strcat(n, "}\n"); - - terrain_edit(TEREDIT_ENTS_CONCAT, n); - } + { + updatemodelents(); localcmd("mod_terrain_save\n"); //saves a .ent if its a bsp, a .map if it has brushes, and a .hmp if otherwise. or something. ca_checksave = __NULL__; return TRUE; @@ -214,6 +221,9 @@ float(float mode) editor_ents_poll = void() editor_ents_edited = { ca_checksave = editor_ents_poll; + + entsdirty = TRUE; + entsapplytime = cltime+2; } inline float(string model) modelindexforname = @@ -433,6 +443,8 @@ float(float key, float unic, vector mousepos) editor_ents_key = float bestfrac = 1; int bestent = selectedent, e; + //FIXME: we need some click+drag moving like the brush editor has + if (mousepos_x < 128) { editfieldtype = editkey?2:1; @@ -623,4 +635,7 @@ void(vector mousepos) editor_ents_overlay = drawrawstring(pos, sprintf("%s: ", editkey==""?"":editkey), '8 8 0', '0 0 1', 1); pos_y += 8; } + + if (entsdirty && cltime > entsapplytime) + updatemodelents(); };