//----------------------------------------------------------------------------- bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo>& fileVector, S32 depth) { if (isCachePath(path)) { PROFILE_START(dumpPath); const S32 len = dStrlen(path) + 1; char newpath[255]; strcpy(newpath, path); if(newpath[len - 2] == '/') newpath[len - 2] = '\0'; // cut off the trailing slash, if there is one bool ret = recurseDumpPathCache( newpath, fileVector, depth); PROFILE_END(); return ret; } PROFILE_START(dumpPath); bool ret = android_DumpPath( path, fileVector, depth); PROFILE_END(); return ret; }
//----------------------------------------------------------------------------- bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath) { if (isCachePath(path)) { PROFILE_START(dumpDirectories); ResourceManager->initExcludedDirectories(); const S32 len = dStrlen(path)+1; char newpath[len]; dSprintf(newpath, len, "%s", path); if(newpath[len - 1] == '/') newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one // Insert base path to follow what Windows does. if ( !noBasePath ) directoryVector.push_back(StringTable->insert(newpath)); bool ret = recurseDumpDirectoriesCache(newpath, "", directoryVector, depth, noBasePath); PROFILE_END(); return ret; } PROFILE_START(dumpDirectories); ResourceManager->initExcludedDirectories(); bool ret = android_DumpDirectories(path, "", directoryVector, depth, noBasePath); PROFILE_END(); return ret; }
void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, ShadowMap* shadow, bool filtered) { ENSURE(m->phase == Phase_Render); std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches; std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals; if (visiblePatches.empty() && visibleDecals.empty()) return; // render the solid black sides of the map first CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); techSolid->BeginPass(); CShaderProgramPtr shaderSolid = techSolid->GetShader(); shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(shaderSolid); PROFILE_END("render terrain sides"); techSolid->EndPass(); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, context, shadow); PROFILE_END("render terrain base"); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, context, shadow, false); PROFILE_END("render terrain blends"); PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, context, shadow, false); PROFILE_END("render terrain decals"); // restore OpenGL state g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(2, 0); g_Renderer.BindTexture(3, 0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); }
void SceneRenderState::renderObjects( SceneObject** objects, U32 numObjects ) { // Let the objects batch their stuff. PROFILE_START( SceneRenderState_prepRenderImages ); for( U32 i = 0; i < numObjects; ++ i ) { SceneObject* object = objects[ i ]; object->prepRenderImage( this ); } #ifdef TORQUE_EXPERIMENTAL_EC U32 interfaceCount = RenderComponentInterface::all.size(); for (U32 i = 0; i < RenderComponentInterface::all.size(); i++) { Component* comp = dynamic_cast<Component*>(RenderComponentInterface::all[i]); if (comp->isClientObject() && comp->isActive()) { RenderComponentInterface::all[i]->prepRenderImage(this); } } #endif PROFILE_END(); // Render what the objects have batched. getRenderPass()->renderPass( this ); }
void SortModelRenderer::PrepareModels() { CMatrix3D worldToCam; if (m->models.size() == 0) return; g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam); for(std::vector<SModel*>::iterator it = m->models.begin(); it != m->models.end(); ++it) { SModel* smdl = *it; CModel* model = smdl->GetModel(); ENSURE(model->GetRenderData() == smdl); m->vertexRenderer->UpdateModelData(model, smdl->m_Data, smdl->m_UpdateFlags); smdl->m_UpdateFlags = 0; CVector3D modelpos = model->GetTransform().GetTranslation(); modelpos = worldToCam.Transform(modelpos); smdl->m_Distance = modelpos.Z; } PROFILE_START( "sorting transparent" ); std::sort(m->models.begin(), m->models.end(), SortModelsByDist()); PROFILE_END( "sorting transparent" ); }
// This function takes a lot of CPU, so make sure it's not called often! Don't call this function directly, use UpdateEnemy instead whenever possible. CBaseEntity* AvHTurret::FindBestEnemy() { PROFILE_START() CBaseEntity* theEntityList[100]; int theMaxRange = this->GetXYRange(); Vector delta = Vector(theMaxRange, theMaxRange, theMaxRange); CBaseEntity* theCurrentEntity = NULL; CBaseEntity* theBestPlayer = NULL; CBaseEntity* theBestStructure = NULL; float theCurrentEntityRange = 100000; // Find only monsters/clients in box, NOT limited to PVS int theCount = UTIL_EntitiesInBox(theEntityList, 100, this->pev->origin - delta, this->pev->origin + delta, FL_CLIENT | FL_MONSTER); for(int i = 0; i < theCount; i++ ) { theCurrentEntity = theEntityList[i]; if((theCurrentEntity != this) && theCurrentEntity->IsAlive()) { // the looker will want to consider this entity // don't check anything else about an entity that can't be seen, or an entity that you don't care about. if(this->IRelationship(theCurrentEntity ) != R_NO && FInViewCone(theCurrentEntity) && !FBitSet(theCurrentEntity->pev->flags, FL_NOTARGET)) { AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(theCurrentEntity); if(!thePlayer || thePlayer->GetCanBeAffectedByEnemies()) { if(this->GetIsValidTarget(theCurrentEntity)) { // Find nearest enemy float theRangeToTarget = VectorDistance2D(this->pev->origin, theCurrentEntity->pev->origin); if(theRangeToTarget < theCurrentEntityRange) { // FVisible is expensive, so defer until necessary if(!this->GetRequiresLOS() || FVisible(theCurrentEntity)) { theCurrentEntityRange = theRangeToTarget; if ( thePlayer ) { theBestPlayer = theCurrentEntity; } else { theBestStructure = theCurrentEntity; } } } } } } } } PROFILE_END(kAvHTurretFindBestEnemy); return (theBestPlayer != NULL ) ? theBestPlayer : theBestStructure; }
void SceneObject::setRenderTransform(const MatrixF& mat) { PROFILE_START(SceneObj_setRenderTransform); mRenderObjToWorld = mRenderWorldToObj = mat; mRenderWorldToObj.affineInverse(); AssertFatal(mObjBox.isValidBox(), "Bad object box!"); resetRenderWorldBox(); PROFILE_END(); }
void AtlasClipMapBatcher::sort() { PROFILE_START(AtlasClipMapBatcher_sort); // Sort our elements. The other lists are already good to go, and there's // no benefit to drawing them in a specific order as this pass (the first // pass) will already set up the Z buffer. for(S32 i=1; i<4; i++) dQsort(mRenderList[i].address(),mRenderList[i].size(),sizeof(RenderNote*),cmpRenderNote); PROFILE_END(); }
bool WindEmitter::findBest( const Point3F& cameraPos, const VectorF& cameraDir, F32 viewDistance, U32 maxResults, WindEmitterList* results ) { PROFILE_START(WindEmitter_findBest); // Build a sphere from the camera point. SphereF cameraSphere; cameraSphere.center = cameraPos; cameraSphere.radius = viewDistance; // Collect the active spheres within the camera space and score them. WindEmitterList best; WindEmitterList::iterator iter = smAllEmitters.begin(); for ( ; iter != smAllEmitters.end(); iter++ ) { const SphereF& sphere = *(*iter); // Skip any spheres outside of our camera range or that are disabled. if ( !(*iter)->mEnabled || !cameraSphere.isIntersecting( sphere ) ) continue; // Simple score calculation... // // score = ( radius / distance to camera ) * dot( cameraDir, vector from camera to sphere ) // Point3F vect = sphere.center - cameraSphere.center; F32 dist = vect.len(); (*iter)->mScore = dist * sphere.radius; vect /= getMax( dist, 0.001f ); (*iter)->mScore *= mDot( vect, cameraDir ); best.push_back( *iter ); } // Sort the results by score! dQsort( best.address(), best.size(), sizeof(WindEmitter*), &WindEmitter::_sortByScore ); // Clip the results to the max requested. if ( best.size() > maxResults ) best.setSize( maxResults ); // Merge the results and return. results->merge( best ); PROFILE_END(); // WindEmitter_findBest return best.size() > 0; }
void AtlasClipMapBatcher::renderClipMap( SceneGraphData& sgData, BaseMatInstance* overrideMat ) { PROFILE_START(AtlasClipMapBatcher_renderClipMap); for(S32 curBin=1; curBin<4; curBin++) { // If bin is empty, skip. if(mRenderList[curBin].size() == 0) continue; for(S32 i=0; i<mRenderList[curBin].size(); i++) { // Grab the render note. const RenderNote *rn = mRenderList[curBin][i]; // Set up clipmap levels. if( !mFixedFunction ) { BaseMatInstance* material = overrideMat; if( !material ) switch( rn->levelCount ) { case 2: material = mClipMap->getMaterialAndTextures( rn->levelEnd, rn->levelStart, -1, -1, false ); break; case 3: material = mClipMap->getMaterialAndTextures( rn->levelEnd, rn->levelStart + 1, rn->levelStart, -1, false ); break; case 4: material = mClipMap->getMaterialAndTextures( rn->levelEnd, rn->levelStart + 2, rn->levelStart + 1, rn->levelStart, false ); break; default: material = MaterialManager::get()->getWarningMatInstance(); } while( material->setupPass( mState, sgData ) ) rn->chunk->render(); } else { Point4F clipmapMapping; for( U32 curLayer = rn->levelEnd; curLayer >= rn->levelStart; -- curLayer ) { BaseMatInstance* material = overrideMat; if( !material ) material = mClipMap->bindTexturesFF( curLayer, clipmapMapping, curLayer == rn->levelEnd, false ); while( material->setupPass( mState, sgData ) ) rn->chunk->render(); } } } } PROFILE_END(); }
// Updated transforms void PolygonSortModelRenderer::UpdateModelData(CModel* model, void* data, int updateflags) { PSModel* psmdl = (PSModel*)data; if (updateflags & (RENDERDATA_UPDATE_VERTICES|RENDERDATA_UPDATE_COLOR)) { CModelDefPtr mdef = model->GetModelDef(); size_t numVertices = mdef->GetNumVertices(); // build vertices // allocate working space for computing normals if (numVertices > m->normalsNumVertices) { rtl_FreeAligned(m->normals); size_t newSize = round_up_to_pow2(numVertices); m->normals = (char*)rtl_AllocateAligned(newSize*16, 16); m->normalsNumVertices = newSize; } VertexArrayIterator<CVector3D> Position = psmdl->m_Position.GetIterator<CVector3D>(); VertexArrayIterator<CVector3D> Normal = VertexArrayIterator<CVector3D>(m->normals, 16); ModelRenderer::BuildPositionAndNormals(model, Position, Normal); VertexArrayIterator<SColor4ub> Color = psmdl->m_Color.GetIterator<SColor4ub>(); ModelRenderer::BuildColor4ub(model, Normal, Color); // upload everything to vertex buffer psmdl->m_Array.Upload(); } // resort model indices from back to front, according to the view camera position - and store // the returned sqrd distance to the centre of the nearest triangle // Use the view camera instead of the cull camera because: // a) polygon sorting implicitly uses the view camera (and changing that would be costly) // b) using the cull camera is likely not interesting from a debugging POV PROFILE_START( "sorting transparent" ); CMatrix3D worldToCam; g_Renderer.GetViewCamera().m_Orientation.GetInverse(worldToCam); psmdl->BackToFrontIndexSort(worldToCam); PROFILE_END( "sorting transparent" ); }
void GFXD3D9QueryFence::issue() { PROFILE_START( GFXD3D9QueryFence_issue ); // Create the query if we need to if( mQuery == NULL ) { HRESULT hRes = static_cast<GFXD3D9Device *>( mDevice )->getDevice()->CreateQuery( D3DQUERYTYPE_EVENT, &mQuery ); AssertFatal( hRes != D3DERR_NOTAVAILABLE, "Hardware does not support D3D9 Queries, this should be caught before this fence type is created" ); AssertISV( hRes != E_OUTOFMEMORY, "Out of memory" ); } // Issue the query mQuery->Issue( D3DISSUE_END ); PROFILE_END(); }
ReturnValue GraphicsService::Render() { PostEffect::Begin(); Render::SetOrthoMode(); Render::EnableOrthoMode(); Render::EnableAlphaBlend(); GL_CHECK( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ); PROFILE_BEGIN("Batcher"); Batcher::FlushAll(); PROFILE_END(); PostEffect::End(); Debug::OpenGL::FlushErrors("GraphicsService::Render()"); return RETURN_VALUE_OK; }
CDbDataReader CDbCommand::_queryInternal(const CCommandParameterMap & params) throw (CDbException) { mergeParametersWith(params); string parametersDump = _makeParametersDump(_params); #ifdef CWS_DEBUG Cws::trace("Executing SQL: " + _text + parametersDump, "system.db.CDbCommand"); #endif try { SAConnection * connection = _connection->getConnection(); _saCommand = new SACommand(connection, _text.c_str(), SA_CmdSQLStmt); if (connection->Client() == SA_MySQL_Client) { _saCommand->setOption("UseStatement") = "TRUE"; } for (CCommandParameterMap::const_iterator iter = _params.begin(); iter != _params.end(); ++iter) { iter->second.get()->bind(_saCommand); } PROFILE_BEGIN("Executing SQL: " + _text + parametersDump); _saCommand->Execute(); PROFILE_END(); return CDbDataReader(_saCommand); } catch (const SAException & e) { string message = e.ErrText().GetMultiByteChars(); stringstream ss; ss << "CDbCommand::_queryInternal() failed: " << message << ". Code: " << (int)e.ErrNativeCode(); Cws::log( "CDbCommand::_queryInternal() failed: " + ss.str() + ". The SQL statement executed was: " + _text + "." ); #ifdef CWS_DEBUG message += ". The SQL statement executed was: " + _text + parametersDump; #endif throw CDbException( "CDbCommand failed to execute the SQL statement: " + message, (int)e.ErrNativeCode() ); } }
void AtlasClipMapBatcher::init(ClipMap *acm, SceneState *state, AtlasInstance *renderer) { PROFILE_START(AtlasClipMapBatcher_init); // Note our render state. mClipMap = acm; mState = state; mObject = renderer; // Empty the render lists... for(S32 i=1; i<4; i++) mRenderList[i].clear(); mDetailList.clear(); mLightList.clear(); mFogList.clear(); // And clear the render notes. mRenderNoteAlloc.freeBlocks(); PROFILE_END(); }
void ProcessList::advanceObjects() { PROFILE_START(ProcessList_AdvanceObjects); // A little link list shuffling is done here to avoid problems // with objects being deleted from within the process method. ProcessObject list; list.plLinkBefore(mHead.mProcessLink.next); mHead.plUnlink(); for (ProcessObject * pobj = list.mProcessLink.next; pobj != &list; pobj = list.mProcessLink.next) { pobj->plUnlink(); pobj->plLinkBefore(&mHead); onTickObject(pobj); } mTotalTicks++; PROFILE_END(); }
void vertexBufferCopy(vertexType vtype) { PROFILE_SCOPE(Terrain_vbufferCopy); // Do vertexes if (vtype == vertexTypeFullClipMapping) { GVertexBufferHandle<GAtlasVert2> v(GRAPHIC, mCurVertex, GBufferTypeVolatile); PROFILE_START(Terrain_bufferCopy_lockV); v.lock(); PROFILE_END(); dMemcpy(&v[0], &mVertexStore[0], sizeof(GAtlasVert2) * mCurVertex); PROFILE_START(Terrain_bufferCopy_unlockV); v.unlock(); PROFILE_END(); GRAPHIC->setVertexBuffer(v); } else if (vtype == vertexTypeDLight) { GVertexBufferHandle<GVertexPCNTT> vPCNTT(GRAPHIC, mCurVertex, GBufferTypeVolatile); PROFILE_START(Terrain_bufferCopy_lockVPCNTT); vPCNTT.lock(); PROFILE_END(); dMemcpy(&vPCNTT[0], &mVertexStorePCNTT[0], sizeof(GVertexPCNTT) * mCurVertex); PROFILE_START(Terrain_bufferCopy_unlockVPCNTT); vPCNTT.unlock(); PROFILE_END(); GRAPHIC->setVertexBuffer(vPCNTT); } else { GVertexBufferHandle<GVertexPCNT> vPCNT(GRAPHIC, mCurVertex, GBufferTypeVolatile); PROFILE_START(Terrain_bufferCopy_lockVPCNT); vPCNT.lock(); PROFILE_END(); dMemcpy(&vPCNT[0], &mVertexStorePCNT[0], sizeof(GVertexPCNT) * mCurVertex); PROFILE_START(Terrain_bufferCopy_unlockVPCNT); vPCNT.unlock(); PROFILE_END(); GRAPHIC->setVertexBuffer(vPCNT); } }
bool ProcessList::advanceTime(SimTime timeDelta) { PROFILE_START(ProcessList_AdvanceTime); // some drivers change the FPU control state, which will break our control object simulation // (leading to packet mismatch errors due to small FP differences). So set it to the known // state before advancing. // TODO: revisit this. //U32 mathState = Platform::getMathControlState(); //Platform::setMathControlStateKnown(); if (mDirty) orderList(); SimTime targetTime = mLastTime + timeDelta; SimTime targetTick = targetTime - (targetTime % TickMs); SimTime tickDelta = targetTick - mLastTick; bool tickPass = mLastTick != targetTick; if ( tickPass ) mPreTick.trigger(); // Advance all the objects. for (; mLastTick != targetTick; mLastTick += TickMs) onAdvanceObjects(); mLastTime = targetTime; mLastDelta = ((TickMs - ((targetTime+1) % TickMs)) % TickMs) / F32(TickMs); if ( tickPass ) mPostTick.trigger( tickDelta ); // restore math control state in case others are relying on it being a certain value // TODO: revisit this. //Platform::setMathControlState(mathState); PROFILE_END(); return tickPass; }
//----------------------------------------------------------------------------- U32 convertUTF16toUTF8( const UTF16 *unistring, UTF8 *outbuffer, U32 len) { AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); PROFILE_START(convertUTF16toUTF8); U32 walked, nCodeunits, codeunitLen; UTF32 middleman; nCodeunits=0; while( *unistring != '\0' && nCodeunits + 3 < len ) { walked = 1; middleman = oneUTF16toUTF32(unistring,&walked); codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]); unistring += walked; nCodeunits += codeunitLen; } nCodeunits = getMin(nCodeunits,len - 1); outbuffer[nCodeunits] = '\0'; PROFILE_END(); return nCodeunits; }
long unsigned int CDbCommand::execute(const CCommandParameterMap & params) throw (CDbException) { mergeParametersWith(params); string parametersDump = _makeParametersDump(_params); #ifdef CWS_DEBUG Cws::trace("Executing SQL: " + _text + parametersDump, "system.db.CDbCommand"); #endif try { SAConnection * connection = _connection->getConnection(); _saCommand = new SACommand(connection); _saCommand->setCommandText(_text.c_str()); for (CCommandParameterMap::const_iterator iter = _params.begin(); iter != _params.end(); ++iter) { iter->second.get()->bind(_saCommand); } PROFILE_BEGIN("Executing SQL: " + _text + parametersDump); _saCommand->Execute(); connection->Commit(); PROFILE_END(); return _saCommand->RowsAffected(); } catch (const SAException & e) { string message = e.ErrText().GetMultiByteChars(); Cws::log( "CDbCommand::execute() failed: " + message + ". The SQL statement executed was: " + _text + "." ); #ifdef CWS_DEBUG message += ". The SQL statement executed was: " + _text + parametersDump; #endif throw CDbException( "CDbCommand failed to execute the SQL statement: " + message, (int)e.ErrNativeCode() ); } return 0; }
// Process a time event and update all sub-processes void processTimeEvent(S32 elapsedTime) { PROFILE_START(ProcessTimeEvent); // If recording a video and not playinb back a journal, override the elapsedTime if (VIDCAP->isRecording() && !Journal::IsPlaying()) elapsedTime = VIDCAP->getMsPerFrame(); // cap the elapsed time to one second // if it's more than that we're probably in a bad catch-up situation if(elapsedTime > 1024) elapsedTime = 1024; U32 timeDelta; if(ATTS(gTimeAdvance)) timeDelta = ATTS(gTimeAdvance); else timeDelta = (U32) (elapsedTime * ATTS(gTimeScale)); Platform::advanceTime(elapsedTime); // Don't build up more time than a single tick... this makes the sim // frame rate dependent but is a useful hack for singleplayer. if ( ATTS(gFrameSkip) ) if ( timeDelta > TickMs ) timeDelta = TickMs; bool tickPass; PROFILE_START(ServerProcess); tickPass = serverProcess(timeDelta); PROFILE_END(); PROFILE_START(ServerNetProcess); // only send packets if a tick happened if(tickPass) GNet->processServer(); // Used to indicate if server was just ticked. Con::setBoolVariable( "$pref::hasServerTicked", tickPass ); PROFILE_END(); PROFILE_START(SimAdvanceTime); Sim::advanceTime(timeDelta); PROFILE_END(); PROFILE_START(ClientProcess); tickPass = clientProcess(timeDelta); // Used to indicate if client was just ticked. Con::setBoolVariable( "$pref::hasClientTicked", tickPass ); PROFILE_END_NAMED(ClientProcess); PROFILE_START(ClientNetProcess); if(tickPass) GNet->processClient(); PROFILE_END(); GNet->checkTimeouts(); gFPS.update(); // Give the texture manager a chance to cleanup any // textures that haven't been referenced for a bit. if( GFX ) TEXMGR->cleanupCache( 5 ); PROFILE_END(); // Update the console time Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000); }
void ClipMap::initClipStack() { PROFILE_START(ClipMap_initClipStack); // Clear out all the levels. while(mLevels.size()) { mLevels.last().mDebugTex = NULL; mLevels.last().mTex = NULL; mLevels.pop_back(); } // What texture profile are we going to be using? AssertFatal(mImageCache, "ClipMap::initClipStack - must have image cache by this point."); GTextureProfile *texProfile = mImageCache->isRenderToTargetCache() ? &ClipMapTextureRTProfile : &ClipMapTextureProfile; // Figure out how many clipstack textures we'll have. mClipStackDepth = getBinLog2(mTextureSize) - getBinLog2(mClipMapSize) + 1; mLevels.setSize(mClipStackDepth); // Print a little report on our allocation. Con::printf("Allocating a %d px clipmap for a %dpx source texture.", mClipMapSize, mTextureSize); Con::printf(" - %d base clipstack entries, + 1 cap.", mClipStackDepth - 1); U32 baseTexSize = (mClipMapSize * mClipMapSize * 4); Con::printf(" - Using approximately %fMB of texture memory.", (F32(baseTexSize * mClipStackDepth) * 1.33) / (1024.0*1024.0)); // First do our base textures - they are not mipped. // We rely on auto-mipmapping, but if the device/card doesn't support it, we should just not ask for it. U32 numMips = GRAPHIC->getCardProfiler()->queryProfile("autoMipMapLevel", true) ? 0 : 1; for(S32 i=0; i<mClipStackDepth; i++) { mLevels[i].mScale = (F32)BIT(mClipStackDepth - (1 + i)); mLevels[i].mTex.set(mClipMapSize, mClipMapSize, GFormatR8G8B8X8, texProfile, avar("%s() - mLevels[%d].mTex (line %d)", __FUNCTION__, i, __LINE__), numMips); } // Some stuff can get skipped once we're set up. if(mTexCallbackHandle != -1) return; // Don't forget to allocate our debug textures... for(S32 i=0; i<mClipStackDepth; i++) mLevels[i].initDebugTexture(i); GAtlasVert2* vert = NULL; if (GRAPHIC->getPixelShaderVersion() > 0) { // Do shader lookup for 2,3,4 level shaders. for(S32 i=2; i<5; i++) { // Init materials const String matname = String::ToString("AtlasMaterial%d", i); const U32 arrayOffset = i-1; mClipmapMat[arrayOffset] = MaterialManager::get()->createMatInstance(matname, (GVertexFlags)getGVertFlags(vert)); if (!mClipmapMat[arrayOffset]) { Con::errorf("Could not find material: %s", matname.c_str()); continue; } else { if (mMapInfoConst.getElementSize() == 0) mMapInfoConst.setCapacity(4, mClipmapMat[arrayOffset]->getMaterialParameters()->getAlignmentValue(GSCT_Float4)); } BaseMatInstance* matParams = mClipmapMat[arrayOffset]; mMorphTSC[arrayOffset] = matParams->getMaterialParameterHandle("$morphT"); mMapInfoTC[arrayOffset] = matParams->getMaterialParameterHandle("$mapInfo"); mDiffuseMap0TC[arrayOffset] = matParams->getMaterialParameterHandle("$diffuseMap0"); mDiffuseMap1TC[arrayOffset] = matParams->getMaterialParameterHandle("$diffuseMap1"); mDiffuseMap2TC[arrayOffset] = matParams->getMaterialParameterHandle("$diffuseMap2"); mDiffuseMap3TC[arrayOffset] = matParams->getMaterialParameterHandle("$diffuseMap3"); } } else { mClipmapMatBasePassFF = MaterialManager::get()->createMatInstance("AtlasMaterialFFBasePass", (GVertexFlags)getGVertFlags(vert)); mClipmapMatAddPassFF = MaterialManager::get()->createMatInstance("AtlasMaterialFFAddPass", (GVertexFlags)getGVertFlags(vert)); } // Grab a callback from the texture manager to deal with zombification. GRAPHIC->getTextureManager()->registerTexCallback(texCB, this, mTexCallbackHandle); // Ok, everything is ready to go. PROFILE_END(); }
void ClipMap::calculateClipMapLevels(const F32 near, const F32 far, const RectF &texBounds, S32 &outStartLevel, S32 &outEndLevel) { PROFILE_START(ClipMap_calculateClipMapLevels); // We also have to deal w/ the available data. So let's figure out if our // desired TCs are in the loaded textureset. // Adjust the specified TC range into a texel range. F32 ftexsize = F32(mTextureSize); RectF tcR(Point2F(texBounds.point.y * ftexsize, texBounds.point.x * ftexsize), ftexsize * texBounds.extent); // If we're tiling, make sure we're only insetting away from the clipmap bounds. // This avoids making bad LOD selections at clipmap boundaries. // Note: compress several compares into one since a*b=0 iff a==0 or b==0 bool doInset = true;//mTile || (tcR.point.x * tcR.point.y * (tcR.extent.x+tcR.point.x-mTextureSize) * (tcR.extent.y+tcR.point.y-mTextureSize) != 0); if(doInset) tcR.inset(-1, -1); // Put some safe defaults in for starters. outEndLevel = mClipStackDepth-1; outStartLevel = getMax(outEndLevel-3, S32(0)); // Now iterate over every clipstack entry and find the smallest that contains // the relevant TCs. S32 minLevelOverlap = mClipStackDepth - 1; S32 maxLevelOverlap = mClipStackDepth - 1; for(S32 i=mClipStackDepth-2; i>=0; i--) { // Find region for entry at this level. RectF r; F32 biti = F32(BIT(i)); F32 biticms = F32(BIT(i) * mClipMapSize); r.point = Point2F( biti * mLevels[i].mToroidalOffset.x, biti * mLevels[i].mToroidalOffset.y); r.extent.set(biticms,biticms); // Is our tex region fully contained? if(r.contains(tcR)) { // If we're fully contained, then this is our new max. maxLevelOverlap = i; minLevelOverlap = i; continue; } // Or else maybe we've got overlap? if (!r.overlaps(tcR)) break; // If we're overlapping then this is our new min... minLevelOverlap = getMin(minLevelOverlap, i); } // Given our level range, do a best fit. We ALWAYS have to have // enough for the minimum detail, so we fit that constraint then // do our best to give additional detail on top of that. // bias the minimum detail to allow smooth transitions to work properly, // this avoids a LOT of texture popping. maxLevelOverlap++; outEndLevel = mClamp(maxLevelOverlap, 0, mClipStackDepth-1); outStartLevel = mClamp(minLevelOverlap, outEndLevel - 3, outEndLevel - 1); // Make sure we're not exceeding our max delta. const S32 delta = outEndLevel - outStartLevel; AssertFatal(delta >= 1 && delta <= 4, "ClipMap::calculateClipMapLevels - range in levels outside of 2..4 range!"); PROFILE_END(); }
bool QuadTreeTracer::castRay(const Point3F &start, const Point3F &end, RayInfo *info) { PROFILE_START(QuadTreeTracer_castRay); // Do some precalculations we'll use for the rest of this routine. // Set up our intercept calculation methods. F32 invDeltaX; if(end.x == start.x) { calcInterceptX = calcInterceptNone; invDeltaX = 0; } else { invDeltaX = 1.f / (end.x - start.x); calcInterceptX = calcInterceptV; } F32 invDeltaY; if(end.y == start.y) { calcInterceptY = calcInterceptNone; invDeltaY = 0; } else { invDeltaY = 1.f / (end.y - start.y); calcInterceptY = calcInterceptV; } // Subdivide our space based on the size of the lowest level of the tree... const F32 invSize = 1.f / F32(BIT(mTreeDepth-1)); // Grab this off the frame allocator, we don't want to do a proper alloc // on every ray! FrameAllocatorMarker stackAlloc; RayStackNode *stack = (RayStackNode*)stackAlloc.alloc(sizeof(RayStackNode) * (mTreeDepth * 3 + 1)); U32 stackSize = 1; // Kick off the stack with the root node. stack[0].startT = 0; stack[0].endT = 1; stack[0].squarePos.set(0,0); stack[0].level = mTreeDepth - 1; //Con::printf("QuadTreeTracer::castRay(%x)", this); // Aright, now let's do some raycasting! while(stackSize--) { // Get the current node for easy access... RayStackNode *sn = stack + stackSize; const U32 level = sn->level; const F32 startT = sn->startT; const F32 endT = sn->endT; const Point2I squarePos = sn->squarePos; AssertFatal((startT >= 0.f) && (startT <= 1.f), "QuadTreeTracer::castRay - out of range startT on stack!"); AssertFatal((endT >= 0.f) && (endT <= 1.f), "QuadTreeTracer::castRay - out of range endT on stack!"); //Con::printf(" -- node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); // Figure our start and end Z. const F32 startZ = startT * (end.z - start.z) + start.z; const F32 endZ = endT * (end.z - start.z) + start.z; // Ok, now let's see if we hit the lower bound const F32 squareMin = getSquareMin(level, squarePos); if(startZ < squareMin && endZ < squareMin) continue; //Nope, skip out. // Hmm, let's check the upper bound. const F32 squareMax = getSquareMax(level, squarePos); if(startZ > squareMax && endZ > squareMax) continue; //Nope, skip out. // We might be intersecting something... If we've hit // the tree depth let's deal with the leaf intersection. if(level == 0) { //Con::printf(" ++ check node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); if(castLeafRay(squarePos, start, end, startT, endT, info)) { PROFILE_END(); return true; // We hit, tell 'em so! } continue; // Otherwise, keep looking. } else { // Ok, we have to push our children as we're an inner node. // First, figure out some widths... U32 subSqSize = BIT(level - 1); // Now, calculate intercepts so we know how to deal with this // situation... (intercept = position, int = t value for that pos) const F32 xIntercept = (squarePos.x + subSqSize) * invSize; F32 xInt = calcInterceptX(start.x, invDeltaX, xIntercept); const F32 yIntercept = (squarePos.y + subSqSize) * invSize; F32 yInt = calcInterceptY(start.y, invDeltaY, yIntercept); // Our starting position for this subray... const F32 startX = startT * (end.x - start.x) + start.x; const F32 startY = startT * (end.y - start.y) + start.y; // Deal with squares that might be "behind" the ray. if(xInt < startT) xInt = F32_MAX; if(yInt < startT) yInt = F32_MAX; // Do a little magic to calculate our next checks... const U32 x0 = (startX > xIntercept) * subSqSize; const U32 y0 = (startY > yIntercept) * subSqSize; const U32 x1 = subSqSize - x0; const U32 y1 = subSqSize - y0; const U32 nextLevel = level - 1; // Ok, now let's figure out what nodes, in what order, need to go // on the stack. We push things on in reverse order of processing. if(xInt > endT && yInt > endT) { stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize].level = nextLevel; stackSize++; } else if(xInt < yInt) { F32 nextIntersect = endT; if(yInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = yInt; stackSize++; } // Do middle two, order doesn't matter. stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y0); stack[stackSize].startT = xInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else if(yInt < xInt) { F32 nextIntersect = endT; if(xInt <= endT) { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; nextIntersect = xInt; stackSize++; } stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y1); stack[stackSize].startT = yInt; stack[stackSize].endT = nextIntersect; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = yInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } else { stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); stack[stackSize].startT = xInt; stack[stackSize].endT = endT; stack[stackSize].level = nextLevel; stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); stack[stackSize+1].startT = startT; stack[stackSize+1].endT = xInt; stack[stackSize+1].level = nextLevel; stackSize += 2; } } } // Nothing found, so give up. PROFILE_END(); return false; }
void ClipMap::recenter(Point2F center) { bool wantCompleteRefill = false; if(mNeedRefill || mForceClipmapPurge || Con::getBoolVariable("$forceFullClipmapPurgeEveryFrame", false)) wantCompleteRefill = true; PROFILE_START(ClipMap_recenter); // Reset our budget. mMaxTexelUploadPerRecenter = mClipMapSize * mClipMapSize * 2; AssertFatal(isPow2(mClipMapSize), "ClipMap::recenter - require pow2 clipmap size!"); // Clamp the center to the unit square. /* if(!mTile) { center.x = mClampF(center.x, 0.f, 1.f); center.y = mClampF(center.y, 0.f, 1.f); } */ // Ok, we're going to do toroidal updates on each entry of the clipstack // (except for the cap, which covers the whole texture), based on this // new center point. if( !wantCompleteRefill ) { // Calculate the new texel at most detailed level. Point2F texelCenterF = center * F32(mClipMapSize) * mLevels[0].mScale; Point2I texelCenter((S32)mFloor(texelCenterF.y), (S32)mFloor(texelCenterF.x)); // Update interest region. mImageCache->setInterestCenter(texelCenter); } // Note how many we were at so we can cut off at the right time. S32 lastTexelsUpdated = mTexelsUpdated; // For each texture... for(S32 i=mClipStackDepth-2; i>=0; i--) { ClipStackEntry &cse = mLevels[i]; // Calculate new center point for this texture. Point2F texelCenterF = center * F32(mClipMapSize) * cse.mScale; const S32 texelMin = mClipMapSize/2; //const S32 texelMax = S32(F32(mClipMapSize) * cse.mScale) - texelMin; Point2I texelTopLeft; //if(mTile) //{ texelTopLeft.x = S32(mFloor(texelCenterF.y)) - texelMin; texelTopLeft.y = S32(mFloor(texelCenterF.x)) - texelMin; //} //else //{ // texelTopLeft.x = mClamp(S32(mFloor(texelCenterF.y)), texelMin, texelMax) - texelMin; // texelTopLeft.y = mClamp(S32(mFloor(texelCenterF.x)), texelMin, texelMax) - texelMin; //} // Also, prevent very small updates - the RT changes are costly. Point2I d = cse.mToroidalOffset - texelTopLeft; if(mAbs(d.x) <= 2 && mAbs(d.y) <= 2) { // Update the center; otherwise we get some weird conditions around // edges of the clipmap space. cse.mClipCenter = center; continue; } // This + current toroid offset tells us what regions have to be blasted. RectI oldData(cse.mToroidalOffset, Point2I(mClipMapSize, mClipMapSize)); RectI newData(texelTopLeft, Point2I(mClipMapSize, mClipMapSize)); // Update clipstack level. cse.mClipCenter = center; cse.mToroidalOffset = texelTopLeft; // If we're refilling, that's all we want; continue with next level. if( wantCompleteRefill ) continue; // Make sure we have available data... if(!mImageCache->isDataAvailable(getMipLevel(cse.mScale), newData)) continue; // Alright, determine the set of data we actually need to upload. S32 rectCount = 0; RectI buffer[8]; calculateModuloDeltaBounds(oldData, newData, buffer, &rectCount); AssertFatal(rectCount < 8, "ClipMap::recenter - got too many rects back!"); /*if(rectCount) Con::printf(" issuing %d updates to clipmap level %d (offset=%dx%d)", rectCount, i, texelTopLeft.x, texelTopLeft.y); */ if(rectCount) { if (!mImageCache->beginRectUpdates(cse)) { mForceClipmapPurge = true; return; } //Con::errorf("layer %x, %d updates", &cse, rectCount); // And GO! for(S32 j=0; j<rectCount; j++) { PROFILE_START(ClipMap_recenter_upload); AssertFatal(buffer[j].isValidRect(),"ClipMap::recenter - got invalid rect!"); // Note the rect, so we can then wrap and let the image cache do its thing. RectI srcRegion = buffer[j]; buffer[j].point.x = srcRegion.point.x % mClipMapSize; buffer[j].point.y = srcRegion.point.y % mClipMapSize; AssertFatal(newData.contains(srcRegion), "ClipMap::recenter - got update buffer outside of expected new data bounds."); mTotalUpdates++; mTexelsUpdated += srcRegion.extent.x * srcRegion.extent.y; //Con::printf("updating (%d %d %d %d)", // buffer[j].point.x, buffer[j].point.y, buffer[j].extent.x, buffer[j].extent.y); mImageCache->doRectUpdate(getMipLevel(cse.mScale), cse, srcRegion, buffer[j]); PROFILE_END(); } mImageCache->finishRectUpdates(cse); } // Check if we've overrun our budget. if((mTexelsUpdated - lastTexelsUpdated) > mMaxTexelUploadPerRecenter) { //Con::warnf("ClipMap::recenter - exceeded budget for this frame, deferring till next frame."); break; } } if( wantCompleteRefill ) { fillWithTextureData(); mNeedRefill = false; } PROFILE_END(); }
void TerrainRenderer::RenderTerrainShader(ShadowMap* shadow, bool filtered) { ENSURE(m->phase == Phase_Render); std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches; std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals; if (visiblePatches.empty() && visibleDecals.empty()) return; CShaderManager& shaderManager = g_Renderer.GetShaderManager(); typedef std::map<CStr, CStr> Defines; Defines defBasic; if (shadow) { defBasic["USE_SHADOW"] = "1"; if (g_Renderer.m_Caps.m_ARBProgramShadow && g_Renderer.m_Options.m_ARBProgramShadow) defBasic["USE_FP_SHADOW"] = "1"; if (g_Renderer.m_Options.m_ShadowPCF) defBasic["USE_SHADOW_PCF"] = "1"; #if !CONFIG2_GLES defBasic["USE_SHADOW_SAMPLER"] = "1"; #endif } defBasic["LIGHTING_MODEL_" + g_Renderer.GetLightEnv().GetLightingModel()] = "1"; CShaderTechniquePtr techBase(shaderManager.LoadEffect("terrain_base", defBasic)); CShaderTechniquePtr techBlend(shaderManager.LoadEffect("terrain_blend", defBasic)); CShaderTechniquePtr techDecal(shaderManager.LoadEffect("terrain_decal", defBasic)); // render the solid black sides of the map first CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect("gui_solid"); techSolid->BeginPass(); CShaderProgramPtr shaderSolid = techSolid->GetShader(); shaderSolid->Uniform("transform", g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform("color", 0.0f, 0.0f, 0.0f, 1.0f); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(shaderSolid); PROFILE_END("render terrain sides"); techSolid->EndPass(); techBase->BeginPass(); PrepareShader(techBase->GetShader(), shadow); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, techBase->GetShader(), false); PROFILE_END("render terrain base"); techBase->EndPass(); // render blends techBlend->BeginPass(); PrepareShader(techBlend->GetShader(), shadow); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, techBlend->GetShader(), false); PROFILE_END("render terrain blends"); techBlend->EndPass(); // Render terrain decals techDecal->BeginPass(); PrepareShader(techDecal->GetShader(), shadow); PROFILE_START("render terrain decals"); for (size_t i = 0; i < visibleDecals.size(); ++i) visibleDecals[i]->Render(techDecal->GetShader(), false); PROFILE_END("render terrain decals"); techDecal->EndPass(); // restore OpenGL state g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(2, 0); g_Renderer.BindTexture(3, 0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); }
/////////////////////////////////////////////////////////////////// // Full-featured terrain rendering with blending and everything void TerrainRenderer::RenderTerrain(bool filtered) { #if CONFIG2_GLES UNUSED2(filtered); #else ENSURE(m->phase == Phase_Render); std::vector<CPatchRData*>& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches; std::vector<CDecalRData*>& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals; if (visiblePatches.empty() && visibleDecals.empty()) return; CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy"); dummyShader->Bind(); // render the solid black sides of the map first g_Renderer.BindTexture(0, 0); glEnableClientState(GL_VERTEX_ARRAY); glColor3f(0, 0, 0); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(dummyShader); PROFILE_END("render terrain sides"); // switch on required client states glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything fullbright // set up texture environment for base pass pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); static const float one[4] = { 1.f, 1.f, 1.f, 1.f }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, dummyShader, true); PROFILE_END("render terrain base"); // render blends // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // switch on second uv set pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // setup additional texenv required by blend pass pglActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // The decal color array contains lighting data, which we don't want in this non-shader mode glDisableClientState(GL_COLOR_ARRAY); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, dummyShader, true); PROFILE_END("render terrain blends"); // Disable second texcoord array pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Render terrain decals g_Renderer.BindTexture(1, 0); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); PROFILE_START("render terrain decals"); for (size_t i = 0; i < visibleDecals.size(); ++i) visibleDecals[i]->Render(dummyShader, true); PROFILE_END("render terrain decals"); // Now apply lighting const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); pglClientActiveTextureARB(GL_TEXTURE0); glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours glBlendFunc(GL_DST_COLOR, GL_ZERO); // GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly float terrainAmbientColor[4] = { lightEnv.m_TerrainAmbientColor.X, lightEnv.m_TerrainAmbientColor.Y, lightEnv.m_TerrainAmbientColor.Z, 1.f }; CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); int streamflags = STREAM_POS|STREAM_COLOR; pglActiveTextureARB(GL_TEXTURE0); // We're not going to use a texture here, but we have to have a valid texture // bound else the texture unit will be disabled. // We should still have a bound splat texture from some earlier rendering, // so assume that's still valid to use. // (TODO: That's a bit of an ugly hack.) // No shadows: (Ambient + Diffuse) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); losTexture.BindTexture(1); pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); streamflags |= STREAM_POSTOUV1; glMatrixMode(GL_TEXTURE); glLoadMatrixf(&losTexture.GetTextureMatrix()._11); glMatrixMode(GL_MODELVIEW); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); PROFILE_START("render terrain streams"); CPatchRData::RenderStreams(visiblePatches, dummyShader, streamflags); PROFILE_END("render terrain streams"); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); // restore OpenGL state g_Renderer.BindTexture(1, 0); pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); dummyShader->Unbind(); #endif }
void CMiniMap::Draw() { PROFILE3("render minimap"); // The terrain isn't actually initialized until the map is loaded, which // happens when the game is started, so abort until then. if(!(GetGUI() && g_Game && g_Game->IsGameStarted())) return; CSimulation2* sim = g_Game->GetSimulation2(); CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY); ENSURE(cmpRangeManager); // Set our globals in case they hadn't been set before m_Camera = g_Game->GetView()->GetCamera(); m_Terrain = g_Game->GetWorld()->GetTerrain(); m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); m_MapSize = m_Terrain->GetVerticesPerSide(); m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f); if(!m_TerrainTexture || g_GameRestarted) CreateTextures(); // only update 2x / second // (note: since units only move a few pixels per second on the minimap, // we can get away with infrequent updates; this is slow) static double last_time; const double cur_time = timer_Time(); if(cur_time - last_time > 0.5) { last_time = cur_time; if(m_TerrainDirty) RebuildTerrainTexture(); } glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); CMatrix3D matrix = GetDefaultGuiMatrix(); glLoadMatrixf(&matrix._11); // Disable depth updates to prevent apparent z-fighting-related issues // with some drivers causing units to get drawn behind the texture glDepthMask(0); const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; const float z = GetBufferedZ(); const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize; const float angle = GetAngle(); // Draw the main textured quad g_Renderer.BindTexture(0, m_TerrainTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); DrawTexture(texCoordMax, angle, x, y, x2, y2, z); // Draw territory boundaries CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture(); territoryTexture.BindTexture(0); glEnable(GL_BLEND); glMatrixMode(GL_TEXTURE); glLoadMatrixf(territoryTexture.GetMinimapTextureMatrix()); glMatrixMode(GL_MODELVIEW); DrawTexture(1.0f, angle, x, y, x2, y2, z); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); // Draw the LOS quad in black, using alpha values from the LOS texture CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); losTexture.BindTexture(0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor3f(0.0f, 0.0f, 0.0f); glMatrixMode(GL_TEXTURE); glLoadMatrixf(losTexture.GetMinimapTextureMatrix()); glMatrixMode(GL_MODELVIEW); DrawTexture(1.0f, angle, x, y, x2, y2, z); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); // Set up the matrix for drawing points and lines glPushMatrix(); glTranslatef(x, y, z); // Rotate around the center of the map glTranslatef((x2-x)/2.f, (y2-y)/2.f, 0.f); // Scale square maps to fit in circular minimap area float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f); glScalef(unitScale, unitScale, 1.f); glRotatef(angle * 180.f/M_PI, 0.f, 0.f, 1.f); glTranslatef(-(x2-x)/2.f, -(y2-y)/2.f, 0.f); PROFILE_START("minimap units"); // Don't enable GL_POINT_SMOOTH because it's far too slow // (~70msec/frame on a GF4 rendering a thousand points) glPointSize(3.f); float sx = (float)m_Width / ((m_MapSize - 1) * TERRAIN_TILE_SIZE); float sy = (float)m_Height / ((m_MapSize - 1) * TERRAIN_TILE_SIZE); CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap); std::vector<MinimapUnitVertex> vertexArray; vertexArray.reserve(ents.size()); for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { MinimapUnitVertex v; ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second); entity_pos_t posX, posZ; if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ)) { ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetPlayerID()); if (vis != ICmpRangeManager::VIS_HIDDEN) { v.a = 255; v.x = posX.ToFloat()*sx; v.y = -posZ.ToFloat()*sy; vertexArray.push_back(v); } } } if (!vertexArray.empty()) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, sizeof(MinimapUnitVertex), &vertexArray[0].x); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(MinimapUnitVertex), &vertexArray[0].r); glDrawArrays(GL_POINTS, 0, (GLsizei)vertexArray.size()); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } PROFILE_END("minimap units"); DrawViewRect(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // Reset everything back to normal glPointSize(1.0f); glEnable(GL_TEXTURE_2D); glDepthMask(1); }
// TODO: render the minimap in a framebuffer and just draw the frambuffer texture // most of the time, updating the framebuffer twice a frame. // Here it updates as ping-pong either texture or vertex array each sec to lower gpu stalling // (those operations cause a gpu sync, which slows down the way gpu works) void CMiniMap::Draw() { PROFILE3("render minimap"); // The terrain isn't actually initialized until the map is loaded, which // happens when the game is started, so abort until then. if(!(GetGUI() && g_Game && g_Game->IsGameStarted())) return; CSimulation2* sim = g_Game->GetSimulation2(); CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY); ENSURE(cmpRangeManager); // Set our globals in case they hadn't been set before m_Camera = g_Game->GetView()->GetCamera(); m_Terrain = g_Game->GetWorld()->GetTerrain(); m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); m_MapSize = m_Terrain->GetVerticesPerSide(); m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f); if(!m_TerrainTexture || g_GameRestarted) CreateTextures(); // only update 2x / second // (note: since units only move a few pixels per second on the minimap, // we can get away with infrequent updates; this is slow) // TODO: store frequency in a config file? static double last_time; const double cur_time = timer_Time(); const bool doUpdate = cur_time - last_time > 0.5; if(doUpdate) { last_time = cur_time; if(m_TerrainDirty) RebuildTerrainTexture(); } glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); CMatrix3D matrix = GetDefaultGuiMatrix(); glLoadMatrixf(&matrix._11); // Disable depth updates to prevent apparent z-fighting-related issues // with some drivers causing units to get drawn behind the texture glDepthMask(0); CShaderProgramPtr shader; CShaderTechniquePtr tech; if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { CShaderDefines defines; defines.Add(str_MINIMAP_BASE, str_1); tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), defines); tech->BeginPass(); shader = tech->GetShader(); } const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; const float z = GetBufferedZ(); const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize; const float angle = GetAngle(); // Draw the main textured quad if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) shader->BindTexture(str_baseTex, m_TerrainTexture); else g_Renderer.BindTexture(0, m_TerrainTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); DrawTexture(shader, texCoordMax, angle, x, y, x2, y2, z); // Draw territory boundaries CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture(); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) shader->BindTexture(str_baseTex, territoryTexture.GetTexture()); else territoryTexture.BindTexture(0); glEnable(GL_BLEND); glMatrixMode(GL_TEXTURE); glLoadMatrixf(territoryTexture.GetMinimapTextureMatrix()); glMatrixMode(GL_MODELVIEW); DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); // Draw the LOS quad in black, using alpha values from the LOS texture CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { tech->EndPass(); CShaderDefines defines; defines.Add(str_MINIMAP_LOS, str_1); tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), defines); tech->BeginPass(); shader = tech->GetShader(); shader->BindTexture(str_baseTex, losTexture.GetTexture()); } else { losTexture.BindTexture(0); } glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor3f(0.0f, 0.0f, 0.0f); glMatrixMode(GL_TEXTURE); glLoadMatrixf(losTexture.GetMinimapTextureMatrix()); glMatrixMode(GL_MODELVIEW); DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_BLEND); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { tech->EndPass(); CShaderDefines defines; defines.Add(str_MINIMAP_POINT, str_1); tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), defines); tech->BeginPass(); shader = tech->GetShader(); } // Set up the matrix for drawing points and lines glPushMatrix(); glTranslatef(x, y, z); // Rotate around the center of the map glTranslatef((x2-x)/2.f, (y2-y)/2.f, 0.f); // Scale square maps to fit in circular minimap area float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f); glScalef(unitScale, unitScale, 1.f); glRotatef(angle * 180.f/M_PI, 0.f, 0.f, 1.f); glTranslatef(-(x2-x)/2.f, -(y2-y)/2.f, 0.f); PROFILE_START("minimap units"); const float sx = (float)m_Width / ((m_MapSize - 1) * TERRAIN_TILE_SIZE); const float sy = (float)m_Height / ((m_MapSize - 1) * TERRAIN_TILE_SIZE); CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap); if (doUpdate) { VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>(); VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>(); m_EntitiesDrawn = 0; MinimapUnitVertex v; std::vector<MinimapUnitVertex> pingingVertices; pingingVertices.reserve(MAX_ENTITIES_DRAWN/2); const double time = timer_Time(); if (time > m_NextBlinkTime) { m_BlinkState = !m_BlinkState; m_NextBlinkTime = time + m_HalfBlinkDuration; } entity_pos_t posX, posZ; for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second); if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ)) { ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetPlayerID()); if (vis != ICmpRangeManager::VIS_HIDDEN) { v.a = 255; v.x = posX.ToFloat()*sx; v.y = -posZ.ToFloat()*sy; // Check minimap pinging to indicate something if (m_BlinkState && cmpMinimap->CheckPing(time, m_PingDuration)) { v.r = 255; // ping color is white v.g = 255; v.b = 255; pingingVertices.push_back(v); } else { addVertex(v, attrColor, attrPos); ++m_EntitiesDrawn; } } } } // Add the pinged vertices at the end, so they are drawn on top for (size_t v = 0; v < pingingVertices.size(); ++v) { addVertex(pingingVertices[v], attrColor, attrPos); ++m_EntitiesDrawn; } ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN); m_VertexArray.Upload(); } if (m_EntitiesDrawn > 0) { // Don't enable GL_POINT_SMOOTH because it's far too slow // (~70msec/frame on a GF4 rendering a thousand points) glPointSize(3.f); u8* indexBase = m_IndexArray.Bind(); u8* base = m_VertexArray.Bind(); const GLsizei stride = (GLsizei)m_VertexArray.GetStride(); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { shader->VertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset); shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset); shader->AssertPointersBound(); } else { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glDisable(GL_TEXTURE_2D); glVertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset); glColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset); } if (!g_Renderer.m_SkipSubmit) { glDrawElements(GL_POINTS, (GLsizei)(m_EntitiesDrawn), GL_UNSIGNED_SHORT, indexBase); } g_Renderer.GetStats().m_DrawCalls++; CVertexBuffer::Unbind(); } PROFILE_END("minimap units"); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { tech->EndPass(); CShaderDefines defines; defines.Add(str_MINIMAP_LINE, str_1); tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), defines); tech->BeginPass(); shader = tech->GetShader(); } else { glEnable(GL_TEXTURE_2D); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } DrawViewRect(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER) { tech->EndPass(); } // Reset everything back to normal glPointSize(1.0f); glEnable(GL_TEXTURE_2D); glDepthMask(1); }
void AtlasClipMapBatcher::queue(const Point3F &camPos, AtlasResourceGeomStub *args) { PROFILE_START(AtlasClipMapBatcher_queue); AtlasGeomChunk *agc = args->mChunk; Point3F nearPos, farPos; Point2F nearTC, farTC; agc->calculatePoints(camPos, nearPos, farPos, nearTC, farTC ); const F32 nearDistance = (camPos - nearPos).len(); const F32 farDistance = (camPos - farPos).len(); const RectF texBounds(nearTC, farTC - nearTC); // Now, calculate and store levels into a new RenderNote. S32 startLevel, endLevel; mClipMap->calculateClipMapLevels(nearDistance, farDistance, texBounds, startLevel, endLevel); // Allocate a render note. RenderNote *rn = mRenderNoteAlloc.alloc(); // Check if this chunk will get fogged - consider furthest point, and if // it'll be fogged then draw a fog pass. if(mState->getHazeAndFog(farDistance, farPos.z - camPos.z) > (1.0/256.0) || mState->getHazeAndFog(farDistance, -(farPos.z - camPos.z)) > (1.0/256.0)) mFogList.push_back(rn); // Is this chunk within the detail map range? if( isDetailMappingEnabled() && nearDistance < mDetailMapFadeEndDistance ) mDetailList.push_back(rn); // Let our plugins have a shot at the render note. for (U32 i = 0; i < mRenderPlugins.size(); i++) { mRenderPlugins[i]->queue(rn, mObject, args); } rn->levelStart = startLevel; rn->levelEnd = endLevel; rn->levelCount = endLevel - startLevel + 1; rn->chunk = agc; rn->nearDist = nearDistance; // Stuff into right list based on shader. switch(rn->levelCount) { case 2: case 3: case 4: mRenderList[rn->levelCount-1].push_back(rn); break; default: Con::errorf("AtlasClipMapBatcher::queue - got unexpected level count of %d", rn->levelCount); break; } AssertFatal(rn->levelCount >= 2 && rn->levelCount <= 4, "AtlasClipMapBatcher::queue - bad level count!"); PROFILE_END(); }