]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
model_brush: More work on VBSP. Still crashes.
authorcloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 1 Jan 2021 15:09:24 +0000 (15:09 +0000)
committercloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 1 Jan 2021 15:09:24 +0000 (15:09 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@13088 d7cf8633-e32d-0410-b094-e92efae38249

model_brush.c

index 6277339f5a8892edc4893dc4b1a8b151d7a7b3bc..d82d80564b757831e05e90a24d06aed55c108785 100644 (file)
@@ -7764,6 +7764,325 @@ static void Mod_VBSP_LoadPlanes(sizebuf_t *sb)
        }
 }
 
+static void Mod_VBSP_LoadTexinfo(sizebuf_t *sb)
+{
+       mtexinfo_t *out;
+       int i, j, k, count, miptex;
+       int structsize = 72;
+
+       if (sb->cursize % structsize)
+               Host_Error("Mod_VBSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
+       count = sb->cursize / structsize;
+       out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+
+       loadmodel->brushq1.texinfo = out;
+       loadmodel->brushq1.numtexinfo = count;
+
+       for (i = 0;i < count;i++, out++)
+       {
+               for (k = 0;k < 2;k++)
+                       for (j = 0;j < 4;j++)
+                               out->vecs[k][j] = MSG_ReadLittleFloat(sb);
+
+               for (k = 0;k < 2;k++)
+                       for (j = 0;j < 4;j++)
+                               MSG_ReadLittleFloat(sb); // TODO lightmapVecs
+
+               out->q1flags = MSG_ReadLittleLong(sb);
+               miptex = MSG_ReadLittleLong(sb);
+
+               if (out->q1flags & TEX_SPECIAL)
+               {
+                       // if texture chosen is NULL or the shader needs a lightmap,
+                       // force to notexture water shader
+                       out->textureindex = loadmodel->num_textures - 1;
+               }
+               else
+               {
+                       // if texture chosen is NULL, force to notexture
+                       out->textureindex = loadmodel->num_textures - 2;
+               }
+               // see if the specified miptex is valid and try to use it instead
+               if (loadmodel->data_textures)
+               {
+                       if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures)
+                               Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures);
+                       else
+                               out->textureindex = miptex;
+               }
+       }
+}
+
+static void Mod_VBSP_LoadFaces(sizebuf_t *sb)
+{
+       msurface_t *surface;
+       int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber, lightmapsize, totallightmapsamples, lightmapoffset, texinfoindex;
+       float texmins[2], texmaxs[2], val;
+       rtexture_t *lightmaptexture, *deluxemaptexture;
+       char vabuf[1024];
+       int structsize =  56;
+
+       if (sb->cursize % structsize)
+               Host_Error("Mod_VBSP_LoadFaces: funny lump size in %s",loadmodel->name);
+       count = sb->cursize / structsize;
+       loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
+       loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
+
+       loadmodel->num_surfaces = count;
+
+       loadmodel->brushq1.firstrender = true;
+       loadmodel->brushq1.lightmapupdateflags = (unsigned char *)Mem_Alloc(loadmodel->mempool, count*sizeof(unsigned char));
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0;surfacenum < count;surfacenum++)
+       {
+               numedges = BuffLittleShort(sb->data + structsize * surfacenum + 8);
+               totalverts += numedges;
+               totaltris += numedges - 2;
+       }
+
+       Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false);
+
+       lightmaptexture = NULL;
+       deluxemaptexture = r_texture_blanknormalmap;
+       lightmapnumber = 0;
+       lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d);
+       totallightmapsamples = 0;
+
+       totalverts = 0;
+       totaltris = 0;
+       for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+       {
+               surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
+               planenum = (unsigned short)MSG_ReadLittleShort(sb);
+               /*side = */MSG_ReadLittleShort(sb); // TODO support onNode?
+               firstedge = MSG_ReadLittleLong(sb);
+               numedges = (unsigned short)MSG_ReadLittleShort(sb);
+               texinfoindex = (unsigned short)MSG_ReadLittleShort(sb);
+               MSG_ReadLittleLong(sb); // skipping over dispinfo and surfaceFogVolumeID, both short
+               for (i = 0;i < MAXLIGHTMAPS;i++)
+                       surface->lightmapinfo->styles[i] = MSG_ReadByte(sb);
+               lightmapoffset = MSG_ReadLittleLong(sb);
+
+               // FIXME: validate edges, texinfo, etc?
+               if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
+                       Host_Error("Mod_VBSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
+               if ((unsigned int) texinfoindex >= (unsigned int) loadmodel->brushq1.numtexinfo)
+                       Host_Error("Mod_VBSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", texinfoindex, loadmodel->brushq1.numtexinfo);
+               if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
+                       Host_Error("Mod_VBSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
+
+               surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + texinfoindex;
+               surface->texture = loadmodel->data_textures + surface->lightmapinfo->texinfo->textureindex;
+
+               // Q2BSP doesn't use lightmaps on sky or warped surfaces (water), but still has a lightofs of 0
+               if (lightmapoffset == 0 && (surface->texture->q2flags & (Q2SURF_SKY | Q2SURF_WARP)))
+                       lightmapoffset = -1;
+
+               //surface->flags = surface->texture->flags;
+               //if (LittleShort(in->side))
+               //      surface->flags |= SURF_PLANEBACK;
+               //surface->plane = loadmodel->brush.data_planes + planenum;
+
+               surface->num_firstvertex = totalverts;
+               surface->num_vertices = numedges;
+               surface->num_firsttriangle = totaltris;
+               surface->num_triangles = numedges - 2;
+               totalverts += numedges;
+               totaltris += numedges - 2;
+
+               // convert edges back to a normal polygon
+               for (i = 0;i < surface->num_vertices;i++)
+               {
+                       int lindex = loadmodel->brushq1.surfedges[firstedge + i];
+                       float s, t;
+                       // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart)
+                       if (lindex >= 0)
+                               VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
+                       else
+                               VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
+                       s = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
+                       t = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
+                       (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width;
+                       (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height;
+                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0;
+                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0;
+                       (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = 0;
+               }
+
+               for (i = 0;i < surface->num_triangles;i++)
+               {
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex;
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex;
+                       (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex;
+               }
+
+               // compile additional data about the surface geometry
+               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_normal3f, r_smoothnormals_areaweighting.integer != 0);
+               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle), loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
+               BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex));
+
+               // generate surface extents information
+               texmins[0] = texmaxs[0] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
+               texmins[1] = texmaxs[1] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
+               for (i = 1;i < surface->num_vertices;i++)
+               {
+                       for (j = 0;j < 2;j++)
+                       {
+                               val = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3];
+                               texmins[j] = min(texmins[j], val);
+                               texmaxs[j] = max(texmaxs[j], val);
+                       }
+               }
+               for (i = 0;i < 2;i++)
+               {
+                       surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
+                       surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i];
+               }
+
+               smax = surface->lightmapinfo->extents[0] >> 4;
+               tmax = surface->lightmapinfo->extents[1] >> 4;
+               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+
+               // lighting info
+               surface->lightmaptexture = NULL;
+               surface->deluxemaptexture = r_texture_blanknormalmap;
+               if (lightmapoffset == -1)
+               {
+                       surface->lightmapinfo->samples = NULL;
+#if 1
+                       // give non-lightmapped water a 1x white lightmap
+                       if (!loadmodel->brush.isq2bsp && surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
+                       {
+                               surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
+                               surface->lightmapinfo->styles[0] = 0;
+                               memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
+                       }
+#endif
+               }
+               else
+                       surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + lightmapoffset;
+
+               // check if we should apply a lightmap to this
+               if (!(surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
+               {
+                       if (ssize > 256 || tsize > 256)
+                               Host_Error("Bad surface extents");
+
+                       if (lightmapsize < ssize)
+                               lightmapsize = ssize;
+                       if (lightmapsize < tsize)
+                               lightmapsize = tsize;
+
+                       totallightmapsamples += ssize*tsize;
+
+                       // force lightmap upload on first time seeing the surface
+                       //
+                       // additionally this is used by the later code to see if a
+                       // lightmap is needed on this surface (rather than duplicating the
+                       // logic above)
+                       loadmodel->brushq1.lightmapupdateflags[surfacenum] = true;
+                       loadmodel->lit = true;
+               }
+       }
+
+       // small maps (such as ammo boxes especially) don't need big lightmap
+       // textures, so this code tries to guess a good size based on
+       // totallightmapsamples (size of the lightmaps lump basically), as well as
+       // trying to max out the size if there is a lot of lightmap data to store
+       // additionally, never choose a lightmapsize that is smaller than the
+       // largest surface encountered (as it would fail)
+       i = lightmapsize;
+       for (lightmapsize = 64; (lightmapsize < i) && (lightmapsize < bound(128, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d)) && (totallightmapsamples > lightmapsize*lightmapsize); lightmapsize*=2)
+               ;
+
+       // now that we've decided the lightmap texture size, we can do the rest
+       if (cls.state != ca_dedicated)
+       {
+               int stainmapsize = 0;
+               mod_alloclightmap_state_t allocState;
+
+               Mod_AllocLightmap_Init(&allocState, loadmodel->mempool, lightmapsize, lightmapsize);
+               for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+               {
+                       int iu, iv, lightmapx = 0, lightmapy = 0;
+                       float u, v, ubase, vbase, uscale, vscale;
+
+                       if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                               continue;
+
+                       smax = surface->lightmapinfo->extents[0] >> 4;
+                       tmax = surface->lightmapinfo->extents[1] >> 4;
+                       ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                       tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+                       stainmapsize += ssize * tsize * 3;
+
+                       if (!lightmaptexture || !Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy))
+                       {
+                               // allocate a texture pool if we need it
+                               if (loadmodel->texturepool == NULL)
+                                       loadmodel->texturepool = R_AllocTexturePool();
+                               // could not find room, make a new lightmap
+                               loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
+                               loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
+                               loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
+                               loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
+                               if (loadmodel->brushq1.nmaplightdata)
+                                       loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va(vabuf, sizeof(vabuf), "deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, -1, NULL);
+                               lightmapnumber++;
+                               Mod_AllocLightmap_Reset(&allocState);
+                               Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
+                       }
+                       surface->lightmaptexture = lightmaptexture;
+                       surface->deluxemaptexture = deluxemaptexture;
+                       surface->lightmapinfo->lightmaporigin[0] = lightmapx;
+                       surface->lightmapinfo->lightmaporigin[1] = lightmapy;
+
+                       uscale = 1.0f / (float)lightmapsize;
+                       vscale = 1.0f / (float)lightmapsize;
+                       ubase = lightmapx * uscale;
+                       vbase = lightmapy * vscale;
+
+                       for (i = 0;i < surface->num_vertices;i++)
+                       {
+                               u = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0);
+                               v = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0);
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
+                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
+                               // LadyHavoc: calc lightmap data offset for vertex lighting to use
+                               iu = (int) u;
+                               iv = (int) v;
+                               (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
+                       }
+               }
+
+               if (cl_stainmaps.integer)
+               {
+                       // allocate stainmaps for permanent marks on walls and clear white
+                       unsigned char *stainsamples = NULL;
+                       stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize);
+                       memset(stainsamples, 255, stainmapsize);
+                       // assign pointers
+                       for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+                       {
+                               if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                                       continue;
+                               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+                               surface->lightmapinfo->stainsamples = stainsamples;
+                               stainsamples += ssize * tsize * 3;
+                       }
+               }
+       }
+
+       // generate ushort elements array if possible
+       if (loadmodel->surfmesh.data_element3s)
+               for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
+                       loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
+}
+
 // Valve BSP loader
 // Cloudwalk: Wasn't sober when I wrote this. I screamed and ran away at the face loader
 void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
@@ -7802,6 +8121,7 @@ void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
                                Host_Error("Mod_VBSP_Load: %s has invalid lump %i (offset %i, size %i, file size %i)\n", mod->name, i, offset, size, (int)sb.cursize);
                        MSG_InitReadBuffer(&lumpsb[i], sb.data + offset, size);
                }
+
                MSG_ReadLittleLong(&sb); // TODO support revision field
 
                mod->soundfromcenter = true;
@@ -7845,10 +8165,10 @@ void Mod_VBSP_Load(model_t *mod, void *buffer, void *bufferend)
                Mod_VBSP_LoadTextures(&lumpsb[HL2LUMP_TEXDATA/*?*/]);
                //Mod_VBSP_LoadLighting(&lumpsb[HL2LUMP_LIGHTING]);
                Mod_VBSP_LoadPlanes(&lumpsb[HL2LUMP_PLANES]);
-               //Mod_VBSP_LoadTexinfo(&lumpsb[HL2LUMP_TEXINFO]);
+               Mod_VBSP_LoadTexinfo(&lumpsb[HL2LUMP_TEXINFO]);
 
                // AHHHHHHH
-               //Mod_VBSP_LoadFaces(&lumpsb[HL2LUMP_FACES]);
+               Mod_VBSP_LoadFaces(&lumpsb[HL2LUMP_FACES]);
        }
 }