From cc875358fd0c2b3d16ef805e617dbc66b0b1eaf8 Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 18 Sep 2015 20:30:10 +0000 Subject: [PATCH] qcc: allow variable definitions inside for statements. these are strictly subscoped. csaddon: implement convenient vertex dragging. not bug free, but should be sufficiently functional. small optimisation for r_loadlit 2. sdl2: now supports hardware cursors, supposedly. also ensures the window title is set to something that isn't obviously weird. fix #water replacements not being loaded. again. .map loading now ignores all lightstyles, instead of many lights being completely black. .map loading can now properly cope with angled surfaces without the player getting stuck. try to avoid missing d3d11 definitions issue. tp_disputablemacros no longer blocks say commands, but does block +forward etc. supposedly. fix timer precision issue that appears in sdl builds. not sure why it wasn't visible in other cases though. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4989 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_main.c | 5 +- engine/client/m_script.c | 4 +- engine/client/m_single.c | 64 +++- engine/client/render.h | 2 +- engine/client/sys_sdl.c | 16 +- engine/client/zqtp.c | 11 +- engine/common/bothdefs.h | 4 +- engine/common/cmd.c | 92 ++++-- engine/common/cmd.h | 9 +- engine/common/cvar.c | 10 +- engine/common/cvar.h | 1 + engine/common/q1bsp.c | 54 +-- engine/d3d/vid_d3d11.c | 4 +- engine/gl/gl_heightmap.c | 172 +++++++++- engine/gl/gl_model.c | 5 +- engine/gl/gl_shader.c | 4 + engine/gl/gl_vidsdl.c | 63 ++++ engine/gl/ltface.c | 10 +- engine/qclib/execloop.h | 18 +- engine/qclib/qcc.h | 2 +- engine/qclib/qcc_pr_comp.c | 31 +- quakec/csaddon/src/editor_brushes.qc | 478 +++++++++++++++++++++------ quakec/csaddon/src/editor_ents.qc | 73 ++-- 23 files changed, 900 insertions(+), 232 deletions(-) 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(); };