/* * @brief Mark each leaf with an area, bounded by CONTENTS_AREA_PORTAL */ void FloodAreas(tree_t *tree) { Com_Verbose("--- FloodAreas ---\n"); FindAreas_r(tree->head_node); SetAreaPortalAreas_r(tree->head_node); Com_Verbose("%5i areas\n", c_areas); }
/* * ============= * FillOutside * * Fill all nodes that can't be reached by entities * ============= */ void FillOutside(node_t * head_node) { c_outside = 0; c_inside = 0; c_solid = 0; Com_Verbose("--- FillOutside ---\n"); FillOutside_r(head_node); Com_Verbose("%5i solid leafs\n", c_solid); Com_Verbose("%5i leafs filled\n", c_outside); Com_Verbose("%5i inside leafs\n", c_inside); }
/* * @brief */ void WriteBSP(node_t *head_node) { int32_t old_faces; c_nofaces = 0; c_facenodes = 0; Com_Verbose("--- WriteBSP ---\n"); old_faces = d_bsp.num_faces; d_bsp.models[d_bsp.num_models].head_node = EmitDrawNode_r(head_node); Com_Verbose("%5i nodes with faces\n", c_facenodes); Com_Verbose("%5i nodes without faces\n", c_nofaces); Com_Verbose("%5i faces\n", d_bsp.num_faces - old_faces); }
/** * @brief Send the servers list to the specified client address. */ static void Ms_GetServers(struct sockaddr_in *from) { mem_buf_t buf; byte buffer[0xffff]; Mem_InitBuffer(&buf, buffer, sizeof(buffer)); const char *servers = "\xFF\xFF\xFF\xFF" "servers "; Mem_WriteBuffer(&buf, servers, strlen(servers)); uint32_t i = 0; GList *s = ms_servers; while (s) { const ms_server_t *server = (ms_server_t *) s->data; if (server->validated) { Mem_WriteBuffer(&buf, &server->addr.sin_addr, sizeof(server->addr.sin_addr)); Mem_WriteBuffer(&buf, &server->addr.sin_port, sizeof(server->addr.sin_port)); i++; } s = s->next; } if ((sendto(ms_sock, buf.data, buf.size, 0, (struct sockaddr *) from, sizeof(*from))) == -1) { Com_Warn("%s: %s\n", atos(from), strerror(errno)); } else { Com_Verbose("Sent %d servers to %s\n", i, atos(from)); } }
/** * @brief */ static void Ms_Frame(void) { const time_t now = time(NULL); GList *s = ms_servers; while (s) { ms_server_t *server = (ms_server_t *) s->data; if (now - server->last_heartbeat > 30) { if (server->queued_pings > 6) { Com_Print("Server %s timed out\n", stos(server)); Ms_DropServer(server); } else { if (now - server->last_ping >= 10) { server->queued_pings++; server->last_ping = now; Com_Verbose("Pinging %s\n", stos(server)); const char *ping = "\xFF\xFF\xFF\xFF" "ping"; sendto(ms_sock, ping, strlen(ping), 0, (struct sockaddr *) &server->addr, sizeof(server->addr)); } } } s = s->next; } }
/* * @brief */ static int32_t BrushContents(const map_brush_t * b) { int32_t contents; const side_t *s; int32_t i; int32_t trans; s = &b->original_sides[0]; contents = s->contents; trans = d_bsp.texinfo[s->texinfo].flags; for (i = 1; i < b->num_sides; i++, s++) { trans |= d_bsp.texinfo[s->texinfo].flags; if (s->contents != contents) { Com_Verbose("Entity %i, Brush %i: mixed face contents\n", b->entity_num, b->brush_num); break; } } // if any side is translucent, mark the contents and change solid to window if (trans & (SURF_ALPHA_TEST | SURF_BLEND_33 | SURF_BLEND_66)) { contents |= CONTENTS_TRANSLUCENT; if (contents & CONTENTS_SOLID) { contents &= ~CONTENTS_SOLID; contents |= CONTENTS_WINDOW; } } return contents; }
/* * @brief */ void WritePortalFile(tree_t *tree) { char filename[MAX_OSPATH]; node_t *head_node; Com_Verbose("--- WritePortalFile ---\n"); head_node = tree->head_node; num_visclusters = 0; num_visportals = 0; FreeTreePortals_r(head_node); MakeHeadnodePortals(tree); CreateVisPortals_r(head_node); // set the cluster field in every leaf and count the total number of portals NumberLeafs_r(head_node); // write the file StripExtension(map_name, filename); strcat(filename, ".prt"); if (!(prtfile = Fs_OpenWrite(filename))) Com_Error(ERR_FATAL, "Error opening %s\n", filename); Fs_Print(prtfile, "%s\n", PORTALFILE); Fs_Print(prtfile, "%i\n", num_visclusters); Fs_Print(prtfile, "%i\n", num_visportals); Com_Verbose("%5i visclusters\n", num_visclusters); Com_Verbose("%5i visportals\n", num_visportals); WritePortalFile_r(head_node); Fs_Close(prtfile); // we need to store the clusters out now because ordering // issues made us do this after writebsp... clusterleaf = 1; SaveClusters_r(head_node); Com_Verbose("--- WritePortalFile complete ---\n"); }
/* * MakeBrushWindings * * Makes basewindigs for sides and mins / maxs for the brush */ static boolean_t MakeBrushWindings(map_brush_t * ob) { int i, j; side_t *side; ClearBounds(ob->mins, ob->maxs); for (i = 0; i < ob->num_sides; i++) { const map_plane_t *plane = &map_planes[ob->original_sides[i].plane_num]; winding_t *w = BaseWindingForPlane(plane->normal, plane->dist); for (j = 0; j < ob->num_sides && w; j++) { if (i == j) continue; // back side clipaway if (ob->original_sides[j].plane_num == (ob->original_sides[j].plane_num ^ 1)) continue; if (ob->original_sides[j].bevel) continue; plane = &map_planes[ob->original_sides[j].plane_num ^ 1]; ChopWindingInPlace(&w, plane->normal, plane->dist, 0); //CLIP_EPSILON); } side = &ob->original_sides[i]; side->winding = w; if (w) { side->visible = true; for (j = 0; j < w->numpoints; j++) AddPointToBounds(w->p[j], ob->mins, ob->maxs); } } for (i = 0; i < 3; i++) { if (ob->mins[0] < -MAX_WORLD_WIDTH || ob->maxs[0] > MAX_WORLD_WIDTH) Com_Verbose("entity %i, brush %i: bounds out of range\n", ob->entity_num, ob->brush_num); if (ob->mins[0] > MAX_WORLD_WIDTH || ob->maxs[0] < -MAX_WORLD_WIDTH) Com_Verbose("entity %i, brush %i: no visible sides on brush\n", ob->entity_num, ob->brush_num); } return true; }
/* * ============ * FindPortalSide * * Finds a brush side to use for texturing the given portal * ============ */ static void FindPortalSide(portal_t * p) { int32_t viscontents; bsp_brush_t *bb; int32_t i, j; int32_t plane_num; side_t *bestside; vec_t dot, bestdot; map_plane_t *p2; // decide which content change is strongest // solid > lava > water, etc viscontents = VisibleContents(p->nodes[0]->contents ^ p->nodes[1]->contents); if (!viscontents) return; plane_num = p->onnode->plane_num; bestside = NULL; bestdot = 0; for (j = 0; j < 2; j++) { const node_t *n = p->nodes[j]; const map_plane_t *p1 = &map_planes[p->onnode->plane_num]; for (bb = n->brushes; bb; bb = bb->next) { const map_brush_t *brush = bb->original; if (!(brush->contents & viscontents)) continue; for (i = 0; i < brush->num_sides; i++) { side_t *side = &brush->original_sides[i]; if (side->bevel) continue; if (side->texinfo == TEXINFO_NODE) continue; // non-visible if ((side->plane_num & ~1) == plane_num) { // exact match bestside = &brush->original_sides[i]; goto gotit; } // see how close the match is p2 = &map_planes[side->plane_num & ~1]; dot = DotProduct(p1->normal, p2->normal); if (dot > bestdot) { bestdot = dot; bestside = side; } } } } gotit: if (!bestside) Com_Verbose("WARNING: side not found for portal\n"); p->sidefound = true; p->side = bestside; }
/* * @brief Calculate the PHS (Potentially Hearable Set) * by ORing together all the PVS visible from a leaf */ static void CalcPHS(void) { uint32_t i, j, k, l, index; int32_t bitbyte; long *dest, *src; byte *scan; int32_t count; byte uncompressed[MAX_BSP_LEAFS / 8]; byte compressed[MAX_BSP_LEAFS / 8]; Com_Verbose("Building PHS...\n"); count = 0; for (i = 0; i < map_vis.portal_clusters; i++) { scan = map_vis.uncompressed + i * map_vis.leaf_bytes; memcpy(uncompressed, scan, map_vis.leaf_bytes); for (j = 0; j < map_vis.leaf_bytes; j++) { bitbyte = scan[j]; if (!bitbyte) continue; for (k = 0; k < 8; k++) { if (!(bitbyte & (1 << k))) continue; // OR this pvs row into the phs index = ((j << 3) + k); if (index >= map_vis.portal_clusters) Com_Error(ERR_FATAL, "Bad bit vector in PVS\n"); // pad bits should be 0 src = (long *) (map_vis.uncompressed + index * map_vis.leaf_bytes); for (l = 0; l < map_vis.leaf_longs; l++) ((long *) uncompressed)[l] |= src[l]; } } for (j = 0; j < map_vis.portal_clusters; j++) if (uncompressed[j >> 3] & (1 << (j & 7))) count++; // compress the bit string j = CompressVis(uncompressed, compressed); dest = (long *) map_vis.pointer; map_vis.pointer += j; if (map_vis.pointer > map_vis.end) Com_Error(ERR_FATAL, "Overflow\n"); d_vis->bit_offsets[i][DVIS_PHS] = (byte *) dest - map_vis.base; memcpy(dest, compressed, j); } if (map_vis.portal_clusters) Com_Print("Average clusters hearable: %i\n", count / map_vis.portal_clusters); else Com_Print("Average clusters hearable: 0\n"); }
/** * @brief */ void CalcTextureReflectivity(void) { char path[MAX_OS_PATH]; int32_t i, j, texels; uint32_t color[3]; SDL_Surface *surf; // always set index 0 even if no textures VectorSet(texture_reflectivity[0], 0.5, 0.5, 0.5); for (i = 0; i < bsp_file.num_texinfo; i++) { // see if an earlier texinfo already got the value for (j = 0; j < i; j++) { if (!g_strcmp0(bsp_file.texinfo[i].texture, bsp_file.texinfo[j].texture)) { VectorCopy(texture_reflectivity[j], texture_reflectivity[i]); break; } } if (j != i) { // earlier texinfo found, continue continue; } // load the image to calculate reflectivity g_snprintf(path, sizeof(path), "textures/%s", bsp_file.texinfo[i].texture); if (!Img_LoadImage(path, &surf)) { Com_Warn("Couldn't load %s\n", path); VectorSet(texture_reflectivity[i], 0.5, 0.5, 0.5); continue; } // calculate average color texels = surf->w * surf->h; color[0] = color[1] = color[2] = 0; for (j = 0; j < texels; j++) { const byte *pos = (byte *) surf->pixels + j * 4; color[0] += *pos++; // r color[1] += *pos++; // g color[2] += *pos++; // b } Com_Verbose("Loaded %s (%dx%d)\n", bsp_file.texinfo[i].texture, surf->w, surf->h); SDL_FreeSurface(surf); for (j = 0; j < 3; j++) { const vec_t r = color[j] / texels / 255.0; texture_reflectivity[i][j] = r; } } }
/** * @brief Acknowledge the server from the specified address. */ static void Ms_Ack(struct sockaddr_in *from) { ms_server_t *server = Ms_GetServer(from); if (server) { Com_Verbose("Ack from %s (%d)\n", stos(server), server->queued_pings); server->validated = true; server->queued_pings = 0; } else { Com_Warn("Ack from unregistered server %s\n", atos(from)); } }
/** * @brief Accept a "heartbeat" from the specified server address. */ static void Ms_Heartbeat(struct sockaddr_in *from) { ms_server_t *server = Ms_GetServer(from); if (server) { server->last_heartbeat = time(NULL); Com_Verbose("Heartbeat from %s\n", stos(server)); const void *ack = "\xFF\xFF\xFF\xFF" "ack"; sendto(ms_sock, ack, 7, 0, (struct sockaddr *) &server->addr, sizeof(server->addr)); } else { Ms_AddServer(from); } }
/* * @brief */ static void MakeTreePortals_r(node_t * node) { int32_t i; CalcNodeBounds(node); if (node->mins[0] >= node->maxs[0]) { Com_Verbose("WARNING: node without a volume\n"); } for (i = 0; i < 3; i++) { if (node->mins[i] < -8000 || node->maxs[i] > 8000) { Com_Verbose("WARNING: node with unbounded volume\n"); break; } } if (node->plane_num == PLANENUM_LEAF) return; MakeNodePortal(node); SplitNodePortals(node); MakeTreePortals_r(node->children[0]); MakeTreePortals_r(node->children[1]); }
/* * @brief */ void EmitAreaPortals(void) { int32_t i, j; d_bsp_area_portal_t *dp; if (c_areas > MAX_BSP_AREAS) Com_Error(ERR_FATAL, "MAX_BSP_AREAS\n"); d_bsp.num_areas = c_areas + 1; d_bsp.num_area_portals = 1; // leave 0 as an error for (i = 1; i <= c_areas; i++) { d_bsp.areas[i].first_area_portal = d_bsp.num_area_portals; for (j = 0; j < num_entities; j++) { const entity_t *e = &entities[j]; if (!e->area_portal_num) continue; dp = &d_bsp.area_portals[d_bsp.num_area_portals]; if (e->portal_areas[0] == i) { dp->portal_num = e->area_portal_num; dp->other_area = e->portal_areas[1]; d_bsp.num_area_portals++; } else if (e->portal_areas[1] == i) { dp->portal_num = e->area_portal_num; dp->other_area = e->portal_areas[0]; d_bsp.num_area_portals++; } } d_bsp.areas[i].num_area_portals = d_bsp.num_area_portals - d_bsp.areas[i].first_area_portal; } Com_Verbose("%5i num_areas\n", d_bsp.num_areas); Com_Verbose("%5i num_area_portals\n", d_bsp.num_area_portals); }
/* * @brief */ void EndBSPFile(void) { EmitBrushes(); EmitPlanes(); EmitAreaPortals(); UnparseEntities(); // now that the verts have been resolved, align the normals count d_bsp.num_normals = d_bsp.num_vertexes; // write the map Com_Verbose("Writing %s\n", bsp_name); WriteBSPFile(bsp_name); }
/* * ============= * MarkVisibleSides * * ============= */ void MarkVisibleSides(tree_t * tree, int32_t startbrush, int32_t endbrush) { int32_t i, j; Com_Verbose("--- MarkVisibleSides ---\n"); // clear all the visible flags for (i = startbrush; i < endbrush; i++) { map_brush_t *mb = &map_brushes[i]; const int32_t num_sides = mb->num_sides; for (j = 0; j < num_sides; j++) mb->original_sides[j].visible = false; } // set visible flags on the sides that are used by portals MarkVisibleSides_r(tree->head_node); }
/* * @brief */ static void FloodAreas_r(node_t * node) { portal_t *p; int32_t s; if (node->contents == CONTENTS_AREA_PORTAL) { // this node is part of an area portal const bsp_brush_t *b = node->brushes; entity_t *e = &entities[b->original->entity_num]; // if the current area has already touched this // portal, we are done if (e->portal_areas[0] == c_areas || e->portal_areas[1] == c_areas) return; // note the current area as bounding the portal if (e->portal_areas[1]) { Com_Verbose("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entity_num); return; } if (e->portal_areas[0]) e->portal_areas[1] = c_areas; else e->portal_areas[0] = c_areas; return; } if (node->area) return; // already got it node->area = c_areas; for (p = node->portals; p; p = p->next[s]) { s = (p->nodes[1] == node); // TODO: why is this commented out? #if 0 if(points->nodes[!s]->occupied) continue; #endif if (!Portal_EntityFlood(p)) continue; FloodAreas_r(p->nodes[!s]); } }
/* * @brief */ static void ProcessModels(void) { BeginBSPFile(); for (entity_num = 0; entity_num < num_entities; entity_num++) { if (!entities[entity_num].num_brushes) continue; Com_Verbose("############### model %i ###############\n", d_bsp.num_models); BeginModel(); if (entity_num == 0) ProcessWorldModel(); else ProcessSubModel(); EndModel(); } EndBSPFile(); }
/* * EndOfScript */ static boolean_t EndOfScript(boolean_t crossline){ if(!crossline) Com_Error(ERR_FATAL, "EndOfScript: Line %i is incomplete\n", scriptline); if(!strcmp(script->file_name, "memory buffer")){ endofscript = true; return false; } Z_Free(script->buffer); if(script == scriptstack + 1){ endofscript = true; return false; } script--; scriptline = script->line; Com_Verbose("returning to %s\n", script->file_name); return GetToken(crossline); }
static void ProcessBlock_Thread(int blocknum){ int xblock, yblock; vec3_t mins, maxs; bsp_brush_t *brushes; tree_t *tree; node_t *node; yblock = block_yl + blocknum / (block_xh - block_xl + 1); xblock = block_xl + blocknum % (block_xh - block_xl + 1); Com_Verbose("############### block %2i,%2i ###############\n", xblock, yblock); mins[0] = xblock * 1024; mins[1] = yblock * 1024; mins[2] = -MAX_WORLD_WIDTH; maxs[0] = (xblock + 1) * 1024; maxs[1] = (yblock + 1) * 1024; maxs[2] = MAX_WORLD_WIDTH; ThreadLock(); // the makelist and chopbrushes could be cached between the passes... brushes = MakeBspBrushList(brush_start, brush_end, mins, maxs); if(!brushes){ node = AllocNode(); node->plane_num = PLANENUM_LEAF; node->contents = CONTENTS_SOLID; block_nodes[xblock + 5][yblock + 5] = node; ThreadUnlock(); return; } if(!nocsg) brushes = ChopBrushes(brushes); tree = BrushBSP(brushes, mins, maxs); ThreadUnlock(); block_nodes[xblock + 5][yblock + 5] = tree->head_node; }
/** * @brief */ static _Bool EndOfScript(_Bool crossline) { if (!crossline) { Com_Error(ERROR_FATAL, "Line %i is incomplete\n", scriptline); } if (!g_strcmp0(script->file_name, "memory buffer")) { endofscript = true; return false; } Mem_Free(script->buffer); if (script == scriptstack + 1) { endofscript = true; return false; } script--; scriptline = script->line; Com_Verbose("returning to %s\n", script->file_name); return GetToken(crossline); }
/* * AddScriptToStack */ static void AddScriptToStack(const char *file_name){ int size; script++; if(script == &scriptstack[MAX_INCLUDES]) Com_Error(ERR_FATAL, "Script file exceeded MAX_INCLUDES\n"); strcpy(script->file_name, file_name); size = Fs_LoadFile(script->file_name, (void **)(char *)&script->buffer); if(size == -1) Com_Error(ERR_FATAL, "Could not load %s\n", script->file_name); Com_Verbose("Loading %s (%d bytes)\n", script->file_name, size); script->line = 1; script->script_p = script->buffer; script->end_p = script->buffer + size; }
/* * ============= * SetAreaPortalAreas_r * * Just decend the tree, and for each node that hasn't had an * area set, flood fill out from there * ============= */ static void SetAreaPortalAreas_r(node_t *node) { bsp_brush_t *b; entity_t *e; if (node->plane_num != PLANENUM_LEAF) { SetAreaPortalAreas_r(node->children[0]); SetAreaPortalAreas_r(node->children[1]); return; } if (node->contents == CONTENTS_AREA_PORTAL) { if (node->area) return; // already set b = node->brushes; e = &entities[b->original->entity_num]; node->area = e->portal_areas[0]; if (!e->portal_areas[1]) { Com_Verbose("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entity_num); return; } } }
/* * ProcessWorldModel */ static void ProcessWorldModel(void){ entity_t *e; tree_t *tree; boolean_t leaked; boolean_t optimize; e = &entities[entity_num]; brush_start = e->first_brush; brush_end = brush_start + e->num_brushes; leaked = false; // perform per-block operations if(block_xh * 1024 > map_maxs[0]) block_xh = floor(map_maxs[0] / 1024.0); if((block_xl + 1) * 1024 < map_mins[0]) block_xl = floor(map_mins[0] / 1024.0); if(block_yh * 1024 > map_maxs[1]) block_yh = floor(map_maxs[1] / 1024.0); if((block_yl + 1) * 1024 < map_mins[1]) block_yl = floor(map_mins[1] / 1024.0); if(block_xl < -4) block_xl = -4; if(block_yl < -4) block_yl = -4; if(block_xh > 3) block_xh = 3; if(block_yh > 3) block_yh = 3; for(optimize = false; optimize <= true; optimize++){ Com_Verbose("--------------------------------------------\n"); RunThreadsOn((block_xh - block_xl + 1) * (block_yh - block_yl + 1), !verbose, ProcessBlock_Thread); // build the division tree // oversizing the blocks guarantees that all the boundaries // will also get nodes. Com_Verbose("--------------------------------------------\n"); tree = AllocTree(); tree->head_node = BlockTree(block_xl - 1, block_yl - 1, block_xh + 1, block_yh + 1); tree->mins[0] = (block_xl) * 1024; tree->mins[1] = (block_yl) * 1024; tree->mins[2] = map_mins[2] - 8; tree->maxs[0] = (block_xh + 1) * 1024; tree->maxs[1] = (block_yh + 1) * 1024; tree->maxs[2] = map_maxs[2] + 8; // perform the global operations MakeTreePortals(tree); if(FloodEntities(tree)) FillOutside(tree->head_node); else { leaked = true; LeakFile(tree); if(leaktest){ Com_Error(ERR_FATAL, "--- MAP LEAKED, ABORTING LEAKTEST ---\n"); } Com_Verbose("**** leaked ****\n"); } MarkVisibleSides(tree, brush_start, brush_end); if(noopt || leaked) break; if(!optimize){ FreeTree(tree); } } FloodAreas(tree); MakeFaces(tree->head_node); FixTjuncs(tree->head_node); if(!noprune) PruneNodes(tree->head_node); WriteBSP(tree->head_node); if(!leaked) WritePortalFile(tree); FreeTree(tree); }
/** * @brief Initialize the OpenGL context, returning true on success, false on failure. */ void R_InitContext(void) { int32_t w, h; memset(&r_context, 0, sizeof(r_context)); if (SDL_WasInit(SDL_INIT_VIDEO) == 0) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { Com_Error(ERROR_FATAL, "%s\n", SDL_GetError()); } } uint32_t flags = SDL_WINDOW_OPENGL; if (r_allow_high_dpi->integer) { flags |= SDL_WINDOW_ALLOW_HIGHDPI; } if (r_fullscreen->integer) { w = Max(0, r_width->integer); h = Max(0, r_height->integer); if (r_width->integer == 0 && r_height->integer == 0) { SDL_DisplayMode best; SDL_GetDesktopDisplayMode(0, &best); w = best.w; h = best.h; } flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else { w = Max(0, r_windowed_width->integer); h = Max(0, r_windowed_height->integer); flags |= SDL_WINDOW_RESIZABLE; } Com_Print(" Trying %dx%d..\n", w, h); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); const int32_t s = Clamp(r_multisample->integer, 0, 8); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, s ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, s); if ((r_context.window = SDL_CreateWindow(PACKAGE_STRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flags)) == NULL) { Com_Error(ERROR_FATAL, "Failed to set video mode: %s\n", SDL_GetError()); } Com_Print(" Setting up OpenGL context..\n"); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); if ((r_context.context = SDL_GL_CreateContext(r_context.window)) == NULL) { Com_Error(ERROR_FATAL, "Failed to create OpenGL context: %s\n", SDL_GetError()); } int32_t attr[SDL_GL_CONTEXT_RELEASE_BEHAVIOR]; for (int32_t i = 0; i < SDL_GL_CONTEXT_RELEASE_BEHAVIOR; i++) { SDL_GL_GetAttribute(i, &attr[i]); } Com_Verbose(" Buffer Sizes: r %i g %i b %i a %i depth %i stencil %i framebuffer %i\n", attr[SDL_GL_RED_SIZE], attr[SDL_GL_GREEN_SIZE], attr[SDL_GL_BLUE_SIZE], attr[SDL_GL_ALPHA_SIZE], attr[SDL_GL_DEPTH_SIZE], attr[SDL_GL_STENCIL_SIZE], attr[SDL_GL_BUFFER_SIZE]); Com_Verbose(" Double-buffered: %s\n", attr[SDL_GL_DOUBLEBUFFER] ? "yes" : "no"); Com_Verbose(" Multisample: %i buffers, %i samples\n", attr[SDL_GL_MULTISAMPLEBUFFERS], attr[SDL_GL_MULTISAMPLESAMPLES]); Com_Verbose(" Version: %i.%i (%i flags, %i profile)\n", attr[SDL_GL_CONTEXT_MAJOR_VERSION], attr[SDL_GL_CONTEXT_MINOR_VERSION], attr[SDL_GL_CONTEXT_FLAGS], attr[SDL_GL_CONTEXT_PROFILE_MASK]); if (SDL_GL_SetSwapInterval(r_swap_interval->integer) == -1) { Com_Warn("Failed to set swap interval %d: %s\n", r_swap_interval->integer, SDL_GetError()); } if (SDL_SetWindowBrightness(r_context.window, r_gamma->value) == -1) { Com_Warn("Failed to set gamma %1.1f: %s\n", r_gamma->value, SDL_GetError()); } int32_t dw, dh; SDL_GL_GetDrawableSize(r_context.window, &dw, &dh); r_context.render_width = r_context.width = dw; r_context.render_height = r_context.height = dh; int32_t ww, wh; SDL_GetWindowSize(r_context.window, &ww, &wh); r_context.window_width = ww; r_context.window_height = wh; r_context.high_dpi = dw > ww && dh > wh; r_context.fullscreen = SDL_GetWindowFlags(r_context.window) & SDL_WINDOW_FULLSCREEN; R_SetWindowIcon(); }
/* * @brief */ static void LoadPortals(const char *filename) { uint32_t i; portal_t *p; leaf_t *l; char magic[80]; char *buffer, *s; int32_t len; int32_t num_points; winding_t *w; int32_t leaf_nums[2]; plane_t plane; if (Fs_Load(filename, (void **) &buffer) == -1) Com_Error(ERR_FATAL, "Could not open %s\n", filename); s = buffer; memset(&map_vis, 0, sizeof(map_vis)); if (sscanf(s, "%79s\n%u\n%u\n%n", magic, &map_vis.portal_clusters, &map_vis.num_portals, &len) != 3) Com_Error(ERR_FATAL, "Failed to read header: %s\n", filename); s += len; if (g_strcmp0(magic, PORTALFILE)) Com_Error(ERR_FATAL, "Not a portal file: %s\n", filename); Com_Verbose("Loading %4u portals, %4u clusters from %s...\n", map_vis.num_portals, map_vis.portal_clusters, filename); // these counts should take advantage of 64 bit systems automatically map_vis.leaf_bytes = ((map_vis.portal_clusters + 63) & ~63) >> 3; map_vis.leaf_longs = map_vis.leaf_bytes / sizeof(long); map_vis.portal_bytes = ((map_vis.num_portals * 2 + 63) & ~63) >> 3; map_vis.portal_longs = map_vis.portal_bytes / sizeof(long); // each file portal is split into two memory portals map_vis.portals = Mem_Malloc(2 * map_vis.num_portals * sizeof(portal_t)); // allocate the leafs map_vis.leafs = Mem_Malloc(map_vis.portal_clusters * sizeof(leaf_t)); map_vis.uncompressed_size = map_vis.portal_clusters * map_vis.leaf_bytes; map_vis.uncompressed = Mem_Malloc(map_vis.uncompressed_size); map_vis.base = map_vis.pointer = d_bsp.vis_data; d_vis->num_clusters = map_vis.portal_clusters; map_vis.pointer = (byte *) &d_vis->bit_offsets[map_vis.portal_clusters]; map_vis.end = map_vis.base + MAX_BSP_VISIBILITY; for (i = 0, p = map_vis.portals; i < map_vis.num_portals; i++) { int32_t j; if (sscanf(s, "%i %i %i %n", &num_points, &leaf_nums[0], &leaf_nums[1], &len) != 3) { Com_Error(ERR_FATAL, "Failed to read portal %i\n", i); } s += len; if (num_points > MAX_POINTS_ON_WINDING) { Com_Error(ERR_FATAL, "Portal %i has too many points\n", i); } if ((uint32_t) leaf_nums[0] > map_vis.portal_clusters || (uint32_t) leaf_nums[1] > map_vis.portal_clusters) { Com_Error(ERR_FATAL, "Portal %i has invalid leafs\n", i); } w = p->winding = NewWinding(num_points); w->original = true; w->num_points = num_points; for (j = 0; j < num_points; j++) { double v[3]; int32_t k; // scanf into double, then assign to vec_t // so we don't care what size vec_t is if (sscanf(s, "(%lf %lf %lf ) %n", &v[0], &v[1], &v[2], &len) != 3) Com_Error(ERR_FATAL, "Failed to read portal vertex definition %i:%i\n", i, j); s += len; for (k = 0; k < 3; k++) w->points[j][k] = v[k]; } if (sscanf(s, "\n%n", &len)) { s += len; } // calc plane PlaneFromWinding(w, &plane); // create forward portal l = &map_vis.leafs[leaf_nums[0]]; if (l->num_portals == MAX_PORTALS_ON_LEAF) Com_Error(ERR_FATAL, "MAX_PORTALS_ON_LEAF\n"); l->portals[l->num_portals] = p; l->num_portals++; p->winding = w; VectorSubtract(vec3_origin, plane.normal, p->plane.normal); p->plane.dist = -plane.dist; p->leaf = leaf_nums[1]; SetPortalSphere(p); p++; // create backwards portal l = &map_vis.leafs[leaf_nums[1]]; if (l->num_portals == MAX_PORTALS_ON_LEAF) Com_Error(ERR_FATAL, "MAX_PORTALS_ON_LEAF\n"); l->portals[l->num_portals] = p; l->num_portals++; p->winding = NewWinding(w->num_points); p->winding->num_points = w->num_points; for (j = 0; j < w->num_points; j++) { VectorCopy(w->points[w->num_points - 1 - j], p->winding->points[j]); } p->plane = plane; p->leaf = leaf_nums[0]; SetPortalSphere(p); p++; } Fs_Free(buffer); }
/* * @brief */ void LoadMapFile(const char *file_name) { int32_t subdivide; int32_t i; Com_Verbose("--- LoadMapFile ---\n"); LoadScriptFile(file_name); memset(map_brushes, 0, sizeof(map_brush_t) * MAX_BSP_BRUSHES); num_map_brushes = 0; memset(map_brush_sides, 0, sizeof(side_t) * MAX_BSP_SIDES); num_map_brush_sides = 0; memset(map_brush_textures, 0, sizeof(map_brush_texture_t) * MAX_BSP_SIDES); memset(map_planes, 0, sizeof(map_plane_t) * MAX_BSP_PLANES); num_map_planes = 0; num_entities = 0; while (ParseMapEntity()) { } subdivide = atoi(ValueForKey(&entities[0], "subdivide")); if (subdivide >= 256 && subdivide <= 2048) { Com_Verbose("Using subdivide %d from worldspawn.\n", subdivide); subdivide_size = subdivide; } ClearBounds(map_mins, map_maxs); for (i = 0; i < entities[0].num_brushes; i++) { if (map_brushes[i].mins[0] > MAX_WORLD_COORD) continue; // no valid points AddPointToBounds(map_brushes[i].mins, map_mins, map_maxs); AddPointToBounds(map_brushes[i].maxs, map_mins, map_maxs); } Com_Verbose("%5i brushes\n", num_map_brushes); Com_Verbose("%5i clip brushes\n", c_clip_brushes); Com_Verbose("%5i total sides\n", num_map_brush_sides); Com_Verbose("%5i box bevels\n", c_box_bevels); Com_Verbose("%5i edge bevels\n", c_edge_bevels); Com_Verbose("%5i entities\n", num_entities); Com_Verbose("%5i planes\n", num_map_planes); Com_Verbose("%5i area portals\n", c_area_portals); Com_Verbose("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0], map_mins[1], map_mins[2], map_maxs[0], map_maxs[1], map_maxs[2]); }
/* * @brief */ static void ParseBrush(entity_t *mapent) { map_brush_t *b; int32_t i, j, k; side_t *side, *s2; int32_t plane_num; map_brush_texture_t td; vec3_t planepts[3]; if (num_map_brushes == MAX_BSP_BRUSHES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSHES\n"); b = &map_brushes[num_map_brushes]; b->original_sides = &map_brush_sides[num_map_brush_sides]; b->entity_num = num_entities - 1; b->brush_num = num_map_brushes - mapent->first_brush; do { if (!GetToken(true)) break; if (!g_strcmp0(token, "}")) break; if (num_map_brush_sides == MAX_BSP_BRUSH_SIDES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSH_SIDES\n"); side = &map_brush_sides[num_map_brush_sides]; // read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) GetToken(true); if (g_strcmp0(token, "(")) Com_Error(ERR_FATAL, "Parsing brush\n"); for (j = 0; j < 3; j++) { GetToken(false); planepts[i][j] = atof(token); } GetToken(false); if (g_strcmp0(token, ")")) Com_Error(ERR_FATAL, "Parsing brush\n"); } memset(&td, 0, sizeof(td)); // read the texturedef GetToken(false); if (strlen(token) > sizeof(td.name) - 1) Com_Error(ERR_FATAL, "Texture name \"%s\" is too long.\n", token); g_strlcpy(td.name, token, sizeof(td.name)); GetToken(false); td.shift[0] = atoi(token); GetToken(false); td.shift[1] = atoi(token); GetToken(false); td.rotate = atoi(token); GetToken(false); td.scale[0] = atof(token); GetToken(false); td.scale[1] = atof(token); if (TokenAvailable()) { GetToken(false); side->contents = atoi(token); GetToken(false); side->surf = td.flags = atoi(token); GetToken(false); td.value = atoi(token); } else { side->contents = CONTENTS_SOLID; side->surf = td.flags = 0; td.value = 0; } // resolve implicit surface and contents flags SetImpliedFlags(side, td.name); // translucent objects are automatically classified as detail if (side->surf & (SURF_ALPHA_TEST | SURF_BLEND_33 | SURF_BLEND_66)) side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP | CONTENTS_MIST))) side->contents |= CONTENTS_SOLID; // hints and skips are never detail, and have no content if (side->surf & (SURF_HINT | SURF_SKIP)) { side->contents = 0; side->surf &= ~CONTENTS_DETAIL; } // find the plane number plane_num = PlaneFromPoints(planepts[0], planepts[1], planepts[2]); if (plane_num == -1) { Com_Verbose("Entity %i, Brush %i: plane with no normal\n", b->entity_num, b->brush_num); continue; } // see if the plane has been used already for (k = 0; k < b->num_sides; k++) { s2 = b->original_sides + k; if (s2->plane_num == plane_num) { Com_Verbose("Entity %i, Brush %i: duplicate plane\n", b->entity_num, b->brush_num); break; } if (s2->plane_num == (plane_num ^ 1)) { Com_Verbose("Entity %i, Brush %i: mirrored plane\n", b->entity_num, b->brush_num); break; } } if (k != b->num_sides) continue; // duplicated // keep this side side = b->original_sides + b->num_sides; side->plane_num = plane_num; side->texinfo = TexinfoForBrushTexture(&map_planes[plane_num], &td, vec3_origin); // save the td off in case there is an origin brush and we // have to recalculate the texinfo map_brush_textures[num_map_brush_sides] = td; num_map_brush_sides++; b->num_sides++; } while (true); // get the content for the entire brush b->contents = BrushContents(b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL)) { b->num_sides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & MASK_LIQUID)) { b->num_sides = 0; return; } // create windings for sides and bounds for brush MakeBrushWindings(b); // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) { c_clip_brushes++; for (i = 0; i < b->num_sides; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the plane_nums and texinfos will be adjusted for // the origin brush if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Com_Error(ERR_FATAL, "Entity %i, Brush %i: origin brushes not allowed in world\n", b->entity_num, b->brush_num); return; } VectorAdd(b->mins, b->maxs, origin); VectorScale(origin, 0.5, origin); sprintf(string, "%i %i %i", (int32_t)origin[0], (int32_t)origin[1], (int32_t)origin[2]); SetKeyValue(&entities[b->entity_num], "origin", string); VectorCopy(origin, entities[b->entity_num].origin); // don't keep this brush b->num_sides = 0; return; } AddBrushBevels(b); num_map_brushes++; mapent->num_brushes++; }