X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=ccd95dc1a226864f95867019ed9f0748fca781d3;hb=17eda31b40ed5d6c15834c39bc9f247b42902b42;hp=c742c1092dc75f5c495eb51cd9b9b5cb757222c9;hpb=8dcce44300385b12c46d494c06aadcfa35a8bc14;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index c742c109..ccd95dc1 100644 --- a/model_brush.c +++ b/model_brush.c @@ -26,6 +26,7 @@ qboolean hlbsp; // LordHavoc: true if it is a HalfLife BSP file (version 30) cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", true}; cvar_t halflifebsp = {"halflifebsp", "0"}; +cvar_t r_novis = {"r_novis", "0"}; /* =============== @@ -36,6 +37,7 @@ void Mod_BrushInit (void) { Cvar_RegisterVariable (&gl_subdivide_size); Cvar_RegisterVariable (&halflifebsp); + Cvar_RegisterVariable (&r_novis); memset (mod_novis, 0xff, sizeof(mod_novis)); } @@ -45,6 +47,21 @@ Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + +// if (!model || !model->nodes) +// Sys_Error ("Mod_PointInLeaf: bad model"); + + node = model->nodes; + do + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist]; + while (node->contents == 0); + + return (mleaf_t *)node; +} +/* +mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) { mnode_t *node; float d; @@ -68,7 +85,7 @@ mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) return NULL; // never reached } - +*/ /* =================== @@ -82,9 +99,10 @@ byte *Mod_DecompressVis (byte *in, model_t *model) byte *out; int row; - row = (model->numleafs+7)>>3; + row = (model->numleafs+7)>>3; out = decompressed; + /* if (!in) { // no vis info, so make all visible while (row) @@ -94,6 +112,7 @@ byte *Mod_DecompressVis (byte *in, model_t *model) } return decompressed; } + */ do { @@ -117,15 +136,56 @@ byte *Mod_DecompressVis (byte *in, model_t *model) byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) { - if (leaf == model->leafs) + if (r_novis.value || leaf == model->leafs || leaf->compressed_vis == NULL) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } -byte *mod_base; +extern byte *mod_base; extern cvar_t r_fullbrights; +rtexture_t *r_notexture; +texture_t r_notexture_mip; + +void Mod_SetupNoTexture() +{ + int x, y; + byte pix[16][16][4]; + + // create a simple checkerboard texture for the default + // LordHavoc: redesigned this to remove reliance on the palette and texture_t + for (y = 0;y < 16;y++) + { + for (x = 0;x < 16;x++) + { + if ((y < 8) ^ (x < 8)) + { + pix[y][x][0] = 128; + pix[y][x][1] = 128; + pix[y][x][2] = 128; + pix[y][x][3] = 255; + } + else + { + pix[y][x][0] = 64; + pix[y][x][1] = 64; + pix[y][x][2] = 64; + pix[y][x][3] = 255; + } + } + } + + r_notexture = R_LoadTexture("notexture", 16, 16, &pix[0][0][0], TEXF_MIPMAP | TEXF_RGBA); + + strcpy(r_notexture_mip.name, "notexture"); + r_notexture_mip.width = 16; + r_notexture_mip.height = 16; + r_notexture_mip.transparent = false; + r_notexture_mip.texture = r_notexture; + r_notexture_mip.glowtexture = NULL; +} + /* ================= Mod_LoadTextures @@ -133,13 +193,11 @@ Mod_LoadTextures */ void Mod_LoadTextures (lump_t *l) { - int i, j, num, max, altmax, bytesperpixel, freeimage, transparent, fullbrights; - miptex_t *mt; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - dmiptexlump_t *m; - byte *data; + int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs; + miptex_t *dmiptex; + texture_t *tx, *tx2, *anims[10], *altanims[10]; + dmiptexlump_t *m; + byte *data, *mtdata; if (!l->filelen) { @@ -152,139 +210,197 @@ void Mod_LoadTextures (lump_t *l) m->nummiptex = LittleLong (m->nummiptex); loadmodel->numtextures = m->nummiptex; - loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), va("%s texture headers", loadname)); - for (i=0 ; inummiptex ; i++) + // just to work around bounds checking when debugging with it (array index out of bounds error thing) + dofs = m->dataofs; + for (i = 0;i < m->nummiptex;i++) { - m->dataofs[i] = LittleLong(m->dataofs[i]); - if (m->dataofs[i] == -1) + dofs[i] = LittleLong(dofs[i]); + if (dofs[i] == -1) continue; - mt = (miptex_t *)((byte *)m + m->dataofs[i]); - mt->width = LittleLong (mt->width); - mt->height = LittleLong (mt->height); - for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); + dmiptex = (miptex_t *)((byte *)m + dofs[i]); + mtwidth = LittleLong (dmiptex->width); + mtheight = LittleLong (dmiptex->height); + mtdata = NULL; + j = LittleLong (dmiptex->offsets[0]); + if (j) + { + // texture included + if (j < 40 || j + mtwidth * mtheight > l->filelen) + Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name); + mtdata = (byte *)dmiptex + j; + } - if ( (mt->width & 15) || (mt->height & 15) ) - Sys_Error ("Texture %s is not 16 aligned", mt->name); + if ((mtwidth & 15) || (mtheight & 15)) + Host_Error ("Texture %s is not 16 aligned", dmiptex->name); // LordHavoc: rewriting the map texture loader for GLQuake - tx = Hunk_AllocName (sizeof(texture_t), loadname ); + tx = Hunk_AllocName (sizeof(texture_t), va("%s textures", loadname)); loadmodel->textures[i] = tx; - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = 0; - freeimage = TRUE; - bytesperpixel = 4; - fullbrights = FALSE; - transparent = FALSE; - data = loadimagepixels(mt->name, FALSE, tx->width, tx->height); - if (!data) // no external texture found + // LordHavoc: force all names to lowercase and make sure they are terminated while copying + for (j = 0;dmiptex->name[j] && j < 15;j++) { - freeimage = FALSE; - bytesperpixel = 1; - if (!hlbsp && mt->offsets[0]) // texture included + if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z') + tx->name[j] = dmiptex->name[j] + ('a' - 'A'); + else + tx->name[j] = dmiptex->name[j]; + } + for (;j < 16;j++) + tx->name[j] = 0; + + if (!tx->name[0]) + { + Con_Printf("warning: unnamed texture in %s\n", loadname); + sprintf(tx->name, "unnamed%i", i); + } + + tx->transparent = false; + data = loadimagepixels(tx->name, false, 0, 0); + if (data) + { + if (!hlbsp && !strncmp(tx->name,"sky",3) && image_width == 256 && image_height == 128) // LordHavoc: HL sky textures are entirely unrelated { - data = (byte *)((int) mt + mt->offsets[0]); - if (r_fullbrights.value && mt->name[0] != '*') - { - for (j = 0;j < tx->width*tx->height;j++) - if (data[j] >= 224) // fullbright - { - fullbrights = TRUE; - break; - } - } + tx->width = 0; + tx->height = 0; + tx->transparent = false; + tx->texture = NULL; + tx->glowtexture = NULL; + R_InitSky (data, 4); } - else // no texture, and no external replacement texture was found + else { - tx->width = tx->height = 16; - data = (byte *)((int) r_notexture_mip + r_notexture_mip->offsets[0]); + tx->width = mtwidth; + tx->height = mtheight; + tx->transparent = Image_CheckAlpha(data, image_width * image_height, true); + tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE); + tx->glowtexture = NULL; } + qfree(data); } else { - for (j = 0;j < image_width*image_height;j++) - if (data[j*4+3] < 255) + if (hlbsp) + { + if (mtdata) // texture included { - transparent = TRUE; - break; + data = W_ConvertWAD3Texture(dmiptex); + if (data) + { + tx->width = mtwidth; + tx->height = mtheight; + tx->transparent = Image_CheckAlpha(data, mtwidth * mtheight, true); + tx->texture = R_LoadTexture (tx->name, mtwidth, mtheight, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE); + tx->glowtexture = NULL; + qfree(data); + } + } + if (!data) + { + data = W_GetTexture(tx->name); + // get the size from the wad texture + if (data) + { + tx->width = image_width; + tx->height = image_height; + tx->transparent = Image_CheckAlpha(data, image_width * image_height, true); + tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE); + tx->glowtexture = NULL; + qfree(data); + } + } + if (!data) + { + tx->width = 16; + tx->height = 16; + tx->transparent = false; + tx->texture = r_notexture; + tx->glowtexture = NULL; } - } - if (!hlbsp && !strncmp(mt->name,"sky",3)) // LordHavoc: HL sky textures are entirely unrelated - { - tx->transparent = FALSE; - R_InitSky (data, bytesperpixel); - } - else - { - tx->transparent = transparent; - if (fullbrights) - { - char name[64]; - byte *data2; - data2 = malloc(tx->width*tx->height); - for (j = 0;j < tx->width*tx->height;j++) - data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights - tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, data2, true, 0, 1); - strcpy(name, tx->name); - strcat(name, "_glow"); - for (j = 0;j < tx->width*tx->height;j++) - data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights - tx->gl_glowtexturenum = GL_LoadTexture (name, tx->width, tx->height, data2, true, 0, 1); - free(data2); } else { - tx->gl_texturenum = GL_LoadTexture (tx->name, tx->width, tx->height, data, true, transparent, bytesperpixel); - tx->gl_glowtexturenum = 0; + if (!strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128) + { + tx->width = mtwidth; + tx->height = mtheight; + tx->transparent = false; + tx->texture = NULL; + tx->glowtexture = NULL; + R_InitSky (mtdata, 1); + } + else + { + if (mtdata) // texture included + { + int fullbrights; + data = mtdata; + tx->width = mtwidth; + tx->height = mtheight; + tx->transparent = false; + fullbrights = false; + if (r_fullbrights.value && tx->name[0] != '*') + { + for (j = 0;j < tx->width*tx->height;j++) + { + if (data[j] >= 224) // fullbright + { + fullbrights = true; + break; + } + } + } + if (fullbrights) + { + char name[64]; + byte *data2; + data2 = qmalloc(tx->width*tx->height); + for (j = 0;j < tx->width*tx->height;j++) + data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights + tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE); + strcpy(name, tx->name); + strcat(name, "_glow"); + for (j = 0;j < tx->width*tx->height;j++) + data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights + tx->glowtexture = R_LoadTexture (name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE); + qfree(data2); + } + else + { + tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data, TEXF_MIPMAP | TEXF_PRECACHE); + tx->glowtexture = NULL; + } + } + else // no texture, and no external replacement texture was found + { + tx->width = 16; + tx->height = 16; + tx->transparent = false; + tx->texture = r_notexture; + tx->glowtexture = NULL; + } + } } } - if (freeimage) - free(data); - - /* - pixels = mt->width*mt->height/64*85; - tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); - loadmodel->textures[i] = tx; - - memcpy (tx->name, mt->name, sizeof(tx->name)); - tx->width = mt->width; - tx->height = mt->height; - for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); - // the pixels immediately follow the structures - memcpy ( tx+1, mt+1, pixels); - - - if (!strncmp(mt->name,"sky",3)) - R_InitSky (tx); - else - tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, 1); - */ } // // sequence the animations // - for (i=0 ; inummiptex ; i++) + for (i = 0;i < m->nummiptex;i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; - if (tx->anim_next) - continue; // allready sequenced + if (tx->anim_total) + continue; // already sequenced - // find the number of frames in the animation + // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); max = tx->name[1]; altmax = 0; - if (max >= 'a' && max <= 'z') - max -= 'a' - 'A'; if (max >= '0' && max <= '9') { max -= '0'; @@ -292,9 +408,9 @@ void Mod_LoadTextures (lump_t *l) anims[max] = tx; max++; } - else if (max >= 'A' && max <= 'J') + else if (max >= 'a' && max <= 'j') { - altmax = max - 'A'; + altmax = max - 'a'; max = 0; altanims[altmax] = tx; altmax++; @@ -302,7 +418,7 @@ void Mod_LoadTextures (lump_t *l) else Host_Error ("Bad animating texture %s", tx->name); - for (j=i+1 ; jnummiptex ; j++) + for (j = i + 1;j < m->nummiptex;j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') @@ -311,8 +427,6 @@ void Mod_LoadTextures (lump_t *l) continue; num = tx2->name[1]; - if (num >= 'a' && num <= 'z') - num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; @@ -320,42 +434,39 @@ void Mod_LoadTextures (lump_t *l) if (num+1 > max) max = num + 1; } - else if (num >= 'A' && num <= 'J') + else if (num >= 'a' && num <= 'j') { - num = num - 'A'; + num = num - 'a'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else - Sys_Error ("Bad animating texture %s", tx->name); + Host_Error ("Bad animating texture %s", tx->name); } - -#define ANIM_CYCLE 2 - // link them all together - for (j=0 ; jname); - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = anims[ (j+1)%max ]; + Host_Error ("Missing frame %i of %s", j, tx->name); + tx2->anim_total = max; if (altmax) tx2->alternate_anims = altanims[0]; + for (k = 0;k < 10;k++) + tx2->anim_frames[k] = anims[j]; } - for (j=0 ; jname); - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[ (j+1)%altmax ]; + Host_Error ("Missing frame %i of %s", j, tx->name); + tx2->anim_total = altmax; if (max) tx2->alternate_anims = anims[0]; + for (k = 0;k < 10;k++) + tx2->anim_frames[k] = altanims[j]; } } } @@ -372,11 +483,9 @@ void Mod_LoadLighting (lump_t *l) byte d; char litfilename[1024]; loadmodel->lightdata = NULL; - if (!l->filelen) - return; if (hlbsp) // LordHavoc: load the colored lighting data straight { - loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + loadmodel->lightdata = Hunk_AllocName ( l->filelen, va("%s lightmaps", loadname)); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); } else // LordHavoc: bsp version 29 (normal white lighting) @@ -393,6 +502,7 @@ void Mod_LoadLighting (lump_t *l) i = LittleLong(((int *)data)[1]); if (i == 1) { + Con_DPrintf("%s loaded", litfilename); loadmodel->lightdata = data + 8; return; } @@ -403,7 +513,9 @@ void Mod_LoadLighting (lump_t *l) Con_Printf("Corrupt .lit file (old version?), ignoring\n"); } // LordHavoc: oh well, expand the white lighting data - loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, litfilename); + if (!l->filelen) + return; + loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, va("%s lightmaps", loadname)); in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); @@ -430,7 +542,7 @@ void Mod_LoadVisibility (lump_t *l) loadmodel->visdata = NULL; return; } - loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + loadmodel->visdata = Hunk_AllocName ( l->filelen, va("%s visdata", loadname)); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } @@ -450,7 +562,7 @@ void Mod_LoadEntities (lump_t *l) loadmodel->entities = NULL; return; } - loadmodel->entities = Hunk_AllocName ( l->filelen, loadname); + loadmodel->entities = Hunk_AllocName ( l->filelen, va("%s entities", loadname)); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); if (isworldmodel) @@ -471,9 +583,9 @@ void Mod_LoadVertexes (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s vertices", loadname)); loadmodel->vertexes = out; loadmodel->numvertexes = count; @@ -499,9 +611,9 @@ void Mod_LoadSubmodels (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s submodels", loadname)); loadmodel->submodels = out; loadmodel->numsubmodels = count; @@ -535,9 +647,9 @@ void Mod_LoadEdges (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + out = Hunk_AllocName ( (count + 1) * sizeof(*out), va("%s edges", loadname)); loadmodel->edges = out; loadmodel->numedges = count; @@ -558,61 +670,42 @@ void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; - int i, j, count; + int i, j, k, count; int miptex; - float len1, len2; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s texinfo", loadname)); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); - len1 = Length (out->vecs[0]); - len2 = Length (out->vecs[1]); - len1 = (len1 + len2)/2; - if (len1 < 0.32) - out->mipadjust = 4; - else if (len1 < 0.49) - out->mipadjust = 3; - else if (len1 < 0.99) - out->mipadjust = 2; - else - out->mipadjust = 1; -#if 0 - if (len1 + len2 < 0.001) - out->mipadjust = 1; // don't crash - else - out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); -#endif + for (k=0 ; k<2 ; k++) + for (j=0 ; j<4 ; j++) + out->vecs[k][j] = LittleFloat (in->vecs[k][j]); miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); if (!loadmodel->textures) { - out->texture = r_notexture_mip; // checkerboard texture + out->texture = &r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= loadmodel->numtextures) - Sys_Error ("miptex >= loadmodel->numtextures"); + Host_Error ("miptex >= loadmodel->numtextures"); out->texture = loadmodel->textures[miptex]; if (!out->texture) { - out->texture = r_notexture_mip; // texture not found + out->texture = &r_notexture_mip; // checkerboard texture out->flags = 0; } - else - out->texture->transparent = FALSE; } } } @@ -665,8 +758,9 @@ void CalcSurfaceExtents (msurface_t *s) s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; - if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) - Sys_Error ("Bad surface extents"); +// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512) + if ((tex->flags & TEX_SPECIAL) == 0 && (s->extents[i]+1) > (256*16)) + Host_Error ("Bad surface extents"); } } @@ -688,9 +782,9 @@ void Mod_LoadFaces (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s faces", loadname)); loadmodel->surfaces = out; loadmodel->numsurfaces = count; @@ -741,7 +835,7 @@ void Mod_LoadFaces (lump_t *l) // if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent if (out->texinfo->texture->name[0] == '*') // LordHavoc: faster check { - out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED | SURF_LIGHTBOTHSIDES); // LordHavoc: some turbulent textures should be fullbright and solid if (!strncmp(out->texinfo->texture->name,"*lava",5) || !strncmp(out->texinfo->texture->name,"*teleport",9) @@ -787,20 +881,20 @@ void Mod_LoadNodes (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s nodes", loadname)); loadmodel->nodes = out; loadmodel->numnodes = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); - } +// for (j=0 ; j<3 ; j++) +// { +// out->mins[j] = LittleShort (in->mins[j]); +// out->maxs[j] = LittleShort (in->maxs[j]); +// } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; @@ -834,9 +928,9 @@ void Mod_LoadLeafs (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s leafs", loadname)); loadmodel->leafs = out; loadmodel->numleafs = count; @@ -845,8 +939,8 @@ void Mod_LoadLeafs (lump_t *l) { for (j=0 ; j<3 ; j++) { - out->minmaxs[j] = LittleShort (in->mins[j]); - out->minmaxs[3+j] = LittleShort (in->maxs[j]); + out->mins[j] = LittleShort (in->mins[j]); + out->maxs[j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); @@ -861,7 +955,7 @@ void Mod_LoadLeafs (lump_t *l) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; - out->efrags = NULL; +// out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; @@ -891,42 +985,85 @@ void Mod_LoadClipnodes (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname)); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; + if (hlbsp) + { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -36; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 36; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -32; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -18; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 18; + } + else + { + hull = &loadmodel->hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + + hull = &loadmodel->hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + } for (i=0 ; iplanenum = LittleLong(in->planenum); out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); + if (out->children[0] >= count || out->children[1] >= count) + Host_Error("Corrupt clipping hull (out of range child)\n"); } } @@ -939,33 +1076,27 @@ Duplicate the drawing hull structure as a clipping hull */ void Mod_MakeHull0 (void) { - mnode_t *in, *child; + mnode_t *in; dclipnode_t *out; - int i, j, count; + int i, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname)); hull->clipnodes = out; hull->firstclipnode = 0; - hull->lastclipnode = count-1; + hull->lastclipnode = count - 1; hull->planes = loadmodel->planes; - for (i=0 ; iplanenum = in->plane - loadmodel->planes; - for (j=0 ; j<2 ; j++) - { - child = in->children[j]; - if (child->contents < 0) - out->children[j] = child->contents; - else - out->children[j] = child - loadmodel->nodes; - } + out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes; + out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes; } } @@ -982,9 +1113,9 @@ void Mod_LoadMarksurfaces (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname)); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; @@ -993,7 +1124,7 @@ void Mod_LoadMarksurfaces (lump_t *l) { j = LittleShort(in[i]); if (j >= loadmodel->numsurfaces) - Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + Host_Error ("Mod_ParseMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } @@ -1010,9 +1141,9 @@ void Mod_LoadSurfedges (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*sizeof(*out), loadname); + out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname)); loadmodel->surfedges = out; loadmodel->numsurfedges = count; @@ -1037,9 +1168,9 @@ void Mod_LoadPlanes (lump_t *l) in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + out = Hunk_AllocName ( count*2*sizeof(*out), va("%s planes", loadname)); loadmodel->planes = out; loadmodel->numplanes = count; @@ -1061,6 +1192,679 @@ void Mod_LoadPlanes (lump_t *l) } } +#define MAX_POINTS_ON_WINDING 64 + +typedef struct +{ + int numpoints; + vec3_t points[8]; // variable sized +} +winding_t; + +/* +================== +NewWinding +================== +*/ +winding_t *NewWinding (int points) +{ + winding_t *w; + int size; + + if (points > MAX_POINTS_ON_WINDING) + Host_Error("NewWinding: too many points\n"); + + size = (int)((winding_t *)0)->points[points]; + w = malloc (size); + memset (w, 0, size); + + return w; +} + +void FreeWinding (winding_t *w) +{ + free (w); +} + +/* +================= +BaseWindingForPlane +================= +*/ +winding_t *BaseWindingForPlane (mplane_t *p) +{ + vec3_t org, vright, vup; + winding_t *w; + + VectorVectors(p->normal, vright, vup); + + VectorScale (vup, 65536, vup); + VectorScale (vright, 65536, vright); + + // project a really big axis aligned box onto the plane + w = NewWinding (4); + + VectorScale (p->normal, p->dist, org); + + VectorSubtract (org, vright, w->points[0]); + VectorAdd (w->points[0], vup, w->points[0]); + + VectorAdd (org, vright, w->points[1]); + VectorAdd (w->points[1], vup, w->points[1]); + + VectorAdd (org, vright, w->points[2]); + VectorSubtract (w->points[2], vup, w->points[2]); + + VectorSubtract (org, vright, w->points[3]); + VectorSubtract (w->points[3], vup, w->points[3]); + + w->numpoints = 4; + + return w; +} + +/* +================== +ClipWinding + +Clips the winding to the plane, returning the new winding on the positive side +Frees the input winding. +If keepon is true, an exactly on-plane winding will be saved, otherwise +it will be clipped away. +================== +*/ +winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon) +{ + vec_t dists[MAX_POINTS_ON_WINDING + 1]; + int sides[MAX_POINTS_ON_WINDING + 1]; + int counts[3]; + vec_t dot; + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *neww; + int maxpts; + + counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + + // determine sides for each point + for (i = 0;i < in->numpoints;i++) + { + dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist; + if (dot > ON_EPSILON) + sides[i] = SIDE_FRONT; + else if (dot < -ON_EPSILON) + sides[i] = SIDE_BACK; + else + sides[i] = SIDE_ON; + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + if (keepon && !counts[0] && !counts[1]) + return in; + + if (!counts[0]) + { + FreeWinding (in); + return NULL; + } + if (!counts[1]) + return in; + + maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors + neww = NewWinding (maxpts); + + for (i = 0;i < in->numpoints;i++) + { + p1 = in->points[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, neww->points[neww->numpoints]); + neww->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, neww->points[neww->numpoints]); + neww->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + p2 = in->points[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j = 0;j < 3;j++) + { // avoid round off error when possible + if (split->normal[j] == 1) + mid[j] = split->dist; + else if (split->normal[j] == -1) + mid[j] = -split->dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, neww->points[neww->numpoints]); + neww->numpoints++; + } + + if (neww->numpoints > maxpts) + Host_Error ("ClipWinding: points exceeded estimate"); + + // free the original winding + FreeWinding (in); + + return neww; +} + + +/* +================== +DivideWinding + +Divides a winding by a plane, producing one or two windings. The +original winding is not damaged or freed. If only on one side, the +returned winding will be the input winding. If on both sides, two +new windings will be created. +================== +*/ +void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back) +{ + vec_t dists[MAX_POINTS_ON_WINDING + 1]; + int sides[MAX_POINTS_ON_WINDING + 1]; + int counts[3]; + vec_t dot; + int i, j; + vec_t *p1, *p2; + vec3_t mid; + winding_t *f, *b; + int maxpts; + + counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + + // determine sides for each point + for (i = 0;i < in->numpoints;i++) + { + dot = DotProduct (in->points[i], split->normal); + dot -= split->dist; + dists[i] = dot; + if (dot > ON_EPSILON) sides[i] = SIDE_FRONT; + else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK; + else sides[i] = SIDE_ON; + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *front = *back = NULL; + + if (!counts[0]) + { + *back = in; + return; + } + if (!counts[1]) + { + *front = in; + return; + } + + maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors + + *front = f = NewWinding (maxpts); + *back = b = NewWinding (maxpts); + + for (i = 0;i < in->numpoints;i++) + { + p1 = in->points[i]; + + if (sides[i] == SIDE_ON) + { + VectorCopy (p1, f->points[f->numpoints]); + f->numpoints++; + VectorCopy (p1, b->points[b->numpoints]); + b->numpoints++; + continue; + } + + if (sides[i] == SIDE_FRONT) + { + VectorCopy (p1, f->points[f->numpoints]); + f->numpoints++; + } + else if (sides[i] == SIDE_BACK) + { + VectorCopy (p1, b->points[b->numpoints]); + b->numpoints++; + } + + if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) + continue; + + // generate a split point + p2 = in->points[(i+1)%in->numpoints]; + + dot = dists[i] / (dists[i]-dists[i+1]); + for (j = 0;j < 3;j++) + { // avoid round off error when possible + if (split->normal[j] == 1) + mid[j] = split->dist; + else if (split->normal[j] == -1) + mid[j] = -split->dist; + else + mid[j] = p1[j] + dot*(p2[j]-p1[j]); + } + + VectorCopy (mid, f->points[f->numpoints]); + f->numpoints++; + VectorCopy (mid, b->points[b->numpoints]); + b->numpoints++; + } + + if (f->numpoints > maxpts || b->numpoints > maxpts) + Host_Error ("DivideWinding: points exceeded estimate"); +} + +typedef struct portal_s +{ + mplane_t plane; + mnode_t *nodes[2]; // [0] = front side of plane + struct portal_s *next[2]; + winding_t *winding; + struct portal_s *chain; // all portals are linked into a list +} +portal_t; + +static portal_t *portalchain; + +/* +=========== +AllocPortal +=========== +*/ +portal_t *AllocPortal (void) +{ + portal_t *p; + p = malloc(sizeof(portal_t)); + memset(p, 0, sizeof(portal_t)); + p->chain = portalchain; + portalchain = p; + return p; +} + +void Mod_FinalizePortals() +{ + int i, j, numportals, numpoints; + portal_t *p, *pnext; + mportal_t *portal; + mvertex_t *point; + mleaf_t *leaf, *endleaf; + winding_t *w; + + // recalculate bounding boxes for all leafs (because qbsp is very sloppy) + leaf = loadmodel->leafs; + endleaf = leaf + loadmodel->numleafs; + for (;leaf < endleaf;leaf++) + { + VectorSet( 2000000000, 2000000000, 2000000000, leaf->mins); + VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs); + } + p = portalchain; + while(p) + { + if (p->winding) + { + for (i = 0;i < 2;i++) + { + leaf = (mleaf_t *)p->nodes[i]; + w = p->winding; + for (j = 0;j < w->numpoints;j++) + { + if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0]; + if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1]; + if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2]; + if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0]; + if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1]; + if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2]; + } + } + } + p = p->chain; + } + + // tally up portal and point counts + p = portalchain; + numportals = 0; + numpoints = 0; + while(p) + { + if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID) + { + numportals += 2; + numpoints += p->winding->numpoints * 2; + } + p = p->chain; + } + loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name)); + loadmodel->numportals = numportals; + loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name)); + loadmodel->numportalpoints = numpoints; + // clear all leaf portal chains + for (i = 0;i < loadmodel->numleafs;i++) + loadmodel->leafs[i].portals = NULL; + // process all portals in the global portal chain, while freeing them + portal = loadmodel->portals; + point = loadmodel->portalpoints; + p = portalchain; + portalchain = NULL; + while (p) + { + pnext = p->chain; + + if (p->winding) + { + // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides + if (p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID) + { + // first make the back to front portal (forward portal) + portal->points = point; + portal->numpoints = p->winding->numpoints; + portal->plane.dist = p->plane.dist; + VectorCopy(p->plane.normal, portal->plane.normal); + portal->here = (mleaf_t *)p->nodes[1]; + portal->past = (mleaf_t *)p->nodes[0]; + // copy points + for (j = 0;j < portal->numpoints;j++) + { + VectorCopy(p->winding->points[j], point->position); + point++; + } + + // link into leaf's portal chain + portal->next = portal->here->portals; + portal->here->portals = portal; + + // advance to next portal + portal++; + + // then make the front to back portal (backward portal) + portal->points = point; + portal->numpoints = p->winding->numpoints; + portal->plane.dist = -p->plane.dist; + VectorNegate(p->plane.normal, portal->plane.normal); + portal->here = (mleaf_t *)p->nodes[0]; + portal->past = (mleaf_t *)p->nodes[1]; + // copy points + for (j = portal->numpoints - 1;j >= 0;j--) + { + VectorCopy(p->winding->points[j], point->position); + point++; + } + + // link into leaf's portal chain + portal->next = portal->here->portals; + portal->here->portals = portal; + + // advance to next portal + portal++; + } + FreeWinding(p->winding); + } + free(p); + p = pnext; + } +} + +/* +============= +AddPortalToNodes +============= +*/ +void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back) +{ + if (!front) + Host_Error ("AddPortalToNodes: NULL front node"); + if (!back) + Host_Error ("AddPortalToNodes: NULL back node"); + if (p->nodes[0] || p->nodes[1]) + Host_Error ("AddPortalToNodes: already included"); + // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides + + p->nodes[0] = front; + p->next[0] = (portal_t *)front->portals; + front->portals = (mportal_t *)p; + + p->nodes[1] = back; + p->next[1] = (portal_t *)back->portals; + back->portals = (mportal_t *)p; +} + +/* +============= +RemovePortalFromNode +============= +*/ +void RemovePortalFromNodes(portal_t *portal) +{ + int i; + mnode_t *node; + void **portalpointer; + portal_t *t; + for (i = 0;i < 2;i++) + { + node = portal->nodes[i]; + + portalpointer = (void **) &node->portals; + while (1) + { + t = *portalpointer; + if (!t) + Host_Error ("RemovePortalFromNodes: portal not in leaf"); + + if (t == portal) + { + if (portal->nodes[0] == node) + { + *portalpointer = portal->next[0]; + portal->nodes[0] = NULL; + } + else if (portal->nodes[1] == node) + { + *portalpointer = portal->next[1]; + portal->nodes[1] = NULL; + } + else + Host_Error ("RemovePortalFromNodes: portal not bounding leaf"); + break; + } + + if (t->nodes[0] == node) + portalpointer = (void **) &t->next[0]; + else if (t->nodes[1] == node) + portalpointer = (void **) &t->next[1]; + else + Host_Error ("RemovePortalFromNodes: portal not bounding leaf"); + } + } +} + +void Mod_RecursiveNodePortals (mnode_t *node) +{ + int side; + mnode_t *front, *back, *other_node; + mplane_t clipplane, *plane; + portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp; + winding_t *nodeportalwinding, *frontwinding, *backwinding; + + // CheckLeafPortalConsistancy (node); + + // if a leaf, we're done + if (node->contents) + return; + + plane = node->plane; + + front = node->children[0]; + back = node->children[1]; + if (front == back) + Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy"); + + // create the new portal by generating a polygon for the node plane, + // and clipping it by all of the other portals (which came from nodes above this one) + nodeportal = AllocPortal (); + nodeportal->plane = *node->plane; + + nodeportalwinding = BaseWindingForPlane (node->plane); + side = 0; // shut up compiler warning + for (portal = (portal_t *)node->portals;portal;portal = portal->next[side]) + { + clipplane = portal->plane; + if (portal->nodes[0] == portal->nodes[1]) + Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)"); + if (portal->nodes[0] == node) + side = 0; + else if (portal->nodes[1] == node) + { + clipplane.dist = -clipplane.dist; + VectorNegate (clipplane.normal, clipplane.normal); + side = 1; + } + else + Host_Error ("Mod_RecursiveNodePortals: mislinked portal"); + + nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true); + if (!nodeportalwinding) + { + printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n"); + break; + } + } + + if (nodeportalwinding) + { + // if the plane was not clipped on all sides, there was an error + nodeportal->winding = nodeportalwinding; + AddPortalToNodes (nodeportal, front, back); + } + + // split the portals of this node along this node's plane and assign them to the children of this node + // (migrating the portals downward through the tree) + for (portal = (portal_t *)node->portals;portal;portal = nextportal) + { + if (portal->nodes[0] == portal->nodes[1]) + Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)"); + if (portal->nodes[0] == node) + side = 0; + else if (portal->nodes[1] == node) + side = 1; + else + Host_Error ("Mod_RecursiveNodePortals: mislinked portal"); + nextportal = portal->next[side]; + + other_node = portal->nodes[!side]; + RemovePortalFromNodes (portal); + + // cut the portal into two portals, one on each side of the node plane + DivideWinding (portal->winding, plane, &frontwinding, &backwinding); + + if (!frontwinding) + { + if (side == 0) + AddPortalToNodes (portal, back, other_node); + else + AddPortalToNodes (portal, other_node, back); + continue; + } + if (!backwinding) + { + if (side == 0) + AddPortalToNodes (portal, front, other_node); + else + AddPortalToNodes (portal, other_node, front); + continue; + } + + // the winding is split + splitportal = AllocPortal (); + temp = splitportal->chain; + *splitportal = *portal; + splitportal->chain = temp; + splitportal->winding = backwinding; + FreeWinding (portal->winding); + portal->winding = frontwinding; + + if (side == 0) + { + AddPortalToNodes (portal, front, other_node); + AddPortalToNodes (splitportal, back, other_node); + } + else + { + AddPortalToNodes (portal, other_node, front); + AddPortalToNodes (splitportal, other_node, back); + } + } + + Mod_RecursiveNodePortals(front); + Mod_RecursiveNodePortals(back); +} + +/* +void Mod_MakeOutsidePortals(mnode_t *node) +{ + int i, j; + portal_t *p, *portals[6]; + mnode_t *outside_node; + + outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name); + outside_node->contents = CONTENTS_SOLID; + outside_node->portals = NULL; + + for (i = 0;i < 3;i++) + { + for (j = 0;j < 2;j++) + { + portals[j*3 + i] = p = AllocPortal (); + memset (&p->plane, 0, sizeof(mplane_t)); + p->plane.normal[i] = j ? -1 : 1; + p->plane.dist = -65536; + p->winding = BaseWindingForPlane (&p->plane); + if (j) + AddPortalToNodes (p, outside_node, node); + else + AddPortalToNodes (p, node, outside_node); + } + } + + // clip the basewindings by all the other planes + for (i = 0;i < 6;i++) + { + for (j = 0;j < 6;j++) + { + if (j == i) + continue; + portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true); + } + } +} +*/ + +void Mod_MakePortals() +{ +// Con_Printf("building portals for %s\n", loadmodel->name); + + portalchain = NULL; +// Mod_MakeOutsidePortals (loadmodel->nodes); + Mod_RecursiveNodePortals (loadmodel->nodes); + Mod_FinalizePortals(); +} + /* ================= Mod_LoadBrushModel @@ -1077,8 +1881,8 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer) header = (dheader_t *)buffer; i = LittleLong (header->version); - if (i != BSPVERSION & i != 30) - Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION); + if (i != BSPVERSION && i != 30) + Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION); hlbsp = i == 30; halflifebsp.value = hlbsp; @@ -1110,13 +1914,15 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer) Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); + + Mod_MakePortals(); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // - for (i=0 ; inumsubmodels ; i++) + for (i = 0;i < mod->numsubmodels;i++) { bm = &mod->submodels[i]; @@ -1124,7 +1930,7 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer) for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes-1; + mod->hulls[j].lastclipnode = mod->numclipnodes - 1; } mod->firstmodelsurface = bm->firstface; @@ -1137,7 +1943,7 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer) mod->numleafs = bm->visleafs; - if (isworldmodel && i < (mod->numsubmodels-1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels) + if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels) { // duplicate the basic information char name[10];