/** * @brief Add a callback of a function into a node event. There can be more than on listener. * @param[in,out] node The node to add the listener to. * @param[in] property The property of the node to add the listener to. * @param[in] functionNode The node of the listener callback. */ void UI_AddListener (uiNode_t *node, const value_t *property, const uiNode_t *functionNode) { uiAction_t *lastAction; uiAction_t *action; uiAction_t *value; if (node->dynamic) { Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node)); return; } if (functionNode->dynamic) { Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode)); return; } /* create the call action */ action = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0); value = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0); value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0); value->next = NULL; action->type = EA_LISTENER; action->d.nonTerminal.left = value; /** @todo It is a hack, we should remove that */ action->d.terminal.d2.constData = &functionNode->onClick; action->next = NULL; /* insert the action */ lastAction = *(uiAction_t**)((char*)node + property->ofs); if (lastAction) { while (lastAction->next) lastAction = lastAction->next; lastAction->next = action; } else *(uiAction_t**)((char*)node + property->ofs) = action; }
/** * @sa R_ModBeginLoading * @sa R_EndBuildingLightmaps */ void R_BeginBuildingLightmaps (void) { static qboolean gotAllocatedLightmaps = qfalse; if (gotAllocatedLightmaps) R_DisposeLightmaps(); gotAllocatedLightmaps = qtrue; /* users can tune lightmap size for their card */ r_lightmaps.size = r_maxlightmap->integer; r_lightmaps.allocated = (unsigned *)Mem_PoolAlloc(r_lightmaps.size * sizeof(unsigned), vid_lightPool, 0); r_lightmaps.sample_buffer = (byte *)Mem_PoolAlloc( r_lightmaps.size * r_lightmaps.size * sizeof(unsigned), vid_lightPool, 0); r_lightmaps.direction_buffer = (byte *)Mem_PoolAlloc( r_lightmaps.size * r_lightmaps.size * sizeof(unsigned), vid_lightPool, 0); r_lightmaps.lightmap_count = 0; r_lightmaps.deluxemap_count = 0; r_lightmaps.incomplete_atlas = qfalse; }
/** * @brief Parse all language definitions from the script files */ void CL_ParseLanguages (const char *name, const char **text) { const char *errhead = "CL_ParseLanguages: unexpected end of file (language "; const char *token; language_t *language = NULL; localeMapping_t *mapping = NULL; if (!*text) { Com_Printf("CL_ParseLanguages: language without body ignored (%s)\n", name); return; } token = Com_EParse(text, errhead, name); if (!*text || *token != '{') { Com_Printf("CL_ParseLanguages: language without body ignored (%s)\n", name); return; } do { /* get the name type */ token = Com_EParse(text, errhead, name); if (!*text || *token == '}') break; /* inner locale id definition */ if (*token == '{') { if (!language) { Com_Printf("CL_ParseLanguages: language: '%s' - found mappings with language string - ignore it\n", name); return; } do { /* get the locale mappings now type */ token = Com_EParse(text, errhead, name); /* end of locale mappings reached */ if (!*text || *token == '}') break; mapping = (localeMapping_t *)Mem_PoolAlloc(sizeof(*mapping), cl_genericPool, 0); mapping->localeMapping = Mem_PoolStrDup(token, cl_genericPool, 0); /* link it in */ mapping->next = language->localeMapping; language->localeMapping = mapping; } while (*text); language = NULL; } else { if (*token != '_') { Com_Printf("CL_ParseLanguages: language: '%s' - not marked translatable (%s) - ignore it\n", name, token); continue; } language = (language_t *)Mem_PoolAlloc(sizeof(*language), cl_genericPool, 0); language->localeID = Mem_PoolStrDup(name, cl_genericPool, 0); language->localeString = Mem_PoolStrDup(token + 1, cl_genericPool, 0); language->localeMapping = NULL; language->next = languageList; languageList = language; languageCount++; } } while (*text); }
/** * @brief Reorders all surfaces arrays for the specified model, grouping the surface * pointers by texture. This dramatically reduces glBindTexture calls. */ static void R_SortSurfacesArrays (const model_t *mod) { const mBspSurface_t *surf, *s; int i, ns; /* resolve the start surface and total surface count */ if (mod->type == mod_bsp) { /* world model */ s = mod->bsp.surfaces; ns = mod->bsp.numsurfaces; } else { /* submodels */ s = &mod->bsp.surfaces[mod->bsp.firstmodelsurface]; ns = mod->bsp.nummodelsurfaces; } OBJZERO(r_sorted_surfaces); /* allocate the per-texture surfaces arrays and determine counts */ for (i = 0, surf = s; i < ns; i++, surf++) { mBspSurfaces_t *surfs = r_sorted_surfaces[surf->texinfo->image->texnum]; if (!surfs) { /* allocate it */ surfs = (mBspSurfaces_t *)Mem_PoolAlloc(sizeof(*surfs), vid_modelPool, 0); r_sorted_surfaces[surf->texinfo->image->texnum] = surfs; } surfs->count++; } /* allocate the surfaces pointers based on counts */ for (i = 0; i < r_numImages; i++) { mBspSurfaces_t *surfs = r_sorted_surfaces[r_images[i].texnum]; if (surfs) { surfs->surfaces = (mBspSurface_t **)Mem_PoolAlloc(sizeof(mBspSurface_t *) * surfs->count, vid_modelPool, 0); surfs->count = 0; } } /* sort the model's surfaces arrays into the per-texture arrays */ for (i = 0; i < NUM_SURFACES_ARRAYS; i++) { if (mod->bsp.sorted_surfaces[i]->count) { R_SortSurfacesArrays_(mod->bsp.sorted_surfaces[i]); Com_DPrintf(DEBUG_RENDERER, "%i: #%i surfaces\n", i, mod->bsp.sorted_surfaces[i]->count); } } /* free the per-texture surfaces arrays */ for (i = 0; i < r_numImages; i++) { mBspSurfaces_t *surfs = r_sorted_surfaces[r_images[i].texnum]; if (surfs) { if (surfs->surfaces) Mem_Free(surfs->surfaces); Mem_Free(surfs); } } }
static void* _hash_alloc (memPool_t* pool, int n, size_t s) { #ifdef __DEBUG__ _num_allocs++; #endif // __DEBUG__ if (pool) return Mem_PoolAlloc (n * s, pool, 0); return Mem_Alloc(n * s); }
void UI_Init (void) { cvar_t *ui_hunkSize = Cvar_Get("ui_hunksize", "2", CVAR_ARCHIVE, "UI memory hunk size in megabytes"); #ifdef DEBUG ui_debug = Cvar_Get("debug_ui", "0", CVAR_DEVELOPER, "Prints node names for debugging purposes - valid values are 1 and 2"); #endif /* reset global UI structures */ OBJZERO(ui_global); ui_sounds = Cvar_Get("ui_sounds", "1", CVAR_ARCHIVE, "Activates UI sounds"); #ifdef DEBUG Cmd_AddCommand("debug_uimemory", UI_Memory_f, "Display info about UI memory allocation"); #endif ui_sysPool = Mem_CreatePool("Client: UI"); ui_dynStringPool = Mem_CreatePool("Client: Dynamic string for UI"); ui_dynPool = Mem_CreatePool("Client: Dynamic memory for UI"); /* 256kb */ ui_global.adataize = ui_hunkSize->integer * 1024 * 1024; ui_global.adata = (byte*)Mem_PoolAlloc(ui_global.adataize, ui_sysPool, 0); ui_global.curadata = ui_global.adata; UI_InitData(); UI_InitNodes(); UI_InitWindows(); UI_InitDraw(); UI_InitActions(); }
/** * @sa free_element */ static struct dbuffer_element * allocate_element (void) { struct dbuffer_element *e; TH_MutexLock(dbuf_lock); if (free_elements == 0) { struct dbuffer_element *newBuf = (struct dbuffer_element *)Mem_PoolAlloc(sizeof(struct dbuffer_element), com_genericPool, 0); newBuf->next = free_element_list; free_element_list = newBuf; free_elements++; allocated_elements++; } assert(free_element_list); e = free_element_list; free_element_list = free_element_list->next; assert(free_elements > 0); free_elements--; e->space = DBUFFER_ELEMENT_SIZE; e->len = 0; e->next = NULL; TH_MutexUnlock(dbuf_lock); return e; }
/** * @brief Allocate a dbuffer * @return the newly allocated buffer * Allocates a new dbuffer and initialises it to be empty */ struct dbuffer * new_dbuffer (void) { struct dbuffer *buf; TH_MutexLock(dbuf_lock); if (free_dbuffers == 0) { struct dbuffer *newBuf = (struct dbuffer *)Mem_PoolAlloc(sizeof(struct dbuffer), com_genericPool, 0); newBuf->next_free = free_dbuffer_list; free_dbuffer_list = newBuf; free_dbuffers++; allocated_dbuffers++; } assert(free_dbuffer_list); buf = free_dbuffer_list; free_dbuffer_list = free_dbuffer_list->next_free; assert(free_dbuffers > 0); free_dbuffers--; TH_MutexUnlock(dbuf_lock); buf->len = 0; buf->space = DBUFFER_ELEMENT_SIZE; buf->head = buf->tail = allocate_element(); buf->start = buf->end = &buf->head->data[0]; return buf; }
static void UI_MaterialEditorNewStage_f (void) { material_t *m; materialStage_t *s; int id; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <image index>\n", Cmd_Argv(0)); return; } id = atoi(Cmd_Argv(1)); if (id < 0 || id >= r_numImages) { Com_Printf("Given image index (%i) is out of bounds\n", id); return; } m = &R_GetImageAtIndex(id)->material; s = (materialStage_t *)Mem_PoolAlloc(sizeof(*s), vid_imagePool, 0); m->num_stages++; /* append the stage to the chain */ if (!m->stages) m->stages = s; else { materialStage_t *ss = m->stages; while (ss->next) ss = ss->next; ss->next = s; } UI_MaterialEditorUpdate(R_GetImageAtIndex(id), s); }
/** * @brief Register a property to a behaviour. * It should not be used in the code * @param behaviour Target behaviour * @param name Name of the property * @param type Type of the property * @param pos position of the attribute (which store property memory) into the node structure * @param size size of the attribute (which store property memory) into the node structure * @see UI_RegisterNodeProperty * @see UI_RegisterExtradataNodeProperty * @return A link to the node property */ const struct value_s *UI_RegisterNodePropertyPosSize_ (struct uiBehaviour_s *behaviour, const char* name, int type, size_t pos, size_t size) { value_t *property = UI_AllocHunkMemory(sizeof(value_t), STRUCT_MEMORY_ALIGN, qfalse); if (property == NULL) Com_Error(ERR_FATAL, "UI_RegisterNodePropertyPosSize_: UI memory hunk exceeded - increase the size"); if (type == V_STRING || type == V_LONGSTRING || type == V_CVAR_OR_LONGSTRING || V_CVAR_OR_STRING) { size = 0; } property->string = name; property->type = type; property->ofs = pos; property->size = size; if (behaviour->localProperties == NULL) { /* temporary memory allocation */ behaviour->localProperties = (const value_t **)Mem_PoolAlloc(sizeof(*behaviour->localProperties) * LOCAL_PROPERTY_SIZE, ui_sysPool, 0); } if (behaviour->propertyCount >= LOCAL_PROPERTY_SIZE-1) { Com_Error(ERR_FATAL, "UI_RegisterNodePropertyPosSize_: Property memory of behaviour %s is full.", behaviour->name); } behaviour->localProperties[behaviour->propertyCount++] = property; behaviour->localProperties[behaviour->propertyCount] = NULL; return property; }
/** * @brief Loads brush entities like func_door and func_breakable * @sa CMod_LoadSubmodels */ static void R_ModLoadSubmodels (const lump_t *l) { const dBspModel_t *in; mBspHeader_t *out; int i, j, count; in = (const dBspModel_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadSubmodels: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspHeader_t *)Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...submodels: %i\n", count); r_worldmodel->bsp.submodels = out; r_worldmodel->bsp.numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { /* spread the mins / maxs by a pixel */ for (j = 0; j < 3; j++) { out->mins[j] = LittleFloat(in->mins[j]) - 1.0f + (float)shift[j]; out->maxs[j] = LittleFloat(in->maxs[j]) + 1.0f + (float)shift[j]; out->origin[j] = LittleFloat(in->origin[j]) + (float)shift[j]; } out->radius = R_RadiusFromBounds(out->mins, out->maxs); out->headnode = LittleLong(in->headnode); out->firstface = LittleLong(in->firstface); out->numfaces = LittleLong(in->numfaces); } }
/** * @brief A brand new game has been started */ static void SV_InitGame (void) { /* allow next change after map change or restart */ sv_maxclients->flags |= CVAR_LATCH; /* get any latched variable changes (sv_maxclients, etc) */ Cvar_UpdateLatchedVars(); if (svs.serverMutex) Sys_Error("There is still a server running"); svs.clients = (client_t *)Mem_PoolAlloc(sizeof(client_t) * sv_maxclients->integer, sv_genericPool, 0); svs.serverMutex = TH_MutexCreate("server"); /* init network stuff */ if (sv_maxclients->integer > 1) { svs.initialized = SV_Start(NULL, port->string, &SV_ReadPacket); svs.netDatagramSocket = NET_DatagramSocketNew(NULL, Cvar_Get("port", DOUBLEQUOTE(PORT_SERVER), CVAR_NOSET, NULL)->string, &SV_DiscoveryCallback); } else svs.initialized = SV_Start(NULL, NULL, &SV_ReadPacket); SV_Heartbeat_f(); /* init game */ SV_InitGameProgs(); if (sv_maxclients->integer != 1 && (sv_dedicated->integer || sv_public->integer)) SV_SetMaster_f(); }
/** * @brief Only called once at startup, not for each game */ void SV_Init (void) { Com_Printf("\n------ server initialization -------\n"); sv_genericPool = Mem_CreatePool("Server: Generic"); OBJZERO(svs); sv = (serverInstanceGame_t *) Mem_PoolAlloc(sizeof(*sv), sv_genericPool, 0); SV_InitOperatorCommands(); rcon_password = Cvar_Get("rcon_password", "", 0, NULL); Cvar_Get("sv_cheats", "0", CVAR_SERVERINFO | CVAR_LATCH, NULL); Cvar_Get("sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_NOSET, NULL); /* this cvar will become a latched cvar when you start the server */ sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "Max. connected clients"); sv_hostname = Cvar_Get("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE, "The name of the server that is displayed in the serverlist"); sv_http_downloadserver = Cvar_Get("sv_http_downloadserver", "", CVAR_ARCHIVE, "URL to a location where clients can download game content over HTTP"); sv_enablemorale = Cvar_Get("sv_enablemorale", "1", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_LATCH, "Enable morale changes in multiplayer"); sv_maxsoldiersperteam = Cvar_Get("sv_maxsoldiersperteam", "4", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers per team (see sv_maxsoldiersperplayer and sv_teamplay)"); sv_maxsoldiersperplayer = Cvar_Get("sv_maxsoldiersperplayer", "8", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers each player can controll (see maxsoldiers and sv_teamplay)"); Cvar_SetCheckFunction("sv_maxsoldiersperplayer", SV_CheckMaxSoldiersPerPlayer); sv_dumpmapassembly = Cvar_Get("sv_dumpmapassembly", "0", CVAR_ARCHIVE, "Dump map assembly information to game console"); sv_threads = Cvar_Get("sv_threads", "1", CVAR_LATCH | CVAR_ARCHIVE, "Run the server threaded"); sv_public = Cvar_Get("sv_public", "1", 0, "Should heartbeats be send to the masterserver"); sv_reconnect_limit = Cvar_Get("sv_reconnect_limit", "3", CVAR_ARCHIVE, "Minimum seconds between connect messages"); SV_MapcycleInit(); }
/** * @brief Loads and registers a sound file for later use * @param[in] soundFile The name of the soundfile, relative to the sounds dir * @return The index of the loaded sample or 0 * @sa S_LoadSound */ int S_LoadSampleIdx (const char *soundFile) { Mix_Chunk *chunk; s_sample_t *sample; char name[MAX_QPATH]; unsigned hash; if (!s_env.initialized) return 0; Com_StripExtension(soundFile, name, sizeof(name)); sample = S_FindByName(name); if (sample) return sample->index; /* make sure the sound is loaded */ chunk = S_LoadSampleChunk(name); if (!chunk) return 0; /* couldn't load the sound's data */ hash = Com_HashKey(name, SAMPLE_HASH_SIZE); sample = (s_sample_t *)Mem_PoolAlloc(sizeof(*sample), cl_soundSysPool, 0); sample->name = Mem_PoolStrDup(name, cl_soundSysPool, 0); sample->chunk = chunk; sample->hashNext = sampleHash[hash]; sampleHash[hash] = sample; sampleIndex[++sampleIndexLast] = sample; sample->index = sampleIndexLast; return sample->index; }
static void R_ModLoadLeafs (const lump_t *l) { const dBspLeaf_t *in; mBspLeaf_t *out; int i, j, count; in = (const dBspLeaf_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadLeafs: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspLeaf_t *)Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...leafs: %i\n", count); r_worldmodel->bsp.leafs = out; r_worldmodel->bsp.numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort(in->mins[j]) + (float)shift[j]; out->minmaxs[3 + j] = LittleShort(in->maxs[j]) + (float)shift[j]; } out->contents = LittleLong(in->contentFlags); } }
/** * @brief Add a new command to the script interface * @param[in] cmd_name The name the command is available via script interface * @param[in] function The function pointer * @param[in] desc A usually(?) one-line description of what the cmd does * @sa Cmd_RemoveCommand */ void Cmd_AddCommand (const char *cmd_name, xcommand_t function, const char *desc) { cmd_function_t *cmd; unsigned int hash; if (!cmd_name || !cmd_name[0]) return; /* fail if the command is a variable name */ if (Cvar_GetString(cmd_name)[0]) { Com_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name); return; } /* fail if the command already exists */ hash = Com_HashKey(cmd_name, CMD_HASH_SIZE); for (cmd = cmd_functions_hash[hash]; cmd; cmd = cmd->hash_next) { if (Q_streq(cmd_name, cmd->name)) { Com_DPrintf(DEBUG_COMMANDS, "Cmd_AddCommand: %s already defined\n", cmd_name); return; } } cmd = (cmd_function_t *)Mem_PoolAlloc(sizeof(*cmd), com_cmdSysPool, 0); cmd->name = cmd_name; cmd->description = desc; cmd->function = function; cmd->completeParam = NULL; HASH_Add(cmd_functions_hash, cmd, hash); cmd->next = cmd_functions; cmd_functions = cmd; }
/** * @brief Creates a new command that executes a command string (possibly ; separated) */ static void Cmd_Alias_f (void) { cmd_alias_t *a; char cmd[MAX_STRING_CHARS]; size_t len; unsigned int hash; int i, c; const char *s; if (Cmd_Argc() == 1) { Com_Printf("Current alias commands:\n"); for (a = cmd_alias; a; a = a->next) Com_Printf("%s : %s\n", a->name, a->value); return; } s = Cmd_Argv(1); len = strlen(s); if (len == 0) return; if (len >= MAX_ALIAS_NAME) { Com_Printf("Alias name is too long\n"); return; } /* if the alias already exists, reuse it */ hash = Com_HashKey(s, ALIAS_HASH_SIZE); for (a = cmd_alias_hash[hash]; a; a = a->hash_next) { if (Q_streq(s, a->name)) { Mem_Free(a->value); break; } } if (!a) { a = (cmd_alias_t *)Mem_PoolAlloc(sizeof(*a), com_aliasSysPool, 0); a->next = cmd_alias; /* cmd_alias_hash should be null on the first run */ a->hash_next = cmd_alias_hash[hash]; cmd_alias_hash[hash] = a; cmd_alias = a; } Q_strncpyz(a->name, s, sizeof(a->name)); /* copy the rest of the command line */ cmd[0] = 0; /* start out with a null string */ c = Cmd_Argc(); for (i = 2; i < c; i++) { Q_strcat(cmd, Cmd_Argv(i), sizeof(cmd)); if (i != (c - 1)) Q_strcat(cmd, " ", sizeof(cmd)); } if (Q_streq(Cmd_Argv(0), "aliasa")) a->archive = qtrue; a->value = Mem_PoolStrDup(cmd, com_aliasSysPool, 0); }
/** * @brief Allocates data arrays for animated models. Only called once at loading time. */ void R_ModLoadArrayData (mAliasModel_t *mod, mAliasMesh_t *mesh, qboolean loadNormals) { const int v = mesh->num_tris * 3 * 3; const int t = mesh->num_tris * 3 * 4; const int st = mesh->num_tris * 3 * 2; assert(mesh->verts == NULL); assert(mesh->texcoords == NULL); assert(mesh->normals == NULL); assert(mesh->tangents == NULL); assert(mesh->next_verts == NULL); assert(mesh->next_normals == NULL); assert(mesh->next_tangents == NULL); mesh->verts = (float *)Mem_PoolAlloc(sizeof(float) * v, vid_modelPool, 0); mesh->normals = (float *)Mem_PoolAlloc(sizeof(float) * v, vid_modelPool, 0); mesh->tangents = (float *)Mem_PoolAlloc(sizeof(float) * t, vid_modelPool, 0); mesh->texcoords = (float *)Mem_PoolAlloc(sizeof(float) * st, vid_modelPool, 0); if (mod->num_frames == 1) { R_FillArrayData(mod, mesh, 0.0, 0, 0, loadNormals); } else { mesh->next_verts = (float *)Mem_PoolAlloc(sizeof(float) * v, vid_modelPool, 0); mesh->next_normals = (float *)Mem_PoolAlloc(sizeof(float) * v, vid_modelPool, 0); mesh->next_tangents = (float *)Mem_PoolAlloc(sizeof(float) * t, vid_modelPool, 0); mod->curFrame = -1; mod->oldFrame = -1; } }
void R_ModLoadAnims (mAliasModel_t *mod, const char *buffer) { const char *text, *token; mAliasAnim_t *anim; int n; for (n = 0, text = buffer; text; n++) Com_Parse(&text); n /= 4; if (n > MAX_ANIMS) n = MAX_ANIMS; mod->animdata = (mAliasAnim_t *) Mem_PoolAlloc(n * sizeof(*mod->animdata), vid_modelPool, 0); anim = mod->animdata; text = buffer; mod->num_anims = 0; do { /* get the name */ token = Com_Parse(&text); if (!text) break; Q_strncpyz(anim->name, token, sizeof(anim->name)); /* get the start */ token = Com_Parse(&text); if (!text) break; anim->from = atoi(token); if (anim->from < 0) Com_Error(ERR_FATAL, "R_ModLoadAnims: negative start frame for %s", mod->animname); else if (anim->from > mod->num_frames) Com_Error(ERR_FATAL, "R_ModLoadAnims: start frame is higher than models frame count (%i) (model: %s)", mod->num_frames, mod->animname); /* get the end */ token = Com_Parse(&text); if (!text) break; anim->to = atoi(token); if (anim->to < 0) Com_Error(ERR_FATAL, "R_ModLoadAnims: negative start frame for %s", mod->animname); else if (anim->to > mod->num_frames) Com_Error(ERR_FATAL, "R_ModLoadAnims: end frame is higher than models frame count (%i) (model: %s)", mod->num_frames, mod->animname); /* get the fps */ token = Com_Parse(&text); if (!text) break; anim->time = (atof(token) > 0.01) ? (1000.0 / atof(token)) : (1000.0 / 0.01); /* add it */ mod->num_anims++; anim++; } while (mod->num_anims < MAX_ANIMS); }
/** * @brief Load the lightmap data */ static void R_ModLoadLighting (const lump_t *l) { /* map has no lightmap */ if (l->filelen == 0) return; r_worldmodel->bsp.lightdata = (byte *)Mem_PoolAlloc(l->filelen, vid_lightPool, 0); r_worldmodel->bsp.lightquant = *(const byte *) (mod_base + l->fileofs); memcpy(r_worldmodel->bsp.lightdata, mod_base + l->fileofs, l->filelen); }
/** * @sa TR_BuildTracingNode_r * @sa R_RecurseSetParent */ static void R_ModLoadNodes (const lump_t *l) { int i, j, count; const dBspNode_t *in; mBspNode_t *out; mBspNode_t *parent = NULL; in = (const dBspNode_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadNodes: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspNode_t *)Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...nodes: %i\n", count); r_worldmodel->bsp.nodes = out; r_worldmodel->bsp.numnodes = count; for (i = 0; i < count; i++, in++, out++) { const int p = LittleLong(in->planenum); /* skip special pathfinding nodes - they have a negative index */ if (p == PLANENUM_LEAF) { /* in case of "special" pathfinding nodes (they don't have a plane) * we have to set this to NULL */ out->plane = NULL; out->contents = CONTENTS_PATHFINDING_NODE; parent = NULL; } else { out->plane = r_worldmodel->bsp.planes + p; /* differentiate from leafs */ out->contents = CONTENTS_NODE; parent = out; } for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort(in->mins[j]) + (float)shift[j]; out->minmaxs[3 + j] = LittleShort(in->maxs[j]) + (float)shift[j]; } out->firstsurface = LittleShort(in->firstface); out->numsurfaces = LittleShort(in->numfaces); for (j = 0; j < 2; j++) { const int p2 = LittleLong(in->children[j]); if (p2 > LEAFNODE) { assert(p2 < r_worldmodel->bsp.numnodes); out->children[j] = r_worldmodel->bsp.nodes + p2; } else { assert((LEAFNODE - p2) < r_worldmodel->bsp.numleafs); out->children[j] = (mBspNode_t *) (r_worldmodel->bsp.leafs + (LEAFNODE - p2)); } out->children[j]->parent = parent; } } }
/** * @brief Set a new action to a @c uiAction_t pointer * @param[in,out] action Allocated action * @param[in] type Only @c EA_CMD is supported * @param[in] data The data for this action - in case of @c EA_CMD this is the commandline * @note You first have to free existing node actions - only free those that are * not static in @c ui_global.actions array * @todo we should create a function to free the memory. We can use a tag in the Mem_PoolAlloc * calls and use use Mem_FreeTag. */ void UI_PoolAllocAction (uiAction_t** action, int type, const void *data) { if (*action) Com_Error(ERR_FATAL, "There is already an action assigned"); *action = (uiAction_t *)Mem_PoolAlloc(sizeof(**action), ui_sysPool, 0); (*action)->type = type; switch (type) { case EA_CMD: (*action)->d.terminal.d1.constString = Mem_PoolStrDup((const char *)data, ui_sysPool, 0); break; default: Com_Error(ERR_FATAL, "Action type %i is not yet implemented", type); } }
/** * @brief Allocate a node into the UI memory (do notr call behaviour->new) * @note It's not a dynamic memory allocation. Please only use it at the loading time * @todo Assert out when we are not in parsing/loading stage * @param[in] name Name of the new node, else NULL if we dont want to edit it. * @param[in] type Name of the node behavior * @param[in] isDynamic Allocate a node in static or dynamic memory */ static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, qboolean isDynamic) { uiNode_t* node; uiBehaviour_t *behaviour; int nodeSize; behaviour = UI_GetNodeBehaviour(type); if (behaviour == NULL) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type); nodeSize = sizeof(*node) + behaviour->extraDataSize; if (!isDynamic) { if (ui_global.curadata + nodeSize > ui_global.adata + ui_global.adataize) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: No more memory to allocate a new node"); /** @todo fix this hard coded '8' value */ ui_global.curadata = ALIGN_PTR(ui_global.curadata, 8); node = (uiNode_t*) ui_global.curadata; ui_global.curadata += nodeSize; ui_global.numNodes++; memset(node, 0, nodeSize); } else { node = (uiNode_t*)Mem_PoolAlloc(nodeSize, ui_dynPool, 0); memset(node, 0, nodeSize); node->dynamic = qtrue; } node->behaviour = behaviour; #ifdef DEBUG node->behaviour->count++; #endif if (node->behaviour->isAbstract) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type); if (name != NULL) { Q_strncpyz(node->name, name, sizeof(node->name)); if (strlen(node->name) != strlen(name)) Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name); } /* initialize default properties */ if (node->behaviour->loading) node->behaviour->loading(node); return node; }
/** * @brief Allocate a node into the UI memory (do not call behaviour->new) * @note It's not a dynamic memory allocation. Please only use it at the loading time * @todo Assert out when we are not in parsing/loading stage * @param[in] name Name of the new node, else nullptr if we don't want to edit it. * @param[in] type Name of the node behavior * @param[in] isDynamic Allocate a node in static or dynamic memory */ static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, bool isDynamic) { uiNode_t* node; uiBehaviour_t *behaviour; int nodeSize; behaviour = UI_GetNodeBehaviour(type); if (behaviour == nullptr) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type); nodeSize = sizeof(*node) + behaviour->extraDataSize; if (!isDynamic) { void *memory = UI_AllocHunkMemory(nodeSize, STRUCT_MEMORY_ALIGN, true); if (memory == nullptr) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: No more memory to allocate a new node - increase the cvar ui_hunksize"); node = static_cast<uiNode_t*>(memory); ui_global.numNodes++; } else { node = static_cast<uiNode_t*>(Mem_PoolAlloc(nodeSize, ui_dynPool, 0)); node->dynamic = true; } node->behaviour = behaviour; #ifdef DEBUG UI_Node_DebugCountWidget(node, 1); #endif if (UI_Node_IsAbstract(node)) Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type); if (name != nullptr) { Q_strncpyz(node->name, name, sizeof(node->name)); if (strlen(node->name) != strlen(name)) Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name); } /* initialize default properties */ UI_Node_Loading(node); return node; }
static void R_ModLoadEdges (const lump_t *l) { const dBspEdge_t *in; mBspEdge_t *out; int i, count; in = (const dBspEdge_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadEdges: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspEdge_t *)Mem_PoolAlloc((count + 1) * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...edges: %i\n", count); r_worldmodel->bsp.edges = out; r_worldmodel->bsp.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]); } }
static void R_ModLoadSurfedges (const lump_t *l) { int i, count; const int *in; int *out; in = (const int *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadSurfedges: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); if (count < 1 || count >= MAX_MAP_SURFEDGES) Com_Error(ERR_DROP, "R_ModLoadSurfedges: bad surfedges count in %s: %i", r_worldmodel->name, count); out = (int *) Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...surface edges: %i\n", count); r_worldmodel->bsp.surfedges = out; r_worldmodel->bsp.numsurfedges = count; for (i = 0; i < count; i++) out[i] = LittleLong(in[i]); }
static void R_ModLoadVertexes (const lump_t *l) { const dBspVertex_t *in; mBspVertex_t *out; int i, count; in = (const dBspVertex_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadVertexes: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspVertex_t *)Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...verts: %i\n", count); r_worldmodel->bsp.vertexes = out; r_worldmodel->bsp.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]); } }
/** * @sa CMod_LoadPlanes */ static void R_ModLoadPlanes (const lump_t *l) { int i, j; cBspPlane_t *out; const dBspPlane_t *in; int count; in = (const dBspPlane_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadPlanes: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (cBspPlane_t *)Mem_PoolAlloc(count * 2 * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...planes: %i\n", count); r_worldmodel->bsp.planes = out; r_worldmodel->bsp.numplanes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) out->normal[j] = LittleFloat(in->normal[j]); out->dist = LittleFloat(in->dist); out->type = LittleLong(in->type); } }
/** * @sa CP_StartMissionMap */ static void R_ModLoadTexinfo (const lump_t *l) { const dBspTexinfo_t *in; mBspTexInfo_t *out; int i, j, count; char name[MAX_QPATH]; in = (const dBspTexinfo_t *) (mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Com_Error(ERR_DROP, "R_ModLoadTexinfo: funny lump size in %s", r_worldmodel->name); count = l->filelen / sizeof(*in); out = (mBspTexInfo_t *)Mem_PoolAlloc(count * sizeof(*out), vid_modelPool, 0); Com_DPrintf(DEBUG_RENDERER, "...texinfo: %i\n", count); r_worldmodel->bsp.texinfo = out; r_worldmodel->bsp.numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->uv[j] = LittleFloat(in->vecs[0][j]); out->vv[j] = LittleFloat(in->vecs[1][j]); } out->u_offset = LittleFloat(in->vecs[0][3]); out->v_offset = LittleFloat(in->vecs[1][3]); out->flags = LittleLong(in->surfaceFlags); /* exchange the textures with the ones that are needed for base assembly */ if (refdef.mapZone && strstr(in->texture, "tex_terrain/dummy")) Com_sprintf(name, sizeof(name), "textures/tex_terrain/%s", refdef.mapZone); else Com_sprintf(name, sizeof(name), "textures/%s", in->texture); out->image = R_FindImage(name, it_world); } }
/** * @brief Calculates normals and tangents for all frames and does vertex merging based on smoothness * @param mesh The mesh to calculate normals for * @param nFrames How many frames the mesh has * @param smoothness How aggressively should normals be smoothed; value is compared with dotproduct of vectors to decide if they should be merged * @sa R_ModCalcNormalsAndTangents */ void R_ModCalcUniqueNormalsAndTangents (mAliasMesh_t *mesh, int nFrames, float smoothness) { int i, j; vec3_t triangleNormals[MAX_ALIAS_TRIS]; vec3_t triangleTangents[MAX_ALIAS_TRIS]; vec3_t triangleBitangents[MAX_ALIAS_TRIS]; const mAliasVertex_t *vertexes = mesh->vertexes; mAliasCoord_t *stcoords = mesh->stcoords; mAliasVertex_t *newVertexes; mAliasComplexVertex_t tmpVertexes[MAX_ALIAS_VERTS]; vec3_t tmpBitangents[MAX_ALIAS_VERTS]; mAliasCoord_t *newStcoords; const int numIndexes = mesh->num_tris * 3; const int32_t *indexArray = mesh->indexes; int32_t *newIndexArray; int indRemap[MAX_ALIAS_VERTS]; int sharedTris[MAX_ALIAS_VERTS]; int numVerts = 0; newIndexArray = (int32_t *)Mem_PoolAlloc(sizeof(int32_t) * numIndexes, vid_modelPool, 0); /* calculate per-triangle surface normals */ for (i = 0, j = 0; i < numIndexes; i += 3, j++) { vec3_t dir1, dir2; vec2_t dir1uv, dir2uv; /* calculate two mostly perpendicular edge directions */ VectorSubtract(vertexes[indexArray[i + 0]].point, vertexes[indexArray[i + 1]].point, dir1); VectorSubtract(vertexes[indexArray[i + 2]].point, vertexes[indexArray[i + 1]].point, dir2); Vector2Subtract(stcoords[indexArray[i + 0]], stcoords[indexArray[i + 1]], dir1uv); Vector2Subtract(stcoords[indexArray[i + 2]], stcoords[indexArray[i + 1]], dir2uv); /* we have two edge directions, we can calculate a third vector from * them, which is the direction of the surface normal */ CrossProduct(dir1, dir2, triangleNormals[j]); /* then we use the texture coordinates to calculate a tangent space */ if ((dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]) != 0.0) { const float frac = 1.0 / (dir1uv[1] * dir2uv[0] - dir1uv[0] * dir2uv[1]); vec3_t tmp1, tmp2; /* calculate tangent */ VectorMul(-1.0 * dir2uv[1] * frac, dir1, tmp1); VectorMul(dir1uv[1] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleTangents[j]); /* calculate bitangent */ VectorMul(-1.0 * dir2uv[0] * frac, dir1, tmp1); VectorMul(dir1uv[0] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleBitangents[j]); } else { const float frac = 1.0 / (0.00001); vec3_t tmp1, tmp2; /* calculate tangent */ VectorMul(-1.0 * dir2uv[1] * frac, dir1, tmp1); VectorMul(dir1uv[1] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleTangents[j]); /* calculate bitangent */ VectorMul(-1.0 * dir2uv[0] * frac, dir1, tmp1); VectorMul(dir1uv[0] * frac, dir2, tmp2); VectorAdd(tmp1, tmp2, triangleBitangents[j]); } /* normalize */ VectorNormalizeFast(triangleNormals[j]); VectorNormalizeFast(triangleTangents[j]); VectorNormalizeFast(triangleBitangents[j]); Orthogonalize(triangleTangents[j], triangleBitangents[j]); } /* do smoothing */ for (i = 0; i < numIndexes; i++) { const int idx = (i - i % 3) / 3; VectorCopy(triangleNormals[idx], tmpVertexes[i].normal); VectorCopy(triangleTangents[idx], tmpVertexes[i].tangent); VectorCopy(triangleBitangents[idx], tmpBitangents[i]); for (j = 0; j < numIndexes; j++) { const int idx2 = (j - j % 3) / 3; /* don't add a vertex with itself */ if (j == i) continue; /* only average normals if vertices have the same position * and the normals aren't too far apart to start with */ if (VectorEqual(vertexes[indexArray[i]].point, vertexes[indexArray[j]].point) && DotProduct(triangleNormals[idx], triangleNormals[idx2]) > smoothness) { /* average the normals */ VectorAdd(tmpVertexes[i].normal, triangleNormals[idx2], tmpVertexes[i].normal); /* if the tangents match as well, average them too. * Note that having matching normals without matching tangents happens * when the order of vertices in two triangles sharing the vertex * in question is different. This happens quite frequently if the * modeler does not go out of their way to avoid it. */ if (Vector2Equal(stcoords[indexArray[i]], stcoords[indexArray[j]]) && DotProduct(triangleTangents[idx], triangleTangents[idx2]) > smoothness && DotProduct(triangleBitangents[idx], triangleBitangents[idx2]) > smoothness) { /* average the tangents */ VectorAdd(tmpVertexes[i].tangent, triangleTangents[idx2], tmpVertexes[i].tangent); VectorAdd(tmpBitangents[i], triangleBitangents[idx2], tmpBitangents[i]); } } } VectorNormalizeFast(tmpVertexes[i].normal); VectorNormalizeFast(tmpVertexes[i].tangent); VectorNormalizeFast(tmpBitangents[i]); } /* assume all vertices are unique until proven otherwise */ for (i = 0; i < numIndexes; i++) indRemap[i] = -1; /* merge vertices that have become identical */ for (i = 0; i < numIndexes; i++) { vec3_t n, b, t, v; if (indRemap[i] != -1) continue; for (j = i + 1; j < numIndexes; j++) { if (Vector2Equal(stcoords[indexArray[i]], stcoords[indexArray[j]]) && VectorEqual(vertexes[indexArray[i]].point, vertexes[indexArray[j]].point) && (DotProduct(tmpVertexes[i].normal, tmpVertexes[j].normal) > smoothness) && (DotProduct(tmpVertexes[i].tangent, tmpVertexes[j].tangent) > smoothness)) { indRemap[j] = i; newIndexArray[j] = numVerts; } } VectorCopy(tmpVertexes[i].normal, n); VectorCopy(tmpVertexes[i].tangent, t); VectorCopy(tmpBitangents[i], b); /* normalization here does shared-vertex smoothing */ VectorNormalizeFast(n); VectorNormalizeFast(t); VectorNormalizeFast(b); /* Grahm-Schmidt orthogonalization */ VectorMul(DotProduct(t, n), n, v); VectorSubtract(t, v, t); VectorNormalizeFast(t); /* calculate handedness */ CrossProduct(n, t, v); tmpVertexes[i].tangent[3] = (DotProduct(v, b) < 0.0) ? -1.0 : 1.0; VectorCopy(n, tmpVertexes[i].normal); VectorCopy(t, tmpVertexes[i].tangent); newIndexArray[i] = numVerts++; indRemap[i] = i; } for (i = 0; i < numVerts; i++) sharedTris[i] = 0; for (i = 0; i < numIndexes; i++) sharedTris[newIndexArray[i]]++; /* set up reverse-index that maps Vertex objects to a list of triangle verts */ mesh->revIndexes = (mIndexList_t *)Mem_PoolAlloc(sizeof(mIndexList_t) * numVerts, vid_modelPool, 0); for (i = 0; i < numVerts; i++) { mesh->revIndexes[i].length = 0; mesh->revIndexes[i].list = (int32_t *)Mem_PoolAlloc(sizeof(int32_t) * sharedTris[i], vid_modelPool, 0); } /* merge identical vertexes, storing only unique ones */ newVertexes = (mAliasVertex_t *)Mem_PoolAlloc(sizeof(mAliasVertex_t) * numVerts * nFrames, vid_modelPool, 0); newStcoords = (mAliasCoord_t *)Mem_PoolAlloc(sizeof(mAliasCoord_t) * numVerts, vid_modelPool, 0); for (i = 0; i < numIndexes; i++) { const int idx = indexArray[indRemap[i]]; const int idx2 = newIndexArray[i]; /* add vertex to new vertex array */ VectorCopy(vertexes[idx].point, newVertexes[idx2].point); Vector2Copy(stcoords[idx], newStcoords[idx2]); mesh->revIndexes[idx2].list[mesh->revIndexes[idx2].length++] = i; } /* copy over the points from successive frames */ for (i = 1; i < nFrames; i++) { for (j = 0; j < numIndexes; j++) { const int idx = indexArray[indRemap[j]] + (mesh->num_verts * i); const int idx2 = newIndexArray[j] + (numVerts * i); VectorCopy(vertexes[idx].point, newVertexes[idx2].point); } } /* copy new arrays back into original mesh */ Mem_Free(mesh->stcoords); Mem_Free(mesh->indexes); Mem_Free(mesh->vertexes); mesh->num_verts = numVerts; mesh->vertexes = newVertexes; mesh->stcoords = newStcoords; mesh->indexes = newIndexArray; }