X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=3d15e6ffabddd076546b452c947d4ba5f59bc116;hb=045c0de16a295c2599bc83dfa8942c393eed3903;hp=38e7d23a83a0fb575a443525e1ce9e7e625c30a8;hpb=e24d834dcbcc61f28fdf0e2c1ba75693d9e23ed1;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index 38e7d23a..3d15e6ff 100644 --- a/model_brush.c +++ b/model_brush.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "image.h" #include "r_shadow.h" #include "winding.h" +#include "curves.h" // note: model_shared.c sets up r_notexture, and r_surf_notexture @@ -33,8 +34,19 @@ 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 r_sortsurfaces = {0, "r_sortsurfaces", "0"}; - +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"}; + +static void Mod_Q1BSP_Collision_Init (void); void Mod_BrushInit(void) { // Cvar_RegisterVariable(&r_subdivide_size); @@ -43,8 +55,19 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&r_miplightmaps); Cvar_RegisterVariable(&r_lightmaprgba); Cvar_RegisterVariable(&r_nosurftextures); - Cvar_RegisterVariable(&r_sortsurfaces); + 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); memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis)); + Mod_Q1BSP_Collision_Init(); } static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p) @@ -84,45 +107,51 @@ static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, memset(out, 0, outsize); } - -static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) { - int leafnum; -loc0: - if (node->contents < 0) - { - // leaf - if (node->contents == CONTENTS_SOLID) - return false; - leafnum = (mleaf_t *)node - model->brushq1.leafs - 1; - return pvs[leafnum >> 3] & (1 << (leafnum & 7)); - } - - // node - recurse down the BSP tree - switch (BoxOnPlaneSide(mins, maxs, node->plane)) + int clusterindex, side, nodestackindex = 0; + mnode_t *node, *nodestack[1024]; + node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; + for (;;) { - case 1: // front - node = node->children[0]; - goto loc0; - case 2: // back - node = node->children[1]; - goto loc0; - default: // crossing - if (node->children[0]->contents != CONTENTS_SOLID) - if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) + if (node->plane) + { + // node - recurse down the BSP tree + side = BoxOnPlaneSide(mins, maxs, node->plane) - 1; + if (side < 2) + { + // box is on one side of plane, take that path + node = node->children[side]; + } + else + { + // box crosses plane, take one path and remember the other + nodestack[nodestackindex++] = node->children[0]; + node = node->children[1]; + } + } + else + { + // leaf - check cluster bit + clusterindex = ((mleaf_t *)node)->clusterindex; + if (CHECKPVSBIT(pvs, clusterindex)) + { + // it is visible, return immediately with the news return true; - node = node->children[1]; - goto loc0; + } + else + { + // nothing to see here, try another path we didn't take earlier + if (nodestackindex == 0) + break; + node = nodestack[--nodestackindex]; + } + } } - // never reached + // it is not visible return false; } -int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) -{ - return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs); -} - /* static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p) { @@ -164,7 +193,6 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in float surfnormal[3]; #endif msurface_t *surf; - surfmesh_t *mesh; for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++) { surf = info->model->brushq1.surfaces + *mark; @@ -175,103 +203,100 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in if (surf->flags & SURF_PLANEBACK) VectorNegate(surfnormal, surfnormal); #endif - for (mesh = surf->mesh;mesh;mesh = mesh->chain) + for (k = 0;k < surf->mesh.num_triangles;k++) { - for (k = 0;k < mesh->numtriangles;k++) + tri = surf->mesh.data_element3i + k * 3; + VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]); + VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]); + VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]); + VectorSubtract(vert[1], vert[0], edge[0]); + VectorSubtract(vert[2], vert[1], edge[1]); + CrossProduct(edge[1], edge[0], facenormal); + if (facenormal[0] || facenormal[1] || facenormal[2]) { - tri = mesh->element3i + k * 3; - VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]); - VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]); - VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]); - VectorSubtract(vert[1], vert[0], edge[0]); - VectorSubtract(vert[2], vert[1], edge[1]); - CrossProduct(edge[1], edge[0], facenormal); - if (facenormal[0] || facenormal[1] || facenormal[2]) + VectorNormalize(facenormal); +#if 0 + if (VectorDistance(facenormal, surfnormal) > 0.01f) + Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]); +#endif + f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); + if (f <= info->bestdist && f >= -info->bestdist) { - VectorNormalize(facenormal); + VectorSubtract(vert[0], vert[2], edge[2]); + VectorNormalize(edge[0]); + VectorNormalize(edge[1]); + VectorNormalize(edge[2]); + CrossProduct(facenormal, edge[0], edgenormal[0]); + CrossProduct(facenormal, edge[1], edgenormal[1]); + CrossProduct(facenormal, edge[2], edgenormal[2]); #if 0 - if (VectorDistance(facenormal, surfnormal) > 0.01f) - Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]); + if (samelevel.integer & 1) + VectorNegate(edgenormal[0], edgenormal[0]); + if (samelevel.integer & 2) + VectorNegate(edgenormal[1], edgenormal[1]); + if (samelevel.integer & 4) + VectorNegate(edgenormal[2], edgenormal[2]); + for (i = 0;i < 3;i++) + if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f + || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f + || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f) + Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]); #endif - f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); - if (f <= info->bestdist && f >= -info->bestdist) + // face distance + if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0]) + && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1]) + && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2])) { - VectorSubtract(vert[0], vert[2], edge[2]); - VectorNormalize(edge[0]); - VectorNormalize(edge[1]); - VectorNormalize(edge[2]); - CrossProduct(facenormal, edge[0], edgenormal[0]); - CrossProduct(facenormal, edge[1], edgenormal[1]); - CrossProduct(facenormal, edge[2], edgenormal[2]); -#if 0 - if (samelevel.integer & 1) - VectorNegate(edgenormal[0], edgenormal[0]); - if (samelevel.integer & 2) - VectorNegate(edgenormal[1], edgenormal[1]); - if (samelevel.integer & 4) - VectorNegate(edgenormal[2], edgenormal[2]); + // we got lucky, the center is within the face + dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); + if (dist < 0) + { + dist = -dist; + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(facenormal, (info->radius - -dist), info->nudge); + } + } + else + { + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(facenormal, (info->radius - dist), info->nudge); + } + } + } + else + { + // check which edge or vertex the center is nearest for (i = 0;i < 3;i++) - if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f - || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f - || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f) - Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]); -#endif - // face distance - if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0]) - && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1]) - && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2])) { - // we got lucky, the center is within the face - dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); - if (dist < 0) + f = DotProduct(info->center, edge[i]); + if (f >= DotProduct(vert[0], edge[i]) + && f <= DotProduct(vert[1], edge[i])) { - dist = -dist; + // on edge + VectorMA(info->center, -f, edge[i], point); + dist = sqrt(DotProduct(point, point)); if (info->bestdist > dist) { info->bestdist = dist; - VectorScale(facenormal, (info->radius - -dist), info->nudge); + VectorScale(point, (info->radius / dist), info->nudge); } + // skip both vertex checks + // (both are further away than this edge) + i++; } else { + // not on edge, check first vertex of edge + VectorSubtract(info->center, vert[i], point); + dist = sqrt(DotProduct(point, point)); if (info->bestdist > dist) { info->bestdist = dist; - VectorScale(facenormal, (info->radius - dist), info->nudge); - } - } - } - else - { - // check which edge or vertex the center is nearest - for (i = 0;i < 3;i++) - { - f = DotProduct(info->center, edge[i]); - if (f >= DotProduct(vert[0], edge[i]) - && f <= DotProduct(vert[1], edge[i])) - { - // on edge - VectorMA(info->center, -f, edge[i], point); - dist = sqrt(DotProduct(point, point)); - if (info->bestdist > dist) - { - info->bestdist = dist; - VectorScale(point, (info->radius / dist), info->nudge); - } - // skip both vertex checks - // (both are further away than this edge) - i++; - } - else - { - // not on edge, check first vertex of edge - VectorSubtract(info->center, vert[i], point); - dist = sqrt(DotProduct(point, point)); - if (info->bestdist > dist) - { - info->bestdist = dist; - VectorScale(point, (info->radius / dist), info->nudge); - } + VectorScale(point, (info->radius / dist), info->nudge); } } } @@ -415,11 +440,17 @@ loc0: // if the first leaf is solid, set startsolid if (t->trace->allsolid) t->trace->startsolid = true; +#if COLLISIONPARANOID >= 3 + Con_Print("S"); +#endif return HULLCHECKSTATE_SOLID; } else { t->trace->allsolid = false; +#if COLLISIONPARANOID >= 3 + Con_Print("E"); +#endif return HULLCHECKSTATE_EMPTY; } } @@ -443,6 +474,9 @@ loc0: { if (t2 < 0) { +#if COLLISIONPARANOID >= 3 + Con_Print("<"); +#endif num = node->children[1]; goto loc0; } @@ -452,6 +486,9 @@ loc0: { if (t2 >= 0) { +#if COLLISIONPARANOID >= 3 + Con_Print(">"); +#endif num = node->children[0]; goto loc0; } @@ -460,6 +497,9 @@ loc0: // the line intersects, find intersection point // LordHavoc: this uses the original trace for maximum accuracy +#if COLLISIONPARANOID >= 3 + Con_Print("M"); +#endif if (plane->type < 3) { t1 = t->start[plane->type] - plane->dist; @@ -498,17 +538,47 @@ loc0: VectorCopy (plane->normal, t->trace->plane.normal); } - // bias away from surface a bit - t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON); - t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON); - + // calculate the true fraction + t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist; + t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist; midf = t1 / (t1 - t2); - t->trace->fraction = bound(0.0f, midf, 1.0); + t->trace->realfraction = bound(0, midf, 1); + // calculate the return fraction which is nudged off the surface a bit + midf = (t1 - DIST_EPSILON) / (t1 - t2); + t->trace->fraction = bound(0, midf, 1); + +#if COLLISIONPARANOID >= 3 + Con_Print("D"); +#endif return HULLCHECKSTATE_DONE; } -static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) +#if COLLISIONPARANOID < 2 +static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num) +{ + while (num >= 0) + num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist]; + num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num); + t->trace->startsupercontents |= num; + if (num & SUPERCONTENTS_LIQUIDSMASK) + t->trace->inwater = true; + if (num == 0) + t->trace->inopen = true; + if (num & t->trace->hitsupercontentsmask) + { + t->trace->allsolid = t->trace->startsolid = true; + return HULLCHECKSTATE_SOLID; + } + else + { + t->trace->allsolid = t->trace->startsolid = false; + return HULLCHECKSTATE_EMPTY; + } +} +#endif + +static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) { // this function currently only supports same size start and end double boxsize[3]; @@ -519,13 +589,16 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3 rhc.trace = trace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; + rhc.trace->realfraction = 1; rhc.trace->allsolid = true; VectorSubtract(boxstartmaxs, boxstartmins, boxsize); if (boxsize[0] < 3) rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 else if (model->brush.ishlbsp) { - if (boxsize[0] <= 32) + // LordHavoc: this has to have a minor tolerance (the .1) because of + // minor float precision errors from the box being transformed around + if (boxsize[0] < 32.1) { if (boxsize[2] < 54) // pick the nearest of 36 or 72 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36 @@ -537,7 +610,9 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3 } else { - if (boxsize[0] <= 32) + // LordHavoc: this has to have a minor tolerance (the .1) because of + // minor float precision errors from the box being transformed around + if (boxsize[0] < 32.1) rhc.hull = &model->brushq1.hulls[1]; // 32x32x56 else rhc.hull = &model->brushq1.hulls[2]; // 64x64x88 @@ -545,7 +620,115 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start); VectorSubtract(boxendmins, 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]); + Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); + Con_Print("\n"); +#else + if (DotProduct(rhc.dist, rhc.dist)) + Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); + else + Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode); +#endif +} + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +static void Mod_Q1BSP_Collision_Init (void) +{ + int i; + int side; + + //Set up the planes and clipnodes so that the six floats of a bounding box + //can just be stored out and get a proper hull_t structure. + + box_hull.clipnodes = box_clipnodes; + box_hull.planes = box_planes; + box_hull.firstclipnode = 0; + box_hull.lastclipnode = 5; + + for (i = 0;i < 6;i++) + { + box_clipnodes[i].planenum = i; + + side = i&1; + + box_clipnodes[i].children[side] = CONTENTS_EMPTY; + if (i != 5) + box_clipnodes[i].children[side^1] = i + 1; + else + box_clipnodes[i].children[side^1] = CONTENTS_SOLID; + + box_planes[i].type = i>>1; + box_planes[i].normal[i>>1] = 1; + } +} + +void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents) +{ +#if 1 + colbrushf_t cbox; + colplanef_t cbox_planes[6]; + cbox.supercontents = boxsupercontents; + cbox.numplanes = 6; + cbox.numpoints = 0; + cbox.numtriangles = 0; + cbox.planes = cbox_planes; + cbox.points = NULL; + cbox.elements = NULL; + cbox.markframe = 0; + cbox.mins[0] = 0; + cbox.mins[1] = 0; + cbox.mins[2] = 0; + cbox.maxs[0] = 0; + cbox.maxs[1] = 0; + cbox.maxs[2] = 0; + cbox_planes[0].normal[0] = 1;cbox_planes[0].normal[1] = 0;cbox_planes[0].normal[2] = 0;cbox_planes[0].dist = cmaxs[0] - mins[0]; + cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] = 0;cbox_planes[1].normal[2] = 0;cbox_planes[1].dist = maxs[0] - cmins[0]; + cbox_planes[2].normal[0] = 0;cbox_planes[2].normal[1] = 1;cbox_planes[2].normal[2] = 0;cbox_planes[2].dist = cmaxs[1] - mins[1]; + cbox_planes[3].normal[0] = 0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] = 0;cbox_planes[3].dist = maxs[1] - cmins[1]; + cbox_planes[4].normal[0] = 0;cbox_planes[4].normal[1] = 0;cbox_planes[4].normal[2] = 1;cbox_planes[4].dist = cmaxs[2] - mins[2]; + cbox_planes[5].normal[0] = 0;cbox_planes[5].normal[1] = 0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2]; + memset(trace, 0, sizeof(trace_t)); + trace->hitsupercontentsmask = hitsupercontentsmask; + trace->fraction = 1; + trace->realfraction = 1; + Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox); +#else + RecursiveHullCheckTraceInfo_t rhc; + // fill in a default trace + memset(&rhc, 0, sizeof(rhc)); + memset(trace, 0, sizeof(trace_t)); + //To keep everything totally uniform, bounding boxes are turned into small + //BSP trees instead of being compared directly. + // create a temp hull from bounding box sizes + box_planes[0].dist = cmaxs[0] - mins[0]; + box_planes[1].dist = cmins[0] - maxs[0]; + box_planes[2].dist = cmaxs[1] - mins[1]; + box_planes[3].dist = cmins[1] - maxs[1]; + box_planes[4].dist = cmaxs[2] - mins[2]; + box_planes[5].dist = cmins[2] - maxs[2]; +#if COLLISIONPARANOID >= 3 + Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]); +#endif + // trace a line through the generated clipping hull + //rhc.boxsupercontents = boxsupercontents; + rhc.hull = &box_hull; + rhc.trace = trace; + rhc.trace->hitsupercontentsmask = hitsupercontentsmask; + rhc.trace->fraction = 1; + rhc.trace->realfraction = 1; + rhc.trace->allsolid = true; + VectorCopy(start, rhc.start); + VectorCopy(end, rhc.end); + VectorSubtract(rhc.end, rhc.start, rhc.dist); Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); + //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos); + if (rhc.trace->startsupercontents) + rhc.trace->startsupercontents = boxsupercontents; +#endif } static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz) @@ -608,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++) { @@ -696,11 +874,12 @@ void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, v static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend) { int c; + qbyte *outstart = out; while (out < outend) { if (in == inend) { - Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n"); + Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart); return; } c = *in++; @@ -708,11 +887,16 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte * *out++ = c; else { + if (in == inend) + { + Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart); + return; + } for (c = *in++;c > 0;c--) { if (out == outend) { - Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n"); + Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart); return; } *out++ = 0; @@ -732,15 +916,19 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) loadmodel->brushq1.textures = NULL; - if (!l->filelen) - return; - - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); - // add two slots for notexture walls and notexture liquids - loadmodel->brushq1.numtextures = m->nummiptex + 2; + if (l->filelen) + { + m = (dmiptexlump_t *)(mod_base + l->fileofs); + m->nummiptex = LittleLong (m->nummiptex); + loadmodel->brushq1.numtextures = m->nummiptex + 2; + } + else + { + m = NULL; + loadmodel->brushq1.numtextures = 2; + } + loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t)); // fill out all slots with notexture @@ -761,6 +949,9 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l) tx->currentframe = tx; } + if (!m) + return; + // just to work around bounds checking when debugging with it (array index out of bounds error thing) dofs = m->dataofs; // LordHavoc: mostly rewritten map texture loader @@ -792,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++) @@ -1030,9 +1221,9 @@ static void Mod_Q1BSP_LoadLighting(lump_t *l) else // LordHavoc: bsp version 29 (normal white lighting) { // LordHavoc: hope is not lost yet, check for a .lit file to load - strcpy(litfilename, loadmodel->name); - FS_StripExtension(litfilename, litfilename); - strcat(litfilename, ".lit"); + strlcpy (litfilename, loadmodel->name, sizeof (litfilename)); + FS_StripExtension (litfilename, litfilename, sizeof (litfilename)); + strlcat (litfilename, ".lit", sizeof (litfilename)); data = (qbyte*) FS_LoadFile(litfilename, false); if (data) { @@ -1056,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); } } @@ -1085,9 +1276,9 @@ static void Mod_Q1BSP_LoadLightList(void) char lightsfilename[1024], *s, *t, *lightsstring; mlight_t *e; - strcpy(lightsfilename, loadmodel->name); - FS_StripExtension(lightsfilename, lightsfilename); - strcat(lightsfilename, ".lights"); + strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename)); + FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename)); + strlcat (lightsfilename, ".lights", sizeof (lightsfilename)); s = lightsstring = (char *) FS_LoadFile(lightsfilename, false); if (s) { @@ -1451,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; } @@ -1480,13 +1671,13 @@ static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf) Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n"); surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t)); - mesh->numverts = subdivpolyverts; - mesh->numtriangles = subdivpolytriangles; + mesh->num_vertices = subdivpolyverts; + mesh->num_triangles = subdivpolytriangles; mesh->vertex = (surfvertex_t *)(mesh + 1); - mesh->index = (int *)(mesh->vertex + mesh->numverts); - memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t)); + mesh->index = (int *)(mesh->vertex + mesh->num_vertices); + memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t)); - for (i = 0;i < mesh->numtriangles;i++) + for (i = 0;i < mesh->num_triangles;i++) for (j = 0;j < 3;j++) mesh->index[i*3+j] = subdivpolyindex[i][j]; @@ -1503,18 +1694,18 @@ static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles) { surfmesh_t *mesh; mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float)); - mesh->numverts = numverts; - mesh->numtriangles = numtriangles; - mesh->vertex3f = (float *)(mesh + 1); - mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3; - mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2; - mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2; - mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2); - mesh->tvector3f = mesh->svector3f + mesh->numverts * 3; - mesh->normal3f = mesh->tvector3f + mesh->numverts * 3; - mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3); - mesh->element3i = mesh->lightmapoffsets + mesh->numverts; - mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3; + mesh->num_vertices = numverts; + mesh->num_triangles = numtriangles; + mesh->data_vertex3f = (float *)(mesh + 1); + mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3; + mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2; + mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2; + mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2); + mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3; + mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3; + mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3); + mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices; + mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3; return mesh; } @@ -1653,52 +1844,51 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l) } loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris); - loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes); for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++) { - mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes; - mesh->numverts = surf->poly_numverts; - mesh->numtriangles = surf->poly_numverts - 2; - mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3; - mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2; - mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2; - mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2; - mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3; - mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3; - mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3; - mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts; - mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3; - mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3; + mesh = &surf->mesh; + mesh->num_vertices = surf->poly_numverts; + mesh->num_triangles = surf->poly_numverts - 2; + mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3; + mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2; + mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2; + mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2; + mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3; + mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3; + mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3; + mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts; + mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3; + mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3; surf->lightmaptexturestride = 0; surf->lightmaptexture = NULL; - for (i = 0;i < mesh->numverts;i++) + for (i = 0;i < mesh->num_vertices;i++) { - mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0]; - mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1]; - mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2]; - s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]; - t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]; - mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width; - mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height; - mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f); - mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f); - mesh->texcoordlightmap2f[i * 2 + 0] = 0; - mesh->texcoordlightmap2f[i * 2 + 1] = 0; - mesh->lightmapoffsets[i] = 0; + mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0]; + mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1]; + mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2]; + s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]; + t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]; + mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width; + mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height; + mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f); + mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f); + mesh->data_texcoordlightmap2f[i * 2 + 0] = 0; + mesh->data_texcoordlightmap2f[i * 2 + 1] = 0; + mesh->data_lightmapoffsets[i] = 0; } - for (i = 0;i < mesh->numtriangles;i++) + for (i = 0;i < mesh->num_triangles;i++) { - mesh->element3i[i * 3 + 0] = 0; - mesh->element3i[i * 3 + 1] = i + 1; - mesh->element3i[i * 3 + 2] = i + 2; + mesh->data_element3i[i * 3 + 0] = 0; + mesh->data_element3i[i * 3 + 1] = i + 1; + mesh->data_element3i[i * 3 + 2] = i + 2; } - Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles); - Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f); + Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles); + Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f); if (surf->texinfo->texture->shader == &Cshader_wall_lightmap) { @@ -1723,16 +1913,16 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l) uscale = (uscale - ubase) / (smax + 1); vscale = (vscale - vbase) / (tmax + 1); - for (i = 0;i < mesh->numverts;i++) + for (i = 0;i < mesh->num_vertices;i++) { - u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0); - v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0); - mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase; - mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase; + u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0); + v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0); + mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase; + mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase; // LordHavoc: calc lightmap data offset for vertex lighting to use iu = (int) u; iv = (int) v; - mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3; + mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3; } } } @@ -1782,7 +1972,7 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l) if (p >= 0) out->children[j] = loadmodel->brushq1.nodes + p; else - out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p)); + out->children[j] = (mnode_t *)(loadmodel->brushq1.data_leafs + (-1 - p)); } } @@ -1793,8 +1983,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) { dleaf_t *in; mleaf_t *out; - int i, j, count, p, pvschainbytes; - qbyte *pvs; + int i, j, count, p; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -1802,10 +1991,13 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) count = l->filelen / sizeof(*in); out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); - loadmodel->brushq1.leafs = out; - loadmodel->brushq1.numleafs = count; - pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3; - loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes); + loadmodel->brushq1.data_leafs = out; + loadmodel->brushq1.num_leafs = count; + // get visleafs from the submodel data + loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs; + loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3; + loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes); + memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes); for ( i=0 ; ifirstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface); out->nummarksurfaces = LittleShort(in->nummarksurfaces); + if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces) + { + Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces); + out->firstmarksurface = NULL; + out->nummarksurfaces = 0; + } - out->pvsdata = pvs; - pvs += pvschainbytes; + out->clusterindex = i - 1; + if (out->clusterindex >= loadmodel->brush.num_pvsclusters) + out->clusterindex = -1; p = LittleLong(in->visofs); - if (p >= 0) - Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes); - else - memset(out->pvsdata, 0xFF, pvschainbytes); + // ignore visofs errors on leaf 0 (solid) + if (p >= 0 && out->clusterindex >= 0) + { + if (p >= loadmodel->brushq1.num_compressedpvs) + 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); + } for (j = 0;j < 4;j++) out->ambient_sound_level[j] = in->ambient_level[j]; @@ -2077,8 +2280,8 @@ static void Mod_Q1BSP_FinalizePortals(void) winding_t *w; // recalculate bounding boxes for all leafs(because qbsp is very sloppy) - leaf = loadmodel->brushq1.leafs; - endleaf = leaf + loadmodel->brushq1.numleafs; + leaf = loadmodel->brushq1.data_leafs; + endleaf = leaf + loadmodel->brushq1.num_leafs; for (;leaf < endleaf;leaf++) { VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000); @@ -2131,8 +2334,8 @@ static void Mod_Q1BSP_FinalizePortals(void) loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t)); loadmodel->brushq1.numportalpoints = numpoints; // clear all leaf portal chains - for (i = 0;i < loadmodel->brushq1.numleafs;i++) - loadmodel->brushq1.leafs[i].portals = NULL; + for (i = 0;i < loadmodel->brushq1.num_leafs;i++) + loadmodel->brushq1.data_leafs[i].portals = NULL; // process all portals in the global portal chain, while freeing them portal = loadmodel->brushq1.portals; point = loadmodel->brushq1.portalpoints; @@ -2296,9 +2499,9 @@ static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) // create the new portal by generating a polygon for the node plane, // and clipping it by all of the other portals(which came from nodes above this one) nodeportal = AllocPortal(); - nodeportal->plane = *node->plane; + nodeportal->plane = *plane; - nodeportalwinding = Winding_NewFromPlane(node->plane->normal[0], node->plane->normal[1], node->plane->normal[2], node->plane->dist); + nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist); side = 0; // shut up compiler warning for (portal = (portal_t *)node->portals;portal;portal = portal->next[side]) { @@ -2319,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; } } @@ -2535,43 +2738,58 @@ static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model) } } -static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node) +//Returns PVS data for a given point +//(note: can return NULL) +static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p) { - int i; - mplane_t *plane; - float d; + 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; +} - while (1) +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) { - // if this is a leaf, accumulate the pvs bits - if (node->contents < 0) - { - if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata) - for (i = 0;i < pvsbytes;i++) - pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i]; - return; - } - - plane = node->plane; - d = DotProduct(org, plane->normal) - plane->dist; + float d = PlaneDiff(org, node->plane); if (d > radius) node = node->children[0]; else if (d < -radius) node = node->children[1]; else - { // go down both + { + // go down both sides Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]); node = node->children[1]; } } + // if this leaf is in a cluster, accumulate the pvs bits + if (((mleaf_t *)node)->clusterindex >= 0) + { + int i; + qbyte *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes; + for (i = 0;i < pvsbytes;i++) + pvsbuffer[i] |= pvs[i]; + } } //Calculates a PVS that is the inclusive or of all leafs within radius pixels //of the given point. static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) { - int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3; + int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3; bytes = min(bytes, pvsbufferlength); + if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org)) + { + memset(pvsbuffer, 0xFF, bytes); + return bytes; + } memset(pvsbuffer, 0, bytes); Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes); return bytes; @@ -2610,10 +2828,58 @@ 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); -extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz); +extern void R_Model_Brush_DrawLight(entity_render_t *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_Q1BSP_Load(model_t *mod, void *buffer) { int i, j, k; @@ -2624,7 +2890,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) model_t *originalloadmodel; float dist, modelyawradius, modelradius, *vec; msurface_t *surf; - surfmesh_t *mesh; + int numshadowmeshtriangles; mod->type = mod_brush; @@ -2635,14 +2901,16 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION); mod->brush.ishlbsp = i == 30; + mod->soundfromcenter = true; + mod->TraceBox = Mod_Q1BSP_TraceBox; mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents; mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents; - mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint; + mod->brush.GetPVS = Mod_Q1BSP_GetPVS; mod->brush.FatPVS = Mod_Q1BSP_FatPVS; mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS; mod->brush.LightPoint = Mod_Q1BSP_LightPoint; mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation; - mod->brush.TraceBox = Mod_Q1BSP_TraceBox; + mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint; mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize; mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf; mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains; @@ -2676,10 +2944,11 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]); Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]); Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]); + // load submodels before leafs because they contain the number of vis leafs + Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]); Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]); Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]); Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]); - Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]); if (mod->brushq1.data_compressedpvs) Mem_Free(mod->brushq1.data_compressedpvs); @@ -2689,9 +2958,6 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) Mod_Q1BSP_MakeHull0(); Mod_Q1BSP_MakePortals(); - if (developer.integer) - Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals); - mod->numframes = 2; // regular and alternate animation mainmempool = mod->mempool; @@ -2700,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) // @@ -2720,9 +2999,16 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) // this gets altered below if sky is used mod->DrawSky = NULL; mod->Draw = R_Model_Brush_Draw; - mod->DrawFakeShadow = NULL; mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume; mod->DrawLight = R_Model_Brush_DrawLight; + if (i != 0) + { + mod->brush.GetPVS = NULL; + mod->brush.FatPVS = NULL; + mod->brush.BoxTouchingPVS = NULL; + mod->brush.LightPoint = NULL; + mod->brush.AmbientSoundLevelsForPoint = NULL; + } mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **)); mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *)); mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int)); @@ -2744,23 +3030,20 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) if (mod->brush.numsubmodels - 1) surf->flags |= SURF_SOLIDCLIP; // calculate bounding shapes - for (mesh = surf->mesh;mesh;mesh = mesh->chain) + for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3) { - for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3) - { - if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0]; - if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1]; - if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2]; - if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0]; - if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1]; - if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2]; - dist = vec[0]*vec[0]+vec[1]*vec[1]; - if (modelyawradius < dist) - modelyawradius = dist; - dist += vec[2]*vec[2]; - if (modelradius < dist) - modelradius = dist; - } + if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0]; + if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1]; + if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2]; + if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0]; + if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1]; + if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2]; + dist = vec[0]*vec[0]+vec[1]*vec[1]; + if (modelyawradius < dist) + modelyawradius = dist; + dist += vec[2]*vec[2]; + if (modelradius < dist) + modelradius = dist; } } modelyawradius = sqrt(modelyawradius); @@ -2780,7 +3063,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) } Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool); - mod->brushq1.visleafs = bm->visleafs; + mod->brushq1.num_visleafs = bm->visleafs; // LordHavoc: only register submodels if it is the world // (prevents bsp models from replacing world submodels) @@ -2801,6 +3084,9 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) loadmodel = originalloadmodel; //Mod_Q1BSP_ProcessLightList(); + + if (developer.integer) + Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.num_leafs, loadmodel->brushq1.num_visleafs, loadmodel->brushq1.numportals); } static void Mod_Q2BSP_LoadEntities(lump_t *l) @@ -3281,6 +3567,13 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) q3dtexture_t *in; q3mtexture_t *out; int i, count; + int j, c; + fssearch_t *search; + char *f; + const char *text; + int flags; + char shadername[Q3PATHLENGTH]; + char sky[Q3PATHLENGTH]; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -3293,17 +3586,180 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) for (i = 0;i < count;i++, in++, out++) { - strncpy(out->name, in->name, sizeof(out->name) - 1); + out->number = i; + strlcpy (out->name, in->name, sizeof (out->name)); out->surfaceflags = LittleLong(in->surfaceflags); out->nativecontents = LittleLong(in->contents); out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents); - out->renderflags = 0; - if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")) - out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW; - - out->number = i; Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true); + out->surfaceparms = -1; + } + + // do a quick parse of shader files to get surfaceparms + if ((search = FS_Search("scripts/*.shader", true, false))) + { + for (i = 0;i < search->numfilenames;i++) + { + if ((f = FS_LoadFile(search->filenames[i], false))) + { + text = f; + while (COM_ParseToken(&text, false)) + { + strncpy(shadername, com_token, sizeof(shadername)); + flags = 0; + sky[0] = 0; + if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{")) + { + while (COM_ParseToken(&text, false)) + { + if (!strcasecmp(com_token, "}")) + break; + else if (!strcasecmp(com_token, "{")) + { + while (COM_ParseToken(&text, false)) + { + if (!strcasecmp(com_token, "}")) + break; + } + } + else if (!strcasecmp(com_token, "surfaceparm")) + { + if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n")) + { + if (!strcasecmp(com_token, "alphashadow")) + flags |= Q3SURFACEPARM_ALPHASHADOW; + else if (!strcasecmp(com_token, "areaportal")) + flags |= Q3SURFACEPARM_AREAPORTAL; + else if (!strcasecmp(com_token, "clusterportal")) + flags |= Q3SURFACEPARM_CLUSTERPORTAL; + else if (!strcasecmp(com_token, "detail")) + flags |= Q3SURFACEPARM_DETAIL; + else if (!strcasecmp(com_token, "donotenter")) + flags |= Q3SURFACEPARM_DONOTENTER; + else if (!strcasecmp(com_token, "fog")) + flags |= Q3SURFACEPARM_FOG; + else if (!strcasecmp(com_token, "lava")) + flags |= Q3SURFACEPARM_LAVA; + else if (!strcasecmp(com_token, "lightfilter")) + flags |= Q3SURFACEPARM_LIGHTFILTER; + else if (!strcasecmp(com_token, "metalsteps")) + flags |= Q3SURFACEPARM_METALSTEPS; + else if (!strcasecmp(com_token, "nodamage")) + flags |= Q3SURFACEPARM_NODAMAGE; + else if (!strcasecmp(com_token, "nodlight")) + flags |= Q3SURFACEPARM_NODLIGHT; + else if (!strcasecmp(com_token, "nodraw")) + flags |= Q3SURFACEPARM_NODRAW; + else if (!strcasecmp(com_token, "nodrop")) + flags |= Q3SURFACEPARM_NODROP; + else if (!strcasecmp(com_token, "noimpact")) + flags |= Q3SURFACEPARM_NOIMPACT; + else if (!strcasecmp(com_token, "nolightmap")) + flags |= Q3SURFACEPARM_NOLIGHTMAP; + else if (!strcasecmp(com_token, "nomarks")) + flags |= Q3SURFACEPARM_NOMARKS; + else if (!strcasecmp(com_token, "nomipmaps")) + flags |= Q3SURFACEPARM_NOMIPMAPS; + else if (!strcasecmp(com_token, "nonsolid")) + flags |= Q3SURFACEPARM_NONSOLID; + else if (!strcasecmp(com_token, "origin")) + flags |= Q3SURFACEPARM_ORIGIN; + else if (!strcasecmp(com_token, "playerclip")) + flags |= Q3SURFACEPARM_PLAYERCLIP; + else if (!strcasecmp(com_token, "sky")) + flags |= Q3SURFACEPARM_SKY; + else if (!strcasecmp(com_token, "slick")) + flags |= Q3SURFACEPARM_SLICK; + else if (!strcasecmp(com_token, "slime")) + flags |= Q3SURFACEPARM_SLIME; + else if (!strcasecmp(com_token, "structural")) + flags |= Q3SURFACEPARM_STRUCTURAL; + else if (!strcasecmp(com_token, "trans")) + flags |= Q3SURFACEPARM_TRANS; + else if (!strcasecmp(com_token, "water")) + flags |= Q3SURFACEPARM_WATER; + else + Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token); + if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n")) + { + Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]); + goto parseerror; + } + } + else + { + Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]); + 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 } + while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}")); + // break out to top level if it was } + if (!strcasecmp(com_token, "}")) + break; + } + } + // 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 + { + Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token); + goto parseerror; + } + } +parseerror: + Mem_Free(f); + } + } } + + c = 0; + for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++) + { + if (out->surfaceparms == -1) + { + c++; + Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name); + out->surfaceparms = 0; + // these are defaults + if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk") + || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw")) + out->surfaceparms |= Q3SURFACEPARM_NODRAW; + if (!strncmp(out->name, "textures/skies/", 15)) + out->surfaceparms |= Q3SURFACEPARM_SKY; + if (R_TextureHasAlpha(out->skin.base)) + out->surfaceparms |= Q3SURFACEPARM_TRANS; + } + } + Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c); } static void Mod_Q3BSP_LoadPlanes(lump_t *l) @@ -3363,8 +3819,9 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l) { q3dbrush_t *in; q3mbrush_t *out; - int i, j, n, c, count, numplanes, maxplanes; + int i, j, n, c, count, maxplanes; mplane_t *planes; + winding_t *temp1, *temp2; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -3375,6 +3832,9 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l) loadmodel->brushq3.data_brushes = out; loadmodel->brushq3.num_brushes = count; + temp1 = Winding_New(64); + temp2 = Winding_New(64); + maxplanes = 0; planes = NULL; @@ -3392,9 +3852,9 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l) out->texture = loadmodel->brushq3.data_textures + n; // make a list of mplane_t structs to construct a colbrush from - if (maxplanes < numplanes) + if (maxplanes < out->numbrushsides) { - maxplanes = numplanes; + maxplanes = out->numbrushsides; if (planes) Mem_Free(planes); planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes); @@ -3405,10 +3865,12 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l) planes[j].dist = out->firstbrushside[j].plane->dist; } // make the colbrush from the planes - out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents); + out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2); } if (planes) Mem_Free(planes); + Winding_Free(temp1); + Winding_Free(temp2); } static void Mod_Q3BSP_LoadEffects(lump_t *l) @@ -3428,7 +3890,7 @@ static void Mod_Q3BSP_LoadEffects(lump_t *l) for (i = 0;i < count;i++, in++, out++) { - strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1); + strlcpy (out->shadername, in->shadername, sizeof (out->shadername)); n = LittleLong(in->brushindex); if (n < 0 || n >= loadmodel->brushq3.num_brushes) Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes); @@ -3446,13 +3908,13 @@ static void Mod_Q3BSP_LoadVertices(lump_t *l) if (l->filelen % sizeof(*in)) Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name); loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in); - loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); - loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2])); - loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2])); - loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); - loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); - loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); - loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4])); + loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4))); + loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3; + loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2; + loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2; + loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3; + loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3; + loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3; for (i = 0;i < count;i++, in++) { @@ -3490,17 +3952,20 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l) if (l->filelen % sizeof(int[3])) Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2); loadmodel->brushq3.num_triangles = count / 3; loadmodel->brushq3.data_element3i = out; - loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + loadmodel->brushq3.data_neighbor3i = out + count; for (i = 0;i < count;i++, in++, out++) { *out = LittleLong(*in); if (*out < 0 || *out >= loadmodel->brushq3.num_vertices) - Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices); + { + Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices); + *out = 0; + } } } @@ -3510,6 +3975,8 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l) rtexture_t **out; int i, count; + if (!l->filelen) + return; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name); @@ -3527,7 +3994,17 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) { q3dface_t *in; q3mface_t *out; - int i, j, n, count, invalidelements, patchsize[2]; + int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles; + //int *originalelement3i; + //int *originalneighbor3i; + float *originalvertex3f; + //float *originalsvector3f; + //float *originaltvector3f; + //float *originalnormal3f; + float *originalcolor4f; + float *originaltexcoordtexture2f; + float *originaltexcoordlightmap2f; + float *v; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -3547,7 +4024,9 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) && out->type != Q3FACETYPE_MESH && out->type != Q3FACETYPE_FLARE) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; } @@ -3555,7 +4034,9 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->textureindex); if (n < 0 || n >= loadmodel->brushq3.num_textures) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; n = 0; @@ -3564,7 +4045,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->effectindex); if (n < -1 || n >= loadmodel->brushq3.num_effects) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects); n = -1; } if (n == -1) @@ -3574,7 +4055,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->lightmapindex); if (n < -1 || n >= loadmodel->brushq3.num_lightmaps) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps); n = -1; } if (n == -1) @@ -3583,25 +4064,30 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n]; out->firstvertex = LittleLong(in->firstvertex); - out->numvertices = LittleLong(in->numvertices); + out->num_vertices = LittleLong(in->numvertices); out->firstelement = LittleLong(in->firstelement); - out->numelements = LittleLong(in->numelements); - out->numtriangles = out->numelements / 3; - if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices) + out->num_triangles = LittleLong(in->numelements) / 3; + if (out->num_triangles * 3 != LittleLong(in->numelements)) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices); + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements)); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; } - if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3) + if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3); + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; } - if (out->numtriangles * 3 != out->numelements) + if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements); + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; } @@ -3609,6 +4095,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) { case Q3FACETYPE_POLYGON: case Q3FACETYPE_MESH: + // no processing necessary out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3; out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2; out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2; @@ -3625,35 +4112,292 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) if (patchsize[0] < 1 || patchsize[1] < 1) { Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]); + out->num_vertices = 0; + out->num_triangles = 0; out->type = 0; // error continue; } - // FIXME: convert patch to triangles here! - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name); - out->type = 0; - continue; - break; - case Q3FACETYPE_FLARE: - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name); - out->type = 0; - continue; + originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3; + //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3; + //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3; + //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3; + originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2; + originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2; + originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4; + //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement; + //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement; + /* + originalvertex3f = out->data_vertex3f; + //originalsvector3f = out->data_svector3f; + //originaltvector3f = out->data_tvector3f; + //originalnormal3f = out->data_normal3f; + originalcolor4f = out->data_color4f; + originaltexcoordtexture2f = out->data_texcoordtexture2f; + originaltexcoordlightmap2f = out->data_texcoordlightmap2f; + //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; + out->data_normal3f = out->data_tvector3f + finalvertices * 3; + out->data_color4f = out->data_normal3f + finalvertices * 3; + out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4; + out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2; + out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles); + out->data_neighbor3i = out->data_element3i + finaltriangles * 3; + out->type = Q3FACETYPE_MESH; + out->firstvertex = -1; + out->num_vertices = finalvertices; + out->firstelement = -1; + out->num_triangles = finaltriangles; + // generate geometry + // (note: normals are skipped because they get recalculated) + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f); + // generate elements + e = out->data_element3i; + 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_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f); + if (developer.integer) + { + if (out->num_triangles < finaltriangles) + Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles); + else + 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); + // don't render it + out->num_vertices = 0; + out->num_triangles = 0; + out->type = 0; break; } - for (j = 0, invalidelements = 0;j < out->numelements;j++) - if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices) + for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++) + if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices) invalidelements++; if (invalidelements) { - Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->numvertices, out->firstelement, out->numelements); - for (j = 0;j < out->numelements;j++) + Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3); + for (j = 0;j < out->num_triangles * 3;j++) { Con_Printf(" %i", out->data_element3i[j]); - if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices) + 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); + // for per pixel lighting + Mod_BuildTextureVectorsAndNormals(out->num_vertices, out->num_triangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f); + // calculate a bounding box + VectorClear(out->mins); + VectorClear(out->maxs); + if (out->num_vertices) + { + VectorCopy(out->data_vertex3f, out->mins); + VectorCopy(out->data_vertex3f, out->maxs); + for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3) + { + out->mins[0] = min(out->mins[0], v[0]); + out->maxs[0] = max(out->maxs[0], v[0]); + out->mins[1] = min(out->mins[1], v[1]); + out->maxs[1] = max(out->maxs[1], v[1]); + out->mins[2] = min(out->mins[2], v[2]); + out->maxs[2] = max(out->maxs[2], v[2]); + } + out->mins[0] -= 1.0f; + out->mins[1] -= 1.0f; + out->mins[2] -= 1.0f; + out->maxs[0] += 1.0f; + out->maxs[1] += 1.0f; + out->maxs[2] += 1.0f; + } + } + + // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging) + /* + { + int totalverts, totaltris; + int originalnum_vertices; + float *originaldata_vertex3f; + float *originaldata_texcoordtexture2f; + float *originaldata_texcoordlightmap2f; + float *originaldata_svector3f; + float *originaldata_tvector3f; + float *originaldata_normal3f; + float *originaldata_color4f; + int originalnum_triangles; + int *originaldata_element3i; + int *originaldata_neighbor3i; + + totalverts = 0; + totaltris = 0; + for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++) + { + if (!out->type) + continue; + totalverts += out->num_vertices; + totaltris += out->num_triangles; + } + + originalnum_vertices = loadmodel->brushq3.num_vertices; + originaldata_vertex3f = loadmodel->brushq3.data_vertex3f; + originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f; + originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f; + originaldata_svector3f = loadmodel->brushq3.data_svector3f; + originaldata_tvector3f = loadmodel->brushq3.data_tvector3f; + originaldata_normal3f = loadmodel->brushq3.data_normal3f; + originaldata_color4f = loadmodel->brushq3.data_color4f; + originalnum_triangles = loadmodel->brushq3.num_triangles; + originaldata_element3i = loadmodel->brushq3.data_element3i; + originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i; + loadmodel->brushq3.num_vertices = totalverts; + loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2))); + loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3; + loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2; + loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2; + loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3; + loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3; + loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3; + loadmodel->brushq3.num_triangles = totaltris; + loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4); + loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3; + totalverts = 0; + totaltris = 0; + for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++) + { + if (!out->type) + continue; + Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris); + memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float)); + memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float)); + memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float)); + memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float)); + memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float)); + memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float)); + memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float)); + memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int)); + memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int)); + if (out->firstvertex == -1) + Mem_Free(out->data_vertex3f); + if (out->firstelement == -1) + Mem_Free(out->data_element3i); + out->firstvertex = totalverts; + out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3; + out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2; + out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2; + out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3; + out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3; + out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3; + out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4; + out->firstelement = totaltris * 3; + out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement; + out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement; + //for (j = 0;j < out->numtriangles * 3;j++) + // out->data_element3i[j] += totalverts - out->firstvertex; + totalverts += out->num_vertices; + totaltris += out->num_triangles; } + Mem_Free(originaldata_vertex3f); + Mem_Free(originaldata_element3i); + } + */ } static void Mod_Q3BSP_LoadModels(lump_t *l) @@ -3758,15 +4502,15 @@ static void Mod_Q3BSP_LoadLeafs(lump_t *l) for (i = 0;i < count;i++, in++, out++) { - out->isnode = false; out->parent = NULL; + out->plane = NULL; out->clusterindex = LittleLong(in->clusterindex); out->areaindex = LittleLong(in->areaindex); for (j = 0;j < 3;j++) { // yes the mins/maxs are ints - out->mins[j] = LittleLong(in->mins[j]); - out->maxs[j] = LittleLong(in->maxs[j]); + out->mins[j] = LittleLong(in->mins[j]) - 1; + out->maxs[j] = LittleLong(in->maxs[j]) + 1; } n = LittleLong(in->firstleafface); c = LittleLong(in->numleaffaces); @@ -3788,7 +4532,7 @@ static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *p if (node->parent) Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n"); node->parent = parent; - if (node->isnode) + if (node->plane) { Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node); Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node); @@ -3812,7 +4556,6 @@ static void Mod_Q3BSP_LoadNodes(lump_t *l) for (i = 0;i < count;i++, in++, out++) { - out->isnode = true; out->parent = NULL; n = LittleLong(in->planeindex); if (n < 0 || n >= loadmodel->brushq3.num_planes) @@ -3829,13 +4572,18 @@ static void Mod_Q3BSP_LoadNodes(lump_t *l) } else { - n = 1 - n; + n = -1 - n; if (n >= loadmodel->brushq3.num_leafs) Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs); out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n); } } - // we don't load the mins/maxs + for (j = 0;j < 3;j++) + { + // yes the mins/maxs are ints + out->mins[j] = LittleLong(in->mins[j]) - 1; + out->maxs[j] = LittleLong(in->maxs[j]) + 1; + } } // set the parent pointers @@ -3864,17 +4612,37 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1; loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1; count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2]; - 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)\n", l->filelen, 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\n", count * sizeof(*in), l->filelen); + 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)\n", l->filelen, 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\n", count * sizeof(*in), l->filelen); + } out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); loadmodel->brushq3.data_lightgrid = out; loadmodel->brushq3.num_lightgrid = count; // no swapping or validation necessary - memcpy(out, in, count * (int)sizeof(*out)); + if (l->filelen) + memcpy(out, in, count * (int)sizeof(*out)); + else + { + // no data, fill with white + int i; + for (i = 0;i < count;i++) + { + out[i].ambientrgb[0] = 128; + out[i].ambientrgb[1] = 128; + out[i].ambientrgb[2] = 128; + out[i].diffusergb[0] = 0; + out[i].diffusergb[1] = 0; + out[i].diffusergb[2] = 0; + out[i].diffusepitch = 0; + out[i].diffuseyaw = 0; + } + } Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]); Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]); @@ -3885,20 +4653,37 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l) q3dpvs_t *in; int totalchains; + if (l->filelen == 0) + { + int i; + // unvised maps often have cluster indices even without pvs, so check + // leafs to find real number of clusters + loadmodel->brush.num_pvsclusters = 1; + for (i = 0;i < loadmodel->brushq3.num_leafs;i++) + loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1); + + // create clusters + loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8; + totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters; + loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains); + memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains); + return; + } + in = (void *)(mod_base + l->fileofs); if (l->filelen < 9) Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name); - loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters); - loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength); - if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8)) - Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters); - totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters; + loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters); + loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength); + if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8)) + Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", 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)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, 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)\n", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen); - loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains); - memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains); + loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains); + memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains); } static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius) @@ -3907,125 +4692,739 @@ static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3 VectorCopy(in, out); } -static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end) +static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal) { - if (node->isnode) + int i, j, k, index[3]; + float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch; + q3dlightgrid_t *a, *s; + // FIXME: write this + if (!model->brushq3.num_lightgrid) { - // recurse down node sides - int i; - float dist; - colpointf_t *ps, *pe; - // FIXME? if TraceBrushPolygonTransform were to be made usable, the - // node planes would need to be transformed too - dist = node->plane->dist - (1.0f / 8.0f); - for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) + ambientcolor[0] = 1; + ambientcolor[1] = 1; + ambientcolor[2] = 1; + 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]); + transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1); + transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1); + transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1); + index[0] = (int)floor(transformed[0]); + 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]]; + for (k = 0;k < 2;k++) + { + blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2]))); + if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2]) + continue; + for (j = 0;j < 2;j++) { - if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist) + blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1]))); + if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1]) + continue; + for (i = 0;i < 2;i++) { - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end); - break; + blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0]))); + 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; + VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor); + VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor); + pitch = s->diffusepitch * M_PI / 128; + yaw = s->diffuseyaw * M_PI / 128; + sinpitch = sin(pitch); + diffusenormal[0] += blend * (cos(yaw) * sinpitch); + diffusenormal[1] += blend * (sin(yaw) * sinpitch); + diffusenormal[2] += blend * (cos(pitch)); + //Con_Printf("blend %f: ambient %i %i %i, diffuse %i %i %i, diffusepitch %i diffuseyaw %i (%f %f, normal %f %f %f)\n", blend, s->ambientrgb[0], s->ambientrgb[1], s->ambientrgb[2], s->diffusergb[0], s->diffusergb[1], s->diffusergb[2], s->diffusepitch, s->diffuseyaw, pitch, yaw, (cos(yaw) * cospitch), (sin(yaw) * cospitch), (-sin(pitch))); } } - dist = node->plane->dist + (1.0f / 8.0f); - for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) + } + 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]); +} + +static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe) +{ + int i; + q3mleaf_t *leaf; + colbrushf_t *brush; + // find which leaf the point is in + while (node->plane) + node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist]; + // point trace the brushes + leaf = (q3mleaf_t *)node; + for (i = 0;i < leaf->numleafbrushes;i++) + { + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs)) { - if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist) + brush->markframe = markframe; + Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf); + } + } + // can't do point traces on curves (they have no thickness) +} + +static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs) +{ + int i, startside, endside; + float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3]; + q3mleaf_t *leaf; + q3mface_t *face; + colbrushf_t *brush; + if (startfrac > trace->realfraction) + return; + // note: all line fragments past first impact fraction are ignored + if (VectorCompare(start, end)) + { + // find which leaf the point is in + while (node->plane) + node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist]; + } + else + { + // find which nodes the line is in and recurse for them + while (node->plane) + { + // recurse down node sides + dist1 = PlaneDiff(start, node->plane); + dist2 = PlaneDiff(end, node->plane); + startside = dist1 < 0; + endside = dist2 < 0; + if (startside == endside) { - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end); - break; + // most of the time the line fragment is on one side of the plane + node = node->children[startside]; + } + else + { + // line crosses node plane, split the line + midfrac = dist1 / (dist1 - dist2); + VectorLerp(start, midfrac, end, mid); + // take the near side first + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); + if (midfrac <= trace->realfraction) + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); + return; } } - /* - sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane); - if (sides & 1) - Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs); - if (sides & 2) - Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs); - */ } - else + // hit a leaf + nodesegmentmins[0] = min(start[0], end[0]); + nodesegmentmins[1] = min(start[1], end[1]); + nodesegmentmins[2] = min(start[2], end[2]); + nodesegmentmaxs[0] = max(start[0], end[0]); + nodesegmentmaxs[1] = max(start[1], end[1]); + nodesegmentmaxs[2] = max(start[2], end[2]); + // line trace the brushes + leaf = (q3mleaf_t *)node; + for (i = 0;i < leaf->numleafbrushes;i++) { - int i; - q3mleaf_t *leaf; - leaf = (q3mleaf_t *)node; - for (i = 0;i < leaf->numleafbrushes;i++) - if (leaf->firstleafbrush[i]->colbrushf) - Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs)) + { + brush->markframe = markframe; + Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + if (startfrac > trace->realfraction) + return; + } + } + // can't do point traces on curves (they have no thickness) + if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end)) + { + // line trace the curves + for (i = 0;i < leaf->numleaffaces;i++) + { + face = leaf->firstleafface[i]; + if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs)) + { + face->collisionmarkframe = markframe; + Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + if (startfrac > trace->realfraction) + return; + } + } } } -static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal) +static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs) { - // FIXME: write this - ambientcolor[0] += 255; - ambientcolor[1] += 255; - ambientcolor[2] += 255; + int i; + //int sides; + float nodesegmentmins[3], nodesegmentmaxs[3]; + q3mleaf_t *leaf; + colbrushf_t *brush; + q3mface_t *face; + /* + // find which nodes the line is in and recurse for them + while (node->plane) + { + // recurse down node sides + int startside, endside; + float dist1near, dist1far, dist2near, dist2far; + BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far); + BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far); + startside = dist1near < 0; + startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0); + endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0); + if (startside == 2 || endside == 2) + { + // brushes cross plane + // do not clip anything, just take both sides + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + if (startside == 0) + { + if (endside == 0) + { + node = node->children[0]; + continue; + } + else + { + //midf0 = dist1near / (dist1near - dist2near); + //midf1 = dist1far / (dist1far - dist2far); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + } + else + { + if (endside == 0) + { + //midf0 = dist1near / (dist1near - dist2near); + //midf1 = dist1far / (dist1far - dist2far); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + else + { + node = node->children[1]; + continue; + } + } + + if (dist1near < 0 && dist2near < 0 && dist1far < 0 && dist2far < 0){node = node->children[1];continue;} + if (dist1near < 0 && dist2near < 0 && dist1far < 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near < 0 && dist2near < 0 && dist1far >= 0 && dist2far < 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near < 0 && dist2near < 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near < 0 && dist2near >= 0 && dist1far < 0 && dist2far < 0){node = node->children[1];continue;} + if (dist1near < 0 && dist2near >= 0 && dist1far < 0 && dist2far >= 0){} + if (dist1near < 0 && dist2near >= 0 && dist1far >= 0 && dist2far < 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near < 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near >= 0 && dist2near < 0 && dist1far < 0 && dist2far < 0){node = node->children[1];continue;} + if (dist1near >= 0 && dist2near < 0 && dist1far < 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near >= 0 && dist2near < 0 && dist1far >= 0 && dist2far < 0){} + if (dist1near >= 0 && dist2near < 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near >= 0 && dist2near >= 0 && dist1far < 0 && dist2far < 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;} + if (dist1near >= 0 && dist2near >= 0 && dist1far < 0 && dist2far >= 0){node = node->children[0];continue;} + if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far < 0){node = node->children[0];continue;} + if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;} + { + if (dist2near < 0) // d1n<0 && d2n<0 + { + if (dist2near < 0) // d1n<0 && d2n<0 + { + if (dist2near < 0) // d1n<0 && d2n<0 + { + } + else // d1n<0 && d2n>0 + { + } + } + else // d1n<0 && d2n>0 + { + if (dist2near < 0) // d1n<0 && d2n<0 + { + } + else // d1n<0 && d2n>0 + { + } + } + } + else // d1n<0 && d2n>0 + { + } + } + else // d1n>0 + { + if (dist2near < 0) // d1n>0 && d2n<0 + { + } + else // d1n>0 && d2n>0 + { + } + } + if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0) + { + node = node->children[startside]; + continue; + } + if (dist1near < dist2near) + { + // out + if (dist1near >= 0) + { + node = node->children[0]; + continue; + } + if (dist2far < 0) + { + node = node->children[1]; + continue; + } + // dist1near < 0 && dist2far >= 0 + } + else + { + // in + } + startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0); + endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0); + if (startside == 2 || endside == 2) + { + // brushes cross plane + // do not clip anything, just take both sides + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } + else if (startside == endside) + node = node->children[startside]; + else if (startside == 0) // endside = 1 (start infront, end behind) + { + } + else // startside == 1 endside = 0 (start behind, end infront) + { + } + == endside) + { + if (startside < 2) + node = node->children[startside]; + else + { + // start and end brush cross plane + } + } + else + { + } + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + node = node->children[1]; + else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0) + else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0) + else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0) + node = node->children[0]; + else + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + { + } + else if (dist1near >= 0 && dist1far >= 0) + { + } + else // mixed (lying on plane) + { + } + { + if (dist2near < 0 && dist2far < 0) + { + } + else + node = node->children[1]; + } + if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0) + node = node->children[0]; + else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0) + node = node->children[1]; + else + { + // both sides + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } + sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist + startside = dist1 < 0; + endside = dist2 < 0; + if (startside == endside) + { + // most of the time the line fragment is on one side of the plane + node = node->children[startside]; + } + else + { + // line crosses node plane, split the line + midfrac = dist1 / (dist1 - dist2); + VectorLerp(start, midfrac, end, mid); + // take the near side first + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); + if (midfrac <= trace->fraction) + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); + return; + } + } + */ +#if 1 + for (;;) + { + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (!node->plane) + break; + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } +#elif 0 + // FIXME: could be made faster by copying TraceLine code and making it use + // box plane distances... (variant on the BoxOnPlaneSide code) + for (;;) + { + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (!node->plane) + break; + if (mod_q3bsp_debugtracebrush.integer == 2) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + else if (mod_q3bsp_debugtracebrush.integer == 1) + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + // take whichever side the segment box is on + node = node->children[sides - 1]; + continue; + } + else + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently... + sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane); + if (sides == 3) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + } + // take whichever side the segment box is on + node = node->children[sides - 1]; + continue; + } + return; + } +#else + // FIXME: could be made faster by copying TraceLine code and making it use + // box plane distances... (variant on the BoxOnPlaneSide code) + for (;;) + { + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (!node->plane) + break; + if (mod_q3bsp_debugtracebrush.integer == 2) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } + else if (mod_q3bsp_debugtracebrush.integer == 1) + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } + else + { + // take whichever side the segment box is on + node = node->children[sides - 1]; + } + } + else + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently... + sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane); + if (sides == 3) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + sides = 2; + } + } + // take whichever side the segment box is on + node = node->children[sides - 1]; + } + } +#endif + // hit a leaf + leaf = (q3mleaf_t *)node; + for (i = 0;i < leaf->numleafbrushes;i++) + { + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs)) + { + brush->markframe = markframe; + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + } + } + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < leaf->numleaffaces;i++) + { + face = leaf->firstleafface[i]; + 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_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } + } } -static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) +static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) { int i; + float segmentmins[3], segmentmaxs[3]; colbrushf_t *thisbrush_start, *thisbrush_end; matrix4x4_t startmatrix, endmatrix; - // FIXME: finish this code - Matrix4x4_CreateIdentity(&startmatrix); - Matrix4x4_CreateIdentity(&endmatrix); - thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs); - thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs); + static int markframe = 0; + q3mface_t *face; memset(trace, 0, sizeof(*trace)); trace->fraction = 1; + trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; - if (model->brushq3.num_nodes) - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end); + Matrix4x4_CreateIdentity(&startmatrix); + Matrix4x4_CreateIdentity(&endmatrix); + segmentmins[0] = min(boxstartmins[0], boxendmins[0]); + segmentmins[1] = min(boxstartmins[1], boxendmins[1]); + segmentmins[2] = min(boxstartmins[2], boxendmins[2]); + segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]); + segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]); + segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]); + if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs)) + { + if (VectorCompare(boxstartmins, boxendmins)) + { + // point trace + if (model->brushq3.submodel) + { + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) + Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + } + else + Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe); + } + else + { + // line trace + if (model->brushq3.submodel) + { + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) + Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } + } + else + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs); + } + } else - for (i = 0;i < model->brushq3.num_brushes;i++) - if (model->brushq3.data_brushes[i].colbrushf) - Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf); -} - - -static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) -{ - int clusterindex; -loc0: - if (!node->isnode) { - // leaf - clusterindex = ((q3mleaf_t *)node)->clusterindex; - return pvs[clusterindex >> 3] & (1 << (clusterindex & 7)); + // box trace, performed as brush trace + thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs); + thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs); + if (model->brushq3.submodel) + { + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } + } + else + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs); } +} - // node - recurse down the BSP tree - switch (BoxOnPlaneSide(mins, maxs, node->plane)) +static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +{ + int clusterindex, side, nodestackindex = 0; + q3mnode_t *node, *nodestack[1024]; + node = model->brushq3.data_nodes; + if (!loadmodel->brush.num_pvsclusters) + return true; + for (;;) { - case 1: // front - node = node->children[0]; - goto loc0; - case 2: // back - node = node->children[1]; - goto loc0; - default: // crossing - if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) - return true; - node = node->children[1]; - goto loc0; + if (node->plane) + { + // node - recurse down the BSP tree + side = BoxOnPlaneSide(mins, maxs, node->plane) - 1; + if (side < 2) + { + // box is on one side of plane, take that path + node = node->children[side]; + } + else + { + // box crosses plane, take one path and remember the other + nodestack[nodestackindex++] = node->children[0]; + node = node->children[1]; + } + } + else + { + // leaf - check cluster bit + clusterindex = ((q3mleaf_t *)node)->clusterindex; +#if 0 + if (clusterindex >= loadmodel->brush.num_pvsclusters) + { + Con_Printf("%i >= %i\n", clusterindex, loadmodel->brush.num_pvsclusters); + return true; + } +#endif + if (CHECKPVSBIT(pvs, clusterindex)) + { + // it is visible, return immediately with the news + return true; + } + else + { + // nothing to see here, try another path we didn't take earlier + if (nodestackindex == 0) + break; + node = nodestack[--nodestackindex]; + } + } } - // never reached + // it is not visible return false; } -static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +//Returns PVS data for a given point +//(note: can return NULL) +static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p) { - return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs); + q3mnode_t *node; + Mod_CheckLoaded(model); + node = model->brushq3.data_nodes; + while (node->plane) + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; + if (((q3mleaf_t *)node)->clusterindex >= 0) + return model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes; + else + return NULL; } +static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node) +{ + while (node->plane) + { + float d = PlaneDiff(org, node->plane); + if (d > radius) + node = node->children[0]; + else if (d < -radius) + node = node->children[1]; + else + { + // go down both sides + Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]); + node = node->children[1]; + } + } + // if this leaf is in a cluster, accumulate the pvs bits + if (((q3mleaf_t *)node)->clusterindex >= 0) + { + int i; + qbyte *pvs = model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes; + for (i = 0;i < pvsbytes;i++) + pvsbuffer[i] |= pvs[i]; + } +} + +//Calculates a PVS that is the inclusive or of all leafs within radius pixels +//of the given point. static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) { - // FIXME: write this - memset(pvsbuffer, 0xFF, pvsbufferlength); - return pvsbufferlength; + int bytes = model->brush.num_pvsclusterbytes; + bytes = min(bytes, pvsbufferlength); + if (r_novis.integer || !loadmodel->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org)) + { + memset(pvsbuffer, 0xFF, bytes); + return bytes; + } + memset(pvsbuffer, 0, bytes); + Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes); + return bytes; } + static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents) { int supercontents = 0; @@ -4054,18 +5453,83 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco return nativecontents; } -//extern void R_Q3BSP_DrawSky(struct entity_render_s *ent); +/* +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_DrawFakeShadow(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_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz); +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; + int i, j, numshadowmeshtriangles; q3dheader_t *header; float corner[3], yawradius, modelradius; + q3mface_t *face; mod->type = mod_brushq3; + mod->numframes = 1; + mod->numskins = 1; header = (q3dheader_t *)buffer; @@ -4079,18 +5543,19 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer) R_ResetQuakeSky(); } + mod->soundfromcenter = true; + mod->TraceBox = Mod_Q3BSP_TraceBox; mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents; mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents; + mod->brush.GetPVS = Mod_Q3BSP_GetPVS; mod->brush.FatPVS = Mod_Q3BSP_FatPVS; mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS; mod->brush.LightPoint = Mod_Q3BSP_LightPoint; mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation; - mod->brush.TraceBox = Mod_Q3BSP_TraceBox; //mod->DrawSky = R_Q3BSP_DrawSky; mod->Draw = R_Q3BSP_Draw; - //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow; - //mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume; - //mod->DrawLight = R_Q3BSP_DrawLight; + mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume; + mod->DrawLight = R_Q3BSP_DrawLight; mod_base = (qbyte *)header; @@ -4117,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) @@ -4136,8 +5614,14 @@ 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; VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins); VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs); @@ -4154,6 +5638,12 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer) mod->yawmaxs[2] = mod->normalmaxs[2]; mod->radius = modelradius; mod->radius2 = modelradius * modelradius; + + for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++) + if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY) + break; + if (j < mod->brushq3.data_thismodel->numfaces) + mod->DrawSky = R_Q3BSP_DrawSky; } }