openxr plugin: tweaked - inputs should be working properly now, and are visible to csqc. subject to further breaking changes, however.
_pext_vrinputs: added cvar to enable vr inputs protocol extension allowing vr inputs to be networked to ssqc too. defaults to 0 for now, will be renamed when deemed final. updates menu: the prompt to enable sources is now more explicit instead of expecting the user to have a clue. updates menu: added a v3 sources format, which should be more maintainable. not final. updates menu: try to give reasons why sources might be failing (to help blame ISPs if they try fucking over TTH dns again). presets menu: no longer closes the instant a preset is chosen. some presets have a couple of modifiers listed. force the demo loop in the background to serve as a preview. prompts menus: now does word wrapping. ftemaster: support importing server lists from other master servers (requested by Eukara). server: try to detect when non-reply inbound packets are blocked by firewalls/nats/etc (using ftemaster to do so). qcvm: added pointcontentsmask builtin, allowing it to probe more than just world, with fte's full contentbit range instead of just q1 legacy. qcvm: memfill8 builtin now works on createbuffer() pointers. qcvm: add missing unsigned ops. Fixed double comparison ops. fixed bug with op_store_i64. added missing OP_LOADP_I64 qcc: added '#pragma framerate RATE' for overriding implicit nextthink durations. qcc: fixed '#pragma DONT_COMPILE_THIS_FILE' to not screw up comments. qcc: added __GITURL__ __GITHASH__ __GITDATE__ __GITDATETIME__ __GITDESC__ for any mods that might want to make use of that. qcc: fix up -Fhashonly a little setrenderer: support for vulkan gpu enumeration. rulesets: reworked to support custom rulesets (using hashes to catch haxxors, though still nothing prevents just changing the client to ignore rulesets) bspx: use our BIH code for the bspx BRUSHLIST lump instead of the older less efficient code. (static)iqm+obj: these model formats can now be used for the worldmodel (with a suitable .ent file). Also using BIH for much better collision performance. pmove: tried to optimise PM_NudgePosition, should boost fps in stress tests. wayland: fix a crash on startup. mousegrabs now works better. imagetool: uses sdl for previews. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5813 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
cd50a54a5a
commit
b9cd6ec91b
160 changed files with 13078 additions and 5542 deletions
131
quakec/csaddon/src/brush_draw.qc
Normal file
131
quakec/csaddon/src/brush_draw.qc
Normal file
|
@ -0,0 +1,131 @@
|
|||
void(int mod, int id) DrawEngineBrushWireframe =
|
||||
{
|
||||
const vector col = '1 0 0';
|
||||
for(int facenum = 0;;)
|
||||
{
|
||||
int points = brush_getfacepoints(mod, id, ++facenum, &facepoints[0], MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
break; //end of face list, I guess
|
||||
|
||||
R_BeginPolygon("chop");
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
for (int point = 1; point < points; point++)
|
||||
{
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
}
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
R_EndPolygon();
|
||||
}
|
||||
};
|
||||
|
||||
void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushWireframe =
|
||||
{
|
||||
int f;
|
||||
int point, points;
|
||||
for(f = 0; f < numfaces;)
|
||||
{
|
||||
points = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
continue; //should probably warn somehow about this
|
||||
R_BeginPolygon(shader);
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, alpha);
|
||||
for (point = 0; point < points-1; )
|
||||
{
|
||||
point++;
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
|
||||
}
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
}
|
||||
};
|
||||
void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushSolid =
|
||||
{
|
||||
int f;
|
||||
int point, points;
|
||||
for(f = 0; f < numfaces;)
|
||||
{
|
||||
points = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
continue; //should probably warn somehow about this
|
||||
R_BeginPolygon(shader);
|
||||
for (point = 0; point < points; point++)
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
}
|
||||
};
|
||||
void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextured =
|
||||
{
|
||||
int f;
|
||||
int point, points;
|
||||
for(f = 0; f < numfaces; f++)
|
||||
{
|
||||
points = brush_calcfacepoints(1+f, faces, numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (points)
|
||||
{
|
||||
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.
|
||||
//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.
|
||||
//FIXME: we don't manage to pick up the size of the original wad image
|
||||
shaderforname(faces[f].shadername,
|
||||
sprintf("{"
|
||||
"{\n"
|
||||
"map \"%s\"\n"
|
||||
"rgbgen vertex\n"
|
||||
"alphagen vertex\n"
|
||||
"}\n"
|
||||
"}", faces[f].shadername));
|
||||
|
||||
vector sz = drawgetimagesize(faces[f].shadername);
|
||||
R_BeginPolygon(faces[f].shadername);
|
||||
for (point = 0; point < points; point++)
|
||||
R_PolygonVertex(facepoints[point], [(facepoints[point] * faces[f].sdir + faces[f].sbias)/sz_x, (facepoints[point] * faces[f].tdir + faces[f].tbias)/sz_y], col, alpha);
|
||||
R_EndPolygon();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void(vector *p, int points, string shader, vector col, float alpha) DrawAxisExtensions =
|
||||
{
|
||||
R_BeginPolygon(shader);
|
||||
for (int point = 0; point < points; point++)
|
||||
{
|
||||
R_PolygonVertex(p[point] + [ 64, 0, 0], '0 0', col, alpha);
|
||||
R_PolygonVertex(p[point] + [-64, 0, 0], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(p[point] + [0, 64, 0], '0 0', col, alpha);
|
||||
R_PolygonVertex(p[point] + [0, -64, 0], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(p[point] + [0, 0, 64], '0 0', col, alpha);
|
||||
R_PolygonVertex(p[point] + [0, 0, -64], '0 0', col, alpha);
|
||||
R_EndPolygon();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void(int brushid) editor_drawbbox =
|
||||
{
|
||||
static vector bbox[2];
|
||||
int p = brush_getfacepoints(selectedbrushmodel, brushid, 0, bbox, bbox.length);
|
||||
if (p == 2)
|
||||
{
|
||||
R_BeginPolygon("chop");
|
||||
#define line(x,y) R_PolygonVertex(x, '0 0', '1 0 0', 1); R_PolygonVertex(y, '0 0', '1 0 0', 1); R_EndPolygon()
|
||||
line(bbox[0], ([bbox[1][0], bbox[0][1], bbox[0][2]]));
|
||||
line(bbox[0], ([bbox[0][0], bbox[1][1], bbox[0][2]]));
|
||||
line(bbox[0], ([bbox[0][0], bbox[0][1], bbox[1][2]]));
|
||||
line(bbox[1], ([bbox[0][0], bbox[1][1], bbox[1][2]]));
|
||||
line(bbox[1], ([bbox[1][0], bbox[0][1], bbox[1][2]]));
|
||||
line(bbox[1], ([bbox[1][0], bbox[1][1], bbox[0][2]]));
|
||||
|
||||
line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));
|
||||
line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));
|
||||
line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));
|
||||
line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));
|
||||
line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));
|
||||
line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));
|
||||
#undef line
|
||||
}
|
||||
};
|
199
quakec/csaddon/src/brush_history.qc
Normal file
199
quakec/csaddon/src/brush_history.qc
Normal file
|
@ -0,0 +1,199 @@
|
|||
//history is implemented using a ringbuffer
|
||||
typedef struct
|
||||
{
|
||||
float timestamp;
|
||||
int brushmodel;
|
||||
int wasdelete;
|
||||
int id;
|
||||
brushface_t *brushdata; //list of faces
|
||||
patchvert_t *patchdata; //list of control points
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int numfaces;
|
||||
int contents;
|
||||
} brushinfo;
|
||||
patchinfo_t patchinfo;
|
||||
};
|
||||
} history_t;
|
||||
history_t *historyring;
|
||||
int historyring_length;
|
||||
int history_min; //oldest we can go
|
||||
int history; //updated on each change
|
||||
int history_max; //max value for redo
|
||||
|
||||
//when replaying history, ids get changed, which makes things fun. this updates selection state for edited brushes.
|
||||
void(int modelidx, int old, int new) history_remap =
|
||||
{
|
||||
for (int i = history_min; i < history_max; i++)
|
||||
{
|
||||
history_t *h = &historyring[i & (historyring_length-1)];
|
||||
if (h->id == old)
|
||||
h->id = new;
|
||||
}
|
||||
|
||||
int i = brush_isselected(modelidx, old);
|
||||
if (i)
|
||||
{
|
||||
i--;
|
||||
brush_selected(modelidx, old, -1, FALSE);
|
||||
brush_selected(modelidx, new, -1, TRUE);
|
||||
selectedbrushes[i].id = new;
|
||||
}
|
||||
};
|
||||
|
||||
void() brush_undo =
|
||||
{
|
||||
if (history == history_min)
|
||||
print("Nothing to undo.\n");
|
||||
do
|
||||
{
|
||||
if (history <= history_min)
|
||||
return;
|
||||
history--;
|
||||
|
||||
history_t *h = &historyring[history & (historyring_length-1)];
|
||||
|
||||
//we're undoing, so deletes create and creates delete. weird, yes.
|
||||
if (h->wasdelete)
|
||||
{
|
||||
int newid;
|
||||
if (h->patchdata)
|
||||
newid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);
|
||||
else
|
||||
newid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
|
||||
history_remap(h->brushmodel, h->id, newid);
|
||||
h->id = newid;
|
||||
}
|
||||
else
|
||||
{
|
||||
brush_delete(h->brushmodel, h->id);
|
||||
h->id = 0;
|
||||
}
|
||||
if (history == history_min)
|
||||
break; //as far back as we can go, more timestamps are invalid
|
||||
} while (historyring[(history-1) & (historyring_length-1)].timestamp == h->timestamp);
|
||||
};
|
||||
void() brush_redo =
|
||||
{
|
||||
if (history == history_max)
|
||||
print("Nothing to redo.");
|
||||
do
|
||||
{
|
||||
if (history >= history_max)
|
||||
return;
|
||||
|
||||
history_t *h = &historyring[history & (historyring_length-1)];
|
||||
|
||||
//we're redoing stuff that has previously been done. yay.
|
||||
if (h->wasdelete)
|
||||
{
|
||||
brush_delete(h->brushmodel, h->id);
|
||||
h->id = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int newid;
|
||||
if (h->patchdata)
|
||||
newid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);
|
||||
else
|
||||
newid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
|
||||
history_remap(h->brushmodel, h->id, newid);
|
||||
h->id = newid;
|
||||
}
|
||||
history++;
|
||||
if (history == history_max)
|
||||
return; //don't poke beyond the end...
|
||||
} while (historyring[history & (historyring_length-1)].timestamp == h->timestamp);
|
||||
};
|
||||
|
||||
history_t *() history_allocate = //returns a new history entry.
|
||||
{
|
||||
if (!historyring)
|
||||
{
|
||||
history_min = history_max = history;
|
||||
historyring_length = 512;
|
||||
historyring = memalloc(sizeof(*historyring)*historyring_length);
|
||||
}
|
||||
history_t *h = &historyring[history & (historyring_length-1)];
|
||||
|
||||
history++;
|
||||
history_max = history; //always break any pending redos
|
||||
if (history_min < history_max - historyring_length)
|
||||
history_min = history_max - historyring_length;
|
||||
|
||||
h->timestamp = time;
|
||||
memfree(h->brushdata);
|
||||
h->brushdata = __NULL__;
|
||||
return h;
|
||||
};
|
||||
void(history_t *h) history_deallocate = //cancels a newly created history entry, in case something went wrong.
|
||||
{
|
||||
history--;
|
||||
if (h == &historyring[history & (historyring_length-1)])
|
||||
history_max--;
|
||||
};
|
||||
//create and journal.
|
||||
int(int mod, brushface_t *faces, int numfaces, int contents, float selectnow) brush_history_create =
|
||||
{
|
||||
history_t *h = history_allocate();
|
||||
|
||||
h->brushdata = memalloc(sizeof(brushface_t) * numfaces);
|
||||
memcpy(h->brushdata, faces, sizeof(brushface_t) * numfaces);
|
||||
h->brushinfo.numfaces = numfaces;
|
||||
h->brushinfo.contents = contents;
|
||||
h->brushmodel = mod;
|
||||
h->wasdelete = FALSE;
|
||||
|
||||
h->id = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
|
||||
|
||||
if (!h->id)
|
||||
history_deallocate(h);
|
||||
else if (selectnow)
|
||||
brush_select(mod, h->id);
|
||||
return h->id;
|
||||
};
|
||||
//delete and journal.
|
||||
void(int mod, int id) brush_history_delete =
|
||||
{
|
||||
int numfaces = brush_get(mod, id, __NULL__, 0, __NULL__);
|
||||
int numcp = patch_getcp(mod, id, __NULL__, 0, __NULL__);
|
||||
brush_deselect(mod, id);
|
||||
|
||||
if (!numfaces && !numcp)
|
||||
return; //doesn't exist or something, some kind of error. we can't log a delete if it didn't delete anything, if only because we can't recreate it if its undone.
|
||||
|
||||
history_t *h = history_allocate();
|
||||
|
||||
h->brushmodel = mod;
|
||||
h->id = id;
|
||||
h->wasdelete = TRUE;
|
||||
|
||||
if (numcp)
|
||||
{
|
||||
h->patchdata = memalloc(sizeof(patchvert_t) * numcp);
|
||||
patch_getcp(mod, id, h->patchdata, numcp, &h->patchinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
h->brushdata = memalloc(sizeof(brushface_t) * numfaces);
|
||||
h->brushinfo.numfaces = brush_get(mod, id, h->brushdata, numfaces, &h->brushinfo.contents);
|
||||
}
|
||||
brush_delete(mod, id);
|
||||
};
|
||||
|
||||
int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_history_edit =
|
||||
{
|
||||
int wasselected = brush_isselected(mod, id);
|
||||
if (wasselected)
|
||||
selectedbrushes[wasselected-1].id = -id; //hack so that brush_history_delete won't remove this entry.
|
||||
brush_history_delete(mod, id);
|
||||
id = brush_history_create(mod, faces, numfaces, contents, FALSE);
|
||||
if (wasselected)
|
||||
{
|
||||
selectedbrushes[wasselected-1].id = id;
|
||||
brush_selected(mod, id, -1, TRUE);
|
||||
}
|
||||
return id;
|
||||
};
|
430
quakec/csaddon/src/brush_manip.qc
Normal file
430
quakec/csaddon/src/brush_manip.qc
Normal file
|
@ -0,0 +1,430 @@
|
|||
|
||||
//basic cube plane normals, for selection.
|
||||
static nosave const vector axis[6] = {
|
||||
'-1 0 0',
|
||||
'0 -1 0',
|
||||
'0 0 -1',
|
||||
'1 0 0',
|
||||
'0 1 0',
|
||||
'0 0 1'
|
||||
};
|
||||
float dists[6];
|
||||
|
||||
|
||||
//generates default quakeed-style texture mapping for the given surface.
|
||||
//this sucks for cylinders, but keeps slopes and things easy.
|
||||
void(brushface_t *fa) reset_texturecoords =
|
||||
{
|
||||
//figure out some default texture coords
|
||||
fa->sdir = '0 0 0';
|
||||
fa->sbias = 0;
|
||||
fa->tdir = '0 0 0';
|
||||
fa->tbias = 0;
|
||||
float a=fabs(fa->planenormal[0]),b=fabs(fa->planenormal[1]),c=fabs(fa->planenormal[2]);
|
||||
if (a>=b&&a>=c)
|
||||
fa->sdir[1] = 1;
|
||||
else
|
||||
fa->sdir[0] = 1;
|
||||
if (c>a&&c>b)
|
||||
fa->tdir[1] = -1;
|
||||
else
|
||||
fa->tdir[2] = -1;
|
||||
};
|
||||
|
||||
int(brushface_t *fa, int famax, vector *points, int numpoints, float height) BrushFromPoints =
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
fa->planenormal = normalize(crossproduct(points[2] - points[0], points[1] - points[0]));
|
||||
fa->planedist = bt_point[0] * fa->planenormal + height;
|
||||
fa->shadername = autocvar_ca_newbrushtexture;
|
||||
reset_texturecoords(fa);
|
||||
fa++;
|
||||
|
||||
fa->planenormal = -normalize(crossproduct(points[2] - points[0], points[1] - points[0]));
|
||||
fa->planedist = (bt_point[0] * fa->planenormal);
|
||||
fa->shadername = autocvar_ca_newbrushtexture;
|
||||
reset_texturecoords(fa);
|
||||
fa++;
|
||||
count = 2;
|
||||
|
||||
for (int p = 0; p < numpoints; p++)
|
||||
{
|
||||
int n = p + 1;
|
||||
if (n == numpoints)
|
||||
n = 0;
|
||||
fa->planenormal = normalize(crossproduct(points[p] - bt_point[n], tmp.faces[0].planenormal));
|
||||
fa->planedist = points[p] * fa->planenormal;
|
||||
fa->shadername = autocvar_ca_newbrushtexture;
|
||||
reset_texturecoords(fa);
|
||||
fa++;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
vector(vector guess) brush_snappoint =
|
||||
{
|
||||
if (nogrid)
|
||||
return guess;
|
||||
int facenum, points;
|
||||
float bestdist = autocvar_ca_grid*autocvar_ca_grid; //worst case value so we don't snap to grid when there's a vertex 0.001qu away from the grid.
|
||||
vector best = guess * (1.0/autocvar_ca_grid);
|
||||
best_x = rint(best_x); //snap to grid
|
||||
best_y = rint(best_y);
|
||||
best_z = rint(best_z);
|
||||
best *= autocvar_ca_grid;
|
||||
|
||||
//find surfaces within 32qu of the point (via plane volume). use a tetrahedron instead if you want something more circular
|
||||
for (facenum = 0; facenum < axis.length; facenum++)
|
||||
dists[facenum] = (guess * axis[facenum]) + autocvar_ca_grid;
|
||||
int numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length);
|
||||
|
||||
for (int brushnum = 0; brushnum < numbrushes; brushnum++)
|
||||
{
|
||||
for (facenum = 0; ; )
|
||||
{
|
||||
points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, facepoints, MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
break; //end of face list, I guess
|
||||
|
||||
//walk the faces we found and use the point if its nearer than our previous guess.
|
||||
for (int point = 0; point < points; point++)
|
||||
{
|
||||
vector disp = facepoints[point] - guess;
|
||||
float dist = disp*disp;
|
||||
if (dist < bestdist)
|
||||
best = facepoints[point];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//move a brush so that its planes all move without any translations in positions or texcoords
|
||||
void brushface_translate(brushface_t *fa, int numfaces, vector displacement)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < numfaces; i++, fa++)
|
||||
{
|
||||
fa->planedist += fa->planenormal * displacement;
|
||||
fa->sbias -= fa->sdir * displacement;
|
||||
fa->tbias -= fa->tdir * displacement;
|
||||
}
|
||||
};
|
||||
//rotates a list of faces by the current v_* vectors, around the origin.
|
||||
//translate before+after first in order to deal with pivots.
|
||||
void brushface_rotate(brushface_t *fa, int numfaces)
|
||||
{
|
||||
for (int i = 0; i < numfaces; i++, fa++)
|
||||
{
|
||||
vector orig = fa->planenormal;
|
||||
fa->planenormal[0] = orig * v_forward;
|
||||
fa->planenormal[1] = orig * -v_right; //quake just isn't right...
|
||||
fa->planenormal[2] = orig * v_up;
|
||||
|
||||
orig = fa->sdir;
|
||||
fa->sdir[0] = orig * v_forward;
|
||||
fa->sdir[1] = orig * -v_right; //quake just isn't right...
|
||||
fa->sdir[2] = orig * v_up;
|
||||
|
||||
orig = fa->tdir;
|
||||
fa->tdir[0] = orig * v_forward;
|
||||
fa->tdir[1] = orig * -v_right; //quake just isn't right...
|
||||
fa->tdir[2] = orig * v_up;
|
||||
}
|
||||
};
|
||||
|
||||
vector(vector dir) axialize =
|
||||
{
|
||||
vector a;
|
||||
a_x = fabs(dir_x);
|
||||
a_y = fabs(dir_y);
|
||||
a_z = fabs(dir_z);
|
||||
if (a_x > a_y && a_x > a_z)
|
||||
return (dir_x > 0)?[1,0,0]:[-1,0,0];
|
||||
if (a_y > a_x && a_y > a_z)
|
||||
return (dir_y > 0)?[0,1,0]:[0,-1,0];
|
||||
return (dir_z > 0)?[0,0,1]:[0,0,-1];
|
||||
};
|
||||
|
||||
vector(vector in) channelizeangle =
|
||||
{
|
||||
in_x = anglemod(in_x);
|
||||
in_y = anglemod(in_y);
|
||||
in_z = anglemod(in_z);
|
||||
if (in_x > 180)
|
||||
in_x -= 360;
|
||||
if (in_y > 180)
|
||||
in_y -= 360;
|
||||
if (in_z > 180)
|
||||
in_z -= 360;
|
||||
|
||||
float fx = fabs(in_x);
|
||||
float fy = fabs(in_y);
|
||||
float fz = fabs(in_z);
|
||||
cprint(sprintf("%v", in));
|
||||
if (fx > fy && fx > fz)
|
||||
return [in_x,0,0];
|
||||
if (fy > fz)
|
||||
return [0,in_y,0];
|
||||
return [0,0,in_z];
|
||||
}
|
||||
|
||||
vector(vector p1, vector p2, vector norm, float dist) planelinepoint =
|
||||
{
|
||||
float d1 = p1*norm - dist;
|
||||
float d2 = p2*norm - dist;
|
||||
float frac = d1 / (d2-d1);
|
||||
|
||||
// frac = bound(0, frac, 1);
|
||||
|
||||
return p2 + (p1-p2)*frac; //convert that frac into an actual position
|
||||
};
|
||||
|
||||
|
||||
int(brushface_t *faces, int numfaces, vector *points, int numpoints) isconcave =
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
//if any of the points are outside the brush, then we know that one of the planes cut straight through in a concavey kind of way
|
||||
for(int f = 0; f < numfaces; f++)
|
||||
{
|
||||
vector n = faces[f].planenormal;
|
||||
float d = faces[f].planedist + EPSILON; //epsilon to cover precision issues
|
||||
for (int p = 0; p < numpoints; p++)
|
||||
{
|
||||
if (points[p] * n > d)
|
||||
{
|
||||
result++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
//returns the resulting brush id
|
||||
int(int model, int brush1, int brush2, int face1, int face2) mergebrushes =
|
||||
{
|
||||
int extrafaces;
|
||||
float found = FALSE;
|
||||
brushface_t *fa;
|
||||
brushface_t *infa;
|
||||
int i;
|
||||
|
||||
if (brush1 == brush2)
|
||||
{
|
||||
print("cannot merge brush with itself\n");
|
||||
return 0;
|
||||
}
|
||||
if (!brush1 || !brush2)
|
||||
{
|
||||
print("no brush targetted\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (patch_getcp(model, brush1, __NULL__, 0, __NULL__) || patch_getcp(model, brush2, __NULL__, 0, __NULL__))
|
||||
{ //fixme: should be possible if they're reasonably adjacent with the same numbers of CPs
|
||||
print("unable to merge patches\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp.numfaces = brush_get(model, brush1, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
tmp.numcp = 0;
|
||||
infa = &tmp.faces[tmp.numfaces];
|
||||
extrafaces = brush_get(model, brush2, &tmp.faces[tmp.numfaces], tmp.faces.length-tmp.numfaces, &tmp.contents);
|
||||
|
||||
//merge the two sets of planes together.
|
||||
for(infa = &tmp.faces[tmp.numfaces]; extrafaces > 0; infa++, extrafaces--)
|
||||
{
|
||||
for (fa = tmp.faces, i = 0; i < tmp.numfaces; i++, fa++)
|
||||
{
|
||||
//fixme: needs some tolerance / epsilon
|
||||
if (fa->planenormal == -infa->planenormal && fa->planedist == -infa->planedist)
|
||||
{
|
||||
//inverted. this is the plane we're merging over, so strip it out
|
||||
memcpy(fa, &fa[1], sizeof(brushface_t) * ((tmp.numfaces-i)-1));
|
||||
tmp.numfaces--;
|
||||
i--;
|
||||
fa--;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
else if (fa->planenormal * infa->planenormal > 0.999 && fa->planedist == infa->planedist)
|
||||
{
|
||||
//print("coplanar\n");
|
||||
//coplanar surfaces are considered safe to ignore. we use the selected brush's texturing info
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == tmp.numfaces)
|
||||
{ //okay, this plane is important, merge it into the selected brush.
|
||||
//print("merge plane\n");
|
||||
memcpy(fa, infa, sizeof(brushface_t));
|
||||
tmp.numfaces++;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
print("Brushes do not share a plane\n");
|
||||
#if 0
|
||||
//try to find a surface to move to match to the given plane
|
||||
float val;
|
||||
float bestval = -0.707; //must be within 45 degrees
|
||||
int bestface = -1;
|
||||
tmp_numfaces = brush_get(model, brush1, tmp_faces, tmp_faces.length, &tmp_contents);
|
||||
brush_get(model, brush2, &tmp_faces[tmp_numfaces], tmp_faces.length-tmp_numfaces, &tmp_contents);
|
||||
infa = &tmp_faces[tmp_numfaces + face2-1i];
|
||||
for (i = 0; i < tmp_numfaces; i++)
|
||||
{
|
||||
val = tmp_faces[i].planenormal * infa->planenormal;
|
||||
if (val < bestval)
|
||||
{
|
||||
bestval = val;
|
||||
bestface = i;
|
||||
}
|
||||
}
|
||||
if (bestface != -1)
|
||||
{
|
||||
//FIXME: determine volume and reject it if we shrink?
|
||||
tmp_faces[bestface].planenormal = -infa->planenormal;
|
||||
tmp_faces[bestface].planedist = -infa->planedist;
|
||||
|
||||
// if (isconcave(tmp_faces, tmp_numfaces))
|
||||
// {
|
||||
// print("Resulting brush would be concave\n");
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
brush_history_delete(model, brush1);
|
||||
return brush_history_create(model, tmp_faces, tmp_numfaces, tmp_contents);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
vector *points = memalloc(sizeof(vector)*64*64);
|
||||
int numpoints = 0, f;
|
||||
for(f = 0; (i = brush_getfacepoints(model, brush1, ++f, points+numpoints, 64*64-numpoints)); )
|
||||
numpoints += i;
|
||||
for(f = 0; (i = brush_getfacepoints(model, brush2, ++f, points+numpoints, 64*64-numpoints)); )
|
||||
numpoints += i;
|
||||
|
||||
if (isconcave(tmp.faces, tmp.numfaces, points, numpoints))
|
||||
{
|
||||
print("Resulting brush would be concave\n");
|
||||
memfree(points);
|
||||
return 0;
|
||||
}
|
||||
memfree(points);
|
||||
|
||||
//FIXME: verify that no planes got dropped, as this indicates that the region wasn't convex, and we probably just destroyed the entire thing.
|
||||
brush_history_delete(model, brush1);
|
||||
brush_history_delete(model, brush2);
|
||||
|
||||
return brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
void(vector org, vector ang) editor_brushes_simpleclone =
|
||||
{
|
||||
vector midpoint;
|
||||
if (!selectedbrush)
|
||||
return;
|
||||
|
||||
tmp.numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
if (ang != '0 0 0')
|
||||
{
|
||||
brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1);
|
||||
brushface_translate(tmp.faces, tmp_numfaces, -midpoint);
|
||||
makevectors(ang);
|
||||
brushface_rotate(tmp_faces, tmp_numfaces);
|
||||
brushface_translate(tmp.faces, tmp_numfaces, midpoint + org);
|
||||
}
|
||||
else
|
||||
brushface_translate(tmp.faces, tmp_numfaces, org);
|
||||
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
|
||||
};
|
||||
*/
|
||||
|
||||
void() brushedit_subtract =
|
||||
{
|
||||
int discard;
|
||||
vector selnormals[tmp.faces.length];
|
||||
float seldists[tmp.faces.length];
|
||||
|
||||
for (int sb = 0; sb < selectedbrushcount; sb++)
|
||||
{
|
||||
int mod = selectedbrushes[sb].model;
|
||||
int brush = selectedbrushes[sb].id;
|
||||
int planecount = brush_get(mod, brush, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
if (!planecount)
|
||||
continue; //csg can't subtract patches
|
||||
for (int i = 0; i < planecount; i++)
|
||||
{
|
||||
selnormals[i] = tmp.faces[i].planenormal;
|
||||
seldists[i] = tmp.faces[i].planedist;
|
||||
}
|
||||
int numbrushes = brush_findinvolume(mod, selnormals, seldists, planecount, brushlist, __NULL__, brushlist.length);
|
||||
|
||||
while (numbrushes --> 0)
|
||||
{
|
||||
int br = brushlist[numbrushes];
|
||||
|
||||
if (brush_isselected(mod, br))
|
||||
continue; //ignore other selected brushes. race conditions suck
|
||||
|
||||
int counto = brush_get(mod, br, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
if (!counto)
|
||||
continue; //don't cut into patches.
|
||||
int counts = counto + brush_get(mod, brush, tmp.faces+counto, tmp.faces.length-counto, &discard);
|
||||
|
||||
brush_history_delete(mod, br);
|
||||
while(counts --> counto)
|
||||
{
|
||||
//only consider the resulting brush if the new face actually contributed anything.
|
||||
//this reduces dupes.
|
||||
if (brush_calcfacepoints(1+counts, tmp.faces, counts+1, facepoints, MAX_FACEPOINTS))
|
||||
{
|
||||
//determine the brush defined by this plane
|
||||
tmp.faces[counts].planenormal *= -1;
|
||||
tmp.faces[counts].planedist *= -1;
|
||||
brush_history_create(mod, tmp.faces, counts+1, tmp.contents, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void() brushedit_resettextures =
|
||||
{
|
||||
for (int sb = 0; sb < selectedbrushcount; sb++)
|
||||
{
|
||||
int model = selectedbrushes[sb].model;
|
||||
int brush = selectedbrushes[sb].id;
|
||||
int face = selectedbrushes[sb].face;
|
||||
|
||||
int planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
if (planecount)
|
||||
{
|
||||
for (int i = 0; i < planecount; i++)
|
||||
if (!face || face == planecount+1)
|
||||
reset_texturecoords(&tmp.faces[i]);
|
||||
|
||||
brush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);
|
||||
}
|
||||
}
|
||||
};
|
108
quakec/csaddon/src/brush_selection.qc
Normal file
108
quakec/csaddon/src/brush_selection.qc
Normal file
|
@ -0,0 +1,108 @@
|
|||
var float autocvar_ca_brush_view = 0; //0=normal, 1=x, 2=y, 3=z
|
||||
var float autocvar_ca_brush_viewsize = 1024; //for different views.
|
||||
var string autocvar_ca_newbrushtexture = "metal4_2";
|
||||
var float autocvar_ca_newbrushheight = 64;
|
||||
var float autocvar_ca_grid = 16;
|
||||
|
||||
|
||||
#define EPSILON (1.0 / 32) //inprecision sucks.
|
||||
|
||||
#define MAX_FACEPOINTS 64
|
||||
vector facepoints[MAX_FACEPOINTS]; //for temp usage
|
||||
|
||||
#define MAX_BRUSHFACES 64
|
||||
struct
|
||||
{
|
||||
//brush state
|
||||
brushface_t faces[MAX_BRUSHFACES];
|
||||
int numfaces;
|
||||
int contents;
|
||||
|
||||
//patch state
|
||||
patchvert_t *cp;
|
||||
int numcp, maxcp;
|
||||
patchinfo_t patchinfo;
|
||||
} tmp;
|
||||
|
||||
|
||||
|
||||
//int selected;
|
||||
int brushlist[2048];
|
||||
|
||||
enum : int
|
||||
{
|
||||
BT_NONE, //selection
|
||||
BT_MOVE = BT_NONE,
|
||||
BT_ROTATE,
|
||||
BT_MERGE,
|
||||
BT_PUSHFACE,
|
||||
BT_CREATE,
|
||||
BT_CREATEDRAG,
|
||||
BT_CLONEDISPLACE,
|
||||
BT_SLICE,
|
||||
BT_MOVETEXTURE,
|
||||
BT_VERTEXEDIT
|
||||
};
|
||||
int mousetool;
|
||||
int brushtool;
|
||||
int bt_points;
|
||||
vector bt_displace;
|
||||
vector bt_point[64];
|
||||
int nogrid; //+nogrid, temporarily disables grid locks
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int model;
|
||||
int id;
|
||||
int face;
|
||||
//fixme: do we need an array of faces here?
|
||||
} selbrush_t;
|
||||
selbrush_t *selectedbrushes;
|
||||
int selectedbrushcount;
|
||||
|
||||
var int selectedbrushmodel = 1; //by default, the worldmodel. this is tracked to know which submodel to insert new brushes into.
|
||||
|
||||
int(int modelidx, int brushid) brush_isselected =
|
||||
{
|
||||
for (int i = 0; i < selectedbrushcount; i++)
|
||||
if (selectedbrushes[i].id == brushid)
|
||||
if (selectedbrushes[i].model == modelidx)
|
||||
return i+1;
|
||||
return 0;
|
||||
};
|
||||
int(int modelidx, int brushid) brush_deselect =
|
||||
{
|
||||
int i = brush_isselected(modelidx, brushid);
|
||||
if (!i)
|
||||
return FALSE;
|
||||
brush_selected(modelidx, brushid, -1, FALSE);
|
||||
memcpy(&selectedbrushes[i-1], &selectedbrushes[i], sizeof(selbrush_t)*(selectedbrushcount-i));
|
||||
selectedbrushcount--;
|
||||
return TRUE;
|
||||
};
|
||||
void() brush_deselectall =
|
||||
{
|
||||
for (int i = 0; i < selectedbrushcount; i++)
|
||||
brush_selected(selectedbrushes[i].model, selectedbrushes[i].id, -1, FALSE);
|
||||
|
||||
selectedbrushcount = 0;
|
||||
};
|
||||
void(int modelidx, int brushid) brush_select =
|
||||
{
|
||||
if (!brush_isselected(modelidx, brushid))
|
||||
{
|
||||
selbrush_t *n = memalloc(sizeof(selbrush_t) * (selectedbrushcount+1));
|
||||
memcpy(n, selectedbrushes, sizeof(selbrush_t)*selectedbrushcount);
|
||||
memfree(selectedbrushes);
|
||||
selectedbrushes = n;
|
||||
n[selectedbrushcount].model = modelidx;
|
||||
n[selectedbrushcount].id = brushid;
|
||||
selectedbrushcount++;
|
||||
brush_selected(modelidx, brushid, -1, TRUE);
|
||||
}
|
||||
selectedbrushmodel = modelidx;
|
||||
};
|
324
quakec/csaddon/src/brush_vertedit.qc
Normal file
324
quakec/csaddon/src/brush_vertedit.qc
Normal file
|
@ -0,0 +1,324 @@
|
|||
|
||||
typedef struct
|
||||
{
|
||||
int numverts;
|
||||
int numidx;
|
||||
vector *p;
|
||||
int *i;
|
||||
|
||||
int selectedvert1;
|
||||
int selectedvert2;
|
||||
|
||||
int model;
|
||||
int brush;
|
||||
} vertsoup_t;
|
||||
vertsoup_t vertedit;
|
||||
|
||||
|
||||
|
||||
//take a brush apart and generate trisoup from it.
|
||||
//note that the trisoup does not have textures. they will be reconciled when reforming the brush.
|
||||
void(vertsoup_t *vertedit, int model, int brush) Debrushify =
|
||||
{
|
||||
int *ni;
|
||||
int i, j, k;
|
||||
int points;
|
||||
vector p;
|
||||
vector *np;
|
||||
static int fi[64];
|
||||
vertedit->model = model;
|
||||
vertedit->brush = brush;
|
||||
vertedit->numidx = 0;
|
||||
vertedit->numverts = 0;
|
||||
for (i = 0; ; )
|
||||
{
|
||||
points = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length);
|
||||
if (!points)
|
||||
break;
|
||||
|
||||
//allocate a few new indexes
|
||||
ni = memalloc(sizeof(*ni) * (vertedit->numidx+3*(points-2)));
|
||||
memcpy(ni, vertedit->i, sizeof(*ni) * vertedit->numidx);
|
||||
memfree(vertedit->i);
|
||||
vertedit->i = ni;
|
||||
ni += vertedit->numidx;
|
||||
vertedit->numidx += 3*(points-2);
|
||||
|
||||
for (j = 0; j < points; j++)
|
||||
{
|
||||
p = facepoints[j];
|
||||
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
|
||||
p_y = floor(p_y + 0.5);
|
||||
p_z = floor(p_z + 0.5);
|
||||
for (k = 0; k < vertedit->numverts; k++)
|
||||
{ //try to be nice and re-use verts.
|
||||
if (vertedit->p[k] == p)
|
||||
break;
|
||||
}
|
||||
if (k == vertedit->numverts)
|
||||
{
|
||||
//if it wasn't found, we need to allocate a new one
|
||||
np = memalloc(sizeof(*np) * (vertedit->numverts+1));
|
||||
memcpy(np, vertedit->p, sizeof(*np) * vertedit->numverts);
|
||||
memfree(vertedit->p);
|
||||
vertedit->p = np;
|
||||
np += vertedit->numverts;
|
||||
vertedit->numverts += 1;
|
||||
*np = p;
|
||||
}
|
||||
fi[j] = k;
|
||||
}
|
||||
for (j = 2; j < points; j++)
|
||||
{
|
||||
*ni++ = fi[0];
|
||||
*ni++ = fi[j-1];
|
||||
*ni++ = fi[j];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//determines only the various points of the brush, without duplicates. doesn't care about indexes.
|
||||
void(brushface_t *faces, int numfaces) DebrushifyLite =
|
||||
{
|
||||
int points, k;
|
||||
vector p;
|
||||
vector *np;
|
||||
int fi[64];
|
||||
vertedit.numidx = 0;
|
||||
vertedit.numverts = 0;
|
||||
for (int i = 0; ; )
|
||||
{
|
||||
points = brush_calcfacepoints(++i, faces, numfaces, facepoints, facepoints.length);
|
||||
if (!points)
|
||||
break;
|
||||
|
||||
for (int j = 0; j < points; j++)
|
||||
{
|
||||
p = facepoints[j];
|
||||
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
|
||||
p_y = floor(p_y + 0.5);
|
||||
p_z = floor(p_z + 0.5);
|
||||
for (k = 0; k < vertedit.numverts; k++)
|
||||
{ //try to be nice and re-use verts.
|
||||
if (vertedit.p[k] == p)
|
||||
break;
|
||||
}
|
||||
if (k == vertedit.numverts)
|
||||
{
|
||||
//if it wasn't found, we need to allocate a new one
|
||||
np = memalloc(sizeof(*np) * (vertedit.numverts+1));
|
||||
memcpy(np, vertedit.p, sizeof(*np) * vertedit.numverts);
|
||||
memfree(vertedit.p);
|
||||
vertedit.p = np;
|
||||
np += vertedit.numverts;
|
||||
vertedit.numverts += 1;
|
||||
*np = p;
|
||||
}
|
||||
fi[j] = k;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) planenormal =
|
||||
{
|
||||
vector v1 = soup->p[idx[0]];
|
||||
vector v2 = soup->p[idx[1]];
|
||||
vector v3 = soup->p[idx[2]];
|
||||
vector d1 = v3 - v1;
|
||||
vector d2 = v2 - v1;
|
||||
vector d3 = v3 - v2;
|
||||
norm = normalize(crossproduct(d1, d2));
|
||||
dist = norm * v1;
|
||||
|
||||
if (!d1 || !d2 || !d3 || !norm)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
};
|
||||
|
||||
void(vertsoup_t *soup, int drawit) Rebrushify =
|
||||
{
|
||||
brushface_t faces[64];
|
||||
int numfaces, f, point;
|
||||
string shader = 0;
|
||||
|
||||
vector n;
|
||||
float d;
|
||||
int o=0;
|
||||
|
||||
|
||||
tmp.numfaces = brush_get(soup->model, soup->brush, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
|
||||
//if any triangle's neighbour opposes it, reform the edge across the quad to try to keep it convex.
|
||||
for(point = 0; point+2 < soup->numidx; point+=3)
|
||||
{
|
||||
int p1 = soup->i[point+0];
|
||||
int p2 = soup->i[point+1];
|
||||
int p3 = soup->i[point+2];
|
||||
if (!planenormal(soup, &soup->i[point], n, d))
|
||||
continue; //degenerate
|
||||
d += EPSILON;
|
||||
for(f = point+3; f+2 < soup->numidx; f+=3)
|
||||
{
|
||||
int o1 = soup->i[f+0];
|
||||
int o2 = soup->i[f+1];
|
||||
int o3 = soup->i[f+2];
|
||||
p1p2edge:
|
||||
if (o2 == p1 && o1 == p2)
|
||||
o = o3;
|
||||
else if (o3 == p1 && o2 == p2)
|
||||
o = o1;
|
||||
else if (o1 == p1 && o3 == p2)
|
||||
o = o2;
|
||||
else
|
||||
goto p2p3edge;
|
||||
if (soup->p[o] * n > d)
|
||||
{
|
||||
soup->i[f+0] = p3;
|
||||
soup->i[f+1] = o;
|
||||
soup->i[f+2] = p2;
|
||||
p2 = o;
|
||||
}
|
||||
continue;
|
||||
p2p3edge:
|
||||
if (o2 == p2 && o1 == p3)
|
||||
o = o3;
|
||||
else if (o3 == p2 && o2 == p3)
|
||||
o = o1;
|
||||
else if (o1 == p2 && o3 == p3)
|
||||
o = o2;
|
||||
else
|
||||
goto p3p1edge;
|
||||
if (soup->p[o] * n > d)
|
||||
{
|
||||
soup->i[f+0] = p1;
|
||||
soup->i[f+1] = o;
|
||||
soup->i[f+2] = p3;
|
||||
p3 = o;
|
||||
}
|
||||
continue;
|
||||
p3p1edge:
|
||||
if (o2 == p3 && o1 == p1)
|
||||
o = o3;
|
||||
else if (o3 == p3 && o2 == p1)
|
||||
o = o1;
|
||||
else if (o1 == p3 && o3 == p1)
|
||||
o = o2;
|
||||
else
|
||||
continue;
|
||||
if (soup->p[o] * n > d)
|
||||
{
|
||||
soup->i[f+0] = p2;
|
||||
soup->i[f+1] = o;
|
||||
soup->i[f+2] = p1;
|
||||
p1 = o;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
soup->i[point+0] = p1;
|
||||
soup->i[point+1] = p2;
|
||||
soup->i[point+2] = p3;
|
||||
}
|
||||
|
||||
//generate the plane info
|
||||
numfaces = 0;
|
||||
for(point = 0; point+2 < soup->numidx; point+=3)
|
||||
{
|
||||
if (!planenormal(soup, &soup->i[point], n, d))
|
||||
continue; //a degenerate triangle is one that probably got merged or something
|
||||
|
||||
for (f = 0; f < numfaces; f++)
|
||||
{
|
||||
if (faces[f].planenormal == n && faces[f].planedist == d)
|
||||
break;
|
||||
}
|
||||
if (f < numfaces)
|
||||
continue; //duplicate plane
|
||||
|
||||
for (f = 0; f < tmp.numfaces; f++)
|
||||
{
|
||||
if (tmp.faces[f].planenormal * n > 0.999) //stupid inprecisions
|
||||
{
|
||||
if (numfaces == 64)
|
||||
return;
|
||||
|
||||
//note that we only care about the normal, not the dist. this means you can move the entire face forward+back without loosing textures.
|
||||
faces[numfaces].shadername = shader = tmp.faces[f].shadername;
|
||||
faces[numfaces].planenormal = n;
|
||||
faces[numfaces].planedist = d;
|
||||
faces[numfaces].sdir = tmp.faces[f].sdir;
|
||||
faces[numfaces].sbias = tmp.faces[f].sbias;
|
||||
faces[numfaces].tdir = tmp.faces[f].tdir;
|
||||
faces[numfaces].tbias = tmp.faces[f].tbias;
|
||||
numfaces++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f < tmp.numfaces)
|
||||
continue; //matched a plane in the original brush
|
||||
|
||||
if (numfaces == 64)
|
||||
return;
|
||||
|
||||
//FIXME: find aproximate faces to give corners or something
|
||||
|
||||
//okay, it appears to be new. that's a pain.
|
||||
faces[numfaces].shadername = 0;
|
||||
faces[numfaces].planenormal = n;
|
||||
faces[numfaces].planedist = d;
|
||||
reset_texturecoords(&faces[numfaces]);
|
||||
numfaces++;
|
||||
}
|
||||
|
||||
//any surface without a texture/shader yet should inherit one from some other surface
|
||||
if (!shader)
|
||||
shader = autocvar_ca_newbrushtexture;
|
||||
for(f = 0; f < numfaces; f++)
|
||||
{
|
||||
if (!faces[f].shadername)
|
||||
faces[f].shadername = shader;
|
||||
}
|
||||
|
||||
int internals = 0;
|
||||
//If we have a point outside a plane, then we know we have a concave volume.
|
||||
//chop the points
|
||||
for(f = 0; f < numfaces; f++)
|
||||
{
|
||||
n = faces[f].planenormal;
|
||||
d = faces[f].planedist;
|
||||
for (point = 0; point < soup->numidx; point++) //would ideally use points, but I want to cover dead ones
|
||||
{
|
||||
if (soup->p[soup->i[point]] * n > d + EPSILON)
|
||||
{
|
||||
internals++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces));
|
||||
|
||||
if (numfaces <= 3)
|
||||
return; //can't possibly be valid.
|
||||
|
||||
if (drawit)
|
||||
{
|
||||
//draw it wireframe WITH depth testing
|
||||
//DrawQCBrushWireframe(faces, numfaces, "terrainedit", '1 0 0', 1);
|
||||
|
||||
//draw textured preview (yay for block lighting)
|
||||
DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1);
|
||||
|
||||
//draw it wireframe without depth testing
|
||||
DrawQCBrushWireframe(faces, numfaces, "chop", internals?[1,0,0]:[0,0,1], 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
brush_history_edit(soup->model, soup->brush, faces, numfaces, 1i);
|
||||
|
||||
soup->numidx = 0;
|
||||
soup->numverts = 0;
|
||||
}
|
||||
};
|
|
@ -20,9 +20,6 @@ float releasedmouse;
|
|||
#ifdef WALLBROWSERS
|
||||
string pointedshadername;
|
||||
#endif
|
||||
vector pointedsurfacenormal;
|
||||
entity pointedent;
|
||||
float pointedsurface;
|
||||
|
||||
float editorrsd[editornames.length];
|
||||
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -180,7 +180,7 @@ void(float num) editor_ents_delete =
|
|||
if (num > 0 && num < numents)
|
||||
{
|
||||
hash_destroytab(editents[num].fields);
|
||||
editents[num].fields = 0;
|
||||
editents[num].fields = 0i;
|
||||
terrain_edit(TEREDIT_ENT_SET, num, __NULL__);
|
||||
}
|
||||
};
|
||||
|
@ -271,8 +271,8 @@ void(entedit_t *nent) editor_ents_updated =
|
|||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
nent->bboxverts[i].st = (vec2){0,0};
|
||||
nent->bboxverts[i].rgba = (vec4){nent->colourmod[0], nent->colourmod[1], nent->colourmod[2], nent->alpha?nent->alpha:1};
|
||||
nent->bboxverts[i].st = (vec2){{0,0}};
|
||||
nent->bboxverts[i].rgba = (vec4){{nent->colourmod[0], nent->colourmod[1], nent->colourmod[2], nent->alpha?nent->alpha:1}};
|
||||
nent->bboxverts[i].xyz[0] = nent->org[0] + ((i&1i)?nent->maxs[0]:nent->mins[0]);
|
||||
nent->bboxverts[i].xyz[1] = nent->org[1] + (((i&2i)?nent->maxs[1]:nent->mins[1]));
|
||||
nent->bboxverts[i].xyz[2] = nent->org[2] + ((i&4)?nent->maxs[2]:nent->mins[2]);
|
||||
|
@ -335,7 +335,7 @@ void() editor_ents_reload =
|
|||
nent = &editents[id];
|
||||
if (nent->fields)
|
||||
hash_destroytab(nent->fields);
|
||||
nent->fields = 0;
|
||||
nent->fields = __NULL__;
|
||||
if (field == __NULL__) //empty entity slot.
|
||||
continue;
|
||||
nent->fields = hash_createtab(12, EV_STRING);
|
||||
|
|
|
@ -219,7 +219,7 @@ static void(string descname) loadparticles =
|
|||
if (descname != "")
|
||||
{
|
||||
f = fopen(strcat("particles/", descname, ".cfg"), FILE_READNL);
|
||||
if (f >= 0)
|
||||
if ((float)f >= 0)
|
||||
{
|
||||
particlesfile = strzone(fgets(f));
|
||||
fclose(f);
|
||||
|
@ -253,7 +253,7 @@ static void(string descname) saveparticle =
|
|||
if (descname != "")
|
||||
{
|
||||
f = fopen(strcat("particles/", descname, ".cfg"), FILE_WRITE);
|
||||
if (f >= 0)
|
||||
if ((float)f >= 0)
|
||||
{
|
||||
fputs(f, particlesfile);
|
||||
fclose(f);
|
||||
|
|
|
@ -588,7 +588,7 @@ void(vector mousepos) editor_terrain_overlay =
|
|||
}
|
||||
if (curtool == ter_tex)
|
||||
{
|
||||
if (texturesearch < 0)
|
||||
if ((float)texturesearch < 0)
|
||||
texturesearch = search_begin("textures/*", FALSE, TRUE);
|
||||
if (texturesearchfirst > search_getsize(texturesearch)-4)
|
||||
texturesearchfirst = search_getsize(texturesearch)-4;
|
||||
|
|
|
@ -383,7 +383,7 @@ void(string shader, vector org, vector s, vector t, string text, vector col, flo
|
|||
vector st = draw_getimagesize(shader);
|
||||
vector pos = org;
|
||||
float chr;
|
||||
float idx;
|
||||
float idx = 0;
|
||||
vector tc0,tc1,tc2,tc3;
|
||||
|
||||
//with GL_LINEAR sampling, the sample value is interpolated from the nearby two pixels
|
||||
|
|
|
@ -78,8 +78,8 @@ void(vector pos, textfield_t *fld, textfield_linedesc_t *fields) textfield_draw
|
|||
{
|
||||
string t;
|
||||
int l;
|
||||
int nummatches;
|
||||
int lastmatch;
|
||||
int nummatches = 0;
|
||||
int lastmatch = 0;
|
||||
for (l = 0i; l < fld->linecount; l+=1i, pos_y += 8)
|
||||
{
|
||||
if (l == fld->cursorline && ((cltime * 4) & 1))
|
||||
|
@ -154,7 +154,7 @@ void(textfield_t *fld) textfield_localcmd =
|
|||
string(textfield_t *fld) textfield_strcat =
|
||||
{
|
||||
int l;
|
||||
string res;
|
||||
string res = "";
|
||||
for (l = 0i; l < fld->linecount; l+=1i)
|
||||
{
|
||||
res = strcat(res, fld->lines[l], "\n");
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
float show_scoreboard;
|
||||
string dbgstr;
|
||||
vector mousepos;
|
||||
|
||||
void() CSQC_Shutdown = {};
|
||||
|
||||
|
@ -223,6 +224,8 @@ void() drawloadingscreen =
|
|||
|
||||
void(float apilevel, string enginename, float engineversion) CSQC_Init =
|
||||
{
|
||||
__using renderflags;
|
||||
|
||||
checkengineversion();
|
||||
|
||||
precache_model("progs/eyes.mdl");
|
||||
|
@ -248,6 +251,8 @@ void(float apilevel, string enginename, float engineversion) CSQC_Init =
|
|||
|
||||
void(entity ent) CSQC_DrawViewModel =
|
||||
{
|
||||
__using shaderforname, checkbuiltin, forceshader;
|
||||
|
||||
if (player_local.sveffects & SVE_INVIS)
|
||||
{
|
||||
if (!checkbuiltin(shaderforname))
|
||||
|
@ -321,6 +326,18 @@ float (float event, float parama, float paramb, float paramc) CSQC_InputEvent =
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (event == IE_MOUSEABS)
|
||||
mousepos = [parama, paramb];
|
||||
else if (event == IE_MOUSEDELTA)
|
||||
{ //move it...
|
||||
mousepos += [parama, paramb];
|
||||
|
||||
//bound it.
|
||||
vector ssize = getviewprop(VF_SCREENVSIZE);
|
||||
mousepos_x = bound(0, mousepos_x, ssize_x);
|
||||
mousepos_y = bound(0, mousepos_y, ssize_y);
|
||||
}
|
||||
|
||||
return Menu_InputEvent(event, parama, paramb);
|
||||
};
|
||||
|
||||
|
@ -334,8 +351,6 @@ void(float width, float height, float do2d) CSQC_UpdateView =
|
|||
|
||||
clearscene();
|
||||
|
||||
setviewprop(VF_DRAWCROSSHAIR, 1);
|
||||
|
||||
//force fullscreen views (ignore viewsize).
|
||||
setviewprop(VF_MIN, '0 0 0');
|
||||
setviewprop(VF_SIZE_X, width);
|
||||
|
@ -355,6 +370,7 @@ void(float width, float height, float do2d) CSQC_UpdateView =
|
|||
{
|
||||
setviewprop(VF_DRAWENGINESBAR, 1);
|
||||
}
|
||||
setviewprop(VF_DRAWCROSSHAIR, !player_local || !checkbuiltin(project));
|
||||
|
||||
addentities(MASK_NORMAL|MASK_ENGINE);
|
||||
|
||||
|
@ -404,6 +420,33 @@ void(float width, float height, float do2d) CSQC_UpdateView =
|
|||
default:
|
||||
if (hudtype)
|
||||
Hud_Draw(hudtype, show_scoreboard, width, height);
|
||||
//quicky `project` test
|
||||
if (player_local)
|
||||
{
|
||||
if (checkbuiltin(project))
|
||||
{
|
||||
setviewprop(VF_DRAWCROSSHAIR, 0);
|
||||
vector shotorg = player_local.origin+'0 0 16'; //quake is weird.
|
||||
traceline(shotorg, shotorg + v_forward*8192, FALSE, player_local);
|
||||
drawcharacter(project(trace_endpos)-'4 4', '+', '8 8', '1 1 1',1);
|
||||
}
|
||||
|
||||
if (autocvar(cg_unprojecttest, FALSE))
|
||||
{
|
||||
static float nexttime;
|
||||
nexttime += clframetime;
|
||||
drawcharacter(mousepos-'4 4', '+', '8 8', '1 1 1',1);
|
||||
if (nexttime > 1)
|
||||
{
|
||||
vector near = unproject([mousepos_x,mousepos_y,0]);
|
||||
vector far = unproject([mousepos_x,mousepos_y,1]);
|
||||
traceline(near, far, FALSE, player_local);
|
||||
te_explosion(trace_endpos);
|
||||
nexttime-=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ ParsePlayer: Called from CSQC_Ent_Parse for csqc protocol ents
|
|||
|
||||
static void() RemovePlayer;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
MF_BAD,
|
||||
|
@ -69,7 +70,7 @@ static float() Player_Interpolate =
|
|||
self.renderflags = RF_EXTERNALMODEL;
|
||||
|
||||
#ifdef POWERUP_SHELLS
|
||||
if (!checkbuiltin(shaderforname))
|
||||
if (checkbuiltin(shaderforname))
|
||||
{
|
||||
if (self.sveffects & SVE_INVIS)
|
||||
self.forceshader = shaderforname("powerups/invisibility");
|
||||
|
|
|
@ -1158,7 +1158,7 @@ nonstatic vector(string skinname) Anim_GetHeadOffset =
|
|||
|
||||
|
||||
|
||||
float(float channel, string soundname, vector pos, float vol, float attenuation) CSQC_ServerSound =
|
||||
float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) CSQC_ServerSound =
|
||||
{ //the server started a sound on an entity that the csqc has control over.
|
||||
if (!self.headent)
|
||||
{
|
||||
|
@ -1248,7 +1248,7 @@ float(float sventnum, float channel, string soundname, float vol, float att, vec
|
|||
{
|
||||
self = findfloat(world, entnum, sventnum);
|
||||
if (self)
|
||||
return CSQC_ServerSound(channel, soundname, org, vol, att);
|
||||
return CSQC_ServerSound(channel, soundname, org, vol, att, flags);
|
||||
return false;
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
#define CSQC
|
||||
optsall.qc
|
||||
#ifdef QSS
|
||||
qsextensions.qc
|
||||
#endif
|
||||
csqc_api.qc
|
||||
//qsextensions.qc
|
||||
#else
|
||||
fteextensions.qc
|
||||
#endif
|
||||
cs/defs.qc
|
||||
|
||||
common/classes.qc
|
||||
|
|
|
@ -9,7 +9,7 @@ You will probably need to use FTEQCC to compile this.
|
|||
#if defined(QUAKEWORLD) || defined(MENU)
|
||||
#error Mixed up module defs
|
||||
#endif
|
||||
#ifndef CSQC
|
||||
#if !defined(CSQC) && !defined(SSQC) && !defined(MENU)
|
||||
#define CSQC
|
||||
#endif
|
||||
#ifndef CSQC_SIMPLE
|
||||
|
@ -97,6 +97,7 @@ You will probably need to use FTEQCC to compile this.
|
|||
|
||||
//Explicitly flag this stuff as probably-not-referenced, meaning fteqcc will shut up about it and silently strip what it can.
|
||||
#pragma noref 1
|
||||
#if defined(CSQC_SIMPLE) || defined(SSQC)
|
||||
entity self,other,world;
|
||||
float time,frametime,force_retouch;
|
||||
string mapname;
|
||||
|
@ -110,6 +111,7 @@ float trace_inopen,trace_inwater;
|
|||
entity msg_entity;
|
||||
void() main,StartFrame,PlayerPreThink,PlayerPostThink,ClientKill,ClientConnect,PutClientInServer,ClientDisconnect,SetNewParms,SetChangeParms;
|
||||
void end_sys_globals;
|
||||
|
||||
.float modelindex;
|
||||
.vector absmin, absmax;
|
||||
.float ltime,movetype,solid;
|
||||
|
@ -142,6 +144,7 @@ void end_sys_globals;
|
|||
.float sounds;
|
||||
.string noise, noise1, noise2, noise3;
|
||||
void end_sys_fields;
|
||||
#endif
|
||||
|
||||
|
||||
//Some custom types (that might be redefined as accessors by fteextensions.qc, although we don't define any methods here)
|
||||
|
@ -158,15 +161,35 @@ accessor filestream:float;
|
|||
#define infostring string
|
||||
#define filestream float
|
||||
#endif
|
||||
void(string cmd) SV_ParseClientCommand;
|
||||
void() EndFrame;
|
||||
void(float apilevel, string enginename, float engineversion) CSQC_Init;
|
||||
float(string cmdstr) CSQC_ConsoleCommand;
|
||||
void(vector virtsize, float showscores) CSQC_DrawHud;
|
||||
void(vector virtsize, float showscores) CSQC_DrawScores;
|
||||
float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent;
|
||||
void() CSQC_Parse_Event;
|
||||
float(float save, float take, vector dir) CSQC_Parse_Damage;
|
||||
|
||||
|
||||
//Common entry points
|
||||
void() EndFrame
|
||||
void(string cmdtext) GameCommand
|
||||
|
||||
|
||||
//Serverside entry points
|
||||
void(string cmd) SV_ParseClientCommand
|
||||
void() SV_RunClientCommand
|
||||
|
||||
|
||||
//CSQC entry points
|
||||
void(float apilevel, string enginename, float engineversion) CSQC_Init
|
||||
void() CSQC_Shutdown
|
||||
void(vector virtsize, float showscores) CSQC_DrawHud
|
||||
void(vector virtsize, float showscores) CSQC_DrawScores
|
||||
float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent
|
||||
float(string cmdstr) CSQC_ConsoleCommand
|
||||
void() CSQC_Parse_Event
|
||||
float(float save, float take, vector dir) CSQC_Parse_Damage
|
||||
void(float vwidth, float vheight, float notmenu) CSQC_UpdateView
|
||||
void() CSQC_Input_Frame
|
||||
float(string msg) CSQC_Parse_CenterPrint
|
||||
void(string printmsg, float printlvl) CSQC_Parse_Print
|
||||
void(float isnew) CSQC_Ent_Update
|
||||
void() CSQC_Ent_Remove
|
||||
float() CSQC_Parse_TempEntity
|
||||
void(string msg) CSQC_Parse_StuffCmd
|
||||
float cltime; /* increases regardless of pause state or game speed */
|
||||
float maxclients; /* maximum number of players possible on this server */
|
||||
float intermission; /* in intermission */
|
||||
|
@ -175,11 +198,8 @@ float player_localnum; /* the player slot that is believed to be assigned to us
|
|||
float player_localentnum; /* the entity number that the view is attached to */
|
||||
const float FALSE = 0;
|
||||
const float TRUE = 1;
|
||||
const float IE_KEYDOWN = 0;
|
||||
const float IE_KEYUP = 1;
|
||||
const float IE_MOUSEDELTA = 2;
|
||||
const float IE_MOUSEABS = 3;
|
||||
const float IE_JOYAXIS = 6;
|
||||
const float MASK_ENGINE = 1;
|
||||
const float MASK_VIEWMODEL = 2;
|
||||
const float STAT_HEALTH = 0; /* Player's health. */
|
||||
const float STAT_WEAPONMODELI = 2; /* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */
|
||||
const float STAT_AMMO = 3; /* player.currentammo */
|
||||
|
@ -202,7 +222,39 @@ const float STAT_IDEALPITCH = 25;
|
|||
const float STAT_PUNCHANGLE_X = 26;
|
||||
const float STAT_PUNCHANGLE_Y = 27;
|
||||
const float STAT_PUNCHANGLE_Z = 28;
|
||||
const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */
|
||||
const float SOLID_BBOX = 2;
|
||||
const float SOLID_BSP = 4;
|
||||
const float SOLID_NOT = 0;
|
||||
const float SOLID_SLIDEBOX = 3;
|
||||
const float SOLID_TRIGGER = 1;
|
||||
const float CONTENTS_EMPTY = -1;
|
||||
const float CONTENTS_SOLID = -2;
|
||||
const float CONTENTS_WATER = -3;
|
||||
const float CONTENTS_SLIME = -4;
|
||||
const float CONTENTS_LAVA = -5;
|
||||
const float CONTENTS_SKY = -6;
|
||||
const float CONTENTS_LADDER = -16;
|
||||
__used var float physics_mode = 2;
|
||||
const float TE_SPIKE = 0;
|
||||
const float TE_SUPERSPIKE = 1;
|
||||
const float TE_GUNSHOT = 2;
|
||||
const float TE_EXPLOSION = 3;
|
||||
const float TE_TAREXPLOSION = 4;
|
||||
const float TE_LIGHTNING1 = 5;
|
||||
const float TE_LIGHTNING2 = 6;
|
||||
const float TE_WIZSPIKE = 7;
|
||||
const float TE_KNIGHTSPIKE = 8;
|
||||
const float TE_LIGHTNING3 = 9;
|
||||
const float TE_LAVASPLASH = 10;
|
||||
const float TE_TELEPORT = 11;
|
||||
const float TE_EXPLOSION2 = 12;
|
||||
const float TE_BEAM = 13;
|
||||
const float IE_KEYDOWN = 0;
|
||||
const float IE_KEYUP = 1;
|
||||
const float IE_MOUSEDELTA = 2;
|
||||
const float IE_MOUSEABS = 3;
|
||||
const float IE_JOYAXIS = 6;
|
||||
const float VF_MIN = 1;const float VF_MIN_X = 2;const float VF_MIN_Y = 3;const float VF_SIZE = 4;const float VF_SIZE_X = 5;const float VF_SIZE_Y = 6;const float VF_VIEWPORT = 7;const float VF_FOV = 8;const float VF_FOV_X = 9;const float VF_FOV_Y = 10;const float VF_ORIGIN = 11;const float VF_ORIGIN_X = 12;const float VF_ORIGIN_Y = 13;const float VF_ORIGIN_Z = 14;const float VF_ANGLES = 15;const float VF_ANGLES_X = 16;const float VF_ANGLES_Y = 17;const float VF_ANGLES_Z = 18;const float VF_DRAWWORLD = 19;const float VF_DRAWENGINESBAR = 20;const float VF_DRAWCROSSHAIR = 21;const float VF_CL_VIEWANGLES = 33;const float VF_CL_VIEWANGLES_X = 34;const float VF_CL_VIEWANGLES_Y = 35;const float VF_CL_VIEWANGLES_Z = 36;const float VF_AFOV = 203;const float VF_SCREENVSIZE = 204;const float VF_SCREENPSIZE = 205;const float RF_VIEWMODEL = 1;const float RF_EXTERNALMODEL = 2;const float RF_DEPTHHACK = 4;const float RF_ADDITIVE = 8;const float RF_USEAXIS = 16;const float RF_NOSHADOW = 32;const float RF_WEIRDFRAMETIMES = 64;const float SLIST_HOSTCACHEVIEWCOUNT = 0;const float SLIST_HOSTCACHETOTALCOUNT = 1;const float SLIST_SORTFIELD = 6;const float SLIST_SORTDESCENDING = 7;const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */
|
||||
const float EV_VOID = 0;
|
||||
const float EV_STRING = 1;
|
||||
const float EV_FLOAT = 2;
|
||||
|
@ -215,26 +267,41 @@ const float EV_INTEGER = 8;
|
|||
|
||||
|
||||
//Supported Extension fields
|
||||
.float gravity;
|
||||
//.float items2; /*if defined, overrides serverflags for displaying runes on the hud*/
|
||||
.float traileffectnum; /*can also be set with 'traileffect' from a map editor*/
|
||||
.float emiteffectnum; /*can also be set with 'traileffect' from a map editor*/
|
||||
.vector movement; /*describes which forward/right/up keys the player is holidng*/
|
||||
.entity viewmodelforclient; /*attaches this entity to the specified player's view. invisible to other players*/
|
||||
.entity exteriormodeltoclient;/*hides the entity in the specified player's main view. it will remain visible in mirrors etc.*/
|
||||
.float scale; /*rescales the etntiy*/
|
||||
.float alpha; /*entity opacity*/
|
||||
.vector colormod; /*tints the entity's colours*/
|
||||
.float alpha;
|
||||
.float scale;
|
||||
.vector colormod;
|
||||
.entity tag_entity;
|
||||
.float tag_index;
|
||||
.float modelflags;
|
||||
.vector origin;
|
||||
.vector angles;
|
||||
.float frame;
|
||||
.float skin;
|
||||
.void() customphysics;
|
||||
.float gravity;
|
||||
.float frame2;
|
||||
.float lerpfrac;
|
||||
.float frame1time;
|
||||
.float frame2time;
|
||||
.float renderflags;
|
||||
.float entnum;
|
||||
.float drawmask;
|
||||
.float() predraw;
|
||||
//.float items2;
|
||||
.vector movement;
|
||||
.entity viewmodelforclient;
|
||||
.entity exteriormodeltoclient;
|
||||
.float traileffectnum;
|
||||
.float emiteffectnum;
|
||||
.float button3;
|
||||
.float button4;
|
||||
.float button5;
|
||||
.float button6;
|
||||
.float button7;
|
||||
.float button8;
|
||||
.float viewzoom; /*rescales the user's fov*/
|
||||
.float modelflags; /*provides additional modelflags to use (effects&EF_NOMODELFLAGS to replace the original model's)*/
|
||||
.float viewzoom;
|
||||
.float(entity to, float changedflags) SendEntity;
|
||||
.float SendFlags;
|
||||
|
||||
|
||||
//Supported Extension Constants
|
||||
|
@ -273,23 +340,60 @@ void(entity,vector) setorigin = #2;
|
|||
void(entity,string) setmodel = #3;
|
||||
void(entity,vector,vector) setsize = #4;
|
||||
float() random = #7;
|
||||
//void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8;
|
||||
vector(vector) normalize = #9;
|
||||
void(string e) error = #10;
|
||||
void(string n) objerror = #11;
|
||||
float(vector) vlen = #12;
|
||||
//float(vector fwd) vectoyaw = #13;
|
||||
entity() spawn = #14;
|
||||
void(entity e) remove = #15;
|
||||
void(vector v1, vector v2, float flags, entity ent) traceline = #16;
|
||||
entity() checkclient = #17;
|
||||
entity(entity start, .string fld, string match) find = #18;
|
||||
string(string s) precache_sound = #19;
|
||||
string(string s) precache_model = #20;
|
||||
void(entity client, string s) stuffcmd = #21;
|
||||
//entity(vector org, float rad, optional .entity chainfield) findradius = #22;
|
||||
//void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) bprint = #23;
|
||||
//void(entity pl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) sprint = #24;
|
||||
void(string,...) dprint = #25;
|
||||
string(float) ftos = #26;
|
||||
string(vector) vtos = #27;
|
||||
//float(float yaw, float dist, optional float settraceglobals) walkmove = #32;
|
||||
float() droptofloor = #34;
|
||||
//void(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35;
|
||||
float(float n) rint = #36;
|
||||
float(float n) floor = #37;
|
||||
float(float n) ceil = #38;
|
||||
float(entity e) checkbottom = #40;
|
||||
float(vector point) pointcontents = #41;
|
||||
float(float n) fabs = #43;
|
||||
vector(entity e, float speed) aim = #44;
|
||||
float(string) cvar = #45;
|
||||
void(string,...) localcmd = #46;
|
||||
entity(entity) nextent = #47;
|
||||
//void(vector o, vector d, float color, float count) particle = #48;
|
||||
void() changeyaw = #49;
|
||||
void(float to, float val) WriteByte = #52;
|
||||
void(float to, float val) WriteChar = #53;
|
||||
void(float to, float val) WriteShort = #54;
|
||||
void(float to, float val) WriteLong = #55;
|
||||
void(float to, float val) WriteCoord = #56;
|
||||
void(float to, float val) WriteAngle = #57;
|
||||
void(float to, string val) WriteString = #58;
|
||||
void(float to, entity val) WriteEntity = #59;
|
||||
void(float step) movetogoal = #67;
|
||||
string(string s) precache_file = #68;
|
||||
void(entity e) makestatic = #69;
|
||||
void(string mapname, optional string newmapstartspot) changelevel = #70;
|
||||
void(string var, string val) cvar_set = #72;
|
||||
void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73;
|
||||
void (vector pos, string samp, float vol, float atten) ambientsound = #74;
|
||||
string(string str) precache_model2 = #75;
|
||||
string(string str) precache_sound2 = #76;
|
||||
string(string str) precache_file2 = #77;
|
||||
void(entity player) setspawnparms = #78;
|
||||
|
||||
|
||||
//Builtin list
|
||||
|
@ -691,8 +795,8 @@ entity(entity from, optional entity to) copyentity = #400; /*
|
|||
void(entity ent, float colours) setcolors = #401; /*
|
||||
Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */
|
||||
|
||||
entity(.string field, string match) findchain = #402;
|
||||
entity(.float fld, float match) findchainfloat = #403;
|
||||
entity(.string field, string match, optional .entity chainfield) findchain = #402;
|
||||
entity(.float fld, float match, optional .entity chainfield) findchainfloat = #403;
|
||||
void(vector org, vector dir, float count) te_blood = #405;
|
||||
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409;
|
||||
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410;
|
||||
|
@ -723,7 +827,7 @@ string(float n) argv = #442;
|
|||
float() argc = #0;
|
||||
void(entity e, entity tagentity, string tagname) setattachment = #443; /* */
|
||||
|
||||
searchhandle(string pattern, optional float caseinsensitive, optional float quiet) search_begin = #444; /*
|
||||
searchhandle(string pattern, float flags, float quiet, optional string filterpackage) search_begin = #444; /*
|
||||
initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. */
|
||||
|
||||
void(searchhandle handle) search_end = #445; /* */
|
||||
|
@ -745,7 +849,7 @@ string(searchhandle handle, float num) search_getpackagename = #0; /*
|
|||
|
||||
string(string cvarname) cvar_string = #448;
|
||||
entity(entity start, .float fld, float match) findflags = #449;
|
||||
entity(.float fld, float match) findchainflags = #450;
|
||||
entity(.float fld, float match, optional .entity chainfield) findchainflags = #450;
|
||||
void(entity player) dropclient = #453;
|
||||
entity() spawnclient = #454; /*
|
||||
Spawns a dummy player entity.
|
||||
|
@ -890,9 +994,10 @@ string(float n, float prop) getgamedirinfo = #626; /*
|
|||
string(string fmt, ...) sprintf = #627;
|
||||
float(entity e, float s) getsurfacenumtriangles = #628;
|
||||
vector(entity e, float s, float n) getsurfacetriangle = #629;
|
||||
float(float key, string bind, optional float bindmap) setkeybind = #630; /*
|
||||
float(float key, string bind, optional float bindmap, optional float modifier) setkeybind = #630; /*
|
||||
Changes a key binding. */
|
||||
|
||||
string(string digest, string data, ...) digest_hex = #639;
|
||||
|
||||
|
||||
//Builtin Stubs List (these are present for simpler compatibility, but not properly supported in QuakeSpasm at this time).
|
||||
|
@ -915,7 +1020,6 @@ void(float mask, float fld, float num, float op) sethostcachemasknumber = #617;
|
|||
void(string key) addwantedhostcachekey = #623;
|
||||
vector() getbindmaps = #631;
|
||||
float(vector bm) setbindmaps = #632;
|
||||
string(string digest, string data, ...) digest_hex = #639;
|
||||
*/
|
||||
const float K_TAB = 9;
|
||||
const float K_ENTER = 13;
|
||||
|
|
|
@ -541,11 +541,8 @@ void() PutClientInServer =
|
|||
self.effects = 0;
|
||||
self.invincible_time = 0;
|
||||
|
||||
if (serverusingcsqc)
|
||||
{
|
||||
self.SendEntity = SendPlayer;
|
||||
self.SendFlags = FULLSEND;
|
||||
}
|
||||
self.SendEntity = SendPlayer;
|
||||
self.SendFlags = FULLSEND;
|
||||
|
||||
DecodeLevelParms ();
|
||||
|
||||
|
@ -1258,7 +1255,7 @@ enum {
|
|||
float(entity ent) GetGender =
|
||||
{
|
||||
local string s;
|
||||
if (!isdp)
|
||||
if (checkbuiltin(infokey))
|
||||
{
|
||||
s = infokey(ent, "s");
|
||||
if (s == "" || s == "m" || s == "male" || s == "yesplease")
|
||||
|
@ -1267,7 +1264,8 @@ float(entity ent) GetGender =
|
|||
return GENDER_FEMALE;
|
||||
return GENDER_NEUTER;
|
||||
}
|
||||
return GENDER_MALE;
|
||||
else
|
||||
return GENDER_NEUTER;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -505,3 +505,7 @@ float isdp;
|
|||
|
||||
#define FULLSEND 0xffffff
|
||||
|
||||
#ifdef QWSSQC
|
||||
#define sprint(pl,...) sprint(pl, PRINT_HIGH, __VA_ARGS__)
|
||||
#define bprint(...) bprint(PRINT_HIGH, __VA_ARGS__)
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,9 @@ void() Laser_Touch =
|
|||
{
|
||||
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
||||
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
|
||||
#ifdef QWSSQC
|
||||
WriteByte (MSG_BROADCAST, 1); //count
|
||||
#endif
|
||||
WriteCoord (MSG_BROADCAST, org_x);
|
||||
WriteCoord (MSG_BROADCAST, org_y);
|
||||
WriteCoord (MSG_BROADCAST, org_z);
|
||||
|
|
|
@ -573,6 +573,7 @@ void() trigger_hurt =
|
|||
|
||||
float PUSH_ONCE = 1;
|
||||
|
||||
void() target_position = {};
|
||||
//function provided by mercury (of oztf)
|
||||
void () trigger_push_findtarget =
|
||||
{
|
||||
|
@ -634,6 +635,7 @@ Pushes the player
|
|||
*/
|
||||
void() trigger_push =
|
||||
{
|
||||
print("Found trigger_push\n");
|
||||
InitTrigger ();
|
||||
precache_sound ("ambience/windfly.wav");
|
||||
self.touch = trigger_push_touch;
|
||||
|
|
|
@ -64,6 +64,9 @@ void() W_FireAxe =
|
|||
WriteCoord (MSG_BROADCAST, org_x);
|
||||
WriteCoord (MSG_BROADCAST, org_y);
|
||||
WriteCoord (MSG_BROADCAST, org_z);
|
||||
|
||||
WriteByte (MSG_BROADCAST, 8);
|
||||
WriteString (MSG_BROADCAST, "overlong\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -72,6 +75,9 @@ void() W_FireAxe =
|
|||
WriteCoord (MSG_MULTICAST, org_x);
|
||||
WriteCoord (MSG_MULTICAST, org_y);
|
||||
WriteCoord (MSG_MULTICAST, org_z);
|
||||
|
||||
WriteByte (MSG_MULTICAST, 8);
|
||||
WriteString (MSG_MULTICAST, "overlong\n");
|
||||
multicast(org, MULTICAST_PVS);
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +237,9 @@ void(float damage, vector dir) TraceAttack =
|
|||
{
|
||||
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
||||
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
|
||||
#ifdef QWSSQC
|
||||
WriteByte (MSG_BROADCAST, 1); //count
|
||||
#endif
|
||||
WriteCoord (MSG_BROADCAST, org_x);
|
||||
WriteCoord (MSG_BROADCAST, org_y);
|
||||
WriteCoord (MSG_BROADCAST, org_z);
|
||||
|
|
|
@ -332,10 +332,17 @@ void() worldspawn =
|
|||
// 63 testing
|
||||
lightstyle(63, "a");
|
||||
|
||||
isdp = autocvar(test_dpcompat, 0);
|
||||
serverusingcsqc = checkextension("EXT_CSQC") || checkextension("EXT_CSQC_1");
|
||||
if (isdp)
|
||||
serverusingcsqc = TRUE;
|
||||
if (!serverusingcsqc)
|
||||
{
|
||||
if (checkbuiltin(print))
|
||||
print("Server does not support csqc\n");
|
||||
else
|
||||
{
|
||||
cvar_set("developer", "1");
|
||||
dprint("Server is not using csqc\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void() StartFrame =
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
../progs.dat
|
||||
|
||||
//#define QWSSQC
|
||||
#define SSQC
|
||||
optsall.qc
|
||||
#ifdef QSS
|
||||
#if defined(QSS) && !defined(QWSSQC)
|
||||
qsextensions.qc
|
||||
#undef CSQC
|
||||
#endif
|
||||
fteextensions.qc
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue