+ type = LittleLong(in->type);
+ firstvertex = LittleLong(in->firstvertex);
+ firstelement = LittleLong(in->firstelement);
+ out->mesh.data_vertex3f = mesh->data_vertex3f + meshvertices * 3;
+ out->mesh.data_svector3f = mesh->data_svector3f + meshvertices * 3;
+ out->mesh.data_tvector3f = mesh->data_tvector3f + meshvertices * 3;
+ out->mesh.data_normal3f = mesh->data_normal3f + meshvertices * 3;
+ out->mesh.data_texcoordtexture2f = mesh->data_texcoordtexture2f + meshvertices * 2;
+ out->mesh.data_texcoordlightmap2f = mesh->data_texcoordlightmap2f + meshvertices * 2;
+ out->mesh.data_lightmapcolor4f = mesh->data_lightmapcolor4f + meshvertices * 4;
+ out->mesh.data_element3i = mesh->data_element3i + meshtriangles * 3;
+ out->mesh.data_neighbor3i = mesh->data_neighbor3i + meshtriangles * 3;
+ switch(type)
+ {
+ case Q3FACETYPE_POLYGON:
+ case Q3FACETYPE_MESH:
+ // no processing necessary
+ for (j = 0;j < out->mesh.num_vertices;j++)
+ {
+ out->mesh.data_vertex3f[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
+ out->mesh.data_vertex3f[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
+ out->mesh.data_vertex3f[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
+ out->mesh.data_texcoordtexture2f[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
+ out->mesh.data_texcoordtexture2f[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
+ out->mesh.data_texcoordlightmap2f[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
+ out->mesh.data_texcoordlightmap2f[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
+ out->mesh.data_lightmapcolor4f[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
+ out->mesh.data_lightmapcolor4f[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
+ out->mesh.data_lightmapcolor4f[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
+ out->mesh.data_lightmapcolor4f[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
+ }
+ for (j = 0;j < out->mesh.num_triangles*3;j++)
+ out->mesh.data_element3i[j] = loadmodel->brushq3.data_element3i[firstelement + j];
+ break;
+ case Q3FACETYPE_PATCH:
+ patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
+ patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
+ originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
+ originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
+ originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
+ originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
+ // convert patch to Q3FACETYPE_MESH
+ xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
+ ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
+ // bound to user settings
+ xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
+ ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
+ // bound to sanity settings
+ xtess = bound(1, xtess, 1024);
+ ytess = bound(1, ytess, 1024);
+ // bound to user limit on vertices
+ while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
+ {
+ if (xtess > ytess)
+ xtess--;
+ else
+ ytess--;
+ }
+ finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+ finalheight = ((patchsize[1] - 1) * ytess) + 1;
+ finalvertices = finalwidth * finalheight;
+ finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+ type = Q3FACETYPE_MESH;
+ // generate geometry
+ // (note: normals are skipped because they get recalculated)
+ Q3PatchTesselateFloat(3, sizeof(float[3]), out->mesh.data_vertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+ Q3PatchTesselateFloat(2, sizeof(float[2]), out->mesh.data_texcoordtexture2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
+ Q3PatchTesselateFloat(2, sizeof(float[2]), out->mesh.data_texcoordlightmap2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
+ Q3PatchTesselateFloat(4, sizeof(float[4]), out->mesh.data_lightmapcolor4f, patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
+ Q3PatchTriangleElements(out->mesh.data_element3i, finalwidth, finalheight);
+ out->mesh.num_triangles = Mod_RemoveDegenerateTriangles(out->mesh.num_triangles, out->mesh.data_element3i, out->mesh.data_element3i, out->mesh.data_vertex3f);
+ if (developer.integer >= 2)
+ {
+ if (out->mesh.num_triangles < finaltriangles)
+ Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->mesh.num_vertices, finaltriangles, finaltriangles - out->mesh.num_triangles, out->mesh.num_triangles);
+ else
+ Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->mesh.num_vertices, out->mesh.num_triangles);
+ }
+ // q3map does not put in collision brushes for curves... ugh
+ // build the lower quality collision geometry
+ xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
+ ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
+ // bound to user settings
+ xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
+ ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
+ // bound to sanity settings
+ xtess = bound(1, xtess, 1024);
+ ytess = bound(1, ytess, 1024);
+ // bound to user limit on vertices
+ while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
+ {
+ if (xtess > ytess)
+ xtess--;
+ else
+ ytess--;
+ }
+ finalwidth = ((patchsize[0] - 1) * xtess) + 1;
+ finalheight = ((patchsize[1] - 1) * ytess) + 1;
+ finalvertices = finalwidth * finalheight;
+ finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+
+ out->mesh.data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
+ out->mesh.data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
+ out->mesh.num_collisionvertices = finalvertices;
+ out->mesh.num_collisiontriangles = finaltriangles;
+ Q3PatchTesselateFloat(3, sizeof(float[3]), out->mesh.data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
+ Q3PatchTriangleElements(out->mesh.data_collisionelement3i, finalwidth, finalheight);
+
+ //Mod_SnapVertices(3, out->mesh.num_vertices, out->mesh.data_vertex3f, 0.25);
+ Mod_SnapVertices(3, out->mesh.num_collisionvertices, out->mesh.data_collisionvertex3f, 1);
+
+ oldnumtriangles = out->mesh.num_triangles;
+ oldnumtriangles2 = out->mesh.num_collisiontriangles;
+ out->mesh.num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->mesh.num_collisiontriangles, out->mesh.data_collisionelement3i, out->mesh.data_collisionelement3i, out->mesh.data_collisionvertex3f);
+ if (developer.integer)
+ Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->mesh.num_vertices, out->mesh.num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->mesh.num_triangles, oldnumtriangles2 - out->mesh.num_collisiontriangles);
+ break;
+ default:
+ break;
+ }
+ meshvertices += out->mesh.num_vertices;
+ meshtriangles += out->mesh.num_triangles;
+ for (j = 0, invalidelements = 0;j < out->mesh.num_triangles * 3;j++)
+ if (out->mesh.data_element3i[j] < 0 || out->mesh.data_element3i[j] >= out->mesh.num_vertices)
+ invalidelements++;
+ if (invalidelements)
+ {
+ Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, type, out->texture->name, out->texture->surfaceflags, firstvertex, out->mesh.num_vertices, firstelement, out->mesh.num_triangles * 3);
+ for (j = 0;j < out->mesh.num_triangles * 3;j++)
+ {
+ Con_Printf(" %i", out->mesh.data_element3i[j]);
+ if (out->mesh.data_element3i[j] < 0 || out->mesh.data_element3i[j] >= out->mesh.num_vertices)
+ out->mesh.data_element3i[j] = 0;
+ }
+ Con_Print("\n");
+ }
+ // for shadow volumes
+ Mod_BuildTriangleNeighbors(out->mesh.data_neighbor3i, out->mesh.data_element3i, out->mesh.num_triangles);
+ // for per pixel lighting
+ Mod_BuildTextureVectorsAndNormals(out->mesh.num_vertices, out->mesh.num_triangles, out->mesh.data_vertex3f, out->mesh.data_texcoordtexture2f, out->mesh.data_element3i, out->mesh.data_svector3f, out->mesh.data_tvector3f, out->mesh.data_normal3f);
+ // calculate a bounding box
+ VectorClear(out->mins);
+ VectorClear(out->maxs);
+ if (out->mesh.num_vertices)
+ {
+ VectorCopy(out->mesh.data_vertex3f, out->mins);
+ VectorCopy(out->mesh.data_vertex3f, out->maxs);
+ for (j = 1, v = out->mesh.data_vertex3f + 3;j < out->mesh.num_vertices;j++, v += 3)
+ {
+ out->mins[0] = min(out->mins[0], v[0]);
+ out->maxs[0] = max(out->maxs[0], v[0]);
+ out->mins[1] = min(out->mins[1], v[1]);
+ out->maxs[1] = max(out->maxs[1], v[1]);
+ out->mins[2] = min(out->mins[2], v[2]);
+ out->maxs[2] = max(out->maxs[2], v[2]);
+ }
+ out->mins[0] -= 1.0f;
+ out->mins[1] -= 1.0f;
+ out->mins[2] -= 1.0f;
+ out->maxs[0] += 1.0f;
+ out->maxs[1] += 1.0f;
+ out->maxs[2] += 1.0f;
+ }
+ }
+ }
+
+ // now store the completed list of meshes
+ loadmodel->nummeshes = meshnum;
+ if (loadmodel->nummeshes)
+ {
+ loadmodel->meshlist = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
+ memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
+ }
+
+ // free the no longer needed vertex data
+ loadmodel->brushq3.num_vertices = 0;
+ Mem_Free(loadmodel->brushq3.data_vertex3f);
+ loadmodel->brushq3.data_vertex3f = NULL;
+ loadmodel->brushq3.data_texcoordtexture2f = NULL;
+ loadmodel->brushq3.data_texcoordlightmap2f = NULL;
+ loadmodel->brushq3.data_color4f = NULL;
+ // free the no longer needed triangle data
+ loadmodel->brushq3.num_triangles = 0;
+ Mem_Free(loadmodel->brushq3.data_element3i);
+ loadmodel->brushq3.data_element3i = NULL;
+}
+
+static void Mod_Q3BSP_LoadModels(lump_t *l)
+{
+ q3dmodel_t *in;