csaddon can now create/edit patches.

This commit is contained in:
Shpoike 2023-04-17 03:39:30 +01:00
parent 7b3096bbf5
commit 75c6527893
20 changed files with 904 additions and 331 deletions

View file

@ -14164,18 +14164,18 @@ image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned
image_t *tex;
if (!subdir)
subdir = "";
tex = Hash_Get(&imagetable, identifier);
tex = Hash_GetInsensitive(&imagetable, identifier);
while(tex)
{
if (!((tex->flags ^ flags) & (IF_CLAMP|IF_PALETTIZE|IF_PREMULTIPLYALPHA)))
{
if (r_ignoremapprefixes.ival || !strcmp(subdir, tex->subpath?tex->subpath:"") || ((flags|tex->flags) & IF_INEXACT))
if (r_ignoremapprefixes.ival || !Q_strcasecmp(subdir, tex->subpath?tex->subpath:"") || ((flags|tex->flags) & IF_INEXACT))
{
tex->regsequence = r_regsequence;
return tex;
}
}
tex = Hash_GetNext(&imagetable, identifier, tex);
tex = Hash_GetNextInsensitive(&imagetable, identifier, tex);
}
return NULL;
}
@ -14214,7 +14214,7 @@ static image_t *Image_CreateTexture_Internal (const char *identifier, const char
tex->fallbackheight = 0;
tex->fallbackfmt = TF_INVALID;
if (*tex->ident)
Hash_Add(&imagetable, tex->ident, tex, buck);
Hash_AddInsensitive(&imagetable, tex->ident, tex, buck);
return tex;
}
@ -14551,7 +14551,7 @@ void Image_DestroyTexture(image_t *tex)
Sys_UnlockMutex(com_resourcemutex);
#endif
if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex);
Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
Z_Free(tex);
}
@ -14789,7 +14789,7 @@ void Image_Shutdown(void)
{
tex = imagelist;
if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex);
Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
imagelist = tex->next;
if (tex->status == TEX_LOADED)
j++;

View file

@ -7209,7 +7209,7 @@ static struct {
{"patch_getcp", PF_patch_getcp, 0},
{"patch_getmesh", PF_patch_getmesh, 0},
{"patch_create", PF_patch_create, 0},
// {"patch_calculate", PF_patch_calculate, 0},
{"patch_evaluate", PF_patch_evaluate, 0},
#endif
#ifdef ENGINE_ROUTING
@ -8438,7 +8438,9 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
Con_Printf(CON_WARNING"Unable to load \"csprogsvers/%x.dat\"\n", csprogs_checksum);
}
if (csqc_singlecheats || anycsqc)
if (csprogsnum >= 0 && !Q_strcasecmp(csprogs_checkname, "csaddon.dat"))
; //using csaddon directly... map editor mode?
else if (csqc_singlecheats || anycsqc)
{
csaddonnum = PR_LoadProgs(csqcprogs, "csaddon.dat");
if (csaddonnum >= 0)

View file

@ -692,7 +692,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);
vfsfile_t *FS_OpenWithFriends(const char *fname, char *sysname, size_t sysnamesize, int numfriends, ...);
#define countof(array) (sizeof(array)/sizeof(array[0]))
#define countof(array) (sizeof(array)/sizeof((array)[0]))
#ifdef _WIN32
//windows doesn't support utf-8. Which is a shame really, because that's the charset we expect from everything.
char *narrowen(char *out, size_t outlen, wchar_t *wide);

View file

@ -559,7 +559,8 @@ static void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const ve
Patch_Evaluate
===============
*/
static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const int *tess, vec_t *dest, int comp )
#define Patch_Evaluate(p,numcp,tess,dest, comp) Patch_EvaluateStride(p,comp,numcp,tess,dest,comp,comp)
static void Patch_EvaluateStride(const vec_t *p, int pstride, const unsigned short *numcp, const int *tess, vec_t *dest, int deststride, int comp)
{
int num_patches[2], num_tess[2];
int index[3], dstpitch, i, u, v, x, y;
@ -570,14 +571,15 @@ static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const i
if (!tess[0] || !tess[1])
{ //not really a patch
for( i = 0; i < comp*numcp[1]*numcp[0]; i++ )
dest[i] = p[i];
for( u = 0; u < numcp[1]*numcp[0]; u++, dest += deststride, p += pstride)
for( i = 0; i < comp; i++ )
dest[i] = p[i];
return;
}
num_patches[0] = numcp[0] / 2;
num_patches[1] = numcp[1] / 2;
dstpitch = ( num_patches[0] * tess[0] + 1 ) * comp;
dstpitch = ( num_patches[0] * tess[0] + 1 ) * deststride;
step[0] = 1.0f / (float)tess[0];
step[1] = 1.0f / (float)tess[1];
@ -605,24 +607,113 @@ static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const i
// current 3x3 patch control points
for( i = 0; i < 3; i++ )
{
pv[i][0] = &p[( index[0]+i ) * comp];
pv[i][1] = &p[( index[1]+i ) * comp];
pv[i][2] = &p[( index[2]+i ) * comp];
pv[i][0] = &p[( index[0]+i ) * pstride];
pv[i][1] = &p[( index[1]+i ) * pstride];
pv[i][2] = &p[( index[2]+i ) * pstride];
}
tvec = dest + v * tess[1] * dstpitch + u * tess[0] * comp;
tvec = dest + v * tess[1] * dstpitch + u * tess[0] * deststride;
for( y = 0, t = 0.0f; y < num_tess[1]; y++, t += step[1], tvec += dstpitch )
{
Patch_Evaluate_QuadricBezier( t, pv[0][0], pv[0][1], pv[0][2], v1, comp );
Patch_Evaluate_QuadricBezier( t, pv[1][0], pv[1][1], pv[1][2], v2, comp );
Patch_Evaluate_QuadricBezier( t, pv[2][0], pv[2][1], pv[2][2], v3, comp );
for( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += comp )
for( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += deststride )
Patch_Evaluate_QuadricBezier( s, v1, v2, v3, tvec2, comp );
}
}
}
}
#ifdef TERRAIN
#include "gl_terrain.h"
patchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size)
{
int step[2], flat[2];
float subdivlevel;
unsigned int numverts;
patchtessvert_t *out;
int i;
if (subdiv[0]>=0 && subdiv[1]>=0)
{ //fixed
step[0] = subdiv[0];
step[1] = subdiv[1];
}
else
{
// find the degree of subdivision in the u and v directions
subdivlevel = bound(1, r_subdivisions.ival, 15);
Patch_GetFlatness ( subdivlevel, cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, flat );
step[0] = 1 << flat[0];
step[1] = 1 << flat[1];
}
if (!step[0] || !step[1])
{
size[0] = patch_cp[0];
size[1] = patch_cp[1];
}
else
{
size[0] = ( patch_cp[0] >> 1 ) * step[0] + 1;
size[1] = ( patch_cp[1] >> 1 ) * step[1] + 1;
}
if( size[0] <= 0 || size[1] <= 0 )
return NULL;
numverts = (unsigned int)size[0] * size[1];
// fill in
out = BZ_Malloc(sizeof(*out) * numverts);
for (i = 0; i < numverts*sizeof(*out)/sizeof(vec_t); i++)
((vec_t *)out)[i] = -1;
Patch_EvaluateStride ( cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->v, sizeof(*out)/sizeof(vec_t), countof(cp->v));
Patch_EvaluateStride ( cp->rgba, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->rgba, sizeof(*out)/sizeof(vec_t), countof(cp->rgba));
Patch_EvaluateStride ( cp->tc, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->tc, sizeof(*out)/sizeof(vec_t), countof(cp->tc));
return out;
}
unsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes)
{
int i, u, v, p;
// compute new indexes avoiding adding invalid triangles
unsigned int numindexes = 0;
index_t *indexes = out_indexes;
for (v = 0, i = 0; v < size[1]-1; v++)
{
for (u = 0; u < size[0]-1; u++, i += 6)
{
indexes[0] = p = v * size[0] + u;
indexes[1] = p + size[0];
indexes[2] = p + 1;
// if ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
// !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
// !VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )
{
indexes += 3;
numindexes += 3;
}
indexes[0] = p + 1;
indexes[1] = p + size[0];
indexes[2] = p + size[0] + 1;
// if ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
// !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
// !VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )
{
indexes += 3;
numindexes += 3;
}
}
}
return numindexes;
}
#endif
#define PLANE_NORMAL_EPSILON 0.00001

View file

@ -356,6 +356,7 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s
void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
#endif
void QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);

View file

@ -18,12 +18,6 @@ See gl_terrain.h for terminology, networking notes, etc.
#include "gl_terrain.h"
static plugterrainfuncs_t terrainfuncs;
typedef struct
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
} qcpatchvert_t;
cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes.");
@ -3321,6 +3315,8 @@ unsigned int Heightmap_PointContents(model_t *model, const vec3_t axis[3], const
for (i = 0; i < hm->numbrushes; i++)
{
br = &hm->wbrushes[i];
if (br->patch)
continue; //infinitely thin...
for (j = 0; j < br->numplanes; j++)
{
@ -3785,7 +3781,7 @@ static qboolean Heightmap_Trace_Patch(hmtrace_t *tr, brushes_t *brushinfo)
if (!patch->tessvert)
{
const struct patchcpvert_s *r1 = patch->cp, *r2;
const struct qcpatchvert_s *r1 = patch->cp, *r2;
w = patch->numcp[0];
h = patch->numcp[1];
@ -6255,6 +6251,8 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
out->planes = NULL;
out->faces = NULL;
out->numplanes = 0;
out->ispatch = !!brush->patch;
out->selected = false;
ClearBounds(out->mins, out->maxs);
if (brush->numplanes)
{
@ -6426,6 +6424,9 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
AddPointToBounds(out->mins, model->mins, model->maxs);
AddPointToBounds(out->maxs, model->mins, model->maxs);
if (out->patch && (out->patch->subdiv[0] || out->patch->subdiv[1]))
out->patch->tessvert = PatchInfo_Evaluate(out->patch->cp, out->patch->numcp, out->patch->subdiv, out->patch->tesssize);
return out;
}
@ -6481,6 +6482,11 @@ static void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx)
}
BZ_Free(br->planes);
if (br->patch)
{
BZ_Free(br->patch->tessvert);
BZ_Free(br->patch);
}
hm->numbrushes--;
hm->brushesedited = true;
//plug the hole with some other brush.
@ -6506,6 +6512,104 @@ static qboolean Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid)
return false;
}
static void Patch_Serialise(sizebuf_t *sb, brushes_t *br)
{
qbyte flags = 0;
unsigned int i, m = br->patch->numcp[0]*br->patch->numcp[1];
for (i = 0; i < m; i++)
{
if (br->patch->cp[i].rgba[0] != 1)
flags |= 1;
if (br->patch->cp[i].rgba[1] != 1)
flags |= 2;
if (br->patch->cp[i].rgba[2] != 1)
flags |= 4;
if (br->patch->cp[i].rgba[3] != 1)
flags |= 8;
}
MSG_WriteLong(sb, br->id);
MSG_WriteLong(sb, br->contents);
MSG_WriteShort(sb, br->patch->numcp[0]);
MSG_WriteShort(sb, br->patch->numcp[1]);
MSG_WriteByte(sb, flags);
MSG_WriteString(sb, br->patch->tex->shadername);
MSG_WriteShort(sb, br->patch->subdiv[0]);
MSG_WriteShort(sb, br->patch->subdiv[1]);
for (i = 0; i < m; i++)
{
MSG_WriteFloat(sb, br->patch->cp[i].v[0]);
MSG_WriteFloat(sb, br->patch->cp[i].v[1]);
MSG_WriteFloat(sb, br->patch->cp[i].v[2]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[0]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[1]);
if (flags&1)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[0]);
if (flags&2)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[1]);
if (flags&4)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[2]);
if (flags&8)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[3]);
}
}
static size_t Patch_DeserialiseHeader(brushes_t *br)
{
unsigned int numcp[2];
br->id = MSG_ReadLong();
br->contents = MSG_ReadLong();
br->numplanes = numcp[0] = (unsigned short)MSG_ReadShort();
br->axialplanes = numcp[1] = (unsigned short)MSG_ReadShort();
if (numcp[0]*numcp[1] > 8192)
return 0; //too many. reject it as bad.
return sizeof(*br->patch) + sizeof(*br->patch->cp)*(numcp[0]*numcp[1]-countof(br->patch->cp));
}
static qboolean Patch_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)
{
struct qcpatchvert_s vert;
qboolean flags;
unsigned int i, m;
flags = MSG_ReadByte();
br->patch = mem;
br->patch->numcp[0] = br->numplanes;
br->patch->numcp[1] = br->axialplanes;
br->numplanes = br->axialplanes = 0;
m = br->patch->numcp[0]*br->patch->numcp[1];
//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.
br->patch->tex = Terr_Brush_FindTexture(hm, MSG_ReadString());
br->patch->subdiv[0] = MSG_ReadShort();
br->patch->subdiv[1] = MSG_ReadShort();
for (i = 0; i < m; i++)
{
vert.v[0] = MSG_ReadFloat();
vert.v[1] = MSG_ReadFloat();
vert.v[2] = MSG_ReadFloat();
vert.tc[0] = MSG_ReadFloat();
vert.tc[1] = MSG_ReadFloat();
vert.rgba[0] = (flags&1)?MSG_ReadFloat():1;
vert.rgba[1] = (flags&2)?MSG_ReadFloat():1;
vert.rgba[2] = (flags&4)?MSG_ReadFloat():1;
vert.rgba[3] = (flags&8)?MSG_ReadFloat():1;
br->patch->cp[i] = vert;
}
return true;
}
static void Brush_Serialise(sizebuf_t *sb, brushes_t *br)
{
unsigned int i;
@ -6533,16 +6637,30 @@ static void Brush_Serialise(sizebuf_t *sb, brushes_t *br)
MSG_WriteFloat(sb, br->faces[i].stdir[1][3]);
}
}
static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br)
static size_t Brush_DeserialiseHeader(brushes_t *br, qboolean ispatch)
{
unsigned int i;
unsigned int maxplanes = br->numplanes;
br->ispatch = ispatch;
if (br->ispatch)
return Patch_DeserialiseHeader(br);
br->id = MSG_ReadLong();
br->contents = MSG_ReadLong();
br->numplanes = MSG_ReadLong();
if (br->numplanes > maxplanes)
return false;
if (br->numplanes > 8192)
return 0; //abusive
return sizeof(*br->faces) * br->numplanes
+ sizeof(*br->planes) * br->numplanes;
}
static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)
{
unsigned int i;
if (br->ispatch)
return Patch_Deserialise(hm, br, mem);
br->faces = mem;
br->planes = (vec4_t*)(br->faces + br->numplanes);
for (i = 0; i < br->numplanes; i++)
{
@ -6568,88 +6686,6 @@ static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br)
return true;
}
static void Patch_Serialise(sizebuf_t *sb, brushes_t *br)
{
qbyte flags = 0;
unsigned int i, m = br->patch->numcp[0]*br->patch->numcp[1];
for (i = 0; i < m; i++)
{
if (br->patch->cp[i].rgba[0] != 1)
flags |= 1;
if (br->patch->cp[i].rgba[1] != 1)
flags |= 2;
if (br->patch->cp[i].rgba[2] != 1)
flags |= 4;
if (br->patch->cp[i].rgba[3] != 1)
flags |= 8;
}
MSG_WriteLong(sb, br->id);
MSG_WriteByte(sb, flags);
MSG_WriteLong(sb, br->contents);
MSG_WriteString(sb, br->patch->tex->shadername);
MSG_WriteShort(sb, br->patch->numcp[0]);
MSG_WriteShort(sb, br->patch->numcp[1]);
MSG_WriteShort(sb, br->patch->subdiv[0]);
MSG_WriteShort(sb, br->patch->subdiv[1]);
for (i = 0; i < m; i++)
{
MSG_WriteFloat(sb, br->patch->cp[i].v[0]);
MSG_WriteFloat(sb, br->patch->cp[i].v[1]);
MSG_WriteFloat(sb, br->patch->cp[i].v[2]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[0]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[1]);
if (flags&1)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[0]);
if (flags&2)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[1]);
if (flags&4)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[2]);
if (flags&8)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[3]);
}
}
static qboolean Patch_Deserialise(heightmap_t *hm, brushes_t *br)
{
struct patchcpvert_s vert;
qboolean flags;
unsigned int i, maxverts = br->patch->numcp[0]*br->patch->numcp[1];
br->id = MSG_ReadLong();
flags = MSG_ReadByte();
br->contents = MSG_ReadLong();
//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.
br->patch->tex = Terr_Brush_FindTexture(hm, MSG_ReadString());
br->patch->numcp[0] = MSG_ReadShort();
br->patch->numcp[1] = MSG_ReadShort();
br->patch->subdiv[0] = MSG_ReadShort();
br->patch->subdiv[1] = MSG_ReadShort();
for (i = 0; i < br->patch->numcp[0]*br->patch->numcp[1]; i++)
{
vert.v[0] = MSG_ReadFloat();
vert.v[1] = MSG_ReadFloat();
vert.v[2] = MSG_ReadFloat();
vert.tc[0] = MSG_ReadFloat();
vert.tc[1] = MSG_ReadFloat();
vert.rgba[0] = (flags&1)?MSG_ReadFloat():1;
vert.rgba[1] = (flags&2)?MSG_ReadFloat():1;
vert.rgba[2] = (flags&4)?MSG_ReadFloat():1;
vert.rgba[3] = (flags&8)?MSG_ReadFloat():1;
if (i < maxverts)
br->patch->cp[i] = vert;
}
return i <= maxverts;
}
#ifndef SERVERONLY
heightmap_t *CL_BrushEdit_ForceContext(model_t *mod)
@ -6699,45 +6735,36 @@ void CL_Parse_BrushEdit(void)
else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert) //1=create/replace
{
brushes_t brush;
size_t tempmemsize;
hm = CL_BrushEdit_ForceContext(mod); //do this early, to ensure that the textures are correct
memset(&brush, 0, sizeof(brush));
if (cmd == hmcmd_patch_insert)
tempmemsize = Brush_DeserialiseHeader(&brush, (cmd == hmcmd_patch_insert));
if (!tempmemsize)
Host_EndGame("CL_Parse_BrushEdit: unparsable %s\n", brush.ispatch?"patch":"brush");
if (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))
Host_EndGame("CL_Parse_BrushEdit: unparsable %s\n", brush.ispatch?"patch":"brush");
if (!ignore) //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent).
{
const unsigned int maxpoints = 64*64;
brush.patch = alloca(sizeof(*brush.patch) + sizeof(*brush.patch->cp)*(maxpoints-countof(brush.patch->cp)));
brush.patch->numcp[0] = 1;
brush.patch->numcp[1] = maxpoints;
if (!Patch_Deserialise(hm, &brush))
Host_EndGame("CL_Parse_BrushEdit: unparsable patch\n");
}
else
{
brush.numplanes = 128;
brush.planes = alloca(sizeof(*brush.planes) * brush.numplanes);
brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes);
if (!Brush_Deserialise(hm, &brush))
Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n");
}
if (ignore)
return; //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent).
if (brush.id)
{
int i;
if (cls.demoplayback)
Terr_Brush_DeleteId(hm, brush.id);
else
if (brush.id)
{
for (i = 0; i < hm->numbrushes; i++)
int i;
if (cls.demoplayback)
Terr_Brush_DeleteId(hm, brush.id);
else
{
brushes_t *br = &hm->wbrushes[i];
if (br->id == brush.id)
return; //we already have it. assume we just edited it.
for (i = 0; i < hm->numbrushes; i++)
{
brushes_t *br = &hm->wbrushes[i];
if (br->id == brush.id)
return; //we already have it. assume we just edited it.
}
}
}
Terr_Brush_Insert(mod, hm, &brush);
}
Terr_Brush_Insert(mod, hm, &brush);
}
else if (cmd == hmcmd_prespawning)
{ //delete all
@ -6899,31 +6926,22 @@ qboolean SV_Parse_BrushEdit(void)
else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert)
{
brushes_t brush;
size_t tempmemsize;
memset(&brush, 0, sizeof(brush));
if (cmd == hmcmd_patch_insert)
brush.ispatch = (cmd == hmcmd_patch_insert);
tempmemsize = Brush_DeserialiseHeader(&brush, cmd == hmcmd_patch_insert);
if (!tempmemsize)
{
const unsigned int maxpoints = 64*64;
brush.patch = alloca(sizeof(*brush.patch) + sizeof(*brush.patch->cp)*(maxpoints-countof(brush.patch->cp)));
memset(brush.patch, 0, sizeof(*brush.patch));
brush.patch->numcp[0] = maxpoints;
brush.patch->numcp[1] = 1;
if (!Patch_Deserialise(hm, &brush))
{
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable patch\n", host_client->name);
return false;
}
Con_Printf("SV_Parse_BrushEdit: %s sent an abusive %s\n", host_client->name, brush.ispatch?"patch":"brush");
return false;
}
else
if (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))
{
brush.numplanes = 128;
brush.planes = alloca(sizeof(*brush.planes) * brush.numplanes);
brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes);
if (!Brush_Deserialise(hm, &brush))
{
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable brush\n", host_client->name);
return false;
}
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable brush\n", host_client->name);
return false;
}
if (!authorise)
{
SV_PrintToClient(host_client, PRINT_MEDIUM, "Brush editing ignored: you are not a mapper\n");
@ -6980,28 +6998,6 @@ qboolean SV_Parse_BrushEdit(void)
}
#endif
typedef struct
{
string_t shadername;
vec3_t planenormal;
float planedist;
vec3_t sdir;
float sbias;
vec3_t tdir;
float tbias;
} qcbrushface_t;
typedef struct
{
string_t shadername;
unsigned int contents;
unsigned int cp_width;
unsigned int cp_height;
unsigned int subdiv_x;
unsigned int subdiv_y;
vec3_t texinfo;
} qcpatchinfo_t;
static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elementsize, size_t elementcount, qboolean allownull)
{
//make sure that the sizes can't overflow
@ -7066,9 +7062,9 @@ void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
G_INT(OFS_RETURN) = br->patch->numcp[0]*br->patch->numcp[1];
else
{
maxverts = min(br->numplanes, maxverts);
maxverts = min(br->patch->numcp[0]*br->patch->numcp[1], maxverts);
for (j = 0; j < br->patch->numcp[0]*br->patch->numcp[1]; j++)
for (j = 0; j < maxverts; j++)
{
VectorCopy(br->patch->cp[j].v, out_verts->v);
Vector2Copy(br->patch->cp[j].tc, out_verts->tc);
@ -7082,7 +7078,43 @@ void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
}
}
}
// {"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
// {"patch_evaluate", PF_patch_evaluate, 0, 0, 0, 0, D("int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info)", "Calculates the geometry of a hyperthetical patch.")},
void QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
qcpatchinfo_t *inout_info = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*inout_info), 1, false);
unsigned int maxverts = G_INT(OFS_PARM2);
qcpatchvert_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*out_verts), maxverts, true);
qcpatchvert_t *in_cp = validateqcpointer(prinst, G_INT(OFS_PARM0), sizeof(*in_cp), inout_info->cp_width*inout_info->cp_height, false);
unsigned short numcp[] = {inout_info->cp_width, inout_info->cp_height}, size[2];
short subdiv[] = {inout_info->subdiv_x, inout_info->subdiv_y};
patchtessvert_t *working_verts = PatchInfo_Evaluate(in_cp, numcp, subdiv, size);
unsigned int i;
if (working_verts)
{
if (out_verts)
{
maxverts = min(maxverts, size[0]*size[1]);
for (i = 0; i < maxverts; i++)
{
VectorCopy(working_verts[i].v, out_verts[i].v);
Vector4Copy(working_verts[i].rgba, out_verts[i].rgba);
Vector2Copy(working_verts[i].tc, out_verts[i].tc);
}
}
BZ_Free(working_verts);
inout_info->cp_width = size[0]; //not really controlpoints, but the data works the same.
inout_info->cp_height = size[1];
}
else
inout_info->cp_width = inout_info->cp_height = 0; //erk...
inout_info->subdiv_x = inout_info->subdiv_y = 0; //make it as explicit tessellation, so we can maybe skip this.
G_INT(OFS_RETURN) = maxverts;
}
// {"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *vmw = prinst->parms->user;
@ -7113,10 +7145,10 @@ void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_
if (out_info)
{
out_info->contents = br->contents;
out_info->cp_width = br->patch->numcp[0];
out_info->cp_height = br->patch->numcp[1];
out_info->subdiv_x = br->patch->subdiv[0];
out_info->subdiv_y = br->patch->subdiv[1];
out_info->cp_width = br->patch->tesssize[0];
out_info->cp_height = br->patch->tesssize[1];
out_info->subdiv_x = 0;
out_info->subdiv_y = 0;
out_info->shadername = PR_TempString(prinst, br->patch->tex->shadername);
}
@ -7124,9 +7156,9 @@ void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_
G_INT(OFS_RETURN) = br->patch->tesssize[0]*br->patch->tesssize[1];
else
{
maxverts = min(br->numplanes, maxverts);
maxverts = min(br->patch->tesssize[0]*br->patch->tesssize[1], maxverts);
for (j = 0; j < br->patch->tesssize[0]*br->patch->tesssize[1]; j++)
for (j = 0; j < maxverts; j++)
{
VectorCopy(br->patch->tessvert[j].v, out_verts->v);
Vector2Copy(br->patch->tessvert[j].tc, out_verts->tc);

View file

@ -259,13 +259,32 @@ typedef struct brushtex_s
struct brushtex_s *next;
} brushtex_t;
typedef struct patchtessvert_s
{
vec3_t v;
vec4_t rgba;
vec2_t tc;
// vec3_t norm;
// vec3_t sdir;
// vec3_t tdir;
} patchtessvert_t;
typedef struct qcpatchvert_s
{
vec3_t v;
vec4_t rgba;
vec2_t tc;
} qcpatchvert_t;
patchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size);
unsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes);
typedef struct
{
unsigned int contents;
unsigned int contents; //bitmask
unsigned int id; //networked/gamecode id.
unsigned int axialplanes; //+x,+y,+z,-x,-y,-z. used for bevel stuff.
unsigned int numplanes;
unsigned char selected:1; //different shader stuff
unsigned char ispatch:1; //just for parsing really
vec4_t *planes;
vec3_t mins, maxs; //for optimisation and stuff
struct patchdata_s
@ -275,23 +294,10 @@ typedef struct
short subdiv[2]; //<0=regular q3 patch, 0=cp-only, >0=fixed-tessellation.
unsigned short tesssize[2];
struct patchtessvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
// vec3_t norm;
// vec3_t sdir;
// vec3_t tdir;
} *tessvert; //x+(y*tesssize[0])
patchtessvert_t *tessvert; //x+(y*tesssize[0])
//control points
struct patchcpvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
} cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s
qcpatchvert_t cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s
} *patch; //if this is NULL, then its a regular brush. otherwise its a patch.
struct brushface_s
{
@ -311,6 +317,28 @@ typedef struct
qbyte *lightdata;
} *faces;
} brushes_t;
typedef struct
{
string_t shadername;
vec3_t planenormal;
float planedist;
vec3_t sdir;
float sbias;
vec3_t tdir;
float tbias;
} qcbrushface_t;
typedef struct
{
string_t shadername;
unsigned int contents;
unsigned int cp_width;
unsigned int cp_height;
unsigned int subdiv_x;
unsigned int subdiv_y;
vec3_t texinfo;
} qcpatchinfo_t;
typedef struct heightmap_s
{
char path[MAX_QPATH];

View file

@ -11797,9 +11797,9 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
"} patchvert_t;\n" \
"#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\n"
{"patch_getcp", PF_patch_getcp, 0, 0, 0, 0, D(qcpatchvert "int(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
{"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
{"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
{"patch_create", PF_patch_create, 0, 0, 0, 0, D("int(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info)", "Inserts a new patch into the model. Return value is the new patch's id.")},
// {"patch_calculate", PF_patch_calculate, 0, 0, 0, 0, D("int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, __inout patchinfo_t inout_info)", "Calculates the geometry of a hyperthetical patch.")},
{"patch_evaluate", PF_patch_evaluate, 0, 0, 0, 0, D("int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info)", "Calculates the geometry of a hyperthetical patch.")},
#endif
#ifdef ENGINE_ROUTING

View file

@ -3,7 +3,7 @@ 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);
int points = brush_getfacepoints(mod, id, ++facenum, &facepoints[0], facepoints.length);
if (!points)
break; //end of face list, I guess
@ -19,6 +19,77 @@ void(int mod, int id) DrawEngineBrushWireframe =
R_EndPolygon();
}
};
void(patchvert_t *cp, patchinfo_t info, vector col, float alpha) DrawQCPatchTextured;
void(int mod, int id, float alpha) DrawEngineBrushFaded =
{ //draw one of the engine's brushes, but faded.
const vector col = '1 1 1';
int contents;
brushface_t faces[MAX_BRUSHFACES];
int numfaces = brush_get(mod, id, &faces[0], faces.length, &contents);
if (!numfaces)
{
numfaces = patch_getmesh(mod, id, __NULL__, 0, __NULL__); //ask how much space we need
if (numfaces)
{
patchvert_t *v = memalloc(sizeof(*v)*numfaces);
patchinfo_t patchinfo;
patch_getmesh(mod, id, v, numfaces, &patchinfo); //now we can actually get it
DrawQCPatchTextured(v, patchinfo, col, alpha);
memfree(v);
}
return;
}
for(int f = 0; f < numfaces; f++)
{
int points = brush_getfacepoints(mod, id, 1+f, &facepoints[0], facepoints.length);
if (!points)
continue;
//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
string materialname = strcat("textures/", faces[f].shadername);
shaderforname(materialname,
sprintf("{"
"{\n"
"map \"%s\"\n"
"rgbgen vertex\n"
"alphagen vertex\n"
"}\n"
"}", faces[f].shadername));
vector sz = drawgetimagesize(materialname);
R_BeginPolygon(materialname, 3);
for (int 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(patchvert_t *vert, patchinfo_t info, string shader, vector col, float alpha) DrawQCPatchWireframe =
{
int x,y,cp;
R_BeginPolygon(shader);
for (y = 0, cp = 0; y < info.cpheight; y++, cp++)
for (x = 0; x < info.cpwidth-1; x++)
{
R_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);
cp++;
R_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);
R_EndPolygon();
}
for (x = 0; x < info.cpwidth; x++)
for (y = 0, cp=x; y < info.cpheight-1; y++)
{
R_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);
cp += info.cpwidth;
R_PolygonVertex(vert[cp].xyz, [vert[cp].s, vert[cp].t], col, alpha);
R_EndPolygon();
}
};
void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushWireframe =
{
@ -69,7 +140,8 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.
//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.
//FIXME: we don't manage to pick up the size of the original wad image
shaderforname(faces[f].shadername,
string materialname = strcat("textures/", faces[f].shadername);
shaderforname(materialname,
sprintf("{"
"{\n"
"map \"%s\"\n"
@ -78,14 +150,46 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu
"}\n"
"}", faces[f].shadername));
vector sz = drawgetimagesize(faces[f].shadername);
R_BeginPolygon(faces[f].shadername);
vector sz = drawgetimagesize(materialname);
R_BeginPolygon(materialname, 3);
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(patchvert_t *cp, patchinfo_t info, vector col, float alpha) DrawQCPatchTextured =
{
int x, y;
string materialname = strcat("textures/", info.shadername);
shaderforname(materialname,
sprintf("{"
"{\n"
"map \"%s\"\n"
"rgbgen vertex\n"
"alphagen vertex\n"
"}\n"
"}", info.shadername));
R_BeginPolygon(materialname);
for (y = 0; y < info.cpheight-1; y++, cp++)
for (x = 0; x < info.cpwidth-1; x++, cp++)
{
#if 1
R_PolygonVertex(cp[0].xyz, [cp[0].s,cp[0].t], col, alpha);
R_PolygonVertex(cp[1].xyz, [cp[1].s,cp[1].t], col, alpha);
R_PolygonVertex(cp[1+info.cpwidth].xyz, [cp[1+info.cpwidth].s,cp[1+info.cpwidth].t], col, alpha);
R_PolygonVertex(cp[0+info.cpwidth].xyz, [cp[0+info.cpwidth].s,cp[0+info.cpwidth].t], col, alpha);
#else
R_PolygonVertex(cp[0].xyz, [cp[0].s,cp[0].t], cp[0].rgb, cp[0].a);
R_PolygonVertex(cp[1].xyz, [cp[1].s,cp[1].t], cp[1].rgb, cp[1].a);
R_PolygonVertex(cp[1+info.cpwidth].xyz, [cp[1+info.cpwidth].s,cp[1+info.cpwidth].t], cp[1+info.cpwidth].rgb, cp[1+info.cpwidth].a);
R_PolygonVertex(cp[0+info.cpwidth].xyz, [cp[0+info.cpwidth].s,cp[0+info.cpwidth].t], cp[0+info.cpwidth].rgb, cp[0+info.cpwidth].a);
#endif
R_EndPolygon();
}
};
void(vector *p, int points, string shader, vector col, float alpha) DrawAxisExtensions =
{

View file

@ -154,6 +154,26 @@ int(int mod, brushface_t *faces, int numfaces, int contents, float selectnow) br
brush_select(mod, h->id);
return h->id;
};
//create and journal.
int(int mod, patchvert_t *vert, patchinfo_t info, float selectnow) patch_history_create =
{
int totalcp = info.cpwidth * info.cpheight;
history_t *h = history_allocate();
h->patchdata = memalloc(sizeof(patchvert_t) * totalcp);
memcpy(h->patchdata, vert, sizeof(patchvert_t) * totalcp);
h->patchinfo = info;
h->brushmodel = mod;
h->wasdelete = FALSE;
h->id = patch_create(h->brushmodel, 0, vert, info);
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 =
{
@ -197,3 +217,17 @@ int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_histo
}
return id;
};
int(int mod, int id, patchvert_t *vert, patchinfo_t info) patch_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 = patch_history_create(mod, vert, info, FALSE);
if (wasselected)
{
selectedbrushes[wasselected-1].id = id;
brush_selected(mod, id, -1, TRUE);
}
return id;
};

View file

@ -67,7 +67,7 @@ int(brushface_t *fa, int famax, vector *points, int numpoints, float height) Bru
vector(vector guess) brush_snappoint =
{
if (nogrid)
if (nogrid || autocvar_ca_grid <= 0)
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.
@ -107,36 +107,60 @@ vector(vector guess) brush_snappoint =
//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)
void brushface_translate(vector displacement)
{
int i;
for (i = 0; i < numfaces; i++, fa++)
if (tmp.numcp)
{
fa->planedist += fa->planenormal * displacement;
fa->sbias -= fa->sdir * displacement;
fa->tbias -= fa->tdir * displacement;
for (i = 0; i < tmp.numcp; i++)
tmp.cp[i].xyz += displacement;
}
else
{
for (i = 0; i < tmp.numfaces; i++)
{
tmp.faces[i].planedist += tmp.faces[i].planenormal * displacement;
tmp.faces[i].sbias -= tmp.faces[i].sdir * displacement;
tmp.faces[i].tbias -= tmp.faces[i].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)
void brushface_rotate(void)
{
for (int i = 0; i < numfaces; i++, fa++)
if (tmp.numcp)
{
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;
for (int i = 0; i < tmp.numcp; i++)
{
vector orig = tmp.cp[i].xyz;
tmp.cp[i].xyz = [orig * v_forward,
orig * -v_right,
orig * v_up];
//don't need to touch tcs
}
}
else
{
brushface_t *fa = tmp.faces;
int numfaces = tmp.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->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;
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;
}
}
};
@ -415,16 +439,36 @@ void() brushedit_resettextures =
{
int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
int face = selectedbrushes[sb].face;
__uint64 facemask = selectedbrushes[sb].facemask;
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)
if (facemask & (1lu << i))
reset_texturecoords(&tmp.faces[i]);
brush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);
}
}
};
void(string newtexture) brushedit_settexture =
{
for (int sb = 0; sb < selectedbrushcount; sb++)
{
int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
__uint64 facemask = selectedbrushes[sb].facemask;
int planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);
if (planecount)
{
for (int i = 0; i < planecount; i++)
if (facemask & (1lu << i))
tmp.faces[i].shadername = newtexture;
brush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);
}
}
};

View file

@ -3,6 +3,9 @@ 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;
var float autocvar_ca_explicitpatch = 0;
var float autocvar_ca_cpwidth = 2;
var float autocvar_ca_cpheight = 2;
#define EPSILON (1.0 / 32) //inprecision sucks.
@ -36,7 +39,8 @@ enum : int
BT_ROTATE,
BT_MERGE,
BT_PUSHFACE,
BT_CREATE,
BT_CREATEBRUSH,
BT_CREATEPATCH,
BT_CREATEDRAG,
BT_CLONEDISPLACE,
BT_SLICE,
@ -58,15 +62,14 @@ typedef struct
{
int model;
int id;
int face;
//fixme: do we need an array of faces here?
__uint64 facemask; //MAX_BRUSHFACES is 64... handy.
} 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 =
int(int modelidx, int brushid) brush_isselected = //0 for faceid means 'all'
{
for (int i = 0; i < selectedbrushcount; i++)
if (selectedbrushes[i].id == brushid)
@ -74,6 +77,14 @@ int(int modelidx, int brushid) brush_isselected =
return i+1;
return 0;
};
int(int modelidx, int brushid, int faceid) brushface_isselected = //0 for faceid means 'all'
{
for (int i = 0; i < selectedbrushcount; i++)
if (selectedbrushes[i].id == brushid)
if (selectedbrushes[i].model == modelidx)
return !!(selectedbrushes[i].facemask&(1ull<<(faceid-1i)));
return 0;
};
int(int modelidx, int brushid) brush_deselect =
{
int i = brush_isselected(modelidx, brushid);
@ -84,6 +95,25 @@ int(int modelidx, int brushid) brush_deselect =
selectedbrushcount--;
return TRUE;
};
int(int modelidx, int brushid, int face) brushface_deselect =
{
//deselecting a single face
int i = brush_isselected(modelidx, brushid); //get the brush selection index
if (!i)
return FALSE;
i--;
face--;
if (!selectedbrushes[i].facemask) //no faces means all..
selectedbrushes[i].facemask = ~0ul;
selectedbrushes[i].facemask &= ~(1ul<<face);
if (!selectedbrushes[i].facemask)
{ //no faces left selected.
brush_selected(modelidx, brushid, -1, FALSE);
memcpy(&selectedbrushes[i], &selectedbrushes[i+1], sizeof(selbrush_t)*(selectedbrushcount-i));
selectedbrushcount--;
}
return TRUE;
};
void() brush_deselectall =
{
for (int i = 0; i < selectedbrushcount; i++)
@ -101,8 +131,29 @@ void(int modelidx, int brushid) brush_select =
selectedbrushes = n;
n[selectedbrushcount].model = modelidx;
n[selectedbrushcount].id = brushid;
n[selectedbrushcount].facemask = ~0ul;
selectedbrushcount++;
brush_selected(modelidx, brushid, -1, TRUE);
}
selectedbrushmodel = modelidx;
};
void(int modelidx, int brushid, int faceid) brushface_select =
{
faceid--;
int i = brush_isselected(modelidx, brushid); //get the brush selection index
if (!i)
{ //brush not selected yet
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;
n[selectedbrushcount].facemask = 0;
selectedbrushcount++;
brush_selected(modelidx, brushid, -1, TRUE);
i = selectedbrushcount;
}
selectedbrushes[i-1].facemask |= 1ul<<faceid;
selectedbrushmodel = modelidx;
};

View file

@ -14,6 +14,9 @@ typedef struct
} vertsoup_t;
vertsoup_t vertedit;
static patchinfo_t patchinfo;
static patchvert_t *patchvert;
static int maxcp = 64*64;
//take a brush apart and generate trisoup from it.
@ -29,7 +32,38 @@ void(vertsoup_t *vertedit, int model, int brush) Debrushify =
vertedit->model = model;
vertedit->brush = brush;
vertedit->numidx = 0;
vertedit->numverts = 0;
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);
if (vertedit->numverts)
{ //okay, this is a patch. one logical face.
memfree(vertedit->i);
memfree(vertedit->p);
vertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);
if (!patchvert)
patchvert = memalloc(sizeof(*patchvert)*maxcp);
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);
for (i = 0; i < vertedit->numverts; i++)
vertedit->p[i] = patchvert[i].xyz;
//some of the rest of the code assumes we have indexes. oh well.
k = (patchinfo.cpwidth-1)*(patchinfo.cpheight-1);
vertedit->numidx = k*6;
vertedit->i = ni = memalloc(sizeof(*ni) * vertedit->numidx);
for (i = 0; i < k; i++, ni+=6)
{
ni[0] = i;
ni[1] = i+1;
ni[2] = i+patchinfo.cpwidth;
ni[3] = i+1;
ni[4] = i+patchinfo.cpwidth+1;
ni[5] = i+patchinfo.cpwidth;
}
return;
}
//brush.
for (i = 0; ; )
{
points = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length);
@ -78,17 +112,36 @@ void(vertsoup_t *vertedit, int model, int brush) Debrushify =
};
//determines only the various points of the brush, without duplicates. doesn't care about indexes.
void(brushface_t *faces, int numfaces) DebrushifyLite =
void(void) DebrushifyLite =
{
int points, k;
vector p;
vector *np;
int fi[64];
vertedit.numidx = 0;
vertedit.numverts = 0;
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);
if (vertedit->numverts)
{ //okay, this is a patch. one logical face.
memfree(vertedit->i);
memfree(vertedit->p);
vertedit->i = __NULL__; //don't bother.
vertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);
if (!patchvert)
patchvert = memalloc(sizeof(*patchvert)*maxcp);
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);
for (int i = 0; i < vertedit->numverts; i++)
vertedit->p[i] = patchvert[i].xyz;
return;
}
for (int i = 0; ; )
{
points = brush_calcfacepoints(++i, faces, numfaces, facepoints, facepoints.length);
points = brush_calcfacepoints(++i, tmp.faces, tmp.numfaces, facepoints, facepoints.length);
if (!points)
break;
@ -140,7 +193,7 @@ static float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) pl
void(vertsoup_t *soup, int drawit) Rebrushify =
{
brushface_t faces[64];
brushface_t faces[MAX_BRUSHFACES];
int numfaces, f, point;
string shader = 0;
@ -148,6 +201,36 @@ void(vertsoup_t *soup, int drawit) Rebrushify =
float d;
int o=0;
f = patch_getcp(soup->model, soup->brush, patchvert, maxcp, &patchinfo);
if (f)
{
for (o = 0; o < f; o++)
patchvert[o].xyz = soup.p[o];
if (drawit)
{
const int maxtessverts=64*64;
patchinfo_t tessinfo = patchinfo;
patchvert_t *tessverts = memalloc(sizeof(*tessverts)*maxtessverts);
if (patch_evaluate(patchvert, tessverts, maxtessverts, &tessinfo) <= maxtessverts)
{
//draw textured preview (yay for fullbright lighting being overbright)
DrawQCPatchTextured(tessverts, tessinfo, '0.7 0.7 0.7', 0.7); //display the patch. in-place so you know what it'll actually look like. stoopid tessellation.
DrawQCPatchWireframe(tessverts, tessinfo, "chop", [0.3,0,0.3], 1); //show a somewhat faded indication of how many quads are actually being used.
}
memfree(tessverts);
//draw it wireframe without depth testing
DrawQCPatchWireframe(patchvert, patchinfo, "chop", [0,0,1], 1);
}
else
{
patch_history_edit(soup->model, soup->brush, patchvert, patchinfo);
soup->numidx = 0;
soup->numverts = 0;
}
return;
}
tmp.numfaces = brush_get(soup->model, soup->brush, tmp.faces, tmp.faces.length, &tmp.contents);
@ -305,9 +388,6 @@ p3p1edge:
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);

View file

@ -183,7 +183,7 @@ void() wrap_renderscene =
return;
}
if (mousedown == 2)
if (mousedown & 2)
{ //RMB re-enables mlook
if (releasedmouse)
{
@ -269,8 +269,8 @@ void() wrap_renderscene =
drawcharacter(curmousepos - '4 4', '+', '8 8', '1 1 1', 1);
};
var float(float,float,float) orig_input_event = __NULL__;
float (float event, float parama, float paramb) wrap_InputEvent =
var float(float,float,float,float) orig_input_event = __NULL__;
float (float event, float parama, float paramb, float devid) CSQC_InputEvent =
{
if (event == IE_KEYDOWN || event == IE_KEYUP)
{
@ -341,23 +341,32 @@ float (float event, float parama, float paramb) wrap_InputEvent =
}
if (parama == K_MOUSE1)
{
mousedown = 1;
mousedown |= 1;
return TRUE;
}
if (parama == K_MOUSE2)
{
mousedown = 2;
mousedown |= 2;
return TRUE;
}
if (parama == K_MOUSE3)
{
mousedown |= 4;
return TRUE;
}
}
else if (event == IE_KEYUP)
{
if (parama == K_MOUSE1-1+mousedown)
mousedown = FALSE;
if (parama == K_MOUSE1)
mousedown &= ~1;
if (parama == K_MOUSE2)
mousedown &= ~2;
if (parama == K_MOUSE3)
mousedown &= ~4;
}
else if (event == IE_MOUSEDELTA)
{
if (mousedown == 2)
if (mousedown & 2)
return FALSE;
originalmousepos = curmousepos;
curmousepos_x += parama;
@ -377,7 +386,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
{
curmousepos_x = parama;
curmousepos_y = paramb;
if (mousedown == 2)
if (mousedown & 2)
return FALSE;
return TRUE;
}
@ -403,7 +412,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
#endif
if (orig_input_event)
return orig_input_event(event, parama, paramb);
return orig_input_event(event, parama, paramb, devid);
return FALSE;
};
@ -520,7 +529,7 @@ void() CSQC_Input_Frame =
if (pointedshadername != "")
{
input_buttons = input_buttons - (input_buttons & 1);
input_buttons &= ~1;
makevectors(input_angles);
if (v_forward * pointedsurfacenormal >= 0)
@ -607,10 +616,10 @@ float(string cmd) CSQC_ConsoleCommand =
};
/*this is a fallback function, in case the main progs does not have one*/
float (float event, float parama, float paramb, float devid) CSQC_InputEvent =
/*float (float event, float parama, float paramb, float devid) CSQC_InputEvent =
{
return wrap_InputEvent(event, parama, paramb);
};
};*/
void(float prevprogs) init =
{
@ -626,9 +635,9 @@ void(float prevprogs) init =
externset(0, wrap_renderscene, "renderscene");
externset(0, wrap_addentities, "addentities");
/*wrap the parent's input event function*/
orig_input_event = (float(float, float, float))externvalue(0, "CSQC_InputEvent");
externset(0, wrap_InputEvent, "CSQC_InputEvent");
/*wrap the parent's input event function in favour of ours*/
orig_input_event = (float(float, float, float, float))externvalue(0, "CSQC_InputEvent");
externset(0, CSQC_InputEvent, "CSQC_InputEvent");
}
csfixups();
localcmd(sprintf("alias rtlight_editor \"set ca_show 1; set ca_editormode %g\"\n", MODE_LIGHTEDIT));
@ -640,11 +649,16 @@ void(float prevprogs) init =
//brush editor needs some commands for easier binds.
editor_brushes_registercommands();
};
void() initents =
{
world_hitcontentsmaski = &world.hitcontentsmaski;
}
void() CSQC_Shutdown =
{
#ifdef CAMQUAKE
spline_shutdown();
#endif
brush_deselectall(); //so they don't stay hidden when restarted...
};

View file

@ -6,7 +6,7 @@
#define CSQC
#define _ACCESSORS
#pragma target FTE
#pragma target FTE_5768
//#pragma optimise 2
//#pragma optimise no-filename

View file

@ -27,6 +27,7 @@ void() csfixups =
ptr_self = (entity*)externvalue(0, "&self");
};
int *world_hitcontentsmaski; //evilness, so we can assign to world without errors.
vector mousenear;
vector mousefar;

View file

@ -2414,11 +2414,12 @@ typedef struct
int(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info) patch_getcp = #0:patch_getcp; /*
Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */
int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info) patch_getmesh = #0:patch_getmesh; /*
int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info) patch_getmesh = #0:patch_getmesh; /*
Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error. */
int(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info) patch_create = #0:patch_create; /*
Inserts a new patch into the model. Return value is the new patch's id. */
int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info) patch_evaluate = #0;
typedef struct
{

View file

@ -41,19 +41,28 @@ static void() editor_brushes_drawselected =
int facenum, point, points;
vector col;
for (int sb = 0; sb < selectedbrushcount; sb++)
{
int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
//draw a faded version of all the brushes.
DrawEngineBrushFaded(model, brush, 0.5);
}
//draw all selected brush faces.
for (int sb = 0; sb < selectedbrushcount; sb++)
{
int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
int face = selectedbrushes[sb].face;
for(facenum = 0;;)
__uint64 facemask = selectedbrushes[sb].facemask;
for(facenum = 0;; facenum++)
{
points = brush_getfacepoints(model, brush, ++facenum, facepoints, MAX_FACEPOINTS);
points = brush_getfacepoints(model, brush, 1+facenum, facepoints, MAX_FACEPOINTS);
if (!points)
break; //end of face list, I guess
if (facenum == face)
if (facemask & (1ul<<facenum))
col = [0,intensity,0];
else
col = [intensity,0,0];
@ -238,7 +247,7 @@ void(vector mousepos) editor_brushes_addentities =
Rebrushify(&vertedit, TRUE);
}
else if (brushtool == BT_CREATE)
else if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
{
editor_brushes_drawselected();
@ -335,7 +344,7 @@ void(vector mousepos) editor_brushes_addentities =
{
int model = oldselectedbrushes[sb].model;
int brush = oldselectedbrushes[sb].id;
int face = oldselectedbrushes[sb].face;
__uint64 facemask = oldselectedbrushes[sb].facemask;
if (!brush)
continue;
@ -344,19 +353,25 @@ void(vector mousepos) editor_brushes_addentities =
if (mousetool == BT_PUSHFACE)
{
if (!face)
continue; //FIXME: should not happen.
tmp.faces[face-1].planedist += tmp.faces[face-1].planenormal * displace;
for (facenum = 0; facenum < tmp.numfaces; facenum++)
{
if (facemask & (1ul<<facenum))
tmp.faces[facenum].planedist += tmp.faces[facenum].planenormal * displace;
}
}
else if (mousetool == BT_MOVETEXTURE)
{
if (!face)
continue; //FIXME: should not happen.
tmp.faces[face-1].sbias -= tmp.faces[face-1].sdir * displace;
tmp.faces[face-1].tbias -= tmp.faces[face-1].tdir * displace;
for (facenum = 0; facenum < tmp.numfaces; facenum++)
{
if (facemask & (1ul<<facenum))
{
tmp.faces[facenum].sbias -= tmp.faces[facenum].sdir * displace;
tmp.faces[facenum].tbias -= tmp.faces[facenum].tdir * displace;
}
}
}
else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE)
brushface_translate(tmp.faces, tmp.numfaces, displace);
brushface_translate(displace);
else if (mousetool == BT_ROTATE)
{
//find the brush's middle (based on its bbox)
@ -365,18 +380,20 @@ void(vector mousepos) editor_brushes_addentities =
makevectors(col);
//move it so its pivot is at the origin
brushface_translate(tmp.faces, tmp.numfaces, -mid);
//rotate it
brushface_rotate(tmp.faces, tmp.numfaces);
brushface_translate(-mid);
//rotate it (by v_forward etc)
brushface_rotate();
//reposition it around its pivot again
brushface_translate(tmp.faces, tmp.numfaces, mid);
brushface_translate(mid);
}
else
continue;
if (mousetool == BT_MOVETEXTURE)
if (0)//mousetool == BT_MOVETEXTURE)
{
points = brush_calcfacepoints(model, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
DrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3); //faded to give an idea, without stopping you from seeing what you're aligning it to.
/*points = brush_calcfacepoints(model, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
if (points)
{
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.
@ -396,39 +413,45 @@ void(vector mousepos) editor_brushes_addentities =
for (point = 0; point < points; point++)
R_PolygonVertex(facepoints[point] + tmp.faces[face-1].planenormal*0.1, [(facepoints[point] * tmp.faces[face-1].sdir + tmp.faces[face-1].sbias)/sz_x, (facepoints[point] * tmp.faces[face-1].tdir + tmp.faces[face-1].tbias)/sz_y], col, 1);
R_EndPolygon();
}
}*/
}
else
{
//draw it wireframe
for(facenum = 0; facenum < tmp.numfaces;)
//draw it wireframe WITH depth testing
if (tmp.numcp)
DrawQCPatchWireframe(tmp.cp, tmp.patchinfo, "terrainedit", '0 0 1', 1);
else
{
points = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
if (!points)
continue; //should probably warn somehow about this
//should we use two colour channels? one depth one not?
if (facenum == face)
col = '1 0 0';
else
col = '0 0.5 0';
R_BeginPolygon("chop");
R_PolygonVertex(facepoints[0], '0 0', col, 1);
for (point = 0; point < points-1; )
//draw it wireframe
for(facenum = 0; facenum < tmp.numfaces;)
{
point++;
R_PolygonVertex(facepoints[point], '0 0', col, 1);
points = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
if (!points)
continue; //should probably warn somehow about this
//should we use two colour channels? one depth one not?
if (facemask & (1ul<<facenum))
col = '1 0 0';
else
col = '0 0.5 0';
R_BeginPolygon("chop");
R_PolygonVertex(facepoints[0], '0 0', col, 1);
for (point = 0; point < points-1; )
{
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();
R_PolygonVertex(facepoints[point], '0 0', col, 1);
}
R_PolygonVertex(facepoints[0], '0 0', col, 1);
R_EndPolygon();
DrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3); //faded to give an idea, without stopping you from seeing what you're aligning it to.
DrawQCBrushWireframe(tmp.faces, tmp.numfaces, "terrainedit", '0 0 1', 1);
}
}
//draw it wireframe WITH depth testing
DrawQCBrushWireframe(tmp.faces, tmp.numfaces, "terrainedit", '0 0 1', 1);
DebrushifyLite(tmp.faces, tmp.numfaces);
DebrushifyLite();
DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1);
if (!mousedown)
@ -438,6 +461,8 @@ void(vector mousepos) editor_brushes_addentities =
if (displace*displace > 1)
brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
}
else if (tmp.numcp)
patch_history_edit(model, brush, tmp.cp, tmp.patchinfo);
else
brush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents);
bt_points = 0;
@ -478,7 +503,9 @@ void(vector mousepos) editor_brushes_addentities =
vector o = mousenear;
if (vlen(o - t) > 8192 && !autocvar_ca_brush_view)
t = o + normalize(t)*8192;
*world_hitcontentsmaski = ~0; //I want to be able to select water...
traceline(o, t, TRUE, world);
*world_hitcontentsmaski = 0; //don't break stuff
#if 0
//find all the brushes within 32qu of the mouse cursor's impact point
@ -515,7 +542,7 @@ void(vector mousepos) editor_brushes_addentities =
// bt_point[showpoints++] = brush_snappoint(trace_endpos);
showpoints = 3;
}
if (brushtool == BT_CREATE)
if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
bt_point[showpoints++] = brush_snappoint(trace_endpos);
//FIXME: if slicing, draw the intersection
@ -607,7 +634,13 @@ void(vector mousepos) editor_brushes_overlay =
case BT_CREATEDRAG:
drawrawstring('0 32 0', "Drag+Create", '8 8 0', '1 1 1', 1);
break;
case BT_CREATE:
case BT_CREATEPATCH:
if (autocvar_ca_explicitpatch)
drawrawstring('0 32 0', sprintf("Create Explicit %g*%g Patch", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);
else
drawrawstring('0 32 0', sprintf("Create Auto %g*%g Patch", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);
break;
case BT_CREATEBRUSH:
drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1);
break;
case BT_SLICE:
@ -653,10 +686,12 @@ brusheditormodes
registercommand("brushedit_redo");
registercommand("brushedit_delete");
registercommand("brushedit_create");
registercommand("brushedit_createpatch");
registercommand("brushedit_slice");
registercommand("brushedit_matchface");
registercommand("brushedit_resettexcoords");
registercommand("brushedit_settexture");
registercommand("brushedit_scrolltex");
registercommand("brushedit_subtract");
registercommand("brushedit_binds");
registercommand("brushedit_binds_default");
@ -679,13 +714,21 @@ brusheditormodes
brush_undo();
break;
case "brushedit_create":
brushtool = BT_CREATE;
brushtool = BT_CREATEBRUSH;
bt_points = 0;
break;
case "brushedit_createpatch":
brushtool = BT_CREATEPATCH;
bt_points = 0;
break;
case "brushedit_slice":
brushtool = BT_SLICE;
bt_points = 0;
break;
case "brushedit_scrolltex":
brushtool = BT_MOVETEXTURE;
bt_points = 0;
break;
case "+brushedit_nogrid": nogrid = TRUE; break;
case "-brushedit_nogrid": nogrid = FALSE; break;
@ -719,9 +762,7 @@ brusheditormodes
brushedit_resettextures();
break;
case "brushedit_settexture":
//IMPLEMENTME
brushtool = BT_NONE;
bt_points = 0;
brushedit_settexture(argv(1));
break;
case "brushedit_subtract":
brushedit_subtract();
@ -737,15 +778,16 @@ brusheditormodes
localcmd("bind ctrl+z brushedit_undo\n");
localcmd("bind ctrl+y brushedit_redo\n");
localcmd("bind ctrl+i brushedit_create\n");
localcmd("bind ctrl+p brushedit_createpatch\n");
localcmd("bind ctrl+s brushedit_slice\n");
localcmd("bind ctrl+m brushedit_matchface\n");
localcmd("bind ctrl+delete brushedit_delete\n");
localcmd("bind ctrl+del brushedit_delete\n");
localcmd("bind ctrl+backspace brushedit_subtract\n");
localcmd("bind ctrl+c +brushedit_clone\n");
localcmd("bind ctrl+d +brushedit_draw\n");
localcmd("bind ctrl+r +brushedit_rotate\n");
localcmd("bind ctrl+f +brushedit_pushface\n");
localcmd("bind ctrl+t +brushedit_scrolltex\n");
localcmd("bind ctrl+t brushedit_scrolltex\n");
localcmd("bind ctrl+v +brushedit_vertex\n");
break;
case "brushedit_binds":
@ -806,7 +848,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
{
if (vertedit.selectedvert1 != -1)
{
mousedown = TRUE;
mousedown |= 1;
mousetool = BT_VERTEXEDIT;
if (vertedit.selectedvert2 != -1)
{
@ -823,7 +865,9 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
return TRUE;
}
*world_hitcontentsmaski = ~0; //I want to be able to select water...
traceline(o, t, TRUE, world);
*world_hitcontentsmaski = 0; //don't break stuff.
float tracemodel = trace_ent.modelindex;
/*if (brushtool == BT_CREATEDRAG || (brushtool == BT_PUSHFACE && selectedbrushface))
@ -835,10 +879,12 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
else*/ if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE)
trace_brush_faceid = 0;
if (brushtool == BT_SLICE || brushtool == BT_CREATE)
if (brushtool == BT_SLICE || brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
{
if (brushtool == BT_SLICE && bt_points > 2)
bt_points = 2;
if (brushtool == BT_CREATEPATCH && bt_points > 3)
bt_points = 3;
//FIXME: BT_CREATE is planar. so keep the points planar too.
//FIXME: need a way to move points already placed
@ -883,10 +929,10 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
if (!tracemodel || !trace_brush_id)
{
}
else if (brush_isselected(tracemodel, trace_brush_id))
brush_deselect(tracemodel, trace_brush_id);
else if (brushface_isselected(tracemodel, trace_brush_id, trace_brush_faceid))
brushface_deselect(tracemodel, trace_brush_id, trace_brush_faceid);
else
brush_select(tracemodel, trace_brush_id);
brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
}
else
{ //deselect everything but the targetted brush.
@ -895,11 +941,11 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
else if (!brush_isselected(tracemodel, trace_brush_id))
{
brush_deselectall();
brush_select(tracemodel, trace_brush_id);
brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
}
else
{ //its already selected, start doing whatever the current mouse action is meant to be
mousedown = TRUE;
mousedown |= 1;
mousetool = brushtool;
vertedit.selectedvert1 = -1;
vertedit.selectedvert2 = -1;
@ -939,7 +985,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
if (selectedbrushface && (brushtool == BT_PUSHFACE || brushtool == BT_MOVETEXTURE))
{
mousedown = TRUE;
mousedown |= 1;
mousetool = brushtool;
vertedit.selectedvert1 = -1;
vertedit.selectedvert2 = -1;
@ -975,7 +1021,51 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
mousedown = FALSE;
return TRUE;
}
else if (brushtool == BT_CREATE)
else if (brushtool == BT_CREATEPATCH)
{
if (bt_points == 4)
{
int x,y;
patchvert_t *vert;
patchvert_t *cp;
patchinfo_t patchinfo = {autocvar_ca_newbrushtexture, CONTENTBIT_SOLID, max(2,autocvar_ca_cpwidth), max(2,autocvar_ca_cpheight), 0, 0, '0 0 0'};
tmp.numfaces = 0;
tmp.numcp = 0;
bt_points = 0;
if (!autocvar_ca_explicitpatch)
{ //must be odd sizes. cos grr.
patchinfo.cpwidth|=1;
patchinfo.cpheight|=1;
patchinfo.tesswidth = patchinfo.tessheight = -1; //proper q3 patch, engine decides according to smoothness (which means nothing quite fits).
}
if (patchinfo.cpwidth*patchinfo.cpheight>64*64)
{
cprint(sprintf("%i*%i patch dimensions too large", patchinfo.cpwidth, patchinfo.cpheight));
return TRUE;
}
//populate the CPs, averaging from the 4 specified coords.
cp = vert = memalloc(sizeof(*vert) * patchinfo.cpwidth*patchinfo.cpheight);
for (y = 0; y < patchinfo.cpheight; y++)
for (x = 0; x < patchinfo.cpwidth; x++)
{
cp->s = x/(patchinfo.cpwidth-1);
cp->t = y/(patchinfo.cpheight-1);
vector top = bt_point[0]+(bt_point[1]-bt_point[0])*cp->s;
vector bot = bt_point[3]+(bt_point[2]-bt_point[3])*cp->s;
cp->xyz = top+(bot-top)*cp->t;
cp->rgb = '1 1 1'; cp->a = 1;
cp++;
}
brush_deselectall();
patch_history_create(selectedbrushmodel, vert, patchinfo, TRUE);
memfree(vert);
}
return TRUE;
}
else if (brushtool == BT_CREATEBRUSH)
{
if (bt_points >= 3)
{
@ -983,7 +1073,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
tmp.numcp = 0;
bt_points = 0;
brush_deselectall();
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, 1i, TRUE);
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, CONTENTBIT_SOLID, TRUE);
}
return TRUE;
}

View file

@ -31,7 +31,7 @@ void() editor_lights_addentities =
tempent.effects |= 8192f;
}
else
tempent.effects &~= 8192f;
tempent.effects &= ~8192f;
if (!(float)dynamiclight_get(l, LFIELD_RADIUS))
continue;
setorigin(tempent, dynamiclight_get(l, LFIELD_ORIGIN));

View file

@ -326,7 +326,7 @@ float(float keyc, float unic, vector m) editor_particles_key =
/*middle mouse*/
if (keyc == 514)
{
mousedown = 3;
mousedown |= 4;
applyparticles();
}
@ -364,7 +364,7 @@ void(vector mousepos) editor_particles_overlay =
loadparticles(setname);
}
if (mousedown == 3 && cureffect >= 0 && cureffect < numptypes)
if (mousedown & 4 && cureffect >= 0 && cureffect < numptypes)
{
vector t = unproject(mousepos + '0 0 8192');
vector o = unproject(mousepos);