]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
don't crash if the model produces no shadowmesh (no geometry)
[xonotic/darkplaces.git] / model_brush.c
index 7b965f450895a72e8e1fccb8b5b50c5537c97327..c2f5320cd12ec5fa29af574d15a9158dadec914e 100644 (file)
@@ -522,17 +522,17 @@ static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *in
                surface = info->model->data_surfaces + *mark;
                if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
                {
-                       if(surface->num_bboxstride > 0)
+                       if(surface->deprecatedq3num_bboxstride > 0)
                        {
                                int i, cnt, tri;
-                               cnt = (surface->num_triangles + surface->num_bboxstride - 1) / surface->num_bboxstride;
+                               cnt = (surface->num_triangles + surface->deprecatedq3num_bboxstride - 1) / surface->deprecatedq3num_bboxstride;
                                for(i = 0; i < cnt; ++i)
                                {
-                                       if(BoxesOverlap(surface->data_bbox6f + i * 6, surface->data_bbox6f + i * 6 + 3, info->absmin, info->absmax))
+                                       if(BoxesOverlap(surface->deprecatedq3data_bbox6f + i * 6, surface->deprecatedq3data_bbox6f + i * 6 + 3, info->absmin, info->absmax))
                                        {
-                                               for(k = 0; k < surface->num_bboxstride; ++k)
+                                               for(k = 0; k < surface->deprecatedq3num_bboxstride; ++k)
                                                {
-                                                       tri = i * surface->num_bboxstride + k;
+                                                       tri = i * surface->deprecatedq3num_bboxstride + k;
                                                        if(tri >= surface->num_triangles)
                                                                break;
                                                        Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, tri);
@@ -1567,7 +1567,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        // LordHavoc: HL sky textures are entirely different than quake
                        if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == mtheight * 2)
                        {
-                               data = loadimagepixelsbgra(tx->name, false, false);
+                               data = loadimagepixelsbgra(tx->name, false, false, r_texture_convertsRGB_skin.integer);
                                if (data && image_width == image_height * 2)
                                {
                                        R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
@@ -3402,7 +3402,6 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        int i, j, k;
        dheader_t *header;
        dmodel_t *bm;
-       mempool_t *mainmempool;
        float dist, modelyawradius, modelradius;
        msurface_t *surface;
        int numshadowmeshtriangles;
@@ -3532,8 +3531,6 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->numframes = 2;             // regular and alternate animation
        mod->numskins = 1;
 
-       mainmempool = mod->mempool;
-
        // make a single combined shadow mesh to allow optimized shadow volume creation
        numshadowmeshtriangles = 0;
        for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
@@ -4481,8 +4478,16 @@ 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 = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
 
+       if(!loadmodel->brushq3.num_vertices)
+       {
+               if (count)
+                       Con_Printf("Mod_Q3BSP_LoadTriangles: %s has triangles but no vertexes, broken compiler, ignoring problem\n", loadmodel->name);
+               loadmodel->brushq3.num_triangles = 0;
+               return;
+       }
+
+       out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
        loadmodel->brushq3.num_triangles = count / 3;
        loadmodel->brushq3.data_element3i = out;
 
@@ -4492,8 +4497,6 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
                if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
                {
                        Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
-                       if(!loadmodel->brushq3.num_vertices)
-                               Host_Error("Mod_Q1BSP_LoadTrianglles: %s has triangles but no vertexes, cannot fix\n", loadmodel->name);
                        *out = 0;
                }
        }
@@ -4502,7 +4505,7 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
 static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
 {
        q3dlightmap_t *input_pointer;
-       int i, j, k, count, power, power2, mask, endlightmap, mergewidth, mergeheight;
+       int i, j, k, count, power, power2, endlightmap, mergewidth, mergeheight;
        unsigned char *c;
 
        unsigned char *convertedpixels;
@@ -4546,7 +4549,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                if (developer_loading.integer)
                        Con_Printf("Using external lightmaps\n");
                FS_StripExtension(loadmodel->name, mapname, sizeof(mapname));
-               inpixels[0] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, 0), false, false);
+               inpixels[0] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, 0), false, false, false);
                if(!inpixels[0])
                        return;
 
@@ -4566,7 +4569,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
 
                for(count = 1; ; ++count)
                {
-                       inpixels[count] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, count), false, false);
+                       inpixels[count] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, count), false, false, false);
                        if(!inpixels[count])
                                break; // we got all of them
                        if(image_width != size || image_height != size)
@@ -4670,7 +4673,6 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
 
        power = loadmodel->brushq3.num_lightmapmergepower;
        power2 = power * 2;
-       mask = (1 << power) - 1;
        for (i = 0;i < count;i++)
        {
                // figure out which merged lightmap texture this fits into
@@ -4801,7 +4803,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
 {
        q3dface_t *in, *oldin;
        msurface_t *out, *oldout;
-       int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, numvertices, numtriangles, cxtess, cytess;
+       int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, collisionvertices, collisiontriangles, numvertices, numtriangles, cxtess, cytess;
        float lightmaptcbase[2], lightmaptcscale[2];
        //int *originalelement3i;
        //int *originalneighbor3i;
@@ -4812,6 +4814,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
        float *originalcolor4f;
        float *originaltexcoordtexture2f;
        float *originaltexcoordlightmap2f;
+       float *surfacecollisionvertex3f;
+       int *surfacecollisionelement3i;
        float *v;
        patchtess_t *patchtess = NULL;
        int patchtesscount = 0;
@@ -4993,6 +4997,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
        while (again);
 
        // Calculate resulting number of triangles
+       collisionvertices = 0;
+       collisiontriangles = 0;
        for(i = 0; i < patchtesscount; ++i)
        {
                finalwidth = Q3PatchDimForTess(patchtess[i].info.xsize, patchtess[i].info.lods[PATCH_LOD_VISUAL].xtess);
@@ -5004,14 +5010,31 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                oldout[patchtess[i].surface_id].num_triangles = numtriangles;
                meshvertices += oldout[patchtess[i].surface_id].num_vertices;
                meshtriangles += oldout[patchtess[i].surface_id].num_triangles;
+
+               finalwidth = Q3PatchDimForTess(patchtess[i].info.xsize, patchtess[i].info.lods[PATCH_LOD_COLLISION].xtess);
+               finalheight = Q3PatchDimForTess(patchtess[i].info.ysize,patchtess[i].info.lods[PATCH_LOD_COLLISION].ytess);
+               numvertices = finalwidth * finalheight;
+               numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+
+               oldout[patchtess[i].surface_id].num_collisionvertices = numvertices;
+               oldout[patchtess[i].surface_id].num_collisiontriangles = numtriangles;
+               collisionvertices += oldout[patchtess[i].surface_id].num_collisionvertices;
+               collisiontriangles += oldout[patchtess[i].surface_id].num_collisiontriangles;
        }
 
        i = oldi;
        in = oldin;
        out = oldout;
        Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
+       if (collisiontriangles)
+       {
+               loadmodel->brush.data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, collisionvertices * sizeof(float[3]));
+               loadmodel->brush.data_collisionelement3i = Mem_Alloc(loadmodel->mempool, collisiontriangles * sizeof(int[3]));
+       }
        meshvertices = 0;
        meshtriangles = 0;
+       collisionvertices = 0;
+       collisiontriangles = 0;
        for (;i < count && meshvertices + out->num_vertices <= loadmodel->surfmesh.num_vertices;i++, in++, out++)
        {
                if (out->num_vertices < 3 || out->num_triangles < 1)
@@ -5022,6 +5045,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                firstelement = LittleLong(in->firstelement);
                out->num_firstvertex = meshvertices;
                out->num_firsttriangle = meshtriangles;
+               out->num_firstcollisiontriangle = collisiontriangles;
                switch(type)
                {
                case Q3FACETYPE_FLAT:
@@ -5102,26 +5126,40 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        finalvertices = finalwidth * finalheight;
                        finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
 
-                       out->data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
-                       out->data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
+                       // legacy collision geometry implementation
+                       out->deprecatedq3data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
+                       out->deprecatedq3data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
                        out->num_collisionvertices = finalvertices;
                        out->num_collisiontriangles = finaltriangles;
-                       Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, cxtess, cytess);
-                       Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), out->deprecatedq3data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, cxtess, cytess);
+                       Q3PatchTriangleElements(out->deprecatedq3data_collisionelement3i, finalwidth, finalheight, 0);
 
                        //Mod_SnapVertices(3, out->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), 0.25);
-                       Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
+                       Mod_SnapVertices(3, out->num_collisionvertices, out->deprecatedq3data_collisionvertex3f, 1);
 
                        oldnumtriangles = out->num_triangles;
                        oldnumtriangles2 = out->num_collisiontriangles;
-                       out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
+                       out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->deprecatedq3data_collisionelement3i, out->deprecatedq3data_collisionelement3i, out->deprecatedq3data_collisionvertex3f);
 
                        // now optimize the collision mesh by finding triangle bboxes...
-                       Mod_Q3BSP_BuildBBoxes(out->data_collisionelement3i, out->num_collisiontriangles, out->data_collisionvertex3f, &out->data_collisionbbox6f, &out->num_collisionbboxstride, mod_q3bsp_curves_collisions_stride.integer);
-                       Mod_Q3BSP_BuildBBoxes(loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle, out->num_triangles, loadmodel->surfmesh.data_vertex3f, &out->data_bbox6f, &out->num_bboxstride, mod_q3bsp_curves_stride.integer);
+                       Mod_Q3BSP_BuildBBoxes(out->deprecatedq3data_collisionelement3i, out->num_collisiontriangles, out->deprecatedq3data_collisionvertex3f, &out->deprecatedq3data_collisionbbox6f, &out->deprecatedq3num_collisionbboxstride, mod_q3bsp_curves_collisions_stride.integer);
+                       Mod_Q3BSP_BuildBBoxes(loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle, out->num_triangles, loadmodel->surfmesh.data_vertex3f, &out->deprecatedq3data_bbox6f, &out->deprecatedq3num_bboxstride, mod_q3bsp_curves_stride.integer);
+
+                       // store collision geometry for BIH collision tree
+                       surfacecollisionvertex3f = loadmodel->brush.data_collisionvertex3f + collisionvertices * 3;
+                       surfacecollisionelement3i = loadmodel->brush.data_collisionelement3i + collisiontriangles * 3;
+                       Q3PatchTesselateFloat(3, sizeof(float[3]), surfacecollisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, cxtess, cytess);
+                       Q3PatchTriangleElements(surfacecollisionelement3i, finalwidth, finalheight, collisionvertices);
+                       Mod_SnapVertices(3, finalvertices, surfacecollisionvertex3f, 1);
+                       oldnumtriangles = out->num_triangles;
+                       oldnumtriangles2 = out->num_collisiontriangles;
+                       out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, surfacecollisionelement3i, surfacecollisionelement3i, loadmodel->brush.data_collisionvertex3f);
 
                        if (developer_extra.integer)
                                Con_DPrintf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->num_vertices, out->num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->num_triangles, oldnumtriangles2 - out->num_collisiontriangles);
+
+                       collisionvertices += finalvertices;
+                       collisiontriangles += out->num_collisiontriangles;
                        break;
                default:
                        break;
@@ -5731,10 +5769,10 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, dp_model_t *mod
                for (i = 0;i < leaf->numleafsurfaces;i++)
                {
                        surface = model->data_surfaces + leaf->firstleafsurface[i];
-                       if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
+                       if (surface->num_collisiontriangles && surface->deprecatedq3collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
-                               surface->collisionmarkframe = markframe;
-                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                               surface->deprecatedq3collisionmarkframe = markframe;
+                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
@@ -5812,10 +5850,10 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, dp_model_t *mo
                for (i = 0;i < leaf->numleafsurfaces;i++)
                {
                        surface = model->data_surfaces + leaf->firstleafsurface[i];
-                       if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
+                       if (surface->num_collisiontriangles && surface->deprecatedq3collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
-                               surface->collisionmarkframe = markframe;
-                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                               surface->deprecatedq3collisionmarkframe = markframe;
+                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
@@ -5872,7 +5910,7 @@ static void Mod_Q3BSP_TraceLine(dp_model_t *model, const frameblend_t *frameblen
                if (mod_q3bsp_curves_collisions.integer)
                        for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
                                if (surface->num_collisiontriangles)
-                                       Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                                       Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
        }
        else
                Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
@@ -5927,7 +5965,7 @@ static void Mod_Q3BSP_TraceBox(dp_model_t *model, const frameblend_t *frameblend
                if (mod_q3bsp_curves_collisions.integer)
                        for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
                                if (surface->num_collisiontriangles)
-                                       Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                                       Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
        }
        else
                Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, &thisbrush_start.brush, &thisbrush_end.brush, ++markframe, segmentmins, segmentmaxs);
@@ -5965,6 +6003,88 @@ static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const
        return supercontents;
 }
 
+void Mod_MakeCollisionData(dp_model_t *model)
+{
+       int j;
+       int bihnumleafs;
+       int bihmaxnodes;
+       int brushindex;
+       int triangleindex;
+       int bihleafindex;
+       int nummodelbrushes = model->nummodelbrushes;
+       int nummodelsurfaces = model->nummodelsurfaces;
+       const int *e;
+       const int *collisionelement3i;
+       const float *collisionvertex3f;
+       bih_leaf_t *bihleafs;
+       bih_node_t *bihnodes;
+       int *temp_leafsort;
+       int *temp_leafsortscratch;
+       const msurface_t *surface;
+       const q3mbrush_t *brush;
+
+       // find out how many BIH leaf nodes we need
+       bihnumleafs = model->nummodelbrushes;
+       surface = model->data_surfaces + model->firstmodelsurface;
+       for (j = 0, surface = model->data_surfaces + model->firstmodelsurface;j < nummodelsurfaces;j++, surface++)
+               bihnumleafs += surface->num_collisiontriangles;
+       bihmaxnodes = bihnumleafs >> 1;
+
+       // allocate the memory for the BIH leaf nodes
+       bihleafs = Mem_Alloc(loadmodel->mempool, sizeof(bih_leaf_t) * bihnumleafs);
+
+       // add BIH leaf nodes for all the collision brushes
+       bihleafindex = 0;
+       for (brushindex = 0, brush = model->brush.data_brushes + brushindex+model->firstmodelbrush;brushindex < nummodelbrushes;brushindex++, brush++)
+       {
+               bihleafs[bihleafindex].type = BIH_LEAF;
+               bihleafs[bihleafindex].textureindex = brush->texture - model->data_textures;
+               bihleafs[bihleafindex].itemindex = brushindex+model->firstmodelbrush;
+               VectorCopy(brush->colbrushf->mins, bihleafs[bihleafindex].mins);
+               VectorCopy(brush->colbrushf->mins, bihleafs[bihleafindex].maxs);
+               bihleafindex++;
+       }
+
+       // add BIH leaf nodes for all the collision surfaces
+       collisionelement3i = model->brush.data_collisionelement3i;
+       collisionvertex3f = model->brush.data_collisionvertex3f;
+       for (j = 0, surface = model->data_surfaces + model->firstmodelsurface;j < nummodelsurfaces;j++, surface++)
+       {
+               e = collisionelement3i + surface->num_firstcollisiontriangle;
+               for (triangleindex = 0;triangleindex < surface->num_collisiontriangles;triangleindex++)
+               {
+                       bihleafs[bihleafindex].type = BIH_LEAF + 1;
+                       bihleafs[bihleafindex].textureindex = surface->texture - model->data_textures;
+                       bihleafs[bihleafindex].itemindex = triangleindex+surface->num_firstcollisiontriangle;
+                       bihleafs[bihleafindex].mins[0] = min(collisionvertex3f[3*e[0]+0], min(collisionvertex3f[3*e[1]+0], collisionvertex3f[3*e[2]+0]));
+                       bihleafs[bihleafindex].mins[1] = min(collisionvertex3f[3*e[0]+1], min(collisionvertex3f[3*e[1]+1], collisionvertex3f[3*e[2]+1]));
+                       bihleafs[bihleafindex].mins[2] = min(collisionvertex3f[3*e[0]+2], min(collisionvertex3f[3*e[1]+2], collisionvertex3f[3*e[2]+2]));
+                       bihleafs[bihleafindex].maxs[0] = max(collisionvertex3f[3*e[0]+0], max(collisionvertex3f[3*e[1]+0], collisionvertex3f[3*e[2]+0]));
+                       bihleafs[bihleafindex].maxs[1] = max(collisionvertex3f[3*e[0]+1], max(collisionvertex3f[3*e[1]+1], collisionvertex3f[3*e[2]+1]));
+                       bihleafs[bihleafindex].maxs[2] = max(collisionvertex3f[3*e[0]+2], max(collisionvertex3f[3*e[1]+2], collisionvertex3f[3*e[2]+2]));
+                       bihleafindex++;
+               }
+       }
+
+       // allocate buffers for the produced and temporary data
+       bihnodes = Mem_Alloc(loadmodel->mempool, sizeof(bih_node_t) * bihmaxnodes);
+       temp_leafsort = Mem_Alloc(loadmodel->mempool, sizeof(int) * bihnumleafs * 2);
+       temp_leafsortscratch = temp_leafsort + bihnumleafs;
+
+       // now build it
+       BIH_Build(&model->collision_bih, 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)
+       {
+               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));
+       }
+}
+
 static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents)
 {
        int supercontents = 0;
@@ -6174,7 +6294,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                        if (surface->num_triangles > 0)
                                Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
                loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
-               Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
+               if (loadmodel->brush.shadowmesh)
+                       Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
        }
 
        loadmodel->brush.num_leafs = 0;
@@ -6277,6 +6398,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
                if (j < mod->nummodelsurfaces)
                        mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
 
+               Mod_MakeCollisionData(mod);
+
                // generate VBOs and other shared data before cloning submodels
                if (i == 0)
                        Mod_BuildVBOs();
@@ -6676,7 +6799,7 @@ void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
        {
                VectorCopy(vertexhashdata[j].v, loadmodel->surfmesh.data_vertex3f + 3*j);
                VectorCopy(vertexhashdata[j].vn, loadmodel->surfmesh.data_normal3f + 3*j);
-               VectorCopy(vertexhashdata[j].vt, loadmodel->surfmesh.data_texcoordtexture2f + 2*j);
+               Vector2Copy(vertexhashdata[j].vt, loadmodel->surfmesh.data_texcoordtexture2f + 2*j);
        }
 
        // load the textures