X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=prvm_cmds.c;h=48184e519169af58496829b94490ea17e18193e2;hb=a7160f8d92d7278b9292087401847c18841852f4;hp=20f75b55170d99ce576a608b5b05fe9c76d101a7;hpb=636c2207a7feebbc93dacfbe1e4626df48c0269b;p=xonotic%2Fdarkplaces.git diff --git a/prvm_cmds.c b/prvm_cmds.c index 20f75b55..48184e51 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)); @@ -3344,7 +3346,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 +3372,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; @@ -4344,18 +4345,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++ ) @@ -5603,7 +5602,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]); } } @@ -5618,7 +5617,7 @@ 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]); } } @@ -5730,7 +5729,6 @@ void VM_sprintf(void) { case 0: goto finished; - break; case '%': ++s; @@ -5805,6 +5803,11 @@ noflags: else width = argpos++; width = GETARG_FLOAT(width); + if(width < 0) + { + flags |= PRINTF_LEFT; + width = -width; + } } else if(*s >= '0' && *s <= '9') { @@ -5815,12 +5818,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 == '.') @@ -5896,24 +5900,28 @@ 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 '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))); @@ -5927,7 +5935,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)); @@ -5952,3 +5960,424 @@ 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) +{ + 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 + VectorCopy(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], PRVM_G_VECTOR(OFS_RETURN)); +}