+static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
+{
+ // FIXME: finish this code
+ VectorCopy(in, out);
+}
+
+static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
+{
+ int i, j, k, index[3];
+ float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
+ q3dlightgrid_t *a, *s;
+ // FIXME: write this
+ if (!model->brushq3.num_lightgrid)
+ {
+ ambientcolor[0] += 128;
+ ambientcolor[1] += 128;
+ ambientcolor[2] += 128;
+ 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]);
+ transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
+ transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
+ 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])));
+ for (j = 0;j < 2;j++)
+ {
+ blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
+ for (i = 0;i < 2;i++)
+ {
+ blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
+ 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)));
+ }
+ }
+ }
+ 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_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)
+{
+ int i, startside, endside;
+ float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
+ q3mleaf_t *leaf;
+ q3mface_t *face;
+ colbrushf_t *brush;
+ if (startfrac >= trace->fraction)
+ return;
+ // note: all line fragments past first impact fraction are ignored
+ while (node->isnode)
+ {
+ // recurse down node sides
+ dist1 = PlaneDiff(start, node->plane);
+ dist2 = PlaneDiff(end, node->plane);
+ 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(linestart, midfrac, lineend, mid);
+ // take the near side first
+ Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
+ if (midfrac < trace->fraction)
+ Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
+ return;
+ }
+ }
+ // hit a leaf
+ segmentmins[0] = min(start[0], end[0]);
+ segmentmins[1] = min(start[1], end[1]);
+ segmentmins[2] = min(start[2], end[2]);
+ segmentmaxs[0] = max(start[0], end[0]);
+ segmentmaxs[1] = max(start[1], end[1]);
+ segmentmaxs[2] = max(start[2], end[2]);
+ leaf = (q3mleaf_t *)node;
+ for (i = 0;i < leaf->numleafbrushes;i++)
+ {
+ if (startfrac >= trace->fraction)
+ return;
+ brush = leaf->firstleafbrush[i]->colbrushf;
+ if (brush && brush->markframe != markframe)
+ {
+ brush->markframe = markframe;
+ if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
+ Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+ }
+ }
+ if (mod_q3bsp_curves_collisions.integer)
+ {
+ for (i = 0;i < leaf->numleaffaces;i++)
+ {
+ if (startfrac >= trace->fraction)
+ return;
+ face = leaf->firstleafface[i];
+ if (face->collisions && face->collisionmarkframe != markframe)
+ {
+ face->collisionmarkframe = markframe;
+ if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
+ Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ }
+ }
+ }
+}
+
+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)
+{
+ int i, sides;
+ float nodesegmentmins[3], nodesegmentmaxs[3];
+ q3mleaf_t *leaf;
+ colbrushf_t *brush;
+ q3mface_t *face;
+ 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->isnode)
+ {
+ // recurse down node sides
+ sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
+ if (sides == 3)
+ {
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
+ }
+ else if (sides == 2)
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+ else // sides == 1
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+ /*
+ 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++)
+ {
+ if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
+ {
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
+ break;
+ }
+ }
+ */
+ /*
+ 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++)
+ {
+ if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
+ {
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
+ break;
+ }
+ }
+ */
+ /*
+ 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
+ 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];
+ // note: this can not be optimized with a face->collisionmarkframe because each triangle of the face would need to be marked as done individually (because each one is bbox culled individually), and if all are marked, then the face could be marked as done
+ if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
+ Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, 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)
+{
+ int i;
+ float segmentmins[3], segmentmaxs[3];
+ colbrushf_t *thisbrush_start, *thisbrush_end;
+ matrix4x4_t startmatrix, endmatrix;
+ static int markframe = 0;
+ q3mface_t *face;
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = 1;
+ trace->hitsupercontentsmask = hitsupercontentsmask;
+ 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))
+ {
+ // 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->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ }
+ }
+ }
+ else
+ Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
+ }
+ else
+ {
+ // 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->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+ }
+ }
+ }
+ else
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
+ }
+}
+
+
+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));
+ }
+
+ // node - recurse down the BSP tree
+ switch (BoxOnPlaneSide(mins, maxs, node->plane))
+ {
+ 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;
+ }
+ // never reached
+ return false;
+}
+
+static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
+{
+ return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
+}
+
+//Returns PVS data for a given point
+//(note: can return NULL)
+static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
+{
+ q3mnode_t *node;
+ Mod_CheckLoaded(model);
+ node = model->brushq3.data_nodes;
+ while (node->isnode)
+ 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->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
+ 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)
+{
+ int i;
+ float d;
+ qbyte *pvs;
+
+ while (node->isnode)
+ {
+ 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 is a leaf with a pvs, accumulate the pvs bits
+ if (((q3mleaf_t *)node)->clusterindex >= 0)
+ {
+ pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
+ for (i = 0;i < pvsbytes;i++)
+ pvsbuffer[i] |= pvs[i];
+ }
+ return;
+}
+
+//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)
+{
+ int bytes = model->brushq3.num_pvschainlength;
+ bytes = min(bytes, pvsbufferlength);
+ 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;
+ if (nativecontents & Q2CONTENTS_SOLID)
+ supercontents |= SUPERCONTENTS_SOLID;
+ if (nativecontents & Q2CONTENTS_WATER)
+ supercontents |= SUPERCONTENTS_WATER;
+ if (nativecontents & Q2CONTENTS_SLIME)
+ supercontents |= SUPERCONTENTS_SLIME;
+ if (nativecontents & Q2CONTENTS_LAVA)
+ supercontents |= SUPERCONTENTS_LAVA;
+ return supercontents;
+}
+
+static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
+{
+ int nativecontents = 0;
+ if (supercontents & SUPERCONTENTS_SOLID)
+ nativecontents |= Q2CONTENTS_SOLID;
+ if (supercontents & SUPERCONTENTS_WATER)
+ nativecontents |= Q2CONTENTS_WATER;
+ if (supercontents & SUPERCONTENTS_SLIME)
+ nativecontents |= Q2CONTENTS_SLIME;
+ if (supercontents & SUPERCONTENTS_LAVA)
+ nativecontents |= Q2CONTENTS_LAVA;
+ return nativecontents;
+}
+
+//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);