csaddon can now create/edit patches.
This commit is contained in:
parent
7b3096bbf5
commit
75c6527893
20 changed files with 904 additions and 331 deletions
|
@ -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++;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ var float autocvar_ca_brush_viewsize = 1024; //for different views.
|
|||
var string autocvar_ca_newbrushtexture = "metal4_2";
|
||||
var float autocvar_ca_newbrushheight = 64;
|
||||
var float autocvar_ca_grid = 16;
|
||||
var float autocvar_ca_explicitpatch = 0;
|
||||
var float autocvar_ca_cpwidth = 2;
|
||||
var float autocvar_ca_cpheight = 2;
|
||||
|
||||
|
||||
#define EPSILON (1.0 / 32) //inprecision sucks.
|
||||
|
@ -36,7 +39,8 @@ enum : int
|
|||
BT_ROTATE,
|
||||
BT_MERGE,
|
||||
BT_PUSHFACE,
|
||||
BT_CREATE,
|
||||
BT_CREATEBRUSH,
|
||||
BT_CREATEPATCH,
|
||||
BT_CREATEDRAG,
|
||||
BT_CLONEDISPLACE,
|
||||
BT_SLICE,
|
||||
|
@ -58,15 +62,14 @@ typedef struct
|
|||
{
|
||||
int model;
|
||||
int id;
|
||||
int face;
|
||||
//fixme: do we need an array of faces here?
|
||||
__uint64 facemask; //MAX_BRUSHFACES is 64... handy.
|
||||
} selbrush_t;
|
||||
selbrush_t *selectedbrushes;
|
||||
int selectedbrushcount;
|
||||
|
||||
var int selectedbrushmodel = 1; //by default, the worldmodel. this is tracked to know which submodel to insert new brushes into.
|
||||
|
||||
int(int modelidx, int brushid) brush_isselected =
|
||||
int(int modelidx, int brushid) brush_isselected = //0 for faceid means 'all'
|
||||
{
|
||||
for (int i = 0; i < selectedbrushcount; i++)
|
||||
if (selectedbrushes[i].id == brushid)
|
||||
|
@ -74,6 +77,14 @@ int(int modelidx, int brushid) brush_isselected =
|
|||
return i+1;
|
||||
return 0;
|
||||
};
|
||||
int(int modelidx, int brushid, int faceid) brushface_isselected = //0 for faceid means 'all'
|
||||
{
|
||||
for (int i = 0; i < selectedbrushcount; i++)
|
||||
if (selectedbrushes[i].id == brushid)
|
||||
if (selectedbrushes[i].model == modelidx)
|
||||
return !!(selectedbrushes[i].facemask&(1ull<<(faceid-1i)));
|
||||
return 0;
|
||||
};
|
||||
int(int modelidx, int brushid) brush_deselect =
|
||||
{
|
||||
int i = brush_isselected(modelidx, brushid);
|
||||
|
@ -84,6 +95,25 @@ int(int modelidx, int brushid) brush_deselect =
|
|||
selectedbrushcount--;
|
||||
return TRUE;
|
||||
};
|
||||
int(int modelidx, int brushid, int face) brushface_deselect =
|
||||
{
|
||||
//deselecting a single face
|
||||
int i = brush_isselected(modelidx, brushid); //get the brush selection index
|
||||
if (!i)
|
||||
return FALSE;
|
||||
i--;
|
||||
face--;
|
||||
if (!selectedbrushes[i].facemask) //no faces means all..
|
||||
selectedbrushes[i].facemask = ~0ul;
|
||||
selectedbrushes[i].facemask &= ~(1ul<<face);
|
||||
if (!selectedbrushes[i].facemask)
|
||||
{ //no faces left selected.
|
||||
brush_selected(modelidx, brushid, -1, FALSE);
|
||||
memcpy(&selectedbrushes[i], &selectedbrushes[i+1], sizeof(selbrush_t)*(selectedbrushcount-i));
|
||||
selectedbrushcount--;
|
||||
}
|
||||
return TRUE;
|
||||
};
|
||||
void() brush_deselectall =
|
||||
{
|
||||
for (int i = 0; i < selectedbrushcount; i++)
|
||||
|
@ -101,8 +131,29 @@ void(int modelidx, int brushid) brush_select =
|
|||
selectedbrushes = n;
|
||||
n[selectedbrushcount].model = modelidx;
|
||||
n[selectedbrushcount].id = brushid;
|
||||
n[selectedbrushcount].facemask = ~0ul;
|
||||
selectedbrushcount++;
|
||||
brush_selected(modelidx, brushid, -1, TRUE);
|
||||
}
|
||||
selectedbrushmodel = modelidx;
|
||||
};
|
||||
void(int modelidx, int brushid, int faceid) brushface_select =
|
||||
{
|
||||
faceid--;
|
||||
int i = brush_isselected(modelidx, brushid); //get the brush selection index
|
||||
if (!i)
|
||||
{ //brush not selected yet
|
||||
selbrush_t *n = memalloc(sizeof(selbrush_t) * (selectedbrushcount+1));
|
||||
memcpy(n, selectedbrushes, sizeof(selbrush_t)*selectedbrushcount);
|
||||
memfree(selectedbrushes);
|
||||
selectedbrushes = n;
|
||||
n[selectedbrushcount].model = modelidx;
|
||||
n[selectedbrushcount].id = brushid;
|
||||
n[selectedbrushcount].facemask = 0;
|
||||
selectedbrushcount++;
|
||||
brush_selected(modelidx, brushid, -1, TRUE);
|
||||
i = selectedbrushcount;
|
||||
}
|
||||
selectedbrushes[i-1].facemask |= 1ul<<faceid;
|
||||
selectedbrushmodel = modelidx;
|
||||
};
|
|
@ -14,6 +14,9 @@ typedef struct
|
|||
} vertsoup_t;
|
||||
vertsoup_t vertedit;
|
||||
|
||||
static patchinfo_t patchinfo;
|
||||
static patchvert_t *patchvert;
|
||||
static int maxcp = 64*64;
|
||||
|
||||
|
||||
//take a brush apart and generate trisoup from it.
|
||||
|
@ -29,7 +32,38 @@ void(vertsoup_t *vertedit, int model, int brush) Debrushify =
|
|||
vertedit->model = model;
|
||||
vertedit->brush = brush;
|
||||
vertedit->numidx = 0;
|
||||
vertedit->numverts = 0;
|
||||
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);
|
||||
if (vertedit->numverts)
|
||||
{ //okay, this is a patch. one logical face.
|
||||
memfree(vertedit->i);
|
||||
memfree(vertedit->p);
|
||||
|
||||
vertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);
|
||||
|
||||
if (!patchvert)
|
||||
patchvert = memalloc(sizeof(*patchvert)*maxcp);
|
||||
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);
|
||||
for (i = 0; i < vertedit->numverts; i++)
|
||||
vertedit->p[i] = patchvert[i].xyz;
|
||||
|
||||
//some of the rest of the code assumes we have indexes. oh well.
|
||||
k = (patchinfo.cpwidth-1)*(patchinfo.cpheight-1);
|
||||
vertedit->numidx = k*6;
|
||||
vertedit->i = ni = memalloc(sizeof(*ni) * vertedit->numidx);
|
||||
for (i = 0; i < k; i++, ni+=6)
|
||||
{
|
||||
ni[0] = i;
|
||||
ni[1] = i+1;
|
||||
ni[2] = i+patchinfo.cpwidth;
|
||||
|
||||
ni[3] = i+1;
|
||||
ni[4] = i+patchinfo.cpwidth+1;
|
||||
ni[5] = i+patchinfo.cpwidth;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//brush.
|
||||
for (i = 0; ; )
|
||||
{
|
||||
points = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length);
|
||||
|
@ -78,17 +112,36 @@ void(vertsoup_t *vertedit, int model, int brush) Debrushify =
|
|||
};
|
||||
|
||||
//determines only the various points of the brush, without duplicates. doesn't care about indexes.
|
||||
void(brushface_t *faces, int numfaces) DebrushifyLite =
|
||||
void(void) DebrushifyLite =
|
||||
{
|
||||
int points, k;
|
||||
vector p;
|
||||
vector *np;
|
||||
int fi[64];
|
||||
vertedit.numidx = 0;
|
||||
vertedit.numverts = 0;
|
||||
|
||||
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, __NULL__, 0, __NULL__);
|
||||
if (vertedit->numverts)
|
||||
{ //okay, this is a patch. one logical face.
|
||||
memfree(vertedit->i);
|
||||
memfree(vertedit->p);
|
||||
|
||||
vertedit->i = __NULL__; //don't bother.
|
||||
vertedit->p = memalloc(sizeof(*vertedit->p) * vertedit->numverts);
|
||||
|
||||
if (!patchvert)
|
||||
patchvert = memalloc(sizeof(*patchvert)*maxcp);
|
||||
|
||||
vertedit->numverts = patch_getcp(vertedit->model, vertedit->brush, patchvert, maxcp, &patchinfo);
|
||||
|
||||
for (int i = 0; i < vertedit->numverts; i++)
|
||||
vertedit->p[i] = patchvert[i].xyz;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; ; )
|
||||
{
|
||||
points = brush_calcfacepoints(++i, faces, numfaces, facepoints, facepoints.length);
|
||||
points = brush_calcfacepoints(++i, tmp.faces, tmp.numfaces, facepoints, facepoints.length);
|
||||
if (!points)
|
||||
break;
|
||||
|
||||
|
@ -140,7 +193,7 @@ static float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) pl
|
|||
|
||||
void(vertsoup_t *soup, int drawit) Rebrushify =
|
||||
{
|
||||
brushface_t faces[64];
|
||||
brushface_t faces[MAX_BRUSHFACES];
|
||||
int numfaces, f, point;
|
||||
string shader = 0;
|
||||
|
||||
|
@ -148,6 +201,36 @@ void(vertsoup_t *soup, int drawit) Rebrushify =
|
|||
float d;
|
||||
int o=0;
|
||||
|
||||
f = patch_getcp(soup->model, soup->brush, patchvert, maxcp, &patchinfo);
|
||||
if (f)
|
||||
{
|
||||
for (o = 0; o < f; o++)
|
||||
patchvert[o].xyz = soup.p[o];
|
||||
if (drawit)
|
||||
{
|
||||
const int maxtessverts=64*64;
|
||||
patchinfo_t tessinfo = patchinfo;
|
||||
patchvert_t *tessverts = memalloc(sizeof(*tessverts)*maxtessverts);
|
||||
if (patch_evaluate(patchvert, tessverts, maxtessverts, &tessinfo) <= maxtessverts)
|
||||
{
|
||||
//draw textured preview (yay for fullbright lighting being overbright)
|
||||
DrawQCPatchTextured(tessverts, tessinfo, '0.7 0.7 0.7', 0.7); //display the patch. in-place so you know what it'll actually look like. stoopid tessellation.
|
||||
DrawQCPatchWireframe(tessverts, tessinfo, "chop", [0.3,0,0.3], 1); //show a somewhat faded indication of how many quads are actually being used.
|
||||
}
|
||||
memfree(tessverts);
|
||||
|
||||
//draw it wireframe without depth testing
|
||||
DrawQCPatchWireframe(patchvert, patchinfo, "chop", [0,0,1], 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
patch_history_edit(soup->model, soup->brush, patchvert, patchinfo);
|
||||
|
||||
soup->numidx = 0;
|
||||
soup->numverts = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tmp.numfaces = brush_get(soup->model, soup->brush, tmp.faces, tmp.faces.length, &tmp.contents);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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...
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#define CSQC
|
||||
#define _ACCESSORS
|
||||
#pragma target FTE
|
||||
#pragma target FTE_5768
|
||||
//#pragma optimise 2
|
||||
//#pragma optimise no-filename
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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<<facenum))
|
||||
col = [0,intensity,0];
|
||||
else
|
||||
col = [intensity,0,0];
|
||||
|
@ -238,7 +247,7 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
|
||||
Rebrushify(&vertedit, TRUE);
|
||||
}
|
||||
else if (brushtool == BT_CREATE)
|
||||
else if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
|
||||
{
|
||||
editor_brushes_drawselected();
|
||||
|
||||
|
@ -335,7 +344,7 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
{
|
||||
int model = oldselectedbrushes[sb].model;
|
||||
int brush = oldselectedbrushes[sb].id;
|
||||
int face = oldselectedbrushes[sb].face;
|
||||
__uint64 facemask = oldselectedbrushes[sb].facemask;
|
||||
if (!brush)
|
||||
continue;
|
||||
|
||||
|
@ -344,19 +353,25 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
|
||||
if (mousetool == BT_PUSHFACE)
|
||||
{
|
||||
if (!face)
|
||||
continue; //FIXME: should not happen.
|
||||
tmp.faces[face-1].planedist += tmp.faces[face-1].planenormal * displace;
|
||||
for (facenum = 0; facenum < tmp.numfaces; facenum++)
|
||||
{
|
||||
if (facemask & (1ul<<facenum))
|
||||
tmp.faces[facenum].planedist += tmp.faces[facenum].planenormal * displace;
|
||||
}
|
||||
}
|
||||
else if (mousetool == BT_MOVETEXTURE)
|
||||
{
|
||||
if (!face)
|
||||
continue; //FIXME: should not happen.
|
||||
tmp.faces[face-1].sbias -= tmp.faces[face-1].sdir * displace;
|
||||
tmp.faces[face-1].tbias -= tmp.faces[face-1].tdir * displace;
|
||||
for (facenum = 0; facenum < tmp.numfaces; facenum++)
|
||||
{
|
||||
if (facemask & (1ul<<facenum))
|
||||
{
|
||||
tmp.faces[facenum].sbias -= tmp.faces[facenum].sdir * displace;
|
||||
tmp.faces[facenum].tbias -= tmp.faces[facenum].tdir * displace;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mousetool == BT_MOVE || mousetool == BT_CLONEDISPLACE)
|
||||
brushface_translate(tmp.faces, tmp.numfaces, displace);
|
||||
brushface_translate(displace);
|
||||
else if (mousetool == BT_ROTATE)
|
||||
{
|
||||
//find the brush's middle (based on its bbox)
|
||||
|
@ -365,18 +380,20 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
makevectors(col);
|
||||
|
||||
//move it so its pivot is at the origin
|
||||
brushface_translate(tmp.faces, tmp.numfaces, -mid);
|
||||
//rotate it
|
||||
brushface_rotate(tmp.faces, tmp.numfaces);
|
||||
brushface_translate(-mid);
|
||||
//rotate it (by v_forward etc)
|
||||
brushface_rotate();
|
||||
//reposition it around its pivot again
|
||||
brushface_translate(tmp.faces, tmp.numfaces, mid);
|
||||
brushface_translate(mid);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if (mousetool == BT_MOVETEXTURE)
|
||||
if (0)//mousetool == BT_MOVETEXTURE)
|
||||
{
|
||||
points = brush_calcfacepoints(model, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
|
||||
DrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3); //faded to give an idea, without stopping you from seeing what you're aligning it to.
|
||||
|
||||
/*points = brush_calcfacepoints(model, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (points)
|
||||
{
|
||||
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.
|
||||
|
@ -396,39 +413,45 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
for (point = 0; point < points; point++)
|
||||
R_PolygonVertex(facepoints[point] + tmp.faces[face-1].planenormal*0.1, [(facepoints[point] * tmp.faces[face-1].sdir + tmp.faces[face-1].sbias)/sz_x, (facepoints[point] * tmp.faces[face-1].tdir + tmp.faces[face-1].tbias)/sz_y], col, 1);
|
||||
R_EndPolygon();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
else
|
||||
{
|
||||
//draw it wireframe
|
||||
for(facenum = 0; facenum < tmp.numfaces;)
|
||||
//draw it wireframe WITH depth testing
|
||||
if (tmp.numcp)
|
||||
DrawQCPatchWireframe(tmp.cp, tmp.patchinfo, "terrainedit", '0 0 1', 1);
|
||||
else
|
||||
{
|
||||
points = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
continue; //should probably warn somehow about this
|
||||
//should we use two colour channels? one depth one not?
|
||||
if (facenum == face)
|
||||
col = '1 0 0';
|
||||
else
|
||||
col = '0 0.5 0';
|
||||
R_BeginPolygon("chop");
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
for (point = 0; point < points-1; )
|
||||
//draw it wireframe
|
||||
for(facenum = 0; facenum < tmp.numfaces;)
|
||||
{
|
||||
point++;
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
points = brush_calcfacepoints(++facenum, tmp.faces, tmp.numfaces, facepoints, MAX_FACEPOINTS);
|
||||
if (!points)
|
||||
continue; //should probably warn somehow about this
|
||||
//should we use two colour channels? one depth one not?
|
||||
if (facemask & (1ul<<facenum))
|
||||
col = '1 0 0';
|
||||
else
|
||||
col = '0 0.5 0';
|
||||
R_BeginPolygon("chop");
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
for (point = 0; point < points-1; )
|
||||
{
|
||||
point++;
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
}
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
R_EndPolygon();
|
||||
R_PolygonVertex(facepoints[point], '0 0', col, 1);
|
||||
}
|
||||
R_PolygonVertex(facepoints[0], '0 0', col, 1);
|
||||
R_EndPolygon();
|
||||
|
||||
DrawQCBrushTextured(tmp.faces, tmp.numfaces, '1 1 1', 0.3); //faded to give an idea, without stopping you from seeing what you're aligning it to.
|
||||
DrawQCBrushWireframe(tmp.faces, tmp.numfaces, "terrainedit", '0 0 1', 1);
|
||||
}
|
||||
}
|
||||
|
||||
//draw it wireframe WITH depth testing
|
||||
DrawQCBrushWireframe(tmp.faces, tmp.numfaces, "terrainedit", '0 0 1', 1);
|
||||
|
||||
DebrushifyLite(tmp.faces, tmp.numfaces);
|
||||
DebrushifyLite();
|
||||
DrawAxisExtensions(vertedit.p, vertedit.numverts, "terrainedit", '0 0 0.3', 1);
|
||||
|
||||
if (!mousedown)
|
||||
|
@ -438,6 +461,8 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
if (displace*displace > 1)
|
||||
brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
|
||||
}
|
||||
else if (tmp.numcp)
|
||||
patch_history_edit(model, brush, tmp.cp, tmp.patchinfo);
|
||||
else
|
||||
brush_history_edit(model, brush, tmp.faces, tmp.numfaces, tmp.contents);
|
||||
bt_points = 0;
|
||||
|
@ -478,7 +503,9 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
vector o = mousenear;
|
||||
if (vlen(o - t) > 8192 && !autocvar_ca_brush_view)
|
||||
t = o + normalize(t)*8192;
|
||||
*world_hitcontentsmaski = ~0; //I want to be able to select water...
|
||||
traceline(o, t, TRUE, world);
|
||||
*world_hitcontentsmaski = 0; //don't break stuff
|
||||
|
||||
#if 0
|
||||
//find all the brushes within 32qu of the mouse cursor's impact point
|
||||
|
@ -515,7 +542,7 @@ void(vector mousepos) editor_brushes_addentities =
|
|||
// bt_point[showpoints++] = brush_snappoint(trace_endpos);
|
||||
showpoints = 3;
|
||||
}
|
||||
if (brushtool == BT_CREATE)
|
||||
if (brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
|
||||
bt_point[showpoints++] = brush_snappoint(trace_endpos);
|
||||
|
||||
//FIXME: if slicing, draw the intersection
|
||||
|
@ -607,7 +634,13 @@ void(vector mousepos) editor_brushes_overlay =
|
|||
case BT_CREATEDRAG:
|
||||
drawrawstring('0 32 0', "Drag+Create", '8 8 0', '1 1 1', 1);
|
||||
break;
|
||||
case BT_CREATE:
|
||||
case BT_CREATEPATCH:
|
||||
if (autocvar_ca_explicitpatch)
|
||||
drawrawstring('0 32 0', sprintf("Create Explicit %g*%g Patch", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);
|
||||
else
|
||||
drawrawstring('0 32 0', sprintf("Create Auto %g*%g Patch", autocvar_ca_cpwidth, autocvar_ca_cpheight), '8 8 0', '1 1 1', 1);
|
||||
break;
|
||||
case BT_CREATEBRUSH:
|
||||
drawrawstring('0 32 0', "Paint+Create", '8 8 0', '1 1 1', 1);
|
||||
break;
|
||||
case BT_SLICE:
|
||||
|
@ -653,10 +686,12 @@ brusheditormodes
|
|||
registercommand("brushedit_redo");
|
||||
registercommand("brushedit_delete");
|
||||
registercommand("brushedit_create");
|
||||
registercommand("brushedit_createpatch");
|
||||
registercommand("brushedit_slice");
|
||||
registercommand("brushedit_matchface");
|
||||
registercommand("brushedit_resettexcoords");
|
||||
registercommand("brushedit_settexture");
|
||||
registercommand("brushedit_scrolltex");
|
||||
registercommand("brushedit_subtract");
|
||||
registercommand("brushedit_binds");
|
||||
registercommand("brushedit_binds_default");
|
||||
|
@ -679,13 +714,21 @@ brusheditormodes
|
|||
brush_undo();
|
||||
break;
|
||||
case "brushedit_create":
|
||||
brushtool = BT_CREATE;
|
||||
brushtool = BT_CREATEBRUSH;
|
||||
bt_points = 0;
|
||||
break;
|
||||
case "brushedit_createpatch":
|
||||
brushtool = BT_CREATEPATCH;
|
||||
bt_points = 0;
|
||||
break;
|
||||
case "brushedit_slice":
|
||||
brushtool = BT_SLICE;
|
||||
bt_points = 0;
|
||||
break;
|
||||
case "brushedit_scrolltex":
|
||||
brushtool = BT_MOVETEXTURE;
|
||||
bt_points = 0;
|
||||
break;
|
||||
|
||||
case "+brushedit_nogrid": nogrid = TRUE; break;
|
||||
case "-brushedit_nogrid": nogrid = FALSE; break;
|
||||
|
@ -719,9 +762,7 @@ brusheditormodes
|
|||
brushedit_resettextures();
|
||||
break;
|
||||
case "brushedit_settexture":
|
||||
//IMPLEMENTME
|
||||
brushtool = BT_NONE;
|
||||
bt_points = 0;
|
||||
brushedit_settexture(argv(1));
|
||||
break;
|
||||
case "brushedit_subtract":
|
||||
brushedit_subtract();
|
||||
|
@ -737,15 +778,16 @@ brusheditormodes
|
|||
localcmd("bind ctrl+z brushedit_undo\n");
|
||||
localcmd("bind ctrl+y brushedit_redo\n");
|
||||
localcmd("bind ctrl+i brushedit_create\n");
|
||||
localcmd("bind ctrl+p brushedit_createpatch\n");
|
||||
localcmd("bind ctrl+s brushedit_slice\n");
|
||||
localcmd("bind ctrl+m brushedit_matchface\n");
|
||||
localcmd("bind ctrl+delete brushedit_delete\n");
|
||||
localcmd("bind ctrl+del brushedit_delete\n");
|
||||
localcmd("bind ctrl+backspace brushedit_subtract\n");
|
||||
localcmd("bind ctrl+c +brushedit_clone\n");
|
||||
localcmd("bind ctrl+d +brushedit_draw\n");
|
||||
localcmd("bind ctrl+r +brushedit_rotate\n");
|
||||
localcmd("bind ctrl+f +brushedit_pushface\n");
|
||||
localcmd("bind ctrl+t +brushedit_scrolltex\n");
|
||||
localcmd("bind ctrl+t brushedit_scrolltex\n");
|
||||
localcmd("bind ctrl+v +brushedit_vertex\n");
|
||||
break;
|
||||
case "brushedit_binds":
|
||||
|
@ -806,7 +848,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
{
|
||||
if (vertedit.selectedvert1 != -1)
|
||||
{
|
||||
mousedown = TRUE;
|
||||
mousedown |= 1;
|
||||
mousetool = BT_VERTEXEDIT;
|
||||
if (vertedit.selectedvert2 != -1)
|
||||
{
|
||||
|
@ -823,7 +865,9 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
*world_hitcontentsmaski = ~0; //I want to be able to select water...
|
||||
traceline(o, t, TRUE, world);
|
||||
*world_hitcontentsmaski = 0; //don't break stuff.
|
||||
float tracemodel = trace_ent.modelindex;
|
||||
|
||||
/*if (brushtool == BT_CREATEDRAG || (brushtool == BT_PUSHFACE && selectedbrushface))
|
||||
|
@ -835,10 +879,12 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
else*/ if (brushtool != BT_PUSHFACE && brushtool != BT_MOVETEXTURE)
|
||||
trace_brush_faceid = 0;
|
||||
|
||||
if (brushtool == BT_SLICE || brushtool == BT_CREATE)
|
||||
if (brushtool == BT_SLICE || brushtool == BT_CREATEBRUSH || brushtool == BT_CREATEPATCH)
|
||||
{
|
||||
if (brushtool == BT_SLICE && bt_points > 2)
|
||||
bt_points = 2;
|
||||
if (brushtool == BT_CREATEPATCH && bt_points > 3)
|
||||
bt_points = 3;
|
||||
|
||||
//FIXME: BT_CREATE is planar. so keep the points planar too.
|
||||
//FIXME: need a way to move points already placed
|
||||
|
@ -883,10 +929,10 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
if (!tracemodel || !trace_brush_id)
|
||||
{
|
||||
}
|
||||
else if (brush_isselected(tracemodel, trace_brush_id))
|
||||
brush_deselect(tracemodel, trace_brush_id);
|
||||
else if (brushface_isselected(tracemodel, trace_brush_id, trace_brush_faceid))
|
||||
brushface_deselect(tracemodel, trace_brush_id, trace_brush_faceid);
|
||||
else
|
||||
brush_select(tracemodel, trace_brush_id);
|
||||
brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
|
||||
}
|
||||
else
|
||||
{ //deselect everything but the targetted brush.
|
||||
|
@ -895,11 +941,11 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
else if (!brush_isselected(tracemodel, trace_brush_id))
|
||||
{
|
||||
brush_deselectall();
|
||||
brush_select(tracemodel, trace_brush_id);
|
||||
brushface_select(tracemodel, trace_brush_id, trace_brush_faceid);
|
||||
}
|
||||
else
|
||||
{ //its already selected, start doing whatever the current mouse action is meant to be
|
||||
mousedown = TRUE;
|
||||
mousedown |= 1;
|
||||
mousetool = brushtool;
|
||||
vertedit.selectedvert1 = -1;
|
||||
vertedit.selectedvert2 = -1;
|
||||
|
@ -939,7 +985,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
|
||||
if (selectedbrushface && (brushtool == BT_PUSHFACE || brushtool == BT_MOVETEXTURE))
|
||||
{
|
||||
mousedown = TRUE;
|
||||
mousedown |= 1;
|
||||
mousetool = brushtool;
|
||||
vertedit.selectedvert1 = -1;
|
||||
vertedit.selectedvert2 = -1;
|
||||
|
@ -975,7 +1021,51 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
mousedown = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
else if (brushtool == BT_CREATE)
|
||||
else if (brushtool == BT_CREATEPATCH)
|
||||
{
|
||||
if (bt_points == 4)
|
||||
{
|
||||
int x,y;
|
||||
patchvert_t *vert;
|
||||
patchvert_t *cp;
|
||||
patchinfo_t patchinfo = {autocvar_ca_newbrushtexture, CONTENTBIT_SOLID, max(2,autocvar_ca_cpwidth), max(2,autocvar_ca_cpheight), 0, 0, '0 0 0'};
|
||||
tmp.numfaces = 0;
|
||||
tmp.numcp = 0;
|
||||
bt_points = 0;
|
||||
|
||||
if (!autocvar_ca_explicitpatch)
|
||||
{ //must be odd sizes. cos grr.
|
||||
patchinfo.cpwidth|=1;
|
||||
patchinfo.cpheight|=1;
|
||||
patchinfo.tesswidth = patchinfo.tessheight = -1; //proper q3 patch, engine decides according to smoothness (which means nothing quite fits).
|
||||
}
|
||||
if (patchinfo.cpwidth*patchinfo.cpheight>64*64)
|
||||
{
|
||||
cprint(sprintf("%i*%i patch dimensions too large", patchinfo.cpwidth, patchinfo.cpheight));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//populate the CPs, averaging from the 4 specified coords.
|
||||
cp = vert = memalloc(sizeof(*vert) * patchinfo.cpwidth*patchinfo.cpheight);
|
||||
for (y = 0; y < patchinfo.cpheight; y++)
|
||||
for (x = 0; x < patchinfo.cpwidth; x++)
|
||||
{
|
||||
cp->s = x/(patchinfo.cpwidth-1);
|
||||
cp->t = y/(patchinfo.cpheight-1);
|
||||
vector top = bt_point[0]+(bt_point[1]-bt_point[0])*cp->s;
|
||||
vector bot = bt_point[3]+(bt_point[2]-bt_point[3])*cp->s;
|
||||
cp->xyz = top+(bot-top)*cp->t;
|
||||
cp->rgb = '1 1 1'; cp->a = 1;
|
||||
cp++;
|
||||
}
|
||||
|
||||
brush_deselectall();
|
||||
patch_history_create(selectedbrushmodel, vert, patchinfo, TRUE);
|
||||
memfree(vert);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else if (brushtool == BT_CREATEBRUSH)
|
||||
{
|
||||
if (bt_points >= 3)
|
||||
{
|
||||
|
@ -983,7 +1073,7 @@ float(float key, float unic, vector mousepos) editor_brushes_key =
|
|||
tmp.numcp = 0;
|
||||
bt_points = 0;
|
||||
brush_deselectall();
|
||||
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, 1i, TRUE);
|
||||
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, CONTENTBIT_SOLID, TRUE);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue