/** * @file pipeline.cpp * @brief Rendering pipeline. * * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "llviewerprecompiledheaders.h" #include "pipeline.h" // library includes #include "audioengine.h" // For MAX_BUFFERS for debugging. #include "imageids.h" #include "llagpmempool.h" #include "llerror.h" #include "llviewercontrol.h" #include "llfasttimer.h" #include "llfontgl.h" #include "llmemory.h" #include "llnamevalue.h" #include "llprimitive.h" #include "llvolume.h" #include "material_codes.h" #include "timing.h" #include "v3color.h" #include "llui.h" // newview includes #include "llagent.h" #include "llagparray.h" #include "lldrawable.h" #include "lldrawpoolalpha.h" #include "lldrawpoolavatar.h" #include "lldrawpoolground.h" #include "lldrawpoolsimple.h" #include "lldrawpooltree.h" #include "lldrawpoolhud.h" #include "lldrawpoolwater.h" #include "llface.h" #include "llfeaturemanager.h" #include "llfloatertelehub.h" #include "llframestats.h" #include "llgldbg.h" #include "llhudmanager.h" #include "lllightconstants.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llsky.h" #include "lltracker.h" #include "lltool.h" #include "lltoolmgr.h" #include "llviewercamera.h" #include "llviewerimagelist.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" // for audio debugging. #include "llviewerwindow.h" // For getSpinAxis #include "llvoavatar.h" #include "llvoground.h" #include "llvosky.h" #include "llvotree.h" #include "llvovolume.h" #include "llworld.h" #include "viewer.h" #include "llagpmempoolarb.h" #include "llagparray.inl" #ifdef _DEBUG // Debug indices is disabled for now for debug performance - djs 4/24/02 //#define DEBUG_INDICES #else //#define DEBUG_INDICES #endif const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f; const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f; const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f; const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f; const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40; const S32 MAX_OFFSCREEN_GEOMETRY_CHANGES_PER_FRAME = 10; // Guess on the number of visible objects in the scene, used to // pre-size std::vector and other arrays. JC const S32 ESTIMATED_VISIBLE_OBJECT_COUNT = 8192; // If the sum of the X + Y + Z scale of an object exceeds this number, // it will be considered a potential occluder. For instance, // a box of size 6 x 6 x 1 has sum 13, which might be an occluder. JC const F32 OCCLUDE_SCALE_SUM_THRESHOLD = 8.f; // Max number of occluders to search for. JC const S32 MAX_OCCLUDER_COUNT = 2; extern S32 gBoxFrame; extern BOOL gRenderLightGlows; extern BOOL gHideSelectedObjects; BOOL gAvatarBacklight = FALSE; F32 gMinObjectDistance = MIN_NEAR_PLANE; S32 gTrivialAccepts = 0; BOOL gRenderForSelect = FALSE; BOOL gUsePickAlpha = TRUE; F32 gPickAlphaThreshold = 0.f; F32 gPickAlphaTargetThreshold = 0.f; //glsl parameter tables const char* LLPipeline::sReservedAttribs[] = { "materialColor", "specularColor", "binormal" }; U32 LLPipeline::sReservedAttribCount = LLPipeline::GLSL_END_RESERVED_ATTRIBS; const char* LLPipeline::sAvatarAttribs[] = { "weight", "clothing", "gWindDir", "gSinWaveParams", "gGravity" }; U32 LLPipeline::sAvatarAttribCount = sizeof(LLPipeline::sAvatarAttribs)/sizeof(char*); const char* LLPipeline::sAvatarUniforms[] = { "matrixPalette" }; U32 LLPipeline::sAvatarUniformCount = 1; const char* LLPipeline::sReservedUniforms[] = { "diffuseMap", "specularMap", "bumpMap", "environmentMap", "scatterMap" }; U32 LLPipeline::sReservedUniformCount = LLPipeline::GLSL_END_RESERVED_UNIFORMS; const char* LLPipeline::sTerrainUniforms[] = { "detail0", "detail1", "alphaRamp" }; U32 LLPipeline::sTerrainUniformCount = sizeof(LLPipeline::sTerrainUniforms)/sizeof(char*); const char* LLPipeline::sWaterUniforms[] = { "screenTex", "eyeVec", "time", "d1", "d2", "lightDir", "specular", "lightExp", "fbScale", "refScale" }; U32 LLPipeline::sWaterUniformCount = sizeof(LLPipeline::sWaterUniforms)/sizeof(char*); // the SSE variable is dependent on software blending being enabled. //---------------------------------------- void stamp(F32 x, F32 y, F32 xs, F32 ys) { glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex3f(x, y, 0.0f); glTexCoord2f(1,0); glVertex3f(x+xs,y, 0.0f); glTexCoord2f(1,1); glVertex3f(x+xs,y+ys,0.0f); glTexCoord2f(0,1); glVertex3f(x, y+ys,0.0f); glEnd(); } //---------------------------------------- S32 LLPipeline::sCompiles = 0; S32 LLPipeline::sAGPMaxPoolSize = 1 << 25; // 32MB BOOL LLPipeline::sRenderPhysicalBeacons = FALSE; BOOL LLPipeline::sRenderScriptedBeacons = FALSE; BOOL LLPipeline::sRenderParticleBeacons = FALSE; BOOL LLPipeline::sRenderSoundBeacons = FALSE; LLPipeline::LLPipeline() : mVertexShadersEnabled(FALSE), mVertexShadersLoaded(0), mLastRebuildPool(NULL), mAlphaPool(NULL), mSkyPool(NULL), mStarsPool(NULL), mCloudsPool(NULL), mTerrainPool(NULL), mWaterPool(NULL), mGroundPool(NULL), mHUDPool(NULL), mAGPMemPool(NULL), mGlobalFence(0), mBufferIndex(0), mBufferCount(kMaxBufferCount), mUseOcclusionCulling(FALSE), mLightMask(0), mLightMovingMask(0) { for(S32 i = 0; i < kMaxBufferCount; i++) { mBufferMemory[i] = NULL; } for (S32 i = 0; i < kMaxBufferCount; i++) { mBufferFence[i] = 0; } } void LLPipeline::init() { LLMemType mt(LLMemType::MTYPE_PIPELINE); stop_glerror(); mAGPBound = FALSE; mObjectPartition = new LLSpatialPartition; mTrianglesDrawnStat.reset(); resetFrameStats(); mRenderTypeMask = 0xffffffff; // All render types start on mRenderDebugFeatureMask = 0xffffffff; // All debugging features on mRenderFeatureMask = 0; // All features start off mRenderDebugMask = 0; // All debug starts off mBackfaceCull = TRUE; // Disable AGP initially. mRenderFeatureMask &= ~RENDER_FEATURE_AGP; stop_glerror(); // Enable features mUseVBO = gSavedSettings.getBOOL("RenderUseVBO"); // Allocate the shared buffers for software skinning for(S32 i=0; i < mBufferCount; i++) { mBufferMemory[i] = new LLAGPArray; mBufferMemory[i]->reserve_block(AVATAR_VERTEX_BYTES*AVATAR_BUFFER_ELEMENTS); } if (gFeatureManagerp->isFeatureAvailable("RenderAGP")) { setUseAGP(gSavedSettings.getBOOL("RenderUseAGP") && gGLManager.mHasAnyAGP); } else { setUseAGP(FALSE); } stop_glerror(); for(S32 i=0; i < mBufferCount; i++) { if (!mBufferMemory[i]->isAGP() && usingAGP()) { llwarns << "pipeline buffer memory is non-AGP when AGP available!" << llendl; } } setShaders(); } void LLPipeline::LLScatterShader::init(GLhandleARB shader, int map_stage) { glUseProgramObjectARB(shader); glUniform1iARB(glGetUniformLocationARB(shader, "scatterMap"), map_stage); glUseProgramObjectARB(0); } LLPipeline::~LLPipeline() { } void LLPipeline::cleanup() { for(pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ) { pool_set_t::iterator curiter = iter++; LLDrawPool* poolp = *curiter; if (poolp->mReferences.empty()) { mPools.erase(curiter); removeFromQuickLookup( poolp ); delete poolp; } } if (!mSimplePools.empty()) { llwarns << "Simple Pools not cleaned up" << llendl; } if (!mTerrainPools.empty()) { llwarns << "Terrain Pools not cleaned up" << llendl; } if (!mTreePools.empty()) { llwarns << "Tree Pools not cleaned up" << llendl; } if (!mTreeNewPools.empty()) { llwarns << "TreeNew Pools not cleaned up" << llendl; } if (!mBumpPools.empty()) { llwarns << "Bump Pools not cleaned up" << llendl; } delete mAlphaPool; mAlphaPool = NULL; delete mSkyPool; mSkyPool = NULL; delete mStarsPool; mStarsPool = NULL; delete mCloudsPool; mCloudsPool = NULL; delete mTerrainPool; mTerrainPool = NULL; delete mWaterPool; mWaterPool = NULL; delete mGroundPool; mGroundPool = NULL; delete mHUDPool; mHUDPool = NULL; mBloomImagep = NULL; mBloomImage2p = NULL; mFaceSelectImagep = NULL; mAlphaSizzleImagep = NULL; for(S32 i=0; i < mBufferCount; i++) { delete mBufferMemory[i]; mBufferMemory[i] = NULL; } delete mObjectPartition; mObjectPartition = NULL; if (mAGPMemPool && mGlobalFence) { mAGPMemPool->deleteFence(mGlobalFence); mGlobalFence = 0; } delete mAGPMemPool; mAGPMemPool = NULL; } //============================================================================ BOOL LLPipeline::initAGP() { LLMemType mt(LLMemType::MTYPE_PIPELINE); mAGPMemPool = LLAGPMemPool::createPool(sAGPMaxPoolSize, mUseVBO); if (!mAGPMemPool) { llinfos << "Warning! Couldn't allocate AGP memory!" << llendl; llinfos << "Disabling AGP!" << llendl; mAGPMemPool = NULL; mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag return FALSE; } else if (!mAGPMemPool->getSize()) { llinfos << "Warning! Unable to allocate AGP memory! Disabling AGP" << llendl; delete mAGPMemPool; mAGPMemPool = NULL; mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag return FALSE; } else { llinfos << "Allocated " << mAGPMemPool->getSize() << " bytes of AGP memory" << llendl; mAGPMemPool->bind(); if (mAGPMemPool->getSize() < MIN_AGP_SIZE) { llwarns << "Not enough AGP memory!" << llendl; delete mAGPMemPool; mAGPMemPool = NULL; mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag return FALSE; } if (mAGPMemPool) { // Create the fence that we use for global synchronization. mGlobalFence = mAGPMemPool->createFence(); } return TRUE; } } void LLPipeline::cleanupAGP() { int i; for(i=0; i < mBufferCount; i++) { mBufferMemory[i]->deleteFence(mBufferFence[i]); mBufferMemory[i]->setUseAGP(FALSE); } flushAGPMemory(); if (mAGPMemPool && mGlobalFence) { mAGPMemPool->deleteFence(mGlobalFence); mGlobalFence = 0; } delete mAGPMemPool; mAGPMemPool = NULL; } BOOL LLPipeline::usingAGP() const { return (mRenderFeatureMask & RENDER_FEATURE_AGP) ? TRUE : FALSE; } void LLPipeline::setUseAGP(const BOOL use_agp) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (use_agp == usingAGP()) { return; } else if (use_agp) { mRenderFeatureMask |= RENDER_FEATURE_AGP; initAGP(); // Forces us to allocate an AGP memory block immediately. int i; for(i=0; i < mBufferCount; i++) { mBufferMemory[i]->setUseAGP(use_agp); mBufferMemory[i]->realloc(mBufferMemory[i]->getMax()); mBufferFence[i] = mBufferMemory[i]->createFence(); } // Must be done AFTER you initialize AGP for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; poolp->setUseAGP(use_agp); } } else { unbindAGP(); mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Must be done BEFORE you blow away AGP for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; poolp->setUseAGP(use_agp); } int i; for(i=0; i < mBufferCount; i++) { if (mBufferMemory[i]) { mBufferMemory[i]->setUseAGP(use_agp); mBufferMemory[i]->deleteFence(mBufferFence[i]); mBufferFence[i] = 0; } else { llerrs << "setUseAGP without buffer memory" << llendl; } } cleanupAGP(); } } //============================================================================ void LLPipeline::destroyGL() { setUseAGP(FALSE); stop_glerror(); unloadShaders(); mHighlightFaces.reset(); } void LLPipeline::restoreGL() { if (mVertexShadersEnabled) { setShaders(); } if (mObjectPartition) { mObjectPartition->restoreGL(); } } //============================================================================ // Load Shader static LLString get_object_log(GLhandleARB ret) { LLString res; //get log length GLint length; glGetObjectParameterivARB(ret, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length); if (length > 0) { //the log could be any size, so allocate appropriately GLcharARB* log = new GLcharARB[length]; glGetInfoLogARB(ret, length, &length, log); res = LLString(log); delete[] log; } return res; } void LLPipeline::dumpObjectLog(GLhandleARB ret, BOOL warns) { LLString log = get_object_log(ret); if (warns) { llwarns << log << llendl; } else { llinfos << log << llendl; } } GLhandleARB LLPipeline::loadShader(const LLString& filename, S32 cls, GLenum type) { GLenum error; error = glGetError(); if (error != GL_NO_ERROR) { llwarns << "GL ERROR entering loadShader(): " << error << llendl; } llinfos << "Loading shader file: " << filename << llendl; if (filename.empty()) { return 0; } //read in from file FILE* file = NULL; S32 try_gpu_class = mVertexShaderLevel[cls]; S32 gpu_class; //find the most relevant file for (gpu_class = try_gpu_class; gpu_class > 0; gpu_class--) { //search from the current gpu class down to class 1 to find the most relevant shader std::stringstream fname; fname << gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/class"); fname << gpu_class << "/" << filename; llinfos << "Looking in " << fname.str().c_str() << llendl; file = fopen(fname.str().c_str(), "r"); if (file) { break; // done } } if (file == NULL) { llinfos << "GLSL Shader file not found: " << filename << llendl; return 0; } //we can't have any lines longer than 1024 characters //or any shaders longer than 1024 lines... deal - DaveP GLcharARB buff[1024]; GLcharARB* text[1024]; GLuint count = 0; //copy file into memory while(fgets(buff, 1024, file) != NULL) { text[count++] = strdup(buff); } fclose(file); //create shader object GLhandleARB ret = glCreateShaderObjectARB(type); error = glGetError(); if (error != GL_NO_ERROR) { llwarns << "GL ERROR in glCreateShaderObjectARB: " << error << llendl; } else { //load source glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL); error = glGetError(); if (error != GL_NO_ERROR) { llwarns << "GL ERROR in glShaderSourceARB: " << error << llendl; } else { //compile source glCompileShaderARB(ret); error = glGetError(); if (error != GL_NO_ERROR) { llwarns << "GL ERROR in glCompileShaderARB: " << error << llendl; } } } //free memory for (GLuint i = 0; i < count; i++) { free(text[i]); } if (error == GL_NO_ERROR) { //check for errors GLint success = GL_TRUE; glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &success); error = glGetError(); if (error != GL_NO_ERROR || success == GL_FALSE) { //an error occured, print log llwarns << "GLSL Compilation Error: (" << error << ") in " << filename << llendl; dumpObjectLog(ret); ret = 0; } } else { ret = 0; } stop_glerror(); //successfully loaded, save results #if 1 // 1.9.1 if (ret) { mVertexShaderLevel[cls] = try_gpu_class; } else { if (mVertexShaderLevel[cls] > 1) { mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1; ret = loadShader(filename,cls,type); if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls]) { mMaxVertexShaderLevel[cls] = mVertexShaderLevel[cls]; } } } #else if (ret) { S32 max = -1; /*if (try_gpu_class == mMaxVertexShaderLevel[cls]) { max = gpu_class; }*/ saveVertexShaderLevel(cls,try_gpu_class,max); } else { if (mVertexShaderLevel[cls] > 1) { mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1; ret = loadShader(f,cls,type); if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls]) { saveVertexShaderLevel(cls, mVertexShaderLevel[cls], mVertexShaderLevel[cls]); } } } #endif return ret; } BOOL LLPipeline::linkProgramObject(GLhandleARB obj, BOOL suppress_errors) { //check for errors glLinkProgramARB(obj); GLint success = GL_TRUE; glGetObjectParameterivARB(obj, GL_OBJECT_LINK_STATUS_ARB, &success); if (!suppress_errors && success == GL_FALSE) { //an error occured, print log llwarns << "GLSL Linker Error:" << llendl; } LLString log = get_object_log(obj); LLString::toLower(log); if (log.find("software") != LLString::npos) { llwarns << "GLSL Linker: Running in Software:" << llendl; success = GL_FALSE; suppress_errors = FALSE; } if (!suppress_errors) { dumpObjectLog(obj, !success); } return success; } BOOL LLPipeline::validateProgramObject(GLhandleARB obj) { //check program validity against current GL glValidateProgramARB(obj); GLint success = GL_TRUE; glGetObjectParameterivARB(obj, GL_OBJECT_VALIDATE_STATUS_ARB, &success); if (success == GL_FALSE) { llwarns << "GLSL program not valid: " << llendl; dumpObjectLog(obj); } else { dumpObjectLog(obj, FALSE); } return success; } //============================================================================ // Shader Management void LLPipeline::setShaders() { if (gViewerWindow) { gViewerWindow->setCursor(UI_CURSOR_WAIT); } // Lighting setLightingDetail(-1); // Shaders for (S32 i=0; isetCursor(UI_CURSOR_ARROW); } } BOOL LLPipeline::canUseVertexShaders() { if (!gGLManager.mHasVertexShader || !gGLManager.mHasFragmentShader || !gFeatureManagerp->isFeatureAvailable("VertexShaderEnable") || mVertexShadersLoaded == -1) { return FALSE; } else { return TRUE; } } void LLPipeline::unloadShaders() { mObjectSimpleProgram.unload(); mObjectBumpProgram.unload(); mObjectAlphaProgram.unload(); mWaterProgram.unload(); mTerrainProgram.unload(); mGroundProgram.unload(); mAvatarProgram.unload(); mAvatarEyeballProgram.unload(); mAvatarPickProgram.unload(); mHighlightProgram.unload(); mVertexShaderLevel[SHADER_LIGHTING] = 0; mVertexShaderLevel[SHADER_OBJECT] = 0; mVertexShaderLevel[SHADER_AVATAR] = 0; mVertexShaderLevel[SHADER_ENVIRONMENT] = 0; mVertexShaderLevel[SHADER_INTERFACE] = 0; mLightVertex = mLightFragment = mScatterVertex = mScatterFragment = 0; mVertexShadersLoaded = 0; } #if 0 // 1.9.2 // Any time shader options change BOOL LLPipeline::loadShaders() { unloadShaders(); if (!canUseVertexShaders()) { return FALSE; } S32 light_class = mMaxVertexShaderLevel[SHADER_LIGHTING]; if (getLightingDetail() == 0) { light_class = 1; // Use minimum lighting shader } else if (getLightingDetail() == 1) { light_class = 2; // Use medium lighting shader } mVertexShaderLevel[SHADER_LIGHTING] = light_class; mVertexShaderLevel[SHADER_OBJECT] = llmin(mMaxVertexShaderLevel[SHADER_OBJECT], gSavedSettings.getS32("VertexShaderLevelObject")); mVertexShaderLevel[SHADER_AVATAR] = llmin(mMaxVertexShaderLevel[SHADER_AVATAR], gSavedSettings.getS32("VertexShaderLevelAvatar")); mVertexShaderLevel[SHADER_ENVIRONMENT] = llmin(mMaxVertexShaderLevel[SHADER_ENVIRONMENT], gSavedSettings.getS32("VertexShaderLevelEnvironment")); mVertexShaderLevel[SHADER_INTERFACE] = mMaxVertexShaderLevel[SHADER_INTERFACE]; BOOL loaded = loadShadersLighting(); if (loaded) { loadShadersEnvironment(); // Must load this before object/avatar for scatter loadShadersObject(); loadShadersAvatar(); loadShadersInterface(); mVertexShadersLoaded = 1; } else { unloadShaders(); mVertexShadersEnabled = FALSE; mVertexShadersLoaded = 0; //-1; // -1 = failed setLightingDetail(-1); } return loaded; } #endif BOOL LLPipeline::loadShadersLighting() { // Load light dependency shaders first // All of these have to load for any shaders to function std::string lightvertex = "lighting/lightV.glsl"; //get default light function implementation mLightVertex = loadShader(lightvertex, SHADER_LIGHTING, GL_VERTEX_SHADER_ARB); if( !mLightVertex ) { llwarns << "Failed to load " << lightvertex << llendl; return FALSE; } std::string lightfragment = "lighting/lightF.glsl"; mLightFragment = loadShader(lightfragment, SHADER_LIGHTING, GL_FRAGMENT_SHADER_ARB); if ( !mLightFragment ) { llwarns << "Failed to load " << lightfragment << llendl; return FALSE; } // NOTE: Scatter shaders use the ENVIRONMENT detail level std::string scattervertex = "environment/scatterV.glsl"; mScatterVertex = loadShader(scattervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB); if ( !mScatterVertex ) { llwarns << "Failed to load " << scattervertex << llendl; return FALSE; } std::string scatterfragment = "environment/scatterF.glsl"; mScatterFragment = loadShader(scatterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB); if ( !mScatterFragment ) { llwarns << "Failed to load " << scatterfragment << llendl; return FALSE; } return TRUE; } BOOL LLPipeline::loadShadersEnvironment() { GLhandleARB baseObjects[] = { mLightFragment, mLightVertex, mScatterFragment, mScatterVertex }; S32 baseCount = 4; BOOL success = TRUE; if (mVertexShaderLevel[SHADER_ENVIRONMENT] == 0) { mWaterProgram.unload(); mGroundProgram.unload(); mTerrainProgram.unload(); return FALSE; } if (success) { //load water vertex shader std::string waterfragment = "environment/waterF.glsl"; std::string watervertex = "environment/waterV.glsl"; mWaterProgram.mProgramObject = glCreateProgramObjectARB(); mWaterProgram.attachObjects(baseObjects, baseCount); mWaterProgram.attachObject(loadShader(watervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB)); mWaterProgram.attachObject(loadShader(waterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB)); success = mWaterProgram.mapAttributes(); if (success) { success = mWaterProgram.mapUniforms(sWaterUniforms, sWaterUniformCount); } if (!success) { llwarns << "Failed to load " << watervertex << llendl; } } if (success) { //load ground vertex shader std::string groundvertex = "environment/groundV.glsl"; std::string groundfragment = "environment/groundF.glsl"; mGroundProgram.mProgramObject = glCreateProgramObjectARB(); mGroundProgram.attachObjects(baseObjects, baseCount); mGroundProgram.attachObject(loadShader(groundvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB)); mGroundProgram.attachObject(loadShader(groundfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB)); success = mGroundProgram.mapAttributes(); if (success) { success = mGroundProgram.mapUniforms(); } if (!success) { llwarns << "Failed to load " << groundvertex << llendl; } } if (success) { //load terrain vertex shader std::string terrainvertex = "environment/terrainV.glsl"; std::string terrainfragment = "environment/terrainF.glsl"; mTerrainProgram.mProgramObject = glCreateProgramObjectARB(); mTerrainProgram.attachObjects(baseObjects, baseCount); mTerrainProgram.attachObject(loadShader(terrainvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB)); mTerrainProgram.attachObject(loadShader(terrainfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB)); success = mTerrainProgram.mapAttributes(); if (success) { success = mTerrainProgram.mapUniforms(sTerrainUniforms, sTerrainUniformCount); } if (!success) { llwarns << "Failed to load " << terrainvertex << llendl; } } if( !success ) { mVertexShaderLevel[SHADER_ENVIRONMENT] = 0; mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = 0; return FALSE; } if (gWorldPointer) { gWorldPointer->updateWaterObjects(); } return TRUE; } BOOL LLPipeline::loadShadersObject() { GLhandleARB baseObjects[] = { mLightFragment, mLightVertex, mScatterFragment, mScatterVertex }; S32 baseCount = 4; BOOL success = TRUE; if (mVertexShaderLevel[SHADER_OBJECT] == 0) { mObjectSimpleProgram.unload(); mObjectBumpProgram.unload(); mObjectAlphaProgram.unload(); return FALSE; } if (success) { //load object (volume/tree) vertex shader std::string simplevertex = "objects/simpleV.glsl"; std::string simplefragment = "objects/simpleF.glsl"; mObjectSimpleProgram.mProgramObject = glCreateProgramObjectARB(); mObjectSimpleProgram.attachObjects(baseObjects, baseCount); mObjectSimpleProgram.attachObject(loadShader(simplevertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB)); mObjectSimpleProgram.attachObject(loadShader(simplefragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB)); success = mObjectSimpleProgram.mapAttributes(); if (success) { success = mObjectSimpleProgram.mapUniforms(); } if( !success ) { llwarns << "Failed to load " << simplevertex << llendl; } } if (success) { //load object bumpy vertex shader std::string bumpshinyvertex = "objects/bumpshinyV.glsl"; std::string bumpshinyfragment = "objects/bumpshinyF.glsl"; mObjectBumpProgram.mProgramObject = glCreateProgramObjectARB(); mObjectBumpProgram.attachObjects(baseObjects, baseCount); mObjectBumpProgram.attachObject(loadShader(bumpshinyvertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB)); mObjectBumpProgram.attachObject(loadShader(bumpshinyfragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB)); success = mObjectBumpProgram.mapAttributes(); if (success) { success = mObjectBumpProgram.mapUniforms(); } if( !success ) { llwarns << "Failed to load " << bumpshinyvertex << llendl; } } if (success) { //load object alpha vertex shader std::string alphavertex = "objects/alphaV.glsl"; std::string alphafragment = "objects/alphaF.glsl"; mObjectAlphaProgram.mProgramObject = glCreateProgramObjectARB(); mObjectAlphaProgram.attachObjects(baseObjects, baseCount); mObjectAlphaProgram.attachObject(loadShader(alphavertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB)); mObjectAlphaProgram.attachObject(loadShader(alphafragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB)); success = mObjectAlphaProgram.mapAttributes(); if (success) { success = mObjectAlphaProgram.mapUniforms(); } if( !success ) { llwarns << "Failed to load " << alphavertex << llendl; } } if( !success ) { mVertexShaderLevel[SHADER_OBJECT] = 0; mMaxVertexShaderLevel[SHADER_OBJECT] = 0; return FALSE; } return TRUE; } BOOL LLPipeline::loadShadersAvatar() { GLhandleARB baseObjects[] = { mLightFragment, mLightVertex, mScatterFragment, mScatterVertex }; S32 baseCount = 4; BOOL success = TRUE; if (mVertexShaderLevel[SHADER_AVATAR] == 0) { mAvatarProgram.unload(); mAvatarEyeballProgram.unload(); mAvatarPickProgram.unload(); return FALSE; } if (success) { //load specular (eyeball) vertex program std::string eyeballvertex = "avatar/eyeballV.glsl"; std::string eyeballfragment = "avatar/eyeballF.glsl"; mAvatarEyeballProgram.mProgramObject = glCreateProgramObjectARB(); mAvatarEyeballProgram.attachObjects(baseObjects, baseCount); mAvatarEyeballProgram.attachObject(loadShader(eyeballvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB)); mAvatarEyeballProgram.attachObject(loadShader(eyeballfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB)); success = mAvatarEyeballProgram.mapAttributes(); if (success) { success = mAvatarEyeballProgram.mapUniforms(); } if( !success ) { llwarns << "Failed to load " << eyeballvertex << llendl; } } if (success) { mAvatarSkinVertex = loadShader("avatar/avatarSkinV.glsl", SHADER_AVATAR, GL_VERTEX_SHADER_ARB); //load avatar vertex shader std::string avatarvertex = "avatar/avatarV.glsl"; std::string avatarfragment = "avatar/avatarF.glsl"; mAvatarProgram.mProgramObject = glCreateProgramObjectARB(); mAvatarProgram.attachObjects(baseObjects, baseCount); mAvatarProgram.attachObject(mAvatarSkinVertex); mAvatarProgram.attachObject(loadShader(avatarvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB)); mAvatarProgram.attachObject(loadShader(avatarfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB)); success = mAvatarProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount); if (success) { success = mAvatarProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount); } if( !success ) { llwarns << "Failed to load " << avatarvertex << llendl; } } if (success) { //load avatar picking shader std::string pickvertex = "avatar/pickAvatarV.glsl"; std::string pickfragment = "avatar/pickAvatarF.glsl"; mAvatarPickProgram.mProgramObject = glCreateProgramObjectARB(); mAvatarPickProgram.attachObject(loadShader(pickvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB)); mAvatarPickProgram.attachObject(loadShader(pickfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB)); mAvatarPickProgram.attachObject(mAvatarSkinVertex); success = mAvatarPickProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount); if (success) { success = mAvatarPickProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount); } if( !success ) { llwarns << "Failed to load " << pickvertex << llendl; } } if( !success ) { mVertexShaderLevel[SHADER_AVATAR] = 0; mMaxVertexShaderLevel[SHADER_AVATAR] = 0; return FALSE; } return TRUE; } BOOL LLPipeline::loadShadersInterface() { BOOL success = TRUE; if (mVertexShaderLevel[SHADER_INTERFACE] == 0) { mHighlightProgram.unload(); return FALSE; } if (success) { //load highlighting shader std::string highlightvertex = "interface/highlightV.glsl"; std::string highlightfragment = "interface/highlightF.glsl"; mHighlightProgram.mProgramObject = glCreateProgramObjectARB(); mHighlightProgram.attachObject(loadShader(highlightvertex, SHADER_INTERFACE, GL_VERTEX_SHADER_ARB)); mHighlightProgram.attachObject(loadShader(highlightfragment, SHADER_INTERFACE, GL_FRAGMENT_SHADER_ARB)); success = mHighlightProgram.mapAttributes(); if (success) { success = mHighlightProgram.mapUniforms(); } if( !success ) { llwarns << "Failed to load " << highlightvertex << llendl; } } if( !success ) { mVertexShaderLevel[SHADER_INTERFACE] = 0; mMaxVertexShaderLevel[SHADER_INTERFACE] = 0; return FALSE; } return TRUE; } //============================================================================ void LLPipeline::enableShadows(const BOOL enable_shadows) { //should probably do something here to wrangle shadows.... } S32 LLPipeline::getMaxLightingDetail() const { if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS) { return 3; } else { return 1; } } S32 LLPipeline::setLightingDetail(S32 level) { if (level < 0) { level = gSavedSettings.getS32("RenderLightingDetail"); } level = llclamp(level, 0, getMaxLightingDetail()); if (level != mLightingDetail) { gSavedSettings.setS32("RenderLightingDetail", level); if (level >= 2) { gObjectList.relightAllObjects(); } mLightingDetail = level; if (mVertexShadersLoaded == 1) { gPipeline.setShaders(); } } return mLightingDetail; } LLAGPMemBlock *LLPipeline::allocAGPFromPool(const S32 bytes, const U32 target) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (!mAGPMemPool) { llwarns << "Attempting to allocate AGP memory when AGP disabled!" << llendl; return NULL; } else { if (mUseVBO) { return ((LLAGPMemPoolARB*) mAGPMemPool)->allocBlock(bytes, target); } else { return mAGPMemPool->allocBlock(bytes); } } } void LLPipeline::unbindAGP() { if (mAGPMemPool && mAGPBound) { mAGPMemPool->disable(); mAGPBound = FALSE; } } void LLPipeline::bindAGP() { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (mAGPMemPool && !mAGPBound && usingAGP()) { mAGPMemPool->enable(); mAGPBound = TRUE; } } U8* LLPipeline::bufferGetScratchMemory(void) { LLMemType mt(LLMemType::MTYPE_PIPELINE); return(mBufferMemory[mBufferIndex]->getScratchMemory()); } void LLPipeline::bufferWaitFence(void) { mBufferMemory[mBufferIndex]->waitFence(mBufferFence[mBufferIndex]); } void LLPipeline::bufferSendFence(void) { mBufferMemory[mBufferIndex]->sendFence(mBufferFence[mBufferIndex]); } void LLPipeline::bufferRotate(void) { mBufferIndex++; if(mBufferIndex >= mBufferCount) mBufferIndex = 0; } // Called when a texture changes # of channels (rare, may cause faces to move to alpha pool) void LLPipeline::dirtyPoolObjectTextures(const LLViewerImage *texturep) { for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; poolp->dirtyTexture(texturep); } } LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0) { LLDrawPool *poolp = NULL; switch( type ) { case LLDrawPool::POOL_SIMPLE: poolp = get_if_there(mSimplePools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_TREE: poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_TREE_NEW: poolp = get_if_there(mTreeNewPools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_TERRAIN: poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_BUMP: poolp = get_if_there(mBumpPools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_MEDIA: poolp = get_if_there(mMediaPools, (uintptr_t)tex0, (LLDrawPool*)0 ); break; case LLDrawPool::POOL_ALPHA: poolp = mAlphaPool; break; case LLDrawPool::POOL_AVATAR: break; // Do nothing case LLDrawPool::POOL_SKY: poolp = mSkyPool; break; case LLDrawPool::POOL_STARS: poolp = mStarsPool; break; case LLDrawPool::POOL_CLOUDS: poolp = mCloudsPool; break; case LLDrawPool::POOL_WATER: poolp = mWaterPool; break; case LLDrawPool::POOL_GROUND: poolp = mGroundPool; break; case LLDrawPool::POOL_HUD: poolp = mHUDPool; break; default: llassert(0); llerrs << "Invalid Pool Type in LLPipeline::findPool() type=" << type << llendl; break; } return poolp; } LLDrawPool *LLPipeline::getPool(const U32 type, LLViewerImage *tex0) { LLMemType mt(LLMemType::MTYPE_PIPELINE); LLDrawPool *poolp = findPool(type, tex0); if (poolp) { return poolp; } LLDrawPool *new_poolp = LLDrawPool::createPool(type, tex0); addPool( new_poolp ); return new_poolp; } // static LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerImage* imagep) { LLMemType mt(LLMemType::MTYPE_PIPELINE); bool alpha = te->getColor().mV[3] < 0.999f; if (imagep) { alpha = alpha || (imagep->getComponents() == 4) || (imagep->getComponents() == 2); } #if 0 // Not currently used if (te->getMediaFlags() == LLTextureEntry::MF_WEB_PAGE) { return gPipeline.getPool(LLDrawPool::POOL_MEDIA, imagep); } else #endif if (alpha) { return gPipeline.getPool(LLDrawPool::POOL_ALPHA); } else if ((te->getBumpmap() || te->getShiny())) { return gPipeline.getPool(LLDrawPool::POOL_BUMP, imagep); } else { return gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep); } } void LLPipeline::addPool(LLDrawPool *new_poolp) { LLMemType mt(LLMemType::MTYPE_PIPELINE); mPools.insert(new_poolp); addToQuickLookup( new_poolp ); } void LLPipeline::allocDrawable(LLViewerObject *vobj) { LLMemType mt(LLMemType::MTYPE_DRAWABLE); LLDrawable *drawable = new LLDrawable(); vobj->mDrawable = drawable; drawable->mVObjp = vobj; //encompass completely sheared objects by taking //the most extreme point possible (<1,1,0.5>) drawable->setRadius(LLVector3(1,1,0.5f).scaleVec(vobj->getScale()).magVec()); if (vobj->isOrphaned()) { drawable->setState(LLDrawable::FORCE_INVISIBLE); } drawable->updateXform(TRUE); } void LLPipeline::unlinkDrawable(LLDrawable *drawablep) { LLFastTimer t(LLFastTimer::FTM_PIPELINE); // Based on flags, remove the drawable from the queues that it's on. if (drawablep->isState(LLDrawable::ON_MOVE_LIST)) { mMovedList.erase(drawablep); } if (drawablep->getSpatialGroup()) { if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup())) { #ifdef LL_RELEASE_FOR_DOWNLOAD llwarns << "Couldn't remove object from spatial group!" << llendl; #else llerrs << "Couldn't remove object from spatial group!" << llendl; #endif } } mLights.erase(drawablep); } U32 LLPipeline::addObject(LLViewerObject *vobj) { LLMemType mt(LLMemType::MTYPE_DRAWABLE); if (gNoRender) { return 0; } LLDrawable *drawablep = vobj->createDrawable(this); llassert(drawablep); //mCompleteSet.put(drawable); //gResyncObjects = TRUE; if (vobj->getParent()) { vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1 } else { vobj->setDrawableParent(NULL); // LLPipeline::addObject 2 } if ((!drawablep->getVOVolume()) && (vobj->getPCode() != LLViewerObject::LL_VO_SKY) && (vobj->getPCode() != LLViewerObject::LL_VO_STARS) && (vobj->getPCode() != LLViewerObject::LL_VO_GROUND)) { drawablep->getSpatialPartition()->put(drawablep); if (!drawablep->getSpatialGroup()) { #ifdef LL_RELEASE_FOR_DOWNLOAD llwarns << "Failure adding drawable to object partition!" << llendl; #else llerrs << "Failure adding drawable to object partition!" << llendl; #endif } } else { markMoved(drawablep); } markMaterialed(drawablep); markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE); return 1; } void LLPipeline::resetFrameStats() { sCompiles = 0; mVerticesRelit = 0; mLightingChanges = 0; mGeometryChanges = 0; mNumVisibleFaces = 0; } //external functions for asynchronous updating void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep) { if (gSavedSettings.getBOOL("FreezeTime")) { return; } if (!drawablep) { llerrs << "updateMove called with NULL drawablep" << llendl; } if (drawablep->isState(LLDrawable::EARLY_MOVE)) { return; } // update drawable now drawablep->clearState(LLDrawable::MOVE_UNDAMPED); // force to DAMPED drawablep->updateMove(); // returns done drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame // Put on move list so that EARLY_MOVE gets cleared if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) { mMovedList.insert(drawablep); drawablep->setState(LLDrawable::ON_MOVE_LIST); } } void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep) { if (gSavedSettings.getBOOL("FreezeTime")) { return; } if (!drawablep) { llerrs << "updateMove called with NULL drawablep" << llendl; } if (drawablep->isState(LLDrawable::EARLY_MOVE)) { return; } // update drawable now drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED drawablep->updateMove(); drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame // Put on move list so that EARLY_MOVE gets cleared if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) { mMovedList.insert(drawablep); drawablep->setState(LLDrawable::ON_MOVE_LIST); } } void LLPipeline::updateMove() { mObjectPartition->mOctree->validate(); LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE); LLMemType mt(LLMemType::MTYPE_PIPELINE); if (gSavedSettings.getBOOL("FreezeTime")) { return; } mMoveChangesStat.addValue((F32)mMovedList.size()); for (LLDrawable::drawable_set_t::iterator iter = mMovedList.begin(); iter != mMovedList.end(); ) { LLDrawable::drawable_set_t::iterator curiter = iter++; LLDrawable *drawablep = *curiter; BOOL done = TRUE; if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE))) { done = drawablep->updateMove(); } drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); if (done) { mMovedList.erase(curiter); drawablep->clearState(LLDrawable::ON_MOVE_LIST); } } for (LLDrawable::drawable_set_t::iterator iter = mActiveQ.begin(); iter != mActiveQ.end(); ) { LLDrawable::drawable_set_t::iterator curiter = iter++; LLDrawable* drawablep = *curiter; if (drawablep && !drawablep->isDead()) { if (drawablep->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && (!drawablep->getParent() || !drawablep->getParent()->isActive())) { drawablep->makeStatic(); // removes drawable and its children from mActiveQ iter = mActiveQ.upper_bound(drawablep); // next valid entry } } else { mActiveQ.erase(curiter); } } for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin(); iter != mRetexturedList.end(); ++iter) { LLDrawable* drawablep = *iter; if (drawablep && !drawablep->isDead()) { drawablep->updateTexture(); } } mRetexturedList.clear(); for (LLDrawable::drawable_set_t::iterator iter = mRematerialedList.begin(); iter != mRematerialedList.end(); ++iter) { LLDrawable* drawablep = *iter; if (drawablep && !drawablep->isDead()) { drawablep->updateMaterial(); } } mRematerialedList.clear(); if (mObjectPartition->mOctree) { //balance octree LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE); mObjectPartition->mOctree->validate(); mObjectPartition->mOctree->balance(); mObjectPartition->mOctree->validate(); } } ///////////////////////////////////////////////////////////////////////////// // Culling and occlusion testing ///////////////////////////////////////////////////////////////////////////// void LLPipeline::updateCull() { LLFastTimer t(LLFastTimer::FTM_CULL); LLMemType mt(LLMemType::MTYPE_PIPELINE); LLDrawable::incrementVisible(); mVisibleList.resize(0); mVisibleList.reserve(ESTIMATED_VISIBLE_OBJECT_COUNT); gTrivialAccepts = 0; if (mObjectPartition) { if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery) { mObjectPartition->processOcclusion(gCamera); stop_glerror(); } mObjectPartition->cull(*gCamera); } // Hack for avatars - warning - this is really FRAGILE! - djs 05/06/02 LLVOAvatar::updateAllAvatarVisiblity(); // If there are any other hacks here, make sure to add them to the // standard pick code. gMinObjectDistance = llclamp(gMinObjectDistance, MIN_NEAR_PLANE, MAX_NEAR_PLANE); F32 water_height = gAgent.getRegion()->getWaterHeight(); F32 camera_height = gAgent.getCameraPositionAgent().mV[2]; if (fabs(camera_height - water_height) < 2.f) { gMinObjectDistance = MIN_NEAR_PLANE; } gCamera->setNear(gMinObjectDistance); // Disable near clip stuff for now... // now push it back out to max value gMinObjectDistance = MIN_NEAR_PLANE; if (gSky.mVOSkyp.notNull() && gSky.mVOSkyp->mDrawable.notNull()) { // Hack for sky - always visible. gSky.mVOSkyp->mDrawable->setVisible(*gCamera); mVisibleList.push_back(gSky.mVOSkyp->mDrawable); gSky.updateCull(); stop_glerror(); } else { llinfos << "No sky drawable!" << llendl; } if (gSky.mVOGroundp.notNull() && gSky.mVOGroundp->mDrawable.notNull()) { gSky.mVOGroundp->mDrawable->setVisible(*gCamera); mVisibleList.push_back(gSky.mVOGroundp->mDrawable); } // add all HUD attachments LLVOAvatar* my_avatarp = gAgent.getAvatarObject(); if (my_avatarp && my_avatarp->hasHUDAttachment()) { for (LLViewerJointAttachment* attachmentp = my_avatarp->mAttachmentPoints.getFirstData(); attachmentp; attachmentp = my_avatarp->mAttachmentPoints.getNextData()) { if (attachmentp->getIsHUDAttachment() && attachmentp->getObject(0)) { LLViewerObject* objectp = attachmentp->getObject(0); markVisible(objectp->mDrawable); objectp->mDrawable->updateDistance(*gCamera); for (S32 i = 0; i < (S32)objectp->mChildList.size(); i++) { LLViewerObject* childp = objectp->mChildList[i]; if (childp->mDrawable.notNull()) { markVisible(childp->mDrawable); childp->mDrawable->updateDistance(*gCamera); } } } } } } void LLPipeline::markNotCulled(LLDrawable* drawablep, LLCamera& camera) { if (drawablep->isVisible()) { return; } // Tricky render mode to hide selected objects, but we definitely // don't want to do any unnecessary pointer dereferences. JC if (gHideSelectedObjects) { if (drawablep->getVObj() && drawablep->getVObj()->isSelected()) { return; } } if (drawablep && (hasRenderType(drawablep->mRenderType))) { if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE)) { mVisibleList.push_back(drawablep); drawablep->setVisible(camera, NULL, FALSE); } else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE)) { // clear invisible flag here to avoid single frame glitch drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE); } } } void LLPipeline::doOcclusion() { if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery) { mObjectPartition->doOcclusion(gCamera); } } BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority) { BOOL update_complete = drawablep->updateGeometry(priority); if (update_complete) { drawablep->setState(LLDrawable::BUILT); mGeometryChanges++; } return update_complete; } void LLPipeline::updateGeom(F32 max_dtime) { LLTimer update_timer; LLMemType mt(LLMemType::MTYPE_PIPELINE); LLPointer drawablep; LLFastTimer t(LLFastTimer::FTM_GEO_UPDATE); // notify various object types to reset internal cost metrics, etc. // for now, only LLVOVolume does this to throttle LOD changes LLVOVolume::preUpdateGeom(); // Iterate through all drawables on the priority build queue, for (LLDrawable::drawable_set_t::iterator iter = mBuildQ1.begin(); iter != mBuildQ1.end();) { LLDrawable::drawable_set_t::iterator curiter = iter++; LLDrawable* drawablep = *curiter; BOOL update_complete = TRUE; if (drawablep && !drawablep->isDead()) { update_complete = updateDrawableGeom(drawablep, TRUE); } if (update_complete) { drawablep->clearState(LLDrawable::IN_REBUILD_Q1); mBuildQ1.erase(curiter); } } // Iterate through some drawables on the non-priority build queue S32 min_count = 16; if (mBuildQ2.size() > 1000) { min_count = mBuildQ2.size(); } else { mBuildQ2.sort(LLDrawable::CompareDistanceGreaterVisibleFirst()); } S32 count = 0; max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime); for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin(); iter != mBuildQ2.end(); ) { LLDrawable::drawable_list_t::iterator curiter = iter++; LLDrawable* drawablep = *curiter; BOOL update_complete = TRUE; if (drawablep && !drawablep->isDead()) { update_complete = updateDrawableGeom(drawablep, FALSE); count++; } if (update_complete) { drawablep->clearState(LLDrawable::IN_REBUILD_Q2); mBuildQ2.erase(curiter); } if ((update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count) { break; } } } void LLPipeline::markVisible(LLDrawable *drawablep) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if(!drawablep || drawablep->isDead()) { llwarns << "LLPipeline::markVisible called with NULL drawablep" << llendl; return; } if (!drawablep->isVisible()) { drawablep->setVisible(*gCamera); mVisibleList.push_back(drawablep); } } void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (!drawablep) { llerrs << "Sending null drawable to moved list!" << llendl; return; } if (drawablep->isDead()) { llwarns << "Marking NULL or dead drawable moved!" << llendl; return; } if (drawablep->getParent()) { //ensure that parent drawables are moved first markMoved(drawablep->getParent(), damped_motion); } if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) { mMovedList.insert(drawablep); drawablep->setState(LLDrawable::ON_MOVE_LIST); } if (damped_motion == FALSE) { drawablep->setState(LLDrawable::MOVE_UNDAMPED); // UNDAMPED trumps DAMPED } else if (drawablep->isState(LLDrawable::MOVE_UNDAMPED)) { drawablep->clearState(LLDrawable::MOVE_UNDAMPED); } } void LLPipeline::markShift(LLDrawable *drawablep) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (!drawablep || drawablep->isDead()) { return; } if (!drawablep->isState(LLDrawable::ON_SHIFT_LIST)) { drawablep->getVObj()->setChanged(LLXform::SHIFTED | LLXform::SILHOUETTE); if (drawablep->getParent()) { markShift(drawablep->getParent()); } mShiftList.push_back(drawablep); drawablep->setState(LLDrawable::ON_SHIFT_LIST); } } void LLPipeline::shiftObjects(const LLVector3 &offset) { LLMemType mt(LLMemType::MTYPE_PIPELINE); for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin(); iter != mShiftList.end(); iter++) { LLDrawable *drawablep = *iter; if (drawablep->isDead()) { continue; } drawablep->shiftPos(offset); drawablep->clearState(LLDrawable::ON_SHIFT_LIST); } mShiftList.resize(0); mObjectPartition->shift(offset); } void LLPipeline::markTextured(LLDrawable *drawablep) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (!drawablep->isDead()) { mRetexturedList.insert(drawablep); } } void LLPipeline::markMaterialed(LLDrawable *drawablep) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (!drawablep->isDead()) { mRematerialedList.insert(drawablep); } } void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority) { LLMemType mt(LLMemType::MTYPE_PIPELINE); if (drawablep && !drawablep->isDead()) { if (!drawablep->isState(LLDrawable::BUILT)) { priority = TRUE; } if (priority) { mBuildQ1.insert(drawablep); drawablep->setState(LLDrawable::IN_REBUILD_Q1); // flag is not needed, just for debugging } else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2)) { mBuildQ2.push_back(drawablep); drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list } if (flag & LLDrawable::REBUILD_VOLUME) { drawablep->getVObj()->setChanged(LLXform::SILHOUETTE); } drawablep->setState(flag); if ((flag & LLDrawable::REBUILD_LIGHTING) && drawablep->getLit()) { if (drawablep->isLight()) { drawablep->clearState(LLDrawable::LIGHTING_BUILT); } else { drawablep->clearState(LLDrawable::LIGHTING_BUILT); } } } } void LLPipeline::markRelight(LLDrawable *drawablep, const BOOL priority) { if (getLightingDetail() >= 2) { markRebuild(drawablep, LLDrawable::REBUILD_LIGHTING, FALSE); } } void LLPipeline::stateSort() { LLFastTimer ftm(LLFastTimer::FTM_STATESORT); LLMemType mt(LLMemType::MTYPE_PIPELINE); for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++) { LLDrawable *drawablep = *iter; if (drawablep->isDead()) { continue; } if (!drawablep->isActive()) { drawablep->updateDistance(*gCamera); } /* if (!drawablep->isState(LLDrawable::BUILT)) { // This geometry hasn't been rebuilt but it's visible, make sure it gets put on the rebuild list. llerrs << "Visible object " << drawablep << ":" << drawablep->getVObj()->getPCodeString(); llcont << " visible but not built, put on rebuild" << llendl; markRebuild(drawablep); continue; } */ for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin(); iter != drawablep->mFaces.end(); iter++) { LLFace* facep = *iter; if (facep->hasGeometry()) { facep->getPool()->enqueue(facep); } } if (sRenderPhysicalBeacons) { // Only show the beacon on the root object. LLViewerObject *vobj = drawablep->getVObj(); if (vobj && !vobj->isAvatar() && !vobj->getParent() && vobj->usePhysics()) { gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f)); } } if (sRenderScriptedBeacons) { // Only show the beacon on the root object. LLViewerObject *vobj = drawablep->getVObj(); if (vobj && !vobj->isAvatar() && !vobj->getParent() && vobj->flagScripted()) { gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f)); } } if (sRenderParticleBeacons) { // Look for attachments, objects, etc. LLViewerObject *vobj = drawablep->getVObj(); if (vobj && vobj->isParticleSource()) { LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f); gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f)); } } // Draw physical objects in red. if (gHUDManager->getShowPhysical()) { LLViewerObject *vobj; vobj = drawablep->getVObj(); if (vobj && !vobj->isAvatar()) { if (!vobj->isAvatar() && (vobj->usePhysics() || vobj->flagHandleTouch())) { if (!drawablep->isVisible()) { // Skip objects that aren't visible. continue; } S32 face_id; for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++) { mHighlightFaces.put(drawablep->getFace(face_id) ); } } } } mNumVisibleFaces += drawablep->getNumFaces(); } // If god mode, also show audio cues if (sRenderSoundBeacons && gAudiop) { // Update all of our audio sources, clean up dead ones. LLAudioEngine::source_map::iterator iter; for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter) { LLAudioSource *sourcep = iter->second; LLVector3d pos_global = sourcep->getPositionGlobal(); LLVector3 pos = gAgent.getPosAgentFromGlobal(pos_global); //pos += LLVector3(0.f, 0.f, 0.2f); gObjectList.addDebugBeacon(pos, "", LLColor4(1.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f)); } } // If managing your telehub, draw beacons at telehub and currently selected spawnpoint. if (LLFloaterTelehub::renderBeacons()) { LLFloaterTelehub::addBeacons(); } mSelectedFaces.reset(); // Draw face highlights for selected faces. if (gSelectMgr->getTEMode()) { LLViewerObject *vobjp; S32 te; gSelectMgr->getFirstTE(&vobjp,&te); while (vobjp) { LLDrawable *drawablep = vobjp->mDrawable; if (!drawablep || drawablep->isDead() || (!vobjp->isHUDAttachment() && !drawablep->isVisible())) { llwarns << "Dead drawable on selected face list!" << llendl; } else { LLVOVolume *volp = drawablep->getVOVolume(); if (volp) { if (volp->getAllTEsSame()) { SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1); faceinfo->mFacep = drawablep->getFace(vobjp->getFaceIndexOffset()); faceinfo->mTE = te; } else { // This is somewhat inefficient, but works correctly. S32 face_id; for (face_id = 0; face_id < vobjp->getVolume()->getNumFaces(); face_id++) { LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset()); if (te == facep->getTEOffset()) { SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1); faceinfo->mFacep = facep; faceinfo->mTE = -1; } } } } else { // This is somewhat inefficient, but works correctly. S32 face_id; for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++) { LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset()); if (te == facep->getTEOffset()) { SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1); faceinfo->mFacep = facep; faceinfo->mTE = -1; } } } } gSelectMgr->getNextTE(&vobjp,&te); } } } static void render_hud_elements() { LLFastTimer t(LLFastTimer::FTM_RENDER_UI); gPipeline.disableLights(); gPipeline.renderDebug(); LLGLDisable fog(GL_FOG); LLGLSUIDefault gls_ui; if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) { gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD bersion in render_ui_3d() // Draw the tracking overlays LLTracker::render3D(); // Show the property lines if (gWorldp) { gWorldp->renderPropertyLines(); } if (gParcelMgr) { gParcelMgr->render(); gParcelMgr->renderParcelCollision(); } // Render debugging beacons. gObjectList.renderObjectBeacons(); LLHUDObject::renderAll(); gObjectList.resetObjectBeacons(); } else if (gForceRenderLandFence) { // This is only set when not rendering the UI, for parcel snapshots gParcelMgr->render(); } } void LLPipeline::renderHighlights() { // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD) // Render highlighted faces. LLColor4 color(1.f, 1.f, 1.f, 0.5f); LLGLEnable color_mat(GL_COLOR_MATERIAL); disableLights(); if ((mVertexShaderLevel[SHADER_INTERFACE] > 0)) { mHighlightProgram.bind(); gPipeline.mHighlightProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,1,0,0,0.5f); } if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) { // Make sure the selection image gets downloaded and decoded if (!mFaceSelectImagep) { mFaceSelectImagep = gImageList.getImage(IMG_FACE_SELECT); } mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA); for (S32 i = 0; i < mSelectedFaces.count(); i++) { LLFace *facep = mSelectedFaces[i].mFacep; if (!facep || facep->getDrawable()->isDead()) { llerrs << "Bad face on selection" << llendl; } LLDrawPool* poolp = facep->getPool(); if (!poolp->canUseAGP()) { unbindAGP(); } else if (usingAGP()) { bindAGP(); } if (mSelectedFaces[i].mTE == -1) { // Yes, I KNOW this is stupid... poolp->renderFaceSelected(facep, mFaceSelectImagep, color); } else { LLVOVolume *volp = (LLVOVolume *)facep->getViewerObject(); // Do the special coalesced face mode. S32 j; S32 offset = 0; S32 count = volp->getVolume()->getVolumeFace(0).mIndices.size(); for (j = 0; j <= mSelectedFaces[i].mTE; j++) { count = volp->getVolume()->getVolumeFace(j).mIndices.size(); if (j < mSelectedFaces[i].mTE) { offset += count; } } poolp->renderFaceSelected(facep, mFaceSelectImagep, color, offset, count); } } } if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) { // Paint 'em red! color.setVec(1.f, 0.f, 0.f, 0.5f); for (S32 i = 0; i < mHighlightFaces.count(); i++) { LLFace* facep = mHighlightFaces[i]; LLDrawPool* poolp = facep->getPool(); if (!poolp->canUseAGP()) { unbindAGP(); } else if (usingAGP()) { bindAGP(); } poolp->renderFaceSelected(facep, LLViewerImage::sNullImagep, color); } } // Contains a list of the faces of objects that are physical or // have touch-handlers. mHighlightFaces.reset(); if (mVertexShaderLevel[SHADER_INTERFACE] > 0) { mHighlightProgram.unbind(); } } void LLPipeline::renderGeom() { LLMemType mt(LLMemType::MTYPE_PIPELINE); LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY); if (!mAlphaSizzleImagep) { mAlphaSizzleImagep = gImageList.getImage(LLUUID(gViewerArt.getString("alpha_sizzle.tga")), MIPMAP_TRUE, TRUE); } /////////////////////////////////////////// // // Sync and verify GL state // // stop_glerror(); gFrameStats.start(LLFrameStats::RENDER_SYNC); // Do verification of GL state #ifndef LL_RELEASE_FOR_DOWNLOAD LLGLState::checkStates(); LLGLState::checkTextureChannels(); #endif if (mRenderDebugMask & RENDER_DEBUG_VERIFY) { if (!verify()) { llerrs << "Pipeline verification failed!" << llendl; } } if (mAGPMemPool) { mAGPMemPool->waitFence(mGlobalFence); } unbindAGP(); for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; if (hasRenderType(poolp->getType())) { poolp->prerender(); poolp->syncAGP(); } } gFrameStats.start(LLFrameStats::RENDER_GEOM); // Initialize lots of GL state to "safe" values mTrianglesDrawn = 0; glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); LLGLSPipeline gls_pipeline; LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2); LLGLState normalize(GL_NORMALIZE, TRUE); // Toggle backface culling for debugging LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0); // Set fog LLGLEnable fog_enable(hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG) ? GL_FOG : 0); LLViewerImage::sDefaultImagep->bind(0); LLViewerImage::sDefaultImagep->setClamp(FALSE, FALSE); ////////////////////////////////////////////// // // Actually render all of the geometry // // stop_glerror(); BOOL non_agp = FALSE; BOOL did_hud_elements = FALSE; U32 cur_type = 0; S32 skipped_vertices = 0; { LLFastTimer t(LLFastTimer::FTM_POOLS); BOOL occlude = TRUE; calcNearbyLights(); pool_set_t::iterator iter1 = mPools.begin(); while ( iter1 != mPools.end() ) { LLDrawPool *poolp = *iter1; cur_type = poolp->getType(); if (cur_type >= LLDrawPool::POOL_TREE && occlude) { //all the occluders have been drawn, do occlusion queries if (mVertexShadersEnabled) { glUseProgramObjectARB(0); } doOcclusion(); occlude = FALSE; } if (cur_type >= LLDrawPool::POOL_HUD && !did_hud_elements) { renderHighlights(); // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD) if (mVertexShadersEnabled) { glUseProgramObjectARB(0); } render_hud_elements(); did_hud_elements = TRUE; } pool_set_t::iterator iter2 = iter1; if (hasRenderType(poolp->getType())) { LLFastTimer t(LLFastTimer::FTM_POOLRENDER); setupHWLights(poolp); if (mVertexShadersEnabled && poolp->getVertexShaderLevel() == 0) { glUseProgramObjectARB(0); } else if (mVertexShadersEnabled) { mMaterialIndex = mSpecularIndex = 0; switch(cur_type) { case LLDrawPool::POOL_SKY: case LLDrawPool::POOL_STARS: case LLDrawPool::POOL_CLOUDS: glUseProgramObjectARB(0); break; case LLDrawPool::POOL_TERRAIN: mTerrainProgram.bind(); break; case LLDrawPool::POOL_GROUND: mGroundProgram.bind(); break; case LLDrawPool::POOL_TREE: case LLDrawPool::POOL_TREE_NEW: case LLDrawPool::POOL_SIMPLE: case LLDrawPool::POOL_MEDIA: mObjectSimpleProgram.bind(); break; case LLDrawPool::POOL_BUMP: mObjectBumpProgram.bind(); break; case LLDrawPool::POOL_AVATAR: glUseProgramObjectARB(0); break; case LLDrawPool::POOL_WATER: glUseProgramObjectARB(0); break; case LLDrawPool::POOL_ALPHA: mObjectAlphaProgram.bind(); break; case LLDrawPool::POOL_HUD: default: glUseProgramObjectARB(0); break; } } for( S32 i = 0; i < poolp->getNumPasses(); i++ ) { poolp->beginRenderPass(i); for (iter2 = iter1; iter2 != mPools.end(); iter2++) { LLDrawPool *p = *iter2; if (p->getType() != cur_type) { break; } if (p->getType() != LLDrawPool::POOL_AVATAR && p->getType() != LLDrawPool::POOL_ALPHA && p->getType() != LLDrawPool::POOL_HUD && (!p->getIndexCount() || !p->getVertexCount())) { continue; } if (p->canUseAGP() && usingAGP()) { bindAGP(); } else { //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl; unbindAGP(); non_agp = TRUE; } p->resetTrianglesDrawn(); p->render(i); mTrianglesDrawn += p->getTrianglesDrawn(); skipped_vertices += p->mSkippedVertices; p->mSkippedVertices = 0; } poolp->endRenderPass(i); #ifndef LL_RELEASE_FOR_DOWNLOAD LLGLState::checkStates(); LLGLState::checkTextureChannels(); LLGLState::checkClientArrays(); #endif } } else { // Skip all pools of this type for (iter2 = iter1; iter2 != mPools.end(); iter2++) { LLDrawPool *p = *iter2; if (p->getType() != cur_type) { break; } } } iter1 = iter2; stop_glerror(); } if (occlude) { if (mVertexShadersEnabled) { glUseProgramObjectARB(0); } doOcclusion(); } } stop_glerror(); if (mVertexShadersEnabled) { glUseProgramObjectARB(0); } if (!did_hud_elements) { renderHighlights(); render_hud_elements(); } static S32 agp_mix_count = 0; if (non_agp && usingAGP()) { if (0 == agp_mix_count % 16) { lldebugs << "Mixing AGP and non-AGP pools, slow!" << llendl; } agp_mix_count++; } else { agp_mix_count = 0; } // Contains a list of the faces of objects that are physical or // have touch-handlers. mHighlightFaces.reset(); // This wait is in case we try to do multiple renders of a frame, // I don't know what happens when we send a fence multiple times without // checking it. if (mAGPMemPool) { mAGPMemPool->waitFence(mGlobalFence); mAGPMemPool->sendFence(mGlobalFence); } } void LLPipeline::renderDebug() { LLMemType mt(LLMemType::MTYPE_PIPELINE); // Disable all client state glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); // Debug stuff. mObjectPartition->renderDebug(); if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_LIGHT_TRACE) { LLGLSNoTexture no_texture; LLVector3 pos, pos1; for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++) { LLDrawable *drawablep = *iter; if (drawablep->isDead()) { continue; } for (LLDrawable::drawable_set_t::iterator iter = drawablep->mLightSet.begin(); iter != drawablep->mLightSet.end(); iter++) { LLDrawable *targetp = *iter; if (targetp->isDead() || !targetp->getVObj()->getNumTEs()) { continue; } else { if (targetp->getTextureEntry(0)) { if (drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) { glColor4f(0.f, 1.f, 0.f, 1.f); gObjectList.addDebugBeacon(drawablep->getPositionAgent(), "TC"); } else { glColor4fv (targetp->getTextureEntry(0)->getColor().mV); } glBegin(GL_LINES); glVertex3fv(targetp->getPositionAgent().mV); glVertex3fv(drawablep->getPositionAgent().mV); glEnd(); } } } } } mCompilesStat.addValue(sCompiles); mLightingChangesStat.addValue(mLightingChanges); mGeometryChangesStat.addValue(mGeometryChanges); mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f); mVerticesRelitStat.addValue(mVerticesRelit); mNumVisibleFacesStat.addValue(mNumVisibleFaces); mNumVisibleDrawablesStat.addValue((S32)mVisibleList.size()); if (gRenderLightGlows) { displaySSBB(); } /*if (mRenderDebugMask & RENDER_DEBUG_BBOXES) { LLGLSPipelineAlpha gls_pipeline_alpha; LLGLSNoTexture no_texture; for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++) { LLDrawable *drawablep = *iter; if (drawablep->isDead()) { continue; } LLVector3 min, max; if (drawablep->getVObj() && drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) { // Render drawable bbox drawablep->getBounds(min, max); glColor4f(0.f, 1.f, 0.f, 0.25f); render_bbox(min, max); // Render object bbox LLVector3 scale = drawablep->getVObj()->getScale(); LLVector3 pos = drawablep->getVObj()->getPositionAgent(); min = pos - scale * 0.5f; max = pos + scale * 0.5f; glColor4f(1.f, 0.f, 0.f, 0.25f); render_bbox(min, max); } } }*/ /* // Debugging code for parcel sound. F32 x, y; LLGLSNoTexture gls_no_texture; glBegin(GL_POINTS); if (gAgent.getRegion()) { // Draw the composition layer for the region that I'm in. for (x = 0; x <= 260; x++) { for (y = 0; y <= 260; y++) { if (gParcelMgr->isSoundLocal(gAgent.getRegion()->getOriginGlobal() + LLVector3d(x, y, 0.f))) { glColor4f(1.f, 0.f, 0.f, 1.f); } else { glColor4f(0.f, 0.f, 1.f, 1.f); } glVertex3f(x, y, gAgent.getRegion()->getLandHeightRegion(LLVector3(x, y, 0.f))); } } } glEnd(); */ if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION) { // Debug composition layers F32 x, y; LLGLSNoTexture gls_no_texture; glBegin(GL_POINTS); if (gAgent.getRegion()) { // Draw the composition layer for the region that I'm in. for (x = 0; x <= 260; x++) { for (y = 0; y <= 260; y++) { if ((x > 255) || (y > 255)) { glColor4f(1.f, 0.f, 0.f, 1.f); } else { glColor4f(0.f, 0.f, 1.f, 1.f); } F32 z = gAgent.getRegion()->getCompositionXY((S32)x, (S32)y); z *= 5.f; z += 50.f; glVertex3f(x, y, z); } } } glEnd(); } if (mRenderDebugMask & RENDER_DEBUG_AGP_MEM) { displayAGP(); } if (mRenderDebugMask & RENDER_DEBUG_POOLS) { displayPools(); } // if (mRenderDebugMask & RENDER_DEBUG_QUEUES) // { // displayQueues(); // } if (mRenderDebugMask & RENDER_DEBUG_MAP) { displayMap(); } } BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max) { min.setVec(1000,1000); max.setVec(-1000,-1000); if (box.mMatrix[3][3] <= 0.0f) return FALSE; const F32 vec[8][3] = { { -0.5f,-0.5f,-0.5f }, { -0.5f,-0.5f,+0.5f }, { -0.5f,+0.5f,-0.5f }, { -0.5f,+0.5f,+0.5f }, { +0.5f,-0.5f,-0.5f }, { +0.5f,-0.5f,+0.5f }, { +0.5f,+0.5f,-0.5f }, { +0.5f,+0.5f,+0.5f } }; LLVector4 v; for (S32 i=0;i<8;i++) { v.setVec(vec[i][0],vec[i][1],vec[i][2],1); v = v * box; F32 iw = 1.0f / v.mV[3]; v.mV[0] *= iw; v.mV[1] *= iw; min.mV[0] = llmin(min.mV[0],v.mV[0]); max.mV[0] = llmax(max.mV[0],v.mV[0]); min.mV[1] = llmin(min.mV[1],v.mV[1]); max.mV[1] = llmax(max.mV[1],v.mV[1]); } /* min.mV[0] = max.mV[0] = box.mMatrix[3][0]; min.mV[1] = max.mV[1] = box.mMatrix[3][1]; F32 iw = 1.0f / box.mMatrix[3][3]; F32 f0 = (fabs(box.mMatrix[0][0])+fabs(box.mMatrix[1][0])+fabs(box.mMatrix[2][0])) * 0.5f; F32 f1 = (fabs(box.mMatrix[0][1])+fabs(box.mMatrix[1][1])+fabs(box.mMatrix[2][1])) * 0.5f; F32 f2 = (fabs(box.mMatrix[0][2])+fabs(box.mMatrix[1][2])+fabs(box.mMatrix[2][2])) * 0.5f; min.mV[0] -= f0; min.mV[1] -= f1; max.mV[0] += f0; max.mV[1] += f1; min.mV[0] *= iw; min.mV[1] *= iw; max.mV[0] *= iw; max.mV[1] *= iw; */ return TRUE; } void LLPipeline::displaySSBB() { LLMatrix4 proj; LLMatrix4 cfr(OGL_TO_CFR_ROTATION); LLMatrix4 camera; LLMatrix4 comb; gCamera->getMatrixToLocal(camera); if (!mBloomImagep) { mBloomImagep = gImageList.getImage(IMG_BLOOM1); } // don't write to depth buffer with light glows so that chat bubbles can pop through LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); LLViewerImage::bindTexture(mBloomImagep); glGetFloatv(GL_PROJECTION_MATRIX,(float*)proj.mMatrix); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); LLGLSPipelineAlpha gls_pipeline_alpha; //glScalef(0.25,0.25,0.25); S32 sizex = gViewerWindow->getWindowWidth() / 2; S32 sizey = gViewerWindow->getWindowHeight() / 2; F32 aspect = (float)sizey / (float)sizex; for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); iter++) { LLDrawable *lightp = *iter; if (lightp->isDead()) { continue; } LLMatrix4 mat = lightp->mXform.getWorldMatrix(); mat *= camera; mat *= cfr; mat *= proj; U8 color[64]; LLVector2 min,max; if (mat.mMatrix[3][3] < 160 && compute_min_max(mat,min,max)) { F32 cx = (max.mV[0] + min.mV[0]) * 0.5f; F32 cy = (max.mV[1] + min.mV[1]) * 0.5f; F32 sx = (max.mV[0] - min.mV[0]) * 2.0f; F32 sy = (max.mV[1] - min.mV[1]) * 2.0f; S32 x = (S32)(cx * (F32)sizex) + sizex; S32 y = (S32)(cy * (F32)sizey) + sizey; if (cx > -1 && cx < 1 && cy > -1 && cy < 1) { glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]); S32 total = 0; for (S32 i=0;i<64;i++) { total += color[i]; } total /= 64; sx = (sy = (sx + sy) * 0.5f * ((float)total/255.0f)) * aspect; if (total > 60) { color[3+32] = total >> 1; glBegin(GL_QUADS); glColor4ubv(&color[32]); glTexCoord2f(0,0); glVertex3f(cx-sx,cy-sy,0); glTexCoord2f(1,0); glVertex3f(cx+sx,cy-sy,0); glTexCoord2f(1,1); glVertex3f(cx+sx,cy+sy,0); glTexCoord2f(0,1); glVertex3f(cx-sx,cy+sy,0); glEnd(); } } } } // sun { LLVector4 sdir(gSky.getSunDirection() * 10000.0f + gAgent.getPositionAgent()); sdir.mV[3] = 1.0f; sdir = sdir * camera; sdir = sdir * cfr; sdir = sdir * proj; // todo: preconcat sdir.mV[0] /= sdir.mV[3]; sdir.mV[1] /= sdir.mV[3]; U8 color[64]; if (sdir.mV[3] > 0) { F32 cx = sdir.mV[0]; F32 cy = sdir.mV[1]; F32 sx, sy; S32 x = (S32)(cx * (F32)sizex) + sizex; S32 y = (S32)(cy * (F32)sizey) + sizey; if (cx > -1 && cx < 1 && cy > -1 && cy < 1) { glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]); S32 total = 0; for (S32 i=0;i<64;i++) { total += color[i]; } total >>= 7; sx = (sy = ((float)total/255.0f)) * aspect; const F32 fix = -0.1f; color[32] = (U8)(color[32] * 0.5f + 255 * 0.5f); color[33] = (U8)(color[33] * 0.5f + 255 * 0.5f); color[34] = (U8)(color[34] * 0.5f + 255 * 0.5f); if (total > 80) { color[32+3] = (U8)total; glBegin(GL_QUADS); glColor4ubv(&color[32]); glTexCoord2f(0,0); glVertex3f(cx-sx,cy-sy+fix,0); glTexCoord2f(1,0); glVertex3f(cx+sx,cy-sy+fix,0); glTexCoord2f(1,1); glVertex3f(cx+sx,cy+sy+fix,0); glTexCoord2f(0,1); glVertex3f(cx-sx,cy+sy+fix,0); glEnd(); } } } } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void LLPipeline::displayMap() { LLGLSPipelineAlpha gls_pipeline_alpha; LLGLSNoTexture no_texture; LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glTranslatef(-1,-1,0); glScalef(2,2,0); glColor4f(0,0,0,0.5); glBegin(GL_QUADS); glVertex3f(0,0,0); glVertex3f(1,0,0); glVertex3f(1,1,0); glVertex3f(0,1,0); glEnd(); static F32 totalW = 1.5f; static F32 offset = 0.5f; static F32 scale = 1.0f; S32 mousex = gViewerWindow->getCurrentMouseX(); S32 mousey = gViewerWindow->getCurrentMouseY(); S32 w = gViewerWindow->getWindowWidth(); S32 h = gViewerWindow->getWindowHeight(); if (mousex < 20 && offset > 0) offset -= (20 - mousex) * 0.02f; if (mousex > (w - 20)) offset += (20 - (w - mousex)) * 0.02f; if (offset > (totalW-1)) offset = (totalW-1); if (mousey < 20 && scale > 0.1) scale -= (20 - mousey) * 0.001f; if (mousey > (h - 20) && scale < 1.0f) scale += (20 - (h - mousey)) * 0.001f; glScalef(scale*scale,scale,1); glTranslatef(-offset,0,0); //totalW = mStaticTree->render2D(0,0.8f/scale); //mDynamicTree->render2D(0,0.4f/scale); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } void LLPipeline::renderForSelect() { LLMemType mt(LLMemType::MTYPE_PIPELINE); //for each drawpool glMatrixMode(GL_MODELVIEW); LLGLSDefault gls_default; LLGLSObjectSelect gls_object_select; LLGLDepthTest gls_depth(GL_TRUE); disableLights(); glEnableClientState ( GL_VERTEX_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); #ifndef LL_RELEASE_FOR_DOWNLOAD LLGLState::checkStates(); LLGLState::checkTextureChannels(); LLGLState::checkClientArrays(); U32 last_type = 0; #endif for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; if (poolp->canUseAGP() && usingAGP()) { bindAGP(); } else { //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl; unbindAGP(); } poolp->renderForSelect(); #ifndef LL_RELEASE_FOR_DOWNLOAD if (poolp->getType() != last_type) { last_type = poolp->getType(); LLGLState::checkStates(); LLGLState::checkTextureChannels(); LLGLState::checkClientArrays(); } #endif } // Disable all of the client state glDisableClientState( GL_VERTEX_ARRAY ); if (mAGPMemPool) { mAGPMemPool->waitFence(mGlobalFence); mAGPMemPool->sendFence(mGlobalFence); } } void LLPipeline::renderFaceForUVSelect(LLFace* facep) { if (facep) facep->renderSelectedUV(); } void LLPipeline::rebuildPools() { LLMemType mt(LLMemType::MTYPE_PIPELINE); S32 max_count = mPools.size(); S32 num_rebuilds = 0; pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool); while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS) { if (iter1 == mPools.end()) { iter1 = mPools.begin(); } LLDrawPool* poolp = *iter1; num_rebuilds += poolp->rebuild(); if (poolp->mReferences.empty()) { mPools.erase(iter1++); removeFromQuickLookup( poolp ); if (poolp == mLastRebuildPool) { mLastRebuildPool = NULL; } delete poolp; } else { mLastRebuildPool = poolp; iter1++; } max_count--; } if (gAgent.getAvatarObject()) { gAgent.getAvatarObject()->rebuildHUD(); } } void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) { LLMemType mt(LLMemType::MTYPE_PIPELINE); switch( new_poolp->getType() ) { case LLDrawPool::POOL_SIMPLE: mSimplePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_TREE: mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_TREE_NEW: mTreeNewPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_TERRAIN: mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_BUMP: mBumpPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_MEDIA: mMediaPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; break; case LLDrawPool::POOL_ALPHA: if( mAlphaPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl; } else { mAlphaPool = new_poolp; } break; case LLDrawPool::POOL_AVATAR: break; // Do nothing case LLDrawPool::POOL_SKY: if( mSkyPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl; } else { mSkyPool = new_poolp; } break; case LLDrawPool::POOL_STARS: if( mStarsPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Stars pool" << llendl; } else { mStarsPool = new_poolp; } break; case LLDrawPool::POOL_CLOUDS: if( mCloudsPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Clouds pool" << llendl; } else { mCloudsPool = new_poolp; } break; case LLDrawPool::POOL_WATER: if( mWaterPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl; } else { mWaterPool = new_poolp; } break; case LLDrawPool::POOL_GROUND: if( mGroundPool ) { llassert(0); llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl; } else { mGroundPool = new_poolp; } break; case LLDrawPool::POOL_HUD: if( mHUDPool ) { llerrs << "LLPipeline::addPool(): Duplicate HUD Pool!" << llendl; } mHUDPool = new_poolp; break; default: llassert(0); llwarns << "Invalid Pool Type in LLPipeline::addPool()" << llendl; break; } } void LLPipeline::removePool( LLDrawPool* poolp ) { removeFromQuickLookup(poolp); mPools.erase(poolp); delete poolp; } void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp ) { LLMemType mt(LLMemType::MTYPE_PIPELINE); switch( poolp->getType() ) { case LLDrawPool::POOL_SIMPLE: #ifdef _DEBUG { BOOL found = mSimplePools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mSimplePools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_TREE: #ifdef _DEBUG { BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mTreePools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_TREE_NEW: #ifdef _DEBUG { BOOL found = mTreeNewPools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mTreeNewPools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_TERRAIN: #ifdef _DEBUG { BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_BUMP: #ifdef _DEBUG { BOOL found = mBumpPools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mBumpPools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_MEDIA: #ifdef _DEBUG { BOOL found = mMediaPools.erase( (uintptr_t)poolp->getTexture() ); llassert( found ); } #else mMediaPools.erase( (uintptr_t)poolp->getTexture() ); #endif break; case LLDrawPool::POOL_ALPHA: llassert( poolp == mAlphaPool ); mAlphaPool = NULL; break; case LLDrawPool::POOL_AVATAR: break; // Do nothing case LLDrawPool::POOL_SKY: llassert( poolp == mSkyPool ); mSkyPool = NULL; break; case LLDrawPool::POOL_STARS: llassert( poolp == mStarsPool ); mStarsPool = NULL; break; case LLDrawPool::POOL_CLOUDS: llassert( poolp == mCloudsPool ); mCloudsPool = NULL; break; case LLDrawPool::POOL_WATER: llassert( poolp == mWaterPool ); mWaterPool = NULL; break; case LLDrawPool::POOL_GROUND: llassert( poolp == mGroundPool ); mGroundPool = NULL; break; case LLDrawPool::POOL_HUD: llassert( poolp == mHUDPool ); mHUDPool = NULL; break; default: llassert(0); llwarns << "Invalid Pool Type in LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl; break; } } void LLPipeline::flushAGPMemory() { LLMemType mt(LLMemType::MTYPE_PIPELINE); for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; poolp->flushAGP(); } } void LLPipeline::resetDrawOrders() { // Iterate through all of the draw pools and rebuild them. for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; poolp->resetDrawOrders(); } } //------------------------------- LLViewerObject *LLPipeline::nearestObjectAt(F32 yaw, F32 pitch) { // Stub to find nearest Object at given yaw and pitch /* LLEdge *vd = NULL; if (vd) { return (LLViewerObject*)vd->mDrawablep->getVObj(); } */ return NULL; } void LLPipeline::printPools() { /* // Iterate through all of the draw pools and rebuild them. for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; if (pool->mTexturep[0]) { llinfos << "Op pool " << pool->mTexturep[0]->mID << llendflush; } else { llinfos << "Opaque pool NULL" << llendflush; } llinfos << " Vertices: \t" << pool->getVertexCount() << llendl; } for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; llinfos << "Al pool " << pool; llcont << " Vertices: \t" << pool->getVertexCount() << llendl; } pool = mHighlightPools.getFirst(); llinfos << "Si pool " << pool; llcont << " Vertices: \t" << pool->getVertexCount() << llendl; */ } void LLPipeline::displayPools() { // Needs to be fixed to handle chained pools - djs LLUI::setLineWidth(1.0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); LLGLSPipelineAlpha gls_pipeline_alpha; LLGLSNoTexture no_texture; LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); glScalef(2,2,1); glTranslatef(-0.5f,-0.5f,0); F32 x = 0.0f, y = 0.05f; F32 xs = 0.01f, ys = 0.01f; F32 xs2 = xs*0.1f, ys2 = ys * 0.1f; for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLGLSTexture gls_texture; LLDrawPool *poolp = *iter; if (poolp->getDebugTexture()) { poolp->getDebugTexture()->bind(); glColor4f(1,1,1,1); stamp(x,y,xs*4,ys*4); } LLGLSNoTexture no_texture; F32 a = 1.f - (F32)poolp->mRebuildTime / (F32)poolp->mRebuildFreq; glColor4f(a,a,a,1); stamp(x,y,xs,ys); x = (xs + xs2) * 4.0f; F32 h = ys * 0.5f; S32 total = 0; for (std::vector::iterator iter = poolp->mReferences.begin(); iter != poolp->mReferences.end(); iter++) { LLFace *face = *iter; F32 w = xs; if (!face || !face->getDrawable()) { w = 16 / 3000.0f; stamp(x,y,w,h); if (x+w > 0.95f) { x = (xs + xs2) * 4.0f; y += h + ys2; } else { if (w) x += w + xs2; } continue; } if (face->getDrawable()->isVisible()) { if (face->isState(LLFace::BACKLIST)) { glColor4f(1,0,1,1); } else if (total > poolp->getMaxVertices()) { glColor4f(1,0,0,1); } else { glColor4f(0,1,0,1); total += face->getGeomCount(); } } else { if (face->isState(LLFace::BACKLIST)) { glColor4f(0,0,1,1); } else { glColor4f(1,1,0,1); } } w = face->getGeomCount() / 3000.0f; stamp(x,y,w,h); if (x+w > 0.95f) { x = (xs + xs2) * 4.0f; y += h + ys2; } else { if (w) x += w + xs2; } } y += ys + ys2; x = 0; } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } static F32 xs = 0.01f, ys = 0.01f; static F32 xs2 = xs*0.1f, ys2 = ys * 0.1f; static F32 winx, winy; void displayDrawable(F32 &x, F32 &y, LLDrawable *drawable, F32 alpha = 0.5f) { F32 w = 0; F32 h = ys * 0.5f; if (drawable && !drawable->isDead()) { for (S32 f=0;f < drawable->getNumFaces(); f++) { w += drawable->getFace(f)->getGeomCount() / 30000.0f; } w+=xs; glColor4f(1,1,0, alpha); } else { w = 0.01f; glColor4f(0,0,0,alpha); } // const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); // U8 pcode = drawable->getVObj()->getPCode(); //char *string = (char*)LLPrimitive::pCodeToString(pcode); //if (pcode == 0x3e) string = "terrain"; //else if (pcode == 0x2e) string = "cloud"; //string[3] = 0; stamp(x * winx,y * winy,w * winx,h * winy); /* glColor4f(0,0,0,1); font->render(string,x*winx+1,y*winy+1); LLGLSNoTexture no_texture; */ if (x+w > 0.95f) { x = (xs + xs2) * 4.0f; y += h + ys2; } else { if (w) x += w + xs2; } } #if 0 // No longer up date void displayQueue(F32 &x, F32 &y, LLDynamicArray& processed, LLDynamicQueuePtr >& remaining) { S32 i; for (i=0;iisDead()) { displayDrawable(x,y,drawable,0.5); } } y += ys * 4; x = (xs + xs2) * 4.0f; } void LLPipeline::displayQueues() { LLUI::setLineWidth(1.0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); LLGLSPipelineAlpha gls_pipeline_alpha; LLGLSNoTexture no_texture; LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); glScalef(2,2,1); glTranslatef(-0.5f,-0.5f,0); winx = (F32)gViewerWindow->getWindowWidth(); winy = (F32)gViewerWindow->getWindowHeight(); glScalef(1.0f/winx,1.0f/winy,1); F32 x = (xs + xs2) * 4.0f; F32 y = 0.1f; const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF ); font->renderUTF8("Build1", 0,0,(S32)(y*winy),LLColor4(1,1,1,1)); displayQueue(x,y, gBuildProcessed, mBuildQ1); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } #endif // static void render_bbox(const LLVector3 &min, const LLVector3 &max) { S32 i; LLVector3 verticesp[16]; verticesp[0].setVec(min.mV[0],min.mV[1],max.mV[2]); verticesp[1].setVec(min.mV[0],min.mV[1],min.mV[2]); verticesp[2].setVec(min.mV[0],max.mV[1],min.mV[2]); verticesp[3].setVec(min.mV[0],max.mV[1],max.mV[2]); verticesp[4].setVec(max.mV[0],max.mV[1],max.mV[2]); verticesp[5].setVec(max.mV[0],max.mV[1],min.mV[2]); verticesp[6].setVec(max.mV[0],min.mV[1],min.mV[2]); verticesp[7].setVec(max.mV[0],min.mV[1],max.mV[2]); verticesp[8 ] = verticesp[0]; verticesp[9 ] = verticesp[1]; verticesp[10] = verticesp[6]; verticesp[11] = verticesp[7]; verticesp[12] = verticesp[4]; verticesp[13] = verticesp[5]; verticesp[14] = verticesp[2]; verticesp[15] = verticesp[3]; LLGLSNoTexture gls_no_texture; { LLUI::setLineWidth(1.f); glBegin(GL_LINE_LOOP); for (i = 0; i < 16; i++) { glVertex3fv(verticesp[i].mV); } glEnd(); } { LLGLDepthTest gls_depth(GL_TRUE); LLUI::setLineWidth(3.0f); glBegin(GL_LINE_LOOP); for (i = 0; i < 16; i++) { glVertex3fv(verticesp[i].mV); } glEnd(); } LLUI::setLineWidth(1.0f); } //============================================================================ // Once-per-frame setup of hardware lights, // including sun/moon, avatar backlight, and up to 6 local lights void LLPipeline::setupAvatarLights(BOOL for_edit) { const LLColor4 black(0,0,0,1); if (for_edit) { LLColor4 diffuse(0.8f, 0.8f, 0.8f, 0.f); LLVector4 light_pos_cam(-8.f, 0.25f, 10.f, 0.f); // w==0 => directional light LLMatrix4 camera_mat = gCamera->getModelview(); LLMatrix4 camera_rot(camera_mat.getMat3()); camera_rot.invert(); LLVector4 light_pos = light_pos_cam * camera_rot; light_pos.normVec(); mHWLightColors[1] = diffuse; glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse.mV); glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV); glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV); glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV); glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); } else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini) { LLVector3 opposite_pos = -1.f * mSunDir; LLVector3 orthog_light_pos = mSunDir % LLVector3::z_axis; LLVector4 backlight_pos = LLVector4(lerp(opposite_pos, orthog_light_pos, 0.3f), 0.0f); backlight_pos.normVec(); LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor; LLColor4 backlight_diffuse(1.f - light_diffuse.mV[VRED], 1.f - light_diffuse.mV[VGREEN], 1.f - light_diffuse.mV[VBLUE], 1.f); F32 max_component = 0.001f; for (S32 i = 0; i < 3; i++) { if (backlight_diffuse.mV[i] > max_component) { max_component = backlight_diffuse.mV[i]; } } F32 backlight_mag; if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS) { backlight_mag = BACKLIGHT_DAY_MAGNITUDE_OBJECT; } else { backlight_mag = BACKLIGHT_NIGHT_MAGNITUDE_OBJECT; } backlight_diffuse *= backlight_mag / max_component; mHWLightColors[1] = backlight_diffuse; glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction glLightfv(GL_LIGHT1, GL_DIFFUSE, backlight_diffuse.mV); glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV); glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV); glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); } else { mHWLightColors[1] = black; glLightfv(GL_LIGHT1, GL_DIFFUSE, black.mV); glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV); glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV); } } static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_dist) { F32 inten = light->getLightIntensity(); if (inten < .001f) { return max_dist; } F32 radius = light->getLightRadius(); BOOL selected = light->isSelected(); LLVector3 dpos = light->getRenderPosition() - cam_pos; F32 dist2 = dpos.magVecSquared(); if (!selected && dist2 > (max_dist + radius)*(max_dist + radius)) { return max_dist; } F32 dist = fsqrtf(dist2); dist *= 1.f / inten; dist -= radius; if (selected) { dist -= 10000.f; // selected lights get highest priority } if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE)) { // moving lights get a little higher priority (too much causes artifacts) dist -= light->getLightRadius()*0.25f; } return dist; } void LLPipeline::calcNearbyLights() { if (mLightingDetail >= 1) { // mNearbyLight (and all light_set_t's) are sorted such that // begin() == the closest light and rbegin() == the farthest light const S32 MAX_LOCAL_LIGHTS = 6; // LLVector3 cam_pos = gAgent.getCameraPositionAgent(); LLVector3 cam_pos = gAgent.getPositionAgent(); F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad // UPDATE THE EXISTING NEARBY LIGHTS light_set_t cur_nearby_lights; for (light_set_t::iterator iter = mNearbyLights.begin(); iter != mNearbyLights.end(); iter++) { const Light* light = &(*iter); LLDrawable* drawable = light->drawable; LLVOVolume* volight = drawable->getVOVolume(); if (!volight || !drawable->isState(LLDrawable::LIGHT)) { drawable->clearState(LLDrawable::NEARBY_LIGHT); continue; } if (light->fade <= -LIGHT_FADE_TIME) { drawable->clearState(LLDrawable::NEARBY_LIGHT); } else { F32 dist = calc_light_dist(volight, cam_pos, max_dist); cur_nearby_lights.insert(Light(drawable, dist, light->fade)); } } mNearbyLights = cur_nearby_lights; // FIND NEW LIGHTS THAT ARE IN RANGE light_set_t new_nearby_lights; for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) { LLDrawable* drawable = *iter; LLVOVolume* light = drawable->getVOVolume(); if (!light || drawable->isState(LLDrawable::NEARBY_LIGHT)) { continue; } if (light->isHUDAttachment()) { continue; // no lighting from HUD objects } F32 dist = calc_light_dist(light, cam_pos, max_dist); if (dist >= max_dist) { continue; } new_nearby_lights.insert(Light(drawable, dist, 0.f)); if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS) { new_nearby_lights.erase(--new_nearby_lights.end()); const Light& last = *new_nearby_lights.rbegin(); max_dist = last.dist; } } // INSERT ANY NEW LIGHTS for (light_set_t::iterator iter = new_nearby_lights.begin(); iter != new_nearby_lights.end(); iter++) { const Light* light = &(*iter); if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS) { mNearbyLights.insert(*light); ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT); } else { // crazy cast so that we can overwrite the fade value // even though gcc enforces sets as const // (fade value doesn't affect sort so this is safe) Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin())))); if (light->dist < farthest_light->dist) { if (farthest_light->fade >= 0.f) { farthest_light->fade = -gFrameIntervalSeconds; } } else { break; // none of the other lights are closer } } } } } void LLPipeline::setupHWLights(LLDrawPool* pool) { const LLColor4 black(0,0,0,1); setLightingDetail(-1); // update // Ambient LLColor4 ambient = gSky.getTotalAmbientColor(); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV); // Light 0 = Sun or Moon (All objects) { mSunShadowFactor = 1.f; // no shadowing by defailt if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS) { mSunDir.setVec(gSky.getSunDirection()); mSunDiffuse.setVec(gSky.getSunDiffuseColor()); } else { mSunDir.setVec(gSky.getMoonDirection()); mSunDiffuse.setVec(gSky.getMoonDiffuseColor() * 1.5f); } F32 max_color = llmax(mSunDiffuse.mV[0], mSunDiffuse.mV[1], mSunDiffuse.mV[2]); if (max_color > 1.f) { mSunDiffuse *= 1.f/max_color; } mSunDiffuse.clamp(); LLVector4 light_pos(mSunDir, 0.0f); LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor; mHWLightColors[0] = light_diffuse; glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.mV); glLightfv(GL_LIGHT0, GL_AMBIENT, black.mV); glLightfv(GL_LIGHT0, GL_SPECULAR, black.mV); glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f); glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f); glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f); glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 0.0f); glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f); } // Light 1 = Backlight (for avatars) // (set by enableLightsAvatar) S32 cur_light = 2; // Nearby lights = LIGHT 2-7 mLightMovingMask = 0; if (mLightingDetail >= 1) { for (light_set_t::iterator iter = mNearbyLights.begin(); iter != mNearbyLights.end(); ++iter) { LLDrawable* drawable = iter->drawable; LLVOVolume* light = drawable->getVOVolume(); if (!light) { continue; } if (drawable->isState(LLDrawable::ACTIVE)) { mLightMovingMask |= (1<getLightColor(); light_color.mV[3] = 0.0f; F32 fade = iter->fade; if (fade < LIGHT_FADE_TIME) { // fade in/out light if (fade >= 0.f) { fade = fade / LIGHT_FADE_TIME; ((Light*) (&(*iter)))->fade += gFrameIntervalSeconds; } else { fade = 1.f + fade / LIGHT_FADE_TIME; ((Light*) (&(*iter)))->fade -= gFrameIntervalSeconds; } fade = llclamp(fade,0.f,1.f); light_color *= fade; } LLVector3 light_pos(light->getRenderPosition()); LLVector4 light_pos_gl(light_pos, 1.0f); F32 light_radius = llmax(light->getLightRadius(), 0.001f); F32 atten, quad; #if 0 //1.9.1 if (pool->getVertexShaderLevel() > 0) { atten = light_radius; quad = llmax(light->getLightFalloff(), 0.0001f); } else #endif { F32 x = (3.f * (1.f + light->getLightFalloff())); atten = x / (light_radius); // % of brightness at radius quad = 0.0f; } mHWLightColors[cur_light] = light_color; S32 gllight = GL_LIGHT0+cur_light; glLightfv(gllight, GL_POSITION, light_pos_gl.mV); glLightfv(gllight, GL_DIFFUSE, light_color.mV); glLightfv(gllight, GL_AMBIENT, black.mV); glLightfv(gllight, GL_SPECULAR, black.mV); glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); glLightf (gllight, GL_LINEAR_ATTENUATION, atten); glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad); glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); cur_light++; if (cur_light >= 8) { break; // safety } } } for ( ; cur_light < 8 ; cur_light++) { mHWLightColors[cur_light] = black; S32 gllight = GL_LIGHT0+cur_light; glLightfv(gllight, GL_DIFFUSE, black.mV); glLightfv(gllight, GL_AMBIENT, black.mV); glLightfv(gllight, GL_SPECULAR, black.mV); } // Init GL state glDisable(GL_LIGHTING); for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++) { glDisable(gllight); } mLightMask = 0; } void LLPipeline::enableLights(U32 mask, F32 shadow_factor) { if (mLightingDetail == 0) { mask &= 0xf003; // sun and backlight only (and fullbright bit) } if (mLightMask != mask) { if (!mLightMask) { glEnable(GL_LIGHTING); } if (mask) { for (S32 i=0; i<8; i++) { if (mask & (1<= 2) { mask |= mLightMovingMask; // Hardware moving lights glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default } else { mask |= 0xff & (~2); // Hardware local lights } enableLights(mask, shadow_factor); } void LLPipeline::enableLightsDynamic(F32 shadow_factor) { U32 mask = 0xff & (~2); // Local lights enableLights(mask, shadow_factor); if (mLightingDetail >= 2) { glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default } } void LLPipeline::enableLightsAvatar(F32 shadow_factor) { U32 mask = 0xff; // All lights setupAvatarLights(FALSE); enableLights(mask, shadow_factor); } void LLPipeline::enableLightsAvatarEdit(const LLColor4& color) { U32 mask = 0x2002; // Avatar backlight only, set ambient setupAvatarLights(TRUE); enableLights(mask, 1.0f); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); } void LLPipeline::enableLightsFullbright(const LLColor4& color) { U32 mask = 0x1000; // Non-0 mask, set ambient enableLights(mask, 1.f); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); if (mLightingDetail >= 2) { glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default } } void LLPipeline::disableLights() { enableLights(0, 0.f); // no lighting (full bright) glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default } // Call *after*s etting up lights void LLPipeline::setAmbient(const LLColor4& ambient) { mLightMask |= 0x4000; // tweak mask so that ambient will get reset LLColor4 amb = ambient + gSky.getTotalAmbientColor(); amb.clamp(); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb.mV); } //============================================================================ class LLMenuItemGL; class LLInvFVBridge; struct cat_folder_pair; class LLVOBranch; class LLVOLeaf; class Foo; template<> char* LLAGPArray::sTypeName = "U8 [AGP]"; template<> char* LLAGPArray::sTypeName = "U32 [AGP]"; template<> char* LLAGPArray::sTypeName = "F32 [AGP]"; template<> char* LLAGPArray::sTypeName = "LLColor4 [AGP]"; template<> char* LLAGPArray::sTypeName = "LLColor4U [AGP]"; template<> char* LLAGPArray::sTypeName = "LLVector4 [AGP]"; template<> char* LLAGPArray::sTypeName = "LLVector3 [AGP]"; template<> char* LLAGPArray::sTypeName = "LLVector2 [AGP]"; template<> char* LLAGPArray::sTypeName = "LLFace* [AGP]"; void scale_stamp(const F32 x, const F32 y, const F32 xs, const F32 ys) { stamp(0.25f + 0.5f*x, 0.5f + 0.45f*y, 0.5f*xs, 0.45f*ys); } void drawBars(const F32 begin, const F32 end, const F32 height = 1.f) { if (begin >= 0 && end <=1) { F32 lines = 40.0f; S32 ibegin = (S32)(begin * lines); S32 iend = (S32)(end * lines); F32 fbegin = begin * lines - ibegin; F32 fend = end * lines - iend; F32 line_height = height/lines; if (iend == ibegin) { scale_stamp(fbegin, (F32)ibegin/lines,fend-fbegin, line_height); } else { // Beginning row scale_stamp(fbegin, (F32)ibegin/lines, 1.0f-fbegin, line_height); // End row scale_stamp(0.0, (F32)iend/lines, fend, line_height); // Middle rows for (S32 l = (ibegin+1); l < iend; l++) { scale_stamp(0.0f, (F32)l/lines, 1.0f, line_height); } } } } void LLPipeline::displayAGP() { LLUI::setLineWidth(1.0); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); LLGLSPipelineAlpha gls_alpha; LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); LLImageGL::unbindTexture(0, GL_TEXTURE_2D); glScalef(2,2,1); glTranslatef(-0.5f,-0.5f,0); glColor4f(0,0,0,0.5f); scale_stamp(0,0,1,1); F32 x = 0.0f, y = 0.05f; F32 xs = 0.015f, ys = 0.015f; F32 xs2 = xs*0.1f, ys2 = ys * 0.1f; F32 w = xs+xs2, h = ys + ys2; S32 i=0; F32 agp_size = 1.f; if (mAGPMemPool) { agp_size = (F32)mAGPMemPool->getSize(); } x = (xs + xs2) * 4.0f; static float c = 0.0f; c += 0.0001f; LLAGPMemBlock *blockp = mBufferMemory[mBufferIndex]->getAGPMemBlock(); pool_set_t::iterator iter = mPools.begin(); LLDrawPool *poolp = *iter; // Dump the shared AGP buffer if (blockp) { F32 begin = blockp->getOffset()/agp_size; F32 end = begin + (blockp->getSize()/agp_size); F32 used = begin + (poolp->mMemory.count()/agp_size); LLViewerImage::bindTexture(NULL); glColor4f(0.5f, 0.5f, 0.5f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(NULL); glColor4f(0.5f, 0.5f, 0.5f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(NULL); glColor4f(0.5f, 0.5f, 0.5f, 0.5f); drawBars(begin,used, 0.5f); glColor4f(0.5f, 0.5f, 0.5f, 0.5f); drawBars(begin,end, 0.25f); } S32 used_bytes = 0; S32 total_bytes = 0; while (iter != mPools.end()) { poolp = *iter++; BOOL synced = FALSE; i++; total_bytes += poolp->mMemory.getMax(); used_bytes += poolp->mMemory.count(); LLViewerImage *texturep = poolp->getDebugTexture(); if (poolp->mMemory.mSynced) { poolp->mMemory.mSynced = FALSE; synced = TRUE; } LLAGPMemBlock *blockp = poolp->mMemory.getAGPMemBlock(); if (blockp) { F32 begin = blockp->getOffset()/agp_size; F32 end = begin + (blockp->getSize()/agp_size); F32 used = begin + (poolp->mMemory.count()/agp_size); LLViewerImage::bindTexture(NULL); glColor4f(1.f, 0.5f, 0.5f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(texturep); glColor4f(1.f, 0.75f, 0.75f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(NULL); glColor4f(1.f, 0.75f, 0.75f, 1.f); drawBars(begin,used, 0.5f); glColor3fv(poolp->getDebugColor().mV); drawBars(begin,end, 0.25f); if (synced) { LLViewerImage::bindTexture(NULL); glColor4f(1.f, 1.f, 1.f, 0.4f); drawBars(begin,end); } } synced = FALSE; if (poolp->mWeights.mSynced) { poolp->mWeights.mSynced = FALSE; synced = TRUE; } blockp = poolp->mWeights.getAGPMemBlock(); if (blockp) { F32 begin = blockp->getOffset()/agp_size; F32 end = begin + (blockp->getSize()/agp_size); F32 used = begin + (poolp->mWeights.count()*sizeof(float)/agp_size); LLViewerImage::bindTexture(NULL); glColor4f(0.0f, 0.f, 0.75f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(texturep); glColor4f(0.0, 0.f, 0.75f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(NULL); glColor4f(0.0, 0.f, 0.75f, 1.f); drawBars(begin,used, 0.5f); LLViewerImage::bindTexture(NULL); glColor3fv(poolp->getDebugColor().mV); drawBars(begin,end, 0.25f); if (synced) { LLViewerImage::bindTexture(NULL); glColor4f(1.f, 1.f, 1.f, 0.4f); drawBars(begin,end); } } synced = FALSE; if (poolp->mClothingWeights.mSynced) { poolp->mClothingWeights.mSynced = FALSE; synced = TRUE; } blockp = poolp->mClothingWeights.getAGPMemBlock(); if (blockp) { F32 begin = blockp->getOffset()/agp_size; F32 end = begin + (blockp->getSize()/agp_size); F32 used = begin + (poolp->mClothingWeights.count()*sizeof(LLVector4)/agp_size); LLViewerImage::bindTexture(NULL); glColor4f(0.75f, 0.f, 0.75f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(texturep); glColor4f(0.75f, 0.f, 0.75f, 0.5f); drawBars(begin,end); LLViewerImage::bindTexture(NULL); glColor4f(0.75f, 0.f, 0.75f, 0.5f); drawBars(begin,used, 0.5f); LLViewerImage::bindTexture(NULL); glColor3fv(poolp->getDebugColor().mV); drawBars(begin,end, 0.25f); if (synced) { LLViewerImage::bindTexture(NULL); glColor4f(1.f, 1.f, 1.f, 0.5f); drawBars(begin,end); } } // // Stamps on bottom of screen // LLViewerImage::bindTexture(texturep); glColor4f(1.f, 1.f, 1.f, 1.f); stamp(x,y,xs,ys); LLViewerImage::bindTexture(NULL); glColor3fv(poolp->getDebugColor().mV); stamp(x,y,xs, ys*0.25f); if (x+w > 0.95f) { x = (xs + xs2) * 4.0f; y += h; } else { x += w + xs2; } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } void LLPipeline::findReferences(LLDrawable *drawablep) { if (std::find(mVisibleList.begin(), mVisibleList.end(), drawablep) != mVisibleList.end()) { llinfos << "In mVisibleList" << llendl; } if (mLights.find(drawablep) != mLights.end()) { llinfos << "In mLights" << llendl; } if (mMovedList.find(drawablep) != mMovedList.end()) { llinfos << "In mMovedList" << llendl; } if (std::find(mShiftList.begin(), mShiftList.end(), drawablep) != mShiftList.end()) { llinfos << "In mShiftList" << llendl; } if (mRetexturedList.find(drawablep) != mRetexturedList.end()) { llinfos << "In mRetexturedList" << llendl; } if (mRematerialedList.find(drawablep) != mRematerialedList.end()) { llinfos << "In mRematerialedList" << llendl; } if (mActiveQ.find(drawablep) != mActiveQ.end()) { llinfos << "In mActiveQ" << llendl; } if (mBuildQ1.find(drawablep) != mBuildQ1.end()) { llinfos << "In mBuildQ2" << llendl; } if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end()) { llinfos << "In mBuildQ2" << llendl; } S32 count; /* count = mStaticTree->count(drawablep); if (count) { llinfos << "In mStaticTree: " << count << " references" << llendl; } count = mDynamicTree->count(drawablep); if (count) { llinfos << "In mStaticTree: " << count << " references" << llendl; } */ count = gObjectList.findReferences(drawablep); if (count) { llinfos << "In other drawables: " << count << " references" << llendl; } } BOOL LLPipeline::verify() { BOOL ok = TRUE; for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; if (!poolp->verify()) { ok = FALSE; } } if (!ok) { llwarns << "Pipeline verify failed!" << llendl; } return ok; } S32 LLPipeline::getAGPMemUsage() { if (mAGPMemPool) { return mAGPMemPool->getSize(); } else { return 0; } } S32 LLPipeline::getMemUsage(const BOOL print) { S32 mem_usage = 0; if (mAGPMemPool) { S32 agp_usage = 0; agp_usage = mAGPMemPool->getSize(); if (print) { llinfos << "AGP Mem: " << agp_usage << llendl; llinfos << "AGP Mem used: " << mAGPMemPool->getTotalAllocated() << llendl; } mem_usage += agp_usage; } S32 pool_usage = 0; for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) { LLDrawPool *poolp = *iter; pool_usage += poolp->getMemUsage(print); } if (print) { llinfos << "Pool Mem: " << pool_usage << llendl; } mem_usage += pool_usage; return mem_usage; } ////////////////////////////// // // Collision detection // // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * A method to compute a ray-AABB intersection. * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) * * Hence this version is faster as well as more robust than the original one. * * Should work provided: * 1) the integer representation of 0.0f is 0x00000000 * 2) the sign bit of the float is the most significant one * * Report bugs: p.terdiman@codercorner.com * * \param aabb [in] the axis-aligned bounding box * \param origin [in] ray origin * \param dir [in] ray direction * \param coord [out] impact coordinates * \return true if ray intersects AABB */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //#define RAYAABB_EPSILON 0.00001f #define IR(x) ((U32&)x) bool LLRayAABB(const LLVector3 ¢er, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon) { BOOL Inside = TRUE; LLVector3 MinB = center - size; LLVector3 MaxB = center + size; LLVector3 MaxT; MaxT.mV[VX]=MaxT.mV[VY]=MaxT.mV[VZ]=-1.0f; // Find candidate planes. for(U32 i=0;i<3;i++) { if(origin.mV[i] < MinB.mV[i]) { coord.mV[i] = MinB.mV[i]; Inside = FALSE; // Calculate T distances to candidate planes if(IR(dir.mV[i])) MaxT.mV[i] = (MinB.mV[i] - origin.mV[i]) / dir.mV[i]; } else if(origin.mV[i] > MaxB.mV[i]) { coord.mV[i] = MaxB.mV[i]; Inside = FALSE; // Calculate T distances to candidate planes if(IR(dir.mV[i])) MaxT.mV[i] = (MaxB.mV[i] - origin.mV[i]) / dir.mV[i]; } } // Ray origin inside bounding box if(Inside) { coord = origin; return true; } // Get largest of the maxT's for final choice of intersection U32 WhichPlane = 0; if(MaxT.mV[1] > MaxT.mV[WhichPlane]) WhichPlane = 1; if(MaxT.mV[2] > MaxT.mV[WhichPlane]) WhichPlane = 2; // Check final candidate actually inside box if(IR(MaxT.mV[WhichPlane])&0x80000000) return false; for(U32 i=0;i<3;i++) { if(i!=WhichPlane) { coord.mV[i] = origin.mV[i] + MaxT.mV[WhichPlane] * dir.mV[i]; if (epsilon > 0) { if(coord.mV[i] < MinB.mV[i] - epsilon || coord.mV[i] > MaxB.mV[i] + epsilon) return false; } else { if(coord.mV[i] < MinB.mV[i] || coord.mV[i] > MaxB.mV[i]) return false; } } } return true; // ray hits box } ////////////////////////////// // // Macros, functions, and inline methods from other classes // // void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light) { if (drawablep) { if (is_light) { mLights.insert(drawablep); drawablep->setState(LLDrawable::LIGHT); } else { mLights.erase(drawablep); drawablep->clearState(LLDrawable::LIGHT); } markRelight(drawablep); } } void LLPipeline::setActive(LLDrawable *drawablep, BOOL active) { if (active) { mActiveQ.insert(drawablep); } else { mActiveQ.erase(drawablep); } } //static void LLPipeline::toggleRenderType(void* data) { S32 type = (S32)(intptr_t)data; U32 bit = (1<= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB) { //this here is a texture glUniform1iARB(location, mActiveTextureChannels); llinfos << "Assigned to texture channel " << mActiveTextureChannels << llendl; return mActiveTextureChannels++; } return -1; } BOOL LLGLSLShader::mapUniforms(const char** uniform_names, S32 count) { BOOL res = TRUE; mActiveTextureChannels = 0; mUniform.clear(); mTexture.clear(); //initialize arrays mUniform.resize(count + LLPipeline::sReservedUniformCount, -1); mTexture.resize(count + LLPipeline::sReservedUniformCount, -1); bind(); //get the number of active uniforms GLint activeCount; glGetObjectParameterivARB(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount); for (S32 i = 0; i < activeCount; i++) { mapUniform(i, uniform_names, count); } unbind(); return res; } BOOL LLGLSLShader::link(BOOL suppress_errors) { return gPipeline.linkProgramObject(mProgramObject, suppress_errors); } void LLGLSLShader::bind() { glUseProgramObjectARB(mProgramObject); if (mAttribute.size() > 0) { gPipeline.mMaterialIndex = mAttribute[0]; } } void LLGLSLShader::unbind() { for (U32 i = 0; i < mAttribute.size(); ++i) { vertexAttrib4f(i, 0,0,0,1); } glUseProgramObjectARB(0); } S32 LLGLSLShader::enableTexture(S32 uniform, S32 mode) { if (uniform < 0 || uniform >= (S32)mTexture.size()) { llerrs << "LLGLSLShader::enableTexture: uniform out of range: " << uniform << llendl; } S32 index = mTexture[uniform]; if (index != -1) { glActiveTextureARB(GL_TEXTURE0_ARB+index); glEnable(mode); } return index; } S32 LLGLSLShader::disableTexture(S32 uniform, S32 mode) { S32 index = mTexture[uniform]; if (index != -1) { glActiveTextureARB(GL_TEXTURE0_ARB+index); glDisable(mode); } return index; } void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { if (mAttribute[index] > 0) { glVertexAttrib4fARB(mAttribute[index], x, y, z, w); } } void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v) { if (mAttribute[index] > 0) { glVertexAttrib4fvARB(mAttribute[index], v); } } LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision) { LLDrawable* drawable = mObjectPartition->pickDrawable(start, end, collision); return drawable ? drawable->getVObj() : NULL; }