/** * @param[in] tile Tile to check (normally 0 - except in assembled maps) * @param[in] start trace start vector * @param[in] end trace end vector * @param[in] mins box mins * @param[in] maxs box maxs * @param[in] headnode if < 0 we are in a leaf node * @param[in] contentmask content flags the trace should stop at (see MASK_*) * @param[in] brushrejects brushes the trace should ignore (see MASK_*) * @param[in] origin center for rotating objects * @param[in] angles current rotation status (in degrees) for rotating objects * @param[in] rmaShift how much the object was shifted by the RMA process (needed for doors) * @param[in] fraction The furthest distance needed to trace before we stop. * @brief Handles offseting and rotation of the end points for moving and rotating entities * @sa CM_BoxTrace */ trace_t CM_HintedTransformedBoxTrace (mapTile_t *tile, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, const int headnode, const int contentmask, const int brushrejects, const vec3_t origin, const vec3_t angles, const vec3_t rmaShift, const float fraction) { trace_t trace; vec3_t start_l, end_l; vec3_t forward, right, up; vec3_t temp; bool rotated; /* subtract origin offset */ VectorSubtract(start, origin, start_l); VectorSubtract(end, origin, end_l); /* rotate start and end into the models frame of reference */ if (headnode != tile->box_headnode && VectorNotEmpty(angles)) { rotated = true; } else { rotated = false; } if (rotated) { AngleVectors(angles, forward, right, up); VectorCopy(start_l, temp); start_l[0] = DotProduct(temp, forward); start_l[1] = -DotProduct(temp, right); start_l[2] = DotProduct(temp, up); VectorCopy(end_l, temp); end_l[0] = DotProduct(temp, forward); end_l[1] = -DotProduct(temp, right); end_l[2] = DotProduct(temp, up); } /* When tracing through a model, we want to use the nodes, planes etc. as calculated by ufo2map. * But nodes and planes have been shifted in case of an RMA. At least for doors we need to undo the shift. */ if (VectorNotEmpty(origin)) { /* only doors seem to have their origin set */ VectorAdd(start_l, rmaShift, start_l); /* undo the shift */ VectorAdd(end_l, rmaShift, end_l); } /* sweep the box through the model */ trace = TR_BoxTrace(tile, start_l, end_l, mins, maxs, headnode, contentmask, brushrejects, fraction); trace.mapTile = tile->idx; if (rotated && trace.fraction != 1.0) { vec3_t a; /** @todo figure out how to do this with existing angles */ VectorNegate(angles, a); AngleVectors(a, forward, right, up); VectorCopy(trace.plane.normal, temp); trace.plane.normal[0] = DotProduct(temp, forward); trace.plane.normal[1] = -DotProduct(temp, right); trace.plane.normal[2] = DotProduct(temp, up); } VectorInterpolation(start, end, trace.fraction, trace.endpos); return trace; }
/** * @brief Draws the field marker entity is specified in CL_AddTargeting * @sa CL_AddTargeting * @sa RF_BOX */ static void R_DrawBox (const entity_t* e) { const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha}; if (e->texture) { R_Color(color); R_BindTexture(e->texture->texnum); if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) { R_DrawTexturedBox(e->eBox.mins, e->eBox.maxs); } else { R_DrawTexturedBox(e->oldorigin, e->origin); } R_Color(nullptr); return; } glDisable(GL_TEXTURE_2D); R_Color(color); if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) { R_DrawBoundingBox(e->eBox); } else { vec3_t points[] = { { e->oldorigin[0], e->oldorigin[1], e->oldorigin[2] }, { e->oldorigin[0], e->origin[1], e->oldorigin[2] }, { e->origin[0], e->origin[1], e->oldorigin[2] }, { e->origin[0], e->oldorigin[1], e->oldorigin[2] } }; glLineWidth(2.0f); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); /** @todo fill one array */ glDrawArrays(GL_LINE_LOOP, 0, 4); refdef.batchCount++; points[0][2] = e->origin[2]; points[1][2] = e->origin[2]; points[2][2] = e->origin[2]; points[3][2] = e->origin[2]; glDrawArrays(GL_LINE_LOOP, 0, 4); refdef.batchCount++; points[0][2] = e->oldorigin[2]; points[1][1] = e->oldorigin[1]; points[2][2] = e->oldorigin[2]; points[3][1] = e->origin[1]; glDrawArrays(GL_LINES, 0, 4); refdef.batchCount++; points[0][0] = e->origin[0]; points[1][0] = e->origin[0]; points[2][0] = e->oldorigin[0]; points[3][0] = e->oldorigin[0]; glDrawArrays(GL_LINES, 0, 4); refdef.batchCount++; R_BindDefaultArray(GL_VERTEX_ARRAY); } glEnable(GL_TEXTURE_2D); R_Color(nullptr); }
/** * @brief Calculates the worst case bounding box for the given bsp model * @param[in] model The model to calculate the bbox for * @param[out] box The bbox to fill */ static void CM_CalculateWidestBoundingBox (const cBspModel_t* model, AABB& box) { /* Quickly calculate the bounds of this model to see if they can overlap. */ box.set(model->cbmBox); box.shift(model->origin); if (VectorNotEmpty(model->angles)) { const float offset = std::max(std::max(box.getWidthX(), box.getWidthY()), box.getWidthZ()) / 2.0; box.expand(offset); /* expand the whole box by the highest extent we found */ } }
/** * @brief Calculates the bounding box for the given bsp model * @param[in] model The model to calculate the bbox for * @param[out] mins The maxs of the bbox * @param[out] maxs The mins of the bbox */ static void CM_CalculateBoundingBox (const cBspModel_t* model, vec3_t mins, vec3_t maxs) { /* Quickly calculate the bounds of this model to see if they can overlap. */ VectorAdd(model->origin, model->mins, mins); VectorAdd(model->origin, model->maxs, maxs); if (VectorNotEmpty(model->angles)) { vec3_t acenter, aoffset; const float offset = std::max(std::max(fabs(mins[0] - maxs[0]), fabs(mins[1] - maxs[1])), fabs(mins[2] - maxs[2])) / 2.0; VectorCenterFromMinsMaxs(mins, maxs, acenter); VectorSet(aoffset, offset, offset, offset); VectorAdd(acenter, aoffset, maxs); VectorSubtract(acenter, aoffset, mins); } }
/** * @brief Builds a plane normal and distance from three points on the plane. * If the normal is nearly axial, it will be snapped to be axial. Looks * up the plane in the unique planes. * @param[in] b The brush that the points belong to. * @param[in] p0 Three points on the plane. (A vector with plane coordinates) * @param[in] p1 Three points on the plane. (A vector with plane coordinates) * @param[in] p2 Three points on the plane. (A vector with plane coordinates) * @return the index of the plane in the planes list. */ static int16_t PlaneFromPoints (const mapbrush_t* b, const vec3_t p0, const vec3_t p1, const vec3_t p2) { vec3_t t1, t2, normal; vec_t dist; VectorSubtract(p0, p1, t1); VectorSubtract(p2, p1, t2); CrossProduct(t1, t2, normal); VectorNormalize(normal); dist = DotProduct(p0, normal); if (!VectorNotEmpty(normal)) Sys_Error("PlaneFromPoints: Bad normal (null) for brush %i", b->brushnum); return FindOrCreateFloatPlane(normal, dist); }
/** * @brief Draws an animated, colored shell for the specified entity. Rather than * re-lerping or re-scaling the entity, the currently bound vertex arrays * are simply re-drawn using a small depth offset and varying texcoord delta. */ static void R_DrawMeshModelShell (const mAliasMesh_t *mesh, const vec4_t color) { /* check whether rgb is set */ if (!VectorNotEmpty(color)) return; R_Color(color); R_BindTexture(r_envmaptextures[1]->texnum); R_EnableShell(true); glDrawArrays(GL_TRIANGLES, 0, mesh->num_tris * 3); refdef.batchCount++; R_EnableShell(false); R_Color(NULL); }
void CalculateMinsMaxs (const vec3_t angles, const vec3_t mins, const vec3_t maxs, const vec3_t origin, vec3_t absmin, vec3_t absmax) { /* expand for rotation */ if (VectorNotEmpty(angles)) { vec3_t minVec, maxVec, tmpMinVec, tmpMaxVec; vec3_t centerVec, halfVec, newCenterVec, newHalfVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(mins, maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(angles, m); VectorRotate(m, centerVec, newCenterVec); VectorRotate(m, halfVec, newHalfVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, newHalfVec, tmpMinVec); VectorAdd(newCenterVec, newHalfVec, tmpMaxVec); /* rotation may have changed min and max of the box, so adjust it */ minVec[0] = min(tmpMinVec[0], tmpMaxVec[0]); minVec[1] = min(tmpMinVec[1], tmpMaxVec[1]); minVec[2] = min(tmpMinVec[2], tmpMaxVec[2]); maxVec[0] = max(tmpMinVec[0], tmpMaxVec[0]); maxVec[1] = max(tmpMinVec[1], tmpMaxVec[1]); maxVec[2] = max(tmpMinVec[2], tmpMaxVec[2]); /* Adjust the absolute mins/maxs */ VectorAdd(origin, minVec, absmin); VectorAdd(origin, maxVec, absmax); } else { /* normal */ VectorAdd(origin, mins, absmin); VectorAdd(origin, maxs, absmax); } }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] routing The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] box The box around the inline model (alternative to name) * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, Routing &routing, const char *name, const GridBox &box, const char **list) { if (box.isZero()) { pos3_t min, max; vec3_t absmin, absmax; const cBspModel_t *model; unsigned int i; /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } #if 1 /* An attempt to fix the 'doors starting opened' bug (# 3456). * The main difference is the (missing) rotation of the halfVec. * The results are better, but do not fix the problem. */ CalculateMinsMaxs(model->angles, model->mins, model->maxs, model->origin, absmin, absmax); #else /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, absmin); VectorAdd(maxVec, model->origin, absmax); } else { /* normal */ /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, absmin); VectorAdd(model->maxs, model->origin, absmax); } #endif VecToPos(absmin, min); VecToPos(absmax, max); /* fit min/max into the world size */ max[0] = std::min(max[0], (pos_t)(PATHFINDING_WIDTH - 1)); max[1] = std::min(max[1], (pos_t)(PATHFINDING_WIDTH - 1)); max[2] = std::min(max[2], (pos_t)(PATHFINDING_HEIGHT - 1)); for (i = 0; i < 3; i++) min[i] = std::max(min[i], (pos_t)0); /* We now have the dimensions, call the generic rerouting function. */ GridBox rerouteBox(min, max); Grid_RecalcBoxRouting(mapTiles, routing, rerouteBox, list); } else { /* use the passed box */ Grid_RecalcBoxRouting(mapTiles, routing, box, list); } }
/** * @brief Puts the map data into buffers * @sa R_ModAddMapTile * @note Shift the verts after the texcoords for diffuse and lightmap are loaded * @sa R_ModShiftTile * @todo Don't use the buffers from r_state here - they might overflow * @todo Decrease MAX_GL_ARRAY_LENGTH to 32768 again when this is fixed */ static void R_LoadBspVertexArrays (model_t *mod) { int i, j; int vertOfs, texCoordOfs, tangOfs; float *vecShifted; float soff, toff, s, t; float *point, *sdir, *tdir; vec4_t tangent; vec3_t binormal; mBspSurface_t *surf; mBspVertex_t *vert; int vertexCount, indexCount; vertOfs = texCoordOfs = tangOfs = 0; vertexCount = indexCount = 0; for (i = 0, surf = mod->bsp.surfaces; i < mod->bsp.numsurfaces; i++, surf++) { const int numedges = surf->numedges; vertexCount += numedges; if (numedges > 2) /* no triangles for degenerate polys */ indexCount += (numedges - 2) * 3; } surf = mod->bsp.surfaces; /* allocate the vertex arrays */ mod->bsp.texcoords = Mem_PoolAllocTypeN(GLfloat, vertexCount * 2, vid_modelPool); mod->bsp.lmtexcoords = Mem_PoolAllocTypeN(GLfloat, vertexCount * 2, vid_modelPool); mod->bsp.verts = Mem_PoolAllocTypeN(GLfloat, vertexCount * 3, vid_modelPool); mod->bsp.normals = Mem_PoolAllocTypeN(GLfloat, vertexCount * 3, vid_modelPool); mod->bsp.tangents = Mem_PoolAllocTypeN(GLfloat, vertexCount * 4, vid_modelPool); mod->bsp.indexes = Mem_PoolAllocTypeN(GLint, indexCount, vid_modelPool); /* Will be filled at the end of map loading, after building surface lists */ for (i = 0; i < mod->bsp.numsurfaces; i++, surf++) { surf->index = vertOfs / 3; surf->firstTriangle = -1; /* Mark as "no triangles generated yet" */ for (j = 0; j < surf->numedges; j++) { const float *normal; const int index = mod->bsp.surfedges[surf->firstedge + j]; /* vertex */ if (index > 0) { /* negative indices to differentiate which end of the edge */ const mBspEdge_t *edge = &mod->bsp.edges[index]; vert = &mod->bsp.vertexes[edge->v[0]]; } else { const mBspEdge_t *edge = &mod->bsp.edges[-index]; vert = &mod->bsp.vertexes[edge->v[1]]; } point = vert->position; /* shift it for assembled maps */ vecShifted = &mod->bsp.verts[vertOfs]; /* origin (func_door, func_rotating) bmodels must not have shifted vertices, * they are translated by their entity origin value */ if (surf->isOriginBrushModel) VectorCopy(point, vecShifted); else VectorAdd(point, shift, vecShifted); /* texture directional vectors and offsets */ sdir = surf->texinfo->uv; soff = surf->texinfo->u_offset; tdir = surf->texinfo->vv; toff = surf->texinfo->v_offset; /* texture coordinates */ s = DotProduct(point, sdir) + soff; s /= surf->texinfo->image->width; t = DotProduct(point, tdir) + toff; t /= surf->texinfo->image->height; mod->bsp.texcoords[texCoordOfs + 0] = s; mod->bsp.texcoords[texCoordOfs + 1] = t; if (surf->flags & MSURF_LIGHTMAP) { /* lightmap coordinates */ s = DotProduct(point, sdir) + soff; s -= surf->stmins[0]; s += surf->light_s * surf->lightmap_scale; s += surf->lightmap_scale / 2.0; s /= r_lightmaps.size * surf->lightmap_scale; t = DotProduct(point, tdir) + toff; t -= surf->stmins[1]; t += surf->light_t * surf->lightmap_scale; t += surf->lightmap_scale / 2.0; t /= r_lightmaps.size * surf->lightmap_scale; } mod->bsp.lmtexcoords[texCoordOfs + 0] = s; mod->bsp.lmtexcoords[texCoordOfs + 1] = t; /* normal vectors */ if ((surf->texinfo->flags & SURF_PHONG) && VectorNotEmpty(vert->normal)) normal = vert->normal; /* phong shaded */ else normal = surf->normal; /* per plane */ memcpy(&mod->bsp.normals[vertOfs], normal, sizeof(vec3_t)); /* tangent vector */ TangentVectors(normal, sdir, tdir, tangent, binormal); memcpy(&mod->bsp.tangents[tangOfs], tangent, sizeof(vec4_t)); vertOfs += 3; texCoordOfs += 2; tangOfs += 4; } } R_ReallocateStateArrays(vertOfs / 3); if (qglBindBuffer) { /* and also the vertex buffer objects */ qglGenBuffers(1, &mod->bsp.vertex_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.vertex_buffer); qglBufferData(GL_ARRAY_BUFFER, vertOfs * sizeof(GLfloat), mod->bsp.verts, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.texcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.texcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, texCoordOfs * sizeof(GLfloat), mod->bsp.texcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.lmtexcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.lmtexcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, texCoordOfs * sizeof(GLfloat), mod->bsp.lmtexcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.normal_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.normal_buffer); qglBufferData(GL_ARRAY_BUFFER, vertOfs * sizeof(GLfloat), mod->bsp.normals, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.tangent_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.tangent_buffer); qglBufferData(GL_ARRAY_BUFFER, tangOfs * sizeof(GLfloat), mod->bsp.tangents, GL_STATIC_DRAW); qglBindBuffer(GL_ARRAY_BUFFER, 0); } }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] map The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, routing_t *map, const char *name, const char **list) { const cBspModel_t *model; pos3_t min, max; unsigned int i; double start, end; start = time(NULL); /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } Com_DPrintf(DEBUG_PATHING, "Model:%s origin(%f,%f,%f) angles(%f,%f,%f) mins(%f,%f,%f) maxs(%f,%f,%f)\n", name, model->origin[0], model->origin[1], model->origin[2], model->angles[0], model->angles[1], model->angles[2], model->mins[0], model->mins[1], model->mins[2], model->maxs[0], model->maxs[1], model->maxs[2]); /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, minVec); VecToPos(minVec, min); VectorAdd(maxVec, model->origin, maxVec); VecToPos(maxVec, max); } else { /* normal */ vec3_t temp; /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, temp); VecToPos(temp, min); VectorAdd(model->maxs, model->origin, temp); VecToPos(temp, max); } /* fit min/max into the world size */ max[0] = min(max[0] + 1, PATHFINDING_WIDTH - 1); max[1] = min(max[1] + 1, PATHFINDING_WIDTH - 1); max[2] = min(max[2] + 1, PATHFINDING_HEIGHT - 1); for (i = 0; i < 3; i++) min[i] = max(min[i] - 1, 0); /* We now have the dimensions, call the generic rerouting function. */ Grid_RecalcBoxRouting(mapTiles, map, min, max, list); end = time(NULL); Com_DPrintf(DEBUG_ROUTING, "Retracing for model %s between (%i, %i, %i) and (%i, %i %i) in %5.1fs\n", name, min[0], min[1], min[2], max[0], max[1], max[2], end - start); }
/** * @sa CM_AddMapTile * @sa R_ModBeginLoading * @param[in] name The name of the map. Relative to maps/ and without extension * @param[in] day Load the day lightmap * @param[in] sX Shift x grid units * @param[in] sY Shift y grid units * @param[in] sZ Shift z grid units * @sa UNIT_SIZE */ static void R_ModAddMapTile (const char *name, qboolean day, int sX, int sY, int sZ) { int i; byte *buffer; dBspHeader_t *header; const int lightingLump = day ? LUMP_LIGHTING_DAY : LUMP_LIGHTING_NIGHT; /* get new model */ if (r_numModels < 0 || r_numModels >= MAX_MOD_KNOWN) Com_Error(ERR_DROP, "R_ModAddMapTile: r_numModels >= MAX_MOD_KNOWN"); if (r_numMapTiles < 0 || r_numMapTiles >= MAX_MAPTILES) Com_Error(ERR_DROP, "R_ModAddMapTile: Too many map tiles"); /* alloc model and tile */ r_worldmodel = &r_models[r_numModels++]; r_mapTiles[r_numMapTiles++] = r_worldmodel; OBJZERO(*r_worldmodel); Com_sprintf(r_worldmodel->name, sizeof(r_worldmodel->name), "maps/%s.bsp", name); /* load the file */ FS_LoadFile(r_worldmodel->name, &buffer); if (!buffer) Com_Error(ERR_DROP, "R_ModAddMapTile: %s not found", r_worldmodel->name); /* init */ r_worldmodel->type = mod_bsp; /* prepare shifting */ VectorSet(shift, sX * UNIT_SIZE, sY * UNIT_SIZE, sZ * UNIT_SIZE); /* test version */ header = (dBspHeader_t *) buffer; i = LittleLong(header->version); if (i != BSPVERSION) Com_Error(ERR_DROP, "R_ModAddMapTile: %s has wrong version number (%i should be %i)", r_worldmodel->name, i, BSPVERSION); /* swap all the lumps */ mod_base = (byte *) header; for (i = 0; i < (int)sizeof(dBspHeader_t) / 4; i++) ((int *) header)[i] = LittleLong(((int *) header)[i]); /* load into heap */ R_ModLoadVertexes(&header->lumps[LUMP_VERTEXES]); R_ModLoadNormals(&header->lumps[LUMP_NORMALS]); R_ModLoadEdges(&header->lumps[LUMP_EDGES]); R_ModLoadSurfedges(&header->lumps[LUMP_SURFEDGES]); R_ModLoadLighting(&header->lumps[lightingLump]); R_ModLoadPlanes(&header->lumps[LUMP_PLANES]); R_ModLoadTexinfo(&header->lumps[LUMP_TEXINFO]); R_ModLoadSurfaces(day, &header->lumps[LUMP_FACES]); R_ModLoadLeafs(&header->lumps[LUMP_LEAFS]); R_ModLoadNodes(&header->lumps[LUMP_NODES]); R_ModLoadSubmodels(&header->lumps[LUMP_MODELS]); R_SetupSubmodels(); R_LoadBspVertexArrays(r_worldmodel); /* in case of random map assembly shift some vectors */ if (VectorNotEmpty(shift)) R_ModShiftTile(); FS_FreeFile(buffer); }
/** * @brief Puts the map data into buffers * @sa R_ModAddMapTile * @note Shift the verts after the texcoords for diffuse and lightmap are loaded * @sa R_ModShiftTile * @todo Don't use the buffers from r_state here - they might overflow * @todo Decrease MAX_GL_ARRAY_LENGTH to 32768 again when this is fixed */ static void R_LoadBspVertexArrays (model_t *mod) { int i, j; int vertind, coordind, tangind; float *vecShifted; float soff, toff, s, t; float *point, *sdir, *tdir; vec4_t tangent; vec3_t binormal; mBspSurface_t *surf; mBspVertex_t *vert; int vertexcount; vertind = coordind = tangind = vertexcount = 0; for (i = 0, surf = mod->bsp.surfaces; i < mod->bsp.numsurfaces; i++, surf++) for (j = 0; j < surf->numedges; j++) vertexcount++; surf = mod->bsp.surfaces; /* allocate the vertex arrays */ mod->bsp.texcoords = (GLfloat *)Mem_PoolAlloc(vertexcount * 2 * sizeof(GLfloat), vid_modelPool, 0); mod->bsp.lmtexcoords = (GLfloat *)Mem_PoolAlloc(vertexcount * 2 * sizeof(GLfloat), vid_modelPool, 0); mod->bsp.verts = (GLfloat *)Mem_PoolAlloc(vertexcount * 3 * sizeof(GLfloat), vid_modelPool, 0); mod->bsp.normals = (GLfloat *)Mem_PoolAlloc(vertexcount * 3 * sizeof(GLfloat), vid_modelPool, 0); mod->bsp.tangents = (GLfloat *)Mem_PoolAlloc(vertexcount * 4 * sizeof(GLfloat), vid_modelPool, 0); for (i = 0; i < mod->bsp.numsurfaces; i++, surf++) { surf->index = vertind / 3; for (j = 0; j < surf->numedges; j++) { const float *normal; const int index = mod->bsp.surfedges[surf->firstedge + j]; if (vertind >= MAX_GL_ARRAY_LENGTH * 3) Com_Error(ERR_DROP, "R_LoadBspVertexArrays: Exceeded MAX_GL_ARRAY_LENGTH %i", vertind); /* vertex */ if (index > 0) { /* negative indices to differentiate which end of the edge */ const mBspEdge_t *edge = &mod->bsp.edges[index]; vert = &mod->bsp.vertexes[edge->v[0]]; } else { const mBspEdge_t *edge = &mod->bsp.edges[-index]; vert = &mod->bsp.vertexes[edge->v[1]]; } point = vert->position; /* shift it for assembled maps */ vecShifted = &mod->bsp.verts[vertind]; /* origin (func_door, func_rotating) bmodels must not have shifted vertices, * they are translated by their entity origin value */ if (surf->isOriginBrushModel) VectorCopy(point, vecShifted); else VectorAdd(point, shift, vecShifted); /* texture directional vectors and offsets */ sdir = surf->texinfo->uv; soff = surf->texinfo->u_offset; tdir = surf->texinfo->vv; toff = surf->texinfo->v_offset; /* texture coordinates */ s = DotProduct(point, sdir) + soff; s /= surf->texinfo->image->width; t = DotProduct(point, tdir) + toff; t /= surf->texinfo->image->height; mod->bsp.texcoords[coordind + 0] = s; mod->bsp.texcoords[coordind + 1] = t; if (surf->flags & MSURF_LIGHTMAP) { /* lightmap coordinates */ s = DotProduct(point, sdir) + soff; s -= surf->stmins[0]; s += surf->light_s * surf->lightmap_scale; s += surf->lightmap_scale / 2.0; s /= r_lightmaps.size * surf->lightmap_scale; t = DotProduct(point, tdir) + toff; t -= surf->stmins[1]; t += surf->light_t * surf->lightmap_scale; t += surf->lightmap_scale / 2.0; t /= r_lightmaps.size * surf->lightmap_scale; } mod->bsp.lmtexcoords[coordind + 0] = s; mod->bsp.lmtexcoords[coordind + 1] = t; /* normal vectors */ if (surf->texinfo->flags & SURF_PHONG && VectorNotEmpty(vert->normal)) normal = vert->normal; /* phong shaded */ else normal = surf->normal; /* per plane */ memcpy(&mod->bsp.normals[vertind], normal, sizeof(vec3_t)); /* tangent vector */ TangentVectors(normal, sdir, tdir, tangent, binormal); memcpy(&mod->bsp.tangents[tangind], tangent, sizeof(vec4_t)); vertind += 3; coordind += 2; tangind += 4; } } if (qglBindBuffer) { /* and also the vertex buffer objects */ qglGenBuffers(1, &mod->bsp.vertex_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.vertex_buffer); qglBufferData(GL_ARRAY_BUFFER, vertind * sizeof(GLfloat), mod->bsp.verts, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.texcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.texcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, coordind * sizeof(GLfloat), mod->bsp.texcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.lmtexcoord_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.lmtexcoord_buffer); qglBufferData(GL_ARRAY_BUFFER, coordind * sizeof(GLfloat), mod->bsp.lmtexcoords, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.normal_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.normal_buffer); qglBufferData(GL_ARRAY_BUFFER, vertind * sizeof(GLfloat), mod->bsp.normals, GL_STATIC_DRAW); qglGenBuffers(1, &mod->bsp.tangent_buffer); qglBindBuffer(GL_ARRAY_BUFFER, mod->bsp.tangent_buffer); qglBufferData(GL_ARRAY_BUFFER, tangind * sizeof(GLfloat), mod->bsp.tangents, GL_STATIC_DRAW); qglBindBuffer(GL_ARRAY_BUFFER, 0); } }
/** * @brief Prepares the particle rendering, calculate new position, velocity and all the * other particle values that are needed to display it * @sa CL_ParticleRun * @param[in,out] p The particle to handle */ static void CL_ParticleRun2 (ptl_t *p) { /* advance time */ p->dt = cls.frametime; p->t = (cl.time - p->startTime) * 0.001f; p->lastThink += p->dt; p->lastFrame += p->dt; if (p->rounds && !p->roundsCnt) p->roundsCnt = p->rounds; /* test for end of life */ if (p->life && p->t >= p->life && !p->parent) { CL_ParticleFree(p); return; /* don't play the weather particles if a user don't want them there can * be a lot of weather particles - which might slow the computer down */ } else if (p->weather && !cl_particleweather->integer) { CL_ParticleFree(p); return; } /* kinematics */ if (p->style != STYLE_LINE) { VectorMA(p->s, 0.5 * p->dt * p->dt, p->a, p->s); VectorMA(p->s, p->dt, p->v, p->s); VectorMA(p->v, p->dt, p->a, p->v); VectorMA(p->angles, p->dt, p->omega, p->angles); } /* basic 'physics' for particles */ if (p->physics) { vec3_t mins, maxs; const float size = std::max(p->size[0], p->size[1]); if (p->hitSolid && p->bounce) { VectorCopy(p->oldV, p->v); VectorNegate(p->a, p->a); p->hitSolid = false; } /* if the particle hit a solid already and is sticking to the surface, no further * traces are needed */ if (p->hitSolid && p->stick) return; VectorSet(mins, -size, -size, -size); VectorSet(maxs, size, size, size); const trace_t tr = PTL_Trace(p, mins, maxs); /* hit something solid */ if (tr.fraction < 1.0 || tr.startsolid) { p->hitSolid = true; /* now execute the physics handler */ if (p->ctrl->physics) CL_ParticleFunction(p, p->ctrl->physics); /* let them stay on the ground until they fade out or die */ if (!p->stayalive) { CL_ParticleFree(p); return; } else if (p->bounce) { /* bounce */ vec3_t temp; VectorCopy(p->v, p->oldV); VectorScale(tr.plane.normal, -DotProduct(tr.plane.normal, p->v), temp); VectorAdd(temp, p->v, temp); VectorAdd(temp, temp, p->v); VectorNegate(p->a, p->a); } else { VectorClear(p->v); } VectorCopy(tr.endpos, p->s); } } /* run */ CL_ParticleFunction(p, p->ctrl->run); /* think */ while (p->tps && p->lastThink * p->tps >= 1) { CL_ParticleFunction(p, p->ctrl->think); p->lastThink -= 1.0 / p->tps; } /* animate */ while (p->fps && p->lastFrame * p->fps >= 1) { /* advance frame */ p->frame++; if (p->frame > p->endFrame) p->frame = 0; p->lastFrame -= 1.0 / p->fps; /* load next frame */ assert(p->pic); p->pic = CL_ParticleGetArt(p->pic->name, p->frame, ART_PIC); } /* fading */ if (p->thinkFade || p->frameFade) { const bool onlyAlpha = (p->blend == BLEND_BLEND); if (!onlyAlpha) Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f); else p->color[3] = 1.0; if (p->thinkFade) CL_Fading(p->color, p->thinkFade, p->lastThink * p->tps, onlyAlpha); if (p->frameFade) CL_Fading(p->color, p->frameFade, p->lastFrame * p->fps, onlyAlpha); } /* this is useful for particles like weather effects that are on top of * some other brushes in higher level but should be visible in lower ones */ if (p->autohide) { const int z = (int)p->s[2] / UNIT_HEIGHT; if (z > cl_worldlevel->integer) { p->invis = true; return; } else if (z < 0) { CL_ParticleFree(p); return; } } /* add light to the scene */ if (VectorNotEmpty(p->lightColor)) { const float intensity = 0.5 + p->lightIntensity; if (p->lightSustain) R_AddSustainedLight(p->s, intensity * PTL_INTENSITY_TO_RADIUS, p->lightColor, p->lightSustain); else R_AddLight(p->s, intensity * PTL_INTENSITY_TO_RADIUS, p->lightColor); } /* set the new origin */ VectorCopy(p->s, p->origin); p->invis = false; }
/** * @brief * @sa FinalLightFace */ void BuildFacelights (unsigned int facenum) { dBspSurface_t* face; dBspPlane_t* plane; dBspTexinfo_t* tex; float* center; float* sdir, *tdir; vec3_t normal, binormal; vec4_t tangent; lightinfo_t li; float scale; int i, j, numsamples; facelight_t* fl; int* headhints; const int grid_type = config.soft ? 1 : 0; if (facenum >= MAX_MAP_FACES) { Com_Printf("MAX_MAP_FACES hit\n"); return; } face = &curTile->faces[facenum]; plane = &curTile->planes[face->planenum]; tex = &curTile->texinfo[face->texinfo]; if (tex->surfaceFlags & SURF_WARP) return; /* non-lit texture */ sdir = tex->vecs[0]; tdir = tex->vecs[1]; /* lighting -extra antialiasing */ if (config.extrasamples) numsamples = config.soft ? SOFT_SAMPLES : MAX_SAMPLES; else numsamples = 1; OBJZERO(li); scale = 1.0 / numsamples; /* each sample contributes this much */ li.face = face; li.facedist = plane->dist; VectorCopy(plane->normal, li.facenormal); /* negate the normal and dist */ if (face->side) { VectorNegate(li.facenormal, li.facenormal); li.facedist = -li.facedist; } /* get the origin offset for rotating bmodels */ VectorCopy(face_offset[facenum], li.modelorg); /* calculate lightmap texture mins and maxs */ CalcLightinfoExtents(&li); /* and the lightmap texture vectors */ CalcLightinfoVectors(&li); /* now generate all of the sample points */ CalcPoints(&li, 0, 0); fl = &facelight[config.compile_for_day][facenum]; fl->numsamples = li.numsurfpt; fl->samples = Mem_AllocTypeN(vec3_t, fl->numsamples); fl->directions = Mem_AllocTypeN(vec3_t, fl->numsamples); center = face_extents[facenum].center; /* center of the face */ /* Also setup the hints. Each hint is specific to each light source, including sunlight. */ headhints = Mem_AllocTypeN(int, (numlights[config.compile_for_day] + 1)); /* calculate light for each sample */ for (i = 0; i < fl->numsamples; i++) { float* const sample = fl->samples[i]; /* accumulate lighting here */ float* const direction = fl->directions[i]; /* accumulate direction here */ if (tex->surfaceFlags & SURF_PHONG) /* interpolated normal */ SampleNormal(&li, li.surfpt[i], normal); else /* or just plane normal */ VectorCopy(li.facenormal, normal); for (j = 0; j < numsamples; j++) { /* with antialiasing */ vec3_t pos; /* add offset for supersampling */ VectorMA(li.surfpt[i], sampleofs[grid_type][j][0] * li.step, li.textoworld[0], pos); VectorMA(pos, sampleofs[grid_type][j][1] * li.step, li.textoworld[1], pos); NudgeSamplePosition(pos, normal, center, pos); GatherSampleLight(pos, normal, sample, direction, scale, headhints); } if (VectorNotEmpty(direction)) { vec3_t dir; /* normalize it */ VectorNormalize(direction); /* finalize the lighting direction for the sample */ TangentVectors(normal, sdir, tdir, tangent, binormal); dir[0] = DotProduct(direction, tangent); dir[1] = DotProduct(direction, binormal); dir[2] = DotProduct(direction, normal); VectorCopy(dir, direction); } } /* Free the hints. */ Mem_Free(headhints); for (i = 0; i < fl->numsamples; i++) { /* pad them */ float* const direction = fl->directions[i]; if (VectorEmpty(direction)) VectorSet(direction, 0.0, 0.0, 1.0); } /* free the sample positions for the face */ Mem_Free(li.surfpt); }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] routing The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] box The box around the inline model (alternative to name) * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t* mapTiles, Routing& routing, const char* name, const GridBox& box, const char** list) { if (box.isZero()) { /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } const cBspModel_t* model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } AABB absbox; #if 1 /* An attempt to fix the 'doors starting opened' bug (# 3456). * The main difference is the (missing) rotation of the halfVec. * The results are better, but do not fix the problem. */ CalculateMinsMaxs(model->angles, model->cbmBox, model->origin, absbox); #else /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ model->cbmBox.getCenter(centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->cbmBox.maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ absbox.set(minVec, maxVec); absbox.shift(model->origin); } else { /* normal */ /* Now offset by origin then convert to position (Doors do not have 0 origins) */ absbox.set(model->cbmBox); absbox.shift(model->origin); } #endif GridBox rerouteBox(absbox); /* fit min/max into the world size */ rerouteBox.clipToMaxBoundaries(); /* We now have the dimensions, call the generic rerouting function. */ Grid_RecalcBoxRouting(mapTiles, routing, rerouteBox, list); } else { /* use the passed box */ Grid_RecalcBoxRouting(mapTiles, routing, box, list); } }
/** * @brief Parsed map entites and brushes * @sa ParseBrush * @param[in] filename The map filename * @param[in] entityString The body of the entity we are parsing */ static bool ParseMapEntity (const char* filename, const char* entityString) { entity_t* mapent; const char* entName; static int worldspawnCount = 0; int notCheckOrFix = !(config.performMapCheck || config.fixMap); if (Q_strnull(GetToken())) return false; if (*parsedToken != '{') Sys_Error("ParseMapEntity: { not found"); if (num_entities == MAX_MAP_ENTITIES) Sys_Error("num_entities == MAX_MAP_ENTITIES (%i)", num_entities); mapent = &entities[num_entities++]; OBJZERO(*mapent); mapent->firstbrush = nummapbrushes; mapent->numbrushes = 0; do { if (Q_strnull(GetToken())) Sys_Error("ParseMapEntity: EOF without closing brace"); if (*parsedToken == '}') break; if (*parsedToken == '{') ParseBrush(mapent, filename); else { epair_t* e = ParseEpair(num_entities); e->next = mapent->epairs; mapent->epairs = e; } } while (true); GetVectorForKey(mapent, "origin", mapent->origin); entName = ValueForKey(mapent, "classname"); /* offset all of the planes and texinfo if needed */ if (IsInlineModelEntity(entName) && VectorNotEmpty(mapent->origin)) AdjustBrushesForOrigin(mapent); if (num_entities == 1 && !Q_streq("worldspawn", entName)) Sys_Error("The first entity must be worldspawn, it is: %s", entName); if (notCheckOrFix && Q_streq("func_group", entName)) { /* group entities are just for editor convenience * toss all brushes into the world entity */ MoveBrushesToWorld(mapent); num_entities--; } else if (IsInlineModelEntity(entName)) { if (mapent->numbrushes == 0 && notCheckOrFix) { Com_Printf("Warning: %s has no brushes assigned (entnum: %i)\n", entName, num_entities); num_entities--; } } else if (Q_streq("worldspawn", entName)) { worldspawnCount++; if (worldspawnCount > 1) Com_Printf("Warning: more than one %s in one map\n", entName); const char* text = entityString; do { const char* token = Com_Parse(&text); if (Q_strnull(token)) break; const char* key = Mem_StrDup(token); token = Com_Parse(&text); if (Q_strnull(token)) break; const char* value = Mem_StrDup(token); epair_t* e = AddEpair(key, value, num_entities); e->next = mapent->epairs; mapent->epairs = e; } while (true); } return true; }
/** @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); }
/** * @brief Draw a model from the battlescape entity list * @sa R_GetEntityLists */ void R_DrawAliasModel (entity_t *e) { mAliasModel_t *mod = &e->model->alias; /* the values are sane here already - see R_GetEntityLists */ const image_t *skin = mod->meshes[e->as.mesh].skins[e->skinnum].skin; int i; float g; vec4_t color = {0.8, 0.8, 0.8, 1.0}; mAliasMesh_t *mesh; /* IR goggles override color for entities that are affected */ if ((refdef.rendererFlags & RDF_IRGOGGLES) && (e->flags & RF_IRGOGGLES)) Vector4Set(e->shell, 1.0, 0.3, 0.3, 1.0); if (e->flags & RF_PULSE) { /* and then adding in a pulse */ const float f = 1.0 + sin((refdef.time + (e->model->alias.meshes[0].num_tris)) * 6.0) * 0.33; VectorScale(color, 1.0 + f, color); } g = 0.0; /* find brightest component */ for (i = 0; i < 3; i++) { if (color[i] > g) /* keep it */ g = color[i]; } /* scale it back to 1.0 */ if (g > 1.0) VectorScale(color, 1.0 / g, color); R_Color(color); assert(skin->texnum > 0); R_BindTexture(skin->texnum); R_EnableGlowMap(skin->glowmap); R_UpdateLightList(e); R_EnableModelLights(e->lights, e->numLights, e->inShadow, true); /** @todo this breaks the encapsulation - don't call CL_* functions from within the renderer code */ if (r_debug_lights->integer) { for (i = 0; i < e->numLights && i < r_dynamic_lights->integer; i++) CL_ParticleSpawn("lightTracerDebug", 0, e->transform.matrix + 12, e->lights[i]->origin); } if (skin->normalmap) R_EnableBumpmap(skin->normalmap); if (skin->specularmap) R_EnableSpecularMap(skin->specularmap, true); if (skin->roughnessmap) R_EnableRoughnessMap(skin->roughnessmap, true); glPushMatrix(); glMultMatrixf(e->transform.matrix); if (VectorNotEmpty(e->scale)) glScalef(e->scale[0], e->scale[1], e->scale[2]); mesh = R_DrawAliasModelBuffer(e); if (r_state.specularmap_enabled) R_EnableSpecularMap(NULL, false); if (r_state.roughnessmap_enabled) R_EnableRoughnessMap(NULL, false); R_EnableModelLights(NULL, 0, false, false); R_EnableGlowMap(NULL); if (r_state.active_normalmap) R_EnableBumpmap(NULL); R_DrawMeshShadow(e, mesh); if (mod->num_frames == 1) R_ResetArraysAfterStaticMeshRender(); glPopMatrix(); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mod->frames[e->as.frame].mins, mod->frames[e->as.frame].maxs); R_Color(NULL); }