X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=3d15e6ffabddd076546b452c947d4ba5f59bc116;hb=ff46d6ff516fda192c5adc55a5c9b82007545bd2;hp=6940f010c5d233e3786d0ba63c23f0a778fcffd8;hpb=68287aff6f373628781abed3b1b4b896f3563861;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index 6940f010..3d15e6ff 100644 --- a/model_brush.c +++ b/model_brush.c @@ -34,7 +34,14 @@ cvar_t r_novis = {0, "r_novis", "0"}; cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"}; cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"}; cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"}; -cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"}; +cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4"}; +cvar_t r_subdivisions_minlevel = {0, "r_subdivisions_minlevel", "0"}; +cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "10"}; +cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536"}; +cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15"}; +cvar_t r_subdivisions_collision_minlevel = {0, "r_subdivisions_collision_minlevel", "0"}; +cvar_t r_subdivisions_collision_maxlevel = {0, "r_subdivisions_collision_maxlevel", "10"}; +cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225"}; cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"}; cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"}; cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"}; @@ -48,7 +55,14 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&r_miplightmaps); Cvar_RegisterVariable(&r_lightmaprgba); Cvar_RegisterVariable(&r_nosurftextures); - Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level); + Cvar_RegisterVariable(&r_subdivisions_tolerance); + Cvar_RegisterVariable(&r_subdivisions_minlevel); + Cvar_RegisterVariable(&r_subdivisions_maxlevel); + Cvar_RegisterVariable(&r_subdivisions_maxvertices); + Cvar_RegisterVariable(&r_subdivisions_collision_tolerance); + Cvar_RegisterVariable(&r_subdivisions_collision_minlevel); + Cvar_RegisterVariable(&r_subdivisions_collision_maxlevel); + Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices); Cvar_RegisterVariable(&mod_q3bsp_curves_collisions); Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline); Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush); @@ -427,7 +441,7 @@ loc0: if (t->trace->allsolid) t->trace->startsolid = true; #if COLLISIONPARANOID >= 3 - Con_Printf("S"); + Con_Print("S"); #endif return HULLCHECKSTATE_SOLID; } @@ -435,7 +449,7 @@ loc0: { t->trace->allsolid = false; #if COLLISIONPARANOID >= 3 - Con_Printf("E"); + Con_Print("E"); #endif return HULLCHECKSTATE_EMPTY; } @@ -461,7 +475,7 @@ loc0: if (t2 < 0) { #if COLLISIONPARANOID >= 3 - Con_Printf("<"); + Con_Print("<"); #endif num = node->children[1]; goto loc0; @@ -473,7 +487,7 @@ loc0: if (t2 >= 0) { #if COLLISIONPARANOID >= 3 - Con_Printf(">"); + Con_Print(">"); #endif num = node->children[0]; goto loc0; @@ -484,7 +498,7 @@ loc0: // the line intersects, find intersection point // LordHavoc: this uses the original trace for maximum accuracy #if COLLISIONPARANOID >= 3 - Con_Printf("M"); + Con_Print("M"); #endif if (plane->type < 3) { @@ -535,7 +549,7 @@ loc0: t->trace->fraction = bound(0, midf, 1); #if COLLISIONPARANOID >= 3 - Con_Printf("D"); + Con_Print("D"); #endif return HULLCHECKSTATE_DONE; } @@ -609,7 +623,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, #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]); Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); - Con_Printf("\n"); + Con_Print("\n"); #else if (DotProduct(rhc.dist, rhc.dist)) Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); @@ -777,26 +791,21 @@ loc0: if (!(surf->flags & SURF_LIGHTMAP)) continue; // no lightmaps - ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]); - dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]); - - if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) - continue; - - ds -= surf->texturemins[0]; - dt -= surf->texturemins[1]; - - if (ds > surf->extents[0] || dt > surf->extents[1]) - continue; + ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3])- surf->texturemins[0]; + dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]) - surf->texturemins[1]; + ds = bound(0, ds, surf->extents[0]); + dt = bound(0, dt, surf->extents[1]); if (surf->samples) { qbyte *lightmap; - int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; - line3 = ((surf->extents[0]>>4)+1)*3; - size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting + int lmwidth, lmheight, maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; + lmwidth = ((surf->extents[0]>>4)+1); + lmheight = ((surf->extents[1]>>4)+1); + line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting + size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting - lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color + lightmap = surf->samples + ((dt>>4) * lmwidth + (ds>>4))*3; // LordHavoc: *3 for colored lighting for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++) { @@ -974,7 +983,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) } if ((mtwidth & 15) || (mtheight & 15)) - Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name); + Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name); // LordHavoc: force all names to lowercase for (j = 0;name[j];j++) @@ -1238,9 +1247,9 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l) else { if (fs_filesize == 8) - Con_Printf("Empty .lit file, ignoring\n"); + Con_Print("Empty .lit file, ignoring\n"); else - Con_Printf("Corrupt .lit file (old version?), ignoring\n"); + Con_Print("Corrupt .lit file (old version?), ignoring\n"); Mem_Free(data); } } @@ -1633,7 +1642,7 @@ static void SubdividePolygon(int numverts, float *verts) { if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES) { - Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n"); + Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n"); return; } @@ -2020,7 +2029,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) if (p >= 0 && out->clusterindex >= 0) { if (p >= loadmodel->brushq1.num_compressedpvs) - Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n"); + Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n"); else Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, loadmodel->brush.data_pvsclusters + out->clusterindex * loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.data_pvsclusters + (out->clusterindex + 1) * loadmodel->brush.num_pvsclusterbytes); } @@ -2513,7 +2522,7 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true); if (!nodeportalwinding) { - Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n"); + Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n"); break; } } @@ -2729,6 +2738,21 @@ static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model) } } +//Returns PVS data for a given point +//(note: can return NULL) +static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p) +{ + mnode_t *node; + Mod_CheckLoaded(model); + node = model->brushq1.nodes; + while (node->plane) + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; + if (((mleaf_t *)node)->clusterindex >= 0) + return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes; + else + return NULL; +} + static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node) { while (node->plane) @@ -2761,7 +2785,7 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt { int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3; bytes = min(bytes, pvsbufferlength); - if (r_novis.integer) + if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org)) { memset(pvsbuffer, 0xFF, bytes); return bytes; @@ -2771,21 +2795,6 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt return bytes; } -//Returns PVS data for a given point -//(note: can return NULL) -static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p) -{ - mnode_t *node; - Mod_CheckLoaded(model); - node = model->brushq1.nodes; - while (node->plane) - node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; - if (((mleaf_t *)node)->clusterindex >= 0) - return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes; - else - return NULL; -} - static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs) { vec3_t size; @@ -2819,6 +2828,54 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co VectorAdd(inmins, hull->clip_size, outmaxs); } +/* +void Mod_Q1BSP_RecursiveGetVisible(mnode_t *node, model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, mleaf_t *leaflist, int *numleafs, int maxsurfaces, msurface_t *surfacelist, int *numsurfaces, const qbyte *pvs) +{ + mleaf_t *leaf; + for (;;) + { + if (!BoxesOverlap(node->mins, node->maxs, mins, maxs)) + return; + if (!node->plane) + break; + Mod_Q1BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs); + node = node->children[1]; + } + leaf = (mleaf_t *)node; + if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex))) + { + int marksurfacenum; + msurface_t *surf; + if (maxleafs && *numleafs < maxleafs) + leaflist[(*numleafs)++] = leaf; + if (maxsurfaces) + { + for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++) + { + surf = model->brushq1.surfaces + leaf->firstmarksurface[marksurfacenum]; + if (surf->shadowmark != shadowmarkcount) + { + surf->shadowmark = shadowmarkcount; + if (BoxesOverlap(mins, maxs, surf->poly_mins, surf->poly_maxs) && ((surf->flags & SURF_PLANEBACK) ? PlaneDiff(point, surf->plane) < 0 : PlaneDiff(point, surf->plane) > 0) && *numsurfaces < maxsurfaces) + surfacelist[(*numsurfaces)++] = surf; + } + } + } + } +} + +void Mod_Q1BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, mleaf_t *leaflist, int *numleafs, int maxsurfaces, msurface_t *surfacelist, int *numsurfaces) +{ + // FIXME: support portals + if (maxsurfaces) + *numsurfaces = 0; + if (maxleafs) + *numleafs = 0; + pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin); + Mod_Q1BSP_RecursiveGetVisible(ent->model->brushq1.nodes + ent->model->brushq1.firstclipnode, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces); +} +*/ + extern void R_Model_Brush_DrawSky(entity_render_t *ent); extern void R_Model_Brush_Draw(entity_render_t *ent); extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius); @@ -2833,6 +2890,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) model_t *originalloadmodel; float dist, modelyawradius, modelradius, *vec; msurface_t *surf; + int numshadowmeshtriangles; mod->type = mod_brush; @@ -2908,6 +2966,19 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) Mod_Q1BSP_LoadLightList(); originalloadmodel = loadmodel; + // make a single combined shadow mesh to allow optimized shadow volume creation + numshadowmeshtriangles = 0; + for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++) + { + surf->num_firstshadowmeshtriangle = numshadowmeshtriangles; + numshadowmeshtriangles += surf->mesh.num_triangles; + } + loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true); + for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++) + Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i); + loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true); + Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles); + // // set up the submodels(FIXME: this is confusing) // @@ -3502,6 +3573,7 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) const char *text; int flags; char shadername[Q3PATHLENGTH]; + char sky[Q3PATHLENGTH]; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -3533,8 +3605,9 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) text = f; while (COM_ParseToken(&text, false)) { - snprintf(shadername, sizeof(shadername), "%s", com_token); + strncpy(shadername, com_token, sizeof(shadername)); flags = 0; + sky[0] = 0; if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{")) { while (COM_ParseToken(&text, false)) @@ -3619,6 +3692,22 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) goto parseerror; } } + else if (!strcasecmp(com_token, "sky")) + { + if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n")) + if (strlen(com_token) < sizeof(sky)) + strcpy(sky, com_token); + } + else if (!strcasecmp(com_token, "skyparms")) + { + if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n")) + { + if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-")) + strcpy(sky, com_token); + if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n")) + COM_ParseToken(&text, true); + } + } else { // look for linebreak or } @@ -3631,8 +3720,14 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) // add shader to list (shadername and flags) // actually here we just poke into the texture settings for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++) + { if (!strcasecmp(out->name, shadername)) + { out->surfaceparms = flags; + if ((flags & Q3SURFACEPARM_SKY) && sky[0]) + strcpy(loadmodel->brush.skybox, sky); + } + } } else { @@ -4022,13 +4117,6 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) out->type = 0; // error continue; } - // convert patch to Q3FACETYPE_MESH - xlevel = mod_q3bsp_curves_subdivide_level.integer; - ylevel = mod_q3bsp_curves_subdivide_level.integer; - finalwidth = ((patchsize[0] - 1) << xlevel) + 1; - finalheight = ((patchsize[1] - 1) << ylevel) + 1; - finalvertices = finalwidth * finalheight; - finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2; originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3; //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3; //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3; @@ -4049,6 +4137,27 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) //originalelement3i = out->data_element3i; //originalneighbor3i = out->data_neighbor3i; */ + // convert patch to Q3FACETYPE_MESH + xlevel = QuadraticSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10); + ylevel = QuadraticSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10); + // bound to user settings + xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer); + ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer); + // bound to sanity settings + xlevel = bound(0, xlevel, 10); + ylevel = bound(0, ylevel, 10); + // bound to user limit on vertices + while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144)) + { + if (xlevel > ylevel) + xlevel--; + else + ylevel--; + } + finalwidth = ((patchsize[0] - 1) << xlevel) + 1; + finalheight = ((patchsize[1] - 1) << ylevel) + 1; + finalvertices = finalwidth * finalheight; + finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2; out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices); out->data_svector3f = out->data_vertex3f + finalvertices * 3; out->data_tvector3f = out->data_svector3f + finalvertices * 3; @@ -4096,7 +4205,59 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles); } // q3map does not put in collision brushes for curves... ugh + // build the lower quality collision geometry out->collisions = true; + xlevel = QuadraticSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10); + ylevel = QuadraticSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10); + // bound to user settings + xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer); + ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer); + // bound to sanity settings + xlevel = bound(0, xlevel, 10); + ylevel = bound(0, ylevel, 10); + // bound to user limit on vertices + while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144)) + { + if (xlevel > ylevel) + xlevel--; + else + ylevel--; + } + finalwidth = ((patchsize[0] - 1) << xlevel) + 1; + finalheight = ((patchsize[1] - 1) << ylevel) + 1; + finalvertices = finalwidth * finalheight; + finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2; + out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices); + out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles); + out->num_collisionvertices = finalvertices; + out->num_collisiontriangles = finaltriangles; + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f); + // generate elements + e = out->data_collisionelement3i; + for (y = 0;y < finalheight - 1;y++) + { + row0 = (y + 0) * finalwidth; + row1 = (y + 1) * finalwidth; + for (x = 0;x < finalwidth - 1;x++) + { + *e++ = row0; + *e++ = row1; + *e++ = row0 + 1; + *e++ = row1; + *e++ = row1 + 1; + *e++ = row0 + 1; + row0++; + row1++; + } + } + out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f); + if (developer.integer) + { + if (out->num_collisiontriangles < finaltriangles) + Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_collisionvertices, finaltriangles, finaltriangles - out->num_collisiontriangles, out->num_collisiontriangles); + else + Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_collisionvertices, out->num_collisiontriangles); + } break; case Q3FACETYPE_FLARE: Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name); @@ -4118,7 +4279,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices) out->data_element3i[j] = 0; } - Con_Printf("\n"); + Con_Print("\n"); } // for shadow volumes Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles); @@ -4685,7 +4846,7 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs)) { face->collisionmarkframe = markframe; - Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); if (startfrac > trace->realfraction) return; } @@ -5065,7 +5226,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs)) { face->markframe = markframe; - Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); } } } @@ -5119,7 +5280,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const { face = model->brushq3.data_thismodel->firstface + i; if (face->collisions) - Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); } } } @@ -5143,7 +5304,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const { face = model->brushq3.data_thismodel->firstface + i; if (face->collisions) - Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); } } } @@ -5253,7 +5414,7 @@ static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt { int bytes = model->brush.num_pvsclusterbytes; bytes = min(bytes, pvsbufferlength); - if (r_novis.integer || !loadmodel->brush.num_pvsclusters) + if (r_novis.integer || !loadmodel->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org)) { memset(pvsbuffer, 0xFF, bytes); return bytes; @@ -5292,15 +5453,79 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco return nativecontents; } +/* +void Mod_Q3BSP_RecursiveGetVisible(q3mnode_t *node, model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, q3mleaf_t *leaflist, int *numleafs, int maxsurfaces, q3msurface_t *surfacelist, int *numsurfaces, const qbyte *pvs) +{ + mleaf_t *leaf; + for (;;) + { + if (!BoxesOverlap(node->mins, node->maxs, mins, maxs)) + return; + if (!node->plane) + break; + Mod_Q3BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs); + node = node->children[1]; + } + leaf = (mleaf_t *)node; + if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex))) + { + int marksurfacenum; + q3mface_t *surf; + if (maxleafs && *numleafs < maxleafs) + leaflist[(*numleaf)++] = leaf; + if (maxsurfaces) + { + for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++) + { + face = leaf->firstleafface[marksurfacenum]; + if (face->shadowmark != shadowmarkcount) + { + face->shadowmark = shadowmarkcount; + if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces) + surfacelist[(*numsurfaces)++] = face; + } + } + } + } +} + +void Mod_Q3BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, q3mleaf_t *leaflist, int *numleafs, int maxsurfaces, q3msurface_t *surfacelist, int *numsurfaces) +{ + // FIXME: support portals + if (maxsurfaces) + *numsurfaces = 0; + if (maxleafs) + *numleafs = 0; + if (model->submodel) + { + if (maxsurfaces) + { + for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++) + { + face = ent->model->brushq3.surfaces + leaf->firstmarksurface[marksurfacenum]; + if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces) + surfacelist[(*numsurfaces)++] = face; + } + } + } + else + { + pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin); + Mod_Q3BSP_RecursiveGetVisible(ent->model->brushq3.data_nodes, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs); + } +} +*/ + extern void R_Q3BSP_DrawSky(struct entity_render_s *ent); extern void R_Q3BSP_Draw(struct entity_render_s *ent); extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius); extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap); void Mod_Q3BSP_Load(model_t *mod, void *buffer) { - int i, j; + int i, j, numshadowmeshtriangles; q3dheader_t *header; float corner[3], yawradius, modelradius; + q3mface_t *face; mod->type = mod_brushq3; mod->numframes = 1; @@ -5357,6 +5582,19 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer) Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]); loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models; + // make a single combined shadow mesh to allow optimized shadow volume creation + numshadowmeshtriangles = 0; + for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++) + { + face->num_firstshadowmeshtriangle = numshadowmeshtriangles; + numshadowmeshtriangles += face->num_triangles; + } + loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true); + for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++) + Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i); + loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true); + Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles); + for (i = 0;i < loadmodel->brushq3.num_models;i++) { if (i == 0) @@ -5376,6 +5614,11 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer) // textures and memory belong to the main model mod->texturepool = NULL; mod->mempool = NULL; + mod->brush.GetPVS = NULL; + mod->brush.FatPVS = NULL; + mod->brush.BoxTouchingPVS = NULL; + mod->brush.LightPoint = NULL; + mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation; } mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i; mod->brushq3.submodel = i;