+ mod->brush.qw_md4sum2 ^= LittleLong(temp);
+ }
+
+ // many of these functions are identical to Q1 loaders, so we use those where possible
+ Mod_Q1BSP_LoadEntities(&lumpsb[Q2LUMP_ENTITIES]);
+ Mod_Q1BSP_LoadVertexes(&lumpsb[Q2LUMP_VERTEXES]);
+ Mod_Q1BSP_LoadEdges(&lumpsb[Q2LUMP_EDGES]);
+ Mod_Q1BSP_LoadSurfedges(&lumpsb[Q2LUMP_SURFEDGES]);
+ Mod_Q2BSP_LoadLighting(&lumpsb[Q2LUMP_LIGHTING]);
+ Mod_Q1BSP_LoadPlanes(&lumpsb[Q2LUMP_PLANES]);
+ Mod_Q2BSP_LoadTexinfo(&lumpsb[Q2LUMP_TEXINFO]);
+ Mod_Q2BSP_LoadBrushSides(&lumpsb[Q2LUMP_BRUSHSIDES]);
+ Mod_Q2BSP_LoadBrushes(&lumpsb[Q2LUMP_BRUSHES]);
+ Mod_Q1BSP_LoadFaces(&lumpsb[Q2LUMP_FACES]);
+ Mod_Q1BSP_LoadLeaffaces(&lumpsb[Q2LUMP_LEAFFACES]);
+ Mod_Q2BSP_LoadLeafBrushes(&lumpsb[Q2LUMP_LEAFBRUSHES]);
+ Mod_Q2BSP_LoadVisibility(&lumpsb[Q2LUMP_VISIBILITY]);
+ Mod_Q2BSP_LoadPOP(&lumpsb[Q2LUMP_POP]);
+ Mod_Q2BSP_LoadAreas(&lumpsb[Q2LUMP_AREAS]);
+ Mod_Q2BSP_LoadAreaPortals(&lumpsb[Q2LUMP_AREAPORTALS]);
+ Mod_Q2BSP_LoadLeafs(&lumpsb[Q2LUMP_LEAFS]);
+ Mod_Q2BSP_LoadNodes(&lumpsb[Q2LUMP_NODES]);
+ Mod_Q2BSP_LoadSubmodels(&lumpsb[Q2LUMP_MODELS]);
+
+ for (i = 0; i < Q2HEADER_LUMPS; i++)
+ if (lumpsb[i].readcount != lumpsb[i].cursize)
+ Host_Error("Lump %i incorrectly loaded (readcount %i, size %i)\n", i, lumpsb[i].readcount, lumpsb[i].cursize);
+
+ // we don't actually set MATERIALFLAG_WATERALPHA on anything, so this
+ // doesn't enable the cvar, just indicates that transparent water is OK
+ loadmodel->brush.supportwateralpha = true;
+
+ // we don't need the compressed pvs data anymore
+ if (mod->brushq1.data_compressedpvs)
+ Mem_Free(mod->brushq1.data_compressedpvs);
+ mod->brushq1.data_compressedpvs = NULL;
+ mod->brushq1.num_compressedpvs = 0;
+
+ // the MakePortals code works fine on the q2bsp data as well
+ if (mod_bsp_portalize.integer)
+ Mod_Q1BSP_MakePortals();
+
+ mod->numframes = 0; // q2bsp animations are kind of special, frame is unbounded...
+ mod->numskins = 1;
+
+ // make a single combined shadow mesh to allow optimized shadow volume creation
+ Mod_Q1BSP_CreateShadowMesh(loadmodel);
+
+ if (loadmodel->brush.numsubmodels)
+ loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
+
+ totalstylesurfaces = 0;
+ totalstyles = 0;
+ for (i = 0;i < mod->brush.numsubmodels;i++)
+ {
+ memset(stylecounts, 0, sizeof(stylecounts));
+ for (k = 0;k < mod->brushq1.submodels[i].numfaces;k++)
+ {
+ surface = mod->data_surfaces + mod->brushq1.submodels[i].firstface + k;
+ for (j = 0;j < MAXLIGHTMAPS;j++)
+ stylecounts[surface->lightmapinfo->styles[j]]++;
+ }
+ for (k = 0;k < 255;k++)
+ {
+ totalstyles++;
+ if (stylecounts[k])
+ totalstylesurfaces += stylecounts[k];
+ }
+ }
+ datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
+ // set up the world model, then on each submodel copy from the world model
+ // and set up the submodel with the respective model info.
+ mod = loadmodel;
+ for (i = 0;i < loadmodel->brush.numsubmodels;i++)
+ {
+ mnode_t *rootnode = NULL;
+ int firstbrush = loadmodel->brush.num_brushes, lastbrush = 0;
+ if (i > 0)
+ {
+ char name[10];
+ // duplicate the basic information
+ dpsnprintf(name, sizeof(name), "*%i", i);
+ mod = Mod_FindName(name, loadmodel->name);
+ // copy the base model to this one
+ *mod = *loadmodel;
+ // rename the clone back to its proper name
+ strlcpy(mod->name, name, sizeof(mod->name));
+ mod->brush.parentmodel = loadmodel;
+ // 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.BoxTouchingLeafPVS = NULL;
+ mod->brush.BoxTouchingVisibleLeafs = NULL;
+ mod->brush.FindBoxClusters = NULL;
+ mod->brush.LightPoint = NULL;
+ mod->brush.AmbientSoundLevelsForPoint = NULL;
+ }
+ mod->brush.submodel = i;
+ if (loadmodel->brush.submodels)
+ loadmodel->brush.submodels[i] = mod;
+
+ bm = &mod->brushq1.submodels[i];
+
+ // we store the headnode (there's only one in Q2BSP) as if it were the first hull
+ mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
+
+ mod->firstmodelsurface = bm->firstface;
+ mod->nummodelsurfaces = bm->numfaces;
+
+ // set node/leaf parents for this submodel
+ // note: if the root of this submodel is a leaf (headnode[0] < 0) then there is nothing to do...
+ // (this happens in base3.bsp)
+ if (bm->headnode[0] >= 0)
+ rootnode = mod->brush.data_nodes + bm->headnode[0];
+ else
+ rootnode = (mnode_t*)(mod->brush.data_leafs + -1 - bm->headnode[0]);
+ Mod_Q1BSP_LoadNodes_RecursiveSetParent(rootnode, NULL);
+
+ // make the model surface list (used by shadowing/lighting)
+ mod->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int);
+ Mod_Q2BSP_FindSubmodelBrushRange_r(mod, rootnode, &firstbrush, &lastbrush);
+ if (firstbrush <= lastbrush)
+ {
+ mod->firstmodelbrush = firstbrush;
+ mod->nummodelbrushes = lastbrush + 1 - firstbrush;
+ }
+ else
+ {
+ mod->firstmodelbrush = 0;
+ mod->nummodelbrushes = 0;
+ }
+ Mod_MakeSortedSurfaces(mod);
+
+ VectorCopy(bm->mins, mod->normalmins);
+ VectorCopy(bm->maxs, mod->normalmaxs);
+ dist = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
+ modelyawradius = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
+ modelyawradius = dist*dist+modelyawradius*modelyawradius;
+ modelradius = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
+ modelradius = modelyawradius + modelradius * modelradius;
+ modelyawradius = sqrt(modelyawradius);
+ modelradius = sqrt(modelradius);
+ mod->yawmins[0] = mod->yawmins[1] = -modelyawradius;
+ mod->yawmins[2] = mod->normalmins[2];
+ mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius;
+ mod->yawmaxs[2] = mod->normalmaxs[2];
+ mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
+ mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
+ mod->radius = modelradius;
+ mod->radius2 = modelradius * modelradius;
+
+ // this gets altered below if sky or water is used
+ mod->DrawSky = NULL;
+ mod->DrawAddWaterPlanes = NULL;
+
+ // scan surfaces for sky and water and flag the submodel as possessing these features or not
+ // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker
+ if (mod->nummodelsurfaces)
+ {
+ for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
+ if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawSky = R_Q1BSP_DrawSky;
+
+ for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
+ if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA))
+ break;
+ if (j < mod->nummodelsurfaces)
+ mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+
+ // build lightstyle update chains
+ // (used to rapidly mark lightmapupdateflags on many surfaces
+ // when d_lightstylevalue changes)
+ memset(stylecounts, 0, sizeof(stylecounts));
+ for (k = 0;k < mod->nummodelsurfaces;k++)
+ {
+ surface = mod->data_surfaces + mod->firstmodelsurface + k;
+ for (j = 0;j < MAXLIGHTMAPS;j++)
+ stylecounts[surface->lightmapinfo->styles[j]]++;
+ }
+ mod->brushq1.num_lightstyles = 0;
+ for (k = 0;k < 255;k++)
+ {
+ if (stylecounts[k])
+ {
+ styleinfo[mod->brushq1.num_lightstyles].style = k;
+ styleinfo[mod->brushq1.num_lightstyles].value = 0;
+ styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
+ styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
+ remapstyles[k] = mod->brushq1.num_lightstyles;
+ mod->brushq1.num_lightstyles++;
+ }
+ }
+ for (k = 0;k < mod->nummodelsurfaces;k++)
+ {
+ surface = mod->data_surfaces + mod->firstmodelsurface + k;
+ for (j = 0;j < MAXLIGHTMAPS;j++)
+ {
+ if (surface->lightmapinfo->styles[j] != 255)
+ {
+ int r = remapstyles[surface->lightmapinfo->styles[j]];
+ styleinfo[r].surfacelist[styleinfo[r].numsurfaces++] = mod->firstmodelsurface + k;
+ }
+ }
+ }
+ mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
+ memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
+ }
+ else
+ {
+ Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
+ }
+ //mod->brushq1.num_visleafs = bm->visleafs;
+
+ // build a Bounding Interval Hierarchy for culling triangles in light rendering
+ Mod_MakeCollisionBIH(mod, false, &mod->collision_bih);
+
+ // build a Bounding Interval Hierarchy for culling brushes in collision detection
+ Mod_MakeCollisionBIH(mod, true, &mod->render_bih);
+
+ // generate VBOs and other shared data before cloning submodels
+ if (i == 0)
+ Mod_BuildVBOs();