X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=prvm_cmds.c;h=83eb617c73ac90abb894f94d638d3d23101514ae;hp=69db32caaffddbc6e967c4aca295c1e4c9d3a350;hb=406539417c894954ef88ecc2fc28c26ad01bbba6;hpb=cd9227cae3c1619c304381491597710ec259d0f5 diff --git a/prvm_cmds.c b/prvm_cmds.c index 69db32ca..83eb617c 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; @@ -189,11 +191,11 @@ void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const f ed->priv.server->skeleton.relativetransforms = 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); } } @@ -2876,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 @@ -3344,7 +3390,7 @@ void VM_drawcolorcodedstring(void) { float *pos,*scale; const char *string; - int flag,color; + int flag; float sx, sy; VM_SAFEPARMCOUNT(5,VM_drawstring); @@ -3370,7 +3416,6 @@ 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; 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; @@ -3755,7 +3800,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; @@ -4344,18 +4389,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++ ) @@ -4440,19 +4483,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 @@ -5042,7 +5100,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) @@ -5057,7 +5115,10 @@ void VM_str2chr (void) if((unsigned)index < strlen(s)) { - ch = u8_getchar_noendptr(s + index); + if (utf8_enable.integer) + ch = u8_getchar_noendptr(s + index); + else + ch = (unsigned char)s[index]; PRVM_G_FLOAT(OFS_RETURN) = ch; } else @@ -5422,7 +5483,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 @@ -5556,7 +5617,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; @@ -5600,7 +5661,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]); } } @@ -5615,10 +5676,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 @@ -5706,6 +5787,9 @@ void VM_sprintf(void) char formatbuf[16]; char *f; qboolean 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 @@ -5717,7 +5801,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(;;) @@ -5727,7 +5813,6 @@ void VM_sprintf(void) { case 0: goto finished; - break; case '%': ++s; @@ -5802,6 +5887,11 @@ noflags: else width = argpos++; width = GETARG_FLOAT(width); + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } } else if(*s >= '0' && *s <= '9') { @@ -5812,12 +5902,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 == '.') @@ -5893,24 +5984,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))); @@ -5924,7 +6029,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)); @@ -5949,3 +6054,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))); + 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))); + 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 = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_svector3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_tvector3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_normal3f = 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(&m, &n); + 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)); +}