#ifdef SYSTEM_WIN32 #pragma warning(disable:4996) #endif #pragma warning(disable:4018) // '<' : signed/unsigned mismatch #include "csg.h" int g_nummapbrushes; brush_t g_mapbrushes[MAX_MAP_BRUSHES]; int g_numbrushsides; side_t g_brushsides[MAX_MAP_SIDES]; int g_nMapFileVersion; static const vec3_t s_baseaxis[18] = { {0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor {0, 0, -1}, {1, 0, 0}, {0, -1, 0}, // ceiling {1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // west wall {-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // east wall {0, 1, 0}, {1, 0, 0}, {0, 0, -1}, // south wall {0, -1, 0}, {1, 0, 0}, {0, 0, -1}, // north wall }; // ===================================================================================== // TextureAxisFromPlane // ===================================================================================== void TextureAxisFromPlane(const plane_t* const pln, vec3_t xv, vec3_t yv) { int bestaxis; vec_t dot, best; int i; best = 0; bestaxis = 0; for (i = 0; i < 6; i++) { dot = DotProduct(pln->normal, s_baseaxis[i * 3]); if (dot > best) { best = dot; bestaxis = i; } } VectorCopy(s_baseaxis[bestaxis * 3 + 1], xv); VectorCopy(s_baseaxis[bestaxis * 3 + 2], yv); } #define ScaleCorrection (1.0/128.0) // ===================================================================================== // CopySKYtoCLIP // clips a particluar sky brush // ===================================================================================== static void CopySKYtoCLIP(const brush_t* const b) { int i; entity_t* mapent; brush_t* newbrush; if (b->contents != CONTENTS_SKY) Error("[MOD] CopySKYtoCLIP: Got a NON-SKY for passed brush! (%s)",b->contents ); hlassert(b->contents == CONTENTS_SKY); // Only SKY brushes should be passed down to this function(sanity check) hlassert(b->entitynum == 0); // SKY must be in worldspawn entity mapent = &g_entities[b->entitynum]; mapent->numbrushes++; newbrush = &g_mapbrushes[g_nummapbrushes]; newbrush->entitynum = b->entitynum; newbrush->brushnum = g_nummapbrushes - mapent->firstbrush; newbrush->firstside = g_numbrushsides; newbrush->numsides = b->numsides; newbrush->contents = CONTENTS_CLIP; newbrush->noclip = 0; for (i = 0; i < b->numsides; i++) { int j; side_t* side = &g_brushsides[g_numbrushsides]; *side = g_brushsides[b->firstside + i]; safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name)); for (j = 0; j < NUM_HULLS; j++) { newbrush->hulls[j].faces = NULL; newbrush->hulls[j].bounds = b->hulls[j].bounds; } g_numbrushsides++; hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES); } g_nummapbrushes++; hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES); } // ===================================================================================== // HandleSKYCLIP // clips the whole sky, unconditional of g_skyclip // ===================================================================================== static void HandleSKYCLIP() { int i; int last; entity_t* e = &g_entities[0]; for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++) { if (g_mapbrushes[i].contents == CONTENTS_SKY) { CopySKYtoCLIP(&g_mapbrushes[i]); } } } // ===================================================================================== // CheckForInvisible // see if a brush is part of an invisible entity (KGP) // ===================================================================================== #ifdef HLCSG_NULLIFY_INVISIBLE static bool CheckForInvisible(entity_t* mapent) { using namespace std; string keyval(ValueForKey(mapent,"classname")); if(g_invisible_items.count(keyval)) { return true; } keyval.assign(ValueForKey(mapent,"targetname")); if(g_invisible_items.count(keyval)) { return true; } keyval.assign(ValueForKey(mapent,"zhlt_invisible")); if(!keyval.empty() && strcmp(keyval.c_str(),"0")) { return true; } return false; } #endif // ===================================================================================== // ParseBrush // parse a brush from script // ===================================================================================== static contents_t ParseBrush(entity_t* mapent) { brush_t* b; int i, j; side_t* side; contents_t contents; bool ok; #ifdef HLCSG_NULLIFY_INVISIBLE // KGP bool nullify = CheckForInvisible(mapent); #endif hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES); b = &g_mapbrushes[g_nummapbrushes]; g_nummapbrushes++; b->firstside = g_numbrushsides; b->entitynum = g_numentities - 1; b->brushnum = g_nummapbrushes - mapent->firstbrush - 1; #ifdef HLCSG_CLIPECONOMY // AJM b->noclip = 0; #endif mapent->numbrushes++; ok = GetToken(true); while (ok) { g_TXcommand = 0; if (!strcmp(g_token, "}")) { break; } hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES); side = &g_brushsides[g_numbrushsides]; g_numbrushsides++; b->numsides++; // read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) { GetToken(true); } if (strcmp(g_token, "(")) { Error("Parsing Entity %i, Brush %i, Side %i : Expecting '(' got '%s'", b->entitynum, b->brushnum, b->numsides, g_token); } for (j = 0; j < 3; j++) { GetToken(false); side->planepts[i][j] = atof(g_token); } GetToken(false); if (strcmp(g_token, ")")) { Error("Parsing Entity %i, Brush %i, Side %i : Expecting ')' got '%s'", b->entitynum, b->brushnum, b->numsides, g_token); } } // read the texturedef GetToken(false); _strupr(g_token); #ifdef HLCSG_NULLIFY_INVISIBLE if(nullify && strncmp(g_token,"BEVEL",5) && strncmp(g_token,"ORIGIN",6)) { safe_strncpy(g_token,"NULL",sizeof(g_token)); } #endif safe_strncpy(side->td.name, g_token, sizeof(side->td.name)); if (g_nMapFileVersion < 220) // Worldcraft 2.1-, Radiant { GetToken(false); side->td.vects.valve.shift[0] = atof(g_token); GetToken(false); side->td.vects.valve.shift[1] = atof(g_token); GetToken(false); side->td.vects.valve.rotate = atof(g_token); GetToken(false); side->td.vects.valve.scale[0] = atof(g_token); GetToken(false); side->td.vects.valve.scale[1] = atof(g_token); } else // Worldcraft 2.2+ { // texture U axis GetToken(false); if (strcmp(g_token, "[")) { hlassume(false, assume_MISSING_BRACKET_IN_TEXTUREDEF); } GetToken(false); side->td.vects.valve.UAxis[0] = atof(g_token); GetToken(false); side->td.vects.valve.UAxis[1] = atof(g_token); GetToken(false); side->td.vects.valve.UAxis[2] = atof(g_token); GetToken(false); side->td.vects.valve.shift[0] = atof(g_token); GetToken(false); if (strcmp(g_token, "]")) { Error("missing ']' in texturedef (U)"); } // texture V axis GetToken(false); if (strcmp(g_token, "[")) { Error("missing '[' in texturedef (V)"); } GetToken(false); side->td.vects.valve.VAxis[0] = atof(g_token); GetToken(false); side->td.vects.valve.VAxis[1] = atof(g_token); GetToken(false); side->td.vects.valve.VAxis[2] = atof(g_token); GetToken(false); side->td.vects.valve.shift[1] = atof(g_token); GetToken(false); if (strcmp(g_token, "]")) { Error("missing ']' in texturedef (V)"); } // Texture rotation is implicit in U/V axes. GetToken(false); side->td.vects.valve.rotate = 0; // texure scale GetToken(false); side->td.vects.valve.scale[0] = atof(g_token); GetToken(false); side->td.vects.valve.scale[1] = atof(g_token); } ok = GetToken(true); // Done with line, this reads the first item from the next line if ((g_TXcommand == '1' || g_TXcommand == '2')) { // We are QuArK mode and need to translate some numbers to align textures its way // from QuArK, the texture vectors are given directly from the three points vec3_t TexPt[2]; int k; float dot22, dot23, dot33, mdet, aa, bb, dd; k = g_TXcommand - '0'; for (j = 0; j < 3; j++) { TexPt[1][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection; } k = 3 - k; for (j = 0; j < 3; j++) { TexPt[0][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection; } dot22 = DotProduct(TexPt[0], TexPt[0]); dot23 = DotProduct(TexPt[0], TexPt[1]); dot33 = DotProduct(TexPt[1], TexPt[1]); mdet = dot22 * dot33 - dot23 * dot23; if (mdet < 1E-6 && mdet > -1E-6) { aa = bb = dd = 0; Warning ("Degenerate QuArK-style brush texture : Entity %i, Brush %i @ (%f,%f,%f) (%f,%f,%f) (%f,%f,%f)", b->entitynum, b->brushnum, side->planepts[0][0], side->planepts[0][1], side->planepts[0][2], side->planepts[1][0], side->planepts[1][1], side->planepts[1][2], side->planepts[2][0], side->planepts[2][1], side->planepts[2][2]); } else { mdet = 1.0 / mdet; aa = dot33 * mdet; bb = -dot23 * mdet; //cc = -dot23*mdet; // cc = bb dd = dot22 * mdet; } for (j = 0; j < 3; j++) { side->td.vects.quark.vects[0][j] = aa * TexPt[0][j] + bb * TexPt[1][j]; side->td.vects.quark.vects[1][j] = -( /*cc */ bb * TexPt[0][j] + dd * TexPt[1][j]); } side->td.vects.quark.vects[0][3] = -DotProduct(side->td.vects.quark.vects[0], side->planepts[0]); side->td.vects.quark.vects[1][3] = -DotProduct(side->td.vects.quark.vects[1], side->planepts[0]); } side->td.txcommand = g_TXcommand; // Quark stuff, but needs setting always }; b->contents = contents = CheckBrushContents(b); // // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity // 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); } } return contents; } // ===================================================================================== // ParseMapEntity // parse an entity from script // ===================================================================================== bool ParseMapEntity() { bool all_clip = true; int this_entity; entity_t* mapent; epair_t* e; if (!GetToken(true)) { return false; } this_entity = g_numentities; if (strcmp(g_token, "{")) { Error("Parsing Entity %i, expected '{' got '%s'", this_entity, g_token); } hlassume(g_numentities < MAX_MAP_ENTITIES, assume_MAX_MAP_ENTITIES); g_numentities++; mapent = &g_entities[this_entity]; mapent->firstbrush = g_nummapbrushes; mapent->numbrushes = 0; while (1) { if (!GetToken(true)) Error("ParseEntity: EOF without closing brace"); if (!strcmp(g_token, "}")) // end of our context break; if (!strcmp(g_token, "{")) // must be a brush { contents_t contents = ParseBrush(mapent); if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN)) all_clip = false; } else // else assume an epair { e = ParseEpair(); if (!strcmp(e->key, "mapversion")) { g_nMapFileVersion = atoi(e->value); } e->next = mapent->epairs; mapent->epairs = e; } } if (mapent->numbrushes && all_clip) Fatal(assume_NO_VISIBILE_BRUSHES, "Entity %i has no visible brushes\n", this_entity); CheckFatal(); #ifdef ZHLT_DETAIL // AJM if (!strcmp(ValueForKey(mapent, "classname"), "info_detail") && g_bDetailBrushes && this_entity != 0) { // mark all of the brushes in this entity as contents_detail for (int i = mapent->firstbrush; i < mapent->firstbrush + mapent->numbrushes; i++) { g_mapbrushes[i].contents = CONTENTS_DETAIL; } // move these brushes to worldspawn { brush_t* temp; int newbrushes; int worldbrushes; int i; newbrushes = mapent->numbrushes; worldbrushes = g_entities[0].numbrushes; temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t)); memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t)); for (i = 0; i < newbrushes; i++) { temp[i].entitynum = 0; } // make space to move the brushes (overlapped copy) memmove(g_mapbrushes + worldbrushes + newbrushes, g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes)); // copy the new brushes down memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes); // fix up indexes g_numentities--; g_entities[0].numbrushes += newbrushes; for (i = 1; i < g_numentities; i++) { g_entities[i].firstbrush += newbrushes; } memset(mapent, 0, sizeof(*mapent)); Free(temp); } // delete this entity g_numentities--; return true; } #endif #ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters")) { GetParamsFromEnt(mapent); } #endif // if its the worldspawn entity and we need to skyclip, then do it if ((this_entity == 0) && g_skyclip) // first entitiy { HandleSKYCLIP(); } // 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]); } GetVectorForKey(mapent, "origin", mapent->origin); // group entities are just for editor convenience // toss all brushes into the world entity if (!g_onlyents && !strcmp("func_group", ValueForKey(mapent, "classname"))) { // this is pretty gross, because the brushes are expected to be // in linear order for each entity brush_t* temp; int newbrushes; int worldbrushes; int i; newbrushes = mapent->numbrushes; worldbrushes = g_entities[0].numbrushes; temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t)); memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t)); for (i = 0; i < newbrushes; i++) { temp[i].entitynum = 0; } // make space to move the brushes (overlapped copy) memmove(g_mapbrushes + worldbrushes + newbrushes, g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes)); // copy the new brushes down memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes); // fix up indexes g_numentities--; g_entities[0].numbrushes += newbrushes; for (i = 1; i < g_numentities; i++) { g_entities[i].firstbrush += newbrushes; } memset(mapent, 0, sizeof(*mapent)); Free(temp); } return true; } // ===================================================================================== // CountEngineEntities // ===================================================================================== unsigned int CountEngineEntities() { unsigned int x; unsigned num_engine_entities = 0; entity_t* mapent = g_entities; // for each entity in the map for (x=0; x