X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=8f907bfb7198aa289bfe6f8289eae5ac325dbbc9;hb=80b09064d8227075b6e9c4dfe4b007c6676082f5;hp=a6825e0d33c760674a75ad9851e1682d6fd712ea;hpb=569f9c5b7a62696b0bd33e2a1af5ce47e37f83ff;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index a6825e0d..8f907bfb 100644 --- a/model_brush.c +++ b/model_brush.c @@ -614,6 +614,7 @@ RecursiveHullCheckTraceInfo_t; #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 @@ -768,6 +769,9 @@ loc0: 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 @@ -1118,7 +1122,8 @@ middle sample (the one which was requested) 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) @@ -1129,7 +1134,7 @@ static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char { 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++; @@ -1139,14 +1144,14 @@ static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char { 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; @@ -1256,10 +1261,13 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) // 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->basematerialflags = 0; if (i == loadmodel->num_textures - 1) { @@ -1323,7 +1331,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) 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; @@ -1352,8 +1360,8 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) } 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) @@ -1369,21 +1377,21 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) { 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; } } @@ -1424,7 +1432,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags; tx->basematerialflags |= MATERIALFLAG_WALL; } - if (tx->skin.fog) + if (tx->skinframes[0].fog) tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_TRANSPARENT; // start out with no animation @@ -1597,7 +1605,7 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l) 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); @@ -1639,23 +1647,23 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *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 @@ -1677,8 +1685,8 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data) { 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) @@ -2334,7 +2342,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) 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; } @@ -2503,12 +2511,12 @@ static void Mod_Q1BSP_LoadMapBrushes(void) 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 @@ -2519,7 +2527,7 @@ static void Mod_Q1BSP_LoadMapBrushes(void) 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 @@ -2543,7 +2551,7 @@ static void Mod_Q1BSP_LoadMapBrushes(void) } for (;;) { - if (!COM_ParseToken(&data, false)) + if (!COM_ParseTokenConsole(&data)) return; // error if (com_token[0] == '}') break; // end of brush @@ -2552,25 +2560,25 @@ static void Mod_Q1BSP_LoadMapBrushes(void) // 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); @@ -2832,6 +2840,7 @@ static void RemovePortalFromNodes(portal_t *portal) } } +#define PORTAL_DIST_EPSILON (1.0 / 32.0) static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) { int i, side; @@ -2857,6 +2866,7 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) 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 @@ -2878,7 +2888,7 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) 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; } @@ -2916,7 +2926,7 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) 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) { @@ -3347,7 +3357,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend) // 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; @@ -3908,23 +3918,23 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l) 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) @@ -3940,7 +3950,11 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l) 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; @@ -3975,7 +3989,7 @@ static void Mod_Q3BSP_LoadShaders(void) 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; @@ -4027,7 +4041,7 @@ static void Mod_Q3BSP_LoadShaders(void) 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; @@ -4092,19 +4106,26 @@ static void Mod_Q3BSP_LoadShaders(void) } } } - 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 = atoi(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")) @@ -4133,7 +4154,7 @@ static void Mod_Q3BSP_LoadShaders(void) 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; @@ -4158,18 +4179,26 @@ static void Mod_Q3BSP_LoadShaders(void) 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")) @@ -4245,14 +4274,14 @@ static void Mod_Q3BSP_LoadShaders(void) 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; } } @@ -4309,7 +4338,7 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", shader->skyboxname); } } - else if ((shader->surfaceparms & Q3SURFACEPARM_NODRAW) || shader->numlayers == 0) + else if ((out->surfaceflags & Q3SURFACEFLAG_NODRAW) || shader->numlayers == 0) out->basematerialflags |= MATERIALFLAG_NODRAW; else if (shader->surfaceparms & Q3SURFACEPARM_LAVA) out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT; @@ -4319,7 +4348,7 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA; else out->basematerialflags |= MATERIALFLAG_WALL; - if (shader->textureflags & Q3TEXTUREFLAG_ALPHATEST) + if (shader->layers[0].alphatest) out->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_TRANSPARENT; out->customblendfunc[0] = GL_ONE; out->customblendfunc[1] = GL_ZERO; @@ -4362,9 +4391,15 @@ Q3 shader blendfuncs actually used in the game (* = supported by DP) } 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); + } } else { @@ -4383,14 +4418,15 @@ Q3 shader blendfuncs actually used in the game (* = supported by DP) //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]; } if (c) Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c); @@ -4598,6 +4634,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l) q3dlightmap_t *in; rtexture_t **out; int i, count; + unsigned char *c; if (!l->filelen) return; @@ -4610,6 +4647,36 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l) 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); } @@ -4639,15 +4706,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) 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++) { @@ -4659,8 +4719,6 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) } } } - else - loadmodel->brushq3.deluxemapping = false; Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not "); i = 0; @@ -4970,7 +5028,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) // 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; @@ -4978,7 +5037,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) 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; } @@ -5189,9 +5249,9 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) 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; @@ -5232,7 +5292,7 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l) 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); @@ -5241,15 +5301,20 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l) 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]); @@ -5260,6 +5325,7 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc 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]]; @@ -5275,7 +5341,7 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc 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; @@ -5291,6 +5357,8 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc } } } + + // 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]); } @@ -5746,7 +5814,7 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) 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;