diff --git a/engine/client/image.c b/engine/client/image.c index f5e565ffe..45ec9dd9d 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -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++; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 2adf59106..75cdb3ec5 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -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) diff --git a/engine/common/common.h b/engine/common/common.h index a47e87a74..08fdac30f 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -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); diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 95b6eedf6..20f4718a6 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -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 diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 7e4fed65a..0d4b1120a 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -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); diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index af7d9abf2..f1011075b 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -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); diff --git a/engine/gl/gl_terrain.h b/engine/gl/gl_terrain.h index 5ee185993..279b4b0d3 100644 --- a/engine/gl/gl_terrain.h +++ b/engine/gl/gl_terrain.h @@ -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]; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 61ded7c1c..1527a1416 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -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 diff --git a/quakec/csaddon/src/brush_draw.qc b/quakec/csaddon/src/brush_draw.qc index e8be5d021..2543e01af 100644 --- a/quakec/csaddon/src/brush_draw.qc +++ b/quakec/csaddon/src/brush_draw.qc @@ -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 = { diff --git a/quakec/csaddon/src/brush_history.qc b/quakec/csaddon/src/brush_history.qc index e452c2868..c57440290 100644 --- a/quakec/csaddon/src/brush_history.qc +++ b/quakec/csaddon/src/brush_history.qc @@ -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 = { @@ -196,4 +216,18 @@ int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_histo brush_selected(mod, id, -1, TRUE); } 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; }; \ No newline at end of file diff --git a/quakec/csaddon/src/brush_manip.qc b/quakec/csaddon/src/brush_manip.qc index 06cd1d39d..3067ee8cc 100644 --- a/quakec/csaddon/src/brush_manip.qc +++ b/quakec/csaddon/src/brush_manip.qc @@ -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,15 +439,35 @@ 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); } } diff --git a/quakec/csaddon/src/brush_selection.qc b/quakec/csaddon/src/brush_selection.qc index 537a04929..a8265007e 100644 --- a/quakec/csaddon/src/brush_selection.qc +++ b/quakec/csaddon/src/brush_selection.qc @@ -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<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); @@ -304,10 +387,7 @@ p3p1edge: 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); diff --git a/quakec/csaddon/src/csaddon.qc b/quakec/csaddon/src/csaddon.qc index e4437f8a1..fd8cba33c 100644 --- a/quakec/csaddon/src/csaddon.qc +++ b/quakec/csaddon/src/csaddon.qc @@ -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... }; diff --git a/quakec/csaddon/src/csaddon.src b/quakec/csaddon/src/csaddon.src index 78fd40e2a..11353f189 100644 --- a/quakec/csaddon/src/csaddon.src +++ b/quakec/csaddon/src/csaddon.src @@ -6,7 +6,7 @@ #define CSQC #define _ACCESSORS -#pragma target FTE +#pragma target FTE_5768 //#pragma optimise 2 //#pragma optimise no-filename diff --git a/quakec/csaddon/src/csfixups.qc b/quakec/csaddon/src/csfixups.qc index c68680e6d..630f17d97 100644 --- a/quakec/csaddon/src/csfixups.qc +++ b/quakec/csaddon/src/csfixups.qc @@ -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; diff --git a/quakec/csaddon/src/csplat.qc b/quakec/csaddon/src/csplat.qc index 008be45d7..479af35fa 100644 --- a/quakec/csaddon/src/csplat.qc +++ b/quakec/csaddon/src/csplat.qc @@ -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 { diff --git a/quakec/csaddon/src/editor_brushes.qc b/quakec/csaddon/src/editor_brushes.qc index 9e9e6f234..9f709585c 100644 --- a/quakec/csaddon/src/editor_brushes.qc +++ b/quakec/csaddon/src/editor_brushes.qc @@ -40,20 +40,29 @@ static void() editor_brushes_drawselected = float intensity = (sin(gettime(5)*4)+1)*0.05; 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< 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; } diff --git a/quakec/csaddon/src/editor_lights.qc b/quakec/csaddon/src/editor_lights.qc index 2f47cd2e2..717375296 100644 --- a/quakec/csaddon/src/editor_lights.qc +++ b/quakec/csaddon/src/editor_lights.qc @@ -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)); diff --git a/quakec/csaddon/src/editor_particles.qc b/quakec/csaddon/src/editor_particles.qc index 3540c9ffc..730824318 100644 --- a/quakec/csaddon/src/editor_particles.qc +++ b/quakec/csaddon/src/editor_particles.qc @@ -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);