/** * @brief Generates a single texture coordinate for the specified stage and vertex. */ static void R_StageTexCoord (const materialStage_t *stage, const vec3_t v, const vec2_t in, vec2_t out) { if (stage->flags & STAGE_ENVMAP) { /* generate texcoords */ vec3_t tmp; VectorSubtract(v, refdef.viewOrigin, tmp); VectorNormalizeFast(tmp); Vector2Copy(tmp, out); } else { /* or use the ones we were given */ Vector2Copy(in, out); } }
static bool G_InventoryPlaceItemAdjacent (edict_t *ent) { vec2_t oldPos; /* if we have to place it to adjacent */ edict_t *floorAdjacent; int i; Vector2Copy(ent->pos, oldPos); floorAdjacent = NULL; for (i = 0; i < DIRECTIONS; i++) { /** @todo Check whether movement is possible here - otherwise don't use this field */ /* extend pos with the direction vectors */ /** @todo Don't know why the adjacent stuff has been disabled, but if it was buggy, it's probably */ /** because the third ent->pos in the next line should be pos[1] ?!. (Duke, 13.1.11) */ Vector2Set(ent->pos, ent->pos[0] + dvecs[i][0], ent->pos[0] + dvecs[i][1]); /* now try to get a floor entity for that new location */ floorAdjacent = G_GetFloorItems(ent); if (!floorAdjacent) { floorAdjacent = G_SpawnFloor(ent->pos); } else { /* destroy this edict (send this event to all clients that see the edict) */ G_EventPerish(floorAdjacent); G_VisFlagsReset(floorAdjacent); } INVSH_FindSpace(&floorAdjacent->i, &ic->item, INVDEF(gi.csi->idFloor), &x, &y, ic); if (x != NONE) { ic->x = x; ic->y = y; ic->next = FLOOR(floorAdjacent); FLOOR(floorAdjacent) = ic; break; } /* restore original pos */ Vector2Copy(oldPos, ent->pos); } /* added to adjacent pos? */ if (i < DIRECTIONS) { /* restore original pos - if no free space, this was done * already in the for loop */ Vector2Copy(oldPos, ent->pos); return false; } if (floorAdjacent) G_CheckVis(floorAdjacent, true); return true; }
/** * @brief Intercept mission ends: UFO leave earth. * @param[in] mission Pointer to the mission * @param[in] destroyed true if the UFO actually destroyed the installation, false else * @note Intercept mission -- Stage 3 */ void CP_InterceptMissionLeave (mission_t* mission, bool destroyed) { installation_t* installation; assert(mission->ufo); mission->stage = STAGE_RETURN_TO_ORBIT; /* if the mission was an attack of an installation, destroy it */ installation = mission->data.installation; if (installation) { vec3_t missionPos; Vector2Copy(mission->pos, missionPos); missionPos[2] = installation->pos[2]; if (destroyed && VectorCompareEps(missionPos, installation->pos, UFO_EPSILON)) INS_DestroyInstallation(installation); } CP_MissionDisableTimeLimit(mission); UFO_SetRandomDest(mission->ufo); CP_MissionRemoveFromGeoscape(mission); /* Display UFO on geoscape if it is detected */ mission->ufo->landed = false; }
/** * @brief Set base attack mission, and go to base position. * @note Base attack mission -- Stage 1 */ static void CP_BaseAttackGoToBase (mission_t *mission) { base_t *base; mission->stage = STAGE_MISSION_GOTO; base = CP_BaseAttackChooseBase(); if (!base) { Com_Printf("CP_BaseAttackGoToBase: no base found\n"); CP_MissionRemove(mission); return; } mission->data.base = base; mission->mapDef = Com_GetMapDefinitionByID("baseattack"); if (!mission->mapDef) { CP_MissionRemove(mission); Com_Error(ERR_DROP, "Could not find mapdef baseattack"); return; } Vector2Copy(base->pos, mission->pos); mission->posAssigned = qtrue; Com_sprintf(mission->location, sizeof(mission->location), "%s", base->name); if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Build a new installation * @param[in] installationTemplate Template pointer * @param[in] pos Position on Globe to build at * @param[in] name The name of the installation - might already be in utf-8 */ installation_t* INS_Build (const installationTemplate_t *installationTemplate, const vec2_t pos, const char *name) { installation_t installation; const int newInstallationAlienInterest = 1.0f; OBJZERO(installation); Vector2Copy(pos, installation.pos); Q_strncpyz(installation.name, name, sizeof(installation.name)); installation.idx = ccs.campaignStats.installationsBuilt; installation.installationStatus = INSTALLATION_UNDER_CONSTRUCTION; installation.installationTemplate = installationTemplate; installation.buildStart = ccs.date.day; /* a new installation is not discovered (yet) */ installation.alienInterest = newInstallationAlienInterest; /* intialise hit points */ installation.installationDamage = installation.installationTemplate->maxDamage; /* Reset Radar */ RADAR_Initialise(&(installation.radar), 0.0f, 0.0f, 0.0f, qfalse); ccs.campaignStats.installationsBuilt++; return (installation_t*)(LIST_Add(&ccs.installations, (void*)&installation, sizeof(installation)))->data; }
/** * @brief Give a random position to the given UFO * @param[in] ufocraft Pointer to the UFO which position will be changed. * @sa UFO_SetRandomDest */ static void UFO_SetRandomPos (aircraft_t* ufocraft) { vec2_t pos; CP_GetRandomPosOnGeoscape(pos, qfalse); Vector2Copy(pos, ufocraft->pos); }
static void SCP_CampaignAddMission (setState_t *set) { actMis_t *mis; mission_t * mission; const nation_t *nation; /* add mission */ if (scd->numActiveMissions >= MAX_ACTMISSIONS) { return; } mis = &scd->activeMissions[scd->numActiveMissions]; OBJZERO(*mis); /* set relevant info */ mis->def = SCP_GetMission(set); if (mis->def == NULL) { return; } mis->cause = set; if (set->def->expire.day) mis->expire = Date_Add(ccs.date, set->def->expire); /* prepare next event (if any) */ set->num++; if (set->def->number && set->num >= set->def->number) { set->active = false; } else { const date_t minTime = {0, 0}; set->event = Date_Add(ccs.date, Date_Random(minTime, set->def->frame)); } mission = CP_CreateNewMission(INTERESTCATEGORY_TERROR_ATTACK, true); mission->mapDef = cgi->Com_GetMapDefinitionByID(mis->def->id); if (!mission->mapDef) { Com_Printf("SCP_CampaignAddMission: Could not get the mapdef '%s'\n", mis->def->id); CP_MissionRemove(mission); return; } Vector2Copy(mis->def->pos, mission->pos); mission->posAssigned = true; nation = MAP_GetNation(mission->pos); if (nation) { Com_sprintf(mission->location, sizeof(mission->location), "%s", _(nation->name)); } else { Com_sprintf(mission->location, sizeof(mission->location), "%s", _("No nation")); } CP_TerrorMissionStart(mission); mission->finalDate = mis->expire; mis->mission = mission; Com_Printf("spawned map '%s'\n", mis->def->id); scd->numActiveMissions++; }
/** * @brief return true if the node size change and update the cache */ bool uiAbstractScrollableNode::isSizeChange (uiNode_t *node) { assert(UI_Node_IsScrollableContainer(node)); if (!Vector2Equal(node->box.size, EXTRADATA(node).cacheSize)) { Vector2Copy(node->box.size, EXTRADATA(node).cacheSize); return true; } return false; }
/* =============== V_MergeOverviewRefdef merge refdef with overview settings =============== */ void V_MergeOverviewRefdef( ref_params_t *fd ) { ref_overview_t *ov = &clgame.overView; float aspect; float size_x, size_y; vec2_t mins, maxs; if( !gl_overview->integer ) return; // NOTE: Xash3D may use 16:9 or 16:10 aspects aspect = (float)glState.width / (float)glState.height; size_x = fabs( 8192.0f / ov->flZoom ); size_y = fabs( 8192.0f / (ov->flZoom * aspect )); // compute rectangle ov->xLeft = -(size_x / 2); ov->xRight = (size_x / 2); ov->xTop = -(size_y / 2); ov->xBottom = (size_y / 2); if( gl_overview->integer == 1 ) { Con_NPrintf( 0, " Overview: Zoom %.2f, Map Origin (%.2f, %.2f, %.2f), Z Min %.2f, Z Max %.2f, Rotated %i\n", ov->flZoom, ov->origin[0], ov->origin[1], ov->origin[2], ov->zNear, ov->zFar, ov->rotated ); } VectorCopy( ov->origin, fd->vieworg ); fd->vieworg[2] = ov->zFar + ov->zNear; Vector2Copy( fd->vieworg, mins ); Vector2Copy( fd->vieworg, maxs ); mins[!ov->rotated] += ov->xLeft; maxs[!ov->rotated] += ov->xRight; mins[ov->rotated] += ov->xTop; maxs[ov->rotated] += ov->xBottom; fd->viewangles[0] = 90.0f; fd->viewangles[1] = 90.0f; fd->viewangles[2] = (ov->rotated) ? (ov->flZoom < 0.0f) ? 180.0f : 0.0f : (ov->flZoom < 0.0f) ? -90.0f : 90.0f; Mod_SetOrthoBounds( mins, maxs ); }
/** * @brief return true if the node size change and update the cache */ qboolean UI_AbstractScrollableNodeIsSizeChange (uiNode_t *node) { assert(UI_NodeInstanceOf(node, "abstractscrollable")); if (!Vector2Equal(node->size, EXTRADATA(node).cacheSize)) { Vector2Copy(node->size, EXTRADATA(node).cacheSize); return qtrue; } return qfalse; }
static polyVert_t* createVertexArray(Rocket::Core::Vertex* vertices, int count) { polyVert_t* verts = static_cast<polyVert_t*>(Z_Malloc(sizeof(polyVert_t) * count)); for (int i = 0; i < count; i++) { polyVert_t& polyVert = verts[i]; Rocket::Core::Vertex& vert = vertices[i]; Vector2Copy(vert.position, polyVert.xyz); polyVert.modulate[0] = vert.colour.red; polyVert.modulate[1] = vert.colour.green; polyVert.modulate[2] = vert.colour.blue; polyVert.modulate[3] = vert.colour.alpha; Vector2Copy(vert.tex_coord, polyVert.st); } return verts; }
/** * @brief Make the UFOs run * @param[in] campaign The campaign data structure * @param[in] deltaTime The time passed since last call */ void UFO_CampaignRunUFOs (const campaign_t* campaign, int deltaTime) { int ufoIdx, k; /* now the ufos are flying around, too - cycle backward - ufo might be destroyed */ for (ufoIdx = ccs.numUFOs - 1; ufoIdx >= 0; ufoIdx--) { aircraft_t *ufo = UFO_GetByIDX(ufoIdx); /* don't run a landed ufo */ if (ufo->landed) continue; /* Every UFO on geoscape should have a mission assigned */ assert(ufo->mission); /* reached target and not following a phalanx aircraft? then we need a new destination */ if (AIR_AircraftMakeMove(deltaTime, ufo) && ufo->status != AIR_UFO) { float *end; end = ufo->route.point[ufo->route.numPoints - 1]; Vector2Copy(end, ufo->pos); MAP_CheckPositionBoundaries(ufo->pos); if (ufo->mission->stage == STAGE_INTERCEPT && ufo->mission->data.aircraft) { /* Attacking an installation: fly over this installation */ UFO_SetRandomDestAround(ufo, ufo->mission->pos); } else UFO_SetRandomDest(ufo); if (CP_CheckNextStageDestination(campaign, ufo)) /* UFO has been removed from game */ continue; /* UFO was destroyed (maybe because the mission was removed) */ if (ufoIdx == ccs.numUFOs) continue; } /* is there a PHALANX aircraft to shoot at ? */ UFO_SearchAircraftTarget(campaign, ufo); /* antimatter tanks */ if (ufo->fuel <= 0) ufo->fuel = ufo->stats[AIR_STATS_FUELSIZE]; /* Update delay to launch next projectile */ for (k = 0; k < ufo->maxWeapons; k++) { aircraftSlot_t *slot = &ufo->weapons[k]; if (slot->delayNextShot > 0) slot->delayNextShot -= deltaTime; } } }
/** * @brief UFO starts to attack the installation. * @note Intercept mission -- Stage 2 */ static void CP_InterceptAttackInstallation (mission_t* mission) { const date_t minAttackDelay = {0, 3600}; const date_t attackDelay = {0, 21600}; /* How long the UFO should stay on earth */ installation_t* installation; vec3_t missionPos; mission->stage = STAGE_INTERCEPT; installation = mission->data.installation; Vector2Copy(mission->pos, missionPos); if (!VectorCompareEps(missionPos, installation->pos, UFO_EPSILON)) { mission->finalDate = ccs.date; return; } /* Make round around the position of the mission */ UFO_SetRandomDestAround(mission->ufo, mission->pos); mission->finalDate = Date_Add(ccs.date, Date_Random(minAttackDelay, attackDelay)); }
/** * @brief Set Terror attack mission, and go to Terror attack mission pos. * @note Terror attack mission -- Stage 1 * @note Terror missions can only take place in city: pick one in ccs.cities. */ static void CP_TerrorMissionGo (mission_t *mission) { int counter; mission->stage = STAGE_MISSION_GOTO; /* Choose a map */ for (counter = 0; counter < MAX_POS_LOOP; counter++) { city_t *city = CP_ChooseCity(); if (!city) continue; if (GEO_PositionCloseToBase(city->pos)) continue; if (!CP_ChooseMap(mission, city->pos)) continue; if (CP_TerrorInCity(city)) continue; Vector2Copy(city->pos, mission->pos); mission->data.city = city; mission->posAssigned = true; break; } if (counter >= MAX_POS_LOOP) { Com_DPrintf(DEBUG_CLIENT, "CP_TerrorMissionGo: Could not set position.\n"); CP_MissionRemove(mission); return; } if (mission->ufo) { CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); } else { /* Go to next stage on next frame */ mission->finalDate = ccs.date; } }
/** * @brief Set Intercept mission: UFO chooses an installation an flies to it. * @note Intercept mission -- Stage 1 */ void CP_InterceptGoToInstallation (mission_t* mission) { installation_t* installation; assert(mission->ufo); mission->stage = STAGE_MISSION_GOTO; installation = CP_InterceptChooseInstallation(mission); if (!installation) { Com_Printf("CP_InterceptGoToInstallation: no installation found\n"); CP_MissionRemove(mission); return; } mission->data.installation = installation; Vector2Copy(installation->pos, mission->pos); mission->posAssigned = true; CP_MissionDisableTimeLimit(mission); UFO_SendToDestination(mission->ufo, mission->pos); }
/** * @brief Go to base position. * @param[in,out] mission Pointer to the mission * @note Supply mission -- Stage 1 */ static void CP_SupplyGoToBase (mission_t *mission) { alienBase_t *alienBase; assert(mission->ufo); mission->stage = STAGE_MISSION_GOTO; /* Maybe base has been destroyed since mission creation ? */ if (!AB_CheckSupplyMissionPossible()) { Com_DPrintf(DEBUG_CLIENT, "No base in game: removing supply mission.\n"); CP_MissionRemove(mission); return; } alienBase = AB_ChooseBaseToSupply(); assert(alienBase); mission->data.alienBase = alienBase; Vector2Copy(alienBase->pos, mission->pos); UFO_SendToDestination(mission->ufo, mission->pos); }
//---------------------------- void CPoly::Draw() { polyVert_t verts[MAX_CPOLY_VERTS]; for ( int i = 0; i < mCount; i++ ) { // Add our midpoint and vert offset to get the actual vertex VectorAdd( mOrigin1, mOrg[i], verts[i].xyz ); // Assign the same color to each vert verts[i].modulate[0] = mRefEnt.shaderRGBA[0]; verts[i].modulate[1] = mRefEnt.shaderRGBA[1]; verts[i].modulate[2] = mRefEnt.shaderRGBA[2]; verts[i].modulate[3] = mRefEnt.shaderRGBA[3]; // Copy the ST coords Vector2Copy( mST[i], verts[i].st ); } // Add this poly theFxHelper.AddPolyToScene( mRefEnt.customShader, mCount, verts ); drawnFx++; }
/** @note Defaults should match those of ufo2map, or lighting will be inconsistent between world and models */ static void SP_worldspawn (const localEntityParse_t* entData) { /* maximum level */ cl.mapMaxLevel = entData->maxLevel; if (GAME_IsMultiplayer()) { if (cl_teamnum->integer > entData->maxMultiplayerTeams || cl_teamnum->integer <= TEAM_CIVILIAN) { Com_Printf("The selected team is not usable. " "The map doesn't support %i teams but only %i teams\n", cl_teamnum->integer, entData->maxMultiplayerTeams); Cvar_SetValue("cl_teamnum", TEAM_DEFAULT); Com_Printf("Set teamnum to %i\n", cl_teamnum->integer); } } /** @todo - make sun position/color vary based on local time at location? */ const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP); /** @note Some vectors have exra elements to comply with mathlib and/or OpenGL conventions, but handled as shorter ones */ vec3_t sunAngles; vec4_t sunColor; vec_t sunIntensity; if (dayLightmap) { /* set defaults for daylight */ Vector4Set(refdef.ambientColor, 0.26, 0.26, 0.26, 1.0); sunIntensity = 280; VectorSet(sunAngles, -75, 100, 0); Vector4Set(sunColor, 0.90, 0.75, 0.65, 1.0); /* override defaults with data from worldspawn entity, if any */ if (VectorNotEmpty(entData->ambientDayColor)) VectorCopy(entData->ambientDayColor, refdef.ambientColor); if (entData->dayLight) sunIntensity = entData->dayLight; if (Vector2NotEmpty(entData->daySunAngles)) Vector2Copy(entData->daySunAngles, sunAngles); if (VectorNotEmpty(entData->daySunColor)) VectorCopy(entData->daySunColor, sunColor); Vector4Set(refdef.sunSpecularColor, 1.0, 1.0, 0.9, 1); } else { /* set defaults for night light */ Vector4Set(refdef.ambientColor, 0.16, 0.16, 0.17, 1.0); sunIntensity = 15; VectorSet(sunAngles, -80, 220, 0); Vector4Set(sunColor, 0.25, 0.25, 0.35, 1.0); /* override defaults with data from worldspawn entity, if any */ if (VectorNotEmpty(entData->ambientNightColor)) VectorCopy(entData->ambientNightColor, refdef.ambientColor); if (entData->nightLight) sunIntensity = entData->nightLight; if (Vector2NotEmpty(entData->nightSunAngles)) Vector2Copy(entData->nightSunAngles, sunAngles); if (VectorNotEmpty(entData->nightSunColor)) VectorCopy(entData->nightSunColor, sunColor); Vector4Set(refdef.sunSpecularColor, 0.5, 0.5, 0.7, 1); } ColorNormalize(sunColor, sunColor); VectorScale(sunColor, sunIntensity/255.0, sunColor); Vector4Copy(sunColor, refdef.sunDiffuseColor); /* clamp ambient for models */ Vector4Copy(refdef.ambientColor, refdef.modelAmbientColor); for (int i = 0; i < 3; i++) if (refdef.modelAmbientColor[i] < MIN_AMBIENT_COMPONENT) refdef.modelAmbientColor[i] = MIN_AMBIENT_COMPONENT; /* scale it into a reasonable range, the clamp above ensures this will work */ while (VectorSum(refdef.modelAmbientColor) < MIN_AMBIENT_SUM) VectorScale(refdef.modelAmbientColor, 1.25, refdef.modelAmbientColor); AngleVectors(sunAngles, refdef.sunVector, nullptr, nullptr); refdef.sunVector[3] = 0.0; /* to use as directional light source in OpenGL */ /** @todo Parse fog from worldspawn config */ refdef.weather = WEATHER_NONE; refdef.fogColor[3] = 1.0; VectorSet(refdef.fogColor, 0.75, 0.75, 0.75); }
void uiEkgNode::draw (uiNode_t *node) { vec2_t size; vec2_t nodepos; const image_t *image; const char* imageName = UI_GetReferenceString(node, EXTRADATA(node).super.source); if (Q_strnull(imageName)) return; UI_GetNodeAbsPos(node, nodepos); image = UI_LoadWrappedImage(imageName); if (image) { const int ekgHeight = node->box.size[1]; const int ekgWidth = image->width; /* we have different ekg parts in each ekg image... */ const int ekgImageParts = image->height / node->box.size[1]; const int ekgMaxIndex = ekgImageParts - 1; /* we change the index of the image part in 20s steps */ /** @todo this magic number should be replaced with a sane calculation of the value */ const int ekgDivide = 20; /* If we are in the range of (ekgMaxValue + ekgDivide, ekgMaxValue) we are using the first image */ const int ekgMaxValue = ekgDivide * ekgMaxIndex; int ekgValue; float current; /** @todo these cvars should come from the script */ /* ekg_morale and ekg_hp are the node names */ if (node->name[0] == 'm') current = Cvar_GetValue("mn_morale") / EXTRADATA(node).scaleCvarValue; else current = Cvar_GetValue("mn_hp") / EXTRADATA(node).scaleCvarValue; ekgValue = std::min((int)current, ekgMaxValue); EXTRADATA(node).super.texl[1] = (ekgMaxIndex - (int)(ekgValue / ekgDivide)) * ekgHeight; EXTRADATA(node).super.texh[1] = EXTRADATA(node).super.texl[1] + ekgHeight; EXTRADATA(node).super.texl[0] = -(int) (EXTRADATA(node).scrollSpeed * CL_Milliseconds()) % ekgWidth; EXTRADATA(node).super.texh[0] = EXTRADATA(node).super.texl[0] + node->box.size[0]; /** @todo code is duplicated in the image node code */ if (node->box.size[0] && !node->box.size[1]) { const float scale = image->width / node->box.size[0]; Vector2Set(size, node->box.size[0], image->height / scale); } else if (node->box.size[1] && !node->box.size[0]) { const float scale = image->height / node->box.size[1]; Vector2Set(size, image->width / scale, node->box.size[1]); } else { if (EXTRADATA(node).super.preventRatio) { /* maximize the image into the bounding box */ const float ratio = (float) image->width / (float) image->height; if (node->box.size[1] * ratio > node->box.size[0]) { Vector2Set(size, node->box.size[0], node->box.size[0] / ratio); } else { Vector2Set(size, node->box.size[1] * ratio, node->box.size[1]); } } else { Vector2Copy(node->box.size, size); } } UI_DrawNormImage(false, nodepos[0], nodepos[1], size[0], size[1], EXTRADATA(node).super.texh[0], EXTRADATA(node).super.texh[1], EXTRADATA(node).super.texl[0], EXTRADATA(node).super.texl[1], image); } }
/** * @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; }
/** * @brief Converts the model data into the opengl arrays * @param mod The model to convert * @param mesh The particular mesh of the model to convert * @param backlerp The linear back interpolation when loading the data * @param framenum The frame number of the mesh to load (if animated) * @param oldframenum The old frame number (used to interpolate) * @param prerender If this is @c true, all data is filled to the arrays. If @c false, then * e.g. the normals are only filled to the arrays if the lighting is activated. * * @note If GLSL programs are enabled, the actual interpolation will be done on the GPU, but * this function is still needed to fill the GL arrays for the keyframes */ void R_FillArrayData (mAliasModel_t* mod, mAliasMesh_t *mesh, float backlerp, int framenum, int oldframenum, qboolean prerender) { const mAliasFrame_t *frame, *oldframe; vec3_t move; const float frontlerp = 1.0 - backlerp; vec3_t r_mesh_verts[MAX_ALIAS_VERTS]; vec_t *texcoord_array, *vertex_array_3d; frame = mod->frames + framenum; oldframe = mod->frames + oldframenum; /* try to do keyframe-interpolation on the GPU if possible*/ if (r_state.lighting_enabled) { /* we only need to change the array data if we've switched to a new keyframe */ if (mod->curFrame != framenum) { /* if we're rendering frames in order, the "next" keyframe from the previous * time through will be our "previous" keyframe now, so we can swap pointers * instead of generating it again from scratch */ if (mod->curFrame == oldframenum) { vec_t *tmp1 = mesh->verts; vec_t *tmp2 = mesh->normals; vec_t *tmp3 = mesh->tangents; mesh->verts = mesh->next_verts; mesh->next_verts = tmp1; mesh->normals = mesh->next_normals; mesh->next_normals = tmp2; mesh->tangents = mesh->next_tangents; mesh->next_tangents = tmp3; /* if we're alternating between two keyframes, we don't need to generate * anything; otherwise, generate the "next" keyframe*/ if (mod->oldFrame != framenum) R_ModCalcNormalsAndTangents(mesh, framenum, frame->translate, qfalse); } else { /* if we're starting a new animation or otherwise not rendering keyframes * in order, we need to fill the arrays for both keyframes */ R_ModCalcNormalsAndTangents(mesh, oldframenum, oldframe->translate, qtrue); R_ModCalcNormalsAndTangents(mesh, framenum, frame->translate, qfalse); } /* keep track of which keyframes are currently stored in our arrays */ mod->oldFrame = oldframenum; mod->curFrame = framenum; } } else { /* otherwise, we have to do it on the CPU */ const mAliasVertex_t *v, *ov; int i; assert(mesh->num_verts < lengthof(r_mesh_verts)); v = &mesh->vertexes[framenum * mesh->num_verts]; ov = &mesh->vertexes[oldframenum * mesh->num_verts]; if (prerender) R_ModCalcNormalsAndTangents(mesh, 0, oldframe->translate, qtrue); for (i = 0; i < 3; i++) move[i] = backlerp * oldframe->translate[i] + frontlerp * frame->translate[i]; for (i = 0; i < mesh->num_verts; i++, v++, ov++) { /* lerp the verts */ VectorSet(r_mesh_verts[i], move[0] + ov->point[0] * backlerp + v->point[0] * frontlerp, move[1] + ov->point[1] * backlerp + v->point[1] * frontlerp, move[2] + ov->point[2] * backlerp + v->point[2] * frontlerp); } texcoord_array = texunit_diffuse.texcoord_array; vertex_array_3d = r_state.vertex_array_3d; /** @todo damn slow - optimize this */ for (i = 0; i < mesh->num_tris; i++) { /* draw the tris */ int j; for (j = 0; j < 3; j++) { const int arrayIndex = 3 * i + j; const int meshIndex = mesh->indexes[arrayIndex]; Vector2Copy(mesh->stcoords[meshIndex], texcoord_array); VectorCopy(r_mesh_verts[meshIndex], vertex_array_3d); texcoord_array += 2; vertex_array_3d += 3; } } } }
/** * @brief Draw the base inventory * @return The full height requested by the current view (not the node height) */ static int UI_BaseInventoryNodeDrawItems (uiNode_t* node, const objDef_t* highlightType) { bool outOfNode = false; vec2_t nodepos; int items = 0; int rowHeight = 0; const int cellWidth = node->box.size[0] / EXTRADATA(node).columns; containerItemIterator_t iterator; int currentHeight = 0; UI_GetNodeAbsPos(node, nodepos); UI_ContainerItemIteratorInit(&iterator, node); for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) { const int id = iterator.itemID; const objDef_t* obj = INVSH_GetItemByIDX(id); Item tempItem(obj, nullptr, 1); vec3_t pos; vec3_t ammopos; const float* color; bool isHighlight = false; int amount; const int col = items % EXTRADATA(node).columns; int cellHeight = 0; const Item* icItem = iterator.itemFound; /* skip items over and bellow the node view */ if (outOfNode || currentHeight < EXTRADATA(node).scrollY.viewPos) { int height; R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &height, nullptr, nullptr); height += obj->sy * C_UNIT + 10; if (height > rowHeight) rowHeight = height; if (outOfNode || currentHeight + rowHeight < EXTRADATA(node).scrollY.viewPos) { if (col == EXTRADATA(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; } items++; continue; } } Vector2Copy(nodepos, pos); pos[0] += cellWidth * col; pos[1] += currentHeight - EXTRADATA(node).scrollY.viewPos; pos[2] = 0; if (highlightType) { if (obj->isAmmo()) isHighlight = obj->isLoadableInWeapon(highlightType); else isHighlight = highlightType->isLoadableInWeapon(obj); } if (icItem != nullptr) { if (isHighlight) color = colorLoadable; else color = colorDefault; } else { if (isHighlight) color = colorDisabledLoadable; else color = colorDisabledHiden; } if (icItem) amount = icItem->getAmount(); else amount = 0; /* draw item */ pos[0] += obj->sx * C_UNIT / 2.0; pos[1] += obj->sy * C_UNIT / 2.0; UI_DrawItem(node, pos, &tempItem, -1, -1, scale, color); UI_DrawString("f_verysmall", ALIGN_LC, pos[0] + obj->sx * C_UNIT / 2.0, pos[1] + obj->sy * C_UNIT / 2.0, pos[0] + obj->sx * C_UNIT / 2.0, cellWidth - 5, /* maxWidth */ 0, va("x%i", amount)); pos[0] -= obj->sx * C_UNIT / 2.0; pos[1] += obj->sy * C_UNIT / 2.0; cellHeight += obj->sy * C_UNIT; /* save position for ammo */ Vector2Copy(pos, ammopos); ammopos[2] = 0; ammopos[0] += obj->sx * C_UNIT + 10; /* draw the item name. */ cellHeight += UI_DrawString("f_verysmall", ALIGN_UL, pos[0], pos[1], pos[0], cellWidth - 5, /* max width */ 0, _(obj->name)); /* draw ammos of weapon */ if (obj->weapon && EXTRADATA(node).displayAmmoOfWeapon) { for (int ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) { tempItem.setDef(obj->ammos[ammoIdx]); /* skip weapos that are their own ammo -- oneshot and such */ if (obj == tempItem.def()) continue; /* skip unusable ammo */ if (!GAME_ItemIsUseable(tempItem.def())) continue; /* find and skip none existing ammo */ icItem = UI_ContainerNodeGetExistingItem(node, tempItem.def(), (itemFilterTypes_t) EXTRADATA(node).filterEquipType); if (!icItem) continue; /* Calculate the center of the item model/image. */ ammopos[0] += icItem->def()->sx * C_UNIT / 2.0; ammopos[1] -= icItem->def()->sy * C_UNIT / 2.0; UI_DrawItem(node, ammopos, &tempItem, -1, -1, scale, colorDefault); UI_DrawString("f_verysmall", ALIGN_LC, ammopos[0] + icItem->def()->sx * C_UNIT / 2.0, ammopos[1] + icItem->def()->sy * C_UNIT / 2.0, ammopos[0] + icItem->def()->sx * C_UNIT / 2.0, cellWidth - 5 - ammopos[0], /* maxWidth */ 0, va("x%i", icItem->getAmount())); ammopos[0] += icItem->def()->sx * C_UNIT / 2.0; ammopos[1] += icItem->def()->sy * C_UNIT / 2.0; } } cellHeight += 10; if (cellHeight > rowHeight) { rowHeight = cellHeight; } /* add a marge between rows */ if (col == EXTRADATA(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; if (currentHeight - EXTRADATA(node).scrollY.viewPos >= node->box.size[1]) outOfNode = true; } /* count items */ items++; } if (rowHeight != 0) { currentHeight += rowHeight; } return currentHeight; }
/** * @brief Calculates a per-vertex tangentspace basis and stores it in GL arrays attached to the mesh * @param mesh The mesh to calculate normals for * @param framenum The animation frame to calculate normals for * @param translate The frame translation for the given animation frame * @param backlerp Whether to store the results in the GL arrays for the previous keyframe or the next keyframe * @sa R_ModCalcUniqueNormalsAndTangents */ static void R_ModCalcNormalsAndTangents (mAliasMesh_t *mesh, int framenum, const vec3_t translate, qboolean backlerp) { int i, j; mAliasVertex_t *vertexes = &mesh->vertexes[framenum * mesh->num_verts]; mAliasCoord_t *stcoords = mesh->stcoords; const int numIndexes = mesh->num_tris * 3; const int32_t *indexArray = mesh->indexes; vec3_t triangleNormals[MAX_ALIAS_TRIS]; vec3_t triangleTangents[MAX_ALIAS_TRIS]; vec3_t triangleBitangents[MAX_ALIAS_TRIS]; float *texcoords, *verts, *normals, *tangents; /* set up array pointers for either the previous keyframe or the next keyframe */ texcoords = mesh->texcoords; if (backlerp) { verts = mesh->verts; normals = mesh->normals; tangents = mesh->tangents; } else { verts = mesh->next_verts; normals = mesh->next_normals; tangents = mesh->next_tangents; } /* calculate per-triangle surface normals and tangents*/ 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]); /* normalize */ VectorNormalizeFast(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]); /* normalize */ VectorNormalizeFast(triangleTangents[j]); VectorNormalizeFast(triangleBitangents[j]); } else { VectorClear(triangleTangents[j]); VectorClear(triangleBitangents[j]); } } /* for each vertex */ for (i = 0; i < mesh->num_verts; i++) { vec3_t n, b, v; vec4_t t; const int len = mesh->revIndexes[i].length; const int32_t *list = mesh->revIndexes[i].list; VectorClear(n); VectorClear(t); VectorClear(b); /* for each vertex that got mapped to this one (ie. for each triangle this vertex is a part of) */ for (j = 0; j < len; j++) { const int32_t idx = list[j] / 3; VectorAdd(n, triangleNormals[idx], n); VectorAdd(t, triangleTangents[idx], t); VectorAdd(b, triangleBitangents[idx], b); } /* normalization here does shared-vertex smoothing */ VectorNormalizeFast(n); VectorNormalizeFast(t); VectorNormalizeFast(b); /* Grahm-Schmidt orthogonalization */ Orthogonalize(t, n); /* calculate handedness */ CrossProduct(n, t, v); t[3] = (DotProduct(v, b) < 0.0) ? -1.0 : 1.0; /* copy this vertex's info to all the right places in the arrays */ for (j = 0; j < len; j++) { const int32_t idx = list[j]; const int meshIndex = mesh->indexes[list[j]]; Vector2Copy(stcoords[meshIndex], (texcoords + (2 * idx))); VectorAdd(vertexes[meshIndex].point, translate, (verts + (3 * idx))); VectorCopy(n, (normals + (3 * idx))); Vector4Copy(t, (tangents + (4 * idx))); } } }
static void PopulateWithBSPModel(bspModel_t * model, matrix_t transform) { int i, j, x, y, pw[5], r, nodeNum; bspDrawSurface_t *ds; surfaceInfo_t *info; bspDrawVert_t *verts; int *indexes; mesh_t srcMesh, *mesh, *subdivided; traceInfo_t ti; traceWinding_t tw; /* dummy check */ if(model == NULL || transform == NULL) return; /* walk the list of surfaces in this model and fill out the info structs */ for(i = 0; i < model->numBSPSurfaces; i++) { /* get surface and info */ ds = &bspDrawSurfaces[model->firstBSPSurface + i]; info = &surfaceInfos[model->firstBSPSurface + i]; if(info->si == NULL) continue; /* no shadows */ if(!info->castShadows) continue; /* patchshadows? */ if(ds->surfaceType == MST_PATCH && patchShadows == qfalse) continue; /* some surfaces in the bsp might have been tagged as nodraw, with a bogus shader */ if((bspShaders[ds->shaderNum].contentFlags & noDrawContentFlags) || (bspShaders[ds->shaderNum].surfaceFlags & noDrawSurfaceFlags)) continue; /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */ if((info->si->compileFlags & C_NODRAW)) continue; if((info->si->compileFlags & C_TRANSLUCENT) && !(info->si->compileFlags & C_ALPHASHADOW) && !(info->si->compileFlags & C_LIGHTFILTER)) continue; /* setup trace info */ ti.si = info->si; ti.castShadows = info->castShadows; ti.surfaceNum = model->firstBSPBrush + i; ti.skipGrid = (ds->surfaceType == MST_PATCH); /* choose which node (normal or skybox) */ if(info->parentSurfaceNum >= 0) { nodeNum = skyboxNodeNum; /* sky surfaces in portal skies are ignored */ if(info->si->compileFlags & C_SKY) continue; } else nodeNum = headNodeNum; /* setup trace winding */ memset(&tw, 0, sizeof(tw)); tw.infoNum = AddTraceInfo(&ti); tw.numVerts = 3; /* switch on type */ switch (ds->surfaceType) { /* handle patches */ case MST_PATCH: /* subdivide the surface */ srcMesh.width = ds->patchWidth; srcMesh.height = ds->patchHeight; srcMesh.verts = &bspDrawVerts[ds->firstVert]; //% subdivided = SubdivideMesh( srcMesh, 8, 512 ); subdivided = SubdivideMesh2(srcMesh, info->patchIterations); /* fit it to the curve and remove colinear verts on rows/columns */ PutMeshOnCurve(*subdivided); mesh = RemoveLinearMeshColumnsRows(subdivided); FreeMesh(subdivided); /* set verts */ verts = mesh->verts; /* subdivide each quad to place the models */ for(y = 0; y < (mesh->height - 1); y++) { for(x = 0; x < (mesh->width - 1); x++) { /* set indexes */ pw[0] = x + (y * mesh->width); pw[1] = x + ((y + 1) * mesh->width); pw[2] = x + 1 + ((y + 1) * mesh->width); pw[3] = x + 1 + (y * mesh->width); pw[4] = x + (y * mesh->width); /* same as pw[ 0 ] */ /* set radix */ r = (x + y) & 1; /* make first triangle */ VectorCopy(verts[pw[r + 0]].xyz, tw.v[0].xyz); Vector2Copy(verts[pw[r + 0]].st, tw.v[0].st); VectorCopy(verts[pw[r + 1]].xyz, tw.v[1].xyz); Vector2Copy(verts[pw[r + 1]].st, tw.v[1].st); VectorCopy(verts[pw[r + 2]].xyz, tw.v[2].xyz); Vector2Copy(verts[pw[r + 2]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); /* make second triangle */ VectorCopy(verts[pw[r + 0]].xyz, tw.v[0].xyz); Vector2Copy(verts[pw[r + 0]].st, tw.v[0].st); VectorCopy(verts[pw[r + 2]].xyz, tw.v[1].xyz); Vector2Copy(verts[pw[r + 2]].st, tw.v[1].st); VectorCopy(verts[pw[r + 3]].xyz, tw.v[2].xyz); Vector2Copy(verts[pw[r + 3]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); } } /* free the subdivided mesh */ FreeMesh(mesh); break; /* handle triangle surfaces */ case MST_TRIANGLE_SOUP: case MST_PLANAR: /* set verts and indexes */ verts = &bspDrawVerts[ds->firstVert]; indexes = &bspDrawIndexes[ds->firstIndex]; /* walk the triangle list */ for(j = 0; j < ds->numIndexes; j += 3) { VectorCopy(verts[indexes[j]].xyz, tw.v[0].xyz); Vector2Copy(verts[indexes[j]].st, tw.v[0].st); VectorCopy(verts[indexes[j + 1]].xyz, tw.v[1].xyz); Vector2Copy(verts[indexes[j + 1]].st, tw.v[1].st); VectorCopy(verts[indexes[j + 2]].xyz, tw.v[2].xyz); Vector2Copy(verts[indexes[j + 2]].st, tw.v[2].st); MatrixTransformPoint2(transform, tw.v[0].xyz); MatrixTransformPoint2(transform, tw.v[1].xyz); MatrixTransformPoint2(transform, tw.v[2].xyz); FilterTraceWindingIntoNodes_r(&tw, nodeNum); } break; /* other surface types do not cast shadows */ default: break; } } }
static void PopulateWithPicoModel(int castShadows, picoModel_t * model, matrix_t transform) { int i, j, k, numSurfaces, numIndexes; picoSurface_t *surface; picoShader_t *shader; picoVec_t *xyz, *st; picoIndex_t *indexes; traceInfo_t ti; traceWinding_t tw; /* dummy check */ if(model == NULL || transform == NULL) return; /* get info */ numSurfaces = PicoGetModelNumSurfaces(model); /* walk the list of surfaces in this model and fill out the info structs */ for(i = 0; i < numSurfaces; i++) { /* get surface */ surface = PicoGetModelSurface(model, i); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* get shader (fixme: support shader remapping) */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) continue; ti.si = ShaderInfoForShader(PicoGetShaderName(shader)); if(ti.si == NULL) continue; /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */ if((ti.si->compileFlags & C_NODRAW)) continue; if((ti.si->compileFlags & C_TRANSLUCENT) && !(ti.si->compileFlags & C_ALPHASHADOW) && !(ti.si->compileFlags & C_LIGHTFILTER)) continue; /* setup trace info */ ti.castShadows = castShadows; ti.surfaceNum = -1; ti.skipGrid = qtrue; // also ignore picomodels when skipping patches /* setup trace winding */ memset(&tw, 0, sizeof(tw)); tw.infoNum = AddTraceInfo(&ti); tw.numVerts = 3; /* get info */ numIndexes = PicoGetSurfaceNumIndexes(surface); indexes = PicoGetSurfaceIndexes(surface, 0); /* walk the triangle list */ for(j = 0; j < numIndexes; j += 3, indexes += 3) { for(k = 0; k < 3; k++) { xyz = PicoGetSurfaceXYZ(surface, indexes[k]); st = PicoGetSurfaceST(surface, 0, indexes[k]); VectorCopy(xyz, tw.v[k].xyz); Vector2Copy(st, tw.v[k].st); MatrixTransformPoint2(transform, tw.v[k].xyz); } FilterTraceWindingIntoNodes_r(&tw, headNodeNum); } } }
/** * @note this function is a copy-paste of UI_ContainerNodeDrawItems (with remove of unneeded code) */ static Item* UI_BaseInventoryNodeGetItem (const uiNode_t* const node, int mouseX, int mouseY, int* contX, int* contY) { bool outOfNode = false; vec2_t nodepos; int items = 0; int rowHeight = 0; const int cellWidth = node->box.size[0] / EXTRADATACONST(node).columns; int tempX, tempY; containerItemIterator_t iterator; int currentHeight = 0; if (!contX) contX = &tempX; if (!contY) contY = &tempY; UI_GetNodeAbsPos(node, nodepos); UI_ContainerItemIteratorInit(&iterator, node); for (; iterator.itemID < csi.numODs; UI_ContainerItemIteratorNext(&iterator)) { const int id = iterator.itemID; const objDef_t* obj = INVSH_GetItemByIDX(id); vec2_t pos; vec2_t ammopos; const int col = items % EXTRADATACONST(node).columns; int cellHeight = 0; Item* icItem = iterator.itemFound; int height; /* skip items over and bellow the node view */ if (outOfNode || currentHeight < EXTRADATACONST(node).scrollY.viewPos) { int outHeight; R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &outHeight, nullptr, nullptr); outHeight += obj->sy * C_UNIT + 10; if (outHeight > rowHeight) rowHeight = outHeight; if (outOfNode || currentHeight + rowHeight < EXTRADATACONST(node).scrollY.viewPos) { if (col == EXTRADATACONST(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; } items++; continue; } } Vector2Copy(nodepos, pos); pos[0] += cellWidth * col; pos[1] += currentHeight - EXTRADATACONST(node).scrollY.viewPos; /* check item */ if (mouseY < pos[1]) break; if (mouseX >= pos[0] && mouseX < pos[0] + obj->sx * C_UNIT && mouseY >= pos[1] && mouseY < pos[1] + obj->sy * C_UNIT) { if (icItem) { *contX = icItem->getX(); *contY = icItem->getY(); return icItem; } return nullptr; } pos[1] += obj->sy * C_UNIT; cellHeight += obj->sy * C_UNIT; /* save position for ammo */ Vector2Copy(pos, ammopos); ammopos[0] += obj->sx * C_UNIT + 10; /* draw the item name. */ R_FontTextSize("f_verysmall", _(obj->name), cellWidth - 5, LONGLINES_WRAP, nullptr, &height, nullptr, nullptr); cellHeight += height; /* draw ammos of weapon */ if (obj->weapon && EXTRADATACONST(node).displayAmmoOfWeapon) { for (int ammoIdx = 0; ammoIdx < obj->numAmmos; ammoIdx++) { const objDef_t* objammo = obj->ammos[ammoIdx]; /* skip unusable ammo */ if (!GAME_ItemIsUseable(objammo)) continue; /* find and skip none existing ammo */ icItem = UI_ContainerNodeGetExistingItem(node, objammo, (itemFilterTypes_t) EXTRADATACONST(node).filterEquipType); if (!icItem) continue; /* check ammo (ammopos in on the left-lower corner) */ if (mouseX < ammopos[0] || mouseY >= ammopos[1]) break; if (mouseX >= ammopos[0] && mouseX < ammopos[0] + objammo->sx * C_UNIT && mouseY >= ammopos[1] - objammo->sy * C_UNIT && mouseY < ammopos[1]) { *contX = icItem->getX(); *contY = icItem->getY(); return icItem; } ammopos[0] += objammo->sx * C_UNIT; } } cellHeight += 10; if (cellHeight > rowHeight) { rowHeight = cellHeight; } /* add a margin between rows */ if (col == EXTRADATACONST(node).columns - 1) { currentHeight += rowHeight; rowHeight = 0; if (currentHeight - EXTRADATACONST(node).scrollY.viewPos >= node->box.size[1]) return nullptr; } /* count items */ items++; } *contX = NONE; *contY = NONE; return nullptr; }
void uiGeoscapeNode::draw (uiNode_t* node) { vec2_t screenPos; geoscapeNode = node; UI_MAPEXTRADATA(node).flatgeoscape = cl_3dmap->integer == 0; UI_MAPEXTRADATA(node).radarOverlay = Cvar_GetValue("geo_overlay_radar"); UI_MAPEXTRADATA(node).nationOverlay = Cvar_GetValue("geo_overlay_nation"); UI_MAPEXTRADATA(node).xviOverlay = Cvar_GetValue("geo_overlay_xvi"); UI_MAPEXTRADATA(node).ambientLightFactor = cl_3dmapAmbient->value; UI_MAPEXTRADATA(node).mapzoommin = cl_mapzoommin->value; UI_MAPEXTRADATA(node).mapzoommax = cl_mapzoommax->value; UI_GetNodeAbsPos(node, UI_MAPEXTRADATA(node).mapPos); Vector2Copy(node->box.size, UI_MAPEXTRADATA(node).mapSize); if (!UI_MAPEXTRADATACONST(node).flatgeoscape) { /* remove the left padding */ UI_MAPEXTRADATA(node).mapSize[0] -= UI_MAPEXTRADATACONST(node).paddingRight; } /* Draw geoscape */ UI_GetNodeScreenPos(node, screenPos); UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]); if (UI_MAPEXTRADATACONST(node).smoothRotation) { if (UI_MAPEXTRADATACONST(node).flatgeoscape) smoothTranslate(node); else smoothRotate(node); } geoscapeData_t& data = *UI_MAPEXTRADATA(node).geoscapeData; data.geoscapeNode = node; GAME_DrawMap(&data); if (!data.active) return; const char* map = data.map; date_t& date = data.date; /* Draw the map and markers */ if (UI_MAPEXTRADATACONST(node).flatgeoscape) { /* the last q value for the 2d geoscape night overlay */ static float lastQ = 0.0f; /* the sun is not always in the plane of the equator on earth - calculate the angle the sun is at */ const float q = (date.day % DAYS_PER_YEAR + (float)(date.sec / (SECONDS_PER_HOUR * 6)) / 4) * 2 * M_PI / DAYS_PER_YEAR - M_PI; if (lastQ != q) { calcAndUploadDayAndNightTexture(node, q); lastQ = q; } R_DrawFlatGeoscape(UI_MAPEXTRADATACONST(node).mapPos, UI_MAPEXTRADATACONST(node).mapSize, (float) date.sec / SECONDS_PER_DAY, UI_MAPEXTRADATACONST(node).center[0], UI_MAPEXTRADATACONST(node).center[1], 0.5 / UI_MAPEXTRADATACONST(node).zoom, map, data.nationOverlay, data.xviOverlay, data.radarOverlay, r_dayandnightTexture, r_xviTexture, r_radarTexture); GAME_DrawMapMarkers(node); } else { bool disableSolarRender = false; if (UI_MAPEXTRADATACONST(node).zoom > 3.3) disableSolarRender = true; R_EnableRenderbuffer(true); R_Draw3DGlobe(UI_MAPEXTRADATACONST(node).mapPos, UI_MAPEXTRADATACONST(node).mapSize, date.day, date.sec, UI_MAPEXTRADATACONST(node).angles, UI_MAPEXTRADATACONST(node).zoom, map, disableSolarRender, UI_MAPEXTRADATACONST(node).ambientLightFactor, UI_MAPEXTRADATA(node).nationOverlay, UI_MAPEXTRADATA(node).xviOverlay, UI_MAPEXTRADATA(node).radarOverlay, r_xviTexture, r_radarTexture, true); GAME_DrawMapMarkers(node); R_DrawBloom(); R_EnableRenderbuffer(false); } UI_PopClipRect(); }
//------------------------- // FX_AddPoly //------------------------- CPoly *FX_AddPoly( vec3_t *verts, vec2_t *st, int numVerts, vec3_t vel, vec3_t accel, float alpha1, float alpha2, float alphaParm, vec3_t rgb1, vec3_t rgb2, float rgbParm, vec3_t rotationDelta, float bounce, int motionDelay, int killTime, qhandle_t shader, int flags ) { if ( theFxHelper.mFrameTime < 0 || !verts ) { // disallow adding effects when the system is paused or the user doesn't pass in a vert array return 0; } CPoly *fx = new CPoly; if ( fx ) { // Do a cheesy copy of the verts and texture coords into our own structure for ( int i = 0; i < numVerts; i++ ) { VectorCopy( verts[i], fx->mOrg[i] ); Vector2Copy( st[i], fx->mST[i] ); } fx->SetVel( vel ); fx->SetAccel( accel ); // RGB---------------- fx->SetRGBStart( rgb1 ); fx->SetRGBEnd( rgb2 ); if (( flags & FX_RGB_PARM_MASK ) == FX_RGB_WAVE ) { fx->SetRGBParm( rgbParm * PI * 0.001f ); } else if ( flags & FX_RGB_PARM_MASK ) { // rgbParm should be a value from 0-100.. fx->SetRGBParm(rgbParm * 0.01f * killTime + theFxHelper.mTime + theFxHelper.mTimeFraction); } // Alpha---------------- fx->SetAlphaStart( alpha1 ); fx->SetAlphaEnd( alpha2 ); if (( flags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_WAVE ) { fx->SetAlphaParm( alphaParm * PI * 0.001f ); } else if ( flags & FX_ALPHA_PARM_MASK ) { fx->SetAlphaParm(alphaParm * 0.01f * killTime + theFxHelper.mTime + theFxHelper.mTimeFraction); } fx->SetFlags( flags ); fx->SetShader( shader ); fx->SetRot( rotationDelta ); fx->SetElasticity( bounce ); fx->SetMotionTimeStamp( motionDelay ); fx->SetNumVerts( numVerts ); // Now that we've set our data up, let's process it into a useful format fx->PolyInit(); FX_AddPrimitive( (CEffect**)&fx, killTime ); } return fx; }
/** * @brief Calculate some radar values that won't change during a mission * @note Called for every new map (client_state_t is wiped with every * level change) */ static void UI_InitRadar (const uiNode_t* node) { int i, j; const vec3_t offset = {MAP_SIZE_OFFSET, MAP_SIZE_OFFSET, MAP_SIZE_OFFSET}; float distAB, distBC; vec2_t gridSize; /**< Size of the whole grid (in tiles units) */ vec2_t nodepos; vec2_t min; vec2_t max; UI_FreeRadarImages(); UI_BuildRadarImageList(CL_GetConfigString(CS_TILES), CL_GetConfigString(CS_POSITIONS)); UI_GetNodeAbsPos(node, nodepos); radar.x = nodepos[0] + node->box.size[0] / 2; radar.y = nodepos[1] + node->box.size[1] / 2; /* only check once per map whether all the needed images exist */ for (j = 0; j < radar.numImages; j++) { hudRadarImage_t* tile = &radar.images[j]; /* map_mins, map_maxs */ for (i = 0; i < PATHFINDING_HEIGHT; i++) { char imagePath[MAX_QPATH]; const image_t* image; if (!UI_CheckRadarImage(tile->name, i + 1)) { if (i == 0) { /* there should be at least one level */ Com_Printf("No radar images for map: '%s'\n", tile->name); radar.numImages = 0; return; } continue; } Com_sprintf(imagePath, sizeof(imagePath), "radars/%s_%i", tile->name, i + 1); tile->path[i] = Mem_StrDup(imagePath); tile->maxlevel++; image = R_FindImage(va("pics/%s", tile->path[i]), it_pic); tile->width = image->width; tile->height = image->height; if (tile->isTile) { tile->gridWidth = round(image->width / 94.0f); tile->gridHeight = round(image->height / 94.0f); tile->mapWidth = tile->gridWidth * 8 * UNIT_SIZE; tile->mapHeight = tile->gridHeight * 8 * UNIT_SIZE; } else { tile->mapX = cl.mapData->mapBox.getMinX(); tile->mapY = cl.mapData->mapBox.getMinY(); tile->mapWidth = cl.mapData->mapBox.getWidthX(); tile->mapHeight = cl.mapData->mapBox.getWidthY(); } } if (tile->isTile) { tile->mapY = cl.mapData->mapBox.getMaxY() - tile->mapY - tile->mapHeight; } } /* center tiles into the minMap/maxMap */ Vector2Copy(cl.mapData->mapBox.maxs, min); Vector2Copy(cl.mapData->mapBox.mins, max); for (j = 0; j < radar.numImages; j++) { hudRadarImage_t* tile = &radar.images[j]; if (tile->mapX < min[0]) min[0] = tile->mapX; if (tile->mapY < min[1]) min[1] = tile->mapY; if (tile->mapX + tile->mapWidth > max[0]) max[0] = tile->mapX + tile->mapWidth; if (tile->mapY + tile->mapHeight > max[1]) max[1] = tile->mapY + tile->mapHeight; } /* compute translation */ min[0] = cl.mapData->mapBox.getMinX() + (cl.mapData->mapBox.getWidthX() - (max[0] - min[0])) * 0.5 - min[0]; min[1] = cl.mapData->mapBox.getMinY() + (cl.mapData->mapBox.getWidthY() - (max[1] - min[1])) * 0.5 - min[1]; for (j = 0; j < radar.numImages; j++) { hudRadarImage_t* tile = &radar.images[j]; tile->mapX += min[0]; tile->mapY += min[1]; } /* get the three points of the triangle */ VectorSubtract(cl.mapData->mapBox.mins, offset, radar.a); VectorAdd(cl.mapData->mapBox.maxs, offset, radar.c); VectorSet(radar.b, radar.c[0], radar.a[1], 0); distAB = (Vector2Dist(radar.a, radar.b) / UNIT_SIZE); distBC = (Vector2Dist(radar.b, radar.c) / UNIT_SIZE); UI_GetRadarWidth(node, gridSize); /* get the dimensions for one grid field on the radar map */ radar.gridWidth = radar.w / distAB; radar.gridHeight = radar.h / distBC; /* shift the x and y values according to their grid width/height and * their gridX and gridY position */ { const float radarLength = std::max(1.0f, fabsf(gridSize[0])); const float radarHeight = std::max(1.0f, fabsf(gridSize[1])); /* image grid relations */ const float gridFactorX = radar.w / radarLength; const float gridFactorY = radar.h / radarHeight; for (j = 0; j < radar.numImages; j++) { hudRadarImage_t* image = &radar.images[j]; image->x = (image->gridX - radar.gridMin[0]) * gridFactorX; image->y = radar.h - (image->gridY - radar.gridMin[1]) * gridFactorY - image->height; } } /* now align the screen coordinates like it's given by the node */ radar.x -= (radar.w / 2); radar.y -= (radar.h / 2); }
/** * @brief Draws the image node * @param[in] node The UI node to draw */ void uiImageNode::draw (uiNode_t* node) { vec2_t size; vec2_t nodepos; const image_t* image; vec2_t imagepos; vec2_t nodesize; const char* imageName = UI_GetReferenceString(node, node->image); if (Q_strnull(imageName)) return; image = UI_LoadImage(imageName); if (!image) return; /* mouse darken effect */ /** @todo convert all pic using mousefx into button. * @todo delete mousefx */ #if 0 if (node->mousefx && node->state) { vec4_t color; VectorScale(node->color, 0.8, color); color[3] = node->color[3]; R_Color(color); } #endif UI_GetNodeAbsPos(node, nodepos); Vector2Copy(node->box.size, nodesize); nodesize[0] -= node->padding + node->padding; if (nodesize[0] < 0) nodesize[0] = 0; nodesize[1] -= node->padding + node->padding; if (nodesize[1] < 0) nodesize[1] = 0; /** @todo code is duplicated in the ekg node code */ if (node->box.size[0] && !node->box.size[1]) { const float scale = image->width / node->box.size[0]; Vector2Set(size, node->box.size[0], image->height / scale); } else if (node->box.size[1] && !node->box.size[0]) { const float scale = image->height / node->box.size[1]; Vector2Set(size, image->width / scale, node->box.size[1]); } else { Vector2Copy(nodesize, size); if (EXTRADATA(node).preventRatio) { /* maximize the image into the bounding box */ const float ratio = (float) image->width / (float) image->height; if (size[1] * ratio > size[0]) { Vector2Set(size, size[0], size[0] / ratio); } else { Vector2Set(size, size[1] * ratio, size[1]); } } } UI_ImageAlignBoxInBox(nodepos, nodesize, size, (align_t) node->contentAlign, imagepos); UI_DrawNormImage(false, imagepos[0] + node->padding, imagepos[1] + node->padding, size[0], size[1], EXTRADATA(node).texh[0], EXTRADATA(node).texh[1], EXTRADATA(node).texl[0], EXTRADATA(node).texl[1], image); /** @todo convert all pic using mousefx into button. * @todo delete mousefx */ #if 0 if (node->mousefx && node->state) { R_Color(nullptr); } #endif }