engine/quakec/csaddon/src/brush_vertedit.qc

404 lines
9.9 KiB
Plaintext

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;
}
};