+ q3dplane_t *in;
+ mplane_t *out;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_planes = out;
+ loadmodel->brushq3.num_planes = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ out->normal[0] = LittleLong(in->normal[0]);
+ out->normal[1] = LittleLong(in->normal[1]);
+ out->normal[2] = LittleLong(in->normal[2]);
+ out->dist = LittleLong(in->dist);
+ PlaneClassify(out);
+ }
+}
+
+static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
+{
+ q3dbrushside_t *in;
+ q3mbrushside_t *out;
+ int i, n, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_brushsides = out;
+ loadmodel->brushq3.num_brushsides = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ n = LittleLong(in->planeindex);
+ if (n < 0 || n >= loadmodel->brushq3.num_planes)
+ Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
+ out->plane = loadmodel->brushq3.data_planes + n;
+ n = LittleLong(in->textureindex);
+ if (n < 0 || n >= loadmodel->brushq3.num_textures)
+ Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
+ out->texture = loadmodel->brushq3.data_textures + n;
+ }
+}
+
+static void Mod_Q3BSP_LoadBrushes(lump_t *l)
+{
+ q3dbrush_t *in;
+ q3mbrush_t *out;
+ int i, j, n, c, count, maxplanes;
+ mplane_t *planes;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_brushes = out;
+ loadmodel->brushq3.num_brushes = count;
+
+ maxplanes = 0;
+ planes = NULL;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ n = LittleLong(in->firstbrushside);
+ c = LittleLong(in->numbrushsides);
+ if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
+ Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
+ out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
+ out->numbrushsides = c;
+ n = LittleLong(in->textureindex);
+ if (n < 0 || n >= loadmodel->brushq3.num_textures)
+ Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
+ out->texture = loadmodel->brushq3.data_textures + n;
+
+ // make a list of mplane_t structs to construct a colbrush from
+ if (maxplanes < out->numbrushsides)
+ {
+ maxplanes = out->numbrushsides;
+ if (planes)
+ Mem_Free(planes);
+ planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
+ }
+ for (j = 0;j < out->numbrushsides;j++)
+ {
+ VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
+ 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);
+ }
+ if (planes)
+ Mem_Free(planes);
+}
+
+static void Mod_Q3BSP_LoadEffects(lump_t *l)
+{
+ q3deffect_t *in;
+ q3meffect_t *out;
+ int i, n, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_effects = out;
+ loadmodel->brushq3.num_effects = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
+ 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);
+ out->brush = loadmodel->brushq3.data_brushes + n;
+ out->unknown = LittleLong(in->unknown);
+ }
+}
+
+static void Mod_Q3BSP_LoadVertices(lump_t *l)
+{
+ q3dvertex_t *in;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ 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]));
+
+ for (i = 0;i < count;i++, in++)
+ {
+ loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
+ loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
+ loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
+ loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
+ loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
+ loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
+ loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
+ // svector/tvector are calculated later in face loading
+ loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
+ loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
+ loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
+ loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
+ loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
+ loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
+ loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
+ loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
+ loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
+ loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
+ loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
+ loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
+ loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
+ }
+}
+
+static void Mod_Q3BSP_LoadTriangles(lump_t *l)
+{
+ int *in;
+ int *out;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ 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));
+
+ loadmodel->brushq3.num_triangles = count / 3;
+ loadmodel->brushq3.data_element3i = out;
+ loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ 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);
+ }
+}
+
+static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
+{
+ q3dlightmap_t *in;
+ rtexture_t **out;
+ int i, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_lightmaps = out;
+ loadmodel->brushq3.num_lightmaps = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+}
+
+static void Mod_Q3BSP_LoadFaces(lump_t *l)
+{
+ q3dface_t *in;
+ q3mface_t *out;
+ int i, j, n, count, invalidelements, patchsize[2];
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_faces = out;
+ loadmodel->brushq3.num_faces = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ // check face type first
+ out->type = LittleLong(in->type);
+ if (out->type != Q3FACETYPE_POLYGON
+ && out->type != Q3FACETYPE_PATCH
+ && out->type != Q3FACETYPE_MESH
+ && out->type != Q3FACETYPE_FLARE)
+ {
+ Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
+ out->type = 0; // error
+ continue;
+ }
+
+ 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);
+ out->type = 0; // error
+ continue;
+ n = 0;
+ }
+ out->texture = loadmodel->brushq3.data_textures + n;
+ 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);
+ n = -1;
+ }
+ if (n == -1)
+ out->effect = NULL;
+ else
+ out->effect = loadmodel->brushq3.data_effects + n;
+ 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);
+ n = -1;
+ }
+ if (n == -1)
+ out->lightmaptexture = NULL;
+ else
+ out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
+
+ out->firstvertex = LittleLong(in->firstvertex);
+ out->numvertices = 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)
+ {
+ 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);
+ out->type = 0; // error
+ continue;
+ }
+ if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
+ {
+ 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);
+ out->type = 0; // error
+ continue;
+ }
+ if (out->numtriangles * 3 != out->numelements)
+ {
+ Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
+ out->type = 0; // error
+ continue;
+ }
+ switch(out->type)
+ {
+ case Q3FACETYPE_POLYGON:
+ case Q3FACETYPE_MESH:
+ 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->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
+ out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
+ break;
+ case Q3FACETYPE_PATCH:
+ patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
+ patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
+ 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->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;
+ break;
+ }
+ for (j = 0, invalidelements = 0;j < out->numelements;j++)
+ if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
+ 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(" %i", out->data_element3i[j]);
+ if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
+ out->data_element3i[j] = 0;
+ }
+ Con_Printf("\n");
+ }
+ }
+}
+
+static void Mod_Q3BSP_LoadModels(lump_t *l)
+{
+ q3dmodel_t *in;
+ q3mmodel_t *out;
+ int i, j, n, c, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_models = out;
+ loadmodel->brushq3.num_models = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ for (j = 0;j < 3;j++)
+ {
+ out->mins[j] = LittleFloat(in->mins[j]);
+ out->maxs[j] = LittleFloat(in->maxs[j]);
+ }
+ n = LittleLong(in->firstface);
+ c = LittleLong(in->numfaces);
+ if (n < 0 || n + c > loadmodel->brushq3.num_faces)
+ Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
+ out->firstface = loadmodel->brushq3.data_faces + n;
+ out->numfaces = c;
+ n = LittleLong(in->firstbrush);
+ c = LittleLong(in->numbrushes);
+ if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
+ Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
+ out->firstbrush = loadmodel->brushq3.data_brushes + n;
+ out->numbrushes = c;
+ }
+}
+
+static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
+{
+ int *in;
+ q3mbrush_t **out;
+ int i, n, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_leafbrushes = out;
+ loadmodel->brushq3.num_leafbrushes = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ n = LittleLong(*in);
+ if (n < 0 || n >= loadmodel->brushq3.num_brushes)
+ Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
+ *out = loadmodel->brushq3.data_brushes + n;
+ }
+}
+
+static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
+{
+ int *in;
+ q3mface_t **out;
+ int i, n, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_leaffaces = out;
+ loadmodel->brushq3.num_leaffaces = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ n = LittleLong(*in);
+ if (n < 0 || n >= loadmodel->brushq3.num_faces)
+ Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
+ *out = loadmodel->brushq3.data_faces + n;
+ }
+}
+
+static void Mod_Q3BSP_LoadLeafs(lump_t *l)
+{
+ q3dleaf_t *in;
+ q3mleaf_t *out;
+ int i, j, n, c, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_leafs = out;
+ loadmodel->brushq3.num_leafs = count;
+
+ for (i = 0;i < count;i++, in++, out++)
+ {
+ out->isnode = false;
+ out->parent = 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]);
+ }
+ n = LittleLong(in->firstleafface);
+ c = LittleLong(in->numleaffaces);
+ if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
+ Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
+ out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
+ out->numleaffaces = c;
+ n = LittleLong(in->firstleafbrush);
+ c = LittleLong(in->numleafbrushes);
+ if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
+ Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
+ out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
+ out->numleafbrushes = c;
+ }
+}
+
+static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
+{
+ if (node->parent)
+ Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
+ node->parent = parent;
+ if (node->isnode)
+ {
+ Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
+ Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
+ }
+}
+
+static void Mod_Q3BSP_LoadNodes(lump_t *l)
+{
+ q3dnode_t *in;
+ q3mnode_t *out;
+ int i, j, n, count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
+ count = l->filelen / sizeof(*in);
+ out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+ loadmodel->brushq3.data_nodes = out;
+ loadmodel->brushq3.num_nodes = count;
+
+ 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)
+ Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
+ out->plane = loadmodel->brushq3.data_planes + n;
+ for (j = 0;j < 2;j++)
+ {
+ n = LittleLong(in->childrenindex[j]);
+ if (n >= 0)
+ {
+ if (n >= loadmodel->brushq3.num_nodes)
+ Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
+ out->children[j] = loadmodel->brushq3.data_nodes + n;
+ }
+ else
+ {
+ 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);
+ }
+ }
+ 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]);
+ }
+ }
+
+ // set the parent pointers
+ Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
+}
+
+static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
+{
+ q3dlightgrid_t *in;
+ q3dlightgrid_t *out;
+ int count;
+
+ in = (void *)(mod_base + l->fileofs);
+ if (l->filelen % sizeof(*in))
+ Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
+ loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
+ loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
+ loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
+ loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
+ loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
+ loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
+ loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
+ loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
+ loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
+ loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
+ 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);
+
+ 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));
+
+ 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]);
+}
+
+static void Mod_Q3BSP_LoadPVS(lump_t *l)
+{
+ q3dpvs_t *in;
+ int totalchains;
+
+ 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;
+ 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);
+
+ loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
+ memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
+}
+
+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];
+ q3mleaf_t *leaf;
+ // 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
+ {
+ // take the near side first
+ if (startfrac < trace->fraction)
+ {
+ // line crosses node plane, split the line
+ midfrac = dist1 / (dist1 - dist2);
+ VectorLerp(linestart, midfrac, lineend, mid);
+ 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
+ leaf = (q3mleaf_t *)node;
+ for (i = 0;i < leaf->numleafbrushes;i++)
+ {
+ if (endfrac >= trace->fraction)
+ return;
+ if (leaf->firstleafbrush[i]->colbrushf && leaf->firstleafbrush[i]->colbrushf->markframe != markframe)
+ {
+ leaf->firstleafbrush[i]->colbrushf->markframe = markframe;
+ Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+ }
+ }
+}
+
+static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe)
+{
+ int i;
+ float dist;
+ colpointf_t *ps, *pe;
+ q3mleaf_t *leaf;
+ if (node->isnode)
+ {
+ // recurse down node sides
+ 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);
+ 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);
+ 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++)
+ {
+ if (leaf->firstleafbrush[i]->colbrushf && leaf->firstleafbrush[i]->colbrushf->markframe != markframe)
+ {
+ leaf->firstleafbrush[i]->colbrushf->markframe = markframe;
+ Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+ }
+ }
+ }
+}
+
+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;
+ colbrushf_t *thisbrush_start, *thisbrush_end;
+ matrix4x4_t startmatrix, endmatrix;
+ static int markframe = 0;
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = 1;
+ trace->hitsupercontentsmask = hitsupercontentsmask;
+ Matrix4x4_CreateIdentity(&startmatrix);
+ Matrix4x4_CreateIdentity(&endmatrix);
+ if (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);
+ }
+ else
+ Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
+ }
+ else
+ {
+ // box trace, performed as box 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);
+ }
+ else
+ Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe);
+ }
+}
+
+
+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: always returns valid data, never 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];
+ return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
+}
+
+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, accumulate the pvs bits
+ 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);
+void Mod_Q3BSP_Load(model_t *mod, void *buffer)
+{
+ int i;
+ q3dheader_t *header;
+ float corner[3], yawradius, modelradius;
+
+ mod->type = mod_brushq3;
+
+ header = (q3dheader_t *)buffer;
+
+ i = LittleLong(header->version);
+ if (i != Q3BSPVERSION)
+ Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
+ if (loadmodel->isworldmodel)
+ {
+ Cvar_SetValue("halflifebsp", false);
+ // until we get a texture for it...
+ R_ResetQuakeSky();
+ }
+
+ 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_base = (qbyte *)header;
+
+ // swap all the lumps
+ for (i = 0;i < (int) sizeof(*header) / 4;i++)
+ ((int *)header)[i] = LittleLong(((int *)header)[i]);
+
+ Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
+ Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
+ Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
+ Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
+ Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
+ Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
+ Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
+ Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
+ Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
+ Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
+ Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
+ Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
+ Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
+ Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
+ Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
+ Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
+ Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
+ loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
+
+ for (i = 0;i < loadmodel->brushq3.num_models;i++)
+ {
+ if (i == 0)
+ mod = loadmodel;
+ else
+ {
+ char name[10];
+ // LordHavoc: only register submodels if it is the world
+ // (prevents bsp models from replacing world submodels)
+ if (!loadmodel->isworldmodel)
+ continue;
+ // duplicate the basic information
+ sprintf(name, "*%i", i);
+ mod = Mod_FindName(name);
+ *mod = *loadmodel;
+ strcpy(mod->name, name);
+ // textures and memory belong to the main model
+ mod->texturepool = NULL;
+ mod->mempool = NULL;
+ }
+ 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);
+ corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
+ corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
+ corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
+ modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
+ yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
+ mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
+ mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
+ mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
+ mod->yawmins[0] = mod->yawmins[1] = -yawradius;
+ mod->yawmins[2] = mod->normalmins[2];
+ mod->yawmaxs[2] = mod->normalmaxs[2];
+ mod->radius = modelradius;
+ mod->radius2 = modelradius * modelradius;
+ }
+}
+
+void Mod_IBSP_Load(model_t *mod, void *buffer)
+{
+ int i = LittleLong(((int *)buffer)[1]);
+ if (i == Q3BSPVERSION)
+ Mod_Q3BSP_Load(mod,buffer);
+ else if (i == Q2BSPVERSION)
+ Mod_Q2BSP_Load(mod,buffer);
+ else
+ Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
+}
+
+void Mod_MAP_Load(model_t *mod, void *buffer)
+{
+ Host_Error("Mod_MAP_Load: not yet implemented\n");