More preliminary support for patches. Loading+Saving+Display of control points will now work. No QC interactions yet.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5297 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2018-08-21 19:06:41 +00:00
parent ffd726eb50
commit 3fb88f2ec2
3 changed files with 289 additions and 158 deletions

View File

@ -517,7 +517,7 @@ typedef struct
//q2origin,q3origin 0x01000000 //could define, but normally removed by compiler, so why?
#define FTECONTENTS_BODY 0x02000000
#define FTECONTENTS_CORPSE 0x04000000
//q2detail,q3detail 0x08000000 //not very useful to us
#define FTECONTENTS_DETAIL 0x08000000 //not very useful to us, but used by .map support
//q2translucent,q3structual 0x10000000
//q2ladder,q3translucent 0x20000000
//q3trigger 0x40000000

View File

@ -5760,7 +5760,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
const miptex_t *tx = NULL;
#endif
if (!Q_strcasecmp(bt->shadername, "clip"))
if (!Q_strcasecmp(bt->shadername, "clip") || !Q_strcasecmp(bt->shadername, "hint") || !Q_strcasecmp(bt->shadername, "skip"))
bt->shader = R_RegisterShader(bt->shadername, SUF_LIGHTMAP, "{\nsurfaceparm nodraw\n}");
else
bt->shader = R_RegisterCustom (bt->shadername, SUF_LIGHTMAP, Shader_DefaultBSPQ1, NULL);
@ -6015,125 +6015,147 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
out->axialplanes = 0;
out->patch = NULL;
out->planes = BZ_Malloc((sizeof(*out->planes)+sizeof(*out->faces)) * brush->numplanes);
out->faces = (void*)(out->planes+brush->numplanes);
ClearBounds(out->mins, out->maxs);
for (iface = 0, oface = 0; iface < brush->numplanes; iface++)
out->planes = NULL;
out->faces = NULL;
out->numplanes = 0;
if (brush->numplanes)
{
for (j = 0; j < oface; j++)
out->planes = BZ_Malloc((sizeof(*out->planes)+sizeof(*out->faces)) * brush->numplanes);
out->faces = (void*)(out->planes+brush->numplanes);
ClearBounds(out->mins, out->maxs);
for (iface = 0, oface = 0; iface < brush->numplanes; iface++)
{
if (out->planes[j][0] == brush->planes[iface][0] &&
out->planes[j][1] == brush->planes[iface][1] &&
out->planes[j][2] == brush->planes[iface][2] &&
out->planes[j][3] == brush->planes[iface][3])
break;
}
if (j < oface)
{
Con_DPrintf("duplicate plane\n");
continue;
}
//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)
numpoints = Fragment_ClipPlaneToBrush(facepoints, sizeof(facepoints)/sizeof(facepoints[0]), brush->planes, sizeof(*brush->planes), brush->numplanes, brush->planes[iface]);
if (!numpoints)
{
Con_DPrintf("redundant face\n");
continue; //this surface was chopped away entirely, and isn't relevant.
}
//copy the basic face info out so we can save/restore/query/edit it later.
Vector4Copy(brush->planes[iface], out->planes[oface]);
out->faces[oface].tex = brush->faces[iface].tex;
Vector4Copy(brush->faces[iface].stdir[0], out->faces[oface].stdir[0]);
Vector4Copy(brush->faces[iface].stdir[1], out->faces[oface].stdir[1]);
if (out->planes[oface][0] == 1)
out->axialplanes |= 1u<<0;
else if (out->planes[oface][1] == 1)
out->axialplanes |= 1u<<1;
else if (out->planes[oface][2] == 1)
out->axialplanes |= 1u<<2;
else if (out->planes[oface][0] == -1)
out->axialplanes |= 1u<<3;
else if (out->planes[oface][1] == -1)
out->axialplanes |= 1u<<4;
else if (out->planes[oface][2] == -1)
out->axialplanes |= 1u<<5;
//make sure this stuff is rebuilt properly.
out->faces[oface].tex->rebuild = true;
//keep this stuff cached+reused, so everything is consistant. also work out min/max lightmap texture coords
out->faces[oface].points = BZ_Malloc(numpoints * sizeof(*out->faces[oface].points));
Vector2Set(mins, 0, 0);
Vector2Set(maxs, 0, 0);
for (j = 0; j < numpoints; j++)
{
AddPointToBounds(facepoints[j], out->mins, out->maxs);
VectorCopy(facepoints[j], out->faces[oface].points[j]);
for (k = 0; k < 2; k++)
for (j = 0; j < oface; j++)
{
lm[k] = DotProduct(out->faces[oface].points[j], out->faces[oface].stdir[k]) + out->faces[oface].stdir[k][3];
if (j == 0)
mins[k] = maxs[k] = lm[k];
else if (lm[k] > maxs[k])
maxs[k] = lm[k];
else if (lm[k] < mins[k])
mins[k] = lm[k];
if (out->planes[j][0] == brush->planes[iface][0] &&
out->planes[j][1] == brush->planes[iface][1] &&
out->planes[j][2] == brush->planes[iface][2] &&
out->planes[j][3] == brush->planes[iface][3])
break;
}
if (j < oface)
{
Con_DPrintf("duplicate plane\n");
continue;
}
}
out->faces[oface].numpoints = numpoints;
//determine lightmap scale, and extents. rescale the lightmap if it ought to have been subdivided.
out->faces[oface].relight = true;
out->faces[oface].lmscale = 16;
for (k = 0; k < 2; )
{
out->faces[oface].lmbias[k] = floor(mins[k]/out->faces[oface].lmscale);
out->faces[oface].lmextents[k] = ceil((maxs[k])/out->faces[oface].lmscale)-out->faces[oface].lmbias[k]+1;
if (out->faces[oface].lmextents[k] > 128)
{ //surface is too large for lightmap data. just drop its resolution, because splitting the face in plane-defined geometry is a bad idea.
if (out->faces[oface].lmscale > 256)
//generate points now (so we know the correct mins+maxs for the brush, and whether the plane is relevent)
numpoints = Fragment_ClipPlaneToBrush(facepoints, sizeof(facepoints)/sizeof(facepoints[0]), brush->planes, sizeof(*brush->planes), brush->numplanes, brush->planes[iface]);
if (!numpoints)
{
Con_DPrintf("redundant face\n");
continue; //this surface was chopped away entirely, and isn't relevant.
}
//copy the basic face info out so we can save/restore/query/edit it later.
Vector4Copy(brush->planes[iface], out->planes[oface]);
out->faces[oface].tex = brush->faces[iface].tex;
Vector4Copy(brush->faces[iface].stdir[0], out->faces[oface].stdir[0]);
Vector4Copy(brush->faces[iface].stdir[1], out->faces[oface].stdir[1]);
if (out->planes[oface][0] == 1)
out->axialplanes |= 1u<<0;
else if (out->planes[oface][1] == 1)
out->axialplanes |= 1u<<1;
else if (out->planes[oface][2] == 1)
out->axialplanes |= 1u<<2;
else if (out->planes[oface][0] == -1)
out->axialplanes |= 1u<<3;
else if (out->planes[oface][1] == -1)
out->axialplanes |= 1u<<4;
else if (out->planes[oface][2] == -1)
out->axialplanes |= 1u<<5;
//make sure this stuff is rebuilt properly.
out->faces[oface].tex->rebuild = true;
//keep this stuff cached+reused, so everything is consistant. also work out min/max lightmap texture coords
out->faces[oface].points = BZ_Malloc(numpoints * sizeof(*out->faces[oface].points));
Vector2Set(mins, 0, 0);
Vector2Set(maxs, 0, 0);
for (j = 0; j < numpoints; j++)
{
AddPointToBounds(facepoints[j], out->mins, out->maxs);
VectorCopy(facepoints[j], out->faces[oface].points[j]);
for (k = 0; k < 2; k++)
{
out->faces[oface].relight = false;
k++;
lm[k] = DotProduct(out->faces[oface].points[j], out->faces[oface].stdir[k]) + out->faces[oface].stdir[k][3];
if (j == 0)
mins[k] = maxs[k] = lm[k];
else if (lm[k] > maxs[k])
maxs[k] = lm[k];
else if (lm[k] < mins[k])
mins[k] = lm[k];
}
}
out->faces[oface].numpoints = numpoints;
//determine lightmap scale, and extents. rescale the lightmap if it ought to have been subdivided.
out->faces[oface].relight = true;
out->faces[oface].lmscale = 16;
for (k = 0; k < 2; )
{
out->faces[oface].lmbias[k] = floor(mins[k]/out->faces[oface].lmscale);
out->faces[oface].lmextents[k] = ceil((maxs[k])/out->faces[oface].lmscale)-out->faces[oface].lmbias[k]+1;
if (out->faces[oface].lmextents[k] > 128)
{ //surface is too large for lightmap data. just drop its resolution, because splitting the face in plane-defined geometry is a bad idea.
if (out->faces[oface].lmscale > 256)
{
out->faces[oface].relight = false;
k++;
}
else
{
out->faces[oface].lmscale *= 2;
k = 0;
}
}
else
{
out->faces[oface].lmscale *= 2;
k = 0;
}
k++;
}
out->faces[oface].lightmap = -1;
out->faces[oface].lmbase[0] = 0;
out->faces[oface].lmbase[1] = 0;
if (out->faces[oface].relight)
{
out->faces[oface].lightdata = BZ_Malloc(out->faces[oface].lmextents[0] * out->faces[oface].lmextents[1] * 3);
memset(out->faces[oface].lightdata, 0x3f, out->faces[oface].lmextents[0]*out->faces[oface].lmextents[1]*3);
}
else
k++;
}
out->faces[oface].lightmap = -1;
out->faces[oface].lmbase[0] = 0;
out->faces[oface].lmbase[1] = 0;
if (out->faces[oface].relight)
{
out->faces[oface].lightdata = BZ_Malloc(out->faces[oface].lmextents[0] * out->faces[oface].lmextents[1] * 3);
memset(out->faces[oface].lightdata, 0x3f, out->faces[oface].lmextents[0]*out->faces[oface].lmextents[1]*3);
}
else
out->faces[oface].lightdata = NULL;
out->faces[oface].lightdata = NULL;
// Con_Printf("lm extents: %u %u (%i points)\n", out->faces[oface].lmextents[0], out->faces[oface].lmextents[1], numpoints);
oface++;
// Con_Printf("lm extents: %u %u (%i points)\n", out->faces[oface].lmextents[0], out->faces[oface].lmextents[1], numpoints);
oface++;
}
out->numplanes = oface;
}
if (oface < 4)
if (brush->patch)
{
out->patch = BZ_Malloc(sizeof(*out->patch)-sizeof(out->patch->verts) + sizeof(*out->patch->verts)*brush->patch->xpoints*brush->patch->ypoints);
memcpy(out->patch, brush->patch, sizeof(*out->patch)-sizeof(out->patch->verts) + sizeof(*out->patch->verts)*brush->patch->xpoints*brush->patch->ypoints);
numpoints = out->patch->xpoints*out->patch->ypoints;
//FIXME: lightmap...
for (j = 0; j < numpoints; j++)
AddPointToBounds(out->patch->verts[j].v, out->mins, out->maxs);
out->patch->tex->rebuild = true;
}
if ((out->numplanes < 4 && out->numplanes) || (out->numplanes && out->patch) || (!out->numplanes && !out->patch))
{ //a brush with less than 4 planes cannot be a valid convex area (but can happen when certain redundant planes are chopped out). don't accept creation
//(we often get 2-plane brushes if the sides are sucked in)
for (j = 0; j < oface; j++)
for (j = 0; j < out->numplanes; j++)
{
BZ_Free(out->faces[j].lightdata);
BZ_Free(out->faces[j].points);
}
BZ_Free(out->planes);
BZ_Free(out->patch);
return NULL;
}
out->numplanes = oface;
if (brush->id)
out->id = brush->id;
else
@ -6169,6 +6191,42 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
return out;
}
static brushes_t *Terr_Patch_Insert(model_t *model, heightmap_t *hm, brushtex_t *patch_tex, int patch_w, int patch_h, vec5_t *patch_v, int stride)
{
int x, y;
brushes_t brush;
//finish the brush
brush.contents = 0;
brush.numplanes = 0;
brush.planes = NULL;
brush.faces = NULL;
brush.id = 0;
brush.patch = alloca(sizeof(*brush.patch)-sizeof(brush.patch->verts) + sizeof(*brush.patch->verts)*patch_w*patch_h);
brush.patch->tex = patch_tex;
brush.patch->xpoints = patch_w;
brush.patch->ypoints = patch_h;
for (y = 0; y < patch_h; y++)
{
for (x = 0; x < patch_w; x++)
{
brush.patch->verts[x + y*patch_w].v[0] = patch_v[x][0];
brush.patch->verts[x + y*patch_w].v[1] = patch_v[x][1];
brush.patch->verts[x + y*patch_w].v[2] = patch_v[x][2];
brush.patch->verts[x + y*patch_w].tc[0] = patch_v[x][3];
brush.patch->verts[x + y*patch_w].tc[1] = patch_v[x][4];
//brush.patch->verts[x + y*patch_w].norm
//brush.patch->verts[x + y*patch_w].sdir
//brush.patch->verts[x + y*patch_w].tdir
}
patch_v += stride;
}
return Terr_Brush_Insert(model, hm, &brush);
}
static void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx)
{
int i;
@ -6997,17 +7055,14 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s
void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br)
{
//valve 220 format:
//{
//( -0 -0 16 ) ( -0 -0 32 ) ( 64 -0 16 ) texname [x y z d] [x y z d] rotation sscale tscale
//}
float *point[3];
int i, x, y;
qboolean valve220 = true;
VFS_PRINTF(file, "\n{");
if (br->patch)
{
VFS_PRINTF(file, "\n\tpatchDef2\n\t{\n\t\"%s\"\n\t( %.9g %.9g %.9g %.9g %.9g )\n\t(\t\n",
VFS_PRINTF(file, "\n\tpatchDef2\n\t{\n\t\t\"%s\"\n\t\t( %.9g %.9g %.9g %.9g %.9g )\n\t\t(\n",
br->patch->tex?br->patch->tex->shadername:"",
0.0/*xoffset*/,
0.0/*yoffset*/,
@ -7016,36 +7071,73 @@ void Terr_WriteBrushInfo(vfsfile_t *file, brushes_t *br)
1.0/*yscale*/);
for (y = 0; y < br->patch->ypoints; y++)
{
VFS_PRINTF(file, "\t\t( ");
VFS_PRINTF(file, "\t\t\t(\n");
for (x = 0; x < br->patch->xpoints; x++)
{
VFS_PRINTF(file, "( %.9g %.9g %.9g %.9g %.9g )", br->patch->verts[x + y*br->patch->xpoints].v[0],
br->patch->verts[x + y*br->patch->xpoints].v[1],
br->patch->verts[x + y*br->patch->xpoints].v[2],
br->patch->verts[x + y*br->patch->xpoints].tc[0],
br->patch->verts[x + y*br->patch->xpoints].tc[1]);
VFS_PRINTF(file, "\t\t\t\t( %.9g %.9g %.9g %.9g %.9g )\n", br->patch->verts[x + y*br->patch->xpoints].v[0],
br->patch->verts[x + y*br->patch->xpoints].v[1],
br->patch->verts[x + y*br->patch->xpoints].v[2],
br->patch->verts[x + y*br->patch->xpoints].tc[0],
br->patch->verts[x + y*br->patch->xpoints].tc[1]);
}
VFS_PRINTF(file, " )");
VFS_PRINTF(file, "\t\t\t)\n");
}
VFS_PRINTF(file, " )\n\t}\n");
VFS_PRINTF(file, "\t\t)\n\t}\n");
}
else
{
for (i = 0; i < br->numplanes; i++)
{
const char *texname, *s;
point[0] = br->faces[i].points[0];
point[1] = br->faces[i].points[1];
point[2] = br->faces[i].points[2];
//valve 220 format:
//(-0 -0 16) (-0 -0 32) (64 -0 16) texname [x y z d] [x y z d] rotation sscale tscale
//don't treat whitespace as optional, even if it works with qbsp it'll screw up third party editors.
//%.9g is 'meant' to be lossless for a standard ieee single-precision float. (%.17g for a double)
VFS_PRINTF(file, "\n( %.9g %.9g %.9g ) ( %.9g %.9g %.9g ) ( %.9g %.9g %.9g ) \"%s\" [ %.9g %.9g %.9g %.9g ] [ %.9g %.9g %.9g %.9g ] 0 1 1",
point[0][0], point[0][1], point[0][2],
point[1][0], point[1][1], point[1][2],
point[2][0], point[2][1], point[2][2],
br->faces[i].tex?br->faces[i].tex->shadername:"",
br->faces[i].stdir[0][0], br->faces[i].stdir[0][1], br->faces[i].stdir[0][2], br->faces[i].stdir[0][3],
br->faces[i].stdir[1][0], br->faces[i].stdir[1][1], br->faces[i].stdir[1][2], br->faces[i].stdir[1][3]
//write the 3 points-on-plane. I really hope its not degenerate
VFS_PRINTF(file, "\n( %.9g %.9g %.9g ) ( %.9g %.9g %.9g ) ( %.9g %.9g %.9g )",
point[0][0], point[0][1], point[0][2],
point[1][0], point[1][1], point[1][2],
point[2][0], point[2][1], point[2][2]
);
//write the name - if it contains markup or control chars, or other weird glyphs then be sure to quote it.
//we could always quote it, but that can and will screw up some editor somewhere...
for (s = texname = br->faces[i].tex?br->faces[i].tex->shadername:""; *s; s++)
{
if (*s <= 32 || *s >= 127 || *s == '\\' || *s == '(' || *s == '[' || *s == '{' || *s == ')' || *s == ']' || *s == '}')
break; //
}
VFS_PRINTF(file, (!*texname || *s)?" \"%s\"":" %s", texname);
if (valve220)
{
VFS_PRINTF(file, " [ %.9g %.9g %.9g %.9g ] [ %.9g %.9g %.9g %.9g ] 0 1 1",
br->faces[i].stdir[0][0], br->faces[i].stdir[0][1], br->faces[i].stdir[0][2], br->faces[i].stdir[0][3],
br->faces[i].stdir[1][0], br->faces[i].stdir[1][1], br->faces[i].stdir[1][2], br->faces[i].stdir[1][3]
);
}
else
{
float soffset, toffset, rotation, sscale, tscale;
//FIXME: project onto the axial plane, then figure out new values.
soffset = toffset = 0;
rotation = 0;
sscale = tscale = 1;
VFS_PRINTF(file, " %.9g %.9g %.9g %.9g %.9g", soffset, toffset, rotation, sscale, tscale);
}
//historical note: Q2 used contents|surfaceflags|value.
// however, Q3 uses the contents value exclusively for a detail flag. everything else comes from shaders.
if (br->contents != FTECONTENTS_SOLID || br->faces[i].surfaceflags || br->faces[i].surfacevalue)
VFS_PRINTF(file, " %i %i %i", br->contents, br->faces[i].surfaceflags, br->faces[i].surfacevalue);
// else if (hexen2)
// VFS_PRINTF(file, " -1"); //Light
}
}
@ -7201,17 +7293,23 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
char *out, *outstart, *start;
int i;
int submodelnum = 0;
qboolean isdetail = false;
qboolean foundsubmodel = false;
qboolean inbrush = false;
int numplanes = 0;
vec4_t planes[64];
struct brushface_s faces[64];
int brushcontents = FTECONTENTS_SOLID;
heightmap_t *subhm = NULL;
model_t *submod = NULL;
const char *brushpunct = "(){}[]"; //use an empty string for better compat with vanilla qbsp...
//brush planes
int numplanes = 0;
vec4_t planes[256];
struct brushface_s faces[countof(planes)];
//patch info
brushtex_t *patch_tex;
int patch_w, patch_h;
vec5_t patch_v[64][64];
#ifdef RUNTIMELIGHTING
hm->entsdirty = true;
hm->relightcontext = LightStartup(NULL, mod, false, false);
@ -7231,21 +7329,29 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
nest--;
if (inbrush)
{
brushes_t brush;
//finish the brush
brush.contents = brushcontents;
brush.numplanes = numplanes;
brush.planes = planes;
brush.faces = faces;
brush.id = 0;
if (numplanes && subhm)
if (subhm)
{
qboolean oe = subhm->brushesedited;
Terr_Brush_Insert(submod, subhm, &brush);
if (numplanes)
{
brushes_t brush;
//finish the brush
brush.contents = brushcontents;
brush.numplanes = numplanes;
brush.planes = planes;
brush.faces = faces;
brush.id = 0;
brush.patch = NULL;
Terr_Brush_Insert(submod, subhm, &brush);
}
else
Terr_Patch_Insert(submod, subhm, patch_tex, patch_w, patch_h, patch_v[0], countof(patch_v[0]));
subhm->brushesedited = oe;
}
numplanes = 0;
inbrush = false;
patch_tex = NULL;
brushcontents = FTECONTENTS_SOLID;
continue;
}
}
@ -7255,16 +7361,10 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
if (nest == 1)
{ //entering a new entity
foundsubmodel = false;
isdetail = false;
}
if (nest == 2)
{
if (isdetail) //func_detail injects its brushes into the world model for some reason.
{
submod = mod;
subhm = hm;
}
else if (!foundsubmodel)
if (!foundsubmodel)
{
foundsubmodel = true;
if (submodelnum)
@ -7332,12 +7432,18 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
}
else if (inbrush && !strcmp(token, "patchDef2"))
{
vec5_t pvert[64][64];
int x, y;
if (numplanes || patch_tex)
{
Con_Printf(CON_ERROR "%s: mixed patch+planes\n", mod->name);
return false;
}
memset(patch_v, 0, sizeof(patch_v));
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (strcmp(token, "{")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
/*parse texture name*/
patch_tex = Terr_Brush_FindTexture(subhm, token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
@ -7356,6 +7462,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
if (strcmp(token, "(")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
y = 0;
patch_w = patch_h = 0;
while (!strcmp(token, "("))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
@ -7363,31 +7470,35 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
while (!strcmp(token, "("))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
pvert[y][x][0] = atof(token);
patch_v[y][x][0] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
pvert[y][x][1] = atof(token);
patch_v[y][x][1] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
pvert[y][x][2] = atof(token);
patch_v[y][x][2] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
pvert[y][x][3] = atof(token);
patch_v[y][x][3] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
pvert[y][x][4] = atof(token);
patch_v[y][x][4] = atof(token);
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (x < countof(pvert[y])-1)
if (x < countof(patch_v[y])-1)
x++;
}
if (patch_w < x)
patch_w = x;
if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (y < countof(pvert)-1)
if (y < countof(patch_v)-1)
y++;
}
patch_h = y;
if (strcmp(token, ")")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (strcmp(token, "}")) {Con_Printf(CON_ERROR "%s: invalid patch\n", mod->name);return false;}
continue;
}
else if (inbrush)
{
@ -7406,6 +7517,11 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
int p;
qboolean hlstyle = false;
memset(points, 0, sizeof(points));
if (patch_tex)
{
Con_Printf(CON_ERROR "%s: mixed patch+planes\n", mod->name);
return false;
}
for (p = 0; p < 3; p++)
{
if (token[0] != '(' || token[1] != 0)
@ -7454,10 +7570,14 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
brushcontents = FTECONTENTS_SKY;
else if (!Q_strcasecmp(token, "clip"))
brushcontents = FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP;
else if (!Q_strcasecmp(token, "hint"))
brushcontents = 0;
else if (!Q_strcasecmp(token, "skip"))
;//brushcontents = 0;
else
brushcontents = FTECONTENTS_SOLID;
//FIXME: halflife format has the entire [x y z dist] plane specified.
//halflife/valve220 format has the entire [x y z dist] plane specified.
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (*token == '[')
{
@ -7490,7 +7610,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
//]
}
else
{
{ //vanilla quake
VectorClear(texplane[0]);
VectorClear(texplane[1]);
texplane[0][3] = atof(token);
@ -7506,29 +7626,43 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
scale[1] = atof(token);
//hexen2 has some extra junk that is useless - some 'light' value, but its never used and should normally be -1.
//quake3 on the other hand has 3 different args. Contents Unused Unused. The contents conveys only CONTENTS_DETAIL. which is awkward as it varies by game.
//quake2/3 on the other hand has 3 different args. Contents SurfaceFlags SurfaceValue.
//the SurfaceFlags and SurfaceVales are no longer used in q3 (shaders do it all), but contents is still partially used.
//The contents conveys only CONTENTS_DETAIL. which is awkward as it varies somewhat by game, but we assume q2/q3.
faces[numplanes].surfaceflags = 0;
faces[numplanes].surfacevalue = 0;
while (*entities == ' ' || *entities == '\t')
entities++;
if (*entities == '-' || (*entities >= '0' && *entities <= '9'))
{
int ex1;
int ex1, ex2 = 0, ex3 = 0;
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
ex1 = atoi(token);
while (*entities == ' ' || *entities == '\t')
entities++;
if (*entities == '-' || (*entities >= '0' && *entities <= '9'))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
ex2 = atoi(token);
}
while (*entities == ' ' || *entities == '\t')
entities++;
if (*entities == '-' || (*entities >= '0' && *entities <= '9'))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
ex3 = atoi(token);
//if we got this far, then its q3 format.
//q3 is weird. the first extra arg is contents. but only the detail contents is used.
if (ex1 & Q3CONTENTS_DETAIL)
{
brushcontents |= Q3CONTENTS_DETAIL;
}
//propagate these, in case someone tries editing a q2bsp.
faces[numplanes].surfaceflags = ex2;
faces[numplanes].surfacevalue = ex3;
}
}
@ -7592,12 +7726,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities)
else
{
if (!strcmp(token, "classname"))
{
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
if (!strcmp(token, "func_detail"))
isdetail = true;
}
else
entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL);
}

View File

@ -281,7 +281,7 @@ typedef struct
vec3_t sdir;
vec3_t tdir;
} verts[1]; //x+(y*xpoints)
} *patch;
} *patch; //if this is NULL, then its a regular brush. otherwise its a patch.
struct brushface_s
{
brushtex_t *tex;
@ -295,6 +295,8 @@ typedef struct
unsigned short relit:1;
int lmbias[2];
unsigned short lmextents[2];
unsigned int surfaceflags; //used by q2
unsigned int surfacevalue; //used by q2 (generally light levels)
qbyte *lightdata;
} *faces;
} brushes_t;