404 lines
No EOL
9.9 KiB
C++
404 lines
No EOL
9.9 KiB
C++
|
|
typedef struct
|
|
{
|
|
int numverts;
|
|
int numidx;
|
|
vector *p;
|
|
int *i;
|
|
|
|
int selectedvert1;
|
|
int selectedvert2;
|
|
|
|
int model;
|
|
int brush;
|
|
} 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.
|
|
//note that the trisoup does not have textures. they will be reconciled when reforming the brush.
|
|
void(vertsoup_t *vertedit, int model, int brush) Debrushify =
|
|
{
|
|
int *ni;
|
|
int i, j, k;
|
|
int points;
|
|
vector p;
|
|
vector *np;
|
|
static int fi[64];
|
|
vertedit->model = model;
|
|
vertedit->brush = brush;
|
|
vertedit->numidx = 0;
|
|
vertedit->numverts = 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);
|
|
if (!points)
|
|
break;
|
|
|
|
//allocate a few new indexes
|
|
ni = memalloc(sizeof(*ni) * (vertedit->numidx+3*(points-2)));
|
|
memcpy(ni, vertedit->i, sizeof(*ni) * vertedit->numidx);
|
|
memfree(vertedit->i);
|
|
vertedit->i = ni;
|
|
ni += vertedit->numidx;
|
|
vertedit->numidx += 3*(points-2);
|
|
|
|
for (j = 0; j < points; j++)
|
|
{
|
|
p = facepoints[j];
|
|
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
|
|
p_y = floor(p_y + 0.5);
|
|
p_z = floor(p_z + 0.5);
|
|
for (k = 0; k < vertedit->numverts; k++)
|
|
{ //try to be nice and re-use verts.
|
|
if (vertedit->p[k] == p)
|
|
break;
|
|
}
|
|
if (k == vertedit->numverts)
|
|
{
|
|
//if it wasn't found, we need to allocate a new one
|
|
np = memalloc(sizeof(*np) * (vertedit->numverts+1));
|
|
memcpy(np, vertedit->p, sizeof(*np) * vertedit->numverts);
|
|
memfree(vertedit->p);
|
|
vertedit->p = np;
|
|
np += vertedit->numverts;
|
|
vertedit->numverts += 1;
|
|
*np = p;
|
|
}
|
|
fi[j] = k;
|
|
}
|
|
for (j = 2; j < points; j++)
|
|
{
|
|
*ni++ = fi[0];
|
|
*ni++ = fi[j-1];
|
|
*ni++ = fi[j];
|
|
}
|
|
}
|
|
};
|
|
|
|
//determines only the various points of the brush, without duplicates. doesn't care about indexes.
|
|
void(void) DebrushifyLite =
|
|
{
|
|
int points, k;
|
|
vector p;
|
|
vector *np;
|
|
int fi[64];
|
|
vertedit.numidx = 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, tmp.faces, tmp.numfaces, facepoints, facepoints.length);
|
|
if (!points)
|
|
break;
|
|
|
|
for (int j = 0; j < points; j++)
|
|
{
|
|
p = facepoints[j];
|
|
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
|
|
p_y = floor(p_y + 0.5);
|
|
p_z = floor(p_z + 0.5);
|
|
for (k = 0; k < vertedit.numverts; k++)
|
|
{ //try to be nice and re-use verts.
|
|
if (vertedit.p[k] == p)
|
|
break;
|
|
}
|
|
if (k == vertedit.numverts)
|
|
{
|
|
//if it wasn't found, we need to allocate a new one
|
|
np = memalloc(sizeof(*np) * (vertedit.numverts+1));
|
|
memcpy(np, vertedit.p, sizeof(*np) * vertedit.numverts);
|
|
memfree(vertedit.p);
|
|
vertedit.p = np;
|
|
np += vertedit.numverts;
|
|
vertedit.numverts += 1;
|
|
*np = p;
|
|
}
|
|
fi[j] = k;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
static float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) planenormal =
|
|
{
|
|
vector v1 = soup->p[idx[0]];
|
|
vector v2 = soup->p[idx[1]];
|
|
vector v3 = soup->p[idx[2]];
|
|
vector d1 = v3 - v1;
|
|
vector d2 = v2 - v1;
|
|
vector d3 = v3 - v2;
|
|
norm = normalize(crossproduct(d1, d2));
|
|
dist = norm * v1;
|
|
|
|
if (!d1 || !d2 || !d3 || !norm)
|
|
return FALSE;
|
|
return TRUE;
|
|
};
|
|
|
|
void(vertsoup_t *soup, int drawit) Rebrushify =
|
|
{
|
|
brushface_t faces[MAX_BRUSHFACES];
|
|
int numfaces, f, point;
|
|
string shader = 0;
|
|
|
|
vector n;
|
|
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);
|
|
|
|
//if any triangle's neighbour opposes it, reform the edge across the quad to try to keep it convex.
|
|
for(point = 0; point+2 < soup->numidx; point+=3)
|
|
{
|
|
int p1 = soup->i[point+0];
|
|
int p2 = soup->i[point+1];
|
|
int p3 = soup->i[point+2];
|
|
if (!planenormal(soup, &soup->i[point], n, d))
|
|
continue; //degenerate
|
|
d += EPSILON;
|
|
for(f = point+3; f+2 < soup->numidx; f+=3)
|
|
{
|
|
int o1 = soup->i[f+0];
|
|
int o2 = soup->i[f+1];
|
|
int o3 = soup->i[f+2];
|
|
p1p2edge:
|
|
if (o2 == p1 && o1 == p2)
|
|
o = o3;
|
|
else if (o3 == p1 && o2 == p2)
|
|
o = o1;
|
|
else if (o1 == p1 && o3 == p2)
|
|
o = o2;
|
|
else
|
|
goto p2p3edge;
|
|
if (soup->p[o] * n > d)
|
|
{
|
|
soup->i[f+0] = p3;
|
|
soup->i[f+1] = o;
|
|
soup->i[f+2] = p2;
|
|
p2 = o;
|
|
}
|
|
continue;
|
|
p2p3edge:
|
|
if (o2 == p2 && o1 == p3)
|
|
o = o3;
|
|
else if (o3 == p2 && o2 == p3)
|
|
o = o1;
|
|
else if (o1 == p2 && o3 == p3)
|
|
o = o2;
|
|
else
|
|
goto p3p1edge;
|
|
if (soup->p[o] * n > d)
|
|
{
|
|
soup->i[f+0] = p1;
|
|
soup->i[f+1] = o;
|
|
soup->i[f+2] = p3;
|
|
p3 = o;
|
|
}
|
|
continue;
|
|
p3p1edge:
|
|
if (o2 == p3 && o1 == p1)
|
|
o = o3;
|
|
else if (o3 == p3 && o2 == p1)
|
|
o = o1;
|
|
else if (o1 == p3 && o3 == p1)
|
|
o = o2;
|
|
else
|
|
continue;
|
|
if (soup->p[o] * n > d)
|
|
{
|
|
soup->i[f+0] = p2;
|
|
soup->i[f+1] = o;
|
|
soup->i[f+2] = p1;
|
|
p1 = o;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
soup->i[point+0] = p1;
|
|
soup->i[point+1] = p2;
|
|
soup->i[point+2] = p3;
|
|
}
|
|
|
|
//generate the plane info
|
|
numfaces = 0;
|
|
for(point = 0; point+2 < soup->numidx; point+=3)
|
|
{
|
|
if (!planenormal(soup, &soup->i[point], n, d))
|
|
continue; //a degenerate triangle is one that probably got merged or something
|
|
|
|
for (f = 0; f < numfaces; f++)
|
|
{
|
|
if (faces[f].planenormal == n && faces[f].planedist == d)
|
|
break;
|
|
}
|
|
if (f < numfaces)
|
|
continue; //duplicate plane
|
|
|
|
for (f = 0; f < tmp.numfaces; f++)
|
|
{
|
|
if (tmp.faces[f].planenormal * n > 0.999) //stupid inprecisions
|
|
{
|
|
if (numfaces == 64)
|
|
return;
|
|
|
|
//note that we only care about the normal, not the dist. this means you can move the entire face forward+back without loosing textures.
|
|
faces[numfaces].shadername = shader = tmp.faces[f].shadername;
|
|
faces[numfaces].planenormal = n;
|
|
faces[numfaces].planedist = d;
|
|
faces[numfaces].sdir = tmp.faces[f].sdir;
|
|
faces[numfaces].sbias = tmp.faces[f].sbias;
|
|
faces[numfaces].tdir = tmp.faces[f].tdir;
|
|
faces[numfaces].tbias = tmp.faces[f].tbias;
|
|
numfaces++;
|
|
break;
|
|
}
|
|
}
|
|
if (f < tmp.numfaces)
|
|
continue; //matched a plane in the original brush
|
|
|
|
if (numfaces == 64)
|
|
return;
|
|
|
|
//FIXME: find aproximate faces to give corners or something
|
|
|
|
//okay, it appears to be new. that's a pain.
|
|
faces[numfaces].shadername = 0;
|
|
faces[numfaces].planenormal = n;
|
|
faces[numfaces].planedist = d;
|
|
reset_texturecoords(&faces[numfaces]);
|
|
numfaces++;
|
|
}
|
|
|
|
//any surface without a texture/shader yet should inherit one from some other surface
|
|
if (!shader)
|
|
shader = autocvar_ca_newbrushtexture;
|
|
for(f = 0; f < numfaces; f++)
|
|
{
|
|
if (!faces[f].shadername)
|
|
faces[f].shadername = shader;
|
|
}
|
|
|
|
int internals = 0;
|
|
//If we have a point outside a plane, then we know we have a concave volume.
|
|
//chop the points
|
|
for(f = 0; f < numfaces; f++)
|
|
{
|
|
n = faces[f].planenormal;
|
|
d = faces[f].planedist;
|
|
for (point = 0; point < soup->numidx; point++) //would ideally use points, but I want to cover dead ones
|
|
{
|
|
if (soup->p[soup->i[point]] * n > d + EPSILON)
|
|
{
|
|
internals++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces));
|
|
|
|
if (numfaces <= 3)
|
|
return; //can't possibly be valid.
|
|
|
|
if (drawit)
|
|
{
|
|
//draw textured preview (yay for block lighting)
|
|
DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1);
|
|
|
|
//draw it wireframe without depth testing
|
|
DrawQCBrushWireframe(faces, numfaces, "chop", internals?[1,0,0]:[0,0,1], 1);
|
|
}
|
|
else
|
|
{
|
|
brush_history_edit(soup->model, soup->brush, faces, numfaces, 1i);
|
|
|
|
soup->numidx = 0;
|
|
soup->numverts = 0;
|
|
}
|
|
}; |