// for low end hardware void LLDrawPoolWater::renderOpaqueLegacyWater() { LLVOSky *voskyp = gSky.mVOSkyp; stop_glerror(); // Depth sorting and write to depth buffer // since this is opaque, we should see nothing // behind the water. No blending because // of no transparency. And no face culling so // that the underside of the water is also opaque. LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); LLGLDisable no_cull(GL_CULL_FACE); LLGLDisable no_blend(GL_BLEND); gPipeline.disableLights(); mOpaqueWaterImagep->addTextureStats(1024.f*1024.f); // Activate the texture binding and bind one // texture since all images will have the same texture gGL.getTexUnit(0)->activate(); gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->bind(mOpaqueWaterImagep); // Automatically generate texture coords for water texture glEnable(GL_TEXTURE_GEN_S); //texture unit 0 glEnable(GL_TEXTURE_GEN_T); //texture unit 0 glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); // Use the fact that we know all water faces are the same size // to save some computation // Slowly move texture coordinates over time so the watter appears // to be moving. F32 movement_period_secs = 50.f; F32 offset = fmod(gFrameTimeSeconds, movement_period_secs); if (movement_period_secs != 0) { offset /= movement_period_secs; } else { offset = 0; } F32 tp0[4] = { 16.f / 256.f, 0.0f, 0.0f, offset }; F32 tp1[4] = { 0.0f, 16.f / 256.f, 0.0f, offset }; glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0); glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1); glColor3f(1.f, 1.f, 1.f); for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); iter != mDrawFace.end(); iter++) { LLFace *face = *iter; if (voskyp->isReflFace(face)) { continue; } face->renderIndexed(); } stop_glerror(); // Reset the settings back to expected values glDisable(GL_TEXTURE_GEN_S); //texture unit 0 glDisable(GL_TEXTURE_GEN_T); //texture unit 0 gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); }
// for low end hardware void LLDrawPoolWater::renderOpaqueLegacyWater() { LLVOSky *voskyp = gSky.mVOSkyp; LLGLSLShader* shader = NULL; if (LLGLSLShader::sNoFixedFunction) { if (LLPipeline::sUnderWaterRender) { shader = &gObjectSimpleNonIndexedTexGenWaterProgram; } else { shader = &gObjectSimpleNonIndexedTexGenProgram; } shader->bind(); } stop_glerror(); // Depth sorting and write to depth buffer // since this is opaque, we should see nothing // behind the water. No blending because // of no transparency. And no face culling so // that the underside of the water is also opaque. LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); LLGLDisable no_cull(GL_CULL_FACE); LLGLDisable no_blend(GL_BLEND); gPipeline.disableLights(); //Singu note: This is a hack around bizarre opensim behavior. The opaque water texture we get is pure white and only has one channel. // This behavior is clearly incorrect, so we try to detect that case, purge it from the cache, and try to re-fetch the texture. // If the re-fetched texture is still invalid, or doesn't exist, we use transparent water, which is fine since alphablend is unset. // The current logic for refetching is crude here, and probably wont work if, say, a prim were to also have the texture for some reason, // however it works well enough otherwise, and is much cleaner than diving into LLTextureList, LLViewerFetchedTexture, and LLViewerTexture. // Perhaps a proper reload mechanism could be done if we ever add user-level texture reloading, but until then it's not a huge priority. // Failing to fully refetch will just give us the same invalid texture we started with, which will result in the fallback texture being used. if(mOpaqueWaterImagep != mWaterImagep) { if(mOpaqueWaterImagep->isMissingAsset()) { mOpaqueWaterImagep = mWaterImagep; } else if(mOpaqueWaterImagep->hasGLTexture() && mOpaqueWaterImagep->getComponents() < 3) { LLAppViewer::getTextureCache()->removeFromCache(mOpaqueWaterImagep->getID()); static bool sRefetch = true; if(sRefetch) { sRefetch = false; ((LLViewerFetchedTexture*)mOpaqueWaterImagep.get())->forceRefetch(); } else mOpaqueWaterImagep = mWaterImagep; } } mOpaqueWaterImagep->addTextureStats(1024.f*1024.f); // Activate the texture binding and bind one // texture since all images will have the same texture gGL.getTexUnit(0)->activate(); gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->bind(mOpaqueWaterImagep); // Automatically generate texture coords for water texture if (!shader) { glEnable(GL_TEXTURE_GEN_S); //texture unit 0 glEnable(GL_TEXTURE_GEN_T); //texture unit 0 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); } // Use the fact that we know all water faces are the same size // to save some computation // Slowly move texture coordinates over time so the water appears // to be moving. F32 movement_period_secs = 50.f; static const LLCachedControl<bool> freeze_time("FreezeTime",false); static F32 frame_time; if (!freeze_time) frame_time = gFrameTimeSeconds; F32 offset = fmod(frame_time, movement_period_secs); if (movement_period_secs != 0) { offset /= movement_period_secs; } else { offset = 0; } F32 tp0[4] = { 16.f / 256.f, 0.0f, 0.0f, offset }; F32 tp1[4] = { 0.0f, 16.f / 256.f, 0.0f, offset }; if (!shader) { glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0); glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1); } else { shader->uniform4fv("object_plane_s", 1, tp0); shader->uniform4fv("object_plane_t", 1, tp1); } gGL.diffuseColor3f(1.f, 1.f, 1.f); for (std::vector<LLFace*>::iterator iter = mDrawFace.begin(); iter != mDrawFace.end(); iter++) { LLFace *face = *iter; if (voskyp->isReflFace(face)) { continue; } face->renderIndexed(); } stop_glerror(); if (!shader) { // Reset the settings back to expected values glDisable(GL_TEXTURE_GEN_S); //texture unit 0 glDisable(GL_TEXTURE_GEN_T); //texture unit 0 } gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); }