/** * @brief Renders text and images * @sa SEQ_InitStartup * @param[in] context Sequence context * @param[in] backgroundObjects if true, draw background objects, else display foreground objects */ static void SEQ_Render2D (sequenceContext_t *context, bool backgroundObjects) { seq2D_t *s2d; int i, j; int height = 0; /* add texts */ for (i = 0, s2d = context->obj2Ds; i < context->numObj2Ds; i++, s2d++) { if (!s2d->inuse) continue; if (backgroundObjects != s2d->inBackground) continue; if (s2d->relativePos && height > 0) { s2d->pos[1] += height; s2d->relativePos = false; } /* advance in time */ for (j = 0; j < 4; j++) { s2d->color[j] += cls.frametime * s2d->fade[j]; if (s2d->color[j] < 0.0) s2d->color[j] = 0.0; else if (s2d->color[j] > 1.0) s2d->color[j] = 1.0; } for (j = 0; j < 2; j++) { s2d->pos[j] += cls.frametime * s2d->speed[j]; s2d->size[j] += cls.frametime * s2d->enlarge[j]; } /* outside the screen? */ /** @todo We need this check - but this does not work */ /*if (s2d->pos[1] >= VID_NORM_HEIGHT || s2d->pos[0] >= VID_NORM_WIDTH) continue;*/ /* render */ R_Color(s2d->color); /* image can be background */ if (s2d->image[0] != '\0') { const image_t *image = R_FindImage(s2d->image, it_pic); R_DrawImage(s2d->pos[0], s2d->pos[1], image); } /* bgcolor can be overlay */ if (s2d->bgcolor[3] > 0.0) R_DrawFill(s2d->pos[0], s2d->pos[1], s2d->size[0], s2d->size[1], s2d->bgcolor); /* render */ R_Color(s2d->color); /* gettext placeholder */ if (s2d->text) { int maxWidth = (int) s2d->size[0]; if (maxWidth <= 0) maxWidth = VID_NORM_WIDTH; height += UI_DrawString(s2d->font, s2d->align, s2d->pos[0], s2d->pos[1], s2d->pos[0], maxWidth, -1 /** @todo use this for some nice line spacing */, _(s2d->text)); } } R_Color(NULL); }
/** * @brief Draw the day and night images of a flat geoscape * multitexture feature is used to blend the images * @sa R_Draw3DGlobe * @param[in] p The horizontal shift of the night map * @param[in] cx The x texture coordinate * @param[in] cy The y texture coordinate * @param[in] iz The zoomlevel of the geoscape - see ccs.zoom * @param[in] map The geoscape map to draw (can be changed in the campaign definition) * @param[in] overlayNation,overlayXVI,overlayRadar Whether these overlays should be drawn or not */ void R_DrawFlatGeoscape (const vec2_t nodePos, const vec2_t nodeSize, float p, float cx, float cy, float iz, const char *map, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t *r_dayandnightTexture, image_t *r_xviTexture, image_t *r_radarTexture) { image_t *gl; float geoscape_texcoords[4 * 2]; short geoscape_verts[4 * 2]; /* normalize */ const float nx = nodePos[0] * viddef.rx; const float ny = nodePos[1] * viddef.ry; const float nw = nodeSize[0] * viddef.rx; const float nh = nodeSize[1] * viddef.ry; /* load day image */ gl = R_FindImage(va("pics/geoscape/%s_day", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape day image"); /* alter the array pointers */ glVertexPointer(2, GL_SHORT, 0, geoscape_verts); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords); geoscape_texcoords[0] = cx - iz; geoscape_texcoords[1] = cy - iz; geoscape_texcoords[2] = cx + iz; geoscape_texcoords[3] = cy - iz; geoscape_texcoords[4] = cx + iz; geoscape_texcoords[5] = cy + iz; geoscape_texcoords[6] = cx - iz; geoscape_texcoords[7] = cy + iz; geoscape_verts[0] = nx; geoscape_verts[1] = ny; geoscape_verts[2] = nx + nw; geoscape_verts[3] = ny; geoscape_verts[4] = nx + nw; geoscape_verts[5] = ny + nh; geoscape_verts[6] = nx; geoscape_verts[7] = ny + nh; /* draw day image */ R_BindTexture(gl->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; /* draw night map */ gl = R_FindImage(va("pics/geoscape/%s_night", map), it_wrappic); /* maybe the campaign map doesn't have a night image */ if (gl != r_noTexture) { float geoscape_nighttexcoords[4 * 2]; R_BindTexture(gl->texnum); R_EnableTexture(&texunit_lightmap, true); R_SelectTexture(&texunit_lightmap); geoscape_nighttexcoords[0] = geoscape_texcoords[0] + p; geoscape_nighttexcoords[1] = geoscape_texcoords[1]; geoscape_nighttexcoords[2] = geoscape_texcoords[2] + p; geoscape_nighttexcoords[3] = geoscape_texcoords[3]; geoscape_nighttexcoords[4] = geoscape_texcoords[4] + p; geoscape_nighttexcoords[5] = geoscape_texcoords[5]; geoscape_nighttexcoords[6] = geoscape_texcoords[6] + p; geoscape_nighttexcoords[7] = geoscape_texcoords[7]; R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_nighttexcoords); R_BindTexture(r_dayandnightTexture->texnum); R_SelectTexture(&texunit_diffuse); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; R_SelectTexture(&texunit_lightmap); R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, geoscape_texcoords); R_EnableTexture(&texunit_lightmap, false); } /* draw nation overlay */ if (overlayNation) { gl = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image"); /* draw day image */ R_BindTexture(gl->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; } /* draw XVI image */ if (overlayXVI) { gl = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic); if (gl == r_noTexture) Com_Error(ERR_FATAL, "Could not load xvi overlay image"); R_BindTexture(gl->texnum); R_EnableTexture(&texunit_lightmap, true); R_BindLightmapTexture(r_xviTexture->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; R_EnableTexture(&texunit_lightmap, false); } /* draw radar image */ if (overlayRadar) { R_BindTexture(r_radarTexture->texnum); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; } /* and restore them */ R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); }
/** * @brief Draws shadow and highlight effects for the entities (actors) * @note The origins are already transformed */ void R_DrawEntityEffects (void) { const int mask = r_stencilshadows->integer ? RF_BLOOD : (RF_SHADOW | RF_BLOOD); GLint oldDepthFunc; glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc); R_EnableBlend(true); if (actorIndicator == nullptr) { selectedActorIndicator = R_FindImage("pics/sfx/actor_selected", it_effect); actorIndicator = R_FindImage("pics/sfx/actor", it_effect); } for (int i = 0; i < refdef.numEntities; i++) { const entity_t* e = &r_entities[i]; if (e->flags <= RF_BOX) continue; glPushMatrix(); glMultMatrixf(e->transform.matrix); if (e->flags & mask) { const vec3_t points[] = { { -18.0, 14.0, -28.5 }, { 10.0, 14.0, -28.5 }, { 10.0, -14.0, -28.5 }, { -18.0, -14.0, -28.5 } }; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; if (e->flags & RF_SHADOW) { R_BindTexture(shadow->texnum); } else { assert(e->texture); R_BindTexture(e->texture->texnum); } R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); refdef.batchCount++; } if (e->flags & RF_ACTOR) { const float size = 15.0; int texnum; /* draw the circles for team-members and allied troops */ vec4_t color = {1, 1, 1, 1}; const vec3_t points[] = { { -size, size, -SELECTION_DELTA }, { size, size, -SELECTION_DELTA }, { size, -size, -SELECTION_DELTA }, { -size, -size, -SELECTION_DELTA } }; /** @todo use default_texcoords */ const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } }; if (e->flags & RF_SELECTED) Vector4Set(color, 0, 1, 0, 0.5); else if (e->flags & RF_MEMBER) Vector4Set(color, 0, 0.8, 0, 0.5); else if (e->flags & RF_ALLIED) Vector4Set(color, 0, 1, 0.5, 0.5); else if (e->flags & RF_NEUTRAL) Vector4Set(color, 1, 1, 0, 0.5); else if (e->flags & RF_OPPONENT) Vector4Set(color, 1, 0, 0, 0.5); else Vector4Set(color, 0.3, 0.3, 0.3, 0.5); if (e->flags & RF_SELECTED) texnum = selectedActorIndicator->texnum; else texnum = actorIndicator->texnum; R_BindTexture(texnum); R_Color(color); R_EnableDrawAsGlow(true); /* circle points */ R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords); R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); refdef.batchCount++; /* add transparency when something is in front of the circle */ color[3] *= 0.25; R_Color(color); glDepthFunc(GL_GREATER); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDepthFunc(oldDepthFunc); refdef.batchCount++; R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY); R_BindDefaultArray(GL_VERTEX_ARRAY); R_Color(nullptr); R_EnableDrawAsGlow(false); } glPopMatrix(); } }
/** * @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->mapMin[0]; tile->mapY = cl.mapData->mapMin[1]; tile->mapWidth = cl.mapData->getWidthX(); tile->mapHeight = cl.mapData->getWidthY(); } } if (tile->isTile) { tile->mapY = cl.mapData->mapMax[1] - tile->mapY - tile->mapHeight; } } /* center tiles into the minMap/maxMap */ Vector2Copy(cl.mapData->mapMax, min); Vector2Copy(cl.mapData->mapMin, 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->mapMin[0] + (cl.mapData->getWidthX() - (max[0] - min[0])) * 0.5 - min[0]; min[1] = cl.mapData->mapMin[1] + (cl.mapData->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->mapMin, offset, radar.a); VectorAdd(cl.mapData->mapMax, 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.0, fabs(gridSize[0])); const float radarHeight = std::max(1.0, fabs(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 responsible for drawing the 3d globe on geoscape * param[in] rotate the rotate angle of the globe * param[in] zoom the current globe zoon * param[in] map the prefix of the map to use (image must be at base/pics/menu/\<map\>_[day|night]) * @sa R_DrawFlatGeoscape * @sa R_SphereGenerate */ void R_Draw3DGlobe (const vec2_t pos, const vec2_t size, int day, int second, const vec3_t rotate, float zoom, const char *map, bool disableSolarRender, float ambient, bool overlayNation, bool overlayXVI, bool overlayRadar, image_t *r_xviTexture, image_t *r_radarTexture, bool renderNationGlow) { /* globe scaling */ const float fullscale = zoom / STANDARD_3D_ZOOM; /* lighting colors */ static const vec4_t diffuseLightColor = { 1.75f, 1.75f, 1.75f, 1.0f }; static const vec4_t specularLightColor = { 2.0f, 1.9f, 1.7f, 1.0f }; static const vec4_t darknessLightColor = { 0.0f, 0.0f, 0.0f, 1.0f }; static const vec4_t brightDiffuseLightColor = { 5.0f, 5.0f, 5.0f, 1.0f }; const vec4_t ambientLightColor = { ambient + 0.2f, ambient + 0.2f, ambient + 0.2f, ambient + 0.2f }; /* billboard textures */ image_t *starfield; image_t *halo; image_t *sun; image_t *sunOverlay; /* set distance of the sun and moon to make them static on starfield when * time is stoped. this distance should be used for any celestial body * considered at infinite location (sun, moon) */ static const float celestialDist = 1.37f * SKYBOX_HALFSIZE; static const float moonSize = 0.025f; vec4_t sunPos; vec4_t antiSunPos; vec4_t moonLoc; vec4_t sunLoc; /* normalize */ const float nx = pos[0] * viddef.rx; const float ny = pos[1] * viddef.ry; const float nw = size[0] * viddef.rx; const float nh = size[1] * viddef.ry; /* Earth center is in the middle of node. * Due to Orthographic view, this is also camera position */ const vec3_t earthPos = { nx + nw / 2.0f, ny + nh / 2.0f, 0.0f }; /* estimate the progress through the current season so we can do * smooth transitions between textures. Currently there are 12 * "seasons", because we have one image per Earth-month. */ const float season = (float) (day % DAYS_PER_YEAR) / ((float) (DAYS_PER_YEAR) / (float) (SEASONS_PER_YEAR)); const int currSeason = (int) floorf(season) % SEASONS_PER_YEAR; const int nextSeason = (int) ceilf(season) % SEASONS_PER_YEAR; const float seasonProgress = season - (float) currSeason; /* Compute sun position in absolute frame */ const float q = (day % DAYS_PER_YEAR * SECONDS_PER_DAY + second) * (2.0f * M_PI / (SECONDS_PER_DAY * DAYS_PER_YEAR)); /* sun rotation (year) */ const float a = cos(q) * SIN_ALPHA; /* due to earth obliquity */ const float sqrta = sqrt(0.5f * (1 - a * a)); /* earth rotation (day) */ const float p = (second - SECONDS_PER_DAY / 4) * (2.0f * M_PI / SECONDS_PER_DAY); /* lunar orbit */ const float m = p + (((double)((10 * day % 249) / 10.0f) + ((double)second / (double)SECONDS_PER_DAY)) / 24.9f) * (2.0f * M_PI); glPushMatrix(); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_LIGHTING); /* draw the starfield, rotating with the planet */ starfield = R_FindImage(va("pics/geoscape/%s_stars", map), it_wrappic); if (starfield != r_noTexture) R_DrawStarfield(starfield->texnum, earthPos, rotate, p); glPopMatrix(); /* set up position vectors for celestial bodies */ Vector4Set(sunPos, cos(p) * sqrta, -sin(p) * sqrta, a, 0); Vector4Set(antiSunPos, -cos(p) * sqrta, sin(p) * sqrta, -a, 0); /* Rotate the sun in the relative frame of player view, to get sun location */ R_RotateCelestialBody(sunPos, sunLoc, rotate, earthPos, 1.0f); /* load sun texture image */ sun = R_FindImage(va("pics/geoscape/%s_sun", map), it_wrappic); sunOverlay = R_FindImage(va("pics/geoscape/%s_sun_overlay", map), it_pic); if (sun != r_noTexture && sunOverlay != r_noTexture && sunLoc[2] > 0 && !disableSolarRender) { const int sunx = earthPos[0] + viddef.rx * (-128.0f + celestialDist * (sunLoc[0] - earthPos[0])); const int suny = earthPos[1] + viddef.ry * (-128.0f + celestialDist * (sunLoc[1] - earthPos[1])); R_DrawTexture(sunOverlay->texnum, sunx, suny, 256.0f * viddef.rx, 256.0f * viddef.ry); R_DrawBuffers(2); R_DrawTexture(sun->texnum, sunx, suny, 256.0 * viddef.rx, 256.0 * viddef.ry); R_DrawBuffers(1); } /* calculate position of the moon (it rotates around earth with a period of * about 24.9 h, and we must take day into account to avoid moon to "jump" * every time the day is changing) */ VectorSet(moonLoc, cos(m) * sqrta, -sin(m) * sqrta, a); R_RotateCelestialBody(moonLoc, moonLoc, rotate, earthPos, celestialDist); /* free last month's texture image */ if (r_globeEarth.season != currSeason) { r_globeEarth.season = currSeason; R_FreeImage(r_globeEarth.texture); } /* load diffuse texture map (with embedded night-glow map as alpha channel) */ r_globeEarth.texture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, currSeason), it_wrappic); if (r_globeEarth.texture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, currSeason); /* set up for advanced GLSL rendering if we have the capability */ if (r_programs->integer) { r_globeEarth.glslProgram = r_state.geoscape_program; /* load earth image for the next month so we can blend them */ r_globeEarth.blendTexture = R_FindImage(va("pics/geoscape/%s/%s_season_%02d", r_config.lodDir, map, nextSeason), it_wrappic); if (r_globeEarth.blendTexture == r_noTexture) Com_Error(ERR_FATAL, "Could not find pics/geoscape/%s/%s_season_%02d\n", r_config.lodDir, map, nextSeason); /* load normal map (with embedded gloss map as alpha channel) */ r_globeEarth.normalMap = R_FindImage(va("pics/geoscape/%s/%s_bump", r_config.lodDir, map), it_wrappic); if (r_globeEarth.normalMap == r_noTexture) r_globeEarth.normalMap = nullptr; /* weight the blending based on how much of the month has elapsed */ r_globeEarth.blendScale = seasonProgress; /* set up lights for nighttime city glow */ VectorCopy(antiSunPos, r_globeEarth.nightLightPos); glLightfv(GL_LIGHT1, GL_AMBIENT, darknessLightColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, brightDiffuseLightColor); glLightfv(GL_LIGHT1, GL_SPECULAR, darknessLightColor); r_globeEarth.glowScale = 0.7f; } /* load moon texture image */ r_globeMoon.texture = R_FindImage(va("pics/geoscape/%s_moon", map), it_wrappic); /* globe texture scaling */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(2.0f, 1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); /* enable the lighting */ glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLightColor); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLightColor); glLightfv(GL_LIGHT0, GL_SPECULAR, specularLightColor); /* draw the moon */ if (r_globeMoon.texture != r_noTexture && moonLoc[2] > 0 && !disableSolarRender) R_SphereRender(&r_globeMoon, moonLoc, rotate, moonSize, sunPos); /* activate depth to hide 3D models behind earth */ glEnable(GL_DEPTH_TEST); /* draw the earth */ R_DrawBuffers(2); #if 0 /* old rendering code which doesn't render city lights in FFP */ if (r_programs->integer == 0) /* ignore alpha channel, since the city-light map is stored there */ glBlendFunc(GL_ONE, GL_ZERO); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) /* restore default blend function */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #else /* new which does render city lights in FFP */ if (r_programs->integer == 0) { /* set up rendering of city lights map, which is stored in alpha channel; OpenGL 1.3 required */ R_SelectTexture(&texunit_diffuse); /* select texture to edit texture environment for */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); /* enable color combiner */ /* setup texture combiner to blend between daylight diffuse map stored in the RGB channels of the diffuse texture * and the monochomatic emission map (which simulates city lights) stored in the alpha channel; * incoming color value is the blend factor. */ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); /* set day color as blending target*/ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); /* set night color as blending source */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PREVIOUS); /* set incoming color as blending factor */ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); /* set blending mode to interpolation from src1 to src0 */ /* copy alpha from incoming color, bypassing the value read from texture, which is not a "real" alpha anyway */ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); } R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (r_programs->integer == 0) { /* disable combiner */ R_SelectTexture(&texunit_diffuse); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } #endif r_globeEarthAtmosphere.texture = R_FindImage(va("pics/geoscape/%s_atmosphere", map), it_wrappic); /* Draw earth atmosphere */ /** @todo render atmosphere glow even when postprocessing is disabled */ if (r_programs->integer && r_postprocess->integer) { r_globeEarthAtmosphere.normalMap = r_globeEarth.normalMap; r_globeEarthAtmosphere.glowScale = 1.0; r_globeEarthAtmosphere.blendScale = -1.0; r_globeEarthAtmosphere.glslProgram = r_state.atmosphere_program; R_SphereRender(&r_globeEarthAtmosphere, earthPos, rotate, fullscale, sunPos); } else { halo = R_FindImage("pics/geoscape/map_earth_halo", it_pic); if (halo != r_noTexture) { /** @todo Replace this magic number with some speaking constant */ const float earthSizeX = fullscale * 20500.0f * viddef.rx; const float earthSizeY = fullscale * 20500.0f * viddef.ry; glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glDisable(GL_LIGHTING); R_DrawTexture(halo->texnum, earthPos[0] - earthSizeX * 0.5f, earthPos[1] - earthSizeY * 0.5f, earthSizeX, earthSizeY); glEnable(GL_LIGHTING); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } } R_DrawBuffers(1); glDisable(GL_DEPTH_TEST); /* draw nation overlay */ if (overlayNation) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay image"); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); if (renderNationGlow) { /* draw glowing borders */ r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_nations_overlay_glow", map), it_wrappic); if (r_globeEarth.overlay == r_noTexture) Com_Error(ERR_FATAL, "Could not load geoscape nation overlay glow image"); R_DrawBuffers(2); glDisable(GL_LIGHTING); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); glEnable(GL_LIGHTING); R_DrawBuffers(1); } r_globeEarth.overlay = nullptr; } /* draw XVI overlay */ if (overlayXVI) { r_globeEarth.overlay = R_FindImage(va("pics/geoscape/%s_xvi_overlay", map), it_wrappic); r_globeEarth.overlayAlphaMask = r_xviTexture; assert(r_globeEarth.overlayAlphaMask); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlayAlphaMask = nullptr; r_globeEarth.overlay = nullptr; } /* draw radar overlay */ if (overlayRadar) { r_globeEarth.overlay = r_radarTexture; assert(r_globeEarth.overlay); R_SphereRender(&r_globeEarth, earthPos, rotate, fullscale, sunPos); r_globeEarth.overlay = nullptr; } /* disable 3d geoscape lighting */ glDisable(GL_LIGHTING); /* restore the previous matrix */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); }
/** * @brief Load material definitions for each map that has one * @param[in] map the base name of the map to load the material for */ void R_LoadMaterials (const char *map) { char path[MAX_QPATH]; byte *fileBuffer; const char *buffer; bool inmaterial; image_t *image; material_t *m; materialStage_t *ss; /* clear previously loaded materials */ R_ImageClearMaterials(); if (map[0] == '+' || map[0] == '-') map++; else if (map[0] == '-') return; /* load the materials file for parsing */ Com_sprintf(path, sizeof(path), "materials/%s.mat", Com_SkipPath(map)); if (FS_LoadFile(path, &fileBuffer) < 1) { Com_DPrintf(DEBUG_RENDERER, "Couldn't load %s\n", path); return; } else { Com_Printf("load material file: '%s'\n", path); if (!r_materials->integer) Com_Printf("...ignore materials (r_materials is deactivated)\n"); } buffer = (const char *)fileBuffer; inmaterial = false; image = nullptr; m = nullptr; while (true) { const char *c = Com_Parse(&buffer); if (c[0] == '\0') break; if (*c == '{' && !inmaterial) { inmaterial = true; continue; } if (Q_streq(c, "material")) { c = Com_Parse(&buffer); image = R_GetImage(va("textures/%s", c)); if (image == nullptr) Com_DPrintf(DEBUG_RENDERER, "R_LoadMaterials: skip texture: %s - not used in the map\n", c); continue; } if (!image) continue; m = &image->material; if (Q_streq(c, "normalmap")){ c = Com_Parse(&buffer); image->normalmap = R_FindImage(va("textures/%s", c), it_normalmap); if (image->normalmap == r_noTexture){ Com_Printf("R_LoadMaterials: Failed to resolve normalmap: %s\n", c); image->normalmap = nullptr; } } if (Q_streq(c, "glowmap")){ c = Com_Parse(&buffer); image->glowmap = R_FindImage(va("textures/%s", c), it_glowmap); if (image->glowmap == r_noTexture){ Com_Printf("R_LoadMaterials: Failed to resolve glowmap: %s\n", c); image->glowmap = nullptr; } } if (Q_streq(c, "bump")) { m->bump = atof(Com_Parse(&buffer)); if (m->bump < 0.0) { Com_Printf("R_LoadMaterials: Invalid bump value for %s\n", image->name); m->bump = defaultMaterial.bump; } } if (Q_streq(c, "parallax")) { m->parallax = atof(Com_Parse(&buffer)); if (m->parallax < 0.0) { Com_Printf("R_LoadMaterials: Invalid parallax value for %s\n", image->name); m->parallax = defaultMaterial.parallax; } } if (Q_streq(c, "hardness")) { m->hardness = atof(Com_Parse(&buffer)); if (m->hardness < 0.0) { Com_Printf("R_LoadMaterials: Invalid hardness value for %s\n", image->name); m->hardness = defaultMaterial.hardness; } } if (Q_streq(c, "specular")) { m->specular = atof(Com_Parse(&buffer)); if (m->specular < 0.0) { Com_Printf("R_LoadMaterials: Invalid specular value for %s\n", image->name); m->specular = defaultMaterial.specular; } } if (Q_streq(c, "glowscale")) { m->glowscale = atof(Com_Parse(&buffer)); if (m->glowscale < 0.0) { Com_Printf("R_LoadMaterials: Invalid glowscale value for %s\n", image->name); m->glowscale = defaultMaterial.glowscale; } } if (*c == '{' && inmaterial) { materialStage_t* const s = Mem_PoolAllocType(materialStage_t, vid_imagePool); s->glowscale = defaultMaterial.glowscale; if (R_ParseStage(s, &buffer) == -1) { Mem_Free(s); continue; } /* load animation frame images */ if (s->flags & STAGE_ANIM) { if (R_LoadAnimImages(s) == -1) { Mem_Free(s); continue; } } /* append the stage to the chain */ if (!m->stages) m->stages = s; else { ss = m->stages; while (ss->next) ss = ss->next; ss->next = s; } m->flags |= s->flags; m->num_stages++; continue; } if (*c == '}' && inmaterial) { Com_DPrintf(DEBUG_RENDERER, "Parsed material %s with %d stages\n", image->name, m->num_stages); inmaterial = false; image = nullptr; /* multiply stage glowscale values by material glowscale */ ss = m->stages; while (ss) { ss->glowscale *= m->glowscale; ss = ss->next; } } } FS_FreeFile(fileBuffer); R_CreateMaterialData(); }
void LoadMD2(model_t *mod, void *buffer, int modfilelen) { int i, j; dmdl_t *pinmodel, *pheader; dstvert_t *pinst, *poutst; dtriangle_t *pintri, *pouttri; daliasframe_t *pinframe, *poutframe; int *pincmd, *poutcmd; int version; int ofs_end; pinmodel = (dmdl_t *)buffer; version = LittleLong(pinmodel->version); if (version != ALIAS_VERSION) { ri.Sys_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); } ofs_end = LittleLong(pinmodel->ofs_end); if (ofs_end < 0 || ofs_end > modfilelen) ri.Sys_Error (ERR_DROP, "model %s file size(%d) too small, should be %d", mod->name, modfilelen, ofs_end); mod->extradata = Hunk_Begin(modfilelen); pheader = Hunk_Alloc(ofs_end); /* byte swap the header fields and sanity check */ for (i = 0; i < sizeof(dmdl_t) / 4; i++) { ((int *)pheader)[i] = LittleLong(((int *)buffer)[i]); } if (pheader->skinheight > MAX_LBM_HEIGHT) { ri.Sys_Error(ERR_DROP, "model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); } if (pheader->num_xyz <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no vertices", mod->name); } if (pheader->num_xyz > MAX_VERTS) { ri.Sys_Error(ERR_DROP, "model %s has too many vertices", mod->name); } if (pheader->num_st <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no st vertices", mod->name); } if (pheader->num_tris <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no triangles", mod->name); } if (pheader->num_frames <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no frames", mod->name); } /* load base s and t vertices (not used in gl version) */ pinst = (dstvert_t *)((byte *)pinmodel + pheader->ofs_st); poutst = (dstvert_t *)((byte *)pheader + pheader->ofs_st); for (i = 0; i < pheader->num_st; i++) { poutst[i].s = LittleShort(pinst[i].s); poutst[i].t = LittleShort(pinst[i].t); } /* load triangle lists */ pintri = (dtriangle_t *)((byte *)pinmodel + pheader->ofs_tris); pouttri = (dtriangle_t *)((byte *)pheader + pheader->ofs_tris); for (i = 0; i < pheader->num_tris; i++) { for (j = 0; j < 3; j++) { pouttri[i].index_xyz[j] = LittleShort(pintri[i].index_xyz[j]); pouttri[i].index_st[j] = LittleShort(pintri[i].index_st[j]); } } /* load the frames */ for (i = 0; i < pheader->num_frames; i++) { pinframe = (daliasframe_t *)((byte *)pinmodel + pheader->ofs_frames + i * pheader->framesize); poutframe = (daliasframe_t *)((byte *)pheader + pheader->ofs_frames + i * pheader->framesize); memcpy(poutframe->name, pinframe->name, sizeof(poutframe->name)); for (j = 0; j < 3; j++) { poutframe->scale[j] = LittleFloat(pinframe->scale[j]); poutframe->translate[j] = LittleFloat(pinframe->translate[j]); } /* verts are all 8 bit, so no swapping needed */ memcpy(poutframe->verts, pinframe->verts, pheader->num_xyz * sizeof(dtrivertx_t)); } mod->type = mod_alias; /* load the glcmds */ pincmd = (int *)((byte *)pinmodel + pheader->ofs_glcmds); poutcmd = (int *)((byte *)pheader + pheader->ofs_glcmds); for (i = 0; i < pheader->num_glcmds; i++) { poutcmd[i] = LittleLong(pincmd[i]); } /* register all skins */ memcpy((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins, pheader->num_skins * MAX_SKINNAME); for (i = 0; i < pheader->num_skins; i++) { mod->skins[i] = R_FindImage( (char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME, it_skin); } mod->mins[0] = -32; mod->mins[1] = -32; mod->mins[2] = -32; mod->maxs[0] = 32; mod->maxs[1] = 32; mod->maxs[2] = 32; }
/** * @brief Material stage parser * @sa R_LoadMaterials */ static int R_ParseStage (materialStage_t *s, const char **buffer) { int i; while (true) { const char *c = Com_Parse(buffer); if (c[0] == '\0') break; if (Q_streq(c, "glowscale")) { s->glowscale = atof(Com_Parse(buffer)); if (s->glowscale < 0.0) { Com_Printf("R_LoadMaterials: Invalid glowscale value for %s\n", c); s->glowscale = defaultMaterial.glowscale; } continue; } if (Q_streq(c, "texture")) { c = Com_Parse(buffer); s->image = R_FindImage(va("textures/%s", c), it_material); if (s->image == r_noTexture) { Com_Printf("R_ParseStage: Failed to resolve texture: %s\n", c); return -1; } s->flags |= STAGE_TEXTURE; continue; } if (Q_streq(c, "envmap")) { c = Com_Parse(buffer); i = atoi(c); if (i > -1 && i < MAX_ENVMAPTEXTURES) s->image = r_envmaptextures[i]; else s->image = R_FindImage(va("pics/envmaps/%s", c), it_material); if (s->image == r_noTexture) { Com_Printf("R_ParseStage: Failed to resolve envmap: %s\n", c); return -1; } s->flags |= STAGE_ENVMAP; continue; } if (Q_streq(c, "blend")) { c = Com_Parse(buffer); s->blend.src = R_ConstByName(c); if (s->blend.src == -1) { Com_Printf("R_ParseStage: Failed to resolve blend src: %s\n", c); return -1; } c = Com_Parse(buffer); s->blend.dest = R_ConstByName(c); if (s->blend.dest == -1) { Com_Printf("R_ParseStage: Failed to resolve blend dest: %s\n", c); return -1; } s->flags |= STAGE_BLEND; continue; } if (Q_streq(c, "color")) { for (i = 0; i < 3; i++) { c = Com_Parse(buffer); s->color[i] = atof(c); if (s->color[i] < 0.0 || s->color[i] > 1.0) { Com_Printf("R_ParseStage: Failed to resolve color: %s\n", c); return -1; } } s->flags |= STAGE_COLOR; continue; } if (Q_streq(c, "pulse")) { c = Com_Parse(buffer); s->pulse.hz = atof(c); s->pulse.dutycycle = 1.0; if (s->pulse.hz < 0.0) { Com_Printf("R_ParseStage: Failed to resolve frequency: %s\n", c); return -1; } s->flags |= STAGE_PULSE; continue; } if (Q_streq(c, "dutycycle")) { c = Com_Parse(buffer); s->pulse.dutycycle = atof(c); if (s->pulse.dutycycle < 0.0 || s->pulse.dutycycle > 1.0) { Com_Printf("R_ParseStage: Failed to resolve pulse duty cycle: %s\n", c); return -1; } continue; } if (Q_streq(c, "stretch")) { c = Com_Parse(buffer); s->stretch.amp = atof(c); if (s->stretch.amp < 0.0) { Com_Printf("R_ParseStage: Failed to resolve amplitude: %s\n", c); return -1; } c = Com_Parse(buffer); s->stretch.hz = atof(c); if (s->stretch.hz < 0.0) { Com_Printf("R_ParseStage: Failed to resolve frequency: %s\n", c); return -1; } s->flags |= STAGE_STRETCH; continue; } if (Q_streq(c, "rotate")) { c = Com_Parse(buffer); s->rotate.hz = atof(c); if (s->rotate.hz < 0.0) { Com_Printf("R_ParseStage: Failed to resolve rotate: %s\n", c); return -1; } s->flags |= STAGE_ROTATE; continue; } if (Q_streq(c, "scroll.s")) { c = Com_Parse(buffer); s->scroll.s = atof(c); s->flags |= STAGE_SCROLL_S; continue; } if (Q_streq(c, "scroll.t")) { c = Com_Parse(buffer); s->scroll.t = atof(c); s->flags |= STAGE_SCROLL_T; continue; } if (Q_streq(c, "scale.s")) { c = Com_Parse(buffer); s->scale.s = atof(c); s->flags |= STAGE_SCALE_S; continue; } if (Q_streq(c, "scale.t")) { c = Com_Parse(buffer); s->scale.t = atof(c); s->flags |= STAGE_SCALE_T; continue; } if (Q_streq(c, "terrain")) { c = Com_Parse(buffer); s->terrain.floor = atof(c); c = Com_Parse(buffer); s->terrain.ceil = atof(c); if (s->terrain.ceil < s->terrain.floor) { Com_Printf("R_ParseStage: Inverted terrain ceiling and floor " "values for %s\n", (s->image ? s->image->name : "nullptr")); return -1; } s->terrain.height = s->terrain.ceil - s->terrain.floor; if (s->terrain.height == 0.0) { Com_Printf("R_ParseStage: Zero height terrain specified for %s\n", (s->image ? s->image->name : "nullptr")); return -1; } s->flags |= STAGE_TERRAIN; continue; } if (Q_streq(c, "tape")) { c = Com_Parse(buffer); s->tape.center = atof(c); /* how much downwards? */ c = Com_Parse(buffer); s->tape.floor = atof(c); /* how much upwards? */ c = Com_Parse(buffer); s->tape.ceil = atof(c); s->tape.min = s->tape.center - s->tape.floor; s->tape.max = s->tape.center + s->tape.ceil; s->tape.height = s->tape.floor + s->tape.ceil; if (s->tape.height == 0.0) { Com_Printf("R_ParseStage: Zero height tape specified for %s\n", (s->image ? s->image->name : "nullptr")); return -1; } s->flags |= STAGE_TAPE; continue; } if (Q_streq(c, "dirtmap")) { c = Com_Parse(buffer); s->dirt.intensity = atof(c); if (s->dirt.intensity <= 0.0 || s->dirt.intensity > 1.0) { Com_Printf("R_ParseStage: Invalid dirtmap intensity for %s\n", (s->image ? s->image->name : "nullptr")); return -1; } s->flags |= STAGE_DIRTMAP; continue; } if (char const* const rest = Q_strstart(c, "anim")) { switch (rest[0]) { case 'a': s->anim.type = ANIM_ALTERNATE; break; case 'b': s->anim.type = ANIM_BACKWARDS; break; case 'r': s->anim.type = ANIM_RANDOM; break; case 'f': s->anim.type = ANIM_RANDOMFORCE; break; default: s->anim.type = ANIM_NORMAL; break; } c = Com_Parse(buffer); s->anim.num_frames = atoi(c); if (s->anim.num_frames < 1 || s->anim.num_frames > MAX_ANIM_FRAMES) { Com_Printf("R_ParseStage: Invalid number of anim frames for %s (max is %i)\n", (s->image ? s->image->name : "nullptr"), MAX_ANIM_FRAMES); return -1; } c = Com_Parse(buffer); s->anim.fps = atof(c); if (s->anim.fps <= 0) { Com_Printf("R_ParseStage: Invalid anim fps for %s\n", (s->image ? s->image->name : "nullptr")); return -1; } /* the frame images are loaded once the stage is parsed completely */ s->flags |= STAGE_ANIM; continue; } if (Q_streq(c, "glowmaplink")) { s->flags |= STAGE_GLOWMAPLINK; continue; } if (Q_streq(c, "lightmap")) { s->flags |= STAGE_LIGHTMAP; continue; } if (Q_streq(c, "flare")) { c = Com_Parse(buffer); i = atoi(c); if (i > -1 && i < NUM_FLARETEXTURES) s->image = r_flaretextures[i]; else s->image = R_FindImage(va("pics/flares/%s", c), it_material); if (s->image == r_noTexture) { Com_Printf("R_ParseStage: Failed to resolve flare: %s\n", c); return -1; } s->flags |= STAGE_FLARE; continue; } if (*c == '}') { Com_DPrintf(DEBUG_RENDERER, "Parsed stage\n" " flags: %d\n" " image: %s\n" " blend: %d %d\n" " color: %3f %3f %3f\n" " pulse: %3f\n" " pulse duty cycle: %1.2f\n" " stretch: %3f %3f\n" " rotate: %3f\n" " scroll.s: %3f\n" " scroll.t: %3f\n" " scale.s: %3f\n" " scale.t: %3f\n" " terrain.floor: %5f\n" " terrain.ceil: %5f\n" " anim.num_frames: %d\n" " anim.fps: %3f\n", s->flags, (s->image ? s->image->name : "nullptr"), s->blend.src, s->blend.dest, s->color[0], s->color[1], s->color[2], s->pulse.hz, s->pulse.dutycycle, s->stretch.amp, s->stretch.hz, s->rotate.hz, s->scroll.s, s->scroll.t, s->scale.s, s->scale.t, s->terrain.floor, s->terrain.ceil, s->anim.num_frames, s->anim.fps); /* a texture or envmap means render it */ if (s->flags & (STAGE_TEXTURE | STAGE_ENVMAP)) s->flags |= STAGE_RENDER; if (s->flags & (STAGE_TERRAIN | STAGE_DIRTMAP)) s->flags |= STAGE_LIGHTING; return 0; } Com_Printf("Invalid token: '%s'\n", c); } Com_Printf("R_ParseStage: Malformed stage\n"); return -1; }
void Mod_LoadTexinfo(lump_t *l) { texinfo_t *in; mtexinfo_t *out, *step; int i, j, count; char name[MAX_QPATH]; int next; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = LittleFloat(in->vecs[0][j]); out->vecs[1][j] = LittleFloat(in->vecs[1][j]); } out->flags = LittleLong(in->flags); next = LittleLong(in->nexttexinfo); if (next > 0) { out->next = loadmodel->texinfo + next; } else { out->next = NULL; } Com_sprintf(name, sizeof(name), "textures/%s.wal", in->texture); out->image = R_FindImage(name, it_wall); if (!out->image) { R_Printf(PRINT_ALL, "Couldn't load %s\n", name); out->image = r_notexture; } } /* count animation frames */ for (i = 0; i < count; i++) { out = &loadmodel->texinfo[i]; out->numframes = 1; for (step = out->next; step && step != out; step = step->next) { out->numframes++; } } }
/* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j; dmdl_t *pinmodel, *pheader; dstvert_t *pinst, *poutst; dtriangle_t *pintri, *pouttri; daliasframe_t *pinframe, *poutframe; int *pincmd, *poutcmd; int version; pinmodel = (dmdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) ri.Sys_Error (ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); pheader = Hunk_Alloc (LittleLong(pinmodel->ofs_end)); // byte swap the header fields and sanity check for (i=0 ; i<sizeof(dmdl_t)/4 ; i++) ((int *)pheader)[i] = LittleLong (((int *)buffer)[i]); if (pheader->skinheight > MAX_LBM_HEIGHT) ri.Sys_Error (ERR_DROP, "model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); if (pheader->num_xyz <= 0) ri.Sys_Error (ERR_DROP, "model %s has no vertices", mod->name); if (pheader->num_xyz > MAX_VERTS) ri.Sys_Error (ERR_DROP, "model %s has too many vertices", mod->name); if (pheader->num_st <= 0) ri.Sys_Error (ERR_DROP, "model %s has no st vertices", mod->name); if (pheader->num_tris <= 0) ri.Sys_Error (ERR_DROP, "model %s has no triangles", mod->name); if (pheader->num_frames <= 0) ri.Sys_Error (ERR_DROP, "model %s has no frames", mod->name); // // load base s and t vertices (not used in gl version) // pinst = (dstvert_t *) ((byte *)pinmodel + pheader->ofs_st); poutst = (dstvert_t *) ((byte *)pheader + pheader->ofs_st); for (i=0 ; i<pheader->num_st ; i++) { poutst[i].s = LittleShort (pinst[i].s); poutst[i].t = LittleShort (pinst[i].t); } // // load triangle lists // pintri = (dtriangle_t *) ((byte *)pinmodel + pheader->ofs_tris); pouttri = (dtriangle_t *) ((byte *)pheader + pheader->ofs_tris); for (i=0 ; i<pheader->num_tris ; i++) { for (j=0 ; j<3 ; j++) { pouttri[i].index_xyz[j] = LittleShort (pintri[i].index_xyz[j]); pouttri[i].index_st[j] = LittleShort (pintri[i].index_st[j]); } } // // load the frames // for (i=0 ; i<pheader->num_frames ; i++) { pinframe = (daliasframe_t *) ((byte *)pinmodel + pheader->ofs_frames + i * pheader->framesize); poutframe = (daliasframe_t *) ((byte *)pheader + pheader->ofs_frames + i * pheader->framesize); memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name)); for (j=0 ; j<3 ; j++) { poutframe->scale[j] = LittleFloat (pinframe->scale[j]); poutframe->translate[j] = LittleFloat (pinframe->translate[j]); } // verts are all 8 bit, so no swapping needed memcpy (poutframe->verts, pinframe->verts, pheader->num_xyz*sizeof(dtrivertx_t)); } mod->type = mod_alias; // // load the glcmds // pincmd = (int *) ((byte *)pinmodel + pheader->ofs_glcmds); poutcmd = (int *) ((byte *)pheader + pheader->ofs_glcmds); for (i=0 ; i<pheader->num_glcmds ; i++) poutcmd[i] = LittleLong (pincmd[i]); // register all skins memcpy ((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins, pheader->num_skins*MAX_SKINNAME); for (i=0 ; i<pheader->num_skins ; i++) { mod->skins[i] = R_FindImage ((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin); } }
/* ================= Mod_LoadTexinfo ================= */ void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out, *step; int i, j, count; float len1, len2; char name[MAX_QPATH]; int next; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) ri.Sys_Error (ERR_DROP,"MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = Hunk_Alloc ( (count+6)*sizeof(*out)); // extra for skybox loadmodel->texinfo = out; loadmodel->numtexinfo = count; for ( i=0 ; i<count ; i++, in++, out++) { for (j=0 ; j<8 ; j++) out->vecs[0][j] = LittleFloat (in->vecs[0][j]); len1 = VectorLength (out->vecs[0]); len2 = VectorLength (out->vecs[1]); len1 = (len1 + len2)/2; if (len1 < 0.32) out->mipadjust = 4; else if (len1 < 0.49) out->mipadjust = 3; else if (len1 < 0.99) out->mipadjust = 2; else out->mipadjust = 1; #if 0 if (len1 + len2 < 0.001) out->mipadjust = 1; // don't crash else out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); #endif out->flags = LittleLong (in->flags); next = LittleLong (in->nexttexinfo); if (next > 0) out->next = loadmodel->texinfo + next; /* * PATCH: eliasm * * This patch fixes the problem where the game * domed core when loading a new level. */ else { out->next = NULL; } /* END OF PATCH */ Com_sprintf (name, sizeof(name), "textures/%s.wal", in->texture); out->image = R_FindImage (name, it_wall); if (!out->image) { out->image = r_notexture_mip; // texture not found out->flags = 0; } } // count animation frames for (i=0 ; i<count ; i++) { out = &loadmodel->texinfo[i]; out->numframes = 1; for (step = out->next ; step && step != out ; step=step->next) out->numframes++; } }
/* =============== RE_RegisterSkin =============== */ struct image_s *RE_RegisterSkin(char *name) { return R_FindImage(name, it_skin); }