#define HULLCHECKSTATE_SOLID 1
#define HULLCHECKSTATE_DONE 2
+extern cvar_t collision_prefernudgedfraction;
static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
{
// status variables, these don't need to be saved on the stack when
midf = (t1 - DIST_EPSILON) / (t1 - t2);
t->trace->fraction = bound(0, midf, 1);
+ if (collision_prefernudgedfraction.integer)
+ t->trace->realfraction = t->trace->fraction;
+
#if COLLISIONPARANOID >= 3
Con_Print("D");
#endif
cbox_planes[3].normal[0] = 0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] = 0;cbox_planes[3].dist = maxs[1] - cmins[1];
cbox_planes[4].normal[0] = 0;cbox_planes[4].normal[1] = 0;cbox_planes[4].normal[2] = 1;cbox_planes[4].dist = cmaxs[2] - mins[2];
cbox_planes[5].normal[0] = 0;cbox_planes[5].normal[1] = 0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
- cbox_planes[0].supercontents = boxsupercontents;cbox_planes[0].q3surfaceflags = boxq3surfaceflags;cbox_planes[0].texture = boxtexture;
- cbox_planes[1].supercontents = boxsupercontents;cbox_planes[1].q3surfaceflags = boxq3surfaceflags;cbox_planes[1].texture = boxtexture;
- cbox_planes[2].supercontents = boxsupercontents;cbox_planes[2].q3surfaceflags = boxq3surfaceflags;cbox_planes[2].texture = boxtexture;
- cbox_planes[3].supercontents = boxsupercontents;cbox_planes[3].q3surfaceflags = boxq3surfaceflags;cbox_planes[3].texture = boxtexture;
- cbox_planes[4].supercontents = boxsupercontents;cbox_planes[4].q3surfaceflags = boxq3surfaceflags;cbox_planes[4].texture = boxtexture;
- cbox_planes[5].supercontents = boxsupercontents;cbox_planes[5].q3surfaceflags = boxq3surfaceflags;cbox_planes[5].texture = boxtexture;
+ cbox_planes[0].q3surfaceflags = boxq3surfaceflags;cbox_planes[0].texture = boxtexture;
+ cbox_planes[1].q3surfaceflags = boxq3surfaceflags;cbox_planes[1].texture = boxtexture;
+ cbox_planes[2].q3surfaceflags = boxq3surfaceflags;cbox_planes[2].texture = boxtexture;
+ cbox_planes[3].q3surfaceflags = boxq3surfaceflags;cbox_planes[3].texture = boxtexture;
+ cbox_planes[4].q3surfaceflags = boxq3surfaceflags;cbox_planes[4].texture = boxtexture;
+ cbox_planes[5].q3surfaceflags = boxq3surfaceflags;cbox_planes[5].texture = boxtexture;
memset(trace, 0, sizeof(trace_t));
trace->hitsupercontentsmask = hitsupercontentsmask;
trace->fraction = 1;
void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
{
Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2] + 0.125, p[2] - 65536);
- VectorSet(diffusenormal, 0, 0, -1);
+ // pretend lighting is coming down from above (due to lack of a lightgrid to know primary lighting direction)
+ VectorSet(diffusenormal, 0, 0, 1);
}
static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
{
if (in == inend)
{
- Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+ Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
return;
}
c = *in++;
{
if (in == inend)
{
- Con_Printf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+ Con_Printf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
return;
}
for (c = *in++;c > 0;c--)
{
if (out == outend)
{
- Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
+ Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
return;
}
*out++ = 0;
// fill out all slots with notexture
for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
{
- strcpy(tx->name, "NO TEXTURE FOUND");
+ strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
tx->width = 16;
tx->height = 16;
- tx->skin.base = r_texture_notexture;
+ tx->numskinframes = 1;
+ tx->skinframerate = 1;
+ tx->currentskinframe = tx->skinframes;
+ tx->skinframes[0].base = r_texture_notexture;
+ tx->backgroundcurrentskinframe = tx->backgroundskinframes;
tx->basematerialflags = 0;
if (i == loadmodel->num_textures - 1)
{
- tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
+ tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW;
tx->supercontents = mod_q1bsp_texture_water.supercontents;
tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
}
return;
s = loadmodel->name;
- if (!strncasecmp(s, "maps/", 4))
- s += 4;
+ if (!strncasecmp(s, "maps/", 5))
+ s += 5;
FS_StripExtension(s, mapname, sizeof(mapname));
// just to work around bounds checking when debugging with it (array index out of bounds error thing)
name[j] += 'a' - 'A';
tx = loadmodel->data_textures + i;
- strcpy(tx->name, name);
+ strlcpy(tx->name, name, sizeof(tx->name));
tx->width = mtwidth;
tx->height = mtheight;
}
else
{
- if (!Mod_LoadSkinFrame(&tx->skin, gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true)
- && !Mod_LoadSkinFrame(&tx->skin, gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
+ if (!Mod_LoadSkinFrame(&tx->skinframes[0], gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true)
+ && !Mod_LoadSkinFrame(&tx->skinframes[0], gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
{
// did not find external texture, load it from the bsp or wad3
if (loadmodel->brush.ishlbsp)
{
tx->width = image_width;
tx->height = image_height;
- Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
+ Mod_LoadSkinFrame_Internal(&tx->skinframes[0], tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
}
if (freepixels)
Mem_Free(freepixels);
}
else if (mtdata) // texture included
- Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
+ Mod_LoadSkinFrame_Internal(&tx->skinframes[0], tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
}
}
- if (tx->skin.base == NULL)
+ if (tx->skinframes[0].base == NULL)
{
// no texture found
tx->width = 16;
tx->height = 16;
- tx->skin.base = r_texture_notexture;
+ tx->skinframes[0].base = r_texture_notexture;
}
}
if (strncmp(tx->name,"*lava",5)
&& strncmp(tx->name,"*teleport",9)
&& strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
- tx->basematerialflags |= MATERIALFLAG_WATERALPHA;
+ tx->basematerialflags |= MATERIALFLAG_WATERALPHA | MATERIALFLAG_NOSHADOW;
if (!strncmp(tx->name, "*lava", 5))
{
tx->supercontents = mod_q1bsp_texture_lava.supercontents;
tx->supercontents = mod_q1bsp_texture_water.supercontents;
tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
}
- tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
+ tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW;
}
else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
{
tx->supercontents = mod_q1bsp_texture_sky.supercontents;
tx->surfaceflags = mod_q1bsp_texture_sky.surfaceflags;
- tx->basematerialflags |= MATERIALFLAG_SKY;
+ tx->basematerialflags |= MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
}
else
{
tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
tx->basematerialflags |= MATERIALFLAG_WALL;
}
- if (tx->skin.fog)
- tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
+ if (tx->skinframes[0].fog)
+ tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
// start out with no animation
tx->currentframe = tx;
else if (filesize == 8)
Con_Print("Empty .lit file, ignoring\n");
else
- Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", filesize, 8 + l->filelen * 3);
+ Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", (int) filesize, (int) (8 + l->filelen * 3));
if (data)
{
Mem_Free(data);
int i, j, k;
if (!data)
return;
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
if (com_token[0] != '{')
return; // error
while (1)
{
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
- strcpy(key, com_token + 1);
+ strlcpy(key, com_token + 1, sizeof(key));
else
- strcpy(key, com_token);
+ strlcpy(key, com_token, sizeof(key));
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
dpsnprintf(value, sizeof(value), "%s", com_token);
if (!strcmp("wad", key)) // for HalfLife maps
{
k = value[i];
value[i] = 0;
- strcpy(wadname, "textures/");
- strcat(wadname, &value[j]);
+ strlcpy(wadname, "textures/", sizeof(wadname));
+ strlcat(wadname, &value[j], sizeof(wadname));
W_LoadTextureWadFile(wadname, false);
j = i+1;
if (!k)
out->numleafsurfaces = LittleShort(in->nummarksurfaces);
if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
{
- Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", out->firstleafsurface, out->firstleafsurface + out->numleafsurfaces, 0, loadmodel->brush.num_leafsurfaces);
+ Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", (int)(out->firstleafsurface - loadmodel->brush.data_leafsurfaces), (int)(out->firstleafsurface + out->numleafsurfaces - loadmodel->brush.data_leafsurfaces), 0, loadmodel->brush.num_leafsurfaces);
out->firstleafsurface = NULL;
out->numleafsurfaces = 0;
}
}
}
+qboolean Mod_Q1BSP_CheckWaterAlphaSupport(void)
+{
+ int i, j;
+ mleaf_t *leaf;
+ const unsigned char *pvs;
+ // check all liquid leafs to see if they can see into empty leafs, if any
+ // can we can assume this map supports r_wateralpha
+ for (i = 0, leaf = loadmodel->brush.data_leafs;i < loadmodel->brush.num_leafs;i++, leaf++)
+ {
+ if ((leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME) && (leaf->clusterindex >= 0 && loadmodel->brush.data_pvsclusters))
+ {
+ pvs = loadmodel->brush.data_pvsclusters + leaf->clusterindex * loadmodel->brush.num_pvsclusterbytes;
+ for (j = 0;j < loadmodel->brush.num_leafs;j++)
+ if (CHECKPVSBIT(pvs, loadmodel->brush.data_leafs[j].clusterindex) && loadmodel->brush.data_leafs[j].contents == CONTENTS_EMPTY)
+ return true;
+ }
+ }
+ return false;
+}
+
static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
{
dclipnode_t *in, *out;
if (!maptext)
return;
text = maptext;
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
submodel = 0;
for (;;)
{
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
break;
if (com_token[0] != '{')
return; // error
brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
for (;;)
{
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
if (com_token[0] == '}')
break; // end of entity
}
for (;;)
{
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
return; // error
if (com_token[0] == '}')
break; // end of brush
// FIXME: support hl .map format
for (pointnum = 0;pointnum < 3;pointnum++)
{
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
for (componentnum = 0;componentnum < 3;componentnum++)
{
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
point[pointnum][componentnum] = atof(com_token);
}
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
}
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
strlcpy(facetexture, com_token, sizeof(facetexture));
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
//scroll_s = atof(com_token);
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
//scroll_t = atof(com_token);
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
//rotate = atof(com_token);
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
//scale_s = atof(com_token);
- COM_ParseToken(&data, false);
+ COM_ParseTokenConsole(&data);
//scale_t = atof(com_token);
TriangleNormal(point[0], point[1], point[2], planenormal);
VectorNormalizeDouble(planenormal);
}
}
+#define PORTAL_DIST_EPSILON (1.0 / 32.0)
static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
{
int i, side;
nodeportal = AllocPortal();
nodeportal->plane = *plane;
+ // TODO: calculate node bounding boxes during recursion and calculate a maximum plane size accordingly to improve precision (as most maps do not need 1 billion unit plane polygons)
PolygonD_QuadForPlane(nodeportal->points, nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist, 1024.0*1024.0*1024.0);
nodeportal->numpoints = 4;
side = 0; // shut up compiler warning
for (i = 0;i < nodeportal->numpoints*3;i++)
frontpoints[i] = nodeportal->points[i];
- PolygonD_Divide(nodeportal->numpoints, frontpoints, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, 1.0/32.0, MAX_PORTALPOINTS, nodeportal->points, &nodeportal->numpoints, 0, NULL, NULL, NULL);
+ PolygonD_Divide(nodeportal->numpoints, frontpoints, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, PORTAL_DIST_EPSILON, MAX_PORTALPOINTS, nodeportal->points, &nodeportal->numpoints, 0, NULL, NULL, NULL);
if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
break;
}
RemovePortalFromNodes(portal);
// cut the portal into two portals, one on each side of the node plane
- PolygonD_Divide(portal->numpoints, portal->points, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, 1.0/32.0, MAX_PORTALPOINTS, frontpoints, &numfrontpoints, MAX_PORTALPOINTS, backpoints, &numbackpoints, NULL);
+ PolygonD_Divide(portal->numpoints, portal->points, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, PORTAL_DIST_EPSILON, MAX_PORTALPOINTS, frontpoints, &numfrontpoints, MAX_PORTALPOINTS, backpoints, &numbackpoints, NULL);
if (!numfrontpoints)
{
Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
+ // check if the map supports transparent water rendering
+ loadmodel->brush.supportwateralpha = Mod_Q1BSP_CheckWaterAlphaSupport();
+
if (!mod->brushq1.lightdata)
mod->brush.LightPoint = NULL;
// copy the base model to this one
*mod = *loadmodel;
// rename the clone back to its proper name
- strcpy(mod->name, name);
+ strlcpy(mod->name, name, sizeof(mod->name));
// textures and memory belong to the main model
mod->texturepool = NULL;
mod->mempool = NULL;
memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
data = loadmodel->brush.entities;
// some Q3 maps override the lightgrid_cellsize with a worldspawn key
- if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
+ if (data && COM_ParseTokenConsole(&data) && com_token[0] == '{')
{
while (1)
{
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
break; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
- strcpy(key, com_token + 1);
+ strlcpy(key, com_token + 1, sizeof(key));
else
- strcpy(key, com_token);
+ strlcpy(key, com_token, sizeof(key));
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
- if (!COM_ParseToken(&data, false))
+ if (!COM_ParseTokenConsole(&data))
break; // error
- strcpy(value, com_token);
+ strlcpy(value, com_token, sizeof(value));
if (!strcmp("gridsize", key))
{
if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
typedef struct q3shaderinfo_layer_s
{
- char texturename[Q3PATHLENGTH];
+ int alphatest;
+ int clampmap;
+ float framerate;
+ int numframes;
+ char texturename[TEXTURE_MAXFRAMES][Q3PATHLENGTH];
int blendfunc[2];
qboolean rgbgenvertex;
qboolean alphagenvertex;
q3shaderinfo_t *shader;
q3shaderinfo_layer_t *layer;
int numparameters;
- char parameter[4][Q3PATHLENGTH];
+ char parameter[TEXTURE_MAXFRAMES + 4][Q3PATHLENGTH];
search = FS_Search("scripts/*.shader", true, false);
if (!search)
return;
numparameters = 0;
for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
{
- if (j < 4)
+ if (j < TEXTURE_MAXFRAMES + 4)
{
strlcpy(parameter[j], com_token, sizeof(parameter[j]));
numparameters = j + 1;
}
}
}
- if (layer == shader->layers + 0)
- {
- if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
- shader->textureflags |= Q3TEXTUREFLAG_ALPHATEST;
- }
+ if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
+ layer->alphatest = true;
if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
{
- strlcpy(layer->texturename, parameter[1], sizeof(layer->texturename));
+ if (!strcasecmp(parameter[0], "clampmap"))
+ layer->clampmap = true;
+ layer->numframes = 1;
+ layer->framerate = 1;
+ strlcpy(layer->texturename[0], parameter[1], sizeof(layer->texturename));
if (!strcasecmp(parameter[1], "$lightmap"))
shader->lighting = true;
}
- else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
- strlcpy(layer->texturename, parameter[2], sizeof(layer->texturename));
+ else if (numparameters >= 3 && (!strcasecmp(parameter[0], "animmap") || !strcasecmp(parameter[0], "animclampmap")))
+ {
+ int i;
+ layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
+ layer->framerate = atof(parameter[1]);
+ for (i = 0;i < layer->numframes;i++)
+ strlcpy(layer->texturename[i], parameter[i + 2], sizeof(layer->texturename));
+ }
else if (numparameters >= 2 && !strcasecmp(parameter[0], "rgbgen") && !strcasecmp(parameter[1], "vertex"))
layer->rgbgenvertex = true;
else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphagen") && !strcasecmp(parameter[1], "vertex"))
numparameters = 0;
for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
{
- if (j < 4)
+ if (j < TEXTURE_MAXFRAMES + 4)
{
strlcpy(parameter[j], com_token, sizeof(parameter[j]));
numparameters = j + 1;
shader->surfaceparms |= Q3SURFACEPARM_ALPHASHADOW;
else if (!strcasecmp(parameter[1], "areaportal"))
shader->surfaceparms |= Q3SURFACEPARM_AREAPORTAL;
+ else if (!strcasecmp(parameter[1], "botclip"))
+ shader->surfaceparms |= Q3SURFACEPARM_BOTCLIP;
else if (!strcasecmp(parameter[1], "clusterportal"))
shader->surfaceparms |= Q3SURFACEPARM_CLUSTERPORTAL;
else if (!strcasecmp(parameter[1], "detail"))
shader->surfaceparms |= Q3SURFACEPARM_DETAIL;
else if (!strcasecmp(parameter[1], "donotenter"))
shader->surfaceparms |= Q3SURFACEPARM_DONOTENTER;
+ else if (!strcasecmp(parameter[1], "dust"))
+ shader->surfaceparms |= Q3SURFACEPARM_DUST;
+ else if (!strcasecmp(parameter[1], "hint"))
+ shader->surfaceparms |= Q3SURFACEPARM_HINT;
else if (!strcasecmp(parameter[1], "fog"))
shader->surfaceparms |= Q3SURFACEPARM_FOG;
else if (!strcasecmp(parameter[1], "lava"))
shader->surfaceparms |= Q3SURFACEPARM_LAVA;
else if (!strcasecmp(parameter[1], "lightfilter"))
shader->surfaceparms |= Q3SURFACEPARM_LIGHTFILTER;
+ else if (!strcasecmp(parameter[1], "lightgrid"))
+ shader->surfaceparms |= Q3SURFACEPARM_LIGHTGRID;
else if (!strcasecmp(parameter[1], "metalsteps"))
shader->surfaceparms |= Q3SURFACEPARM_METALSTEPS;
else if (!strcasecmp(parameter[1], "nodamage"))
if (shader->numlayers)
{
shader->primarylayer = shader->layers + 0;
- if (shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
+ if ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA) || shader->layers[1].alphatest)
{
// terrain blending or other effects
shader->backgroundlayer = shader->layers + 0;
shader->primarylayer = shader->layers + 1;
}
// now see if the lightmap came first, and if so choose the second texture instead
- if (!strcasecmp(shader->primarylayer->texturename, "$lightmap"))
+ if (!strcasecmp(shader->primarylayer->texturename[0], "$lightmap"))
shader->primarylayer = shader->layers + 1;
}
}
out->basematerialflags = 0;
if (shader->surfaceparms & Q3SURFACEPARM_SKY)
{
- out->basematerialflags |= MATERIALFLAG_SKY;
+ out->basematerialflags |= MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
if (shader->skyboxname[0])
{
// quake3 seems to append a _ to the skybox name, so this must do so as well
dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname);
}
}
- else if ((shader->surfaceparms & Q3SURFACEPARM_NODRAW) || shader->numlayers == 0)
- out->basematerialflags |= MATERIALFLAG_NODRAW;
+ else if ((out->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0)
+ out->basematerialflags |= MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
else if (shader->surfaceparms & Q3SURFACEPARM_LAVA)
- out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
+ out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_NOSHADOW;
else if (shader->surfaceparms & Q3SURFACEPARM_SLIME)
- out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
+ out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_WATERALPHA | MATERIALFLAG_NOSHADOW;
else if (shader->surfaceparms & Q3SURFACEPARM_WATER)
- out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
+ out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_WATERALPHA | MATERIALFLAG_NOSHADOW;
else
out->basematerialflags |= MATERIALFLAG_WALL;
- if (shader->textureflags & Q3TEXTUREFLAG_ALPHATEST)
- out->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_TRANSPARENT;
+ if (shader->layers[0].alphatest)
+ out->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
+ if (shader->textureflags & (Q3TEXTUREFLAG_TWOSIDED | Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+ out->basematerialflags |= MATERIALFLAG_NOSHADOW;
out->customblendfunc[0] = GL_ONE;
out->customblendfunc[1] = GL_ZERO;
if (shader->numlayers > 0)
if (shader->layers[0].blendfunc[0] != GL_ONE || shader->layers[0].blendfunc[1] != GL_ZERO)
{
if (shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ONE)
- out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
+ out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE)
- out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
+ out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
else if (shader->layers[0].blendfunc[0] == GL_SRC_ALPHA && shader->layers[0].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA)
- out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
+ out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
else
- out->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT;
+ out->basematerialflags |= MATERIALFLAG_CUSTOMBLEND | MATERIALFLAG_FULLBRIGHT | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
}
}
if (!shader->lighting)
out->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
- if (cls.state != ca_dedicated)
- if (shader->primarylayer && !Mod_LoadSkinFrame(&out->skin, shader->primarylayer->texturename, ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP), false, true))
- Con_Printf("%s: could not load texture \"%s\" for shader \"%s\"\n", loadmodel->name, shader->primarylayer->texturename, out->name);
+ if (shader->primarylayer && cls.state != ca_dedicated)
+ {
+ int j;
+ out->numskinframes = shader->primarylayer->numframes;
+ out->skinframerate = shader->primarylayer->framerate;
+ for (j = 0;j < shader->primarylayer->numframes;j++)
+ if (!Mod_LoadSkinFrame(&out->skinframes[j], shader->primarylayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->primarylayer->clampmap ? TEXF_CLAMP : 0), false, true))
+ Con_Printf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->primarylayer->texturename[j], j, out->name);
+ }
+ if (shader->backgroundlayer && cls.state != ca_dedicated)
+ {
+ int j;
+ out->backgroundnumskinframes = shader->backgroundlayer->numframes;
+ out->backgroundskinframerate = shader->backgroundlayer->framerate;
+ for (j = 0;j < shader->backgroundlayer->numframes;j++)
+ if (!Mod_LoadSkinFrame(&out->backgroundskinframes[j], shader->backgroundlayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->backgroundlayer->clampmap ? TEXF_CLAMP : 0), false, true))
+ Con_Printf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->backgroundlayer->texturename[j], j, out->name);
+ }
}
else
{
Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
out->surfaceparms = 0;
if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
- out->basematerialflags |= MATERIALFLAG_NODRAW;
+ out->basematerialflags |= MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
- out->basematerialflags |= MATERIALFLAG_SKY;
+ out->basematerialflags |= MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
else
out->basematerialflags |= MATERIALFLAG_WALL;
// these are defaults
//if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
// || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
// out->surfaceparms |= Q3SURFACEPARM_NODRAW;
- //if (R_TextureHasAlpha(out->skin.base))
+ //if (R_TextureHasAlpha(out->skinframes[0].base))
// out->surfaceparms |= Q3SURFACEPARM_TRANS;
if (cls.state != ca_dedicated)
- if (!Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
+ if (!Mod_LoadSkinFrame(&out->skinframes[0], out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
Con_Printf("%s: could not load texture for missing shader \"%s\"\n", loadmodel->name, out->name);
}
- // no animation
+ // init the animation variables
out->currentframe = out;
+ out->currentskinframe = &out->skinframes[0];
+ out->backgroundcurrentskinframe = &out->backgroundskinframes[0];
}
if (c)
Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
{
VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
planes[j].dist = out->firstbrushside[j].plane->dist;
- planes[j].supercontents = out->firstbrushside[j].texture->supercontents;
planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags;
planes[j].texture = out->firstbrushside[j].texture;
}
// make the colbrush from the planes
- out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes);
+ out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
}
if (planes)
Mem_Free(planes);
q3dlightmap_t *in;
rtexture_t **out;
int i, count;
+ unsigned char *c;
if (!l->filelen)
return;
loadmodel->brushq3.data_lightmaps = out;
loadmodel->brushq3.num_lightmaps = count;
+ // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
+ // always index even numbered ones (0, 2, 4, ...), the odd numbered
+ // lightmaps are the deluxemaps (light direction textures), so if we
+ // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
+ // is also not a deluxemapped bsp if it has an odd number of lightmaps or
+ // less than 2
+ loadmodel->brushq3.deluxemapping = true;
+ loadmodel->brushq3.deluxemapping_modelspace = true;
+ if (count < 2 || (count & 1))
+ loadmodel->brushq3.deluxemapping = false;
+
+ // q3map2 sometimes (or always?) makes a second blank lightmap for no
+ // reason when only one lightmap is used, which can throw off the
+ // deluxemapping detection method, so check 2-lightmap bsp's specifically
+ // to see if the second lightmap is blank, if so it is not deluxemapped.
+ if (count == 2)
+ {
+ c = in[count - 1].rgb;
+ for (i = 0;i < 128*128*3;i++)
+ if (c[i])
+ break;
+ if (i == 128*128*3)
+ {
+ // all pixels in the unused lightmap were black...
+ loadmodel->brushq3.deluxemapping = false;
+ }
+ }
+
+ // further deluxemapping detection is done in Mod_Q3BSP_LoadFaces
+
for (i = 0;i < count;i++, in++, out++)
*out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
}
loadmodel->data_surfaces = out;
loadmodel->num_surfaces = count;
- // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
- // always index even numbered ones (0, 2, 4, ...), the odd numbered
- // lightmaps are the deluxemaps (light direction textures), so if we
- // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
- // is also not a deluxemapped bsp if it has an odd number of lightmaps or
- // less than 2
- loadmodel->brushq3.deluxemapping = true;
- loadmodel->brushq3.deluxemapping_modelspace = true;
- if (loadmodel->brushq3.num_lightmaps >= 2 && !(loadmodel->brushq3.num_lightmaps & 1))
+ // now that we have surfaces to look at, see if any of them index an odd numbered lightmap, if so this is not a deluxemapped bsp file
+ if (loadmodel->brushq3.deluxemapping)
{
for (i = 0;i < count;i++)
{
}
}
}
- else
- loadmodel->brushq3.deluxemapping = false;
Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
i = 0;
// free the no longer needed vertex data
loadmodel->brushq3.num_vertices = 0;
- Mem_Free(loadmodel->brushq3.data_vertex3f);
+ if (loadmodel->brushq3.data_vertex3f)
+ Mem_Free(loadmodel->brushq3.data_vertex3f);
loadmodel->brushq3.data_vertex3f = NULL;
loadmodel->brushq3.data_normal3f = NULL;
loadmodel->brushq3.data_texcoordtexture2f = NULL;
loadmodel->brushq3.data_color4f = NULL;
// free the no longer needed triangle data
loadmodel->brushq3.num_triangles = 0;
- Mem_Free(loadmodel->brushq3.data_element3i);
+ if (loadmodel->brushq3.data_element3i)
+ Mem_Free(loadmodel->brushq3.data_element3i);
loadmodel->brushq3.data_element3i = NULL;
}
if (l->filelen)
{
if (l->filelen < count * (int)sizeof(*in))
- Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
+ Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, (int)(count * sizeof(*in)), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
if (l->filelen != count * (int)sizeof(*in))
- Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i", count * sizeof(*in), l->filelen);
+ Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", (int)(count * sizeof(*in)), l->filelen);
out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
loadmodel->brushq3.data_lightgrid = out;
loadmodel->brushq3.num_lightgrid = count;
Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
if (l->filelen < totalchains + (int)sizeof(*in))
- Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen);
+ Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, (int)(totalchains + sizeof(*in)), l->filelen);
loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
{
int i, j, k, index[3];
- float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
+ float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch, stylescale;
q3dlightgrid_t *a, *s;
+
+ // scale lighting by lightstyle[0] so that darkmode in dpmod works properly
+ stylescale = r_refdef.lightstylevalue[0] * (1.0f / 264.0f);
+
if (!model->brushq3.num_lightgrid)
{
- ambientcolor[0] = 1;
- ambientcolor[1] = 1;
- ambientcolor[2] = 1;
+ ambientcolor[0] = stylescale;
+ ambientcolor[1] = stylescale;
+ ambientcolor[2] = stylescale;
return;
}
+
Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
//Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
//Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
index[1] = (int)floor(transformed[1]);
index[2] = (int)floor(transformed[2]);
//Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
+
// now lerp the values
VectorClear(diffusenormal);
a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
continue;
for (i = 0;i < 2;i++)
{
- blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
+ blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0]))) * stylescale;
if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
continue;
s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
}
}
}
+
+ // normalize the light direction before turning
VectorNormalize(diffusenormal);
//Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
}
// the MakePortals code works fine on the q3bsp data as well
Mod_Q1BSP_MakePortals();
+ // FIXME: shader alpha should replace r_wateralpha support in q3bsp
+ loadmodel->brush.supportwateralpha = true;
+
// make a single combined shadow mesh to allow optimized shadow volume creation
numshadowmeshtriangles = 0;
for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
sprintf(name, "*%i", i);
mod = Mod_FindName(name);
*mod = *loadmodel;
- strcpy(mod->name, name);
+ strlcpy(mod->name, name, sizeof(mod->name));
// textures and memory belong to the main model
mod->texturepool = NULL;
mod->mempool = NULL;
break;
if (j < mod->nummodelsurfaces)
mod->DrawSky = R_Q1BSP_DrawSky;
+ else
+ mod->DrawSky = NULL;
}
}