From 09f884dd87cbb5b3da9e261d8c0841cdb9365c5b Mon Sep 17 00:00:00 2001
From: divverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Date: Tue, 29 Jun 2010 06:34:05 +0000
Subject: [PATCH] speed up decal creation in newdecalsystem by using BIH for
 finding the affected triangles

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10254 d7cf8633-e32d-0410-b094-e92efae38249
---
 bih.c          |  16 ++--
 bih.h          |   2 +-
 gl_rmain.c     | 206 ++++++++++++++++++++++++++++++-------------------
 model_alias.c  |  14 ++--
 model_brush.c  |  26 ++++---
 model_shared.h |   3 +-
 6 files changed, 162 insertions(+), 105 deletions(-)

diff --git a/bih.c b/bih.c
index 58cc0715..d5044bff 100644
--- a/bih.c
+++ b/bih.c
@@ -142,7 +142,7 @@ int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_nod
 	return bih->error;
 }
 
-static void BIH_GetTriangleListForBox_Node(const bih_t *bih, int nodenum, int maxtriangles, int *trianglelist, int *numtrianglespointer, const float *mins, const float *maxs)
+static void BIH_GetTriangleListForBox_Node(const bih_t *bih, int nodenum, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, int *numtrianglespointer, const float *mins, const float *maxs)
 {
 	int axis;
 	bih_node_t *node;
@@ -154,7 +154,7 @@ static void BIH_GetTriangleListForBox_Node(const bih_t *bih, int nodenum, int ma
 		if (mins[axis] < node->backmax)
 		{
 			if (maxs[axis] > node->frontmin)
-				BIH_GetTriangleListForBox_Node(bih, node->front, maxtriangles, trianglelist, numtrianglespointer, mins, maxs);
+				BIH_GetTriangleListForBox_Node(bih, node->front, maxtriangles, trianglelist_idx, trianglelist_surf, numtrianglespointer, mins, maxs);
 			nodenum = node->back;
 			continue;
 		}
@@ -175,17 +175,23 @@ static void BIH_GetTriangleListForBox_Node(const bih_t *bih, int nodenum, int ma
 	{
 	case BIH_RENDERTRIANGLE:
 		if (*numtrianglespointer >= maxtriangles)
+		{
+			++*numtrianglespointer; // so the caller can detect overflow
 			return;
-		trianglelist[(*numtrianglespointer)++] = leaf->itemindex;
+		}
+		if(trianglelist_surf)
+			trianglelist_surf[*numtrianglespointer] = leaf->surfaceindex;
+		trianglelist_idx[*numtrianglespointer] = leaf->itemindex;
+		++*numtrianglespointer;
 		break;
 	default:
 		break;
 	}
 }
 
-int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist, const float *mins, const float *maxs)
+int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, const float *mins, const float *maxs)
 {
 	int numtriangles = 0;
-	BIH_GetTriangleListForBox_Node(bih, 0, maxtriangles, trianglelist, &numtriangles, mins, maxs);
+	BIH_GetTriangleListForBox_Node(bih, 0, maxtriangles, trianglelist_idx, trianglelist_surf, &numtriangles, mins, maxs);
 	return numtriangles;
 }
diff --git a/bih.h b/bih.h
index 8b796669..90ec71db 100644
--- a/bih.h
+++ b/bih.h
@@ -81,6 +81,6 @@ bih_t;
 
 int BIH_Build(bih_t *bih, int numleafs, bih_leaf_t *leafs, int maxnodes, bih_node_t *nodes, int *temp_leafsort, int *temp_leafsortscratch);
 
-int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist, const float *mins, const float *maxs);
+int BIH_GetTriangleListForBox(const bih_t *bih, int maxtriangles, int *trianglelist_idx, int *trianglelist_surf, const float *mins, const float *maxs);
 
 #endif
diff --git a/gl_rmain.c b/gl_rmain.c
index 4071854a..0234fdfb 100644
--- a/gl_rmain.c
+++ b/gl_rmain.c
@@ -12226,13 +12226,92 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
 extern cvar_t cl_decals_bias;
 extern cvar_t cl_decals_models;
 extern cvar_t cl_decals_newsystem_intensitymultiplier;
+// baseparms, parms, temps
+static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, float g, float b, float a, float s1, float t1, float s2, float t2, int decalsequence, qboolean dynamic, float (*planes)[4], matrix4x4_t *projection, int triangleindex, int surfaceindex)
+{
+	int cornerindex;
+	int index;
+	float v[9][3];
+	const float *vertex3f;
+	int numpoints;
+	float points[2][9][3];
+	float temp[3];
+	float tc[9][2];
+	float f;
+	float c[9][4];
+	const int *e;
+
+	e = rsurface.modelelement3i + 3*triangleindex;
+
+	vertex3f = rsurface.modelvertex3f;
+
+	for (cornerindex = 0;cornerindex < 3;cornerindex++)
+	{
+		index = 3*e[cornerindex];
+		VectorCopy(vertex3f + index, v[cornerindex]);
+	}
+	// cull backfaces
+	//TriangleNormal(v[0], v[1], v[2], normal);
+	//if (DotProduct(normal, localnormal) < 0.0f)
+	//	continue;
+	// clip by each of the box planes formed from the projection matrix
+	// if anything survives, we emit the decal
+	numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+	if (numpoints < 3)
+		return;
+	numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+	if (numpoints < 3)
+		return;
+	numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+	if (numpoints < 3)
+		return;
+	numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+	if (numpoints < 3)
+		return;
+	numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+	if (numpoints < 3)
+		return;
+	numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
+	if (numpoints < 3)
+		return;
+	// some part of the triangle survived, so we have to accept it...
+	if (dynamic)
+	{
+		// dynamic always uses the original triangle
+		numpoints = 3;
+		for (cornerindex = 0;cornerindex < 3;cornerindex++)
+		{
+			index = 3*e[cornerindex];
+			VectorCopy(vertex3f + index, v[cornerindex]);
+		}
+	}
+	for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
+	{
+		// convert vertex positions to texcoords
+		Matrix4x4_Transform(projection, v[cornerindex], temp);
+		tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
+		tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
+		// calculate distance fade from the projection origin
+		f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
+		f = bound(0.0f, f, 1.0f);
+		c[cornerindex][0] = r * f;
+		c[cornerindex][1] = g * f;
+		c[cornerindex][2] = b * f;
+		c[cornerindex][3] = 1.0f;
+		//VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
+	}
+	if (dynamic)
+		R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex, surfaceindex, decalsequence);
+	else
+		for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
+			R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+}
 static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, int decalsequence)
 {
 	matrix4x4_t projection;
 	decalsystem_t *decalsystem;
 	qboolean dynamic;
 	dp_model_t *model;
-	const float *vertex3f;
 	const msurface_t *surface;
 	const msurface_t *surfaces;
 	const int *surfacelist;
@@ -12242,24 +12321,18 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
 	int surfacelistindex;
 	int surfaceindex;
 	int triangleindex;
-	int cornerindex;
-	int index;
-	int numpoints;
-	const int *e;
 	float localorigin[3];
 	float localnormal[3];
 	float localmins[3];
 	float localmaxs[3];
 	float localsize;
-	float v[9][3];
-	float tc[9][2];
-	float c[9][4];
 	//float normal[3];
 	float planes[6][4];
-	float f;
-	float points[2][9][3];
 	float angles[3];
-	float temp[3];
+	bih_t *bih;
+	int bih_triangles_count;
+	int bih_triangles[256];
+	int bih_surfaces[256];
 
 	decalsystem = &ent->decalsystem;
 	model = ent->model;
@@ -12338,86 +12411,57 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
 #endif
 
 	dynamic = model->surfmesh.isanimated;
-	vertex3f = rsurface.modelvertex3f;
 	numsurfacelist = model->nummodelsurfaces;
 	surfacelist = model->sortedmodelsurfaces;
 	surfaces = model->data_surfaces;
-	for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+
+	bih = NULL;
+	bih_triangles_count = -1;
+	if(!dynamic)
 	{
-		surfaceindex = surfacelist[surfacelistindex];
-		surface = surfaces + surfaceindex;
-		// check cull box first because it rejects more than any other check
-		if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
-			continue;
-		// skip transparent surfaces
-		texture = surface->texture;
-		if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
-			continue;
-		if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
-			continue;
-		numtriangles = surface->num_triangles;
-		for (triangleindex = 0, e = rsurface.modelelement3i + 3*surface->num_firsttriangle;triangleindex < numtriangles;triangleindex++, e += 3)
+		if(model->render_bih.numleafs)
+			bih = &model->render_bih;
+		else if(model->collision_bih.numleafs)
+			bih = &model->collision_bih;
+	}
+	if(bih)
+		bih_triangles_count = BIH_GetTriangleListForBox(bih, sizeof(bih_triangles) / sizeof(*bih_triangles), bih_triangles, bih_surfaces, localmins, localmaxs);
+	if(bih_triangles_count == 0)
+		return;
+	if(bih_triangles_count > (int) (sizeof(bih_triangles) / sizeof(*bih_triangles))) // hit too many, likely bad anyway
+		return;
+	if(bih_triangles_count > 0)
+	{
+		for (triangleindex = 0; triangleindex < bih_triangles_count; ++triangleindex)
 		{
-			for (cornerindex = 0;cornerindex < 3;cornerindex++)
-			{
-				index = 3*e[cornerindex];
-				VectorCopy(vertex3f + index, v[cornerindex]);
-			}
-			// cull backfaces
-			//TriangleNormal(v[0], v[1], v[2], normal);
-			//if (DotProduct(normal, localnormal) < 0.0f)
-			//	continue;
-			// clip by each of the box planes formed from the projection matrix
-			// if anything survives, we emit the decal
-			numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-			if (numpoints < 3)
-				continue;
-			numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-			if (numpoints < 3)
+			surfaceindex = bih_surfaces[triangleindex];
+			surface = surfaces + surfaceindex;
+			texture = surface->texture;
+			if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
 				continue;
-			numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-			if (numpoints < 3)
+			if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
 				continue;
-			numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-			if (numpoints < 3)
+			R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, bih_triangles[triangleindex], surfaceindex);
+		}
+	}
+	else
+	{
+		for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+		{
+			surfaceindex = surfacelist[surfacelistindex];
+			surface = surfaces + surfaceindex;
+			// check cull box first because it rejects more than any other check
+			if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
 				continue;
-			numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-			if (numpoints < 3)
+			// skip transparent surfaces
+			texture = surface->texture;
+			if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
 				continue;
-			numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
-			if (numpoints < 3)
+			if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
 				continue;
-			// some part of the triangle survived, so we have to accept it...
-			if (dynamic)
-			{
-				// dynamic always uses the original triangle
-				numpoints = 3;
-				for (cornerindex = 0;cornerindex < 3;cornerindex++)
-				{
-					index = 3*e[cornerindex];
-					VectorCopy(vertex3f + index, v[cornerindex]);
-				}
-			}
-			for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
-			{
-				// convert vertex positions to texcoords
-				Matrix4x4_Transform(&projection, v[cornerindex], temp);
-				tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
-				tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
-				// calculate distance fade from the projection origin
-				f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
-				f = bound(0.0f, f, 1.0f);
-				c[cornerindex][0] = r * f;
-				c[cornerindex][1] = g * f;
-				c[cornerindex][2] = b * f;
-				c[cornerindex][3] = 1.0f;
-				//VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
-			}
-			if (dynamic)
-				R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex+surface->num_firsttriangle, surfaceindex, decalsequence);
-			else
-				for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
-					R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+			numtriangles = surface->num_triangles;
+			for (triangleindex = 0; triangleindex < numtriangles; triangleindex++)
+				R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, triangleindex + surface->num_firsttriangle, surfaceindex);
 		}
 	}
 }
diff --git a/model_alias.c b/model_alias.c
index df49c9e6..9cf5e50c 100644
--- a/model_alias.c
+++ b/model_alias.c
@@ -1323,7 +1323,7 @@ void Mod_IDP0_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -1580,7 +1580,7 @@ void Mod_IDP2_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -1760,7 +1760,7 @@ void Mod_IDP3_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -2132,7 +2132,7 @@ void Mod_ZYMOTICMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -2482,7 +2482,7 @@ void Mod_DARKPLACESMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -3063,7 +3063,7 @@ void Mod_PSKMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
@@ -3557,7 +3557,7 @@ void Mod_INTERQUAKEMODEL_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 	if (!loadmodel->surfmesh.isanimated)
 	{
-		Mod_MakeCollisionBIH(loadmodel, true);
+		Mod_MakeCollisionBIH(loadmodel, true, &loadmodel->collision_bih);
 		loadmodel->TraceBox = Mod_CollisionBIH_TraceBox;
 		loadmodel->TraceLine = Mod_CollisionBIH_TraceLine;
 		loadmodel->TracePoint = Mod_CollisionBIH_TracePoint_Mesh;
diff --git a/model_brush.c b/model_brush.c
index 7563dd38..1cadbecb 100644
--- a/model_brush.c
+++ b/model_brush.c
@@ -3738,11 +3738,13 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
 		if (mod_q1bsp_polygoncollisions.integer)
 		{
-			Mod_MakeCollisionBIH(mod, true);
+			Mod_MakeCollisionBIH(mod, true, &mod->collision_bih);
 			// point traces and contents checks still use the bsp tree
 			mod->TraceLine = Mod_CollisionBIH_TraceLine;
 			mod->TraceBox = Mod_CollisionBIH_TraceBox;
 		}
+		else
+			Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
 
 		// generate VBOs and other shared data before cloning submodels
 		if (i == 0)
@@ -6589,7 +6591,7 @@ static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const
 	return supercontents;
 }
 
-void Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces)
+bih_t *Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces, bih_t *out)
 {
 	int j;
 	int bihnumleafs;
@@ -6626,14 +6628,14 @@ void Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces)
 		for (j = 0, surface = model->data_surfaces + model->firstmodelsurface;j < nummodelsurfaces;j++, surface++)
 		{
 			if (surface->texture->basematerialflags & MATERIALFLAG_MESHCOLLISIONS)
-				bihnumleafs += surface->num_triangles;
+				bihnumleafs += surface->num_triangles + surface->num_collisiontriangles;
 			else
 				bihnumleafs += surface->num_collisiontriangles;
 		}
 	}
 
 	if (!bihnumleafs)
-		return;
+		return NULL;
 
 	// allocate the memory for the BIH leaf nodes
 	bihleafs = Mem_Alloc(loadmodel->mempool, sizeof(bih_leaf_t) * bihnumleafs);
@@ -6709,17 +6711,19 @@ void Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces)
 	temp_leafsortscratch = temp_leafsort + bihnumleafs;
 
 	// now build it
-	BIH_Build(&model->collision_bih, bihnumleafs, bihleafs, bihmaxnodes, bihnodes, temp_leafsort, temp_leafsortscratch);
+	BIH_Build(out, bihnumleafs, bihleafs, bihmaxnodes, bihnodes, temp_leafsort, temp_leafsortscratch);
 
 	// we're done with the temporary data
 	Mem_Free(temp_leafsort);
 
 	// resize the BIH nodes array if it over-allocated
-	if (model->collision_bih.maxnodes > model->collision_bih.numnodes)
+	if (out->maxnodes > out->numnodes)
 	{
-		model->collision_bih.maxnodes = model->collision_bih.numnodes;
-		model->collision_bih.nodes = Mem_Realloc(loadmodel->mempool, model->collision_bih.nodes, model->collision_bih.numnodes * sizeof(bih_node_t));
+		out->maxnodes = out->numnodes;
+		out->nodes = Mem_Realloc(loadmodel->mempool, out->nodes, out->numnodes * sizeof(bih_node_t));
 	}
+
+	return out;
 }
 
 static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents)
@@ -7035,7 +7039,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 		if (j < mod->nummodelsurfaces)
 			mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
 
-		Mod_MakeCollisionBIH(mod, false);
+		Mod_MakeCollisionBIH(mod, false, &mod->collision_bih);
+		Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
 
 		// generate VBOs and other shared data before cloning submodels
 		if (i == 0)
@@ -7472,7 +7477,8 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
 	Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, true);
 	Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
 
-	Mod_MakeCollisionBIH(loadmodel, true);
+	Mod_MakeCollisionBIH(loadmodel, true, &mod->collision_bih);
+	mod->render_bih = mod->collision_bih;
 }
 
 
diff --git a/model_shared.h b/model_shared.h
index f8a8dabe..8b15a6ef 100644
--- a/model_shared.h
+++ b/model_shared.h
@@ -960,6 +960,7 @@ typedef struct model_s
 	int				nummodelbrushes;
 	// BIH (Bounding Interval Hierarchy) for this (sub)model
 	bih_t			collision_bih;
+	bih_t			render_bih; // if not set, use collision_bih instead for rendering purposes too
 	// for md3 models
 	int				num_tags;
 	int				num_tagframes;
@@ -1169,7 +1170,7 @@ void Mod_CollisionBIH_TraceLine(dp_model_t *model, const struct frameblend_s *fr
 void Mod_CollisionBIH_TraceBox(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask);
 void Mod_CollisionBIH_TracePoint_Mesh(dp_model_t *model, const struct frameblend_s *frameblend, const skeleton_t *skeleton, struct trace_s *trace, const vec3_t start, int hitsupercontentsmask);
 int Mod_CollisionBIH_PointSuperContents_Mesh(struct model_s *model, int frame, const vec3_t point);
-void Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces);
+bih_t *Mod_MakeCollisionBIH(dp_model_t *model, qboolean userendersurfaces, bih_t *out);
 
 // alias models
 struct frameblend_s;
-- 
2.39.5