8dadfb4878
Cmake: Add FTE_WERROR option, defaults to true in debug builds and off in release builds (in case future compilers have issues). Cmake: Pull in libXscreensaver so we don't get interrupted by screensavers when playing demos. Make: Added `make webcl-rel` for a web build without server bloat (eg for sites focused on demo playback. Yes, this means you XantoM). fteqcc: Include the decompiler in fteqcc (non-gui) builds ('-d' arg). fteqcc: Decompiler can now mostly handle hexen2 mods without any unknown opcodes. Allow ezHud and OpenSSL to be compiled as in-engine plugins, potentially for web and windows ports respectively. Web: Fix support for ogg vorbis. Add support for voip. Web: Added basic support for WebXR. QTV: Don't try seeking on unseekable qtv streams. Don't spam when developer 1 is set. QTV: add support for some eztv extensions. MVD: added hack to use ktx's vweps in mvd where mvdsv doesn't bother to record the info. qwfwd: hack around a hack in qwfwd, allowing it to work again. recording: favour qwd in single player, instead of mvd. Protocol: reduce client memory used for precache names. Bump maximum precache counts - some people are just abusive, yes you Orl. hexen2: add enough clientside protocol compat to play the demo included with h2mp. lacks effects. in_xflip: restored this setting. fs_hidesyspaths: new cvar, defaults to enabled so you won't find your username or whatever turning up in screenshots or the like. change it to 0 before debuging stuff eg via 'path'. gl_overbright_models: Added cvar to match QS. netchan: Added MTU determination, we'll no longer fail to connect when routers stupidly drop icmp packets. Win: try a few other versions of xinput too. CSQC: Added a CSQC_GenerateMaterial function, to give the csqc a chance to generate custom materials. MenuQC: Added support for the skeletal objects API.
4210 lines
112 KiB
C
4210 lines
112 KiB
C
/*
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
/*
|
|
TODO:
|
|
Lightmaps - skip ssbump stuff properly. currently extra lightstyles are screwed.
|
|
Ent Lighting - leafs have some list of light 'cubes' that define lighting, allowing for ents to get backlit etc properly.
|
|
Areaportals - the server 'needs' a way to specify areaportals on a per-player basis. for now the gamecode will have to explicitly force-open most of the portals in the game (map_noareas can be used as a workaround).
|
|
Static Props - these are solid, but use the visible mesh for collisions instead of the special/simpler collision mesh. This makes it a bit easier to climb up them, which may be a gameplay issue.
|
|
Detail Props - we don't attempt to handle these. They need manual batching or something (eg clutter-shader stuff). Their loss should not affect gameplay much as they can be disabled in HL2 too.
|
|
Dynamic Lighting - r_dynamic not enabled.
|
|
Realtime Lighting - screwed. Doesn't light all world surfaces for some reason.
|
|
RBE(Bullet) - probably screwed.
|
|
Skyboxes - doesn't handle the whole per-face skies nor weird hdr encoding.
|
|
Fog - no fog here... this results in issues where the pvs provides distance culling.
|
|
Load Times - materials are loaded on a single thread. this gets slow.
|
|
Materials - all kinds of screwed.
|
|
Portal2 Gels - we need to repurpose stainmap code or something.
|
|
Refraction - this stuff is horribly expensive.
|
|
*/
|
|
|
|
#include "../plugin.h"
|
|
#include "quakedef.h"
|
|
#ifdef HAVE_CLIENT
|
|
#include "glquake.h"
|
|
#endif
|
|
#include "com_mesh.h"
|
|
#include "com_bih.h"
|
|
|
|
static plugfsfuncs_t *filefuncs;
|
|
static plugmodfuncs_t *modfuncs;
|
|
static plugthreadfuncs_t *threadfuncs;
|
|
|
|
#define Q_strncpyz Q_strlcpy
|
|
float VectorNormalize2(const vec3_t in,vec3_t out) {float l = sqrt(DotProduct(in,in)); if (l) l = 1.0/l; VectorScale(in,l,out); return l;}
|
|
#define VectorNormalize(v) VectorNormalize2(v,v)
|
|
fte_inlinebody float M_LinearToSRGB(float x, float mag);
|
|
vec3_t vec3_origin;
|
|
static refdef_t *refdef;
|
|
|
|
static vec3_t modelorg;
|
|
static qbyte *frustumvis;
|
|
static int vbsp_nodesequence; //to track which nodes need walking
|
|
static int vbsp_surfsequence; //so we don't draw the same surface if its found multiple ways
|
|
|
|
r_qrenderer_t qrenderer = QR_OPENGL;
|
|
|
|
#define MAX_VBSP_AREAS MAX_Q2MAP_AREAS
|
|
#define SURF_OFFNODE SURF_DRAWBACKGROUND //might as well just reuse that.
|
|
|
|
static cvar_t *hl2_novis;
|
|
static cvar_t *hl2_displacement_scale;
|
|
static cvar_t *hl2_favour_ldr;
|
|
static cvar_t *map_noareas;
|
|
static cvar_t *map_autoopenportals;
|
|
static cvar_t *hl2_contents_remap;
|
|
|
|
char *Q_strlwr(char *s)
|
|
{
|
|
char *ret=s;
|
|
while(*s)
|
|
{
|
|
if (*s >= 'A' && *s <= 'Z')
|
|
*s=*s-'A'+'a';
|
|
s++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)
|
|
{
|
|
int i;
|
|
vec_t val;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
val = v[i];
|
|
if (val < mins[i])
|
|
mins[i] = val;
|
|
if (val > maxs[i])
|
|
maxs[i] = val;
|
|
}
|
|
}
|
|
|
|
void ClearBounds (vec3_t mins, vec3_t maxs)
|
|
{
|
|
mins[0] = mins[1] = mins[2] = FLT_MAX;
|
|
maxs[0] = maxs[1] = maxs[2] = -FLT_MAX;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
BoxOnPlaneSide
|
|
|
|
Returns 1, 2, or 1 + 2
|
|
==================
|
|
*/
|
|
int VARGS BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const mplane_t *p)
|
|
{
|
|
float dist1, dist2;
|
|
int sides;
|
|
|
|
// general case
|
|
switch (p->signbits)
|
|
{
|
|
default:
|
|
case 0:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 1:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 2:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 3:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 4:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 5:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 6:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 7:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
}
|
|
|
|
sides = 0;
|
|
if (dist1 >= p->dist)
|
|
sides = 1;
|
|
if (dist2 < p->dist)
|
|
sides |= 2;
|
|
|
|
return sides;
|
|
}
|
|
|
|
|
|
#define Host_Error Sys_Errorf
|
|
|
|
enum hllumps_e
|
|
{ //note how this order matches q1 so well...
|
|
//and yet the format is disturbingly similar to q2... huh... :P
|
|
VLUMP_ENTITIES = LUMP_ENTITIES,
|
|
VLUMP_PLANES = LUMP_PLANES,
|
|
VLUMP_TEXTURES = LUMP_TEXTURES,
|
|
VLUMP_VERTEXES = LUMP_VERTEXES,
|
|
VLUMP_VISIBILITY = LUMP_VISIBILITY,
|
|
VLUMP_NODES = LUMP_NODES,
|
|
VLUMP_TEXINFO = LUMP_TEXINFO,
|
|
VLUMP_FACES_LDR = LUMP_FACES,
|
|
VLUMP_LIGHTING_LDR = LUMP_LIGHTING,
|
|
// VLUMP_FOO = 9,//LUMP_CLIPNODES
|
|
VLUMP_LEAFS = LUMP_LEAFS,
|
|
// VLUMP_FOO = 11,//LUMP_MARKSURFACES
|
|
VLUMP_EDGES = LUMP_EDGES,
|
|
VLUMP_SURFEDGES = LUMP_SURFEDGES,
|
|
VLUMP_MODELS = LUMP_MODELS,
|
|
// VLUMP_FOO = 15,
|
|
VLUMP_LEAFFACES = 16,
|
|
VLUMP_LEAFBRUSHES = 17,
|
|
VLUMP_BRUSHES = 18,
|
|
VLUMP_BRUSHSIDES = 19,
|
|
VLUMP_AREAS = 20,
|
|
VLUMP_AREAPORTALS = 21,
|
|
// VLUMP_FOO = 22,
|
|
// VLUMP_FOO = 23,
|
|
// VLUMP_FOO = 24,
|
|
// VLUMP_FOO = 25,
|
|
VLUMP_DISP_INFO = 26,
|
|
// VLUMP_FOO = 27,
|
|
// VLUMP_FOO = 28,
|
|
// VLUMP_FOO = 29,
|
|
// VLUMP_FOO = 30,
|
|
// VLUMP_FOO = 31,
|
|
VLUMP_DISP_LMALPHA = 32,
|
|
VLUMP_DISP_VERTS = 33,
|
|
VLUMP_DISP_LMCOORDS = 34,
|
|
VLUMP_GAMELUMP = 35,
|
|
// VLUMP_FOO = 36,
|
|
// VLUMP_FOO = 37,
|
|
// VLUMP_FOO = 38,
|
|
// VLUMP_FOO = 39,
|
|
|
|
VLUMP_ZIPFILE = 40,
|
|
VLUMP_AREAPORTALVERTS = 41,
|
|
// VLUMP_FOO = 42,
|
|
VLUMP_STRINGDATA = 43,
|
|
VLUMP_STRINGOFFSETS = 44,
|
|
// VLUMP_FOO = 45,
|
|
// VLUMP_FOO = 46,
|
|
// VLUMP_FOO = 47,
|
|
VLUMP_DISP_TRIFLAGS = 48,
|
|
// VLUMP_FOO = 49,
|
|
// VLUMP_FOO = 50,
|
|
VLUMP_LEAFLIGHTI_HDR= 51, //indexes into VLUMP_LEAFLIGHTV_HDR, two shorts per leaf.
|
|
VLUMP_LEAFLIGHTI_LDR= 52,
|
|
VLUMP_LIGHTING_HDR = 53,
|
|
// VLUMP_FOO = 54,
|
|
VLUMP_LEAFLIGHTV_HDR= 55,
|
|
VLUMP_LEAFLIGHTV_LDR= 56,
|
|
// VLUMP_FOO = 57,
|
|
VLUMP_FACES_HDR = 58,
|
|
// VLUMP_FOO = 59,
|
|
// VLUMP_FOO = 60,
|
|
// VLUMP_FOO = 61,
|
|
// VLUMP_FOO = 62,
|
|
// VLUMP_FOO = 63,
|
|
HL2_MAXLUMPS = 64
|
|
};
|
|
typedef struct {
|
|
unsigned int fileofs;
|
|
unsigned int filelen;
|
|
unsigned int foo[2];
|
|
} vlump_t;
|
|
typedef struct {
|
|
unsigned int magic;
|
|
unsigned int version;
|
|
vlump_t lumps[HL2_MAXLUMPS];
|
|
} dvbspheader_t;
|
|
|
|
typedef struct
|
|
{
|
|
int numareaportals;
|
|
int firstareaportal;
|
|
} carea_t;
|
|
typedef struct
|
|
{
|
|
int floodnum; // if two areas have equal floodnums, they are connected
|
|
int floodvalid; // flags the area as having been visited (sequence numbers matching prv->floodvalid)
|
|
} careaflood_t;
|
|
typedef struct cmodel_s
|
|
{
|
|
vec3_t mins, maxs;
|
|
vec3_t origin; // for sounds or lights
|
|
mnode_t *headnode;
|
|
mleaf_t *headleaf;
|
|
int numsurfaces;
|
|
int firstsurface;
|
|
|
|
int firstbrush;
|
|
int num_brushes;
|
|
} cmodel_t;
|
|
typedef struct dispinfo_s
|
|
{
|
|
struct msurface_s *surf;
|
|
vec3_t aamin;
|
|
vec3_t aamax;
|
|
pvscache_t pvs; //which pvs clusters this displacement is visible in...
|
|
unsigned int contents;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
vecV_t *xyz; //(width+1)*(height+1)
|
|
index_t *idx; //width*height*6;
|
|
size_t numindexes;
|
|
struct dispvert_s
|
|
{
|
|
vec3_t norm;
|
|
vec2_t st;
|
|
float alpha;
|
|
} *verts;
|
|
//unsigned short *flags;
|
|
} dispinfo_t;
|
|
|
|
typedef struct vbspinfo_s
|
|
{
|
|
struct
|
|
{
|
|
qbyte *vis;
|
|
pvsbuffer_t visbuf;
|
|
int viewcluster[2];
|
|
} vcache;
|
|
|
|
int numbrushsides;
|
|
q2cbrushside_t *brushsides;
|
|
|
|
q2mapsurface_t *surfaces;
|
|
dispinfo_t **surfdisp;
|
|
|
|
int numleafbrushes;
|
|
q2cbrush_t **leafbrushes;
|
|
|
|
int numcmodels;
|
|
cmodel_t *cmodels;
|
|
|
|
int numbrushes;
|
|
q2cbrush_t *brushes;
|
|
|
|
int numvisibility;
|
|
q2dvis_t *vis;
|
|
qbyte *phscalced;
|
|
|
|
struct vbsptexinfo_s
|
|
{
|
|
vec4_t lmvecs[2];
|
|
} *texinfo;
|
|
|
|
int numareas;
|
|
int floodvalid;
|
|
careaflood_t areaflood[MAX_VBSP_AREAS];
|
|
//areas have a list of portals that open into other areas.
|
|
carea_t *areas; //indexes into q2areaportals for flooding
|
|
size_t numareaportals;
|
|
q2dareaportal_t *areaportals;
|
|
|
|
//and this is the state that is actually changed. booleans.
|
|
qbyte portalopen[MAX_Q2MAP_AREAPORTALS]; //memset will work if it's a qbyte, really it should be a qboolean
|
|
mplane_t **portalplane;
|
|
qbyte portalquerying[MAX_Q2MAP_AREAPORTALS];
|
|
mesh_t *portalpoly; //[numq2areaportals]
|
|
int *occlusionqueries; //[numq2areaportals]
|
|
|
|
struct mleaflight_s
|
|
{
|
|
struct leaflightpoint_s
|
|
{
|
|
vec3_t rgb[6];
|
|
qbyte x, y, z;
|
|
} *point;
|
|
int count;
|
|
} *leaflight;
|
|
|
|
size_t numdisplacements;
|
|
dispinfo_t *displacements;
|
|
|
|
size_t numstaticprops;
|
|
struct staticprop_s
|
|
{
|
|
float fademindist;
|
|
float fademaxdist;
|
|
qboolean solid;
|
|
struct bihtransform_s transform;
|
|
vec3_t lightorg;
|
|
entity_t ent;
|
|
} *staticprops;
|
|
|
|
unsigned int contentsremap[32];
|
|
int summed; //for the main thread to wait on.
|
|
} vbspinfo_t;
|
|
|
|
static q2mapsurface_t nullsurface;
|
|
|
|
static int VBSP_NumInlineModels (model_t *model);
|
|
static cmodel_t *VBSP_InlineModel (model_t *model, char *name);
|
|
static void VBSP_FinalizeBrush(q2cbrush_t *brush);
|
|
static void FloodAreaConnections (vbspinfo_t *prv);
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
MAP LOADING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
static unsigned int VBSP_TranslateContentBits(vbspinfo_t *prv, unsigned int source)
|
|
{
|
|
unsigned int ret = 0;
|
|
if (source & 0x0000ffff)
|
|
{
|
|
if (source & 0x0000000f)
|
|
{
|
|
if (source & 0x00000001) ret |= prv->contentsremap[ 0];
|
|
if (source & 0x00000002) ret |= prv->contentsremap[ 1];
|
|
if (source & 0x00000004) ret |= prv->contentsremap[ 2];
|
|
if (source & 0x00000008) ret |= prv->contentsremap[ 3];
|
|
}
|
|
if (source & 0x000000f0)
|
|
{
|
|
if (source & 0x00000010) ret |= prv->contentsremap[ 4];
|
|
if (source & 0x00000020) ret |= prv->contentsremap[ 5];
|
|
if (source & 0x00000040) ret |= prv->contentsremap[ 6];
|
|
if (source & 0x00000080) ret |= prv->contentsremap[ 7];
|
|
}
|
|
if (source & 0x000000f00)
|
|
{
|
|
if (source & 0x00000100) ret |= prv->contentsremap[ 8];
|
|
if (source & 0x00000200) ret |= prv->contentsremap[ 9];
|
|
if (source & 0x00000400) ret |= prv->contentsremap[10];
|
|
if (source & 0x00000800) ret |= prv->contentsremap[11];
|
|
}
|
|
if (source & 0x000000f00)
|
|
{
|
|
if (source & 0x000001000) ret |= prv->contentsremap[12];
|
|
if (source & 0x000002000) ret |= prv->contentsremap[13];
|
|
if (source & 0x000004000) ret |= prv->contentsremap[14];
|
|
if (source & 0x000008000) ret |= prv->contentsremap[15];
|
|
}
|
|
}
|
|
if (source & 0xffff0000)
|
|
{
|
|
if (source & 0x000f0000)
|
|
{
|
|
if (source & 0x00010000) ret |= prv->contentsremap[16];
|
|
if (source & 0x00020000) ret |= prv->contentsremap[17];
|
|
if (source & 0x00040000) ret |= prv->contentsremap[18];
|
|
if (source & 0x00080000) ret |= prv->contentsremap[19];
|
|
}
|
|
if (source & 0x00f00000)
|
|
{
|
|
if (source & 0x00100000) ret |= prv->contentsremap[20];
|
|
if (source & 0x00200000) ret |= prv->contentsremap[21];
|
|
if (source & 0x00400000) ret |= prv->contentsremap[22];
|
|
if (source & 0x00800000) ret |= prv->contentsremap[23];
|
|
}
|
|
if (source & 0x0f000000)
|
|
{
|
|
if (source & 0x01000000) ret |= prv->contentsremap[24];
|
|
if (source & 0x02000000) ret |= prv->contentsremap[25];
|
|
if (source & 0x04000000) ret |= prv->contentsremap[26];
|
|
if (source & 0x08000000) ret |= prv->contentsremap[27];
|
|
}
|
|
if (source & 0xf0000000)
|
|
{
|
|
if (source & 0x10000000) ret |= prv->contentsremap[28];
|
|
if (source & 0x20000000) ret |= prv->contentsremap[29];
|
|
if (source & 0x40000000) ret |= prv->contentsremap[30];
|
|
if (source & 0x80000000) ret |= prv->contentsremap[31];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
static void VBSP_TranslateContentBits_Setup(vbspinfo_t *prv)
|
|
{
|
|
size_t i, j;
|
|
char contname[64];
|
|
const char *contremap = hl2_contents_remap->string;
|
|
static const struct {
|
|
const char *name;
|
|
unsigned int contents;
|
|
} knowncontents[] =
|
|
{
|
|
{"empty", FTECONTENTS_EMPTY},
|
|
{"solid", FTECONTENTS_SOLID},
|
|
{"window", FTECONTENTS_WINDOW},
|
|
{"lava", FTECONTENTS_LAVA},
|
|
{"slime", FTECONTENTS_SLIME},
|
|
{"water", FTECONTENTS_WATER},
|
|
{"ladder", FTECONTENTS_LADDER},
|
|
{"playerclip", FTECONTENTS_PLAYERCLIP},
|
|
{"monsterclip", FTECONTENTS_MONSTERCLIP},
|
|
{"clip", FTECONTENTS_PLAYERCLIP|FTECONTENTS_MONSTERCLIP},
|
|
{"body", FTECONTENTS_BODY},
|
|
{"corpse", FTECONTENTS_CORPSE},
|
|
{"detail", FTECONTENTS_DETAIL},
|
|
{"sky", FTECONTENTS_SKY},
|
|
{"Q2SOLID", Q2CONTENTS_SOLID},
|
|
{"Q2WINDOW", Q2CONTENTS_WINDOW},
|
|
{"Q2AUX", Q2CONTENTS_AUX},
|
|
{"Q2LAVA", Q2CONTENTS_LAVA},
|
|
{"Q2SLIME", Q2CONTENTS_SLIME},
|
|
{"Q2WATER", Q2CONTENTS_WATER},
|
|
{"Q2MIST", Q2CONTENTS_MIST},
|
|
{"Q2AREAPORTAL", Q2CONTENTS_AREAPORTAL},
|
|
{"Q2PLAYERCLIP", Q2CONTENTS_PLAYERCLIP},
|
|
{"Q2MONSTERCLIP", Q2CONTENTS_MONSTERCLIP},
|
|
{"Q2CURRENT_0", Q2CONTENTS_CURRENT_0},
|
|
{"Q2CURRENT_90", Q2CONTENTS_CURRENT_90},
|
|
{"Q2CURRENT_180", Q2CONTENTS_CURRENT_180},
|
|
{"Q2CURRENT_270", Q2CONTENTS_CURRENT_270},
|
|
{"Q2CURRENT_UP", Q2CONTENTS_CURRENT_UP},
|
|
{"Q2CURRENT_DOWN", Q2CONTENTS_CURRENT_DOWN},
|
|
{"Q2ORIGIN", Q2CONTENTS_ORIGIN},
|
|
{"Q2MONSTER", Q2CONTENTS_MONSTER},
|
|
{"Q2DEADMONSTER", Q2CONTENTS_DEADMONSTER},
|
|
{"Q2DETAIL", Q2CONTENTS_DETAIL},
|
|
{"Q2TRANSLUCENT", Q2CONTENTS_TRANSLUCENT},
|
|
{"Q2LADDER", Q2CONTENTS_LADDER},
|
|
{"Q3SOLID", Q3CONTENTS_SOLID},
|
|
{"Q3LAVA", Q3CONTENTS_LAVA},
|
|
{"Q3SLIME", Q3CONTENTS_SLIME},
|
|
{"Q3WATER", Q3CONTENTS_WATER},
|
|
{"Q3NOTTEAM1", Q3CONTENTS_NOTTEAM1},
|
|
{"Q3NOTTEAM2", Q3CONTENTS_NOTTEAM2},
|
|
{"Q3NOBOTCLIP", Q3CONTENTS_NOBOTCLIP},
|
|
{"Q3AREAPORTAL", Q3CONTENTS_AREAPORTAL},
|
|
{"Q3PLAYERCLIP", Q3CONTENTS_PLAYERCLIP},
|
|
{"Q3MONSTERCLIP", Q3CONTENTS_MONSTERCLIP},
|
|
{"Q3TELEPORTER", Q3CONTENTS_TELEPORTER},
|
|
{"Q3JUMPPAD", Q3CONTENTS_JUMPPAD},
|
|
{"Q3CLUSTERPORTAL", Q3CONTENTS_CLUSTERPORTAL},
|
|
{"Q3DONOTENTER", Q3CONTENTS_DONOTENTER},
|
|
{"Q3BOTCLIP", Q3CONTENTS_BOTCLIP},
|
|
{"Q3MOVER", Q3CONTENTS_MOVER},
|
|
{"Q3ORIGIN", Q3CONTENTS_ORIGIN},
|
|
{"Q3BODY", Q3CONTENTS_BODY},
|
|
{"Q3CORSE", Q3CONTENTS_CORPSE},
|
|
{"Q3DETAIL", Q3CONTENTS_DETAIL},
|
|
{"Q3STRUCTURAL", Q3CONTENTS_STRUCTURAL},
|
|
{"Q3TRANSLUCENT", Q3CONTENTS_TRANSLUCENT},
|
|
{"Q3TRIGGER", Q3CONTENTS_TRIGGER},
|
|
{"Q3NODROP", Q3CONTENTS_NODROP},
|
|
};
|
|
if (!*contremap)
|
|
for (i = 0; i < 32; i++)
|
|
prv->contentsremap[i] = 1<<i;
|
|
else for (i = 0; i < 32; i++)
|
|
{
|
|
contremap = cmdfuncs->ParseToken(contremap, contname, sizeof(contname), NULL);
|
|
if (!contremap || !*contname)
|
|
prv->contentsremap[i] = 0;
|
|
else
|
|
{
|
|
char *tmp;
|
|
int bit = strtol(contname, &tmp, 10);
|
|
if (!*tmp)
|
|
{
|
|
if (bit >= 0)
|
|
prv->contentsremap[i] = 1<<bit;
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < countof(knowncontents); j++)
|
|
{
|
|
if (!Q_strcasecmp(contname, knowncontents[j].name))
|
|
{
|
|
prv->contentsremap[i] = knowncontents[j].contents;
|
|
break;
|
|
}
|
|
}
|
|
if (j == countof(knowncontents))
|
|
Con_Printf(CON_WARNING"%s: Unknown bit name %s\n", hl2_contents_remap->name, contname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void VBSP_SetParent (mnode_t *node, mnode_t *parent)
|
|
{
|
|
node->parent = parent;
|
|
if (node->contents != -1)
|
|
return;
|
|
VBSP_SetParent (node->children[0], node);
|
|
VBSP_SetParent (node->children[1], node);
|
|
}
|
|
|
|
static void VBSP_FindBrushRange (vbspinfo_t *prv, mnode_t *node, size_t *firstbrush, size_t *lastbrush)
|
|
{
|
|
size_t u, b;
|
|
mleaf_t *leaf;
|
|
while (node->contents == -1)
|
|
{ //walk every node to find every leaf...
|
|
VBSP_FindBrushRange(prv, node->children[0], firstbrush, lastbrush);
|
|
node = node->children[1];
|
|
}
|
|
|
|
leaf = (mleaf_t*)node;
|
|
for (u = 0; u < leaf->numleafbrushes; u++)
|
|
{
|
|
b = prv->leafbrushes[leaf->firstleafbrush+u]-prv->brushes;
|
|
if (*firstbrush > b)
|
|
*firstbrush = b;
|
|
if (*lastbrush < b+1)
|
|
*lastbrush = b+1;
|
|
}
|
|
}
|
|
|
|
static qboolean VBSP_LoadVertexes (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
dvertex_t *in;
|
|
mvertex_t *out;
|
|
size_t i, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
count = l->filelen / sizeof(*in);
|
|
if (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadVertexes: funny lump size in %s\n", loadmodel->name);
|
|
return false;
|
|
}
|
|
out = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));
|
|
|
|
loadmodel->vertexes = out;
|
|
loadmodel->numvertexes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->position[0] = LittleFloat (in->point[0]);
|
|
out->position[1] = LittleFloat (in->point[1]);
|
|
out->position[2] = LittleFloat (in->point[2]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
static qboolean VBSP_LoadEdges (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
medge_t *out;
|
|
size_t i, count;
|
|
|
|
dsedge_t *in = (void *)(mod_base + l->fileofs);
|
|
count = l->filelen / sizeof(*in);
|
|
if (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf ("VBSP_LoadEdges: funny lump size in %s\n", loadmodel->name);
|
|
return false;
|
|
}
|
|
out = plugfuncs->GMalloc(&loadmodel->memgroup, (count + 1) * sizeof(*out));
|
|
|
|
loadmodel->edges = out;
|
|
loadmodel->numedges = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->v[0] = (unsigned short)LittleShort(in->v[0]);
|
|
out->v[1] = (unsigned short)LittleShort(in->v[1]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
static qboolean VBSP_LoadSurfedges (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
size_t i, count;
|
|
unsigned int *in, *out;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
count = l->filelen / sizeof(*in);
|
|
if (l->filelen % sizeof(*in) || count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadSurfedges: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
out = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));
|
|
|
|
loadmodel->surfedges = out;
|
|
loadmodel->numsurfedges = count;
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
out[i] = LittleLong (in[i]);
|
|
|
|
return true;
|
|
}
|
|
static qboolean VBSP_LoadMarksurfaces (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
size_t i, j, count;
|
|
msurface_t **out;
|
|
|
|
unsigned short *ins;
|
|
ins = (void *)(mod_base + l->fileofs);
|
|
count = l->filelen / sizeof(*ins);
|
|
if (l->filelen % sizeof(*ins) || count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadMarksurfaces: funny lump size in %s\n",loadmodel->name);
|
|
return false;
|
|
}
|
|
out = plugfuncs->GMalloc(&loadmodel->memgroup, count*sizeof(*out));
|
|
|
|
loadmodel->marksurfaces = out;
|
|
loadmodel->nummarksurfaces = count;
|
|
|
|
for ( i=0 ; i<count ; i++)
|
|
{
|
|
j = (unsigned short)LittleShort(ins[i]);
|
|
if (j >= loadmodel->numsurfaces)
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadMarksurfaces: bad surface number\n");
|
|
return false;
|
|
}
|
|
out[i] = loadmodel->surfaces + j;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
static qboolean VBSP_LoadEntities (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
return modfuncs->LoadEntities(loadmodel, mod_base+l->fileofs, l->filelen);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadSubmodels
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadSubmodels (model_t *loadmodel, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)loadmodel->meshinfo;
|
|
q2dmodel_t *in;
|
|
cmodel_t *out;
|
|
int i, j, count;
|
|
size_t firstbrush, lastbrush;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadSubmodels: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map with no models\n");
|
|
return false;
|
|
}
|
|
if (count > SANITY_MAX_Q2MAP_MODELS)
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many models\n");
|
|
return false;
|
|
}
|
|
|
|
out = prv->cmodels = plugfuncs->GMalloc(&loadmodel->memgroup, count * sizeof(*prv->cmodels));
|
|
prv->numcmodels = count;
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{ // spread the mins / maxs by a pixel
|
|
out->mins[j] = LittleFloat (in->mins[j]) - 1;
|
|
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
|
|
out->origin[j] = LittleFloat (in->origin[j]);
|
|
}
|
|
out->headnode = loadmodel->nodes + LittleLong (in->headnode);
|
|
out->firstsurface = LittleLong (in->firstface);
|
|
out->numsurfaces = LittleLong (in->numfaces);
|
|
|
|
|
|
firstbrush = ~0u;
|
|
lastbrush = 0u;
|
|
VBSP_FindBrushRange(prv, out->headnode, &firstbrush, &lastbrush);
|
|
if (lastbrush > firstbrush)
|
|
{
|
|
out->firstbrush = firstbrush;
|
|
out->num_brushes = lastbrush-firstbrush;
|
|
}
|
|
}
|
|
|
|
AddPointToBounds(prv->cmodels[0].mins, loadmodel->mins, loadmodel->maxs);
|
|
AddPointToBounds(prv->cmodels[0].maxs, loadmodel->mins, loadmodel->maxs);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadBrushes
|
|
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadBrushes (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
q2dbrush_t *in;
|
|
q2cbrush_t *out;
|
|
int i, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadBrushes: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many brushes");
|
|
return false;
|
|
}
|
|
|
|
prv->brushes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+1));
|
|
|
|
out = prv->brushes;
|
|
|
|
prv->numbrushes = count;
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
//FIXME: missing bounds checks
|
|
out->brushside = &prv->brushsides[LittleLong(in->firstside)];
|
|
out->numsides = LittleLong(in->numsides);
|
|
out->contents = VBSP_TranslateContentBits(prv, LittleLong(in->contents));
|
|
VBSP_FinalizeBrush(out);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadPlanes
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadPlanes (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
int i, j;
|
|
mplane_t *out;
|
|
dplane_t *in;
|
|
int count;
|
|
int bits;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadPlanes: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map with no planes\n");
|
|
return false;
|
|
}
|
|
// need to save space for box planes
|
|
if (count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many planes (%i)\n", count);
|
|
return false;
|
|
}
|
|
|
|
mod->planes = out = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);
|
|
mod->numplanes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
bits = 0;
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->normal[j] = LittleFloat (in->normal[j]);
|
|
if (out->normal[j] < 0)
|
|
bits |= 1<<j;
|
|
}
|
|
|
|
out->dist = LittleFloat (in->dist);
|
|
out->type = LittleLong (in->type);
|
|
out->signbits = bits;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadLeafBrushes
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadLeafBrushes (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i;
|
|
q2cbrush_t **out;
|
|
unsigned short *in;
|
|
int count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadLeafBrushes: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map with no planes\n");
|
|
return false;
|
|
}
|
|
// need to save space for box planes
|
|
if (count > SANITY_LIMIT(**out))
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many leafbrushes\n");
|
|
return false;
|
|
}
|
|
|
|
//prv->numbrushes is because of submodels being weird.
|
|
out = prv->leafbrushes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+prv->numbrushes));
|
|
prv->numleafbrushes = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
*out = prv->brushes + (unsigned short)(short)LittleShort (*in);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadAreas
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadAreas (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i;
|
|
carea_t *out;
|
|
q2darea_t *in;
|
|
int count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadAreas: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count > MAX_Q2MAP_AREAS)
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many areas\n");
|
|
return false;
|
|
}
|
|
|
|
out = prv->areas = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);
|
|
prv->numareas = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->numareaportals = LittleLong (in->numareaportals);
|
|
out->firstareaportal = LittleLong (in->firstareaportal);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CMod_LoadVisibility
|
|
=================
|
|
*/
|
|
static qboolean VBSP_LoadVisibility (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i;
|
|
|
|
prv->numvisibility = l->filelen;
|
|
// if (l->filelen > MAX_Q2MAP_VISIBILITY)
|
|
// {
|
|
// Con_Printf (CON_ERROR "Map has too large visibility lump\n");
|
|
// return false;
|
|
// }
|
|
|
|
prv->vis = plugfuncs->GMalloc(&mod->memgroup, l->filelen);
|
|
memcpy (prv->vis, mod_base + l->fileofs, l->filelen);
|
|
|
|
mod->vis = prv->vis;
|
|
|
|
prv->vis->numclusters = LittleLong (prv->vis->numclusters);
|
|
for (i=0 ; i<prv->vis->numclusters ; i++)
|
|
{
|
|
prv->vis->bitofs[i][0] = LittleLong (prv->vis->bitofs[i][0]);
|
|
prv->vis->bitofs[i][1] = LittleLong (prv->vis->bitofs[i][1]);
|
|
}
|
|
mod->numclusters = prv->vis->numclusters;
|
|
mod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
static qbyte *CM_LeafnumPVS (model_t *model, int leafnum, qbyte *buffer, unsigned int buffersize)
|
|
{
|
|
return CM_ClusterPVS(model, CM_LeafCluster(model, leafnum), buffer, buffersize);
|
|
}
|
|
*/
|
|
|
|
#ifdef HAVE_CLIENT
|
|
|
|
/*extern int r_dlightframecount;
|
|
static void VBSP_MarkLights (dlight_t *light, dlightbitmask_t bit, mnode_t *node)
|
|
{
|
|
mplane_t *splitplane;
|
|
float dist;
|
|
msurface_t *surf;
|
|
int i;
|
|
|
|
if (node->contents != -1)
|
|
{
|
|
mleaf_t *leaf = (mleaf_t *)node;
|
|
msurface_t **mark;
|
|
|
|
i = leaf->nummarksurfaces;
|
|
mark = leaf->firstmarksurface;
|
|
while(i--!=0)
|
|
{
|
|
surf = *mark++;
|
|
if (surf->dlightframe != r_dlightframecount)
|
|
{
|
|
surf->dlightbits = 0;
|
|
surf->dlightframe = r_dlightframecount;
|
|
}
|
|
surf->dlightbits |= bit;
|
|
}
|
|
return;
|
|
}
|
|
|
|
splitplane = node->plane;
|
|
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
|
|
|
|
if (dist > light->radius)
|
|
{
|
|
VBSP_MarkLights (light, bit, node->children[0]);
|
|
return;
|
|
}
|
|
if (dist < -light->radius)
|
|
{
|
|
VBSP_MarkLights (light, bit, node->children[1]);
|
|
return;
|
|
}
|
|
|
|
// mark the polygons
|
|
surf = cl.worldmodel->surfaces + node->firstsurface;
|
|
for (i=0 ; i<node->numsurfaces ; i++, surf++)
|
|
{
|
|
if (surf->dlightframe != r_dlightframecount)
|
|
{
|
|
surf->dlightbits = 0u;
|
|
surf->dlightframe = r_dlightframecount;
|
|
}
|
|
surf->dlightbits |= bit;
|
|
}
|
|
|
|
VBSP_MarkLights (light, bit, node->children[0]);
|
|
VBSP_MarkLights (light, bit, node->children[1]);
|
|
}
|
|
|
|
static void VBSP_StainNode (mnode_t *node, float *parms)
|
|
{
|
|
mplane_t *splitplane;
|
|
float dist;
|
|
msurface_t *surf;
|
|
int i;
|
|
|
|
if (node->contents != -1)
|
|
return;
|
|
|
|
splitplane = node->plane;
|
|
dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;
|
|
|
|
if (dist > (*parms))
|
|
{
|
|
VBSP_StainNode (node->children[0], parms);
|
|
return;
|
|
}
|
|
if (dist < (-*parms))
|
|
{
|
|
VBSP_StainNode (node->children[1], parms);
|
|
return;
|
|
}
|
|
|
|
// mark the polygons
|
|
surf = cl.worldmodel->surfaces + node->firstsurface;
|
|
for (i=0 ; i<node->numsurfaces ; i++, surf++)
|
|
{
|
|
if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))
|
|
continue;
|
|
Surf_StainSurf(surf, parms);
|
|
}
|
|
|
|
VBSP_StainNode (node->children[0], parms);
|
|
VBSP_StainNode (node->children[1], parms);
|
|
}
|
|
*/
|
|
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
float vecs[2][4]; // [s/t][xyz offset]
|
|
float lmvecs[2][4]; //well that's awkward
|
|
int flags; // miptex flags + overrides
|
|
int textureindex;
|
|
} hltexinfo_t;
|
|
static qboolean VBSP_LoadSurfaces (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
hltexinfo_t *in;
|
|
q2mapsurface_t *out;
|
|
int i, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadSurfaces: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map with no surfaces\n");
|
|
return false;
|
|
}
|
|
// if (count > MAX_Q2MAP_TEXINFO)
|
|
// Host_Error ("Map has too many surfaces");
|
|
|
|
mod->numtexinfo = count;
|
|
out = prv->surfaces = plugfuncs->GMalloc(&mod->memgroup, count * sizeof(*prv->surfaces));
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
Q_strncpyz (out->c.name, "FIXME", sizeof(out->c.name));
|
|
Q_strncpyz (out->rname, "FIXME", sizeof(out->rname));
|
|
out->c.flags = LittleLong (in->flags);
|
|
out->c.value = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
vec3_t reflectivity; //not very useful to us.
|
|
unsigned int stringindex;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int width2; //no idea why there's two of these.
|
|
unsigned int height2;
|
|
} hltexture_t;
|
|
#define TIHL2_LIGHT TI_LIGHT
|
|
#define TIHL2_SKYBOX 0x2
|
|
#define TIHL2_SKYROOM 0x4
|
|
#define TIHL2_WARP TI_WARP
|
|
|
|
#define TIHL2_TRANS TI_TRANS33
|
|
#define TIHL2_NOPORTAL 0x20
|
|
#define TIHL2_TRIGGER 0x40
|
|
#define TIHL2_NODRAW TI_NODRAW
|
|
//#define TIHL2_HINT 0x100
|
|
//#define TIHL2_SKIP 0x200
|
|
#define TIHL2_NOLIGHT 0x400
|
|
//#define TIHL2_BUMPLIGHT 0x800
|
|
//#define TIHL2_NOSHADOWS 0x1000
|
|
//#define TIHL2_NODECALS 0x2000
|
|
//#define TIHL2_NOCHOP 0x4000
|
|
//#define TIHL2_HITBOX 0x8000
|
|
|
|
static qboolean VBSP_LoadTexInfo (model_t *mod, qbyte *mod_base, vlump_t *lumps, char *mapname)
|
|
{ //texinfo->textures->stringoffsets->strings. gah, so many lumps just to find the texture name to use!
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
hltexinfo_t *in;
|
|
mtexinfo_t *out;
|
|
int i, j, count;
|
|
char sname[256];
|
|
int texcount;
|
|
hltexture_t *textures = (void*)(mod_base + lumps[VLUMP_TEXTURES].fileofs);
|
|
unsigned int *stringoffsets = (void*)(mod_base + lumps[VLUMP_STRINGOFFSETS].fileofs);
|
|
char *strings = mod_base + lumps[VLUMP_STRINGDATA].fileofs;
|
|
unsigned int flags;
|
|
|
|
in = (void *)(mod_base + lumps[VLUMP_TEXINFO].fileofs);
|
|
if (lumps[VLUMP_TEXINFO].filelen % sizeof(*in))
|
|
{
|
|
Con_Printf ("VBSP_LoadTexInfo: funny lump size in %s\n", mod->name);
|
|
return false;
|
|
}
|
|
count = lumps[VLUMP_TEXINFO].filelen / sizeof(*in);
|
|
out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));
|
|
prv->texinfo = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->texinfo));
|
|
|
|
mod->textures = plugfuncs->GMalloc(&mod->memgroup, sizeof(texture_t *)*count);
|
|
texcount = 0;
|
|
|
|
mod->texinfo = out;
|
|
mod->numtexinfo = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
hltexture_t *texture = ((in->textureindex>=0)?textures + in->textureindex:NULL);
|
|
char *texturename = (texture?strings + stringoffsets[texture->stringindex]:"INVALID");
|
|
flags = LittleLong (in->flags);
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
|
|
for (j=0 ; j<4 ; j++)
|
|
out->vecs[1][j] = LittleFloat (in->vecs[1][j]);
|
|
out->vecscale[0] = 1.0/Length (out->vecs[0]);
|
|
out->vecscale[1] = 1.0/Length (out->vecs[1]);
|
|
|
|
for (j=0 ; j<4 ; j++)
|
|
prv->texinfo[i].lmvecs[0][j] = LittleFloat (in->lmvecs[0][j]);
|
|
for (j=0 ; j<4 ; j++)
|
|
prv->texinfo[i].lmvecs[1][j] = LittleFloat (in->lmvecs[1][j]);
|
|
|
|
if (flags & (TIHL2_SKYBOX|TIHL2_SKYROOM))
|
|
Q_snprintfz(sname, sizeof(sname), "sky/%s", texturename);
|
|
else
|
|
Q_snprintfz(sname, sizeof(sname), "%s", texturename);
|
|
if (flags & (TIHL2_WARP))
|
|
Q_strncatz(sname, "#WARP", sizeof(sname));
|
|
// if (out->flags & TIHL2_FLOWING)
|
|
// Q_strncatz(sname, "#FLOW", sizeof(sname));
|
|
// if (out->flags & TIHL2_TRANS66)
|
|
// Q_strncatz(sname, "#ALPHA=0.66", sizeof(sname));
|
|
else if (out->flags & TIHL2_TRANS)
|
|
Q_strncatz(sname, "#ALPHA=1", sizeof(sname));
|
|
// else if (out->flags & (TIHL2_WARP))
|
|
// Q_strncatz(sname, "#ALPHA=1", sizeof(sname));
|
|
|
|
out->flags = 0;
|
|
if (flags & TIHL2_NOLIGHT)
|
|
out->flags |= TEX_SPECIAL;
|
|
|
|
//compact the textures.
|
|
for (j=0; j < texcount; j++)
|
|
{
|
|
if (!Q_strcasecmp(sname, mod->textures[j]->name))
|
|
{
|
|
out->texture = mod->textures[j];
|
|
break;
|
|
}
|
|
}
|
|
if (j == texcount) //load a new one
|
|
{
|
|
out->texture = plugfuncs->GMalloc(&mod->memgroup, sizeof(texture_t));
|
|
Q_strncpyz(out->texture->name, sname, sizeof(out->texture->name));
|
|
Q_strlwr(out->texture->name);
|
|
if (texture)
|
|
{
|
|
out->texture->vwidth = texture->width;
|
|
out->texture->vheight = texture->height;
|
|
}
|
|
else
|
|
{
|
|
out->texture->vwidth = out->texture->vheight = 128;
|
|
}
|
|
|
|
mod->textures[texcount++] = out->texture;
|
|
}
|
|
}
|
|
|
|
mod->numtextures = texcount;
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
//shared with q2dnode_t
|
|
int planenum;
|
|
int children[2]; // negative numbers are -(leafs+1), not nodes
|
|
short mins[3]; // for frustom culling
|
|
short maxs[3];
|
|
unsigned short firstface;
|
|
unsigned short numfaces; // counting both sides
|
|
|
|
//new for hl2
|
|
unsigned short area;
|
|
unsigned short pad;
|
|
} hl2dnode_t;
|
|
static qboolean VBSP_LoadNodes (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
hl2dnode_t *in;
|
|
int child;
|
|
mnode_t *out;
|
|
int i, j, count;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadNodes: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map has no nodes\n");
|
|
return false;
|
|
}
|
|
if (count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many nodes\n");
|
|
return false;
|
|
}
|
|
|
|
out = plugfuncs->GMalloc(&mod->memgroup, sizeof(mnode_t)*count);
|
|
|
|
mod->nodes = out;
|
|
mod->numnodes = count;
|
|
|
|
for (i=0 ; i<count ; i++, out++, in++)
|
|
{
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
out->plane = mod->planes + LittleLong(in->planenum);
|
|
|
|
out->firstsurface = (unsigned short)LittleShort (in->firstface);
|
|
out->numsurfaces = (unsigned short)LittleShort (in->numfaces);
|
|
out->contents = -1; // differentiate from leafs
|
|
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
child = LittleLong (in->children[j]);
|
|
out->childnum[j] = child;
|
|
if (child < 0)
|
|
out->children[j] = (mnode_t *)(mod->leafs + -1-child);
|
|
else
|
|
out->children[j] = mod->nodes + child;
|
|
}
|
|
}
|
|
|
|
VBSP_SetParent (mod->nodes, NULL); // sets nodes and leafs
|
|
|
|
return true;
|
|
}
|
|
typedef struct
|
|
{
|
|
//copied from q2dleaf_t
|
|
int contents; // OR of all brushes (NOTE: hl2 doesn't seem to have these all set properly, at least for detail brushes)
|
|
|
|
short cluster;
|
|
short area;
|
|
|
|
short mins[3]; // for frustum culling
|
|
short maxs[3];
|
|
|
|
unsigned short firstleafface;
|
|
unsigned short numleaffaces;
|
|
|
|
unsigned short firstleafbrush;
|
|
unsigned short numleafbrushes;
|
|
|
|
//new for hl2
|
|
short leafwaterid;
|
|
struct //present in v19. gone in v20.
|
|
{
|
|
qbyte rgb[3];
|
|
signed char e;
|
|
} light[6];
|
|
short pad;
|
|
} hl2dleaf_t;
|
|
static qboolean VBSP_LoadLeafs (model_t *mod, qbyte *mod_base, vlump_t *l, int ver)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i, j;
|
|
mleaf_t *out;
|
|
hl2dleaf_t *in;
|
|
int count;
|
|
size_t insize = sizeof(*in);
|
|
struct leaflightpoint_s *lightpoint = NULL;
|
|
if (ver < 20)
|
|
insize = 56; //older maps have some lighting info here.
|
|
else
|
|
insize = 32;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % insize)
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadLeafs: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / insize;
|
|
|
|
if (count < 1)
|
|
{
|
|
Con_Printf (CON_ERROR "Map with no leafs\n");
|
|
return false;
|
|
}
|
|
// need to save space for box planes
|
|
if (count > SANITY_LIMIT(*out))
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many leafs\n");
|
|
return false;
|
|
}
|
|
|
|
out = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * (count+1));
|
|
mod->numclusters = 0;
|
|
|
|
mod->leafs = out;
|
|
mod->numleafs = count;
|
|
|
|
if (ver < 20)
|
|
{
|
|
prv->leaflight = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);
|
|
lightpoint = plugfuncs->GMalloc(&mod->memgroup, sizeof(*lightpoint) * count);
|
|
}
|
|
|
|
for ( i=0 ; i<count ; i++, in = (hl2dleaf_t*)((qbyte*)in+insize), out++)
|
|
{
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
out->minmaxs[j] = LittleShort (in->mins[j]);
|
|
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
|
|
}
|
|
|
|
out->contents = VBSP_TranslateContentBits(prv,LittleLong (in->contents));
|
|
out->cluster = (unsigned short)LittleShort (in->cluster);
|
|
if (out->cluster == 0xffff)
|
|
out->cluster = -1;
|
|
|
|
out->area = (unsigned short)LittleShort (in->area);
|
|
out->area &= 0x1ff; //upper part is flags.
|
|
out->firstleafbrush = (unsigned short)LittleShort (in->firstleafbrush);
|
|
out->numleafbrushes = (unsigned short)LittleShort (in->numleafbrushes);
|
|
|
|
out->firstmarksurface = mod->marksurfaces +
|
|
(unsigned short)LittleShort(in->firstleafface);
|
|
out->nummarksurfaces = (unsigned short)LittleShort(in->numleaffaces);
|
|
|
|
if (out->cluster >= mod->numclusters)
|
|
mod->numclusters = out->cluster + 1;
|
|
|
|
if (lightpoint)
|
|
{
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
float e = pow(2, in->light[j].e);
|
|
lightpoint->rgb[j][0] = e * in->light[j].rgb[0];
|
|
lightpoint->rgb[j][1] = e * in->light[j].rgb[1];
|
|
lightpoint->rgb[j][2] = e * in->light[j].rgb[2];
|
|
}
|
|
prv->leaflight[i].count = 1;
|
|
prv->leaflight[i].point = lightpoint++;
|
|
}
|
|
}
|
|
mod->pvsbytes = ((mod->numclusters + 31)>>3)&~3;
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
unsigned short portalnum;
|
|
unsigned short otherarea;
|
|
|
|
unsigned short firstvert;
|
|
unsigned short numverts;
|
|
unsigned int planenum;
|
|
} hl2dareaportal_t;
|
|
static qboolean VBSP_LoadAreaPortals (model_t *mod, qbyte *mod_base, vlump_t *l, vlump_t *lump_verts)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i;
|
|
q2dareaportal_t *out;
|
|
hl2dareaportal_t *in;
|
|
int count, vcount;
|
|
vec3_t *inverts;
|
|
mesh_t mesh;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadAreaPortals: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
inverts = (void *)(mod_base + lump_verts->fileofs);
|
|
if (lump_verts->filelen % sizeof(*inverts))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadAreaPortals: funny lump size\n");
|
|
return false;
|
|
}
|
|
vcount = lump_verts->filelen / sizeof(*inverts);
|
|
|
|
if (count > MAX_Q2MAP_AREAS)
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many areas\n");
|
|
return false;
|
|
}
|
|
|
|
out = prv->areaportals = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);
|
|
prv->portalpoly = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->portalpoly)*count);
|
|
prv->portalplane = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->portalplane)*count);
|
|
prv->numareaportals = count;
|
|
|
|
mesh.xyz_array = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.xyz_array)*vcount);
|
|
for (i=0 ; i<vcount ; i++)
|
|
{
|
|
mesh.xyz_array[i][0] = LittleFloat(inverts[i][0]);
|
|
mesh.xyz_array[i][1] = LittleFloat(inverts[i][1]);
|
|
mesh.xyz_array[i][2] = LittleFloat(inverts[i][2]);
|
|
}
|
|
mesh.indexes = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.indexes)*64*3);
|
|
mesh.st_array = plugfuncs->GMalloc(&mod->memgroup, sizeof(*mesh.st_array)*64);
|
|
for (i=0 ; i<64 ; i++)
|
|
{
|
|
Vector2Set(mesh.st_array[i], 0,0);
|
|
mesh.indexes[i*3+0] = 0;
|
|
mesh.indexes[i*3+1] = i+1;
|
|
mesh.indexes[i*3+2] = i+2;
|
|
}
|
|
|
|
for (i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
out->portalnum = LittleShort (in->portalnum);
|
|
out->otherarea = LittleShort (in->otherarea);
|
|
|
|
prv->portalplane[i] = mod->planes + LittleLong (in->planenum);
|
|
|
|
prv->portalpoly[i].xyz_array = mesh.xyz_array+LittleLong(in->firstvert);
|
|
prv->portalpoly[i].st_array = mesh.st_array;
|
|
prv->portalpoly[i].numvertexes = LittleLong(in->numverts);
|
|
|
|
if (prv->portalpoly[i].numvertexes>2)
|
|
{
|
|
prv->portalpoly[i].istrifan = true;
|
|
prv->portalpoly[i].indexes = mesh.indexes;
|
|
prv->portalpoly[i].numindexes = (prv->portalpoly[i].numvertexes-2)*3;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{ //the main displacement lump
|
|
vec3_t position; //not really sure how to use this.
|
|
int firstvert; //((1<<power)+1) squared verts
|
|
int firsttriflags; //ignored (two shorts per quad)
|
|
int power;
|
|
int minpower; //ignored... FIXME: add lod...
|
|
float smoothangle;
|
|
unsigned int contents;
|
|
unsigned short faceidx; //mod->surfaces index
|
|
unsigned int lightmapalphaoffset; //erk, rgba? that's going to restrict lightmap formats.
|
|
unsigned int lightofs; //extents? cabbage? oh noes we've gone mad again! or is this some special blend weights in addition to the surface's lighting?
|
|
struct
|
|
{ //erk
|
|
unsigned short peer;
|
|
unsigned char orientation;
|
|
unsigned char span;
|
|
unsigned char peerspan;
|
|
} edgepeers[8]; //two per edge
|
|
struct
|
|
{ //no idea how this works
|
|
unsigned short peer[4];
|
|
unsigned char numpeers;
|
|
} cornerpeers[4];
|
|
unsigned int allowedverts[10];
|
|
} hl2ddisplacement_t;
|
|
typedef struct
|
|
{
|
|
vec3_t norm;
|
|
float dist;
|
|
float alpha; //vertex alpha.
|
|
} hl2displacementvert_t;
|
|
static qboolean VBSP_LoadDisplacements (model_t *mod, qbyte *mod_base, vlump_t *lumps)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
vlump_t *l = &lumps[VLUMP_DISP_INFO];
|
|
vlump_t *vl = &lumps[VLUMP_DISP_VERTS];
|
|
hl2ddisplacement_t *in;
|
|
dispinfo_t *out;
|
|
int i, count, x, y;
|
|
hl2displacementvert_t *inv;
|
|
struct dispvert_s *verts;
|
|
vecV_t *xyz;
|
|
index_t *indexes;
|
|
size_t maxverts;
|
|
msurface_t *surf;
|
|
float *sverts[4];
|
|
signed int e, idx;
|
|
float fx,fy;
|
|
vec3_t p, base;
|
|
size_t stride;
|
|
|
|
int primary;
|
|
float pdist,dist;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf ("VBSP_LoadDisplacements: funny lump size in %s\n",mod->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
if (!count)
|
|
return true; //nothing to worry about.
|
|
out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));
|
|
|
|
for (maxverts = 0, i = 0; i < count; i++)
|
|
maxverts += ((1<<in[i].power)) * ((1<<in[i].power));
|
|
indexes = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*indexes)*6);
|
|
|
|
for (maxverts = 0, i = 0; i < count; i++)
|
|
maxverts += ((1<<in[i].power)+1) * ((1<<in[i].power)+1);
|
|
verts = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*verts));
|
|
xyz = plugfuncs->GMalloc(&mod->memgroup, maxverts*sizeof(*xyz));
|
|
|
|
prv->displacements = out;
|
|
prv->numdisplacements = count;
|
|
for (i = 0; i < count; i++, out++, in++)
|
|
{
|
|
surf = mod->surfaces + in->faceidx;
|
|
if (surf->numedges != 4)
|
|
{
|
|
Con_Printf ("VBSP_LoadDisplacements: displacement surface doesn't have 4 edges in %s\n",mod->name);
|
|
return false;
|
|
}
|
|
|
|
//find the 4 verts... messy.
|
|
for (x = 0; x < 4; x++)
|
|
{
|
|
e = mod->surfedges[surf->firstedge+x];
|
|
idx = e < 0;
|
|
if (idx)
|
|
e = -e;
|
|
if (e < 0 || e >= mod->numedges)
|
|
sverts[x] = mod->vertexes[0].position;
|
|
else
|
|
sverts[x] = mod->vertexes[mod->edges[e].v[idx]].position;
|
|
}
|
|
|
|
//this is just stupid and pointless.
|
|
//the in->position point tells us which point is the primary one, instead of just rotating the edges.
|
|
primary = 0;
|
|
pdist = FLT_MAX;
|
|
for (x = 0; x < 4; x++)
|
|
{
|
|
VectorSubtract(sverts[x], in->position, p);
|
|
dist = DotProduct(p,p);
|
|
if (dist < pdist)
|
|
{
|
|
pdist = dist;
|
|
primary = x;
|
|
}
|
|
}
|
|
|
|
out->surf = surf;
|
|
prv->surfdisp[in->faceidx] = out; //the surface needs to be able to get its proper info when building vbos
|
|
ClearBounds(out->aamin, out->aamax);
|
|
out->contents = VBSP_TranslateContentBits(prv,in->contents);
|
|
out->width = (1<<in->power);
|
|
out->height = (1<<in->power);
|
|
|
|
out->idx = indexes;
|
|
stride = out->width+1;
|
|
for (y=0 ; y<out->height ; y++)
|
|
for (x=0 ; x<out->width ; x++)
|
|
{
|
|
if ((x+y)&1)
|
|
{ //a diamond pattern - flipping alternately
|
|
*indexes++ = (x+0)+(y+1)*stride;
|
|
*indexes++ = (x+1)+(y+1)*stride;
|
|
*indexes++ = (x+1)+(y+0)*stride;
|
|
*indexes++ = (x+0)+(y+1)*stride;
|
|
*indexes++ = (x+1)+(y+0)*stride;
|
|
*indexes++ = (x+0)+(y+0)*stride;
|
|
}
|
|
else
|
|
{
|
|
*indexes++ = (x+0)+(y+0)*stride;
|
|
*indexes++ = (x+0)+(y+1)*stride;
|
|
*indexes++ = (x+1)+(y+1)*stride;
|
|
*indexes++ = (x+0)+(y+0)*stride;
|
|
*indexes++ = (x+1)+(y+1)*stride;
|
|
*indexes++ = (x+1)+(y+0)*stride;
|
|
}
|
|
}
|
|
out->numindexes = indexes - out->idx;
|
|
|
|
inv = (void *)(mod_base + vl->fileofs);
|
|
inv += in->firstvert;
|
|
out->verts = verts;
|
|
out->xyz = xyz;
|
|
for (y = 0; y <= out->height; y++)
|
|
for (x = 0; x <= out->width; x++)
|
|
{
|
|
//I have no idea if this is right. probably not. oh well.
|
|
fx = (float)x/out->width;
|
|
fy = (float)y/out->height;
|
|
VectorClear(base);
|
|
VectorMA(base, (1-fx)*(1-fy), sverts[(primary+0)&3], base);
|
|
VectorMA(base, (1-fx)*( fy), sverts[(primary+1)&3], base);
|
|
VectorMA(base, ( fx)*( fy), sverts[(primary+2)&3], base);
|
|
VectorMA(base, ( fx)*(1-fy), sverts[(primary+3)&3], base);
|
|
verts->st[0] = DotProduct(base, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
|
|
verts->st[1] = DotProduct(base, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
|
|
|
|
VectorScale(inv->norm, inv->dist*hl2_displacement_scale->value, p);
|
|
VectorNormalize2(p, verts->norm);
|
|
VectorAdd(base, p, (*xyz));
|
|
verts->alpha = inv->alpha;
|
|
AddPointToBounds((*xyz), out->aamin, out->aamax);
|
|
|
|
inv++;
|
|
verts++;
|
|
xyz++;
|
|
}
|
|
|
|
surf->mesh->numindexes = out->numindexes;
|
|
surf->mesh->numvertexes = verts-out->verts;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
short planenum;
|
|
qbyte side;
|
|
qbyte onnode; //o.O
|
|
|
|
int firstedge; // we must support > 64k edges
|
|
unsigned short numedges;
|
|
unsigned short texinfo;
|
|
|
|
unsigned short dispinfo;
|
|
short fogvolume;
|
|
|
|
// lighting info
|
|
qbyte styles[4];
|
|
int lightofs; // start of [numstyles*surfsize] samples
|
|
float surfacearea;
|
|
int extents_min[2];
|
|
int extents_size[2];
|
|
|
|
int origface;
|
|
unsigned short numprims;
|
|
unsigned short firstprim;
|
|
unsigned int smoothinggroup;
|
|
} hl2dface_t;
|
|
static qboolean VBSP_LoadFaces (model_t *mod, qbyte *mod_base, vlump_t *lumps, int version)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
vlump_t *l = &lumps[VLUMP_FACES_LDR];
|
|
vlump_t *l2 = &lumps[VLUMP_FACES_HDR];
|
|
hl2dface_t *in;
|
|
msurface_t *out;
|
|
int i, count, surfnum;
|
|
int planenum;
|
|
int ti, st;
|
|
int lumpsize = sizeof(*in);
|
|
|
|
mesh_t *meshes;
|
|
|
|
if (l2->filelen && !(hl2_favour_ldr->ival && lumps[VLUMP_LIGHTING_LDR].filelen))
|
|
l = l2;
|
|
|
|
if (version == 18)
|
|
{ //this version seems to have rgbx*4 prefixed
|
|
in = (void *)(mod_base + l->fileofs + 4*4);
|
|
lumpsize += 4*4;
|
|
}
|
|
else
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % lumpsize)
|
|
{
|
|
Con_Printf ("VBSP_LoadFaces: funny lump size in %s\n",mod->name);
|
|
return false;
|
|
}
|
|
count = l->filelen / lumpsize;
|
|
out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out));
|
|
prv->surfdisp = plugfuncs->GMalloc(&mod->memgroup, count * sizeof(*prv->surfdisp));
|
|
|
|
meshes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*meshes));
|
|
|
|
mod->surfaces = out;
|
|
mod->numsurfaces = count;
|
|
|
|
mod->lightmaps.surfstyles = 1;
|
|
|
|
for ( surfnum=0 ; surfnum<count ; surfnum++, in = (void*)((qbyte*)in+lumpsize), out++)
|
|
{
|
|
out->firstedge = LittleLong(in->firstedge);
|
|
out->numedges = (unsigned short)LittleShort(in->numedges);
|
|
out->flags = 0;
|
|
out->mesh = meshes+surfnum;
|
|
out->mesh->numvertexes = out->numedges;
|
|
out->mesh->numindexes = (out->mesh->numvertexes-2)*3;
|
|
|
|
planenum = (unsigned short)LittleShort(in->planenum);
|
|
if (in->side)
|
|
out->flags |= SURF_PLANEBACK;
|
|
if (!in->onnode)
|
|
out->flags |= SURF_OFFNODE;
|
|
|
|
out->plane = mod->planes + planenum;
|
|
|
|
ti = (unsigned short)LittleShort (in->texinfo);
|
|
if (ti < 0 || ti >= mod->numtexinfo)
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadFaces: bad texinfo number\n");
|
|
return false;
|
|
}
|
|
out->texinfo = mod->texinfo + ti;
|
|
|
|
if (out->texinfo->flags & TI_SKY)
|
|
{
|
|
out->flags |= SURF_DRAWSKY|SURF_DRAWTILED;
|
|
}
|
|
if (out->texinfo->flags & TI_WARP)
|
|
{
|
|
out->flags |= SURF_DRAWTURB|SURF_DRAWTILED;
|
|
}
|
|
|
|
out->lmshift = 0;
|
|
out->texturemins[0] = in->extents_min[0];
|
|
out->texturemins[1] = in->extents_min[1];
|
|
out->extents[0] = in->extents_size[0];
|
|
out->extents[1] = in->extents_size[1];
|
|
|
|
// lighting info
|
|
|
|
for (i=0 ; i<Q1Q2BSP_STYLESPERSURF ; i++)
|
|
{
|
|
st = in->styles[i];
|
|
if (st == 255)
|
|
st = INVALID_LIGHTSTYLE;
|
|
else if (mod->lightmaps.maxstyle < st)
|
|
mod->lightmaps.maxstyle = st;
|
|
out->styles[i] = st;
|
|
}
|
|
for (; i<MAXCPULIGHTMAPS ; i++)
|
|
out->styles[i] = INVALID_LIGHTSTYLE;
|
|
for (i = 0; i<MAXRLIGHTMAPS ; i++)
|
|
out->vlstyles[i] = INVALID_VLIGHTSTYLE;
|
|
i = LittleLong(in->lightofs);
|
|
if (i == -1 || !mod->lightdata)
|
|
out->samples = NULL;
|
|
else
|
|
out->samples = mod->lightdata + i;
|
|
|
|
// set the drawing flags
|
|
|
|
if (out->texinfo->flags & TI_WARP)
|
|
out->flags |= SURF_DRAWTURB;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#ifdef HAVE_CLIENT
|
|
static void VBSP_BuildSurfMesh(model_t *mod, msurface_t *surf, builddata_t *bd)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
|
|
unsigned int vertidx;
|
|
int i, lindex, edgevert;
|
|
mesh_t *mesh = surf->mesh;
|
|
float *vec;
|
|
float s, t, miss;
|
|
int sty;
|
|
struct vbsptexinfo_s *vtexinfo;
|
|
|
|
//displacement surfaces...
|
|
dispinfo_t *d = prv->surfdisp[surf-mod->surfaces];
|
|
if (d)
|
|
{
|
|
struct dispvert_s *dv = d->verts;
|
|
|
|
mesh->istrifan = false;
|
|
|
|
memcpy(mesh->indexes, d->idx, sizeof(index_t)*d->numindexes);
|
|
|
|
//output the renderable verticies
|
|
for (i=0 ; i<mesh->numvertexes ; i++, dv++)
|
|
{
|
|
//xyz
|
|
VectorCopy (d->xyz[i], mesh->xyz_array[i]);
|
|
|
|
//st
|
|
mesh->st_array[i][0] = dv->st[0];
|
|
mesh->st_array[i][1] = dv->st[1];
|
|
if (surf->texinfo->texture->vwidth)
|
|
mesh->st_array[i][0] /= surf->texinfo->texture->vwidth;
|
|
if (surf->texinfo->texture->vheight)
|
|
mesh->st_array[i][1] /= surf->texinfo->texture->vheight;
|
|
|
|
//lmst
|
|
s = (float)(i%(d->width+1))/d->width;
|
|
t = (float)(i/(d->width+1))/d->height;
|
|
for (sty = 0; sty < 1; sty++)
|
|
{
|
|
mesh->lmst_array[sty][i][0] = (s*surf->extents[0] + surf->light_s[sty] + 0.5) / (mod->lightmaps.width);
|
|
mesh->lmst_array[sty][i][1] = (t*surf->extents[1] + surf->light_t[sty] + 0.5) / (mod->lightmaps.height);
|
|
}
|
|
|
|
//normals
|
|
VectorCopy(surf->plane->normal, mesh->normals_array[i]);
|
|
VectorCopy(surf->texinfo->vecs[0], mesh->snormals_array[i]);
|
|
VectorNegate(surf->texinfo->vecs[1], mesh->tnormals_array[i]);
|
|
|
|
//rgba
|
|
for (sty = 0; sty < 1; sty++)
|
|
{
|
|
mesh->colors4f_array[sty][i][0] = 1;
|
|
mesh->colors4f_array[sty][i][1] = 1;
|
|
mesh->colors4f_array[sty][i][2] = 1;
|
|
mesh->colors4f_array[sty][i][3] = ((int)dv->alpha&255)/255.0;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
//regular surfaces...
|
|
mesh->istrifan = true;
|
|
|
|
//output the mesh's indicies
|
|
for (i=0 ; i<mesh->numvertexes-2 ; i++)
|
|
{
|
|
mesh->indexes[i*3] = 0;
|
|
mesh->indexes[i*3+1] = i+1;
|
|
mesh->indexes[i*3+2] = i+2;
|
|
}
|
|
//output the renderable verticies
|
|
for (i=0 ; i<mesh->numvertexes ; i++)
|
|
{
|
|
lindex = mod->surfedges[surf->firstedge + i];
|
|
edgevert = lindex <= 0;
|
|
if (edgevert)
|
|
lindex = -lindex;
|
|
if (lindex < 0 || lindex >= mod->numedges)
|
|
vertidx = 0;
|
|
else
|
|
vertidx = mod->edges[lindex].v[edgevert];
|
|
vec = mod->vertexes[vertidx].position;
|
|
|
|
s = DotProduct (vec, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
|
|
t = DotProduct (vec, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
|
|
|
|
VectorCopy (vec, mesh->xyz_array[i]);
|
|
|
|
mesh->st_array[i][0] = s;
|
|
mesh->st_array[i][1] = t;
|
|
if (surf->texinfo->texture->vwidth)
|
|
mesh->st_array[i][0] /= surf->texinfo->texture->vwidth;
|
|
if (surf->texinfo->texture->vheight)
|
|
mesh->st_array[i][1] /= surf->texinfo->texture->vheight;
|
|
|
|
vtexinfo = &prv->texinfo[surf->texinfo-mod->texinfo];
|
|
s = DotProduct (vec, vtexinfo->lmvecs[0]) + vtexinfo->lmvecs[0][3];
|
|
t = DotProduct (vec, vtexinfo->lmvecs[1]) + vtexinfo->lmvecs[1][3];
|
|
for (sty = 0; sty < 1; sty++)
|
|
{
|
|
mesh->lmst_array[sty][i][0] = (s - surf->texturemins[0] + (surf->light_s[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.width<<surf->lmshift);
|
|
mesh->lmst_array[sty][i][1] = (t - surf->texturemins[1] + (surf->light_t[sty]<<surf->lmshift) + (1<<surf->lmshift)*0.5) / (mod->lightmaps.height<<surf->lmshift);
|
|
}
|
|
|
|
//figure out the texture directions, for bumpmapping and stuff
|
|
if (surf->flags & SURF_PLANEBACK)
|
|
VectorNegate(surf->plane->normal, mesh->normals_array[i]);
|
|
else
|
|
VectorCopy(surf->plane->normal, mesh->normals_array[i]);
|
|
VectorCopy(surf->texinfo->vecs[0], mesh->snormals_array[i]);
|
|
VectorNegate(surf->texinfo->vecs[1], mesh->tnormals_array[i]);
|
|
//the s+t vectors are axis-aligned, so fiddle them so they're normal aligned instead
|
|
miss = -DotProduct(mesh->normals_array[i], mesh->snormals_array[i]);
|
|
VectorMA(mesh->snormals_array[i], miss, mesh->normals_array[i], mesh->snormals_array[i]);
|
|
miss = -DotProduct(mesh->normals_array[i], mesh->tnormals_array[i]);
|
|
VectorMA(mesh->tnormals_array[i], miss, mesh->normals_array[i], mesh->tnormals_array[i]);
|
|
VectorNormalize(mesh->snormals_array[i]);
|
|
VectorNormalize(mesh->tnormals_array[i]);
|
|
|
|
//q1bsp has no colour information (fixme: sample from the lightmap?)
|
|
for (sty = 0; sty < 1; sty++)
|
|
{
|
|
mesh->colors4f_array[sty][i][0] = 1;
|
|
mesh->colors4f_array[sty][i][1] = 1;
|
|
mesh->colors4f_array[sty][i][2] = 1;
|
|
mesh->colors4f_array[sty][i][3] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void VBSP_LoadLighting (model_t *mod, qbyte *mod_base, vlump_t *ldr, vlump_t *hdr)
|
|
{
|
|
qbyte *src;
|
|
unsigned int *out;
|
|
size_t count;
|
|
if (hdr->filelen && !(hl2_favour_ldr->ival && ldr->filelen))
|
|
{
|
|
mod->lightdatasize = hdr->filelen;
|
|
src = (mod_base + hdr->fileofs);
|
|
}
|
|
else if (ldr->filelen)
|
|
{
|
|
mod->lightdatasize = ldr->filelen;
|
|
src = (mod_base + ldr->fileofs);
|
|
}
|
|
else
|
|
return;
|
|
|
|
mod->lightmaps.fmt = LM_E5BGR9;
|
|
mod->lightdata = (qbyte*)(out = plugfuncs->GMalloc(&mod->memgroup, mod->lightdatasize));
|
|
|
|
//convert from linear e8bgr8 to srgb e5bgr9
|
|
for (count = mod->lightdatasize/4; count --> 0; src+=4)
|
|
{
|
|
int e = 0;
|
|
float m;
|
|
float scale;
|
|
unsigned int hdr;
|
|
vec3_t rgb;
|
|
|
|
//decode input
|
|
m = pow(2, (signed char)src[3])/255.0;
|
|
rgb[0] = m * src[0];
|
|
rgb[1] = m * src[1];
|
|
rgb[2] = m * src[2];
|
|
|
|
//rescale its gamma ramp to something we can actually use properly
|
|
rgb[0] = M_LinearToSRGB(rgb[0], 1.0);
|
|
rgb[1] = M_LinearToSRGB(rgb[1], 1.0);
|
|
rgb[2] = M_LinearToSRGB(rgb[2], 1.0);
|
|
|
|
//encode output
|
|
m = max(max(rgb[0], rgb[1]), rgb[2]);
|
|
if (m < 0)
|
|
m = 0;
|
|
|
|
if (m >= 0.5)
|
|
{ //positive exponent
|
|
while (m >= (1<<(e)) && e < 30-15) //don't do nans.
|
|
e++;
|
|
}
|
|
else
|
|
{ //negative exponent...
|
|
while (m < 1/(1<<-e) && e > -14) //don't do denormals.
|
|
e--;
|
|
}
|
|
|
|
scale = pow(2, e-9);
|
|
hdr = ((e+15)<<27);
|
|
hdr |= bound(0, (int)(rgb[0]/scale + 0.5), 0x1ff)<<0;
|
|
hdr |= bound(0, (int)(rgb[1]/scale + 0.5), 0x1ff)<<9;
|
|
hdr |= bound(0, (int)(rgb[2]/scale + 0.5), 0x1ff)<<18;
|
|
*out++ = hdr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
unsigned short planenum;
|
|
short texinfo;
|
|
unsigned short dispinfo;
|
|
short bevel;
|
|
} hl2dbrushside_t;
|
|
static qboolean VBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
unsigned int i, j;
|
|
q2cbrushside_t *out;
|
|
hl2dbrushside_t *in;
|
|
int count;
|
|
int num;
|
|
|
|
in = (void *)(mod_base + l->fileofs);
|
|
if (l->filelen % sizeof(*in))
|
|
{
|
|
Con_Printf (CON_ERROR "VBSP_LoadBrushSides: funny lump size\n");
|
|
return false;
|
|
}
|
|
count = l->filelen / sizeof(*in);
|
|
|
|
// need to save space for box planes
|
|
if (count > SANITY_MAX_MAP_BRUSHSIDES)
|
|
{
|
|
Con_Printf (CON_ERROR "Map has too many brushsides (%i)\n", count);
|
|
return false;
|
|
}
|
|
|
|
out = prv->brushsides = plugfuncs->GMalloc(&mod->memgroup, sizeof(*out) * count);
|
|
prv->numbrushsides = count;
|
|
|
|
for ( i=0 ; i<count ; i++, in++, out++)
|
|
{
|
|
num = (unsigned short)LittleShort (in->planenum);
|
|
out->plane = &mod->planes[num];
|
|
j = (unsigned short)LittleShort (in->texinfo);
|
|
if (j >= mod->numtexinfo)
|
|
out->surface = &nullsurface;
|
|
else
|
|
out->surface = &prv->surfaces[j];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef struct {
|
|
unsigned int count;
|
|
struct {
|
|
unsigned int id;
|
|
unsigned short flags;
|
|
unsigned short version;
|
|
unsigned int ofs;
|
|
unsigned int len;
|
|
} sl[1];
|
|
} hlgamelumpheader_t;
|
|
static qboolean VBSP_LoadStaticProps(model_t *mod, qbyte *offset, size_t size, int version) //present on server, because they're potentially solid.
|
|
{
|
|
vbspinfo_t *prv = mod->meshinfo;
|
|
struct {
|
|
const char name[128];
|
|
} *modelref;
|
|
size_t nummodels, numleafrefs, numprops, i;
|
|
unsigned short *leafref, *eleafref;
|
|
struct staticprop_s *sent;
|
|
entity_t *ent;
|
|
|
|
size_t modelindex;
|
|
|
|
qboolean skip = false;
|
|
int dxlevel = 95, cpulevel=0, gpulevel=0;
|
|
unsigned int leafcount, l;
|
|
|
|
qbyte *prop;
|
|
size_t propsize;
|
|
switch(version)
|
|
{
|
|
case 4:
|
|
propsize = 14*4;
|
|
break;
|
|
case 5: //+scale
|
|
propsize = 15*4;
|
|
break;
|
|
case 6://+dxlevels
|
|
propsize = 16*4;
|
|
break;
|
|
case 7: //+rgba
|
|
case 8: //-dxlevels+[cg]pulevels
|
|
propsize = 17*4;
|
|
break;
|
|
case 9: //+360
|
|
case 10://-360+flags
|
|
propsize = 18*4;
|
|
break;
|
|
case 11://+scale
|
|
propsize = 19*4;
|
|
break;
|
|
default:
|
|
return true; //version not supported, just ignore it entirely. sorry.
|
|
}
|
|
|
|
|
|
nummodels = LittleLong(*(int*)offset);
|
|
offset += 4;
|
|
size -= 4;
|
|
modelref = (void*)offset;
|
|
offset += nummodels*sizeof(*modelref);
|
|
size -= nummodels*sizeof(*modelref);
|
|
|
|
numleafrefs = LittleLong(*(int*)offset);
|
|
offset += 4;
|
|
size -= 4;
|
|
leafref = (void*)offset;
|
|
offset += numleafrefs*sizeof(*leafref);
|
|
size -= numleafrefs*sizeof(*leafref);
|
|
|
|
numprops = LittleLong(*(int*)offset);
|
|
offset += 4;
|
|
size -= 4;
|
|
prop = (void*)offset;
|
|
offset += numprops*propsize;
|
|
size -= numprops*propsize;
|
|
|
|
if (size)
|
|
return true; //funny lump size...
|
|
|
|
prv->staticprops = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->staticprops)*numprops);
|
|
for (i = 0, sent = prv->staticprops; i < numprops; i++)
|
|
{
|
|
ent = &sent->ent;
|
|
skip = false;
|
|
ent->playerindex = -1;
|
|
ent->topcolour = TOP_DEFAULT;
|
|
ent->bottomcolour = BOTTOM_DEFAULT;
|
|
ent->scale = 1;
|
|
ent->shaderRGBAf[0] = 1;
|
|
ent->shaderRGBAf[1] = 1;
|
|
ent->shaderRGBAf[2] = 1;
|
|
ent->shaderRGBAf[3] = 1;
|
|
ent->framestate.g[FS_REG].frame[0] = 0;
|
|
ent->framestate.g[FS_REG].lerpweight[0] = 1;
|
|
|
|
|
|
ent->origin[0] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
ent->origin[1] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
ent->origin[2] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
ent->angles[0] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
ent->angles[1] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
ent->angles[2] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
modelindex = (unsigned short)LittleShort(*(short*)prop); prop += sizeof(unsigned short);
|
|
eleafref = leafref+(unsigned short)LittleShort(*(unsigned short*)prop); prop += sizeof(unsigned short);
|
|
leafcount = LittleShort(*(unsigned short*)prop); prop += sizeof(unsigned short);
|
|
sent->solid = *prop; prop += sizeof(qbyte);
|
|
/*ent->flags = *prop*/; prop += sizeof(qbyte);
|
|
ent->skinnum = LittleLong(*(unsigned int*)prop); prop += sizeof(unsigned int);
|
|
sent->fademindist = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
sent->fademaxdist = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
sent->lightorg[0] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
sent->lightorg[1] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
sent->lightorg[2] = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
if (version >= 5)
|
|
{
|
|
/*ent->fadescale = LittleFloat(*(float*)prop);*/ prop += sizeof(float);
|
|
}
|
|
if (version >= 8)
|
|
{
|
|
skip |= (prop[0] > cpulevel || cpulevel > prop[1]); prop += sizeof(qbyte)*2;
|
|
skip |= (prop[0] > gpulevel || gpulevel > prop[1]); prop += sizeof(qbyte)*2;
|
|
}
|
|
else if (version >= 6)
|
|
{
|
|
unsigned short minlev, maxlev;
|
|
minlev = LittleShort(*(unsigned short*)prop); prop += sizeof(unsigned short);
|
|
maxlev = LittleShort(*(unsigned short*)prop); prop += sizeof(unsigned short);
|
|
skip |= (minlev > dxlevel || dxlevel > maxlev);
|
|
}
|
|
if (version >= 7)
|
|
{
|
|
VectorScale(prop, 1/255.0, ent->shaderRGBAf); prop += sizeof(qbyte)*4;
|
|
}
|
|
|
|
if (version == 9)
|
|
{
|
|
/*disablex360 = LittleLong(*(int*)prop);*/ prop += sizeof(int);
|
|
}
|
|
if (version >= 10)
|
|
{
|
|
ent->flags = LittleLong(*(int*)prop); prop += sizeof(int);
|
|
}
|
|
if (version >= 11)
|
|
{
|
|
ent->scale = LittleFloat(*(float*)prop); prop += sizeof(float);
|
|
}
|
|
|
|
//okay, we parsed the prop data now...
|
|
skip |= modelindex >= nummodels;
|
|
if (skip)
|
|
continue; //we're ignoring it for some reason
|
|
ent->model = modfuncs->BeginSubmodelLoad(modelref[modelindex].name);
|
|
modfuncs->AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
|
|
VectorNegate(ent->axis[1],ent->axis[1]);
|
|
|
|
//transforms, in case we need to recursively walk its bih
|
|
VectorCopy(ent->axis[0], prv->staticprops[i].transform.axis[0]);
|
|
VectorCopy(ent->axis[1], prv->staticprops[i].transform.axis[1]);
|
|
VectorCopy(ent->axis[2], prv->staticprops[i].transform.axis[2]);
|
|
VectorCopy(ent->origin, prv->staticprops[i].transform.origin);
|
|
|
|
//Hack: special value to flag it for linking once we've loaded its model. this needs to go when we make them solid.
|
|
if (leafcount > countof(ent->pvscache.leafnums))
|
|
ent->pvscache.num_leafs = -2; //overflow. calculate it later once its loaded.
|
|
else
|
|
{
|
|
ent->pvscache.areanum = -1; //FIXME
|
|
ent->pvscache.areanum2 = -1;
|
|
ent->pvscache.num_leafs = leafcount; //overflow. calculate it later once its loaded.
|
|
for (l = 0; l < leafcount; l++)
|
|
{
|
|
mleaf_t *lf = mod->leafs + *eleafref++;
|
|
ent->pvscache.leafnums[l]/*actually clusters*/ = lf->cluster;
|
|
//and try to track the areas too. not quite so reliable...
|
|
if (ent->pvscache.areanum == -1)
|
|
ent->pvscache.areanum = lf->area;
|
|
else
|
|
ent->pvscache.areanum2 = lf->area;
|
|
}
|
|
}
|
|
//Hack: lighting is wrong.
|
|
// ent->light_type = ELT_UNKNOWN;
|
|
VectorSet(ent->light_dir, 0, 0.707, 0.707);
|
|
VectorSet(ent->light_avg, 0.75, 0.75, 0.75);
|
|
VectorSet(ent->light_range, 0.5, 0.5, 0.5);
|
|
|
|
//not all props will be emitted, according to d3d levels...
|
|
prv->numstaticprops++;
|
|
sent++;
|
|
}
|
|
return true;
|
|
}
|
|
static qboolean VBSP_LoadGameLump(model_t *mod, qbyte *mod_base, vlump_t *l)
|
|
{
|
|
size_t i;
|
|
hlgamelumpheader_t *blob = (void*)(mod_base + l->fileofs);
|
|
if (!l->filelen)
|
|
return true; //missing
|
|
if (l->filelen < sizeof(*blob) + sizeof(*blob->sl)*(blob->count-1))
|
|
return false; //not even enough space for the header...
|
|
for (i = 0; i < blob->count; i++)
|
|
{
|
|
#define LUMPTYPE(a,b,c,d, minver, maxver) (blob->sl[i].id == (((qbyte)a<<24)|((qbyte)b<<16)|((qbyte)c<<8)|((qbyte)d<<0)) && blob->sl[i].version >= minver && blob->sl[i].version <= maxver)
|
|
if (LUMPTYPE('s','p','r','p', 4,10) && !blob->sl[i].flags) //static props (placed by mapper)
|
|
VBSP_LoadStaticProps(mod, mod_base+blob->sl[i].ofs/*sigh*/, blob->sl[i].len, blob->sl[i].version);
|
|
else if (LUMPTYPE('d','p','r','p', 4,4) && !blob->sl[i].flags) //dynamic props (generated by textures)
|
|
;
|
|
else if (LUMPTYPE('d','p','l','t', 0,0) && !blob->sl[i].flags) //detail prop ldr lighting
|
|
;
|
|
else if (LUMPTYPE('d','p','l','h', 0,0) && !blob->sl[i].flags) //detail prop hdr lighting
|
|
;
|
|
else
|
|
Con_Printf("Unsupported gamelump id/version %c%c%c%c %i\n", (blob->sl[i].id>>24),(blob->sl[i].id>>16),(blob->sl[i].id>>8),(blob->sl[i].id>>0),blob->sl[i].version);
|
|
#undef LUMPTYPE
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifdef HAVE_CLIENT
|
|
static void VBSP_GenerateMaterials(void *ctx, void *data, size_t a, size_t b)
|
|
{
|
|
model_t *mod = ctx;
|
|
const char *script;
|
|
|
|
if (!a)
|
|
{ //submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world).
|
|
for(a = 0; a < mod->numtextures; a++)
|
|
{
|
|
script = NULL;
|
|
if (!strncmp(mod->textures[a]->name, "sky/", 4))
|
|
script =
|
|
"{\n"
|
|
"sort sky\n"
|
|
"surfaceparm nodlight\n"
|
|
"skyparms - - -\n"
|
|
"}\n";
|
|
mod->textures[a]->shader = modfuncs->RegisterBasicShader(mod, mod->textures[a]->name, SUF_LIGHTMAP, script, PTI_INVALID, 0, 0, NULL, NULL);
|
|
}
|
|
}
|
|
modfuncs->Batches_Build(mod, data);
|
|
if (data)
|
|
plugfuncs->Free(data);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
==================
|
|
CM_InlineModel
|
|
==================
|
|
*/
|
|
static cmodel_t *VBSP_InlineModel (model_t *model, char *name)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)model->meshinfo;
|
|
int num;
|
|
|
|
if (!name)
|
|
Host_Error("Bad model\n");
|
|
else if (name[0] != '*')
|
|
Host_Error("Bad model\n");
|
|
|
|
num = atoi (name+1);
|
|
|
|
if (num < 1 || num >= prv->numcmodels)
|
|
Host_Error ("CM_InlineModel: bad number");
|
|
|
|
return &prv->cmodels[num];
|
|
}
|
|
|
|
static int VBSP_NumInlineModels (model_t *model)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)model->meshinfo;
|
|
return prv->numcmodels;
|
|
}
|
|
|
|
static int VBSP_LeafContents (model_t *model, int leafnum)
|
|
{
|
|
if (leafnum < 0 || leafnum >= model->numleafs)
|
|
Host_Error ("CM_LeafContents: bad number");
|
|
return model->leafs[leafnum].contents;
|
|
}
|
|
|
|
static int VBSP_LeafCluster (model_t *model, int leafnum)
|
|
{
|
|
if (leafnum < 0 || leafnum >= model->numleafs)
|
|
Host_Error ("CM_LeafCluster: bad number");
|
|
return model->leafs[leafnum].cluster;
|
|
}
|
|
|
|
static int VBSP_LeafArea (model_t *model, int leafnum)
|
|
{
|
|
if (leafnum < 0 || leafnum >= model->numleafs)
|
|
Host_Error ("CM_LeafArea: bad number");
|
|
return model->leafs[leafnum].area;
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
/*
|
|
==================
|
|
CM_PointLeafnum_r
|
|
|
|
==================
|
|
*/
|
|
static int VBSP_PointLeafnum_r (model_t *mod, const vec3_t p, int num)
|
|
{
|
|
float d;
|
|
mnode_t *node;
|
|
mplane_t *plane;
|
|
|
|
while (num >= 0)
|
|
{
|
|
node = mod->nodes + num;
|
|
plane = node->plane;
|
|
|
|
if (plane->type < 3)
|
|
d = p[plane->type] - plane->dist;
|
|
else
|
|
d = DotProduct (plane->normal, p) - plane->dist;
|
|
if (d < 0)
|
|
num = node->childnum[1];
|
|
else
|
|
num = node->childnum[0];
|
|
}
|
|
|
|
return -1 - num;
|
|
}
|
|
|
|
static int VBSP_PointLeafnum (model_t *mod, const vec3_t p)
|
|
{
|
|
if (mod->loadstate != MLS_LOADED)
|
|
return 0; // sound may call this without map loaded
|
|
return VBSP_PointLeafnum_r (mod, p, 0);
|
|
}
|
|
|
|
static int VBSP_PointCluster (model_t *mod, const vec3_t p, int *area)
|
|
{
|
|
int leaf;
|
|
if (mod->loadstate != MLS_LOADED)
|
|
return 0; // sound may call this without map loaded
|
|
|
|
leaf = VBSP_PointLeafnum_r (mod, p, 0);
|
|
if (area)
|
|
*area = VBSP_LeafArea(mod, leaf);
|
|
return VBSP_LeafCluster(mod, leaf);
|
|
}
|
|
|
|
static unsigned int VBSP_PointContents (model_t *mod, const vec3_t axis[3], const vec3_t p)
|
|
{
|
|
vec3_t np;
|
|
if (mod->loadstate != MLS_LOADED)
|
|
return 0;
|
|
|
|
if (axis)
|
|
{
|
|
np[0] = DotProduct(p, axis[0]);
|
|
np[1] = DotProduct(p, axis[1]);
|
|
np[2] = DotProduct(p, axis[2]);
|
|
p = np;
|
|
}
|
|
|
|
return VBSP_LeafContents(mod, VBSP_PointLeafnum_r (mod, p, 0));
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CM_BoxLeafnums
|
|
|
|
Fills in a list of all the leafs touched
|
|
=============
|
|
*/
|
|
static int leaf_count, leaf_maxcount;
|
|
static int *leaf_list;
|
|
static const float *leaf_mins, *leaf_maxs;
|
|
static int leaf_topnode;
|
|
|
|
static void VBSP_BoxLeafnums_r (model_t *mod, int nodenum)
|
|
{
|
|
mplane_t *plane;
|
|
mnode_t *node;
|
|
int s;
|
|
|
|
while (1)
|
|
{
|
|
if (nodenum < 0)
|
|
{
|
|
if (leaf_count >= leaf_maxcount)
|
|
return;
|
|
leaf_list[leaf_count++] = -1 - nodenum;
|
|
return;
|
|
}
|
|
|
|
node = &mod->nodes[nodenum];
|
|
plane = node->plane;
|
|
// s = BoxOnPlaneSide (leaf_mins, leaf_maxs, plane);
|
|
s = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane);
|
|
if (s == 1)
|
|
nodenum = node->childnum[0];
|
|
else if (s == 2)
|
|
nodenum = node->childnum[1];
|
|
else
|
|
{ // go down both
|
|
if (leaf_topnode == -1)
|
|
leaf_topnode = nodenum;
|
|
VBSP_BoxLeafnums_r (mod, node->childnum[0]);
|
|
nodenum = node->childnum[1];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static int VBSP_BoxLeafnums_headnode (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int headnode, int *topnode)
|
|
{
|
|
leaf_list = list;
|
|
leaf_count = 0;
|
|
leaf_maxcount = listsize;
|
|
leaf_mins = mins;
|
|
leaf_maxs = maxs;
|
|
|
|
leaf_topnode = -1;
|
|
|
|
VBSP_BoxLeafnums_r (mod, headnode);
|
|
|
|
if (topnode)
|
|
*topnode = leaf_topnode;
|
|
|
|
return leaf_count;
|
|
}
|
|
|
|
static int VBSP_BoxLeafnums (model_t *mod, const vec3_t mins, const vec3_t maxs, int *list, int listsize, int *topnode)
|
|
{
|
|
return VBSP_BoxLeafnums_headnode (mod, mins, maxs, list,
|
|
listsize, mod->hulls[0].firstclipnode, topnode);
|
|
}
|
|
|
|
static void VBSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, const float *mins, const float *maxs)
|
|
{
|
|
#define MAX_TOTAL_ENT_LEAFS 128
|
|
int leafs[MAX_TOTAL_ENT_LEAFS];
|
|
int clusters[MAX_TOTAL_ENT_LEAFS];
|
|
int num_leafs;
|
|
int topnode;
|
|
int i, j;
|
|
int area;
|
|
int nullarea = -1;
|
|
|
|
//ent->num_leafs == q2's ent->num_clusters
|
|
ent->num_leafs = 0;
|
|
ent->areanum = nullarea;
|
|
ent->areanum2 = nullarea;
|
|
|
|
if (!mins || !maxs)
|
|
return;
|
|
|
|
//get all leafs, including solids
|
|
num_leafs = VBSP_BoxLeafnums (model, mins, maxs,
|
|
leafs, MAX_TOTAL_ENT_LEAFS, &topnode);
|
|
|
|
// set areas
|
|
for (i=0 ; i<num_leafs ; i++)
|
|
{
|
|
clusters[i] = VBSP_LeafCluster (model, leafs[i]);
|
|
area = VBSP_LeafArea (model, leafs[i]);
|
|
if (area != nullarea)
|
|
{ // doors may legally straggle two areas,
|
|
// but nothing should ever need more than that
|
|
if (ent->areanum != nullarea && ent->areanum != area)
|
|
ent->areanum2 = area;
|
|
else
|
|
ent->areanum = area;
|
|
}
|
|
}
|
|
|
|
if (num_leafs >= MAX_TOTAL_ENT_LEAFS)
|
|
{ // assume we missed some leafs, and mark by headnode
|
|
ent->num_leafs = -1;
|
|
ent->headnode = topnode;
|
|
}
|
|
else
|
|
{
|
|
ent->num_leafs = 0;
|
|
for (i=0 ; i<num_leafs ; i++)
|
|
{
|
|
if (clusters[i] == -1)
|
|
continue; // not a visible leaf
|
|
for (j=0 ; j<i ; j++)
|
|
if (clusters[j] == clusters[i])
|
|
break;
|
|
if (j == i)
|
|
{
|
|
if (ent->num_leafs == MAX_ENT_LEAFS)
|
|
{ // assume we missed some leafs, and mark by headnode
|
|
ent->num_leafs = -1;
|
|
ent->headnode = topnode;
|
|
break;
|
|
}
|
|
|
|
ent->leafnums[ent->num_leafs++] = clusters[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
BOX TRACING
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
static void VBSP_FinalizeBrush(q2cbrush_t *brush)
|
|
{
|
|
vecV_t verts[256];
|
|
vec4_t planes[256];
|
|
int i, j;
|
|
ClearBounds(brush->absmins, brush->absmaxs);
|
|
for (i = 0; i < brush->numsides; i++)
|
|
{
|
|
VectorCopy(brush->brushside[i].plane->normal, planes[i]);
|
|
planes[i][3] = brush->brushside[i].plane->dist;
|
|
}
|
|
for (i = 0; i < brush->numsides; i++)
|
|
{
|
|
//most brushes are axial, which can save some a little loadtime
|
|
if (planes[i][0] == 1)
|
|
brush->absmaxs[0] = planes[i][3];
|
|
else if (planes[i][1] == 1)
|
|
brush->absmaxs[1] = planes[i][3];
|
|
else if (planes[i][2] == 1)
|
|
brush->absmaxs[2] = planes[i][3];
|
|
else if (planes[i][0] == -1)
|
|
brush->absmins[0] = -planes[i][3];
|
|
else if (planes[i][1] == -1)
|
|
brush->absmins[1] = -planes[i][3];
|
|
else if (planes[i][2] == -1)
|
|
brush->absmins[2] = -planes[i][3];
|
|
else
|
|
{
|
|
j = modfuncs->ClipPlaneToBrush(verts, countof(verts), planes, sizeof(planes[0]), brush->numsides, planes[i]);
|
|
while (j-- > 0)
|
|
AddPointToBounds(verts[j], brush->absmins, brush->absmaxs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
AREAPORTALS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
static void FloodArea_r (vbspinfo_t *prv, size_t areaidx, int floodnum)
|
|
{
|
|
size_t i;
|
|
|
|
careaflood_t *flood = &prv->areaflood[areaidx];
|
|
if (flood->floodvalid == prv->floodvalid)
|
|
{
|
|
if (flood->floodnum == floodnum)
|
|
return;
|
|
Con_Printf ("FloodArea_r: reflooded\n");
|
|
return;
|
|
}
|
|
|
|
flood->floodnum = floodnum;
|
|
flood->floodvalid = prv->floodvalid;
|
|
|
|
{
|
|
carea_t *area = &prv->areas[areaidx];
|
|
q2dareaportal_t *p = &prv->areaportals[area->firstareaportal];
|
|
for (i=0 ; i<area->numareaportals ; i++, p++)
|
|
{
|
|
if (prv->portalopen[p->portalnum])
|
|
FloodArea_r (prv, p->otherarea, floodnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
FloodAreaConnections
|
|
|
|
|
|
====================
|
|
*/
|
|
static void FloodAreaConnections (vbspinfo_t *prv)
|
|
{
|
|
size_t i;
|
|
int floodnum;
|
|
|
|
// all current floods are now invalid
|
|
prv->floodvalid++;
|
|
floodnum = 0;
|
|
|
|
// area 0 is not used
|
|
for (i=0 ; i<prv->numareas ; i++)
|
|
{
|
|
if (prv->areaflood[i].floodvalid == prv->floodvalid)
|
|
continue; // already flooded into
|
|
floodnum++;
|
|
FloodArea_r (prv, i, floodnum);
|
|
}
|
|
}
|
|
|
|
static void VBSP_SetAreaPortalState (model_t *mod, unsigned int portalnum, unsigned int area1, unsigned int area2, qboolean open)
|
|
{
|
|
vbspinfo_t *prv;
|
|
prv = (vbspinfo_t*)mod->meshinfo;
|
|
if (portalnum > prv->numareaportals)
|
|
return;
|
|
if (prv->portalopen[portalnum] == open)
|
|
return;
|
|
prv->portalopen[portalnum] = open;
|
|
FloodAreaConnections (prv);
|
|
}
|
|
|
|
static qboolean VBSP_AreasConnected (model_t *mod, unsigned int area1, unsigned int area2)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
|
|
if (map_noareas->value)
|
|
return true;
|
|
|
|
if (area1 == ~0 || area2 == ~0)
|
|
return area1 == area2;
|
|
if (area1 > prv->numareas || area2 > prv->numareas)
|
|
Host_Error ("area > numareas");
|
|
|
|
if (prv->areaflood[area1].floodnum == prv->areaflood[area2].floodnum)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CM_WriteAreaBits
|
|
|
|
Writes a length qbyte followed by a bit vector of all the areas
|
|
that area in the same flood as the area parameter
|
|
|
|
This is used by the client refreshes to cull visibility
|
|
=================
|
|
*/
|
|
static int VBSP_WriteAreaBits (model_t *mod, qbyte *buffer, int area, qboolean merge)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int i;
|
|
int floodnum;
|
|
int bytes;
|
|
int nullarea = 0;
|
|
|
|
bytes = (prv->numareas+7)>>3;
|
|
|
|
if (map_noareas->value || (area == nullarea && !merge))
|
|
{ // for debugging, send everything
|
|
if (!merge)
|
|
memset (buffer, 255, bytes);
|
|
}
|
|
else
|
|
{
|
|
if (!merge)
|
|
memset (buffer, 0, bytes);
|
|
|
|
floodnum = prv->areaflood[area].floodnum;
|
|
for (i=0 ; i<prv->numareas ; i++)
|
|
{
|
|
if (prv->areaflood[i].floodnum == floodnum)
|
|
buffer[i>>3] |= 1<<(i&7);
|
|
}
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CM_WritePortalState
|
|
|
|
Returns a size+pointer to the data that needs to be written into a saved game.
|
|
===================
|
|
*/
|
|
static size_t VBSP_SaveAreaPortalBlob (model_t *mod, void **data)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
|
|
*data = prv->portalopen;
|
|
return sizeof(prv->portalopen);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CM_ReadPortalState
|
|
|
|
Reads the portal state from a savegame file
|
|
and recalculates the area connections
|
|
===================
|
|
*/
|
|
static size_t VBSP_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
|
|
memcpy(prv->portalopen, ptr, min(ptrsize,sizeof(prv->portalopen)));
|
|
|
|
FloodAreaConnections (prv);
|
|
return sizeof(prv->portalopen);
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
PVS / PHS
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
===================
|
|
CM_DecompressVis
|
|
===================
|
|
*/
|
|
static void VBSP_DecompressVis (model_t *mod, qbyte *in, qbyte *out, qboolean merge)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
int c;
|
|
qbyte *out_p;
|
|
int row;
|
|
|
|
row = (mod->numclusters+7)>>3;
|
|
out_p = out;
|
|
|
|
if (!in || !prv->numvisibility)
|
|
{ // no vis info, so make all visible
|
|
while (row)
|
|
{
|
|
*out_p++ = 0xff;
|
|
row--;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (merge)
|
|
{
|
|
do
|
|
{
|
|
if (*in)
|
|
{
|
|
*out_p++ |= *in++;
|
|
continue;
|
|
}
|
|
|
|
out_p += in[1];
|
|
in += 2;
|
|
} while (out_p - out < row);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (*in)
|
|
{
|
|
*out_p++ = *in++;
|
|
continue;
|
|
}
|
|
|
|
c = in[1];
|
|
in += 2;
|
|
if ((out_p - out) + c > row)
|
|
{
|
|
c = row - (out_p - out);
|
|
Con_DPrintf ("warning: Vis decompression overrun\n");
|
|
}
|
|
while (c)
|
|
{
|
|
*out_p++ = 0;
|
|
c--;
|
|
}
|
|
} while (out_p - out < row);
|
|
}
|
|
}
|
|
|
|
static pvsbuffer_t pvsrow;
|
|
static pvsbuffer_t phsrow;
|
|
|
|
|
|
|
|
static qbyte *VBSP_ClusterPVS (model_t *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
if (!buffer)
|
|
buffer = &pvsrow;
|
|
if (buffer->buffersize < mod->pvsbytes)
|
|
buffer->buffer = plugfuncs->Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);
|
|
|
|
if (cluster == -1)
|
|
memset (buffer->buffer, 0, (mod->numclusters+7)>>3);
|
|
else
|
|
VBSP_DecompressVis (mod, ((qbyte*)prv->vis) + prv->vis->bitofs[cluster][DVIS_PVS], buffer->buffer, merge==PVM_MERGE);
|
|
return buffer->buffer;
|
|
}
|
|
|
|
static qbyte *VBSP_ClusterPHS (model_t *mod, int cluster, pvsbuffer_t *buffer)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
|
|
if (!buffer)
|
|
buffer = &phsrow;
|
|
if (buffer->buffersize < mod->pvsbytes)
|
|
buffer->buffer = plugfuncs->Realloc(buffer->buffer, buffer->buffersize=mod->pvsbytes);
|
|
|
|
if (cluster == -1)
|
|
memset (buffer->buffer, 0, (mod->numclusters+7)>>3);
|
|
else
|
|
VBSP_DecompressVis (mod, ((qbyte*)prv->vis) + prv->vis->bitofs[cluster][DVIS_PHS], buffer->buffer, false);
|
|
return buffer->buffer;
|
|
}
|
|
|
|
static unsigned int VBSP_FatPVS (model_t *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge)
|
|
{
|
|
int leafs[64];
|
|
int i, j, count;
|
|
vec3_t mins, maxs;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
mins[i] = org[i] - 8;
|
|
maxs[i] = org[i] + 8;
|
|
}
|
|
|
|
count = VBSP_BoxLeafnums (mod, mins, maxs, leafs, countof(leafs), NULL);
|
|
if (count < 1)
|
|
Sys_Errorf ("SV_Q2FatPVS: count < 1");
|
|
|
|
// convert leafs to clusters
|
|
for (i=0 ; i<count ; i++)
|
|
leafs[i] = VBSP_LeafCluster(mod, leafs[i]);
|
|
|
|
//grow the buffer if needed
|
|
if (result->buffersize < mod->pvsbytes)
|
|
result->buffer = plugfuncs->Realloc(result->buffer, result->buffersize=mod->pvsbytes);
|
|
|
|
if (count == 1 && leafs[0] == -1)
|
|
{ //if the only leaf is the outside then broadcast it.
|
|
memset(result->buffer, 0xff, mod->pvsbytes);
|
|
i = count;
|
|
}
|
|
else
|
|
{
|
|
i = 0;
|
|
if (!merge)
|
|
mod->funcs.ClusterPVS(mod, leafs[i++], result, PVM_REPLACE);
|
|
// or in all the other leaf bits
|
|
for ( ; i<count ; i++)
|
|
{
|
|
for (j=0 ; j<i ; j++)
|
|
if (leafs[i] == leafs[j])
|
|
break;
|
|
if (j != i)
|
|
continue; // already have the cluster we want
|
|
mod->funcs.ClusterPVS(mod, leafs[i], result, PVM_MERGE);
|
|
}
|
|
}
|
|
return mod->pvsbytes;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CM_HeadnodeVisible
|
|
|
|
Returns true if any leaf under headnode has a cluster that
|
|
is potentially visible
|
|
=============
|
|
*/
|
|
static qboolean VBSP_HeadnodeVisible (model_t *mod, int nodenum, const qbyte *visbits)
|
|
{
|
|
int leafnum;
|
|
int cluster;
|
|
mnode_t *node;
|
|
|
|
if (nodenum < 0)
|
|
{
|
|
leafnum = -1-nodenum;
|
|
cluster = mod->leafs[leafnum].cluster;
|
|
if (cluster == -1)
|
|
return false;
|
|
if (visbits[cluster>>3] & (1<<(cluster&7)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
node = &mod->nodes[nodenum];
|
|
if (VBSP_HeadnodeVisible(mod, node->childnum[0], visbits))
|
|
return true;
|
|
return VBSP_HeadnodeVisible(mod, node->childnum[1], visbits);
|
|
}
|
|
|
|
static qboolean VBSP_EdictInFatPVS(model_t *mod, const pvscache_t *ent, const qbyte *pvs, const int *areas)
|
|
{
|
|
int i,l;
|
|
int nullarea = 0;
|
|
if (areas)
|
|
{
|
|
for (i = 1; ; i++)
|
|
{
|
|
if (i > areas[0])
|
|
return false; //none of the camera's areas could see the entity
|
|
if (areas[i] == ent->areanum)
|
|
{
|
|
if (areas[i] != nullarea)
|
|
break;
|
|
//else entity is fully outside the world, invisible to all...
|
|
}
|
|
else if (VBSP_AreasConnected (mod, areas[i], ent->areanum))
|
|
break;
|
|
// doors can legally straddle two areas, so
|
|
// we may need to check another one
|
|
else if (ent->areanum2 != nullarea && VBSP_AreasConnected (mod, areas[i], ent->areanum2))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ent->num_leafs == -1)
|
|
{ // too many leafs for individual check, go by headnode
|
|
if (!VBSP_HeadnodeVisible (mod, ent->headnode, pvs))
|
|
return false;
|
|
}
|
|
else
|
|
{ // check individual leafs
|
|
for (i=0 ; i < ent->num_leafs ; i++)
|
|
{
|
|
l = ent->leafnums[i];
|
|
if (pvs[l >> 3] & (1 << (l&7) ))
|
|
break;
|
|
}
|
|
if (i == ent->num_leafs)
|
|
return false; // not visible
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
Collision Stuff.
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
static void VBSP_BuildBIHSubmodel(model_t *mod, int submodel)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
cmodel_t *sub = &prv->cmodels[submodel];
|
|
|
|
struct bihleaf_s *bihleaf, *l;
|
|
size_t i;
|
|
|
|
bihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*sub->num_brushes);
|
|
for (i = 0; i < sub->num_brushes; i++)
|
|
{
|
|
q2cbrush_t *b = &prv->brushes[sub->firstbrush+i];
|
|
l->type = BIH_BRUSH;
|
|
l->data.brush = b;
|
|
|
|
l->data.contents = b->contents;
|
|
VectorCopy(b->absmins, l->mins);
|
|
VectorCopy(b->absmaxs, l->maxs);
|
|
l++;
|
|
}
|
|
|
|
modfuncs->BIH_Build(mod, bihleaf, l-bihleaf);
|
|
plugfuncs->Free(bihleaf);
|
|
}
|
|
static void VBSP_BuildBIHMain(void *ctx, void *unusedp, size_t unuseda, size_t unusedb)
|
|
{ //NOTE: must be on main thread because we're waiting for submodels for their size.
|
|
model_t *mod = ctx;
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
cmodel_t *sub = &prv->cmodels[0];
|
|
|
|
struct bihleaf_s *bihleaf, *l;
|
|
size_t bihleafs, i;
|
|
|
|
bihleafs = sub->num_brushes;
|
|
for (i = 0; i < prv->numdisplacements; i++)
|
|
bihleafs += prv->displacements[i].numindexes/3;
|
|
for (i = 0; i < prv->numstaticprops; i++)
|
|
{
|
|
if (prv->staticprops[i].solid)
|
|
{
|
|
if (prv->staticprops[i].ent.model && prv->staticprops[i].ent.model->loadstate == MLS_NOTLOADED)
|
|
modfuncs->GetModel(prv->staticprops[i].ent.model->publicname, MLV_WARN); //we use threads, so these'll load in time.
|
|
bihleafs++;
|
|
}
|
|
}
|
|
|
|
bihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*bihleafs);
|
|
|
|
//now we have enough storage, spit them out providing bounds info.
|
|
for (i = 0; i < sub->num_brushes; i++)
|
|
{
|
|
q2cbrush_t *b = &prv->brushes[sub->firstbrush+i];
|
|
l->type = BIH_BRUSH;
|
|
l->data.brush = b;
|
|
|
|
l->data.contents = b->contents;
|
|
VectorCopy(b->absmins, l->mins);
|
|
VectorCopy(b->absmaxs, l->maxs);
|
|
l++;
|
|
}
|
|
for (i = 0; i < prv->numdisplacements; i++)
|
|
{
|
|
dispinfo_t *d = &prv->displacements[i];
|
|
|
|
size_t j;
|
|
for (j = 0; j < d->numindexes; j+=3)
|
|
{
|
|
index_t *v = d->idx+j;
|
|
vec_t *v1 = d->xyz[v[0]], *v2 = d->xyz[v[1]], *v3 = d->xyz[v[2]];
|
|
|
|
l->type = BIH_TRIANGLE;
|
|
l->data.tri.xyz = d->xyz;
|
|
l->data.tri.indexes = v;
|
|
|
|
l->data.contents = d->contents;
|
|
VectorCopy(v1, l->mins);
|
|
VectorCopy(v1, l->maxs);
|
|
AddPointToBounds(v2, l->mins, l->maxs);
|
|
AddPointToBounds(v3, l->mins, l->maxs);
|
|
l++;
|
|
}
|
|
}
|
|
for (i = 0; i < prv->numstaticprops; i++)
|
|
{
|
|
if (!prv->staticprops[i].solid)
|
|
continue;
|
|
|
|
l->type = BIH_MODEL;
|
|
l->data.mesh.model = prv->staticprops[i].ent.model;
|
|
l->data.mesh.tr = &prv->staticprops[i].transform;
|
|
|
|
//*cry*
|
|
while (l->data.mesh.model->loadstate==MLS_LOADING)
|
|
threadfuncs->WaitForCompletion(l->data.mesh.model, &l->data.mesh.model->loadstate, MLS_LOADING);
|
|
|
|
if (!l->data.mesh.model || !l->data.mesh.model->funcs.NativeTrace || !l->data.mesh.model->funcs.NativeContents)
|
|
{
|
|
Con_Printf("%s has no collision info and must not be used as a solid static prop\n", l->data.mesh.model->name);
|
|
continue;
|
|
}
|
|
|
|
l->data.contents = ~0u; //yuck!
|
|
|
|
//a clever person could probably do a better job.
|
|
l->mins[0] = prv->staticprops[i].ent.origin[0] - l->data.mesh.model->radius;
|
|
l->mins[1] = prv->staticprops[i].ent.origin[1] - l->data.mesh.model->radius;
|
|
l->mins[2] = prv->staticprops[i].ent.origin[2] - l->data.mesh.model->radius;
|
|
l->maxs[0] = prv->staticprops[i].ent.origin[0] + l->data.mesh.model->radius;
|
|
l->maxs[1] = prv->staticprops[i].ent.origin[1] + l->data.mesh.model->radius;
|
|
l->maxs[2] = prv->staticprops[i].ent.origin[2] + l->data.mesh.model->radius;
|
|
l++;
|
|
}
|
|
|
|
modfuncs->BIH_Build(mod, bihleaf, l-bihleaf);
|
|
plugfuncs->Free(bihleaf);
|
|
|
|
mod->funcs.PointContents = VBSP_PointContents;
|
|
|
|
//and make sure we finished checksumming, too.
|
|
threadfuncs->WaitForCompletion(mod, &prv->summed, false);
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
Rendering stuff
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
#ifdef HAVE_CLIENT
|
|
static qboolean VBSP_CullBox (vec3_t mins, vec3_t maxs)
|
|
{
|
|
//this isn't very precise.
|
|
//checking each plane individually can be problematic
|
|
//if you have a large object behind the view, it can cross multiple planes, and be infront of each one at some point, yet should still be outside the view.
|
|
//this is quite noticable with terrain where the potential height of a section is essentually infinite.
|
|
//note that this is not a concern for spheres, just boxes.
|
|
int i;
|
|
|
|
for (i = 0; i < refdef->frustum_numplanes; i++)
|
|
if (BOX_ON_PLANE_SIDE (mins, maxs, &refdef->frustum[i]) == 2)
|
|
return true;
|
|
return false;
|
|
}
|
|
static void VBSP_RecursiveWorldNode (model_t *model, mnode_t *node)
|
|
{
|
|
int c, side;
|
|
mplane_t *plane;
|
|
msurface_t *surf, **mark;
|
|
mleaf_t *pleaf;
|
|
double dot;
|
|
|
|
int sidebit;
|
|
|
|
if (node->contents == FTECONTENTS_SOLID)
|
|
return; // solid
|
|
|
|
if (node->visframe != vbsp_nodesequence)
|
|
return;
|
|
if (VBSP_CullBox (node->minmaxs, node->minmaxs+3))
|
|
return;
|
|
|
|
// if a leaf node, draw stuff
|
|
if (node->contents != -1)
|
|
{
|
|
pleaf = (mleaf_t *)node;
|
|
|
|
// check for door connected areas
|
|
if (! (refdef->areabits[pleaf->area>>3] & (1<<(pleaf->area&7)) ) )
|
|
return; // not visible
|
|
|
|
c = pleaf->cluster;
|
|
if (c >= 0)
|
|
frustumvis[c>>3] |= 1<<(c&7);
|
|
|
|
mark = pleaf->firstmarksurface;
|
|
c = pleaf->nummarksurfaces;
|
|
|
|
if (c)
|
|
{
|
|
do
|
|
{
|
|
surf = *mark++;
|
|
if (surf->flags & SURF_OFFNODE)
|
|
{
|
|
if (surf->visframe != vbsp_surfsequence)
|
|
{ //only add once, it might be in multiple leafs.
|
|
surf->visframe = vbsp_surfsequence;
|
|
modfuncs->RenderDynamicLightmaps (surf);
|
|
surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;
|
|
}
|
|
}
|
|
else
|
|
surf->visframe = vbsp_surfsequence;
|
|
} while (--c);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// node is just a decision point, so go down the apropriate sides
|
|
|
|
// find which side of the node we are on
|
|
plane = node->plane;
|
|
|
|
switch (plane->type)
|
|
{
|
|
case PLANE_X:
|
|
dot = modelorg[0] - plane->dist;
|
|
break;
|
|
case PLANE_Y:
|
|
dot = modelorg[1] - plane->dist;
|
|
break;
|
|
case PLANE_Z:
|
|
dot = modelorg[2] - plane->dist;
|
|
break;
|
|
default:
|
|
dot = DotProduct (modelorg, plane->normal) - plane->dist;
|
|
break;
|
|
}
|
|
|
|
if (dot >= 0)
|
|
{
|
|
side = 0;
|
|
sidebit = 0;
|
|
}
|
|
else
|
|
{
|
|
side = 1;
|
|
sidebit = SURF_PLANEBACK;
|
|
}
|
|
|
|
// recurse down the children, front side first
|
|
VBSP_RecursiveWorldNode (model, node->children[side]);
|
|
|
|
// draw stuff
|
|
for ( c = node->numsurfaces, surf = model->surfaces + node->firstsurface; c ; c--, surf++)
|
|
{
|
|
if (surf->visframe != vbsp_surfsequence)
|
|
continue;
|
|
|
|
if ( (surf->flags & SURF_PLANEBACK) != sidebit )
|
|
continue; // wrong side
|
|
|
|
surf->visframe = 0;//vbsp_surfsequence;//-1;
|
|
|
|
modfuncs->RenderDynamicLightmaps (surf);
|
|
|
|
surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;
|
|
}
|
|
|
|
|
|
// recurse down the back side
|
|
VBSP_RecursiveWorldNode (model, node->children[!side]);
|
|
}
|
|
static qbyte *VBSP_MarkLeaves (model_t *model, int clusters[2])
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)model->meshinfo;
|
|
mnode_t *node;
|
|
int i;
|
|
|
|
int cluster;
|
|
mleaf_t *leaf;
|
|
qbyte *vis;
|
|
|
|
int portal = refdef->recurse;
|
|
|
|
if (refdef->forcevis)
|
|
{
|
|
vis = refdef->forcedvis;
|
|
prv->vcache.vis = NULL;
|
|
}
|
|
else if (portal || hl2_novis->ival || clusters[0] == -1 || !model->vis)
|
|
return NULL; //use some blind whole-model thing
|
|
else
|
|
{
|
|
vis = prv->vcache.vis;
|
|
if (prv->vcache.viewcluster[0] == clusters[0] && prv->vcache.viewcluster[1] == clusters[1] && vis)
|
|
return vis;
|
|
|
|
if (clusters[1] != clusters[0]) // may have to combine two clusters because of solid water boundaries
|
|
{
|
|
vis = VBSP_ClusterPVS (model, clusters[0], &prv->vcache.visbuf, PVM_REPLACE);
|
|
vis = VBSP_ClusterPVS (model, clusters[1], &prv->vcache.visbuf, PVM_MERGE);
|
|
}
|
|
else
|
|
vis = VBSP_ClusterPVS (model, clusters[0], &prv->vcache.visbuf, PVM_FAST);
|
|
prv->vcache.vis = vis;
|
|
prv->vcache.viewcluster[0] = clusters[0];
|
|
prv->vcache.viewcluster[1] = clusters[1];
|
|
}
|
|
|
|
vbsp_nodesequence++;
|
|
|
|
for (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)
|
|
{
|
|
cluster = leaf->cluster;
|
|
if (cluster == -1)
|
|
continue;
|
|
if (vis[cluster>>3] & (1<<(cluster&7)))
|
|
{
|
|
node = (mnode_t *)leaf;
|
|
do
|
|
{
|
|
if (node->visframe == vbsp_nodesequence)
|
|
break;
|
|
node->visframe = vbsp_nodesequence;
|
|
node = node->parent;
|
|
} while (node);
|
|
}
|
|
}
|
|
return vis;
|
|
}
|
|
static void VBSP_PrepareFrame(model_t *mod, refdef_t *r_refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
qbyte *surfvis, *entvis;
|
|
|
|
refdef = r_refdef;
|
|
|
|
if (vis->buffersize < mod->pvsbytes)
|
|
vis->buffer = plugfuncs->Realloc(vis->buffer, vis->buffersize=mod->pvsbytes);
|
|
frustumvis = vis->buffer;
|
|
memset(frustumvis, 0, mod->pvsbytes);
|
|
|
|
if (!r_refdef->areabitsknown)
|
|
{ //generate the info each frame, as the gamecode didn't tell us what to use.
|
|
int leafnum = VBSP_PointLeafnum (mod, r_refdef->vieworg);
|
|
int clientarea = VBSP_LeafArea (mod, leafnum);
|
|
VBSP_WriteAreaBits(mod, r_refdef->areabits, clientarea, false);
|
|
r_refdef->areabitsknown = true;
|
|
}
|
|
|
|
|
|
entvis = surfvis = VBSP_MarkLeaves(mod, clusters);
|
|
VectorCopy (r_refdef->vieworg, modelorg);
|
|
if (!surfvis)
|
|
{
|
|
size_t i;
|
|
msurface_t *surf;
|
|
for (i = 0; i < mod->nummodelsurfaces; i++)
|
|
{
|
|
surf = &mod->surfaces[i];
|
|
modfuncs->RenderDynamicLightmaps (surf);
|
|
surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t i;
|
|
dispinfo_t *disp;
|
|
msurface_t *surf;
|
|
int areas[2];
|
|
areas[0] = 1;
|
|
areas[1] = area;
|
|
vbsp_surfsequence++;
|
|
VBSP_RecursiveWorldNode (mod, mod->nodes);
|
|
for (i = 0; i < prv->numdisplacements; i++)
|
|
{
|
|
disp = &prv->displacements[i];
|
|
if (VBSP_EdictInFatPVS(mod, &disp->pvs, surfvis, areas))
|
|
{
|
|
surf = disp->surf;
|
|
modfuncs->RenderDynamicLightmaps (surf);
|
|
surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh;
|
|
}
|
|
}
|
|
}
|
|
|
|
*surfvis_out = frustumvis;
|
|
*entvis_out = entvis;
|
|
|
|
|
|
|
|
if (prv->numstaticprops)
|
|
{
|
|
struct staticprop_s *sent;
|
|
entity_t *src, *ent;
|
|
float d;
|
|
size_t i;
|
|
vec3_t disp;
|
|
for (i = 0; i < prv->numstaticprops; i++)
|
|
{
|
|
sent = &prv->staticprops[i];
|
|
src = &sent->ent;
|
|
|
|
if (sent->fademaxdist)
|
|
{
|
|
VectorSubtract(refdef->vieworg, src->origin, disp);
|
|
d = VectorLength(disp);
|
|
if (d > sent->fademaxdist)
|
|
continue; //skip it.
|
|
d -= sent->fademindist;
|
|
d /= sent->fademaxdist-sent->fademindist;
|
|
if (d < 0)
|
|
d = 0;
|
|
}
|
|
else
|
|
d = 0;
|
|
|
|
if (!src->model || src->model->loadstate != MLS_LOADED)
|
|
{
|
|
if (src->model && src->model->loadstate == MLS_NOTLOADED)
|
|
modfuncs->GetModel(src->model->publicname, MLV_WARN); //we use threads, so these'll load in time.
|
|
continue;
|
|
}
|
|
/*if (!src->light_known)
|
|
{
|
|
vec3_t tmp;
|
|
VectorCopy(src->origin, tmp);
|
|
VectorCopy(sent->lightorg, src->origin);
|
|
R_CalcModelLighting(src, src->model); //bake and cache, now everything else is working.
|
|
VectorCopy(tmp, src->origin);
|
|
}*/
|
|
if (src->pvscache.num_leafs==-2)
|
|
{
|
|
vec3_t absmin, absmax;
|
|
float r = src->model->radius;
|
|
VectorSet(absmin, -r,-r,-r);
|
|
VectorSet(absmax, r,r,r);
|
|
VectorAdd(absmin, src->origin, absmin);
|
|
VectorAdd(absmax, src->origin, absmax);
|
|
VBSP_FindTouchedLeafs(mod, &src->pvscache, absmin, absmax);
|
|
}
|
|
|
|
ent = modfuncs->NewSceneEntity();
|
|
if (!ent)
|
|
return;
|
|
*ent = *src;
|
|
ent->framestate.g[FS_REG].frametime[0] = refdef->time;
|
|
ent->framestate.g[FS_REG].frametime[1] = refdef->time;
|
|
if (d)
|
|
{
|
|
ent->shaderRGBAf[3] *= 1-d;
|
|
ent->flags |= RF_TRANSLUCENT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static void VBSP_InfoForPoint (struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits)
|
|
{
|
|
int leaf = VBSP_PointLeafnum_r (mod, pos, 0);
|
|
*area = VBSP_LeafArea(mod, leaf);
|
|
*cluster = VBSP_LeafCluster(mod, leaf);
|
|
*contentbits = VBSP_LeafContents(mod, leaf);
|
|
}
|
|
|
|
|
|
#ifdef RTLIGHTS
|
|
static int vbsp_shadowsequence;
|
|
static qbyte *shadowedpvs;
|
|
static model_t *shadowmodel;
|
|
static void VBSP_WalkShadows (dlight_t *dl, void (*callback)(msurface_t *surf), mnode_t *node)
|
|
{
|
|
int c, side;
|
|
mplane_t *plane;
|
|
msurface_t *surf, **mark;
|
|
mleaf_t *pleaf;
|
|
double dot;
|
|
|
|
float l, maxdist;
|
|
int j, s, t;
|
|
vec3_t impact;
|
|
struct vbsptexinfo_s *vtexinfo;
|
|
vbspinfo_t *prv;
|
|
|
|
if (node->shadowframe != vbsp_shadowsequence)
|
|
return;
|
|
|
|
//if light areabox is outside node, ignore node + children
|
|
for (c = 0; c < 3; c++)
|
|
{
|
|
if (dl->origin[c] + dl->radius < node->minmaxs[c])
|
|
return;
|
|
if (dl->origin[c] - dl->radius > node->minmaxs[3+c])
|
|
return;
|
|
}
|
|
|
|
// if a leaf node, draw stuff
|
|
if (node->contents != -1)
|
|
{
|
|
pleaf = (mleaf_t *)node;
|
|
|
|
if (pleaf->cluster >= 0)
|
|
shadowedpvs[pleaf->cluster>>3] |= 1<<(pleaf->cluster&7);
|
|
|
|
mark = pleaf->firstmarksurface;
|
|
c = pleaf->nummarksurfaces;
|
|
|
|
if (c)
|
|
{
|
|
do
|
|
{
|
|
surf = *mark++;
|
|
if (surf->flags & SURF_OFFNODE)
|
|
{
|
|
if (surf->shadowframe != vbsp_shadowsequence)
|
|
{ //if its not on a node then its probably not a nice flat surface, so don't bother trying to cull it in fancy ways that depend on its plane.
|
|
surf->shadowframe = vbsp_shadowsequence;
|
|
callback(surf);
|
|
}
|
|
}
|
|
else
|
|
surf->shadowframe = vbsp_shadowsequence;
|
|
} while (--c);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// node is just a decision point, so go down the apropriate sides
|
|
|
|
// find which side of the node we are on
|
|
plane = node->plane;
|
|
|
|
switch (plane->type)
|
|
{
|
|
case PLANE_X:
|
|
dot = dl->origin[0] - plane->dist;
|
|
break;
|
|
case PLANE_Y:
|
|
dot = dl->origin[1] - plane->dist;
|
|
break;
|
|
case PLANE_Z:
|
|
dot = dl->origin[2] - plane->dist;
|
|
break;
|
|
default:
|
|
dot = DotProduct (dl->origin, plane->normal) - plane->dist;
|
|
break;
|
|
}
|
|
|
|
if (dot >= 0)
|
|
side = 0;
|
|
else
|
|
side = 1;
|
|
|
|
// recurse down the children, front side first
|
|
VBSP_WalkShadows (dl, callback, node->children[side]);
|
|
|
|
// draw stuff
|
|
c = node->numsurfaces;
|
|
if (c)
|
|
{
|
|
prv = shadowmodel->meshinfo;
|
|
surf = shadowmodel->surfaces + node->firstsurface;
|
|
maxdist = dl->radius*dl->radius;
|
|
for ( ; c ; c--, surf++)
|
|
{
|
|
if (surf->shadowframe != vbsp_shadowsequence)
|
|
continue;
|
|
|
|
if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))
|
|
continue; // wrong side
|
|
|
|
/* if (surf->flags & (SURF_DRAWALPHA | SURF_DRAWTILED))
|
|
{ // no shadows
|
|
continue;
|
|
}*/
|
|
|
|
//is the light on the right side?
|
|
if (surf->flags & SURF_PLANEBACK)
|
|
{//inverted normal.
|
|
if (-DotProduct(surf->plane->normal, dl->origin)+surf->plane->dist >= dl->radius)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (DotProduct(surf->plane->normal, dl->origin)-surf->plane->dist >= dl->radius)
|
|
continue;
|
|
}
|
|
|
|
//Yeah, you can blame LordHavoc for this alternate code here.
|
|
for (j=0 ; j<3 ; j++)
|
|
impact[j] = dl->origin[j] - surf->plane->normal[j]*dot;
|
|
|
|
vtexinfo = &prv->texinfo[surf->texinfo-shadowmodel->texinfo];
|
|
// clamp center of light to corner and check brightness
|
|
l = DotProduct (impact, vtexinfo->lmvecs[0]) + vtexinfo->lmvecs[0][3] - surf->texturemins[0];
|
|
s = l;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
|
|
s = (l - s)*surf->texinfo->vecscale[0];
|
|
l = DotProduct (impact, vtexinfo->lmvecs[1]) + vtexinfo->lmvecs[1][3] - surf->texturemins[1];
|
|
t = l;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
|
|
t = (l - t)*surf->texinfo->vecscale[1];
|
|
// compare to minimum light
|
|
if ((s*s+t*t+dot*dot) < maxdist)
|
|
callback(surf);
|
|
}
|
|
}
|
|
|
|
// recurse down the back side
|
|
VBSP_WalkShadows (dl, callback, node->children[!side]);
|
|
}
|
|
|
|
static void VBSP_MarkShadows(model_t *model, dlight_t *dl, const qbyte *lvis)
|
|
{
|
|
mnode_t *node;
|
|
int i;
|
|
mleaf_t *leaf;
|
|
int cluster;
|
|
|
|
// if (!dl->die)
|
|
{
|
|
//static
|
|
//variation on mark leaves
|
|
for (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)
|
|
{
|
|
cluster = leaf->cluster;
|
|
if (cluster == -1)
|
|
continue;
|
|
if (lvis[cluster>>3] & (1<<(cluster&7)))
|
|
{
|
|
node = (mnode_t *)leaf;
|
|
do
|
|
{
|
|
if (node->shadowframe == vbsp_shadowsequence)
|
|
break;
|
|
node->shadowframe = vbsp_shadowsequence;
|
|
node = node->parent;
|
|
} while (node);
|
|
}
|
|
}
|
|
}
|
|
/* else
|
|
{
|
|
//dynamic lights will be discarded after this frame anyway, so only include leafs that are visible
|
|
//variation on mark leaves
|
|
for (i=0,leaf=model->leafs ; i<model->numleafs ; i++, leaf++)
|
|
{
|
|
cluster = leaf->cluster;
|
|
if (cluster == -1)
|
|
continue;
|
|
if (lvis[cluster>>3] & (1<<(cluster&7)))
|
|
{
|
|
node = (mnode_t *)leaf;
|
|
do
|
|
{
|
|
if (node->shadowframe == vbsp_shadowsequence)
|
|
break;
|
|
node->shadowframe = vbsp_shadowsequence;
|
|
node = node->parent;
|
|
} while (node);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
static void VBSP_GenerateShadowMesh(model_t *model, dlight_t *dl, const qbyte *lightvis, qbyte *litvis, void (*callback)(msurface_t *surf))
|
|
{
|
|
vbspinfo_t *prv = model->meshinfo;
|
|
dispinfo_t *disp;
|
|
int i;
|
|
//globals are evil
|
|
shadowmodel = model;
|
|
shadowedpvs = litvis; //this is an output
|
|
vbsp_shadowsequence++;
|
|
|
|
VBSP_MarkShadows(model, dl, lightvis);
|
|
|
|
VBSP_WalkShadows(dl, callback, model->nodes);
|
|
|
|
for (i = 0; i < prv->numdisplacements; i++)
|
|
{
|
|
disp = &prv->displacements[i];
|
|
if (VBSP_EdictInFatPVS(model, &disp->pvs, litvis, NULL))
|
|
{
|
|
callback(disp->surf);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void VBSP_LoadLeafLight (model_t *mod, qbyte *mod_base, vlump_t *hdridx, vlump_t *ldridx, vlump_t *hdrvals, vlump_t *ldrvals, int version)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)mod->meshinfo;
|
|
vlump_t *lump_idx, *lump_vals;
|
|
struct leaflightpoint_s *point;
|
|
size_t i, j;
|
|
unsigned short *in;
|
|
qbyte *inpoint;
|
|
|
|
if (version < 20)
|
|
return; //nope. this info is in the leafs.
|
|
|
|
if (hdridx && hdrvals)
|
|
lump_idx = hdridx, lump_vals = hdrvals;
|
|
else if (ldridx && ldrvals)
|
|
lump_idx = ldridx, lump_vals = ldrvals;
|
|
else
|
|
return; //unsupported.
|
|
|
|
if (lump_vals->filelen%(7*4))
|
|
return;
|
|
|
|
if (lump_idx->filelen != mod->numleafs*sizeof(short)*2)
|
|
return; //erk?
|
|
|
|
//easy enough to load some of the data...
|
|
point = plugfuncs->GMalloc(&mod->memgroup, sizeof(*point)*(lump_vals->filelen/(7*4)));
|
|
inpoint = mod_base + lump_vals->fileofs;
|
|
for (i = 0; i < lump_vals->filelen/(7*4); i++)
|
|
{
|
|
for (j = 0; j < 6; j++, inpoint+=4)
|
|
{
|
|
float e = pow(2, (signed char)inpoint[3]);
|
|
point[i].rgb[j][0] = e * inpoint[0];
|
|
point[i].rgb[j][1] = e * inpoint[1];
|
|
point[i].rgb[j][2] = e * inpoint[2];
|
|
}
|
|
point[i].x = *inpoint++;
|
|
point[i].y = *inpoint++;
|
|
point[i].z = *inpoint++;
|
|
inpoint++;
|
|
}
|
|
|
|
prv->leaflight = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv->leaflight)*mod->numleafs);
|
|
in = (unsigned short*)(mod_base + lump_idx->fileofs);
|
|
for (i = 0; i < mod->numleafs; i++)
|
|
{
|
|
prv->leaflight[i].count = LittleShort(*in++);
|
|
prv->leaflight[i].point = point + (unsigned short)LittleShort(*in++);
|
|
}
|
|
}
|
|
static void VBSP_LightPointValues (struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
|
|
{
|
|
vbspinfo_t *prv = (vbspinfo_t*)model->meshinfo;
|
|
int leafnum = VBSP_PointLeafnum(model,point);
|
|
mleaf_t *leaf = model->leafs+leafnum;
|
|
struct mleaflight_s *leaflight = prv->leaflight+leafnum;
|
|
struct leaflightpoint_s *best, *lp;
|
|
size_t i, j, d, bd=~0;
|
|
int xyz[3];
|
|
vec3_t diff[6];
|
|
float sig[6];
|
|
|
|
static cvar_t *srgbmag, *scale, *forceface;
|
|
if (!srgbmag) srgbmag = cvarfuncs->GetNVFDG("hl2_lt_srgb_mag","1", 0, "TEST", "TEST");
|
|
if (!scale) scale = cvarfuncs->GetNVFDG("hl2_lt_scale", "256", 0, "TEST", "TEST");
|
|
if (!forceface) forceface = cvarfuncs->GetNVFDG("hl2_lt_face", "-1", 0, "TEST", "TEST");
|
|
|
|
if (prv->leaflight && leaflight->count)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
xyz[i] = 255*(point[i] - leaf->minmaxs[i]) / (leaf->minmaxs[3+i]-leaf->minmaxs[i]);
|
|
for (i = 0, best=lp = leaflight->point; i < leaflight->count; i++, lp++)
|
|
{
|
|
int m[3];
|
|
m[0] = xyz[0] - lp->x;
|
|
m[1] = xyz[1] - lp->y;
|
|
m[2] = xyz[2] - lp->z;
|
|
d = DotProduct(m,m);
|
|
if (bd > d)
|
|
{
|
|
bd = d;
|
|
best = lp;
|
|
}
|
|
}
|
|
|
|
|
|
VectorClear(res_ambient);
|
|
for (j = 0; j < 6; j++)
|
|
VectorAdd(res_ambient, best->rgb[j], res_ambient);
|
|
VectorScale(res_ambient, 1.0/6, res_ambient);
|
|
|
|
//try and figure out an average dir for the brightest direction
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
VectorSubtract(best->rgb[j], res_ambient, diff[j]);
|
|
sig[j] = VectorLength(diff[j]);
|
|
}
|
|
for (j = 0; j < 3; j++)
|
|
res_dir[j] = sig[j*2+1] - sig[j*2];
|
|
VectorNormalize(res_dir);
|
|
|
|
//figure out how much light there should be in that direction.
|
|
VectorCopy(res_ambient, res_diffuse);
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
if (res_dir[0]>=0)
|
|
VectorMA(res_diffuse, res_dir[0], diff[j*2+1], res_diffuse);
|
|
else
|
|
VectorMA(res_diffuse, -res_dir[0], diff[j*2+0], res_diffuse);
|
|
}
|
|
|
|
if (forceface->ival >= 0)
|
|
{
|
|
VectorCopy(best->rgb[forceface->ival], res_diffuse);
|
|
VectorCopy(best->rgb[forceface->ival], res_ambient);
|
|
VectorClear(res_dir);
|
|
res_dir[forceface->ival/3] = (forceface->ival&1)?-1:1;
|
|
}
|
|
|
|
if (srgbmag->value)
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
res_diffuse[j] = M_LinearToSRGB(res_diffuse[j], srgbmag->value)*scale->value;
|
|
res_ambient[j] = M_LinearToSRGB(res_ambient[j], srgbmag->value)*scale->value;
|
|
}
|
|
|
|
/*for (j = 0; j < 6; j++)
|
|
{
|
|
res_cube[j][0] = M_LinearToSRGB(best->rgb[j][0], srgbmag->value)*scale->value;
|
|
res_cube[j][1] = M_LinearToSRGB(best->rgb[j][1], srgbmag->value)*scale->value;
|
|
res_cube[j][2] = M_LinearToSRGB(best->rgb[j][2], srgbmag->value)*scale->value;
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
VectorScale(res_diffuse, scale->value, res_diffuse);
|
|
VectorScale(res_ambient, scale->value, res_ambient);
|
|
|
|
/*for (j = 0; j < 6; j++)
|
|
VectorScale(best->rgb[j], scale->value, res_cube[j]);*/
|
|
}
|
|
|
|
return;
|
|
}
|
|
VectorSet(res_dir, 0,0.707,0.707);
|
|
VectorSet(res_diffuse, 64,64,64);
|
|
VectorSet(res_ambient, 192,192,192);
|
|
}
|
|
#else
|
|
static void VBSP_LightPointValues (struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
|
|
{
|
|
VectorSet(res_diffuse, 64,64,64);
|
|
VectorSet(res_ambient, 192,192,192);
|
|
VectorSet(res_dir, 0,0.707,0.707);
|
|
}
|
|
static void VBSP_LoadLeafLight (model_t *mod, qbyte *mod_base, vlump_t *hdridx, vlump_t *ldridx, vlump_t *hdrvals, vlump_t *ldrvals, int version)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void VBSP_ComputedChecksum(void *ctx, void *data, size_t sum, size_t b)
|
|
{
|
|
model_t *mod = ctx;
|
|
vbspinfo_t *prv = mod->meshinfo;
|
|
mod->checksum = mod->checksum2 = sum;
|
|
prv->summed = true;
|
|
}
|
|
static void VBSP_ComputeChecksum(void *ctx, void *data, size_t length, size_t b)
|
|
{
|
|
unsigned int checksum = LittleLong (filefuncs->BlockChecksum(data, length));
|
|
threadfuncs->AddWork(WG_LOADER, VBSP_ComputedChecksum, ctx, NULL, checksum, 0);
|
|
}
|
|
|
|
static qboolean VBSP_LoadModel(model_t *mod, qbyte *mod_base, size_t filelen, char *loadname)
|
|
{
|
|
dvbspheader_t *srcheader = (void*)mod_base;
|
|
dvbspheader_t header;
|
|
vbspinfo_t *prv = mod->meshinfo;
|
|
size_t i;
|
|
qboolean noerrors = true;
|
|
#ifdef HAVE_CLIENT
|
|
qboolean haverenderer = qrenderer != QR_NONE;
|
|
#endif
|
|
|
|
VBSP_TranslateContentBits_Setup(prv);
|
|
|
|
threadfuncs->AddWork(WG_LOADER, VBSP_ComputeChecksum, mod, mod_base, filelen, 0);
|
|
|
|
mod->lightmaps.width = LMBLOCK_SIZE_MAX;
|
|
mod->lightmaps.height = LMBLOCK_SIZE_MAX;
|
|
|
|
mod->fromgame = fg_new;
|
|
mod->engineflags |= MDLF_NEEDOVERBRIGHT;
|
|
header.version = LittleLong(srcheader->version);
|
|
for (i=0 ; i<HL2_MAXLUMPS ; i++)
|
|
{
|
|
header.lumps[i].filelen = LittleLong (srcheader->lumps[i].filelen);
|
|
header.lumps[i].fileofs = LittleLong (srcheader->lumps[i].fileofs);
|
|
//fixme: truncate lumps if they go off the end
|
|
}
|
|
|
|
if (header.lumps[VLUMP_ZIPFILE].filelen)
|
|
modfuncs->LoadMapArchive(mod, mod_base+header.lumps[VLUMP_ZIPFILE].fileofs, header.lumps[VLUMP_ZIPFILE].filelen);
|
|
|
|
// load into heap
|
|
noerrors = noerrors && VBSP_LoadVertexes (mod, mod_base, &header.lumps[VLUMP_VERTEXES]);
|
|
noerrors = noerrors && VBSP_LoadEdges (mod, mod_base, &header.lumps[VLUMP_EDGES]);
|
|
noerrors = noerrors && VBSP_LoadSurfedges (mod, mod_base, &header.lumps[VLUMP_SURFEDGES]);
|
|
#ifdef HAVE_CLIENT
|
|
if (noerrors && haverenderer)
|
|
VBSP_LoadLighting (mod, mod_base, &header.lumps[VLUMP_LIGHTING_LDR], &header.lumps[VLUMP_LIGHTING_HDR]);
|
|
#endif
|
|
noerrors = noerrors && VBSP_LoadSurfaces (mod, mod_base, &header.lumps[VLUMP_TEXINFO]);
|
|
noerrors = noerrors && VBSP_LoadPlanes (mod, mod_base, &header.lumps[VLUMP_PLANES]);
|
|
noerrors = noerrors && VBSP_LoadTexInfo (mod, mod_base, header.lumps, loadname);
|
|
if (noerrors)
|
|
VBSP_LoadEntities (mod, mod_base, &header.lumps[VLUMP_ENTITIES]);
|
|
noerrors = noerrors && VBSP_LoadFaces (mod, mod_base, header.lumps, header.version);
|
|
noerrors = noerrors && VBSP_LoadDisplacements (mod, mod_base, header.lumps);
|
|
noerrors = noerrors && VBSP_LoadMarksurfaces (mod, mod_base, &header.lumps[VLUMP_LEAFFACES]);
|
|
noerrors = noerrors && VBSP_LoadVisibility (mod, mod_base, &header.lumps[VLUMP_VISIBILITY]);
|
|
noerrors = noerrors && VBSP_LoadBrushSides (mod, mod_base, &header.lumps[VLUMP_BRUSHSIDES]);
|
|
noerrors = noerrors && VBSP_LoadBrushes (mod, mod_base, &header.lumps[VLUMP_BRUSHES]);
|
|
noerrors = noerrors && VBSP_LoadLeafBrushes (mod, mod_base, &header.lumps[VLUMP_LEAFBRUSHES]);
|
|
noerrors = noerrors && VBSP_LoadLeafs (mod, mod_base, &header.lumps[VLUMP_LEAFS], header.version);
|
|
noerrors = noerrors && VBSP_LoadNodes (mod, mod_base, &header.lumps[VLUMP_NODES]);
|
|
noerrors = noerrors && VBSP_LoadSubmodels (mod, mod_base, &header.lumps[VLUMP_MODELS]);
|
|
noerrors = noerrors && VBSP_LoadAreas (mod, mod_base, &header.lumps[VLUMP_AREAS]);
|
|
noerrors = noerrors && VBSP_LoadAreaPortals (mod, mod_base, &header.lumps[VLUMP_AREAPORTALS], &header.lumps[VLUMP_AREAPORTALVERTS]);
|
|
#ifdef HAVE_CLIENT
|
|
if (noerrors && haverenderer)
|
|
VBSP_LoadLeafLight (mod, mod_base, &header.lumps[VLUMP_LEAFLIGHTI_HDR], &header.lumps[VLUMP_LEAFLIGHTI_LDR],
|
|
&header.lumps[VLUMP_LEAFLIGHTV_HDR], &header.lumps[VLUMP_LEAFLIGHTV_LDR], header.version);
|
|
#endif
|
|
noerrors = noerrors && VBSP_LoadGameLump (mod, mod_base, &header.lumps[VLUMP_GAMELUMP]);
|
|
|
|
if (!noerrors)
|
|
return false;
|
|
#ifdef HAVE_SERVER
|
|
mod->funcs.FatPVS = VBSP_FatPVS;
|
|
mod->funcs.EdictInFatPVS = VBSP_EdictInFatPVS;
|
|
mod->funcs.FindTouchedLeafs = VBSP_FindTouchedLeafs;
|
|
#endif
|
|
#ifdef HAVE_CLIENT
|
|
mod->funcs.LightPointValues = VBSP_LightPointValues;
|
|
// mod->funcs.StainNode = VBSP_StainNode;
|
|
// mod->funcs.MarkLights = VBSP_MarkLights;
|
|
mod->funcs.GenerateShadowMesh = VBSP_GenerateShadowMesh;
|
|
#endif
|
|
mod->funcs.ClusterPVS = VBSP_ClusterPVS;
|
|
mod->funcs.ClusterPHS = VBSP_ClusterPHS;
|
|
mod->funcs.ClusterForPoint = VBSP_PointCluster;
|
|
|
|
mod->funcs.SetAreaPortalState = VBSP_SetAreaPortalState;
|
|
mod->funcs.AreasConnected = VBSP_AreasConnected;
|
|
mod->funcs.LoadAreaPortalBlob = VBSP_LoadAreaPortalBlob;
|
|
mod->funcs.SaveAreaPortalBlob = VBSP_SaveAreaPortalBlob;
|
|
|
|
mod->funcs.PrepareFrame = VBSP_PrepareFrame;
|
|
mod->funcs.InfoForPoint = VBSP_InfoForPoint;
|
|
|
|
//displacements suck
|
|
for (i = 0; i < prv->numdisplacements; i++)
|
|
VBSP_FindTouchedLeafs(mod, &prv->displacements[i].pvs, prv->displacements[i].aamin, prv->displacements[i].aamax);
|
|
// if (noerrors)
|
|
// CM_CreatePatchesForLeafs (mod, prv);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CM_LoadMap
|
|
|
|
Loads in the map and all submodels
|
|
==================
|
|
*/
|
|
static qboolean VBSP_LoadMap (model_t *mod, void *filein, size_t filelen)
|
|
{
|
|
unsigned *buf;
|
|
int i;
|
|
dvbspheader_t header;
|
|
model_t *wmod = mod;
|
|
char loadname[32];
|
|
|
|
#ifdef HAVE_CLIENT
|
|
qbyte *facedata = NULL;
|
|
unsigned int facesize = 0;
|
|
#endif
|
|
vbspinfo_t *prv;
|
|
|
|
filefuncs->FileBase (mod->name, loadname, sizeof(loadname));
|
|
|
|
// free old stuff, just in case.
|
|
mod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(*prv));
|
|
|
|
mod->type = mod_brush;
|
|
|
|
//
|
|
// load the file
|
|
//
|
|
buf = (unsigned *)filein;
|
|
if (!buf)
|
|
{
|
|
Con_Printf (CON_ERROR "Couldn't load %s\n", mod->name);
|
|
return false;
|
|
}
|
|
|
|
header = *(dvbspheader_t *)(buf);
|
|
header.magic = LittleLong(header.magic);
|
|
header.version = LittleLong(header.version);
|
|
|
|
ClearBounds(mod->mins, mod->maxs);
|
|
|
|
switch(header.version)
|
|
{
|
|
//case 17: //
|
|
case 18: //beta
|
|
case 19: //hl2,cs:s,hl2dm
|
|
case 20: //portal, l4d, hl2ep2
|
|
case 21: //cs:go, portal 2, l4d2
|
|
//case 22: //dota 2
|
|
//case 23: //dota 2
|
|
//case 27: //'contagion'
|
|
//case 29: //'titanfall'
|
|
if (!VBSP_LoadModel(mod, filein, filelen, loadname))
|
|
return false;
|
|
break;
|
|
default:
|
|
Con_Printf (CON_ERROR "VBSP with unknown version (%s: %i should be 18, 19, 20, or 21)\n"
|
|
, mod->name, header.version);
|
|
return false;
|
|
}
|
|
|
|
if (map_autoopenportals->value)
|
|
memset (prv->portalopen, 1, sizeof(prv->portalopen)); //open them all. Used for progs that havn't got a clue.
|
|
else
|
|
memset (prv->portalopen, 0, sizeof(prv->portalopen)); //make them start closed.
|
|
FloodAreaConnections (prv);
|
|
|
|
mod->nummodelsurfaces = mod->numsurfaces;
|
|
memset(&mod->batches, 0, sizeof(mod->batches));
|
|
mod->vbos = NULL;
|
|
|
|
mod->numsubmodels = VBSP_NumInlineModels(mod);
|
|
|
|
//in case anyone wants to know the typical player size...
|
|
VectorSet(mod->hulls[1].clip_mins, -16,-16,-36);
|
|
VectorSet(mod->hulls[1].clip_maxs, 16,16,36);
|
|
mod->hulls[0].firstclipnode = prv->cmodels[0].headnode-mod->nodes;
|
|
mod->rootnode = prv->cmodels[0].headnode;
|
|
mod->nummodelsurfaces = prv->cmodels[0].numsurfaces;
|
|
|
|
#ifdef HAVE_CLIENT
|
|
if (qrenderer != QR_NONE)
|
|
{
|
|
builddata_t *bd = plugfuncs->Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);
|
|
bd->buildfunc = VBSP_BuildSurfMesh;
|
|
bd->paintlightmaps = true;
|
|
memcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);
|
|
threadfuncs->AddWork(WG_MAIN, VBSP_GenerateMaterials, mod, bd, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
for (i=1 ; i< mod->numsubmodels ; i++)
|
|
{
|
|
cmodel_t *bm;
|
|
|
|
char name[MAX_QPATH];
|
|
|
|
Q_snprintfz (name, sizeof(name), "*%i:%s", i, wmod->publicname);
|
|
mod = modfuncs->BeginSubmodelLoad(name);
|
|
*mod = *wmod;
|
|
mod->archive = NULL;
|
|
mod->entities_raw = NULL;
|
|
mod->submodelof = wmod;
|
|
Q_strncpyz(mod->publicname, name, sizeof(mod->publicname));
|
|
Q_snprintfz (mod->name, sizeof(mod->name), "*%i:%s", i, wmod->name);
|
|
memset(&mod->memgroup, 0, sizeof(mod->memgroup));
|
|
|
|
bm = VBSP_InlineModel (wmod, name);
|
|
|
|
mod->hulls[0].firstclipnode = -1; //no nodes,
|
|
if (bm->headleaf)
|
|
{
|
|
mod->leafs = bm->headleaf;
|
|
mod->nodes = NULL;
|
|
mod->hulls[0].firstclipnode = -1; //make it refer directly to the first leaf, for things that still use numbers.
|
|
mod->rootnode = (mnode_t*)bm->headleaf;
|
|
}
|
|
else
|
|
{
|
|
mod->leafs = wmod->leafs;
|
|
mod->nodes = wmod->nodes;
|
|
mod->hulls[0].firstclipnode = bm->headnode - mod->nodes; //determine the correct node index
|
|
mod->rootnode = bm->headnode;
|
|
}
|
|
mod->nummodelsurfaces = bm->numsurfaces;
|
|
mod->firstmodelsurface = bm->firstsurface;
|
|
|
|
VBSP_BuildBIHSubmodel(mod, i);
|
|
|
|
memset(&mod->batches, 0, sizeof(mod->batches));
|
|
mod->vbos = NULL;
|
|
|
|
VectorCopy (bm->maxs, mod->maxs);
|
|
VectorCopy (bm->mins, mod->mins);
|
|
#ifdef HAVE_CLIENT
|
|
mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
|
|
|
|
if (qrenderer != QR_NONE)
|
|
{
|
|
builddata_t *bd = plugfuncs->Malloc(sizeof(*bd) + facesize*mod->nummodelsurfaces);
|
|
bd->buildfunc = VBSP_BuildSurfMesh;
|
|
bd->paintlightmaps = true;
|
|
memcpy(bd+1, facedata + mod->firstmodelsurface*facesize, facesize*mod->nummodelsurfaces);
|
|
threadfuncs->AddWork(WG_MAIN, VBSP_GenerateMaterials, mod, bd, i, 0);
|
|
}
|
|
#endif
|
|
modfuncs->EndSubmodelLoad(mod, MLS_LOADED);
|
|
}
|
|
|
|
//urgh, we need to wait for models to load in order to get their sizes. that requires being on the main thread and the caller will think we're loaded on completion so we can't safely pingpong it back before generating the bih tree
|
|
threadfuncs->AddWork(WG_MAIN, VBSP_BuildBIHMain, wmod, NULL, 0, 0);
|
|
return true;
|
|
}
|
|
|
|
|
|
qboolean VBSP_Init(void)
|
|
{
|
|
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
|
|
modfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs));
|
|
if (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION)
|
|
modfuncs = NULL;
|
|
threadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs));
|
|
|
|
if (modfuncs && filefuncs && threadfuncs)
|
|
{
|
|
modfuncs->RegisterModelFormatMagic("Source (V)BSP", "VBSP",4, VBSP_LoadMap);
|
|
|
|
#define MAPOPTIONS "Map Cvar Options"
|
|
hl2_novis = cvarfuncs->GetNVFDG("r_novis", "0", 0, "Multiplier for how far displacements can move.", MAPOPTIONS);
|
|
hl2_displacement_scale = cvarfuncs->GetNVFDG("hl2_displacement_scale", "1", CVAR_RENDERERLATCH|CVAR_CHEAT, "Multiplier for how far displacements can move.", MAPOPTIONS);
|
|
hl2_favour_ldr = cvarfuncs->GetNVFDG("hl2_favour_ldr", "0", CVAR_RENDERERLATCH|CVAR_CHEAT, "Favour LDR data instead of HDR (when both are present).", MAPOPTIONS);
|
|
map_noareas = cvarfuncs->GetNVFDG("map_noareas", "0", 0, "Ignore areaportals.", MAPOPTIONS);
|
|
map_autoopenportals = cvarfuncs->GetNVFDG("map_autoopenportals", "0", CVAR_RENDERERLATCH, "When set to 1, force-opens all area portals. Normally these start closed and are opened by doors when they move, but this requires the gamecode to signal this.", MAPOPTIONS);
|
|
hl2_contents_remap = cvarfuncs->GetNVFDG("hl2_contents_remap",
|
|
"/*solid*/SOLID "
|
|
"/*window*/WINDOW "
|
|
"/*aux*/Q2AUX "
|
|
"/*grate*/CLIP " //would otherwise be LAVA
|
|
"/*slime*/SLIME "
|
|
"/*water*/WATER "
|
|
"/*mist*/Q2MIST "
|
|
"/*opaque*/7 "
|
|
"/*testfogvolume*/8 "
|
|
"/*??*/9 "
|
|
"/*??*/10 "
|
|
"/*team1*/11 "
|
|
"/*team2*/12 "
|
|
"/*ignorenodrawopaque*/13 "
|
|
"/*movable*/-1 " //would otherwise be LADDER
|
|
"/*areaportal*/Q2AREAPORTAL "
|
|
"/*playerclip*/PLAYERCLIP "
|
|
"/*monsterclip*/MONSTERCLIP "
|
|
"/*current_0*/Q2CURRENT_0 "
|
|
"/*current_90*/Q2CURRENT_90 "
|
|
"/*current_180*/Q2CURRENT_180 "
|
|
"/*current_270*/Q2CURRENT_270 "
|
|
"/*current_up*/Q2CURRENT_UP "
|
|
"/*current_down*/Q2CURRENT_DOWN "
|
|
"/*origin*/Q2ORIGIN "
|
|
"/*monster*/BODY "
|
|
"/*deadmonster*/CORPSE "
|
|
"/*detail*/DETAIL "
|
|
"/*translucent*/Q2TRANSLUCENT "
|
|
"/*ladder*/LADDER " //would otherwise be Q2LADDER
|
|
"/*hitbox*/30 "
|
|
"/*??*/SKY"
|
|
,CVAR_RENDERERLATCH, "Specifies a table for hl2->internal contentbits (one entry for each source bit).", MAPOPTIONS);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|