X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=prvm_cmds.c;h=391f9e27cf91e7e802a718b2bb545ff4af2d9ae4;hb=1ae269e1612265dc7c54fbccdb1ca0f285752b79;hp=702d8a82f9d253f21f41aabc729cd6f6a8e2447e;hpb=bcbb99559a7fb1ee350b45035f7132abb714a59e;p=xonotic%2Fdarkplaces.git diff --git a/prvm_cmds.c b/prvm_cmds.c index 702d8a82..391f9e27 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -10,6 +10,8 @@ #include "libcurl.h" #include +#include "cl_collision.h" +#include "clvm_cmds.h" #include "ft2.h" extern cvar_t prvm_backtraceforwarnings; @@ -25,7 +27,7 @@ void VM_Warning(const char *fmt, ...) dpvsnprintf(msg,sizeof(msg),fmt,argptr); va_end(argptr); - Con_Print(msg); + Con_DPrint(msg); // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black] if(prvm_backtraceforwarnings.integer && recursive != realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set @@ -186,14 +188,14 @@ void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const f ed->priv.server->skeleton.model = edmodel; } if (!ed->priv.server->skeleton.relativetransforms && ed->priv.server->skeleton.model && ed->priv.server->skeleton.model->num_bones) - ed->priv.server->skeleton.relativetransforms = Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t)); + ed->priv.server->skeleton.relativetransforms = (matrix4x4_t *)Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t)); if (ed->priv.server->skeleton.relativetransforms) { - int skeletonindex = 0; + int skeletonindex = -1; skeleton_t *skeleton; prvm_eval_t *val; - if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float; - if (skeletonindex > 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones) + if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float - 1; + if (skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones) { // custom skeleton controlled by the game (FTE_CSQC_SKELETONOBJECTS) memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t)); @@ -204,19 +206,19 @@ void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const f int blendindex; int bonenum; int numbones = ed->priv.server->skeleton.model->num_bones; - const float *poses = ed->priv.server->skeleton.model->data_poses; - const float *framebones; + const short *framebones6s; float lerp; + float scale = ed->priv.server->skeleton.model->num_posescale; matrix4x4_t *relativetransforms = ed->priv.server->skeleton.relativetransforms; matrix4x4_t matrix; memset(relativetransforms, 0, numbones * sizeof(matrix4x4_t)); for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++) { lerp = frameblend[blendindex].lerp; - framebones = poses + 12 * frameblend[blendindex].subframe * numbones; + framebones6s = ed->priv.server->skeleton.model->data_poses6s + 6 * frameblend[blendindex].subframe * numbones; for (bonenum = 0;bonenum < numbones;bonenum++) { - Matrix4x4_FromArray12FloatD3D(&matrix, framebones + 12 * bonenum); + Matrix4x4_FromBonePose6s(&matrix, scale, framebones6s + 6 * bonenum); Matrix4x4_Accumulate(&ed->priv.server->skeleton.relativetransforms[bonenum], &matrix, lerp); } } @@ -267,7 +269,7 @@ checkextension(extensionname) static qboolean checkextension(const char *name) { int len; - char *e, *start; + const char *e, *start; len = (int)strlen(name); for (e = prog->extensionstring;*e;e++) @@ -744,15 +746,12 @@ void VM_dprint (void) { char string[VM_STRINGTEMP_LENGTH]; VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint); - if (developer.integer) - { - VM_VarString(0, string, sizeof(string)); + VM_VarString(0, string, sizeof(string)); #if 1 - Con_Printf("%s", string); + Con_DPrintf("%s", string); #else - Con_Printf("%s: %s", PRVM_NAME, string); + Con_DPrintf("%s: %s", PRVM_NAME, string); #endif - } } /* @@ -974,12 +973,12 @@ void VM_remove (void) ed = PRVM_G_EDICT(OFS_PARM0); if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts ) { - if (developer.integer >= 1) + if (developer.integer > 0) VM_Warning( "VM_remove: tried to remove the null entity or a reserved entity!\n" ); } else if( ed->priv.required->free ) { - if (developer.integer >= 1) + if (developer.integer > 0) VM_Warning( "VM_remove: tried to remove an already freed entity!\n" ); } else @@ -1845,14 +1844,14 @@ void VM_fopen(void) if (prog->openfiles[filenum] == NULL) { PRVM_G_FLOAT(OFS_RETURN) = -1; - if (developer.integer >= 100) + if (developer_extra.integer) VM_Warning("VM_fopen: %s: %s mode %s failed\n", PRVM_NAME, filename, modestring); } else { PRVM_G_FLOAT(OFS_RETURN) = filenum; - if (developer.integer >= 100) - Con_Printf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum); + if (developer_extra.integer) + Con_DPrintf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum); prog->openfiles_origin[filenum] = PRVM_AllocationOrigin(); } } @@ -1886,8 +1885,8 @@ void VM_fclose(void) prog->openfiles[filenum] = NULL; if(prog->openfiles_origin[filenum]) PRVM_Free((char *)prog->openfiles_origin[filenum]); - if (developer.integer >= 100) - Con_Printf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum); + if (developer_extra.integer) + Con_DPrintf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum); } /* @@ -1937,8 +1936,8 @@ void VM_fgets(void) if (c != '\n') FS_UnGetc(prog->openfiles[filenum], (unsigned char)c); } - if (developer.integer >= 100) - Con_Printf("fgets: %s: %s\n", PRVM_NAME, string); + if (developer_extra.integer) + Con_DPrintf("fgets: %s: %s\n", PRVM_NAME, string); if (c >= 0 || end) PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string); } @@ -1973,8 +1972,8 @@ void VM_fputs(void) VM_VarString(1, string, sizeof(string)); if ((stringlength = (int)strlen(string))) FS_Write(prog->openfiles[filenum], string, stringlength); - if (developer.integer >= 100) - Con_Printf("fputs: %s: %s\n", PRVM_NAME, string); + if (developer_extra.integer) + Con_DPrintf("fputs: %s: %s\n", PRVM_NAME, string); } /* @@ -2879,6 +2878,50 @@ void VM_gettime(void) } } +/* +========= +VM_getsoundtime + +float getsoundtime(void) +========= +*/ + +void VM_getsoundtime (void) +{ + int entnum, entchannel, pnum; + VM_SAFEPARMCOUNT(2,VM_getsoundtime); + + pnum = PRVM_GetProgNr(); + if (pnum == PRVM_MENUPROG) + { + VM_Warning("VM_getsoundtime: %s: not supported on this progs\n", PRVM_NAME); + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; + } + entnum = ((pnum == PRVM_CLIENTPROG) ? MAX_EDICTS : 0) + PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0)); + entchannel = (int)PRVM_G_FLOAT(OFS_PARM1); + if (entchannel <= 0 || entchannel > 8) + VM_Warning("VM_getsoundtime: %s: bad channel %i\n", PRVM_NAME, entchannel); + PRVM_G_FLOAT(OFS_RETURN) = (float)S_GetEntChannelPosition(entnum, entchannel); +} + +/* +========= +VM_GetSoundLen + +string soundlength (string sample) +========= +*/ +void VM_soundlength (void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1, VM_soundlength); + + s = PRVM_G_STRING(OFS_PARM0); + PRVM_G_FLOAT(OFS_RETURN) = S_SoundLength(s); +} + /* ========= VM_loadfromdata @@ -3151,8 +3194,6 @@ void VM_chr(void) VM_SAFEPARMCOUNT(1, VM_chr); len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp)); - if (len < 0) - len = 0; tmp[len] = 0; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp); } @@ -3193,7 +3234,7 @@ void VM_precache_pic(void) VM_CheckEmptyString (s); // AK Draw_CachePic is supposed to always return a valid pointer - if( Draw_CachePic_Flags(s, CACHEPICFLAG_NOTPERSISTENT)->tex == r_texture_notexture ) + if( Draw_CachePic_Flags(s, 0)->tex == r_texture_notexture ) PRVM_G_INT(OFS_RETURN) = OFS_NULL; } @@ -3216,14 +3257,29 @@ void VM_freepic(void) Draw_FreePic(s); } +void getdrawfontscale(float *sx, float *sy) +{ + vec3_t v; + *sx = *sy = 1; + if(prog->globaloffsets.drawfontscale >= 0) + { + VectorCopy(PRVM_G_VECTOR(prog->globaloffsets.drawfontscale), v); + if(VectorLength2(v) > 0) + { + *sx = v[0]; + *sy = v[1]; + } + } +} + dp_font_t *getdrawfont(void) { if(prog->globaloffsets.drawfont >= 0) { int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont); - if(f < 0 || f >= MAX_FONTS) + if(f < 0 || f >= dp_fonts.maxsize) return FONT_DEFAULT; - return &dp_fonts[f]; + return &dp_fonts.f[f]; } else return FONT_DEFAULT; @@ -3241,6 +3297,7 @@ void VM_drawcharacter(void) float *pos,*scale,*rgb; char character; int flag; + float sx, sy; VM_SAFEPARMCOUNT(6,VM_drawcharacter); character = (char) PRVM_G_FLOAT(OFS_PARM1); @@ -3273,7 +3330,8 @@ void VM_drawcharacter(void) return; } - DrawQ_String_Font(pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont()); + getdrawfontscale(&sx, &sy); + DrawQ_String_Scale(pos[0], pos[1], &character, 1, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont()); PRVM_G_FLOAT(OFS_RETURN) = 1; } @@ -3289,6 +3347,7 @@ void VM_drawstring(void) float *pos,*scale,*rgb; const char *string; int flag; + float sx, sy; VM_SAFEPARMCOUNT(6,VM_drawstring); string = PRVM_G_STRING(OFS_PARM1); @@ -3314,7 +3373,8 @@ void VM_drawstring(void) if(pos[2] || scale[2]) Con_Printf("VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale"))); - DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont()); + getdrawfontscale(&sx, &sy); + DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont()); //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true); PRVM_G_FLOAT(OFS_RETURN) = 1; } @@ -3330,7 +3390,8 @@ void VM_drawcolorcodedstring(void) { float *pos,*scale; const char *string; - int flag,color; + int flag; + float sx, sy; VM_SAFEPARMCOUNT(5,VM_drawstring); string = PRVM_G_STRING(OFS_PARM1); @@ -3355,8 +3416,8 @@ void VM_drawcolorcodedstring(void) if(pos[2] || scale[2]) Con_Printf("VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale"))); - color = -1; - DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], 1, 1, 1, PRVM_G_FLOAT(OFS_PARM3), flag, NULL, false, getdrawfont()); + getdrawfontscale(&sx, &sy); + DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, 1, 1, 1, PRVM_G_FLOAT(OFS_PARM3), flag, NULL, false, getdrawfont()); PRVM_G_FLOAT(OFS_RETURN) = 1; } /* @@ -3372,9 +3433,11 @@ void VM_stringwidth(void) float *szv; float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell int colors; - float x[200]; + float sx, sy; + size_t maxlen = 0; VM_SAFEPARMCOUNTRANGE(2,3,VM_drawstring); + getdrawfontscale(&sx, &sy); if(prog->argc == 3) { szv = PRVM_G_VECTOR(OFS_PARM2); @@ -3382,16 +3445,23 @@ void VM_stringwidth(void) } else { - static float defsize[] = {0, 0}; + // we want the width for 8x8 font size, divided by 8 + static float defsize[] = {8, 8}; szv = defsize; - mult = 1; + mult = 0.125; + // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case + if(sx >= 0.9 && sx <= 1.1) + { + mult *= 2; + sx /= 2; + sy /= 2; + } } - x[180] = 3; string = PRVM_G_STRING(OFS_PARM0); colors = (int)PRVM_G_FLOAT(OFS_PARM1); - PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font_Size(string, szv[0], szv[1], 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw + PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(), 1000000000) * mult; /* if(prog->argc == 3) { @@ -3406,10 +3476,178 @@ void VM_stringwidth(void) string = PRVM_G_STRING(OFS_PARM0); colors = (int)PRVM_G_FLOAT(OFS_PARM1); - PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw + PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw */ +} + +/* +========= +VM_findfont + +float findfont(string s) +========= +*/ + +float getdrawfontnum(const char *fontname) +{ + int i; + + for(i = 0; i < dp_fonts.maxsize; ++i) + if(!strcmp(dp_fonts.f[i].title, fontname)) + return i; + return -1; +} + +void VM_findfont(void) +{ + VM_SAFEPARMCOUNT(1,VM_findfont); + PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0)); +} + +/* +========= +VM_loadfont + +float loadfont(string fontname, string fontmaps, string sizes, float slot) +========= +*/ + +dp_font_t *FindFont(const char *title, qboolean allocate_new); +void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset); +void VM_loadfont(void) +{ + const char *fontname, *filelist, *sizes, *c, *cm; + char mainfont[MAX_QPATH]; + int i, numsizes; + float sz, scale, voffset; + dp_font_t *f; + + VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont); + + fontname = PRVM_G_STRING(OFS_PARM0); + if (!fontname[0]) + fontname = "default"; + + filelist = PRVM_G_STRING(OFS_PARM1); + if (!filelist[0]) + filelist = "gfx/conchars"; + + sizes = PRVM_G_STRING(OFS_PARM2); + if (!sizes[0]) + sizes = "10"; + + // find a font + f = NULL; + if (prog->argc >= 4) + { + i = PRVM_G_FLOAT(OFS_PARM3); + if (i >= 0 && i < dp_fonts.maxsize) + { + f = &dp_fonts.f[i]; + strlcpy(f->title, fontname, sizeof(f->title)); // replace name + } + } + if (!f) + f = FindFont(fontname, true); + if (!f) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; // something go wrong + } + + memset(f->fallbacks, 0, sizeof(f->fallbacks)); + memset(f->fallback_faces, 0, sizeof(f->fallback_faces)); + + // first font is handled "normally" + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->req_face = atoi(c+1); + else + { + f->req_face = 0; + c = cm; + } + if(!c || (c - filelist) > MAX_QPATH) + strlcpy(mainfont, filelist, sizeof(mainfont)); + else + { + memcpy(mainfont, filelist, c - filelist); + mainfont[c - filelist] = 0; + } + + // handle fallbacks + for(i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + c = strchr(filelist, ','); + if(!c) + break; + filelist = c + 1; + if(!*filelist) + break; + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->fallback_faces[i] = atoi(c+1); + else + { + f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index + c = cm; + } + if(!c || (c-filelist) > MAX_QPATH) + { + strlcpy(f->fallbacks[i], filelist, sizeof(mainfont)); + } + else + { + memcpy(f->fallbacks[i], filelist, c - filelist); + f->fallbacks[i][c - filelist] = 0; + } + } + + // handle sizes + for(i = 0; i < MAX_FONT_SIZES; ++i) + f->req_sizes[i] = -1; + for (numsizes = 0,c = sizes;;) + { + if (!COM_ParseToken_VM_Tokenize(&c, 0)) + break; + sz = atof(com_token); + // detect crap size + if (sz < 0.001f || sz > 1000.0f) + { + VM_Warning("VM_loadfont: crap size %s", com_token); + continue; + } + // check overflow + if (numsizes == MAX_FONT_SIZES) + { + VM_Warning("VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES); + break; + } + f->req_sizes[numsizes] = sz; + numsizes++; + } + + // additional scale/hoffset parms + scale = 1; + voffset = 0; + if (prog->argc >= 5) + { + scale = PRVM_G_FLOAT(OFS_PARM4); + if (scale <= 0) + scale = 1; + } + if (prog->argc >= 6) + voffset = PRVM_G_FLOAT(OFS_PARM5); + + // load + LoadFont(true, mainfont, f, scale, voffset); + // return index of loaded font + PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f); } + /* ========= VM_drawpic @@ -3451,7 +3689,7 @@ void VM_drawpic(void) if(pos[2] || size[2]) Con_Printf("VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); - DrawQ_Pic(pos[0], pos[1], Draw_CachePic (picname), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag); + DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag); PRVM_G_FLOAT(OFS_RETURN) = 1; } /* @@ -3496,7 +3734,7 @@ void VM_drawrotpic(void) if(pos[2] || size[2] || org[2]) Con_Printf("VM_drawrotpic: z value from pos/size/org discarded\n"); - DrawQ_RotPic(pos[0], pos[1], Draw_CachePic(picname), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag); + DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag); PRVM_G_FLOAT(OFS_RETURN) = 1; } /* @@ -3544,7 +3782,7 @@ void VM_drawsubpic(void) if(pos[2] || size[2]) Con_Printf("VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); - DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic (picname), + DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], srcPos[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha, srcPos[0] + srcSize[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha, @@ -3664,31 +3902,35 @@ void VM_keynumtostring (void) ========= VM_findkeysforcommand -string findkeysforcommand(string command) +string findkeysforcommand(string command, float bindmap) the returned string is an altstring ========= */ -#define NUMKEYS 5 // TODO: merge the constant in keys.c with this one somewhen - +#define FKFC_NUMKEYS 5 void M_FindKeysForCommand(const char *command, int *keys); void VM_findkeysforcommand(void) { const char *cmd; char ret[VM_STRINGTEMP_LENGTH]; - int keys[NUMKEYS]; + int keys[FKFC_NUMKEYS]; int i; + int bindmap; - VM_SAFEPARMCOUNT(1, VM_findkeysforcommand); + VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand); cmd = PRVM_G_STRING(OFS_PARM0); + if(prog->argc == 2) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" VM_CheckEmptyString(cmd); - M_FindKeysForCommand(cmd, keys); + Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap); ret[0] = 0; - for(i = 0; i < NUMKEYS; i++) + for(i = 0; i < FKFC_NUMKEYS; i++) strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret)); PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret); @@ -3705,7 +3947,80 @@ void VM_stringtokeynum (void) { VM_SAFEPARMCOUNT( 1, VM_keynumtostring ); - PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0)); + PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0)); +} + +/* +========= +VM_getkeybind + +string getkeybind(float key, float bindmap) +========= +*/ +void VM_getkeybind (void) +{ + int bindmap; + VM_SAFEPARMCOUNTRANGE(1, 2, VM_CL_getkeybind); + if(prog->argc == 2) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" + + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap)); +} + +/* +========= +VM_setkeybind + +float setkeybind(float key, string cmd, float bindmap) +========= +*/ +void VM_setkeybind (void) +{ + int bindmap; + VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_setkeybind); + if(prog->argc == 3) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" + + PRVM_G_FLOAT(OFS_RETURN) = 0; + if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1))) + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +========= +VM_getbindmap + +vector getbindmaps() +========= +*/ +void VM_getbindmaps (void) +{ + int fg, bg; + VM_SAFEPARMCOUNT(0, VM_CL_getbindmap); + Key_GetBindMap(&fg, &bg); + PRVM_G_VECTOR(OFS_RETURN)[0] = fg; + PRVM_G_VECTOR(OFS_RETURN)[1] = bg; + PRVM_G_VECTOR(OFS_RETURN)[2] = 0; +} + +/* +========= +VM_setbindmap + +float setbindmaps(vector bindmap) +========= +*/ +void VM_setbindmaps (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_setbindmap); + PRVM_G_FLOAT(OFS_RETURN) = 0; + if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0) + if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1])) + PRVM_G_FLOAT(OFS_RETURN) = 1; } // CL_Video interface functions @@ -3730,7 +4045,7 @@ void VM_cin_open( void ) VM_CheckEmptyString( file ); VM_CheckEmptyString( name ); - if( CL_OpenVideo( file, name, MENUOWNER ) ) + if( CL_OpenVideo( file, name, MENUOWNER, "" ) ) PRVM_G_FLOAT( OFS_RETURN ) = 1; else PRVM_G_FLOAT( OFS_RETURN ) = 0; @@ -4319,18 +4634,16 @@ string altstr_ins(string altstr, float num, string set) void VM_altstr_ins(void) { int num; - const char *setstr; const char *set; - const char *instr; const char *in; char *out; char outstr[VM_STRINGTEMP_LENGTH]; VM_SAFEPARMCOUNT(3, VM_altstr_ins); - in = instr = PRVM_G_STRING( OFS_PARM0 ); + in = PRVM_G_STRING( OFS_PARM0 ); num = (int)PRVM_G_FLOAT( OFS_PARM1 ); - set = setstr = PRVM_G_STRING( OFS_PARM2 ); + set = PRVM_G_STRING( OFS_PARM2 ); out = outstr; for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ ) @@ -4395,8 +4708,8 @@ static int BufStr_SortStringsUP (const void *in1, const void *in2) const char *a, *b; a = *((const char **) in1); b = *((const char **) in2); - if(!a[0]) return 1; - if(!b[0]) return -1; + if(!a || !a[0]) return 1; + if(!b || !b[0]) return -1; return strncmp(a, b, stringbuffers_sortlength); } @@ -4405,8 +4718,8 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2) const char *a, *b; a = *((const char **) in1); b = *((const char **) in2); - if(!a[0]) return 1; - if(!b[0]) return -1; + if(!a || !a[0]) return 1; + if(!b || !b[0]) return -1; return strncmp(b, a, stringbuffers_sortlength); } @@ -4415,19 +4728,34 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2) VM_buf_create creates new buffer, and returns it's index, returns -1 if failed float buf_create(void) = #460; +float newbuf(string format, float flags) = #460; ======================== */ + void VM_buf_create (void) { prvm_stringbuffer_t *stringbuffer; int i; - VM_SAFEPARMCOUNT(0, VM_buf_create); + + VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create); + + // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string" + if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string")) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; + } stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray); for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++); stringbuffer->origin = PRVM_AllocationOrigin(); + // optional flags parm + if(prog->argc == 2) + stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & 0xFF; PRVM_G_FLOAT(OFS_RETURN) = i; } + + /* ======================== VM_buf_del @@ -4651,6 +4979,7 @@ void bufstr_set(float bufhandle, float string_index, string str) = #466; */ void VM_bufstr_set (void) { + size_t alloclen; int strindex; prvm_stringbuffer_t *stringbuffer; const char *news; @@ -4677,10 +5006,11 @@ void VM_bufstr_set (void) Mem_Free(stringbuffer->strings[strindex]); stringbuffer->strings[strindex] = NULL; - news = PRVM_G_STRING(OFS_PARM2); - if (news && news[0]) + if(PRVM_G_INT(OFS_PARM2)) { - size_t alloclen = strlen(news) + 1; + // not the NULL string! + news = PRVM_G_STRING(OFS_PARM2); + alloclen = strlen(news) + 1; stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen); memcpy(stringbuffer->strings[strindex], news, alloclen); } @@ -4712,12 +5042,12 @@ void VM_bufstr_add (void) VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); return; } - string = PRVM_G_STRING(OFS_PARM1); - if(!string || !string[0]) + if(!PRVM_G_INT(OFS_PARM1)) // NULL string { VM_Warning("VM_bufstr_add: can not add an empty string to buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); return; } + string = PRVM_G_STRING(OFS_PARM1); order = (int)PRVM_G_FLOAT(OFS_PARM2); if(order) strindex = stringbuffer->num_strings; @@ -5017,7 +5347,7 @@ void VM_strstrofs (void) if (!match) PRVM_G_FLOAT(OFS_RETURN) = -1; else - PRVM_G_FLOAT(OFS_RETURN) = match - instr; + PRVM_G_FLOAT(OFS_RETURN) = u8_strnlen(instr, match-instr); } //#222 string(string s, float index) str2chr (FTE_STRINGS) @@ -5032,7 +5362,10 @@ void VM_str2chr (void) if((unsigned)index < strlen(s)) { - ch = u8_getchar(s + index, NULL); + if (utf8_enable.integer) + ch = u8_getchar_noendptr(s + index); + else + ch = (unsigned char)s[index]; PRVM_G_FLOAT(OFS_RETURN) = ch; } else @@ -5056,11 +5389,7 @@ void VM_chr2str (void) size_t len = 0; VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str); for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i) - { - int add = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1); - if(add > 0) - len += add; - } + len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1); t[len] = 0; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t); } @@ -5401,7 +5730,7 @@ void VM_uri_escape (void) || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || (*p == '-') || (*p == '_') || (*p == '.') - || (*p == '!') || (*p == '~') || (*p == '*') + || (*p == '!') || (*p == '~') || (*p == '\'') || (*p == '(') || (*p == ')')) *q++ = *p; else @@ -5535,7 +5864,7 @@ void VM_uri_get (void) handle->prognr = PRVM_GetProgNr(); handle->starttime = prog->starttime; handle->id = id; - ret = Curl_Begin_ToMemory(url, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle); + ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle); if(ret) { PRVM_G_INT(OFS_RETURN) = 1; @@ -5579,7 +5908,7 @@ void VM_CL_getextresponse (void) int first; --cl_net_extresponse_count; first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX; - PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl_net_extresponse[first]); + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(cl_net_extresponse[first]); } } @@ -5594,10 +5923,30 @@ void VM_SV_getextresponse (void) int first; --sv_net_extresponse_count; first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX; - PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(sv_net_extresponse[first]); + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(sv_net_extresponse[first]); } } +/* +========= +Common functions between menu.dat and clsprogs +========= +*/ + +//#349 float() isdemo +void VM_CL_isdemo (void) +{ + VM_SAFEPARMCOUNT(0, VM_CL_isdemo); + PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback; +} + +//#355 float() videoplaying +void VM_CL_videoplaying (void) +{ + VM_SAFEPARMCOUNT(0, VM_CL_videoplaying); + PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying; +} + /* ========= VM_M_callfunction @@ -5684,7 +6033,10 @@ void VM_sprintf(void) int width, precision, thisarg, flags; char formatbuf[16]; char *f; - qboolean isfloat; + int isfloat; + static int dummyivec[3] = {0, 0, 0}; + static float dummyvec[3] = {0, 0, 0}; + #define PRINTF_ALTERNATE 1 #define PRINTF_ZEROPAD 2 #define PRINTF_LEFT 4 @@ -5696,7 +6048,9 @@ void VM_sprintf(void) s = PRVM_G_STRING(OFS_PARM0); #define GETARG_FLOAT(a) (((a)>=1 && (a)argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_VECTOR(a) (((a)>=1 && (a)argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec) #define GETARG_INT(a) (((a)>=1 && (a)argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_INTVECTOR(a) (((a)>=1 && (a)argc) ? ((int*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec) #define GETARG_STRING(a) (((a)>=1 && (a)argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "") for(;;) @@ -5706,7 +6060,6 @@ void VM_sprintf(void) { case 0: goto finished; - break; case '%': ++s; @@ -5720,6 +6073,7 @@ void VM_sprintf(void) precision = -1; thisarg = -1; flags = 0; + isfloat = -1; // is number following? if(*s >= '0' && *s <= '9') @@ -5781,6 +6135,11 @@ noflags: else width = argpos++; width = GETARG_FLOAT(width); + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } } else if(*s >= '0' && *s <= '9') { @@ -5791,12 +6150,13 @@ noflags: goto finished; } s = err; + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } } - if(width < 0) - { - flags |= PRINTF_LEFT; - width = -width; - } + // otherwise width stays -1 } if(*s == '.') @@ -5836,14 +6196,13 @@ noflags: } } - isfloat = true; for(;;) { switch(*s) { - case 'h': isfloat = true; break; - case 'l': isfloat = false; break; - case 'L': isfloat = false; break; + case 'h': isfloat = 1; break; + case 'l': isfloat = 0; break; + case 'L': isfloat = 0; break; case 'j': break; case 'z': break; case 't': break; @@ -5854,6 +6213,15 @@ noflags: } nolength: + // now s points to the final directive char and is no longer changed + if(isfloat < 0) + { + if(*s == 'i') + isfloat = 0; + else + isfloat = 1; + } + if(thisarg < 0) thisarg = argpos++; @@ -5872,24 +6240,38 @@ nolength: *f++ = *s; *f++ = 0; - if(width < 0) + if(width < 0) // not set width = 0; switch(*s) { case 'd': case 'i': + if(precision < 0) // not set + precision = 1; o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); break; case 'o': case 'u': case 'x': case 'X': + if(precision < 0) // not set + precision = 1; o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': - if(precision < 0) + if(precision < 0) // not set precision = 6; o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); break; + case 'v': case 'V': + f[-2] += 'g' - 'v'; + if(precision < 0) // not set + precision = 6; + o += dpsnprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2]) + ); + break; case 'c': - if(precision < 0) + if(precision < 0) // not set precision = end - o - 1; if(flags & PRINTF_ALTERNATE) o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); @@ -5903,7 +6285,7 @@ nolength: } break; case 's': - if(precision < 0) + if(precision < 0) // not set precision = end - o - 1; if(flags & PRINTF_ALTERNATE) o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg)); @@ -5928,3 +6310,425 @@ finished: *o = 0; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(outbuf); } + + +// surface querying + +static dp_model_t *getmodel(prvm_edict_t *ed) +{ + switch(PRVM_GetProgNr()) + { + case PRVM_SERVERPROG: + return SV_GetModelFromEdict(ed); + case PRVM_CLIENTPROG: + return CL_GetModelFromEdict(ed); + default: + return NULL; + } +} + +typedef struct +{ + unsigned int progid; + dp_model_t *model; + frameblend_t frameblend[MAX_FRAMEBLENDS]; + skeleton_t *skeleton_p; + skeleton_t skeleton; + float *data_vertex3f; + float *data_svector3f; + float *data_tvector3f; + float *data_normal3f; + int max_vertices; + float *buf_vertex3f; + float *buf_svector3f; + float *buf_tvector3f; + float *buf_normal3f; +} +animatemodel_cache_t; +static animatemodel_cache_t animatemodel_cache; + +void animatemodel(dp_model_t *model, prvm_edict_t *ed) +{ + prvm_eval_t *val; + skeleton_t *skeleton; + int skeletonindex = -1; + qboolean need = false; + if(!model->AnimateVertices) + { + animatemodel_cache.data_vertex3f = model->surfmesh.data_vertex3f; + animatemodel_cache.data_svector3f = model->surfmesh.data_svector3f; + animatemodel_cache.data_tvector3f = model->surfmesh.data_tvector3f; + animatemodel_cache.data_normal3f = model->surfmesh.data_normal3f; + return; + } + if(animatemodel_cache.progid != prog->id) + memset(&animatemodel_cache, 0, sizeof(animatemodel_cache)); + need |= (animatemodel_cache.model != model); + VM_GenerateFrameGroupBlend(ed->priv.server->framegroupblend, ed); + VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model); + need |= (memcmp(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0; + if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float - 1; + if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones)) + skeleton = NULL; + need |= (animatemodel_cache.skeleton_p != skeleton); + if(skeleton) + need |= (memcmp(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0; + if(!need) + return; + if(model->surfmesh.num_vertices > animatemodel_cache.max_vertices) + { + animatemodel_cache.max_vertices = model->surfmesh.num_vertices * 2; + if(animatemodel_cache.buf_vertex3f) Mem_Free(animatemodel_cache.buf_vertex3f); + if(animatemodel_cache.buf_svector3f) Mem_Free(animatemodel_cache.buf_svector3f); + if(animatemodel_cache.buf_tvector3f) Mem_Free(animatemodel_cache.buf_tvector3f); + if(animatemodel_cache.buf_normal3f) Mem_Free(animatemodel_cache.buf_normal3f); + animatemodel_cache.buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + } + animatemodel_cache.data_vertex3f = animatemodel_cache.buf_vertex3f; + animatemodel_cache.data_svector3f = animatemodel_cache.buf_svector3f; + animatemodel_cache.data_tvector3f = animatemodel_cache.buf_tvector3f; + animatemodel_cache.data_normal3f = animatemodel_cache.buf_normal3f; + VM_UpdateEdictSkeleton(ed, model, ed->priv.server->frameblend); + model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache.data_vertex3f, animatemodel_cache.data_normal3f, animatemodel_cache.data_svector3f, animatemodel_cache.data_tvector3f); + animatemodel_cache.progid = prog->id; + animatemodel_cache.model = model; + memcpy(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend)); + animatemodel_cache.skeleton_p = skeleton; + if(skeleton) + memcpy(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton)); +} + +static void getmatrix(prvm_edict_t *ed, matrix4x4_t *out) +{ + switch(PRVM_GetProgNr()) + { + case PRVM_SERVERPROG: + SV_GetEntityMatrix(ed, out, false); + break; + case PRVM_CLIENTPROG: + CL_GetEntityMatrix(ed, out, false); + break; + default: + *out = identitymatrix; + break; + } +} + +static void applytransform_forward(const vec3_t in, prvm_edict_t *ed, vec3_t out) +{ + matrix4x4_t m; + getmatrix(ed, &m); + Matrix4x4_Transform(&m, in, out); +} + +static void applytransform_forward_direction(const vec3_t in, prvm_edict_t *ed, vec3_t out) +{ + matrix4x4_t m; + getmatrix(ed, &m); + Matrix4x4_Transform3x3(&m, in, out); +} + +static void applytransform_inverted(const vec3_t in, prvm_edict_t *ed, vec3_t out) +{ + matrix4x4_t m, n; + getmatrix(ed, &m); + Matrix4x4_Invert_Full(&n, &m); + Matrix4x4_Transform3x3(&n, in, out); +} + +static void applytransform_forward_normal(const vec3_t in, prvm_edict_t *ed, vec3_t out) +{ + matrix4x4_t m; + float p[4]; + getmatrix(ed, &m); + Matrix4x4_TransformPositivePlane(&m, in[0], in[1], in[2], 0, p); + VectorCopy(p, out); +} + +static void clippointtosurface(prvm_edict_t *ed, dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out) +{ + int i, j, k; + float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist; + const int *e; + animatemodel(model, ed); + bestdist = 1000000000; + VectorCopy(p, out); + for (i = 0, e = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3) + { + // clip original point to each triangle of the surface and find the + // triangle that is closest + v[0] = animatemodel_cache.data_vertex3f + e[0] * 3; + v[1] = animatemodel_cache.data_vertex3f + e[1] * 3; + v[2] = animatemodel_cache.data_vertex3f + e[2] * 3; + TriangleNormal(v[0], v[1], v[2], facenormal); + VectorNormalize(facenormal); + offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal); + VectorMA(p, offsetdist, facenormal, temp); + for (j = 0, k = 2;j < 3;k = j, j++) + { + VectorSubtract(v[k], v[j], edgenormal); + CrossProduct(edgenormal, facenormal, sidenormal); + VectorNormalize(sidenormal); + offsetdist = DotProduct(v[k], sidenormal) - DotProduct(temp, sidenormal); + if (offsetdist < 0) + VectorMA(temp, offsetdist, sidenormal, temp); + } + dist = VectorDistance2(temp, p); + if (bestdist > dist) + { + bestdist = dist; + VectorCopy(temp, out); + } + } +} + +static msurface_t *getsurface(dp_model_t *model, int surfacenum) +{ + if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces) + return NULL; + return model->data_surfaces + surfacenum + model->firstmodelsurface; +} + + +//PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434; +void VM_getsurfacenumpoints(void) +{ + dp_model_t *model; + msurface_t *surface; + VM_SAFEPARMCOUNT(2, VM_getsurfacenumpoints); + // return 0 if no such surface + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + + // note: this (incorrectly) assumes it is a simple polygon + PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices; +} +//PF_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint = #435; +void VM_getsurfacepoint(void) +{ + prvm_edict_t *ed; + dp_model_t *model; + msurface_t *surface; + int pointnum; + VM_SAFEPARMCOUNT(3, VM_getsurfacepoint); + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + // note: this (incorrectly) assumes it is a simple polygon + pointnum = (int)PRVM_G_FLOAT(OFS_PARM2); + if (pointnum < 0 || pointnum >= surface->num_vertices) + return; + animatemodel(model, ed); + applytransform_forward(&(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN)); +} +//PF_getsurfacepointattribute, // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; +// float SPA_POSITION = 0; +// float SPA_S_AXIS = 1; +// float SPA_T_AXIS = 2; +// float SPA_R_AXIS = 3; // same as SPA_NORMAL +// float SPA_TEXCOORDS0 = 4; +// float SPA_LIGHTMAP0_TEXCOORDS = 5; +// float SPA_LIGHTMAP0_COLOR = 6; +void VM_getsurfacepointattribute(void) +{ + prvm_edict_t *ed; + dp_model_t *model; + msurface_t *surface; + int pointnum; + int attributetype; + + VM_SAFEPARMCOUNT(4, VM_getsurfacepoint); + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + pointnum = (int)PRVM_G_FLOAT(OFS_PARM2); + if (pointnum < 0 || pointnum >= surface->num_vertices) + return; + attributetype = (int) PRVM_G_FLOAT(OFS_PARM3); + + animatemodel(model, ed); + + switch( attributetype ) { + // float SPA_POSITION = 0; + case 0: + applytransform_forward(&(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN)); + break; + // float SPA_S_AXIS = 1; + case 1: + applytransform_forward_direction(&(animatemodel_cache.data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN)); + break; + // float SPA_T_AXIS = 2; + case 2: + applytransform_forward_direction(&(animatemodel_cache.data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN)); + break; + // float SPA_R_AXIS = 3; // same as SPA_NORMAL + case 3: + applytransform_forward_direction(&(animatemodel_cache.data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN)); + break; + // float SPA_TEXCOORDS0 = 4; + case 4: { + float *ret = PRVM_G_VECTOR(OFS_RETURN); + float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2]; + ret[0] = texcoord[0]; + ret[1] = texcoord[1]; + ret[2] = 0.0f; + break; + } + // float SPA_LIGHTMAP0_TEXCOORDS = 5; + case 5: { + float *ret = PRVM_G_VECTOR(OFS_RETURN); + float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2]; + ret[0] = texcoord[0]; + ret[1] = texcoord[1]; + ret[2] = 0.0f; + break; + } + // float SPA_LIGHTMAP0_COLOR = 6; + case 6: + // ignore alpha for now.. + VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN)); + break; + default: + VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f ); + break; + } +} +//PF_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal = #436; +void VM_getsurfacenormal(void) +{ + dp_model_t *model; + msurface_t *surface; + vec3_t normal; + VM_SAFEPARMCOUNT(2, VM_getsurfacenormal); + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + // note: this only returns the first triangle, so it doesn't work very + // well for curved surfaces or arbitrary meshes + animatemodel(model, PRVM_G_EDICT(OFS_PARM0)); + TriangleNormal((animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex), (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 3, (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 6, normal); + applytransform_forward_normal(normal, PRVM_G_EDICT(OFS_PARM0), PRVM_G_VECTOR(OFS_RETURN)); + VectorNormalize(PRVM_G_VECTOR(OFS_RETURN)); +} +//PF_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture = #437; +void VM_getsurfacetexture(void) +{ + dp_model_t *model; + msurface_t *surface; + VM_SAFEPARMCOUNT(2, VM_getsurfacetexture); + PRVM_G_INT(OFS_RETURN) = OFS_NULL; + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name); +} +//PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438; +void VM_getsurfacenearpoint(void) +{ + int surfacenum, best; + vec3_t clipped, p; + vec_t dist, bestdist; + prvm_edict_t *ed; + dp_model_t *model; + msurface_t *surface; + vec_t *point; + VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint); + PRVM_G_FLOAT(OFS_RETURN) = -1; + ed = PRVM_G_EDICT(OFS_PARM0); + point = PRVM_G_VECTOR(OFS_PARM1); + + if (!ed || ed->priv.server->free) + return; + model = getmodel(ed); + if (!model || !model->num_surfaces) + return; + + animatemodel(model, ed); + + applytransform_inverted(point, ed, p); + best = -1; + bestdist = 1000000000; + for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++) + { + surface = model->data_surfaces + surfacenum + model->firstmodelsurface; + // first see if the nearest point on the surface's box is closer than the previous match + clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0]; + clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1]; + clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2]; + dist = VectorLength2(clipped); + if (dist < bestdist) + { + // it is, check the nearest point on the actual geometry + clippointtosurface(ed, model, surface, p, clipped); + VectorSubtract(clipped, p, clipped); + dist += VectorLength2(clipped); + if (dist < bestdist) + { + // that's closer too, store it as the best match + best = surfacenum; + bestdist = dist; + } + } + } + PRVM_G_FLOAT(OFS_RETURN) = best; +} +//PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; +void VM_getsurfaceclippedpoint(void) +{ + prvm_edict_t *ed; + dp_model_t *model; + msurface_t *surface; + vec3_t p, out; + VM_SAFEPARMCOUNT(3, VM_te_getsurfaceclippedpoint); + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + animatemodel(model, ed); + applytransform_inverted(PRVM_G_VECTOR(OFS_PARM2), ed, p); + clippointtosurface(ed, model, surface, p, out); + VectorAdd(out, ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN)); +} + +//PF_getsurfacenumtriangles, // #??? float(entity e, float s) getsurfacenumtriangles = #???; +void VM_getsurfacenumtriangles(void) +{ + dp_model_t *model; + msurface_t *surface; + VM_SAFEPARMCOUNT(2, VM_SV_getsurfacenumtriangles); + // return 0 if no such surface + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + + // note: this (incorrectly) assumes it is a simple polygon + PRVM_G_FLOAT(OFS_RETURN) = surface->num_triangles; +} +//PF_getsurfacetriangle, // #??? vector(entity e, float s, float n) getsurfacetriangle = #???; +void VM_getsurfacetriangle(void) +{ + const vec3_t d = {-1, -1, -1}; + prvm_edict_t *ed; + dp_model_t *model; + msurface_t *surface; + int trinum; + VM_SAFEPARMCOUNT(3, VM_SV_getsurfacetriangle); + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) + return; + trinum = (int)PRVM_G_FLOAT(OFS_PARM2); + if (trinum < 0 || trinum >= surface->num_triangles) + return; + // FIXME: implement rotation/scaling + VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN)); +}