int pie_AddTexPage(iV_Image *s, const char *filename, bool gameTexture, int page) { ASSERT(s && filename, "Bad input parameter"); if (page < 0) { iTexPage tex; page = _TEX_PAGE.size(); glGenTextures(1, &tex.id); sstrcpy(tex.name, filename); _TEX_PAGE.append(tex); } else // replace { sstrcpy(_TEX_PAGE[page].name, filename); } debug(LOG_TEXTURE, "%s page=%d", filename, page); pie_SetTexturePage(page); if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { glObjectLabel(GL_TEXTURE, pie_Texture(page), -1, filename); } if (gameTexture) // this is a game texture, use texture compression { gluBuild2DMipmaps(GL_TEXTURE_2D, wz_texture_compression, s->width, s->height, iV_getPixelFormat(s), GL_UNSIGNED_BYTE, s->bmp); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else // this is an interface texture, do not use compression { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s->width, s->height, 0, iV_getPixelFormat(s), GL_UNSIGNED_BYTE, s->bmp); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } // it is uploaded, we do not need it anymore free(s->bmp); s->bmp = NULL; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Use anisotropic filtering, if available, but only max 4.0 to reduce processor burden if (GLEW_EXT_texture_filter_anisotropic) { GLfloat max; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, MIN(4.0f, max)); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /* Send back the texpage number so we can store it in the IMD */ return page; }
/** Selects a texture page and binds it for the current texture unit * \param num a number indicating the texture page to bind. If this is a * negative value (doesn't matter what value) it will disable texturing. */ void pie_SetTexturePage(SDWORD num) { // Only bind textures when they're not bound already if (num != rendStates.texPage) { switch (num) { case TEXPAGE_NONE: glBindTexture(GL_TEXTURE_2D, 0); break; case TEXPAGE_EXTERN: break; default: glBindTexture(GL_TEXTURE_2D, pie_Texture(num)); } rendStates.texPage = num; } }
/** Selects a texture page and binds it for the current texture unit * \param num a number indicating the texture page to bind. If this is a * negative value (doesn't matter what value) it will disable texturing. */ void pie_SetTexturePage(SDWORD num) { // Only bind textures when they're not bound already if (num != rendStates.texPage) { switch (num) { case TEXPAGE_NONE: glDisable(GL_TEXTURE_2D); break; case TEXPAGE_EXTERN: // GLC will set the texture, we just need to enable texturing glEnable(GL_TEXTURE_2D); break; default: if (rendStates.texPage == TEXPAGE_NONE) { glEnable(GL_TEXTURE_2D); } glBindTexture(GL_TEXTURE_2D, pie_Texture(num)); } rendStates.texPage = num; } }
pie_internal::SHADER_PROGRAM &pie_ActivateShaderDeprecated(SHADER_MODE shaderMode, const iIMDShape *shape, PIELIGHT teamcolour, PIELIGHT colour, const glm::mat4 &ModelView, const glm::mat4 &Proj, const glm::vec4 &sunPos, const glm::vec4 &sceneColor, const glm::vec4 &ambient, const glm::vec4 &diffuse, const glm::vec4 &specular) { int maskpage = shape->tcmaskpage; int normalpage = shape->normalpage; int specularpage = shape->specularpage; pie_internal::SHADER_PROGRAM &program = pie_internal::shaderProgram[shaderMode]; if (shaderMode != pie_internal::currentShaderMode) { glUseProgram(program.program); // These do not change during our drawing pass glUniform1i(program.locations[4], rendStates.fog); glUniform1f(program.locations[9], timeState); pie_internal::currentShaderMode = shaderMode; } glUniform4fv(program.locations[0], 1, &pal_PIELIGHTtoVec4(colour)[0]); pie_SetTexturePage(shape->texpage); glUniform4fv(program.locations[1], 1, &pal_PIELIGHTtoVec4(teamcolour)[0]); glUniform1i(program.locations[3], maskpage != iV_TEX_INVALID); glUniform1i(program.locations[8], 0); glUniformMatrix4fv(program.locations[10], 1, GL_FALSE, glm::value_ptr(ModelView)); glUniformMatrix4fv(program.locations[11], 1, GL_FALSE, glm::value_ptr(Proj * ModelView)); glUniformMatrix4fv(program.locations[12], 1, GL_FALSE, glm::value_ptr(glm::transpose(glm::inverse(ModelView)))); glUniform4fv(program.locations[13], 1, &sunPos[0]); glUniform4fv(program.locations[14], 1, &sceneColor[0]); glUniform4fv(program.locations[15], 1, &ambient[0]); glUniform4fv(program.locations[16], 1, &diffuse[0]); glUniform4fv(program.locations[17], 1, &specular[0]); glUniform1f(program.locations[18], fogBegin); glUniform1f(program.locations[19], fogEnd); float color[4] = { rendStates.fogColour.vector[0] / 255.f, rendStates.fogColour.vector[1] / 255.f, rendStates.fogColour.vector[2] / 255.f, rendStates.fogColour.vector[3] / 255.f }; glUniform4fv(program.locations[20], 1, color); if (program.locations[2] >= 0) { glUniform1f(program.locations[2], shaderStretch); } if (program.locations[5] >= 0) { glUniform1i(program.locations[5], normalpage != iV_TEX_INVALID); } if (program.locations[6] >= 0) { glUniform1i(program.locations[6], specularpage != iV_TEX_INVALID); } if (program.locations[7] >= 0) { glUniform1i(program.locations[7], ecmState); } if (maskpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, pie_Texture(maskpage)); } if (normalpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, pie_Texture(normalpage)); } if (specularpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, pie_Texture(specularpage)); } glActiveTexture(GL_TEXTURE0); return program; }
/** * Draw the water. */ void drawWater(const glm::mat4 &viewMatrix) { int x, y; const glm::vec4 paramsX(0, 0, -1.0f / world_coord(4), 0); const glm::vec4 paramsY(1.0f / world_coord(4), 0, 0, 0); const glm::vec4 paramsX2(0, 0, -1.0f / world_coord(5), 0); const glm::vec4 paramsY2(1.0f / world_coord(5), 0, 0, 0); const auto &renderState = getCurrentRenderState(); const auto &program = pie_ActivateShader(SHADER_WATER, viewMatrix, paramsX, paramsY, paramsX2, paramsY2, 0, 1, glm::translate(glm::vec3(waterOffset, 0.f, 0.f)), glm::mat4(1.f), renderState.fogEnabled, renderState.fogBegin, renderState.fogEnd); glDepthMask(GL_FALSE); // first texture unit pie_SetTexturePage(iV_GetTexture("page-80-water-1.png")); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // multiplicative blending pie_SetRendMode(REND_MULTIPLICATIVE); // second texture unit glActiveTexture(GL_TEXTURE1); pie_Texture(iV_GetTexture("page-81-water-2.png")).bind(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // bind the vertex buffer waterIndexVBO->bind(); waterVBO->bind(); glVertexAttribPointer(program.locVertex, 3, GL_FLOAT, GL_FALSE, sizeof(RenderVertex), BUFFER_OFFSET(0)); glEnableVertexAttribArray(program.locVertex); for (x = 0; x < xSectors; x++) { for (y = 0; y < ySectors; y++) { if (sectors[x * ySectors + y].draw) { addDrawRangeElements(GL_TRIANGLES, sectors[x * ySectors + y].geometryOffset, sectors[x * ySectors + y].geometryOffset + sectors[x * ySectors + y].geometrySize, sectors[x * ySectors + y].waterIndexSize, GL_UNSIGNED_INT, sectors[x * ySectors + y].waterIndexOffset); } } } finishDrawRangeElements(); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableVertexAttribArray(program.locVertex); // move the water if (!gamePaused()) { waterOffset += graphicsTimeAdjustedIncrement(0.1f); } // disable second texture glActiveTexture(GL_TEXTURE0); // clean up glDepthMask(GL_TRUE); pie_DeactivateShader(); //glBindBuffer(GL_ARRAY_BUFFER, 0); // HACK Must unbind GL_ARRAY_BUFFER (don't know if it has to be unbound everywhere), otherwise text rendering may mysteriously crash. }
bool texLoad(const char *fileName) { char fullPath[PATH_MAX], partialPath[PATH_MAX], *buffer; unsigned int i, j, k, size; int texPage; GLint glval; firstPage = pie_NumberOfPages(); ASSERT_OR_RETURN(false, MIPMAP_MAX == TILE_WIDTH && MIPMAP_MAX == TILE_HEIGHT, "Bad tile sizes"); // store the filename so we can later determine which tileset we are using if (tilesetDir) { free(tilesetDir); } tilesetDir = strdup(fileName); // reset defaults mipmap_max = MIPMAP_MAX; mipmap_levels = MIPMAP_LEVELS; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glval); while (glval < mipmap_max * TILES_IN_PAGE_COLUMN) { mipmap_max /= 2; mipmap_levels--; debug(LOG_ERROR, "Max supported texture size %dx%d is too low - reducing texture detail to %dx%d.", (int)glval, (int)glval, mipmap_max, mipmap_max); ASSERT(mipmap_levels > 0, "Supported texture size %d is too low to load any mipmap levels!", (int)glval); if (mipmap_levels == 0) { exit(1); } } while (maxTextureSize < mipmap_max) { mipmap_max /= 2; mipmap_levels--; debug(LOG_TEXTURE, "Downgrading texture quality to %d due to user setting %d", mipmap_max, maxTextureSize); } /* Get and set radar colours */ sprintf(fullPath, "%s.radar", fileName); if (!loadFile(fullPath, &buffer, &size)) { debug(LOG_FATAL, "texLoad: Could not find radar colours at %s", fullPath); abort(); // cannot recover; we could possibly generate a random palette? } i = 0; // tile j = 0; // place in buffer do { unsigned int r, g, b; int cnt = 0; k = sscanf(buffer + j, "%2x%2x%2x%n", &r, &g, &b, &cnt); j += cnt; if (k >= 3) { radarColour(i, r, g, b); } i++; // next tile } while (k >= 3 && j + 6 < size); free(buffer); /* Now load the actual tiles */ i = mipmap_max; // i is used to keep track of the tile dimensions for (j = 0; j < mipmap_levels; j++) { int xOffset = 0, yOffset = 0; // offsets into the texture atlas int xSize = 1; int ySize = 1; const int xLimit = TILES_IN_PAGE_COLUMN * i; const int yLimit = TILES_IN_PAGE_ROW * i; // pad width and height into ^2 values while (xLimit > (xSize *= 2)) {} while (yLimit > (ySize *= 2)) {} // Generate the empty texture buffer in VRAM texPage = newPage(fileName, j, xSize, ySize, 0); sprintf(partialPath, "%s-%d", fileName, i); // Load until we cannot find anymore of them for (k = 0; k < MAX_TILES; k++) { iV_Image tile; snprintf(fullPath, sizeof(fullPath), "%s/tile-%02d.png", partialPath, k); if (PHYSFS_exists(fullPath)) // avoid dire warning { bool retval = iV_loadImage_PNG(fullPath, &tile); ASSERT_OR_RETURN(false, retval, "Could not load %s!", fullPath); } else { // no more textures in this set ASSERT_OR_RETURN(false, k > 0, "Could not find %s", fullPath); break; } // Insert into texture page pie_Texture(texPage).upload(j, xOffset, yOffset, tile.width, tile.height, gfx_api::pixel_format::rgba, tile.bmp); free(tile.bmp); if (i == mipmap_max) // dealing with main texture page; so register coordinates { tileTexInfo[k].uOffset = (float)xOffset / (float)xSize; tileTexInfo[k].vOffset = (float)yOffset / (float)ySize; tileTexInfo[k].texPage = texPage; debug(LOG_TEXTURE, " texLoad: Registering k=%d i=%d u=%f v=%f xoff=%d yoff=%d xsize=%d ysize=%d tex=%d (%s)", k, i, tileTexInfo[k].uOffset, tileTexInfo[k].vOffset, xOffset, yOffset, xSize, ySize, texPage, fullPath); } xOffset += i; // i is width of tile if (xOffset + i > xLimit) { yOffset += i; // i is also height of tile xOffset = 0; } if (yOffset + i > yLimit) { /* Change to next texture page */ xOffset = 0; yOffset = 0; debug(LOG_TEXTURE, "texLoad: Extra page added at %d for %s, was page %d, opengl id %u", k, partialPath, texPage, (unsigned)pie_Texture(texPage).id()); texPage = newPage(fileName, j, xSize, ySize, k); } } debug(LOG_TEXTURE, "texLoad: Found %d textures for %s mipmap level %d, added to page %d, opengl id %u", k, partialPath, i, texPage, (unsigned)pie_Texture(texPage).id()); i /= 2; // halve the dimensions for the next series; OpenGL mipmaps start with largest at level zero } return true; }
void pie_ActivateShader(int shaderMode, const iIMDShape *shape, PIELIGHT teamcolour, PIELIGHT colour) { int maskpage = shape->tcmaskpage; int normalpage = shape->normalpage; int specularpage = shape->specularpage; GLfloat colour4f[4]; SHADER_PROGRAM program = shaderProgram[shaderMode]; if (shaderMode != currentShaderMode) { glUseProgram(program.program); // These do not change during our drawing pass glUniform1i(program.locFog, rendStates.fog); glUniform1f(program.locTime, timeState); currentShaderMode = shaderMode; } glColor4ubv(colour.vector); pie_SetTexturePage(shape->texpage); pal_PIELIGHTtoRGBA4f(&colour4f[0], teamcolour); glUniform4fv(program.locTeam, 1, &colour4f[0]); glUniform1i(program.locTCMask, maskpage != iV_TEX_INVALID); if (program.locStretch >= 0) { glUniform1f(program.locStretch, shaderStretch); } if (program.locNormalMap >= 0) { glUniform1i(program.locNormalMap, normalpage != iV_TEX_INVALID); } if (program.locSpecularMap >= 0) { glUniform1i(program.locSpecularMap, specularpage != iV_TEX_INVALID); } if (program.locEcm >= 0) { glUniform1i(program.locEcm, ecmState); } if (maskpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, pie_Texture(maskpage)); } if (normalpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, pie_Texture(normalpage)); } if (specularpage != iV_TEX_INVALID) { glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, pie_Texture(specularpage)); } glActiveTexture(GL_TEXTURE0); #ifdef _DEBUG glErrors(); #endif }