void TerrCell::updateGrid( const RectI &gridRect, bool opacityOnly ) { PROFILE_SCOPE( TerrCell_UpdateGrid ); // If we have a VB... then update it. if ( mVertexBuffer.isValid() && !opacityOnly ) _updateVertexBuffer(); // Update our PB, if any _updatePrimitiveBuffer(); // If we don't have children... then we're // a leaf at the bottom of the cell quadtree // and we should just update our bounds. if ( !mChildren[0] ) { if ( !opacityOnly ) _updateBounds(); _updateMaterials(); return; } // Otherwise, we must call updateGrid on our children // and then update our bounds/materials AFTER to contain them. mMaterials = 0; for ( U32 i = 0; i < 4; i++ ) { TerrCell *cell = mChildren[i]; // The overlap test doesn't hit shared edges // so grow it a bit when we create it. const RectI cellRect( cell->mPoint.x - 1, cell->mPoint.y - 1, cell->mSize + 2, cell->mSize + 2 ); // We do an overlap and containment test as it // properly handles zero sized rects. if ( cellRect.contains( gridRect ) || cellRect.overlaps( gridRect ) ) cell->updateGrid( gridRect, opacityOnly ); // Update the bounds from our children. if ( !opacityOnly ) { if ( i == 0 ) mBounds = mChildren[i]->getBounds(); else mBounds.intersect( mChildren[i]->getBounds() ); mRadius = mBounds.len() * 0.5f; } // Update the material flags. mMaterials |= mChildren[i]->getMaterials(); } if ( mMaterial ) mMaterial->init( mTerrain, mMaterials ); }
void TerrainBlock::_renderBlock( SceneRenderState *state ) { PROFILE_SCOPE( TerrainBlock_RenderBlock ); // Prevent rendering shadows if feature is disabled if ( !mCastShadows && state->isShadowPass() ) return; MatrixF worldViewXfm = state->getWorldViewMatrix(); worldViewXfm.mul( getRenderTransform() ); MatrixF worldViewProjXfm = state->getProjectionMatrix(); worldViewProjXfm.mul( worldViewXfm ); const MatrixF &objectXfm = getRenderWorldTransform(); Point3F objCamPos = state->getDiffuseCameraPosition(); objectXfm.mulP( objCamPos ); // Get the shadow material. if ( !mDefaultMatInst ) mDefaultMatInst = TerrainCellMaterial::getShadowMat(); // Make sure we have a base material. if ( !mBaseMaterial ) { mBaseMaterial = new TerrainCellMaterial(); mBaseMaterial->init( this, 0, false, false, true ); } // Did the detail layers change? if ( mDetailsDirty ) { _updateMaterials(); mDetailsDirty = false; } // If the layer texture has been cleared or is // dirty then update it. if ( mLayerTex.isNull() || mLayerTexDirty ) _updateLayerTexture(); // If the layer texture is dirty or we lost the base // texture then regenerate it. if ( mLayerTexDirty || mBaseTex.isNull() ) { _updateBaseTexture( false ); mLayerTexDirty = false; } static Vector<TerrCell*> renderCells; renderCells.clear(); mCell->cullCells( state, objCamPos, &renderCells ); RenderPassManager *renderPass = state->getRenderPass(); MatrixF *riObjectToWorldXfm = renderPass->allocUniqueXform( getRenderTransform() ); const bool isColorDrawPass = state->isDiffusePass() || state->isReflectPass(); // This is here for shadows mostly... it allows the // proper shadow material to be generated. BaseMatInstance *defaultMatInst = state->getOverrideMaterial( mDefaultMatInst ); // Only pass and use the light manager if this is not a shadow pass. LightManager *lm = NULL; if ( isColorDrawPass ) lm = LIGHTMGR; for ( U32 i=0; i < renderCells.size(); i++ ) { TerrCell *cell = renderCells[i]; // Ok this cell is fit to render. TerrainRenderInst *inst = renderPass->allocInst<TerrainRenderInst>(); // Setup lights for this cell. if ( lm ) { SphereF bounds = cell->getSphereBounds(); getRenderTransform().mulP( bounds.center ); LightQuery query; query.init( bounds ); query.getLights( inst->lights, 8 ); } GFXVertexBufferHandleBase vertBuff; GFXPrimitiveBufferHandle primBuff; cell->getRenderPrimitive( &inst->prim, &vertBuff, &primBuff ); inst->mat = defaultMatInst; inst->vertBuff = vertBuff.getPointer(); if ( primBuff.isValid() ) { // Use the cell's custom primitive buffer inst->primBuff = primBuff.getPointer(); } else { // Use the standard primitive buffer for this cell inst->primBuff = mPrimBuffer.getPointer(); } inst->objectToWorldXfm = riObjectToWorldXfm; // If we're not drawing to the shadow map then we need // to include the normal rendering materials. if ( isColorDrawPass ) { const SphereF &bounds = cell->getSphereBounds(); F32 sqDist = ( bounds.center - objCamPos ).lenSquared(); F32 radiusSq = mSquared( ( mMaxDetailDistance + bounds.radius ) * smDetailScale ); // If this cell is near enough to get detail textures then // use the full detail mapping material. Else we use the // simple base only material. if ( !state->isReflectPass() && sqDist < radiusSq ) inst->cellMat = cell->getMaterial(); else if ( state->isReflectPass() ) inst->cellMat = mBaseMaterial->getReflectMat(); else inst->cellMat = mBaseMaterial; } inst->defaultKey = (U32)cell->getMaterials(); // Submit it for rendering. renderPass->addInst( inst ); } // Trigger the debug rendering. if ( state->isDiffusePass() && !renderCells.empty() && smDebugRender ) { // Store the render cells for later. mDebugCells = renderCells; ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>(); ri->renderDelegate.bind( this, &TerrainBlock::_renderDebug ); ri->type = RenderPassManager::RIT_Editor; state->getRenderPass()->addInst( ri ); } }
void TerrCell::_init( TerrainBlock *terrain, const Point2I &point, U32 size, U32 level ) { PROFILE_SCOPE( TerrCell_Init ); mTerrain = terrain; mPoint = point; mSize = size; mLevel = level; // Generate a VB (and maybe a PB) for this cell, unless we are the Root cell. if ( level > 0 ) { _updateVertexBuffer(); _updatePrimitiveBuffer(); } if ( mSize <= smMinCellSize ) { // Update our bounds and materials... the // parent will use it to update itself. _updateBounds(); _updateMaterials(); return; } // Create our children and update our // bounds and materials from them. const U32 childSize = mSize / 2; const U32 childLevel = mLevel + 1; mChildren[0] = new TerrCell; mChildren[0]->_init( mTerrain, Point2I( mPoint.x, mPoint.y ), childSize, childLevel ); mBounds = mChildren[0]->getBounds(); mMaterials = mChildren[0]->getMaterials(); mChildren[1] = new TerrCell; mChildren[1]->_init( mTerrain, Point2I( mPoint.x + childSize, mPoint.y ), childSize, childLevel ); mBounds.intersect( mChildren[1]->getBounds() ); mMaterials |= mChildren[1]->getMaterials(); mChildren[2] = new TerrCell; mChildren[2]->_init( mTerrain, Point2I( mPoint.x, mPoint.y + childSize ), childSize, childLevel ); mBounds.intersect( mChildren[2]->getBounds() ); mMaterials |= mChildren[2]->getMaterials(); mChildren[3] = new TerrCell; mChildren[3]->_init( mTerrain, Point2I( mPoint.x + childSize, mPoint.y + childSize ), childSize, childLevel ); mBounds.intersect( mChildren[3]->getBounds() ); mMaterials |= mChildren[3]->getMaterials(); mRadius = mBounds.len() * 0.5f; _updateOBB(); }