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; image_t *tex;
if (!subdir) if (!subdir)
subdir = ""; subdir = "";
tex = Hash_Get(&imagetable, identifier); tex = Hash_GetInsensitive(&imagetable, identifier);
while(tex) while(tex)
{ {
if (!((tex->flags ^ flags) & (IF_CLAMP|IF_PALETTIZE|IF_PREMULTIPLYALPHA))) 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; tex->regsequence = r_regsequence;
return tex; return tex;
} }
} }
tex = Hash_GetNext(&imagetable, identifier, tex); tex = Hash_GetNextInsensitive(&imagetable, identifier, tex);
} }
return NULL; return NULL;
} }
@ -14214,7 +14214,7 @@ static image_t *Image_CreateTexture_Internal (const char *identifier, const char
tex->fallbackheight = 0; tex->fallbackheight = 0;
tex->fallbackfmt = TF_INVALID; tex->fallbackfmt = TF_INVALID;
if (*tex->ident) if (*tex->ident)
Hash_Add(&imagetable, tex->ident, tex, buck); Hash_AddInsensitive(&imagetable, tex->ident, tex, buck);
return tex; return tex;
} }
@ -14551,7 +14551,7 @@ void Image_DestroyTexture(image_t *tex)
Sys_UnlockMutex(com_resourcemutex); Sys_UnlockMutex(com_resourcemutex);
#endif #endif
if (*tex->ident) if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex); Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
Z_Free(tex); Z_Free(tex);
} }
@ -14789,7 +14789,7 @@ void Image_Shutdown(void)
{ {
tex = imagelist; tex = imagelist;
if (*tex->ident) if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex); Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
imagelist = tex->next; imagelist = tex->next;
if (tex->status == TEX_LOADED) if (tex->status == TEX_LOADED)
j++; j++;

View file

@ -7209,7 +7209,7 @@ static struct {
{"patch_getcp", PF_patch_getcp, 0}, {"patch_getcp", PF_patch_getcp, 0},
{"patch_getmesh", PF_patch_getmesh, 0}, {"patch_getmesh", PF_patch_getmesh, 0},
{"patch_create", PF_patch_create, 0}, {"patch_create", PF_patch_create, 0},
// {"patch_calculate", PF_patch_calculate, 0}, {"patch_evaluate", PF_patch_evaluate, 0},
#endif #endif
#ifdef ENGINE_ROUTING #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); 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"); csaddonnum = PR_LoadProgs(csqcprogs, "csaddon.dat");
if (csaddonnum >= 0) 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, ...); 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 #ifdef _WIN32
//windows doesn't support utf-8. Which is a shame really, because that's the charset we expect from everything. //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); 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 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 num_patches[2], num_tess[2];
int index[3], dstpitch, i, u, v, x, y; 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]) if (!tess[0] || !tess[1])
{ //not really a patch { //not really a patch
for( i = 0; i < comp*numcp[1]*numcp[0]; i++ ) for( u = 0; u < numcp[1]*numcp[0]; u++, dest += deststride, p += pstride)
dest[i] = p[i]; for( i = 0; i < comp; i++ )
dest[i] = p[i];
return; return;
} }
num_patches[0] = numcp[0] / 2; num_patches[0] = numcp[0] / 2;
num_patches[1] = numcp[1] / 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[0] = 1.0f / (float)tess[0];
step[1] = 1.0f / (float)tess[1]; 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 // current 3x3 patch control points
for( i = 0; i < 3; i++ ) for( i = 0; i < 3; i++ )
{ {
pv[i][0] = &p[( index[0]+i ) * comp]; pv[i][0] = &p[( index[0]+i ) * pstride];
pv[i][1] = &p[( index[1]+i ) * comp]; pv[i][1] = &p[( index[1]+i ) * pstride];
pv[i][2] = &p[( index[2]+i ) * comp]; 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 ) 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[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[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 ); 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 ); 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 #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_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_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_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
#endif #endif
void QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); 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" #include "gl_terrain.h"
static plugterrainfuncs_t terrainfuncs; 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."); 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++) for (i = 0; i < hm->numbrushes; i++)
{ {
br = &hm->wbrushes[i]; br = &hm->wbrushes[i];
if (br->patch)
continue; //infinitely thin...
for (j = 0; j < br->numplanes; j++) 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) if (!patch->tessvert)
{ {
const struct patchcpvert_s *r1 = patch->cp, *r2; const struct qcpatchvert_s *r1 = patch->cp, *r2;
w = patch->numcp[0]; w = patch->numcp[0];
h = patch->numcp[1]; 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->planes = NULL;
out->faces = NULL; out->faces = NULL;
out->numplanes = 0; out->numplanes = 0;
out->ispatch = !!brush->patch;
out->selected = false;
ClearBounds(out->mins, out->maxs); ClearBounds(out->mins, out->maxs);
if (brush->numplanes) 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->mins, model->mins, model->maxs);
AddPointToBounds(out->maxs, 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; return out;
} }
@ -6481,6 +6482,11 @@ static void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx)
} }
BZ_Free(br->planes); BZ_Free(br->planes);
if (br->patch)
{
BZ_Free(br->patch->tessvert);
BZ_Free(br->patch);
}
hm->numbrushes--; hm->numbrushes--;
hm->brushesedited = true; hm->brushesedited = true;
//plug the hole with some other brush. //plug the hole with some other brush.
@ -6506,6 +6512,104 @@ static qboolean Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid)
return false; 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) static void Brush_Serialise(sizebuf_t *sb, brushes_t *br)
{ {
unsigned int i; 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]); 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; br->ispatch = ispatch;
unsigned int maxplanes = br->numplanes; if (br->ispatch)
return Patch_DeserialiseHeader(br);
br->id = MSG_ReadLong(); br->id = MSG_ReadLong();
br->contents = MSG_ReadLong(); br->contents = MSG_ReadLong();
br->numplanes = MSG_ReadLong(); br->numplanes = MSG_ReadLong();
if (br->numplanes > maxplanes) if (br->numplanes > 8192)
return false; 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++) for (i = 0; i < br->numplanes; i++)
{ {
@ -6568,88 +6686,6 @@ static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br)
return true; 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 #ifndef SERVERONLY
heightmap_t *CL_BrushEdit_ForceContext(model_t *mod) 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 else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert) //1=create/replace
{ {
brushes_t brush; brushes_t brush;
size_t tempmemsize;
hm = CL_BrushEdit_ForceContext(mod); //do this early, to ensure that the textures are correct hm = CL_BrushEdit_ForceContext(mod); //do this early, to ensure that the textures are correct
memset(&brush, 0, sizeof(brush)); 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; if (brush.id)
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
{ {
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]; for (i = 0; i < hm->numbrushes; i++)
if (br->id == brush.id) {
return; //we already have it. assume we just edited it. 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) else if (cmd == hmcmd_prespawning)
{ //delete all { //delete all
@ -6899,31 +6926,22 @@ qboolean SV_Parse_BrushEdit(void)
else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert) else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert)
{ {
brushes_t brush; brushes_t brush;
size_t tempmemsize;
memset(&brush, 0, sizeof(brush)); 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; Con_Printf("SV_Parse_BrushEdit: %s sent an abusive %s\n", host_client->name, brush.ispatch?"patch":"brush");
brush.patch = alloca(sizeof(*brush.patch) + sizeof(*brush.patch->cp)*(maxpoints-countof(brush.patch->cp))); return false;
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;
}
} }
else if (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))
{ {
brush.numplanes = 128; Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable brush\n", host_client->name);
brush.planes = alloca(sizeof(*brush.planes) * brush.numplanes); return false;
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;
}
} }
if (!authorise) if (!authorise)
{ {
SV_PrintToClient(host_client, PRINT_MEDIUM, "Brush editing ignored: you are not a mapper\n"); 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 #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) 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 //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]; G_INT(OFS_RETURN) = br->patch->numcp[0]*br->patch->numcp[1];
else 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); VectorCopy(br->patch->cp[j].v, out_verts->v);
Vector2Copy(br->patch->cp[j].tc, out_verts->tc); 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) void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
world_t *vmw = prinst->parms->user; 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) if (out_info)
{ {
out_info->contents = br->contents; out_info->contents = br->contents;
out_info->cp_width = br->patch->numcp[0]; out_info->cp_width = br->patch->tesssize[0];
out_info->cp_height = br->patch->numcp[1]; out_info->cp_height = br->patch->tesssize[1];
out_info->subdiv_x = br->patch->subdiv[0]; out_info->subdiv_x = 0;
out_info->subdiv_y = br->patch->subdiv[1]; out_info->subdiv_y = 0;
out_info->shadername = PR_TempString(prinst, br->patch->tex->shadername); 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]; G_INT(OFS_RETURN) = br->patch->tesssize[0]*br->patch->tesssize[1];
else 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); VectorCopy(br->patch->tessvert[j].v, out_verts->v);
Vector2Copy(br->patch->tessvert[j].tc, out_verts->tc); Vector2Copy(br->patch->tessvert[j].tc, out_verts->tc);

View file

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

View file

@ -11797,9 +11797,9 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
"} patchvert_t;\n" \ "} patchvert_t;\n" \
"#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\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_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_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 #endif
#ifdef ENGINE_ROUTING #ifdef ENGINE_ROUTING

View file

@ -3,7 +3,7 @@ void(int mod, int id) DrawEngineBrushWireframe =
const vector col = '1 0 0'; const vector col = '1 0 0';
for(int facenum = 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) if (!points)
break; //end of face list, I guess break; //end of face list, I guess
@ -19,6 +19,77 @@ void(int mod, int id) DrawEngineBrushWireframe =
R_EndPolygon(); 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 = 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. //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. //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 //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("{" sprintf("{"
"{\n" "{\n"
"map \"%s\"\n" "map \"%s\"\n"
@ -78,14 +150,46 @@ void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextu
"}\n" "}\n"
"}", faces[f].shadername)); "}", faces[f].shadername));
vector sz = drawgetimagesize(faces[f].shadername); vector sz = drawgetimagesize(materialname);
R_BeginPolygon(faces[f].shadername); R_BeginPolygon(materialname, 3);
for (point = 0; point < points; point++) 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_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(); 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 = 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); brush_select(mod, h->id);
return 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. //delete and journal.
void(int mod, int id) brush_history_delete = 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); brush_selected(mod, id, -1, TRUE);
} }
return id; 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 = vector(vector guess) brush_snappoint =
{ {
if (nogrid) if (nogrid || autocvar_ca_grid <= 0)
return guess; return guess;
int facenum, points; 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. 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 //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; int i;
for (i = 0; i < numfaces; i++, fa++) if (tmp.numcp)
{ {
fa->planedist += fa->planenormal * displacement; for (i = 0; i < tmp.numcp; i++)
fa->sbias -= fa->sdir * displacement; tmp.cp[i].xyz += displacement;
fa->tbias -= fa->tdir * 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. //rotates a list of faces by the current v_* vectors, around the origin.
//translate before+after first in order to deal with pivots. //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; for (int i = 0; i < tmp.numcp; i++)
fa->planenormal[0] = orig * v_forward; {
fa->planenormal[1] = orig * -v_right; //quake just isn't right... vector orig = tmp.cp[i].xyz;
fa->planenormal[2] = orig * v_up; 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; orig = fa->sdir;
fa->sdir[0] = orig * v_forward; fa->sdir[0] = orig * v_forward;
fa->sdir[1] = orig * -v_right; //quake just isn't right... fa->sdir[1] = orig * -v_right; //quake just isn't right...
fa->sdir[2] = orig * v_up; fa->sdir[2] = orig * v_up;
orig = fa->tdir; orig = fa->tdir;
fa->tdir[0] = orig * v_forward; fa->tdir[0] = orig * v_forward;
fa->tdir[1] = orig * -v_right; //quake just isn't right... fa->tdir[1] = orig * -v_right; //quake just isn't right...
fa->tdir[2] = orig * v_up; fa->tdir[2] = orig * v_up;
}
} }
}; };
@ -415,15 +439,35 @@ void() brushedit_resettextures =
{ {
int model = selectedbrushes[sb].model; int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id; 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); int planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);
if (planecount) if (planecount)
{ {
for (int i = 0; i < planecount; i++) for (int i = 0; i < planecount; i++)
if (!face || face == planecount+1) if (facemask & (1lu << i))
reset_texturecoords(&tmp.faces[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); 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 string autocvar_ca_newbrushtexture = "metal4_2";
var float autocvar_ca_newbrushheight = 64; var float autocvar_ca_newbrushheight = 64;
var float autocvar_ca_grid = 16; 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. #define EPSILON (1.0 / 32) //inprecision sucks.
@ -36,7 +39,8 @@ enum : int
BT_ROTATE, BT_ROTATE,
BT_MERGE, BT_MERGE,
BT_PUSHFACE, BT_PUSHFACE,
BT_CREATE, BT_CREATEBRUSH,
BT_CREATEPATCH,
BT_CREATEDRAG, BT_CREATEDRAG,
BT_CLONEDISPLACE, BT_CLONEDISPLACE,
BT_SLICE, BT_SLICE,
@ -58,15 +62,14 @@ typedef struct
{ {
int model; int model;
int id; int id;
int face; __uint64 facemask; //MAX_BRUSHFACES is 64... handy.
//fixme: do we need an array of faces here?
} selbrush_t; } selbrush_t;
selbrush_t *selectedbrushes; selbrush_t *selectedbrushes;
int selectedbrushcount; int selectedbrushcount;
var int selectedbrushmodel = 1; //by default, the worldmodel. this is tracked to know which submodel to insert new brushes into. 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++) for (int i = 0; i < selectedbrushcount; i++)
if (selectedbrushes[i].id == brushid) if (selectedbrushes[i].id == brushid)
@ -74,6 +77,14 @@ int(int modelidx, int brushid) brush_isselected =
return i+1; return i+1;
return 0; 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(int modelidx, int brushid) brush_deselect =
{ {
int i = brush_isselected(modelidx, brushid); int i = brush_isselected(modelidx, brushid);
@ -84,6 +95,25 @@ int(int modelidx, int brushid) brush_deselect =
selectedbrushcount--; selectedbrushcount--;
return TRUE; 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 = void() brush_deselectall =
{ {
for (int i = 0; i < selectedbrushcount; i++) for (int i = 0; i < selectedbrushcount; i++)
@ -101,8 +131,29 @@ void(int modelidx, int brushid) brush_select =
selectedbrushes = n; selectedbrushes = n;
n[selectedbrushcount].model = modelidx; n[selectedbrushcount].model = modelidx;
n[selectedbrushcount].id = brushid; n[selectedbrushcount].id = brushid;
n[selectedbrushcount].facemask = ~0ul;
selectedbrushcount++; selectedbrushcount++;
brush_selected(modelidx, brushid, -1, TRUE); brush_selected(modelidx, brushid, -1, TRUE);
} }
selectedbrushmodel = modelidx; 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;
vertsoup_t vertedit; 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. //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->model = model;
vertedit->brush = brush; vertedit->brush = brush;
vertedit->numidx = 0; 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; ; ) for (i = 0; ; )
{ {
points = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length); 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. //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; int points, k;
vector p; vector p;
vector *np; vector *np;
int fi[64]; int fi[64];
vertedit.numidx = 0; 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; ; ) 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) if (!points)
break; 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 = void(vertsoup_t *soup, int drawit) Rebrushify =
{ {
brushface_t faces[64]; brushface_t faces[MAX_BRUSHFACES];
int numfaces, f, point; int numfaces, f, point;
string shader = 0; string shader = 0;
@ -148,6 +201,36 @@ void(vertsoup_t *soup, int drawit) Rebrushify =
float d; float d;
int o=0; 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); 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. return; //can't possibly be valid.
if (drawit) if (drawit)
{ {
//draw it wireframe WITH depth testing
//DrawQCBrushWireframe(faces, numfaces, "terrainedit", '1 0 0', 1);
//draw textured preview (yay for block lighting) //draw textured preview (yay for block lighting)
DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1); DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1);

View file

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

View file

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

View file

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

View file

@ -40,20 +40,29 @@ static void() editor_brushes_drawselected =
float intensity = (sin(gettime(5)*4)+1)*0.05; float intensity = (sin(gettime(5)*4)+1)*0.05;
int facenum, point, points; int facenum, point, points;
vector col; 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. //draw all selected brush faces.
for (int sb = 0; sb < selectedbrushcount; sb++) for (int sb = 0; sb < selectedbrushcount; sb++)
{ {
int model = selectedbrushes[sb].model; int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id; int brush = selectedbrushes[sb].id;
int face = selectedbrushes[sb].face; __uint64 facemask = selectedbrushes[sb].facemask;
for(facenum = 0;;) 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) if (!points)
break; //end of face list, I guess break; //end of face list, I guess
if (facenum == face) if (facemask & (1ul<<facenum))
col = [0,intensity,0]; col = [0,intensity,0];
else else
col = [intensity,0,0]; col = [intensity,0,0];
@ -238,7 +247,7 @@ void(vector mousepos) editor_brushes_addentities =
Rebrushify(&vertedit, TRUE); Rebrushify(&vertedit, TRUE);
} }
else if (brushtool == BT_CREATE) else if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
{ {
editor_brushes_drawselected(); editor_brushes_drawselected();
@ -335,7 +344,7 @@ void(vector mousepos) editor_brushes_addentities =
{ {
int model = oldselectedbrushes[sb].model; int model = oldselectedbrushes[sb].model;
int brush = oldselectedbrushes[sb].id; int brush = oldselectedbrushes[sb].id;
int face = oldselectedbrushes[sb].face; __uint64 facemask = oldselectedbrushes[sb].facemask;
if (!brush) if (!brush)
continue; continue;
@ -344,19 +353,25 @@ void(vector mousepos) editor_brushes_addentities =
if (mousetool == BT_PUSHFACE) if (mousetool == BT_PUSHFACE)
{ {
if (!face) for (facenum = 0; facenum < tmp.numfaces; facenum++)
continue; //FIXME: should not happen. {
tmp.faces[face-1].planedist += tmp.faces[face-1].planenormal * displace; if (facemask & (1ul<<facenum))
tmp.faces[facenum].planedist += tmp.faces[facenum].planenormal * displace;
}
} }
else if (mousetool == BT_MOVETEXTURE) else if (mousetool == BT_MOVETEXTURE)
{ {
if (!face) for (facenum = 0; facenum < tmp.numfaces; facenum++)
continue; //FIXME: should not happen. {
tmp.faces[face-1].sbias -= tmp.faces[face-1].sdir * displace; if (facemask & (1ul<<facenum))
tmp.faces[face-1].tbias -= tmp.faces[face-1].tdir * displace; {
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) else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE)
brushface_translate(tmp.faces, tmp.numfaces, displace); brushface_translate(displace);
else if (mousetool == BT_ROTATE) else if (mousetool == BT_ROTATE)
{ {
//find the brush's middle (based on its bbox) //find the brush's middle (based on its bbox)
@ -365,18 +380,20 @@ void(vector mousepos) editor_brushes_addentities =
makevectors(col); makevectors(col);
//move it so its pivot is at the origin //move it so its pivot is at the origin
brushface_translate(tmp.faces, tmp.numfaces, -mid); brushface_translate(-mid);
//rotate it //rotate it (by v_forward etc)
brushface_rotate(tmp.faces, tmp.numfaces); brushface_rotate();
//reposition it around its pivot again //reposition it around its pivot again
brushface_translate(tmp.faces, tmp.numfaces, mid); brushface_translate(mid);
} }
else else
continue; 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) if (points)
{ {
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those. //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++) 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_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(); R_EndPolygon();
} }*/
} }
else else
{ {
//draw it wireframe //draw it wireframe WITH depth testing
for(facenum = 0; facenum < tmp.numfaces;) 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); //draw it wireframe
if (!points) for(facenum = 0; facenum < tmp.numfaces;)
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; )
{ {
point++; points = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
R_PolygonVertex(facepoints[point], '0 0', col, 1); 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_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 DebrushifyLite();
DrawQCBrushWireframe(tmp.faces, tmp.numfaces, "terrainedit", '0 0 1', 1);
DebrushifyLite(tmp.faces, tmp.numfaces);
DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1); DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1);
if (!mousedown) if (!mousedown)
@ -438,6 +461,8 @@ void(vector mousepos) editor_brushes_addentities =
if (displace*displace > 1) if (displace*displace > 1)
brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE); 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 else
brush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents); brush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents);
bt_points = 0; bt_points = 0;
@ -478,7 +503,9 @@ void(vector mousepos) editor_brushes_addentities =
vector o = mousenear; vector o = mousenear;
if (vlen(o - t) > 8192 && !autocvar_ca_brush_view) if (vlen(o - t) > 8192 && !autocvar_ca_brush_view)
t = o + normalize(t)*8192; t = o + normalize(t)*8192;
*world_hitcontentsmaski = ~0; //I want to be able to select water...
traceline(o, t, TRUE, world); traceline(o, t, TRUE, world);
*world_hitcontentsmaski = 0; //don't break stuff
#if 0 #if 0
//find all the brushes within 32qu of the mouse cursor's impact point //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); // bt_point[showpoints++] = brush_snappoint(trace_endpos);
showpoints = 3; showpoints = 3;
} }
if (brushtool == BT_CREATE) if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
bt_point[showpoints++] = brush_snappoint(trace_endpos); bt_point[showpoints++] = brush_snappoint(trace_endpos);
//FIXME: if slicing, draw the intersection //FIXME: if slicing, draw the intersection
@ -607,7 +634,13 @@ void(vector mousepos) editor_brushes_overlay =
case BT_CREATEDRAG: case BT_CREATEDRAG:
drawrawstring('0 32 0', "Drag+Create", '8 8 0', '1 1 1', 1); drawrawstring('0 32 0', "Drag+Create", '8 8 0', '1 1 1', 1);
break; 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); drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1);
break; break;
case BT_SLICE: case BT_SLICE:
@ -653,10 +686,12 @@ brusheditormodes
registercommand("brushedit_redo"); registercommand("brushedit_redo");
registercommand("brushedit_delete"); registercommand("brushedit_delete");
registercommand("brushedit_create"); registercommand("brushedit_create");
registercommand("brushedit_createpatch");
registercommand("brushedit_slice"); registercommand("brushedit_slice");
registercommand("brushedit_matchface"); registercommand("brushedit_matchface");
registercommand("brushedit_resettexcoords"); registercommand("brushedit_resettexcoords");
registercommand("brushedit_settexture"); registercommand("brushedit_settexture");
registercommand("brushedit_scrolltex");
registercommand("brushedit_subtract"); registercommand("brushedit_subtract");
registercommand("brushedit_binds"); registercommand("brushedit_binds");
registercommand("brushedit_binds_default"); registercommand("brushedit_binds_default");
@ -679,13 +714,21 @@ brusheditormodes
brush_undo(); brush_undo();
break; break;
case "brushedit_create": case "brushedit_create":
brushtool = BT_CREATE; brushtool = BT_CREATEBRUSH;
bt_points = 0;
break;
case "brushedit_createpatch":
brushtool = BT_CREATEPATCH;
bt_points = 0; bt_points = 0;
break; break;
case "brushedit_slice": case "brushedit_slice":
brushtool = BT_SLICE; brushtool = BT_SLICE;
bt_points = 0; bt_points = 0;
break; break;
case "brushedit_scrolltex":
brushtool = BT_MOVETEXTURE;
bt_points = 0;
break;
case "+brushedit_nogrid": nogrid = TRUE; break; case "+brushedit_nogrid": nogrid = TRUE; break;
case "-brushedit_nogrid": nogrid = FALSE; break; case "-brushedit_nogrid": nogrid = FALSE; break;
@ -719,9 +762,7 @@ brusheditormodes
brushedit_resettextures(); brushedit_resettextures();
break; break;
case "brushedit_settexture": case "brushedit_settexture":
//IMPLEMENTME brushedit_settexture(argv(1));
brushtool = BT_NONE;
bt_points = 0;
break; break;
case "brushedit_subtract": case "brushedit_subtract":
brushedit_subtract(); brushedit_subtract();
@ -737,15 +778,16 @@ brusheditormodes
localcmd("bind ctrl+z brushedit_undo\n"); localcmd("bind ctrl+z brushedit_undo\n");
localcmd("bind ctrl+y brushedit_redo\n"); localcmd("bind ctrl+y brushedit_redo\n");
localcmd("bind ctrl+i brushedit_create\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+s brushedit_slice\n");
localcmd("bind ctrl+m brushedit_matchface\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+backspace brushedit_subtract\n");
localcmd("bind ctrl+c +brushedit_clone\n"); localcmd("bind ctrl+c +brushedit_clone\n");
localcmd("bind ctrl+d +brushedit_draw\n"); localcmd("bind ctrl+d +brushedit_draw\n");
localcmd("bind ctrl+r +brushedit_rotate\n"); localcmd("bind ctrl+r +brushedit_rotate\n");
localcmd("bind ctrl+f +brushedit_pushface\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"); localcmd("bind ctrl+v +brushedit_vertex\n");
break; break;
case "brushedit_binds": case "brushedit_binds":
@ -806,7 +848,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
{ {
if (vertedit.selectedvert1 != -1) if (vertedit.selectedvert1 != -1)
{ {
mousedown = TRUE; mousedown |= 1;
mousetool = BT_VERTEXEDIT; mousetool = BT_VERTEXEDIT;
if (vertedit.selectedvert2 != -1) if (vertedit.selectedvert2 != -1)
{ {
@ -823,7 +865,9 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
return TRUE; return TRUE;
} }
*world_hitcontentsmaski = ~0; //I want to be able to select water...
traceline(o, t, TRUE, world); traceline(o, t, TRUE, world);
*world_hitcontentsmaski = 0; //don't break stuff.
float tracemodel = trace_ent.modelindex; float tracemodel = trace_ent.modelindex;
/*if (brushtool == BT_CREATEDRAG || (brushtool == BT_PUSHFACE && selectedbrushface)) /*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) else*/ if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE)
trace_brush_faceid = 0; 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) if (brushtool == BT_SLICE && bt_points > 2)
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: BT_CREATE is planar. so keep the points planar too.
//FIXME: need a way to move points already placed //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) if (!tracemodel || !trace_brush_id)
{ {
} }
else if (brush_isselected(tracemodel, trace_brush_id)) else if (brushface_isselected(tracemodel, trace_brush_id, trace_brush_faceid))
brush_deselect(tracemodel, trace_brush_id); brushface_deselect(tracemodel, trace_brush_id, trace_brush_faceid);
else else
brush_select(tracemodel, trace_brush_id); brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
} }
else else
{ //deselect everything but the targetted brush. { //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)) else if (!brush_isselected(tracemodel, trace_brush_id))
{ {
brush_deselectall(); brush_deselectall();
brush_select(tracemodel, trace_brush_id); brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
} }
else else
{ //its already selected, start doing whatever the current mouse action is meant to be { //its already selected, start doing whatever the current mouse action is meant to be
mousedown = TRUE; mousedown |= 1;
mousetool = brushtool; mousetool = brushtool;
vertedit.selectedvert1 = -1; vertedit.selectedvert1 = -1;
vertedit.selectedvert2 = -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)) if (selectedbrushface && (brushtool == BT_PUSHFACE || brushtool == BT_MOVETEXTURE))
{ {
mousedown = TRUE; mousedown |= 1;
mousetool = brushtool; mousetool = brushtool;
vertedit.selectedvert1 = -1; vertedit.selectedvert1 = -1;
vertedit.selectedvert2 = -1; vertedit.selectedvert2 = -1;
@ -975,7 +1021,51 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
mousedown = FALSE; mousedown = FALSE;
return TRUE; 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) if (bt_points >= 3)
{ {
@ -983,7 +1073,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
tmp.numcp = 0; tmp.numcp = 0;
bt_points = 0; bt_points = 0;
brush_deselectall(); 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; return TRUE;
} }

View file

@ -31,7 +31,7 @@ void() editor_lights_addentities =
tempent.effects |= 8192f; tempent.effects |= 8192f;
} }
else else
tempent.effects &~= 8192f; tempent.effects &= ~8192f;
if (!(float)dynamiclight_get(l, LFIELD_RADIUS)) if (!(float)dynamiclight_get(l, LFIELD_RADIUS))
continue; continue;
setorigin(tempent, dynamiclight_get(l, LFIELD_ORIGIN)); 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*/ /*middle mouse*/
if (keyc == 514) if (keyc == 514)
{ {
mousedown = 3; mousedown |= 4;
applyparticles(); applyparticles();
} }
@ -364,7 +364,7 @@ void(vector mousepos) editor_particles_overlay =
loadparticles(setname); loadparticles(setname);
} }
if (mousedown == 3 && cureffect >= 0 && cureffect < numptypes) if (mousedown & 4 && cureffect >= 0 && cureffect < numptypes)
{ {
vector t = unproject(mousepos + '0 0 8192'); vector t = unproject(mousepos + '0 0 8192');
vector o = unproject(mousepos); vector o = unproject(mousepos);