cvar_t r_fxaa = {CVAR_SAVE, "r_fxaa", "0", "fast approximate anti aliasing"};
cvar_t mod_noshader_default_offsetmapping = {CVAR_SAVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"};
cvar_t mod_obj_orientation = {0, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"};
+cvar_t mod_q2bsp_littransparentsurfaces = {0, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
cvar_t mod_q3bsp_curves_collisions_stride = {0, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
cvar_t mod_q3bsp_curves_stride = {0, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
Cvar_RegisterVariable(&r_fxaa);
Cvar_RegisterVariable(&mod_noshader_default_offsetmapping);
Cvar_RegisterVariable(&mod_obj_orientation);
+ Cvar_RegisterVariable(&mod_q2bsp_littransparentsurfaces);
Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
if (tx->skinframes[0] && tx->skinframes[0]->hasalpha)
tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
}
+ else if (tx->name[0] == '{') // fence textures
+ {
+ tx->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
+ }
else if (!strncmp(tx->name, "mirror", 6)) // Tenebrae
{
// replace the texture with black
tx = loadmodel->data_textures + i;
if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
continue;
+ num = tx->name[1];
+ if ((num < '0' || num > '9') && (num < 'a' || num > 'j'))
+ {
+ Con_Printf("Bad animating texture %s\n", tx->name);
+ continue;
+ }
if (tx->anim_total[0] || tx->anim_total[1])
continue; // already sequenced
anims[num - '0'] = tx2;
else if (num >= 'a' && num <= 'j')
altanims[num - 'a'] = tx2;
- else
- Con_Printf("Bad animating texture %s\n", tx->name);
+ // No need to warn otherwise - we already did above.
}
max = altmax = 0;
if (incomplete)
continue;
+ // If we have exactly one frame, something's wrong.
+ if (max + altmax <= 1)
+ {
+ Con_Printf("Texture %s is animated (leading +) but has only one frame\n", tx->name);
+ }
+
if (altmax < 1)
{
// if there is no alternate animation, duplicate the primary
altanims[k] = anims[k];
}
+ if (max < 1)
+ {
+ // Warn.
+ Con_Printf("Missing frame 0 of %s\n", tx->name);
+
+ // however, we can handle this by duplicating the alternate animation into the primary
+ max = altmax;
+ for (k = 0;k < 10;k++)
+ anims[k] = altanims[k];
+ }
+
+
// link together the primary animation
for (j = 0;j < max;j++)
{
for (i = 0;i < (int)sizeof(value);i++)
if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
break;
- if (value[i])
+ if (i < (int)sizeof(value) && value[i])
{
for (;i < (int)sizeof(value);i++)
{
Mod_AllocLightmap_Init(&allocState, lightmapsize, lightmapsize);
for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
{
- int i, iu, iv, lightmapx = 0, lightmapy = 0;
+ int iu, iv, lightmapx = 0, lightmapy = 0;
float u, v, ubase, vbase, uscale, vscale;
if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
out->numsurfaces = (unsigned short)MSG_ReadLittleShort(sb);
if (out->firstsurface + out->numsurfaces > (unsigned int)loadmodel->num_surfaces)
{
- Con_Printf("Mod_Q2BSP_LoadNodes: invalid surface index range %i+%i (file has only %i surfaces)\n", p, out->firstsurface, out->numsurfaces, loadmodel->num_surfaces);
+ Con_Printf("Mod_Q2BSP_LoadNodes: invalid surface index range %i+%i (file has only %i surfaces)\n", out->firstsurface, out->numsurfaces, loadmodel->num_surfaces);
out->firstsurface = 0;
out->numsurfaces = 0;
}
static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb)
{
mtexinfo_t *out;
- int i, j, k, l, count;
+ int i, l, count;
int structsize = 76;
int maxtextures = 1024; // hardcoded limit of quake2 engine, so we may as well use it as an upper bound
char filename[MAX_QPATH];
for (i = 0;i < count;i++, out++)
{
+ int j, k;
for (k = 0;k < 2;k++)
for (j = 0;j < 4;j++)
out->vecs[k][j] = MSG_ReadLittleFloat(sb);
// find an existing match for the texture if possible
dpsnprintf(filename, sizeof(filename), "textures/%s.wal", out->q2texture);
for (j = 0;j < loadmodel->num_texturesperskin;j++)
- if (!strcmp(filename, loadmodel->data_textures[j].name) && out->q2flags == loadmodel->data_textures[j].q2flags)
+ if (!strcmp(filename, loadmodel->data_textures[j].name)
+ && out->q2flags == loadmodel->data_textures[j].q2flags
+ && out->q2value == loadmodel->data_textures[j].q2value)
break;
// if we don't find the texture, store the new texture
if (j == loadmodel->num_texturesperskin)
int q2flags = out->q2flags;
unsigned char *walfile = NULL;
fs_offset_t walfilesize = 0;
- Mod_LoadTextureFromQ3Shader(tx, filename, true, true, TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS);
+ Mod_LoadTextureFromQ3Shader(tx, filename, true, true, TEXF_ALPHA | TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS);
// now read the .wal file to get metadata (even if a .tga was overriding it, we still need the wal data)
walfile = FS_LoadFile(filename, tempmempool, true, &walfilesize);
if (walfile)
{
int w, h;
- char q2animname32c[32];
- LoadWAL_GetMetadata(walfile, (int)walfilesize, &w, &h, &q2flags, &tx->q2value, &tx->q2contents, q2animname32c);
+ LoadWAL_GetMetadata(walfile, (int)walfilesize, &w, &h, NULL, NULL, &tx->q2contents, NULL);
tx->width = w;
tx->height = h;
- tx->q2flags = q2flags;
Mem_Free(walfile);
}
+ else
+ {
+ tx->width = 16;
+ tx->height = 16;
+ }
+ tx->q2flags = out->q2flags;
+ tx->q2value = out->q2value;
// also modify the texture to have the correct contents and such based on flags
// note that we create multiple texture_t structures if q2flags differs
if (q2flags & Q2SURF_LIGHT)
if (q2flags & Q2SURF_TRANS33)
{
tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED;
- tx->basealpha = 0.3333f;
+ tx->basealpha = 1.0f / 3.0f;
tx->supercontents &= ~SUPERCONTENTS_OPAQUE;
if (tx->q2contents & Q2CONTENTS_SOLID)
tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW;
if (q2flags & Q2SURF_TRANS66)
{
tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED;
- tx->basealpha = 0.6667f;
+ tx->basealpha = 2.0f / 3.0f;
tx->supercontents &= ~SUPERCONTENTS_OPAQUE;
if (tx->q2contents & Q2CONTENTS_SOLID)
tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW;
if (q2flags & Q2SURF_FLOWING)
{
tx->tcmods[0].tcmod = Q3TCMOD_SCROLL;
- tx->tcmods[0].parms[0] = -1.6f;
+ if (q2flags & Q2SURF_WARP)
+ tx->tcmods[0].parms[0] = -0.5f;
+ else
+ tx->tcmods[0].parms[0] = -1.6f;
tx->tcmods[0].parms[1] = 0.0f;
}
+ if (q2flags & Q2SURF_ALPHATEST)
+ {
+ // KMQUAKE2 and other modded engines added this flag for lit alpha tested surfaces
+ tx->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_NOSHADOW;
+ }
+ else if (q2flags & (Q2SURF_TRANS33 | Q2SURF_TRANS66 | Q2SURF_WARP))
+ {
+ if (!mod_q2bsp_littransparentsurfaces.integer)
+ tx->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
+ }
if (q2flags & Q2SURF_NODRAW)
{
tx->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
// if we encounter the textures out of order, the later ones won't mark the earlier ones in a sequence, so the earlier
for (i = 0, out = loadmodel->brushq1.texinfo;i < count;i++, out++)
{
- int j = i;
+ int j, k;
texture_t *t = loadmodel->data_textures + out->textureindex;
+ t->currentframe = t; // fix the reallocated pointer
// if this is not animated, skip it
// if this is already processed, skip it (part of an existing sequence)
int i, j, firstside, numsides, contents, count, maxplanes, q3surfaceflags, supercontents;
colplanef_t *planes;
int structsize = 12;
+ qboolean brushmissingtextures;
+ int numbrushesmissingtextures = 0;
+ int numcreatedtextures = 0;
if (sb->cursize % structsize)
Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
maxplanes = 0;
planes = NULL;
- for (i = 0;i < count;i++, out++)
+ for (i = 0; i < count; i++, out++)
{
firstside = MSG_ReadLittleLong(sb);
numsides = MSG_ReadLittleLong(sb);
out->firstbrushside = loadmodel->brush.data_brushsides + firstside;
out->numbrushsides = numsides;
- // not really the same... we should store the q2 contents
- out->texture = out->firstbrushside->texture;
// convert the contents to our values
supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, contents);
+ // problem: q2bsp brushes have contents but not a texture
+ // problem: q2bsp brushsides *may* have a texture or may not
+ // problem: all brushsides and brushes must have a texture for trace_hittexture functionality to work, and the collision code is engineered around this assumption
+ // solution: nasty hacks
+ brushmissingtextures = false;
+ out->texture = NULL;
+ for (j = 0; j < out->numbrushsides; j++)
+ {
+ if (out->firstbrushside[j].texture == &mod_q1bsp_texture_solid)
+ brushmissingtextures = true;
+ else
+ {
+ // if we can find a matching texture on a brush side we can use it instead of creating one
+ if (out->firstbrushside[j].texture->supercontents == supercontents)
+ out->texture = out->firstbrushside[j].texture;
+ }
+ }
+ if (brushmissingtextures || out->texture == NULL)
+ {
+ numbrushesmissingtextures++;
+ // if we didn't find any appropriate texture (matching contents), we'll have to create one
+ // we could search earlier ones for a matching one but that can be slow
+ if (out->texture == NULL)
+ {
+ texture_t *validtexture;
+ validtexture = (texture_t *)Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
+ dpsnprintf(validtexture->name, sizeof(validtexture->name), "brushcollision%i", numcreatedtextures);
+ validtexture->surfaceflags = 0;
+ validtexture->supercontents = supercontents;
+ numcreatedtextures++;
+ out->texture = validtexture;
+ }
+ // out->texture now contains a texture with appropriate contents, copy onto any missing sides
+ for (j = 0; j < out->numbrushsides; j++)
+ if (out->firstbrushside[j].texture == &mod_q1bsp_texture_solid)
+ out->firstbrushside[j].texture = out->texture;
+ }
+
+ // make a colbrush from the brush
+ q3surfaceflags = 0;
// make a list of mplane_t structs to construct a colbrush from
if (maxplanes < out->numbrushsides)
{
Mem_Free(planes);
planes = (colplanef_t *)Mem_Alloc(tempmempool, sizeof(colplanef_t) * maxplanes);
}
- q3surfaceflags = 0;
for (j = 0;j < out->numbrushsides;j++)
{
VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags;
planes[j].texture = out->firstbrushside[j].texture;
q3surfaceflags |= planes[j].q3surfaceflags;
- // LordHavoc: kind of a mean hack here, but we want the surfaces to have the brush contents
- out->firstbrushside[j].texture->supercontents = supercontents;
}
- // make the colbrush from the planes
out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, q3surfaceflags, out->texture, true);
// this whole loop can take a while (e.g. on redstarrepublic4)
}
if (planes)
Mem_Free(planes);
+ if (numcreatedtextures)
+ Con_DPrintf("Mod_Q2BSP_LoadBrushes: %i brushes own sides that lack textures or have differing contents from the brush, %i textures have been created to describe these contents.\n", numbrushesmissingtextures, numcreatedtextures);
}
static void Mod_Q2BSP_LoadPOP(sizebuf_t *sb)
int maxvn = 0, numvn = 1;
char *texturenames = NULL;
float dist, modelradius, modelyawradius, yawradius;
- float *v = NULL;
- float *vt = NULL;
- float *vn = NULL;
+ float *obj_v = NULL;
+ float *obj_vt = NULL;
+ float *obj_vn = NULL;
float mins[3];
float maxs[3];
float corner[3];
skinfile_t *skinfiles = NULL;
unsigned char *data = NULL;
int *submodelfirstsurface;
- msurface_t *surface;
+ msurface_t *tempsurface;
msurface_t *tempsurfaces;
memset(&vfirst, 0, sizeof(vfirst));
if (maxv <= numv)
{
maxv = max(maxv * 2, 1024);
- v = (float *)Mem_Realloc(tempmempool, v, maxv * sizeof(float[3]));
+ obj_v = (float *)Mem_Realloc(tempmempool, obj_v, maxv * sizeof(float[3]));
}
if(mod_obj_orientation.integer)
{
- v[numv*3+0] = atof(argv[1]);
- v[numv*3+2] = atof(argv[2]);
- v[numv*3+1] = atof(argv[3]);
+ obj_v[numv*3+0] = atof(argv[1]);
+ obj_v[numv*3+2] = atof(argv[2]);
+ obj_v[numv*3+1] = atof(argv[3]);
}
else
{
- v[numv*3+0] = atof(argv[1]);
- v[numv*3+1] = atof(argv[2]);
- v[numv*3+2] = atof(argv[3]);
+ obj_v[numv*3+0] = atof(argv[1]);
+ obj_v[numv*3+1] = atof(argv[2]);
+ obj_v[numv*3+2] = atof(argv[3]);
}
numv++;
}
if (maxvt <= numvt)
{
maxvt = max(maxvt * 2, 1024);
- vt = (float *)Mem_Realloc(tempmempool, vt, maxvt * sizeof(float[2]));
+ obj_vt = (float *)Mem_Realloc(tempmempool, obj_vt, maxvt * sizeof(float[2]));
}
- vt[numvt*2+0] = atof(argv[1]);
- vt[numvt*2+1] = 1-atof(argv[2]);
+ obj_vt[numvt*2+0] = atof(argv[1]);
+ obj_vt[numvt*2+1] = 1-atof(argv[2]);
numvt++;
}
else if (!strcmp(argv[0], "vn"))
if (maxvn <= numvn)
{
maxvn = max(maxvn * 2, 1024);
- vn = (float *)Mem_Realloc(tempmempool, vn, maxvn * sizeof(float[3]));
+ obj_vn = (float *)Mem_Realloc(tempmempool, obj_vn, maxvn * sizeof(float[3]));
}
if(mod_obj_orientation.integer)
{
- vn[numvn*3+0] = atof(argv[1]);
- vn[numvn*3+2] = atof(argv[2]);
- vn[numvn*3+1] = atof(argv[3]);
+ obj_vn[numvn*3+0] = atof(argv[1]);
+ obj_vn[numvn*3+2] = atof(argv[2]);
+ obj_vn[numvn*3+1] = atof(argv[3]);
}
else
{
- vn[numvn*3+0] = atof(argv[1]);
- vn[numvn*3+1] = atof(argv[2]);
- vn[numvn*3+2] = atof(argv[3]);
+ obj_vn[numvn*3+0] = atof(argv[1]);
+ obj_vn[numvn*3+1] = atof(argv[2]);
+ obj_vn[numvn*3+2] = atof(argv[3]);
}
numvn++;
}
vcurrent.nextindex = -1;
vcurrent.textureindex = textureindex;
vcurrent.submodelindex = submodelindex;
- if (v && index1 >= 0 && index1 < numv)
- VectorCopy(v + 3*index1, vcurrent.v);
- if (vt && index2 >= 0 && index2 < numvt)
- Vector2Copy(vt + 2*index2, vcurrent.vt);
- if (vn && index3 >= 0 && index3 < numvn)
- VectorCopy(vn + 3*index3, vcurrent.vn);
+ if (obj_v && index1 >= 0 && index1 < numv)
+ VectorCopy(obj_v + 3*index1, vcurrent.v);
+ if (obj_vt && index2 >= 0 && index2 < numvt)
+ Vector2Copy(obj_vt + 2*index2, vcurrent.vt);
+ if (obj_vn && index3 >= 0 && index3 < numvn)
+ VectorCopy(obj_vn + 3*index3, vcurrent.vn);
if (numtriangles == 0)
{
VectorCopy(vcurrent.v, mins);
// allocate storage for the worst case number of surfaces, later we resize
tempsurfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, numtextures * loadmodel->brush.numsubmodels * sizeof(msurface_t));
submodelfirstsurface = (int *)Mem_Alloc(loadmodel->mempool, (loadmodel->brush.numsubmodels+1) * sizeof(int));
- surface = tempsurfaces;
+ tempsurface = tempsurfaces;
for (submodelindex = 0;submodelindex < loadmodel->brush.numsubmodels;submodelindex++)
{
submodelfirstsurface[submodelindex] = loadmodel->num_surfaces;
surfacevertices = 0;
surfaceelements = 0;
// we hack in a texture index in the surface to be fixed up later...
- surface->texture = (texture_t *)((size_t)textureindex);
+ tempsurface->texture = (texture_t *)((size_t)textureindex);
// calculate bounds as we go
- VectorCopy(thisvertex->v, surface->mins);
- VectorCopy(thisvertex->v, surface->maxs);
+ VectorCopy(thisvertex->v, tempsurface->mins);
+ VectorCopy(thisvertex->v, tempsurface->maxs);
for (;vertexindex < numtriangles*3;vertexindex++)
{
thisvertex = vertices + vertexindex;
if (thisvertex->textureindex != textureindex)
continue;
// add vertex to surface bounds
- surface->mins[0] = min(surface->mins[0], thisvertex->v[0]);
- surface->mins[1] = min(surface->mins[1], thisvertex->v[1]);
- surface->mins[2] = min(surface->mins[2], thisvertex->v[2]);
- surface->maxs[0] = max(surface->maxs[0], thisvertex->v[0]);
- surface->maxs[1] = max(surface->maxs[1], thisvertex->v[1]);
- surface->maxs[2] = max(surface->maxs[2], thisvertex->v[2]);
+ tempsurface->mins[0] = min(tempsurface->mins[0], thisvertex->v[0]);
+ tempsurface->mins[1] = min(tempsurface->mins[1], thisvertex->v[1]);
+ tempsurface->mins[2] = min(tempsurface->mins[2], thisvertex->v[2]);
+ tempsurface->maxs[0] = max(tempsurface->maxs[0], thisvertex->v[0]);
+ tempsurface->maxs[1] = max(tempsurface->maxs[1], thisvertex->v[1]);
+ tempsurface->maxs[2] = max(tempsurface->maxs[2], thisvertex->v[2]);
// add the vertex if it is not found in the merged set, and
// get its index (triangle element) for the surface
vertexhashindex = (unsigned int)(thisvertex->v[0] * 3571 + thisvertex->v[0] * 1777 + thisvertex->v[0] * 457) % (unsigned int)vertexhashsize;
surfaceelements++;
}
surfacetriangles = surfaceelements / 3;
- surface->num_vertices = surfacevertices;
- surface->num_triangles = surfacetriangles;
- surface->num_firstvertex = firstvertex;
- surface->num_firsttriangle = firsttriangle;
- firstvertex += surface->num_vertices;
- firsttriangle += surface->num_triangles;
- surface++;
+ tempsurface->num_vertices = surfacevertices;
+ tempsurface->num_triangles = surfacetriangles;
+ tempsurface->num_firstvertex = firstvertex;
+ tempsurface->num_firsttriangle = firsttriangle;
+ firstvertex += tempsurface->num_vertices;
+ firsttriangle += tempsurface->num_triangles;
+ tempsurface++;
loadmodel->num_surfaces++;
}
}
// free data
Mem_Free(vertices);
Mem_Free(texturenames);
- Mem_Free(v);
- Mem_Free(vt);
- Mem_Free(vn);
+ Mem_Free(obj_v);
+ Mem_Free(obj_vt);
+ Mem_Free(obj_vn);
Mem_Free(vertexhashtable);
Mem_Free(vertexhashdata);
for (j = 0;j < mod->nummodelsurfaces;j++)
{
const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
- const float *v = mod->surfmesh.data_vertex3f + 3 * surface->num_firstvertex;
+ const float *v3f = mod->surfmesh.data_vertex3f + 3 * surface->num_firstvertex;
int k;
if (!surface->num_vertices)
continue;
if (!l)
{
l = true;
- VectorCopy(v, mod->normalmins);
- VectorCopy(v, mod->normalmaxs);
+ VectorCopy(v3f, mod->normalmins);
+ VectorCopy(v3f, mod->normalmaxs);
}
- for (k = 0;k < surface->num_vertices;k++, v += 3)
+ for (k = 0;k < surface->num_vertices;k++, v3f += 3)
{
- mod->normalmins[0] = min(mod->normalmins[0], v[0]);
- mod->normalmins[1] = min(mod->normalmins[1], v[1]);
- mod->normalmins[2] = min(mod->normalmins[2], v[2]);
- mod->normalmaxs[0] = max(mod->normalmaxs[0], v[0]);
- mod->normalmaxs[1] = max(mod->normalmaxs[1], v[1]);
- mod->normalmaxs[2] = max(mod->normalmaxs[2], v[2]);
+ mod->normalmins[0] = min(mod->normalmins[0], v3f[0]);
+ mod->normalmins[1] = min(mod->normalmins[1], v3f[1]);
+ mod->normalmins[2] = min(mod->normalmins[2], v3f[2]);
+ mod->normalmaxs[0] = max(mod->normalmaxs[0], v3f[0]);
+ mod->normalmaxs[1] = max(mod->normalmaxs[1], v3f[1]);
+ mod->normalmaxs[2] = max(mod->normalmaxs[2], v3f[2]);
}
}
corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));