This code should also be in the Quake 2 map compiler somewhere, but I don't know where to find the Quake 2 map compiler source code. I looked around at ftp.idsoftware.com and so far haven't been able to find the q2bsp source.
How origin brushes appear to be handled
It looks like origin brushes are treated specially just like water, sky, clip textures, etc.
It appears the texture name must be "origin". Like how "clip" is a specially designated texture name.bspfile.h in Zoner Tools hlbsp wrote:typedef enum
{
CONTENTS_EMPTY = -1,
CONTENTS_SOLID = -2,
CONTENTS_WATER = -3,
CONTENTS_SLIME = -4,
CONTENTS_LAVA = -5,
CONTENTS_SKY = -6,
CONTENTS_ORIGIN = -7, // removed at csg time
CONTENTS_CLIP = -8, // changed to contents_solid
CONTENTS_CURRENT_0 = -9,
CONTENTS_CURRENT_90 = -10,
CONTENTS_CURRENT_180 = -11,
CONTENTS_CURRENT_270 = -12,
CONTENTS_CURRENT_UP = -13,
CONTENTS_CURRENT_DOWN = -14,
CONTENTS_TRANSLUCENT = -15,
CONTENTS_HINT = -16, // Filters down to CONTENTS_EMPTY by bsp, ENGINE SHOULD NEVER SEE THIS
#ifdef ZHLT_NULLTEX
CONTENTS_NULL = -17, // AJM // removed in csg and bsp, VIS or RAD shouldnt have to deal with this, only clip planes!
#endif
#ifdef ZHLT_DETAIL // AJM
CONTENTS_DETAIL = -18,
#endif
}
A function that returns a string version of the name of the brush typestatic contents_t TextureContents(const char* const name)
{
if (!strncasecmp(name, "sky", 3))
return CONTENTS_SKY;
// =====================================================================================
//Cpt_Andrew - Env_Sky Check
// =====================================================================================
if (!strncasecmp(name, "env_sky", 3))
return CONTENTS_SKY;
// =====================================================================================
if (!strncasecmp(name + 1, "!lava", 5))
return CONTENTS_LAVA;
if (!strncasecmp(name + 1, "!slime", 6))
return CONTENTS_SLIME;
if (name[0] == '!') //optimized -- don't check for current unless it's liquid (KGP)
{
if (!strncasecmp(name, "!cur_90", 7))
return CONTENTS_CURRENT_90;
if (!strncasecmp(name, "!cur_0", 6))
return CONTENTS_CURRENT_0;
if (!strncasecmp(name, "!cur_270", )
return CONTENTS_CURRENT_270;
if (!strncasecmp(name, "!cur_180", )
return CONTENTS_CURRENT_180;
if (!strncasecmp(name, "!cur_up", 7))
return CONTENTS_CURRENT_UP;
if (!strncasecmp(name, "!cur_dwn", )
return CONTENTS_CURRENT_DOWN;
return CONTENTS_WATER; //default for liquids
}
if (!strncasecmp(name, "origin", 6))
return CONTENTS_ORIGIN;
Ok, looks like the map compiler better reject any brush that isn't an entity. So an origin brush can't be part of the "world", must be a func_something or other entity type.// =====================================================================================
// ContentsToString
// =====================================================================================
const char* ContentsToString(const contents_t type)
{
switch (type)
{
case CONTENTS_EMPTY:
return "EMPTY";
case CONTENTS_SOLID:
return "SOLID";
case CONTENTS_WATER:
return "WATER";
case CONTENTS_SLIME:
return "SLIME";
case CONTENTS_LAVA:
return "LAVA";
case CONTENTS_SKY:
return "SKY";
case CONTENTS_ORIGIN:
return "ORIGIN";
And here, for completeness is the part where the brush gets an all clear if it is an entity texture.// check to make sure we dont have an origin brush as part of worldspawn
if ((b->entitynum == 0) || (strcmp("func_group", ValueForKey(&g_entities[b->entitynum], "classname"))==0))
{
if (contents == CONTENTS_ORIGIN)
{
Fatal(assume_BRUSH_NOT_ALLOWED_IN_WORLD, "Entity %i, Brush %i: %s brushes not allowed in world\n(did you forget to tie this origin brush to a rotating entity?)", b->entitynum, b->brushnum, ContentsToString(contents));
}
}
The brush isn't part of the clipping hull. I think I understand the concept of a clipping hull, but I'm still learning about the bsp so I don't claim to know completely what is going on here:// otherwise its not worldspawn, therefore its an entity. check to make sure this brush is allowed
// to be an entity.
switch (contents)
{
case CONTENTS_SOLID:
case CONTENTS_WATER:
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_ORIGIN:
case CONTENTS_CLIP:
#ifdef ZHLT_NULLTEX // AJM
case CONTENTS_NULL:
break;
#endif
default:
Fatal(assume_BRUSH_NOT_ALLOWED_IN_ENTITY, "Entity %i, Brush %i: %s brushes not allowed in entity", b->entitynum, b->brushnum, ContentsToString(contents));
break;
}
This is in parse_brush. I am thinking it probably is identifying the center point for rotation but I really don't know for sure.void CreateBrush(const int brushnum)
{
brush_t* b;
int contents;
int h;
b = &g_mapbrushes[brushnum];
contents = b->contents;
if (contents == CONTENTS_ORIGIN)
return;
// HULL 0
MakeBrushPlanes(b);
MakeHullFaces(b, &b->hulls[0]);
// these brush types do not need to be represented in the clipping hull
switch (contents)
{
case CONTENTS_LAVA:
case CONTENTS_SLIME:
case CONTENTS_WATER:
case CONTENTS_TRANSLUCENT:
case CONTENTS_HINT:
return;
}
Treat origin brushes like clip brushes, it seems:static contents_t ParseBrush(entity_t* mapent) { ... wrote:Code: Select all
if (contents == CONTENTS_ORIGIN) { char string[MAXTOKEN]; vec3_t origin; b->contents = CONTENTS_SOLID; CreateBrush(mapent->firstbrush + b->brushnum); // to get sizes b->contents = contents; for (i = 0; i < NUM_HULLS; i++) { b->hulls[i].faces = NULL; } if (b->entitynum != 0) // Ignore for WORLD (code elsewhere enforces no ORIGIN in world message) { VectorAdd(b->hulls[0].bounds.m_Mins, b->hulls[0].bounds.m_Maxs, origin); VectorScale(origin, 0.5, origin); safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue(&g_entities[b->entitynum], "origin", string); } }
Apparently, an origin brush is actually part of the rotating object entity:bool ParseMapEntity() wrote:Code: Select all
if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN)) all_clip = false;
And maybe something again about identifying the center of the origin brush:Code: Select all
// if the given entity only has one brush and its an origin brush if ((mapent->numbrushes == 1) && (g_mapbrushes[mapent->firstbrush].contents == CONTENTS_ORIGIN)) { brushhull_t* hull = g_mapbrushes[mapent->firstbrush].hulls; Error("Entity %i, contains ONLY an origin brush near (%.0f,%.0f,%.0f)\n", this_entity, hull->bounds.m_Mins[0], hull->bounds.m_Mins[1], hull->bounds.m_Mins[2]); }
I'm probably going to try to track down the q2bsp source code. I imagine this all works the same. The license for Zoner Tools is a little weird and goes like this:Code: Select all
// ===================================================================================== // SetModelCenters // ===================================================================================== static void SetModelCenters(int entitynum) { int i; int last; char string[MAXTOKEN]; entity_t* e = &g_entities[entitynum]; BoundingBox bounds; vec3_t center; if ((entitynum == 0) || (e->numbrushes == 0)) // skip worldspawn and point entities return; if (!*ValueForKey(e, "light_origin")) // skip if its not a zhlt_flags light_origin return; for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++) { if (g_mapbrushes[i].contents != CONTENTS_ORIGIN) { bounds.add(g_mapbrushes[i].hulls->bounds); } } VectorAdd(bounds.m_Mins, bounds.m_Maxs, center); VectorScale(center, 0.5, center); safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)center[0], (int)center[1], (int)center[2]); SetKeyValue(e, "model_center", string); }
Code: Select all
II. LICENCE 0) This code is protected by the GPL, a link to the GPL can be found at the end of this page. 1) In addition to the GPL, the Valve SDK 2.3 EULA overrides any rights you may have obtained in the GPL, when needed. 2) The iD Quake 2 Licence overrides portions of both the Valve EULA, and the GPL where needed, please contact iD for information on this subject.