X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=2c1fb455df69d74733b020ea3df1fefb53fb3079;hb=fa06dd40f48b20d738b6bd604758c81defd76cfd;hp=bf59730316867b606e3986d92130d5fe7823cb28;hpb=e1509161ac281e3c4225bf5cc679f073b8eba7ed;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index bf597303..2c1fb455 100644 --- a/model_brush.c +++ b/model_brush.c @@ -26,28 +26,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "wad.h" +cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"}; //cvar_t r_subdivide_size = {CF_CLIENT | CF_ARCHIVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"}; -cvar_t mod_bsp_portalize = {CF_CLIENT | CF_SERVER, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_world_compileportalculling, sv_cullentities_portal"}; cvar_t r_novis = {CF_CLIENT, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"}; cvar_t r_nosurftextures = {CF_CLIENT, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"}; + cvar_t r_subdivisions_tolerance = {CF_CLIENT, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"}; cvar_t r_subdivisions_mintess = {CF_CLIENT, "r_subdivisions_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"}; cvar_t r_subdivisions_maxtess = {CF_CLIENT, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"}; cvar_t r_subdivisions_maxvertices = {CF_CLIENT, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"}; -cvar_t r_subdivisions_collision_tolerance = {CF_CLIENT, "r_subdivisions_collision_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"}; -cvar_t r_subdivisions_collision_mintess = {CF_CLIENT, "r_subdivisions_collision_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"}; -cvar_t r_subdivisions_collision_maxtess = {CF_CLIENT, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"}; -cvar_t r_subdivisions_collision_maxvertices = {CF_CLIENT, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"}; -cvar_t r_trippy = {CF_CLIENT, "r_trippy", "0", "easter egg"}; -cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"}; -cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"}; -cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"}; -cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "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_subdivisions_tolerance = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"}; +cvar_t mod_q3bsp_curves_subdivisions_mintess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_mintess", "0", "minimum number of subdivisions for collision purposes (values above 0 will smooth curves that don't need it)"}; +cvar_t mod_q3bsp_curves_subdivisions_maxtess = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxtess", "1024", "maximum number of subdivisions for collision purposes (prevents curves beyond a certain detail level, limits smoothing)"}; +cvar_t mod_q3bsp_curves_subdivisions_maxvertices = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_subdivisions_maxvertices", "4225", "maximum vertices allowed per subdivided curve for collision purposes"}; cvar_t mod_q3bsp_curves_collisions = {CF_CLIENT | CF_SERVER, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"}; -cvar_t mod_q3bsp_curves_collisions_stride = {CF_CLIENT | CF_SERVER, "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 = {CF_CLIENT | CF_SERVER, "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_t mod_q3bsp_optimizedtraceline = {CF_CLIENT | CF_SERVER, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"}; -cvar_t mod_q3bsp_debugtracebrush = {CF_CLIENT | CF_SERVER, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"}; cvar_t mod_q3bsp_lightmapmergepower = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."}; cvar_t mod_q3bsp_nolightmaps = {CF_CLIENT | CF_ARCHIVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"}; cvar_t mod_q3bsp_tracelineofsight_brushes = {CF_CLIENT | CF_SERVER, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"}; @@ -55,6 +48,7 @@ cvar_t mod_q3bsp_sRGBlightmaps = {CF_CLIENT, "mod_q3bsp_sRGBlightmaps", "0", "tr cvar_t mod_q3bsp_lightgrid_texture = {CF_CLIENT, "mod_q3bsp_lightgrid_texture", "1", "directly apply the lightgrid as a global texture rather than only reading it at the entity origin"}; cvar_t mod_q3bsp_lightgrid_world_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_world_surfaces", "0", "apply lightgrid lighting to the world bsp geometry rather than using lightmaps (experimental/debug tool)"}; cvar_t mod_q3bsp_lightgrid_bsp_surfaces = {CF_CLIENT, "mod_q3bsp_lightgrid_bsp_surfaces", "0", "apply lightgrid lighting to bsp models other than the world rather than using their lightmaps (experimental/debug tool)"}; +cvar_t mod_noshader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"}; cvar_t mod_q3shader_default_offsetmapping = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are using q3 shader files"}; cvar_t mod_q3shader_default_offsetmapping_scale = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_scale", "1", "default scale used for offsetmapping"}; cvar_t mod_q3shader_default_offsetmapping_bias = {CF_CLIENT | CF_ARCHIVE, "mod_q3shader_default_offsetmapping_bias", "0", "default bias used for offsetmapping"}; @@ -64,9 +58,16 @@ cvar_t mod_q3shader_default_refractive_index = {CF_CLIENT, "mod_q3shader_default cvar_t mod_q3shader_force_addalpha = {CF_CLIENT, "mod_q3shader_force_addalpha", "0", "treat GL_ONE GL_ONE (or add) blendfunc as GL_SRC_ALPHA GL_ONE for compatibility with older DarkPlaces releases"}; cvar_t mod_q3shader_force_terrain_alphaflag = {CF_CLIENT, "mod_q3shader_force_terrain_alphaflag", "0", "for multilayered terrain shaders force TEXF_ALPHA flag on both layers"}; +cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "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_q1bsp_polygoncollisions = {CF_CLIENT | CF_SERVER, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"}; +cvar_t mod_q1bsp_zero_hullsize_cutoff = {CF_CLIENT | CF_SERVER, "mod_q1bsp_zero_hullsize_cutoff", "3", "bboxes with an X dimension smaller than this will use the smallest cliphull (0x0x0) instead of being rounded up to the player cliphull (32x32x56) in Q1BSP, or crouching player (32x32x36) in HLBSP"}; + +cvar_t mod_bsp_portalize = {CF_CLIENT, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_dlight_portalculling, r_shadow_realtime_world_compileportalculling"}; cvar_t mod_recalculatenodeboxes = {CF_CLIENT | CF_SERVER, "mod_recalculatenodeboxes", "1", "enables use of generated node bounding boxes based on BSP tree portal reconstruction, rather than the node boxes supplied by the map compiler"}; +cvar_t mod_obj_orientation = {CF_CLIENT | CF_SERVER, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"}; + static texture_t mod_q1bsp_texture_solid; static texture_t mod_q1bsp_texture_sky; static texture_t mod_q1bsp_texture_lava; @@ -85,20 +86,16 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&r_subdivisions_mintess); Cvar_RegisterVariable(&r_subdivisions_maxtess); Cvar_RegisterVariable(&r_subdivisions_maxvertices); - Cvar_RegisterVariable(&r_subdivisions_collision_tolerance); - Cvar_RegisterVariable(&r_subdivisions_collision_mintess); - Cvar_RegisterVariable(&r_subdivisions_collision_maxtess); - Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_tolerance); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_mintess); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxtess); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivisions_maxvertices); Cvar_RegisterVariable(&r_trippy); - 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); Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline); - Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush); Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower); Cvar_RegisterVariable(&mod_q3bsp_nolightmaps); Cvar_RegisterVariable(&mod_q3bsp_sRGBlightmaps); @@ -115,6 +112,7 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&mod_q3shader_force_addalpha); Cvar_RegisterVariable(&mod_q3shader_force_terrain_alphaflag); Cvar_RegisterVariable(&mod_q1bsp_polygoncollisions); + Cvar_RegisterVariable(&mod_q1bsp_zero_hullsize_cutoff); Cvar_RegisterVariable(&mod_recalculatenodeboxes); // these games were made for older DP engines and are no longer @@ -123,27 +121,27 @@ void Mod_BrushInit(void) Cvar_SetQuick(&mod_q3shader_force_addalpha, "1"); memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid)); - strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name)); + dp_strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name)); mod_q1bsp_texture_solid.surfaceflags = 0; mod_q1bsp_texture_solid.supercontents = SUPERCONTENTS_SOLID; mod_q1bsp_texture_sky = mod_q1bsp_texture_solid; - strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name)); + dp_strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name)); mod_q1bsp_texture_sky.surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP; mod_q1bsp_texture_sky.supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP; mod_q1bsp_texture_lava = mod_q1bsp_texture_solid; - strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name)); + dp_strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name)); mod_q1bsp_texture_lava.surfaceflags = Q3SURFACEFLAG_NOMARKS; mod_q1bsp_texture_lava.supercontents = SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP; mod_q1bsp_texture_slime = mod_q1bsp_texture_solid; - strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name)); + dp_strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name)); mod_q1bsp_texture_slime.surfaceflags = Q3SURFACEFLAG_NOMARKS; mod_q1bsp_texture_slime.supercontents = SUPERCONTENTS_SLIME; mod_q1bsp_texture_water = mod_q1bsp_texture_solid; - strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name)); + dp_strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name)); mod_q1bsp_texture_water.surfaceflags = Q3SURFACEFLAG_NOMARKS; mod_q1bsp_texture_water.supercontents = SUPERCONTENTS_WATER; } @@ -471,7 +469,7 @@ typedef struct findnonsolidlocationinfo_s } findnonsolidlocationinfo_t; -static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k) +static void Mod_BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k) { int i, *tri; float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3]; @@ -559,7 +557,7 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t } } -static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf) +static void Mod_BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf) { int surfacenum, k, *mark; msurface_t *surface; @@ -572,26 +570,26 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in { for (k = 0;k < surface->num_triangles;k++) { - Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, k); + Mod_BSP_FindNonSolidLocation_r_Triangle(info, surface, k); } } } } -static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node) +static void Mod_BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node) { if (node->plane) { float f = PlaneDiff(info->center, node->plane); if (f >= -info->bestdist) - Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]); + Mod_BSP_FindNonSolidLocation_r(info, node->children[0]); if (f <= info->bestdist) - Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]); + Mod_BSP_FindNonSolidLocation_r(info, node->children[1]); } else { if (((mleaf_t *)node)->numleafsurfaces) - Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node); + Mod_BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node); } } @@ -620,7 +618,7 @@ static void Mod_BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t info.absmax[0] += info.radius + 1; info.absmax[1] += info.radius + 1; info.absmax[2] += info.radius + 1; - Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode); + Mod_BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode); VectorAdd(info.center, info.nudge, info.center); } while (info.bestdist < radius && ++i < 10); @@ -762,7 +760,7 @@ static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int nu // recurse both sides, front side first ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p1side], p1f, midf, p1, mid); // if this side is not empty, return what it is (solid or done) - if (ret != HULLCHECKSTATE_EMPTY) + if (ret != HULLCHECKSTATE_EMPTY && !t->trace->allsolid) return ret; ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p2side], midf, p2f, mid, p2); @@ -976,7 +974,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb rhc.trace->fraction = 1; rhc.trace->allsolid = true; VectorSubtract(boxmaxs, boxmins, boxsize); - if (boxsize[0] < 3) + if (boxsize[0] < mod_q1bsp_zero_hullsize_cutoff.value) rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 else if (model->brush.ishlbsp) { @@ -1005,7 +1003,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb VectorMAMAM(1, end, 1, boxmins, -1, rhc.hull->clip_mins, rhc.end); VectorSubtract(rhc.end, rhc.start, rhc.dist); #if COLLISIONPARANOID >= 2 - Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]); + Con_Printf("t(%f %f %f,%f %f %f,%li %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]); Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); { @@ -1712,7 +1710,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) skinframemissing = R_SkinFrame_LoadMissing(); for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++) { - strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name)); + dp_strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name)); tx->width = 16; tx->height = 16; tx->basealpha = 1.0f; @@ -1830,7 +1828,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) // no luck with loading shaders or external textures - restore the in-progress texture loading loadmodel->data_textures[i] = backuptex; - strlcpy(tx->name, name, sizeof(tx->name)); + dp_strlcpy(tx->name, name, sizeof(tx->name)); tx->width = mtwidth; tx->height = mtheight; tx->basealpha = 1.0f; @@ -2107,11 +2105,11 @@ static void Mod_Q1BSP_LoadLighting(sizebuf_t *sb) else // LadyHavoc: bsp version 29 (normal white lighting) { // LadyHavoc: hope is not lost yet, check for a .lit file to load - strlcpy (litfilename, loadmodel->name, sizeof (litfilename)); + dp_strlcpy (litfilename, loadmodel->name, sizeof (litfilename)); FS_StripExtension (litfilename, litfilename, sizeof (litfilename)); - strlcpy (dlitfilename, litfilename, sizeof (dlitfilename)); - strlcat (litfilename, ".lit", sizeof (litfilename)); - strlcat (dlitfilename, ".dlit", sizeof (dlitfilename)); + dp_strlcpy (dlitfilename, litfilename, sizeof (dlitfilename)); + dp_strlcat (litfilename, ".lit", sizeof (litfilename)); + dp_strlcat (dlitfilename, ".dlit", sizeof (dlitfilename)); data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize); if (data) { @@ -2204,9 +2202,9 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data) if (com_token[0] == '}') break; // end of worldspawn if (com_token[0] == '_') - strlcpy(key, com_token + 1, sizeof(key)); + dp_strlcpy(key, com_token + 1, sizeof(key)); else - strlcpy(key, com_token, sizeof(key)); + dp_strlcpy(key, com_token, sizeof(key)); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; if (!COM_ParseToken_Simple(&data, false, false, true)) @@ -3375,15 +3373,15 @@ portal_t; static memexpandablearray_t portalarray; -static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node) +static void Mod_BSP_RecursiveRecalcNodeBBox(mnode_t *node) { // process only nodes (leafs already had their box calculated) if (!node->plane) return; // calculate children first - Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]); - Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]); + Mod_BSP_RecursiveRecalcNodeBBox(node->children[0]); + Mod_BSP_RecursiveRecalcNodeBBox(node->children[1]); // make combined bounding box from children node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]); @@ -3517,7 +3515,7 @@ static void Mod_BSP_FinalizePortals(void) } // now recalculate the node bounding boxes from the leafs if (mod_recalculatenodeboxes.integer) - Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode); + Mod_BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode); } /* @@ -3826,11 +3824,11 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co const hull_t *hull; VectorSubtract(inmaxs, inmins, size); - if (cmodel->brush.ishlbsp) + if (size[0] < mod_q1bsp_zero_hullsize_cutoff.value) + hull = &cmodel->brushq1.hulls[0]; // 0x0x0 + else if (cmodel->brush.ishlbsp) { - if (size[0] < 3) - hull = &cmodel->brushq1.hulls[0]; // 0x0x0 - else if (size[0] <= 32) + if (size[0] <= 32) { if (size[2] < 54) // pick the nearest of 36 or 72 hull = &cmodel->brushq1.hulls[3]; // 32x32x36 @@ -3842,9 +3840,7 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co } else { - if (size[0] < 3) - hull = &cmodel->brushq1.hulls[0]; // 0x0x0 - else if (size[0] <= 32) + if (size[0] <= 32) hull = &cmodel->brushq1.hulls[1]; // 32x32x56 else hull = &cmodel->brushq1.hulls[2]; // 64x64x88 @@ -4008,7 +4004,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->brushq1.num_compressedpvs = 0; Mod_Q1BSP_MakeHull0(); - if (mod_bsp_portalize.integer) + if (mod_bsp_portalize.integer && cls.state != ca_dedicated) Mod_BSP_MakePortals(); mod->numframes = 2; // regular and alternate animation @@ -4065,7 +4061,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 - strlcpy(mod->name, name, sizeof(mod->name)); + dp_strlcpy(mod->name, name, sizeof(mod->name)); mod->brush.parentmodel = loadmodel; // textures and memory belong to the main model mod->texturepool = NULL; @@ -4123,26 +4119,10 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->radius = modelradius; mod->radius2 = modelradius * modelradius; - // this gets altered below if sky or water is used - mod->DrawSky = NULL; - mod->DrawAddWaterPlanes = NULL; + Mod_SetDrawSkyAndWater(mod); - // scan surfaces for sky and water and flag the submodel as possessing these features or not - // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker if (mod->submodelsurfaces_start < mod->submodelsurfaces_end) { - for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++) - if (mod->data_surfaces[j].texture->basematerialflags & MATERIALFLAG_SKY) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawSky = R_Mod_DrawSky; - - for (j = mod->submodelsurfaces_start;j < mod->submodelsurfaces_end;j++) - if (mod->data_surfaces[j].texture && mod->data_surfaces[j].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes; - // build lightstyle update chains // (used to rapidly mark lightmapupdateflags on many surfaces // when d_lightstylevalue changes) @@ -4955,7 +4935,7 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->brushq1.num_compressedpvs = 0; // the MakePortals code works fine on the q2bsp data as well - if (mod_bsp_portalize.integer) + if (mod_bsp_portalize.integer && cls.state != ca_dedicated) Mod_BSP_MakePortals(); mod->numframes = 0; // q2bsp animations are kind of special, frame is unbounded... @@ -5000,7 +4980,7 @@ static void Mod_Q2BSP_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 - strlcpy(mod->name, name, sizeof(mod->name)); + dp_strlcpy(mod->name, name, sizeof(mod->name)); mod->brush.parentmodel = loadmodel; // textures and memory belong to the main model mod->texturepool = NULL; @@ -5066,26 +5046,11 @@ static void Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->radius = modelradius; mod->radius2 = modelradius * modelradius; - // this gets altered below if sky or water is used - mod->DrawSky = NULL; - mod->DrawAddWaterPlanes = NULL; + Mod_SetDrawSkyAndWater(mod); - // scan surfaces for sky and water and flag the submodel as possessing these features or not // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker if (mod->submodelsurfaces_start < mod->submodelsurfaces_end) { - for (j = mod->submodelsurfaces_start;j < mod->submodelsurfaces_end;j++) - if (mod->data_surfaces[j].texture && (mod->data_surfaces[j].texture->basematerialflags & MATERIALFLAG_SKY)) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawSky = R_Mod_DrawSky; - - for (j = mod->submodelsurfaces_start;j < mod->submodelsurfaces_end;j++) - if (mod->data_surfaces[j].texture && (mod->data_surfaces[j].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes; - // build lightstyle update chains // (used to rapidly mark lightmapupdateflags on many surfaces // when d_lightstylevalue changes) @@ -5174,14 +5139,14 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l) if (com_token[0] == '}') break; // end of worldspawn if (com_token[0] == '_') - strlcpy(key, com_token + 1, sizeof(key)); + dp_strlcpy(key, com_token + 1, sizeof(key)); else - strlcpy(key, com_token, sizeof(key)); + dp_strlcpy(key, com_token, sizeof(key)); while (key[strlen(key)-1] == ' ') // remove trailing spaces key[strlen(key)-1] = 0; if (!COM_ParseToken_Simple(&data, false, false, true)) break; // error - strlcpy(value, com_token, sizeof(value)); + dp_strlcpy(value, com_token, sizeof(value)); if (!strcasecmp("gridsize", key)) // this one is case insensitive to 100% match q3map2 { #if _MSC_VER >= 1400 @@ -5399,7 +5364,7 @@ static void Mod_Q3BSP_LoadEffects(lump_t *l) for (i = 0;i < count;i++, in++, out++) { - strlcpy (out->shadername, in->shadername, sizeof (out->shadername)); + dp_strlcpy (out->shadername, in->shadername, sizeof (out->shadername)); n = LittleLong(in->brushindex); if (n >= loadmodel->brush.num_brushes) { @@ -5924,7 +5889,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) case Q3FACETYPE_PATCH: patchsize[0] = LittleLong(in->specific.patch.patchsize[0]); patchsize[1] = LittleLong(in->specific.patch.patchsize[1]); - if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= min(r_subdivisions_maxvertices.integer, r_subdivisions_collision_maxvertices.integer)) + if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= (cls.state == ca_dedicated ? mod_q3bsp_curves_subdivisions_maxvertices.integer : min(r_subdivisions_maxvertices.integer, mod_q3bsp_curves_subdivisions_maxvertices.integer))) { Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]); continue; @@ -5943,11 +5908,11 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) // lower quality collision patches! Same procedure as before, but different cvars // convert patch to Q3FACETYPE_MESH - cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value); - cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value); + cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value); + cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, mod_q3bsp_curves_subdivisions_tolerance.value); // bound to user settings - cxtess = bound(r_subdivisions_collision_mintess.integer, cxtess, r_subdivisions_collision_maxtess.integer); - cytess = bound(r_subdivisions_collision_mintess.integer, cytess, r_subdivisions_collision_maxtess.integer); + cxtess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cxtess, mod_q3bsp_curves_subdivisions_maxtess.integer); + cytess = bound(mod_q3bsp_curves_subdivisions_mintess.integer, cytess, mod_q3bsp_curves_subdivisions_maxtess.integer); // bound to sanity settings cxtess = bound(0, cxtess, 1024); cytess = bound(0, cytess, 1024); @@ -6594,7 +6559,7 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) lightgridmatrix[1][3] = -(loadmodel->brushq3.num_lightgrid_imins[1] - 0.5f) / texturesize[1]; lightgridmatrix[2][3] = -(loadmodel->brushq3.num_lightgrid_imins[2] - 1.5f) / texturesize[2]; lightgridmatrix[3][3] = 1; - Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix[0]); + Matrix4x4_FromArrayDoubleD3D(&loadmodel->brushq3.lightgridworldtotexturematrix, lightgridmatrix); loadmodel->brushq3.lightgridtexture = R_LoadTexture3D(loadmodel->texturepool, "lightgrid", texturesize[0], texturesize[1], texturesize[2], texturergba, TEXTYPE_RGBA, TEXF_CLAMP, 0, NULL); Mem_Free(texturergba); } @@ -7551,7 +7516,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models; // the MakePortals code works fine on the q3bsp data as well - if (mod_bsp_portalize.integer) + if (mod_bsp_portalize.integer && cls.state != ca_dedicated) Mod_BSP_MakePortals(); // FIXME: shader alpha should replace r_wateralpha support in q3bsp @@ -7564,6 +7529,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) loadmodel->brush.submodels = (model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *)); mod = loadmodel; + mod->modelsurfaces_sorted = (int*)Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted)); for (i = 0;i < loadmodel->brush.numsubmodels;i++) { if (i > 0) @@ -7575,7 +7541,7 @@ static void Mod_Q3BSP_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 - strlcpy(mod->name, name, sizeof(mod->name)); + dp_strlcpy(mod->name, name, sizeof(mod->name)); mod->brush.parentmodel = loadmodel; // textures and memory belong to the main model mod->texturepool = NULL; @@ -7598,7 +7564,6 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->submodelsurfaces_end = mod->brushq3.data_models[i].firstface + mod->brushq3.data_models[i].numfaces; mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush; mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes; - mod->modelsurfaces_sorted = (int *)Mem_Alloc(loadmodel->mempool, mod->num_surfaces * sizeof(*mod->modelsurfaces_sorted)); VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins); VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs); @@ -7610,7 +7575,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) //printf("Editing model %d... BEFORE re-bounding: %f %f %f - %f %f %f\n", i, mod->normalmins[0], mod->normalmins[1], mod->normalmins[2], mod->normalmaxs[0], mod->normalmaxs[1], mod->normalmaxs[2]); for (j = mod->submodelsurfaces_start;j < mod->submodelsurfaces_end;j++) { - const msurface_t *surface = mod->data_surfaces + j + mod->submodelsurfaces_start; + const msurface_t *surface = mod->data_surfaces + j; const float *v = mod->surfmesh.data_vertex3f + 3 * surface->num_firstvertex; int k; if (!surface->num_vertices) @@ -7640,22 +7605,7 @@ static void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend) mod->radius = modelradius; mod->radius2 = modelradius * modelradius; - // this gets altered below if sky or water is used - mod->DrawSky = NULL; - mod->DrawAddWaterPlanes = NULL; - - for (j = mod->submodelsurfaces_start;j < mod->submodelsurfaces_end;j++) - if (mod->data_surfaces[j].texture && mod->data_surfaces[j].texture->basematerialflags & MATERIALFLAG_SKY) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawSky = R_Mod_DrawSky; - - for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++) - if (mod->data_surfaces[j].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes; - + Mod_SetDrawSkyAndWater(mod); Mod_MakeCollisionBIH(mod, false, &mod->collision_bih); Mod_MakeCollisionBIH(mod, true, &mod->render_bih); @@ -7706,6 +7656,518 @@ void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend) Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i); } +static void Mod_VBSP_LoadEntities(sizebuf_t *sb) +{ + loadmodel->brush.entities = NULL; + if (!sb->cursize) + return; + loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, sb->cursize + 1); + MSG_ReadBytes(sb, sb->cursize, (unsigned char *)loadmodel->brush.entities); + loadmodel->brush.entities[sb->cursize] = 0; +} + +static void Mod_VBSP_LoadVertexes(sizebuf_t *sb) +{ + mvertex_t *out; + int i, count; + int structsize = 12; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadVertexes: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (mvertex_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + + loadmodel->brushq1.vertexes = out; + loadmodel->brushq1.numvertexes = count; + + for ( i=0 ; iposition[0] = MSG_ReadLittleFloat(sb); + out->position[1] = MSG_ReadLittleFloat(sb); + out->position[2] = MSG_ReadLittleFloat(sb); + } +} + +static void Mod_VBSP_LoadEdges(sizebuf_t *sb) +{ + medge_t *out; + int i, count; + int structsize = 4; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadEdges: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (medge_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel->brushq1.edges = out; + loadmodel->brushq1.numedges = count; + + for ( i=0 ; iv[0] = (unsigned short)MSG_ReadLittleShort(sb); + out->v[1] = (unsigned short)MSG_ReadLittleShort(sb); + + if ((int)out->v[0] >= loadmodel->brushq1.numvertexes || (int)out->v[1] >= loadmodel->brushq1.numvertexes) + { + Con_Printf("Mod_VBSP_LoadEdges: %s has invalid vertex indices in edge %i (vertices %i %i >= numvertices %i)\n", loadmodel->name, i, out->v[0], out->v[1], loadmodel->brushq1.numvertexes); + if(!loadmodel->brushq1.numvertexes) + Host_Error("Mod_VBSP_LoadEdges: %s has edges but no vertexes, cannot fix\n", loadmodel->name); + + out->v[0] = 0; + out->v[1] = 0; + } + } +} + +static void Mod_VBSP_LoadSurfedges(sizebuf_t *sb) +{ + int i; + int structsize = 4; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadSurfedges: funny lump size in %s",loadmodel->name); + loadmodel->brushq1.numsurfedges = sb->cursize / structsize; + loadmodel->brushq1.surfedges = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int)); + + for (i = 0;i < loadmodel->brushq1.numsurfedges;i++) + loadmodel->brushq1.surfedges[i] = MSG_ReadLittleLong(sb); +} + +static void Mod_VBSP_LoadTextures(sizebuf_t *sb) +{ + Con_Printf(CON_WARN "Mod_VBSP_LoadTextures: Don't know how to do this yet\n"); +} + +static void Mod_VBSP_LoadPlanes(sizebuf_t *sb) +{ + int i; + mplane_t *out; + int structsize = 20; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadPlanes: funny lump size in %s", loadmodel->name); + loadmodel->brush.num_planes = sb->cursize / structsize; + loadmodel->brush.data_planes = out = (mplane_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out)); + + for (i = 0;i < loadmodel->brush.num_planes;i++, out++) + { + out->normal[0] = MSG_ReadLittleFloat(sb); + out->normal[1] = MSG_ReadLittleFloat(sb); + out->normal[2] = MSG_ReadLittleFloat(sb); + out->dist = MSG_ReadLittleFloat(sb); + MSG_ReadLittleLong(sb); // type is not used, we use PlaneClassify + PlaneClassify(out); + } +} + +static void Mod_VBSP_LoadTexinfo(sizebuf_t *sb) +{ + mtexinfo_t *out; + int i, j, k, count, miptex; + int structsize = 72; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadTexinfo: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel->brushq1.texinfo = out; + loadmodel->brushq1.numtexinfo = count; + + for (i = 0;i < count;i++, out++) + { + for (k = 0;k < 2;k++) + for (j = 0;j < 4;j++) + out->vecs[k][j] = MSG_ReadLittleFloat(sb); + + for (k = 0;k < 2;k++) + for (j = 0;j < 4;j++) + MSG_ReadLittleFloat(sb); // TODO lightmapVecs + + out->q1flags = MSG_ReadLittleLong(sb); + miptex = MSG_ReadLittleLong(sb); + + if (out->q1flags & TEX_SPECIAL) + { + // if texture chosen is NULL or the shader needs a lightmap, + // force to notexture water shader + out->textureindex = loadmodel->num_textures - 1; + } + else + { + // if texture chosen is NULL, force to notexture + out->textureindex = loadmodel->num_textures - 2; + } + // see if the specified miptex is valid and try to use it instead + if (loadmodel->data_textures) + { + if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures) + Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures); + else + out->textureindex = miptex; + } + } +} + +static void Mod_VBSP_LoadFaces(sizebuf_t *sb) +{ + msurface_t *surface; + int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber, lightmapsize, totallightmapsamples, lightmapoffset, texinfoindex; + float texmins[2], texmaxs[2], val; + rtexture_t *lightmaptexture, *deluxemaptexture; + char vabuf[1024]; + int structsize = 56; + + if (sb->cursize % structsize) + Host_Error("Mod_VBSP_LoadFaces: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t)); + loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t)); + + loadmodel->num_surfaces = count; + + loadmodel->brushq1.firstrender = true; + loadmodel->brushq1.lightmapupdateflags = (unsigned char *)Mem_Alloc(loadmodel->mempool, count*sizeof(unsigned char)); + + totalverts = 0; + totaltris = 0; + for (surfacenum = 0;surfacenum < count;surfacenum++) + { + numedges = BuffLittleShort(sb->data + structsize * surfacenum + 8); + totalverts += numedges; + totaltris += numedges - 2; + } + + Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false); + + lightmaptexture = NULL; + deluxemaptexture = r_texture_blanknormalmap; + lightmapnumber = 0; + lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d); + totallightmapsamples = 0; + + totalverts = 0; + totaltris = 0; + for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++) + { + surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum; + planenum = (unsigned short)MSG_ReadLittleShort(sb); + /*side = */MSG_ReadLittleShort(sb); // TODO support onNode? + firstedge = MSG_ReadLittleLong(sb); + numedges = (unsigned short)MSG_ReadLittleShort(sb); + texinfoindex = (unsigned short)MSG_ReadLittleShort(sb); + MSG_ReadLittleLong(sb); // skipping over dispinfo and surfaceFogVolumeID, both short + for (i = 0;i < MAXLIGHTMAPS;i++) + surface->lightmapinfo->styles[i] = MSG_ReadByte(sb); + lightmapoffset = MSG_ReadLittleLong(sb); + + // FIXME: validate edges, texinfo, etc? + if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges) + Host_Error("Mod_VBSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges); + if ((unsigned int) texinfoindex >= (unsigned int) loadmodel->brushq1.numtexinfo) + Host_Error("Mod_VBSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", texinfoindex, loadmodel->brushq1.numtexinfo); + if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes) + Host_Error("Mod_VBSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes); + + surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + texinfoindex; + surface->texture = loadmodel->data_textures + surface->lightmapinfo->texinfo->textureindex; + + // Q2BSP doesn't use lightmaps on sky or warped surfaces (water), but still has a lightofs of 0 + if (lightmapoffset == 0 && (surface->texture->q2flags & (Q2SURF_SKY | Q2SURF_WARP))) + lightmapoffset = -1; + + //surface->flags = surface->texture->flags; + //if (LittleShort(in->side)) + // surface->flags |= SURF_PLANEBACK; + //surface->plane = loadmodel->brush.data_planes + planenum; + + surface->num_firstvertex = totalverts; + surface->num_vertices = numedges; + surface->num_firsttriangle = totaltris; + surface->num_triangles = numedges - 2; + totalverts += numedges; + totaltris += numedges - 2; + + // convert edges back to a normal polygon + for (i = 0;i < surface->num_vertices;i++) + { + int lindex = loadmodel->brushq1.surfedges[firstedge + i]; + float s, t; + // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart) + if (lindex >= 0) + VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3); + else + VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3); + s = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]; + t = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]; + (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width; + (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height; + (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0; + (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0; + (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = 0; + } + + for (i = 0;i < surface->num_triangles;i++) + { + (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex; + (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex; + (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex; + } + + // compile additional data about the surface geometry + Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0); + Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0); + BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)); + + // generate surface extents information + texmins[0] = texmaxs[0] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]; + texmins[1] = texmaxs[1] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]; + for (i = 1;i < surface->num_vertices;i++) + { + for (j = 0;j < 2;j++) + { + val = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3]; + texmins[j] = min(texmins[j], val); + texmaxs[j] = max(texmaxs[j], val); + } + } + for (i = 0;i < 2;i++) + { + surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16; + surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i]; + } + + smax = surface->lightmapinfo->extents[0] >> 4; + tmax = surface->lightmapinfo->extents[1] >> 4; + ssize = (surface->lightmapinfo->extents[0] >> 4) + 1; + tsize = (surface->lightmapinfo->extents[1] >> 4) + 1; + + // lighting info + surface->lightmaptexture = NULL; + surface->deluxemaptexture = r_texture_blanknormalmap; + if (lightmapoffset == -1) + { + surface->lightmapinfo->samples = NULL; +#if 1 + // give non-lightmapped water a 1x white lightmap + if (!loadmodel->brush.isq2bsp && surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256) + { + surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3); + surface->lightmapinfo->styles[0] = 0; + memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3); + } +#endif + } + else + surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + lightmapoffset; + + // check if we should apply a lightmap to this + if (!(surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) || surface->lightmapinfo->samples) + { + if (ssize > 256 || tsize > 256) + Host_Error("Bad surface extents"); + + if (lightmapsize < ssize) + lightmapsize = ssize; + if (lightmapsize < tsize) + lightmapsize = tsize; + + totallightmapsamples += ssize*tsize; + + // force lightmap upload on first time seeing the surface + // + // additionally this is used by the later code to see if a + // lightmap is needed on this surface (rather than duplicating the + // logic above) + loadmodel->brushq1.lightmapupdateflags[surfacenum] = true; + loadmodel->lit = true; + } + } + + // small maps (such as ammo boxes especially) don't need big lightmap + // textures, so this code tries to guess a good size based on + // totallightmapsamples (size of the lightmaps lump basically), as well as + // trying to max out the size if there is a lot of lightmap data to store + // additionally, never choose a lightmapsize that is smaller than the + // largest surface encountered (as it would fail) + i = lightmapsize; + for (lightmapsize = 64; (lightmapsize < i) && (lightmapsize < bound(128, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d)) && (totallightmapsamples > lightmapsize*lightmapsize); lightmapsize*=2) + ; + + // now that we've decided the lightmap texture size, we can do the rest + if (cls.state != ca_dedicated) + { + int stainmapsize = 0; + mod_alloclightmap_state_t allocState; + + Mod_AllocLightmap_Init(&allocState, loadmodel->mempool, lightmapsize, lightmapsize); + for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++) + { + int iu, iv, lightmapx = 0, lightmapy = 0; + float u, v, ubase, vbase, uscale, vscale; + + if (!loadmodel->brushq1.lightmapupdateflags[surfacenum]) + continue; + + smax = surface->lightmapinfo->extents[0] >> 4; + tmax = surface->lightmapinfo->extents[1] >> 4; + ssize = (surface->lightmapinfo->extents[0] >> 4) + 1; + tsize = (surface->lightmapinfo->extents[1] >> 4) + 1; + stainmapsize += ssize * tsize * 3; + + if (!lightmaptexture || !Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy)) + { + // allocate a texture pool if we need it + if (loadmodel->texturepool == NULL) + loadmodel->texturepool = R_AllocTexturePool(); + // could not find room, make a new lightmap + loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1; + loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0])); + loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0])); + loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL); + if (loadmodel->brushq1.nmaplightdata) + loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL); + lightmapnumber++; + Mod_AllocLightmap_Reset(&allocState); + Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy); + } + surface->lightmaptexture = lightmaptexture; + surface->deluxemaptexture = deluxemaptexture; + surface->lightmapinfo->lightmaporigin[0] = lightmapx; + surface->lightmapinfo->lightmaporigin[1] = lightmapy; + + uscale = 1.0f / (float)lightmapsize; + vscale = 1.0f / (float)lightmapsize; + ubase = lightmapx * uscale; + vbase = lightmapy * vscale; + + for (i = 0;i < surface->num_vertices;i++) + { + u = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0); + v = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0); + (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase; + (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase; + // LadyHavoc: calc lightmap data offset for vertex lighting to use + iu = (int) u; + iv = (int) v; + (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3; + } + } + + if (cl_stainmaps.integer) + { + // allocate stainmaps for permanent marks on walls and clear white + unsigned char *stainsamples = NULL; + stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize); + memset(stainsamples, 255, stainmapsize); + // assign pointers + for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++) + { + if (!loadmodel->brushq1.lightmapupdateflags[surfacenum]) + continue; + ssize = (surface->lightmapinfo->extents[0] >> 4) + 1; + tsize = (surface->lightmapinfo->extents[1] >> 4) + 1; + surface->lightmapinfo->stainsamples = stainsamples; + stainsamples += ssize * tsize * 3; + } + } + } + + // generate ushort elements array if possible + if (loadmodel->surfmesh.data_element3s) + for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++) + loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i]; +} + +// Valve BSP loader +// Cloudwalk: Wasn't sober when I wrote this. I screamed and ran away at the face loader +void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend) +{ + static cvar_t *testing = NULL; // TEMPORARY + int i; + sizebuf_t sb; + sizebuf_t lumpsb[HL2HEADER_LUMPS]; + + if(!testing || !testing->integer) + { + if(!testing) + testing = Cvar_Get(&cvars_all, "mod_bsp_vbsptest", "0", CF_CLIENT | CF_SERVER, "uhhh"); + Host_Error("Mod_VBSP_Load: not yet fully implemented. Change the now-generated \"mod_bsp_vbsptest\" to 1 if you wish to test this"); + } + else + { + + MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer); + + mod->type = mod_brushhl2; + + MSG_ReadLittleLong(&sb); + MSG_ReadLittleLong(&sb); // TODO version check + + mod->modeldatatypestring = "VBSP"; + + // read lumps + for (i = 0; i < HL2HEADER_LUMPS; i++) + { + int offset = MSG_ReadLittleLong(&sb); + int size = MSG_ReadLittleLong(&sb); + MSG_ReadLittleLong(&sb); // TODO support version + MSG_ReadLittleLong(&sb); // TODO support ident + if (offset < 0 || offset + size > sb.cursize) + Host_Error("Mod_VBSP_Load: %s has invalid lump %i (offset %i, size %i, file size %i)\n", mod->name, i, offset, size, (int)sb.cursize); + MSG_InitReadBuffer(&lumpsb[i], sb.data + offset, size); + } + + MSG_ReadLittleLong(&sb); // TODO support revision field + + mod->soundfromcenter = true; + mod->TraceBox = Mod_CollisionBIH_TraceBox; + mod->TraceBrush = Mod_CollisionBIH_TraceBrush; + mod->TraceLine = Mod_CollisionBIH_TraceLine; + mod->TracePoint = Mod_CollisionBIH_TracePoint; + mod->PointSuperContents = Mod_CollisionBIH_PointSuperContents; + mod->TraceLineAgainstSurfaces = Mod_CollisionBIH_TraceLine; + mod->brush.TraceLineOfSight = Mod_Q3BSP_TraceLineOfSight; // probably not correct + mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents; // probably not correct + mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents; // probably not correct + mod->brush.GetPVS = Mod_BSP_GetPVS; + mod->brush.FatPVS = Mod_BSP_FatPVS; + mod->brush.BoxTouchingPVS = Mod_BSP_BoxTouchingPVS; + mod->brush.BoxTouchingLeafPVS = Mod_BSP_BoxTouchingLeafPVS; + mod->brush.BoxTouchingVisibleLeafs = Mod_BSP_BoxTouchingVisibleLeafs; + mod->brush.FindBoxClusters = Mod_BSP_FindBoxClusters; + mod->brush.LightPoint = Mod_Q3BSP_LightPoint; // probably not correct + mod->brush.FindNonSolidLocation = Mod_BSP_FindNonSolidLocation; + mod->brush.AmbientSoundLevelsForPoint = NULL; + mod->brush.RoundUpToHullSize = NULL; + mod->brush.PointInLeaf = Mod_BSP_PointInLeaf; + mod->Draw = R_Mod_Draw; + mod->DrawDepth = R_Mod_DrawDepth; + mod->DrawDebug = R_Mod_DrawDebug; + mod->DrawPrepass = R_Mod_DrawPrepass; + mod->GetLightInfo = R_Mod_GetLightInfo; + mod->CompileShadowMap = R_Mod_CompileShadowMap; + mod->DrawShadowMap = R_Mod_DrawShadowMap; + mod->DrawLight = R_Mod_DrawLight; + + // allocate a texture pool if we need it + if (mod->texturepool == NULL) + mod->texturepool = R_AllocTexturePool(); + + Mod_VBSP_LoadEntities(&lumpsb[HL2LUMP_ENTITIES]); + Mod_VBSP_LoadVertexes(&lumpsb[HL2LUMP_VERTEXES]); + Mod_VBSP_LoadEdges(&lumpsb[HL2LUMP_EDGES]); + Mod_VBSP_LoadSurfedges(&lumpsb[HL2LUMP_SURFEDGES]); + Mod_VBSP_LoadTextures(&lumpsb[HL2LUMP_TEXDATA/*?*/]); + //Mod_VBSP_LoadLighting(&lumpsb[HL2LUMP_LIGHTING]); + Mod_VBSP_LoadPlanes(&lumpsb[HL2LUMP_PLANES]); + Mod_VBSP_LoadTexinfo(&lumpsb[HL2LUMP_TEXINFO]); + + // AHHHHHHH + Mod_VBSP_LoadFaces(&lumpsb[HL2LUMP_FACES]); + } +} + void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend) { Host_Error("Mod_MAP_Load: not yet implemented"); @@ -7924,7 +8386,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend) texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH); } textureindex = numtextures++; - strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH); + dp_strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH); } for (j = 1;j < argc;j++) { @@ -8017,7 +8479,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend) texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH); } textureindex = numtextures++; - strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH); + dp_strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH); } } } @@ -8227,7 +8689,7 @@ void Mod_OBJ_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 - strlcpy(mod->name, name, sizeof(mod->name)); + dp_strlcpy(mod->name, name, sizeof(mod->name)); mod->brush.parentmodel = loadmodel; // textures and memory belong to the main model mod->texturepool = NULL; @@ -8291,21 +8753,7 @@ void Mod_OBJ_Load(model_t *mod, void *buffer, void *bufferend) mod->radius = modelradius; mod->radius2 = modelradius * modelradius; - // this gets altered below if sky or water is used - mod->DrawSky = NULL; - mod->DrawAddWaterPlanes = NULL; - - for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++) - if (mod->data_surfaces[j].texture->basematerialflags & MATERIALFLAG_SKY) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawSky = R_Mod_DrawSky; - - for (j = mod->submodelsurfaces_start; j < mod->submodelsurfaces_end; j++) - if (mod->data_surfaces[j].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)) - break; - if (j < mod->submodelsurfaces_end) - mod->DrawAddWaterPlanes = R_Mod_DrawAddWaterPlanes; + Mod_SetDrawSkyAndWater(mod); Mod_MakeCollisionBIH(mod, true, &mod->collision_bih); mod->render_bih = mod->collision_bih;