void D3D9RenderTargetTexture::GetData(Bitmap &B, const Rectangle2i &Source, const Rectangle2i &Dest) { D3DAlwaysValidate(_Device->GetRenderTargetData(_Surface, _SurfacePlain), "GetRenderTargetData"); if(_SurfaceResizing == NULL) { D3DAlwaysValidate(_Device->CreateOffscreenPlainSurface(_Width, _Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &_SurfaceResizing, NULL), "CreateOffscreenPlainSurface"); } D3DAlwaysValidate(_Device->UpdateSurface(_SurfacePlain, NULL, _SurfaceResizing, NULL), "UpdateSurface"); RECT SourceRect = Source.ToRect(); RECT DestRect = Rectangle2i(Vec2i::Origin, Dest.Dimensions()).ToRect(); D3DAlwaysValidate(_Device->StretchRect(_SurfaceResizing, &SourceRect, _Surface, &DestRect, D3DTEXF_LINEAR), "StretchRect"); D3DAlwaysValidate(_Device->GetRenderTargetData(_Surface, _SurfacePlain), "GetRenderTargetData"); D3DLOCKED_RECT LockedRect; D3DAlwaysValidate(_SurfacePlain->LockRect(&LockedRect, NULL, 0), "LockRect"); Assert(int(B.Width()) >= Dest.Max.x && int(B.Height()) >= Dest.Max.y, "Bitmap too small"); RGBColor *SurfData = (RGBColor*)LockedRect.pBits; for(int y = Dest.Min.y; y < Dest.Max.y; y++) { //memcpy(B[_Height - 1 - y], &SurfData[y * LockedRect.Pitch / 4], _Width * 4); memcpy(B[y] + Dest.Min.x, &SurfData[(y - Dest.Min.y) * LockedRect.Pitch / 4], Dest.Dimensions().x * 4); } _SurfacePlain->UnlockRect(); }
void ClipmapRenderer::drawSampleRectangle( const Rectangle2i& rect, const Color3f& color, const WorldTransformation& worldTrans ) const { Pnt2f from = worldTrans.sampleToWorldPos( rect.getTopLeft() ); Pnt2f to = worldTrans.sampleToWorldPos( rect.getBottomRight() - Pnt2i( 1, 1 ) ); glColor3f( color[ 0 ], color[ 1 ], color[ 2 ] ); glBegin( GL_LINE_STRIP ); glVertex3f( from[ 0 ], 0, from[ 1 ] ); glVertex3f( to[ 0 ], 0, from[ 1 ] ); glVertex3f( to[ 0 ], 0, to[ 1 ] ); glVertex3f( from[ 0 ], 0, to[ 1 ] ); glVertex3f( from[ 0 ], 0, from[ 1 ] ); glEnd(); }
void D3D9RenderTargetTexture::RenderStringToBmp(const D3D9Font &Font, const String &Text, const Rectangle2i &Region, RGBColor FontColor, RGBColor BackgroundColor, Bitmap &Bmp) { D3D9ProtectRenderTarget Protection(_Device, true, true); SetAsRenderTarget(); _Device->Clear(0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, D3DCOLOR_ARGB(BackgroundColor.a, BackgroundColor.r, BackgroundColor.g, BackgroundColor.b), 1.0f, 0 ); Font.DrawString(Text, Vec2f(Region.Min), float(Region.Width()), FontColor); GetData(Bmp); }
void D3D9Font::DrawString(const String &Text, const Rectangle2i &Rect, RGBColor Color) const { RECT CurRect = Rect.ToRect(); HRESULT CoopLevel = _Device->TestCooperativeLevel(); if(CoopLevel == D3D_OK) { _Font->DrawText( NULL, Text.CString(), Text.Length(), &CurRect, DT_WORDBREAK | DT_CALCRECT, 0 ); _Font->DrawText( NULL, Text.CString(), Text.Length(), &CurRect, DT_WORDBREAK | DT_NOCLIP, D3DXCOLOR(Color.r / 255.0f, Color.g / 255.0f, Color.b / 255.0f, 1.0f )); } }
void CpuClipmapRenderer::createBlockIndices( GeometryClipmapLevel& level, const Rectangle2i& blockRect, int levelSampleCount, IndexList& indices ) { // todo: this needs rework.. if( blockRect.isEmpty() ) { return; } assert( blockRect._x0 >= 0 ); assert( blockRect._y0 >= 0 ); assert( blockRect._x1 > blockRect._x0 ); assert( blockRect._y1 > blockRect._y0 ); assert( blockRect._x0 < levelSampleCount ); assert( blockRect._y0 < levelSampleCount ); assert( blockRect._x1 < levelSampleCount ); assert( blockRect._y1 < levelSampleCount ); for( int y = blockRect._y0; y < blockRect._y1; ++y ) { assert( y + 1 < levelSampleCount ); const int row0 = ( level.blockOrigin[ 1 ] + y ) % levelSampleCount; const int row1 = ( level.blockOrigin[ 1 ] + y + 1 ) % levelSampleCount; for( int x = blockRect._x0; x < blockRect._x1; ++x ) { assert( x + 1 < levelSampleCount ); const int col0 = ( level.blockOrigin[ 0 ] + x ) % levelSampleCount; const int col1 = ( level.blockOrigin[ 0 ] + x + 1 ) % levelSampleCount; const int idx0 = row0 * levelSampleCount + col0; const int idx1 = row0 * levelSampleCount + col1; const int idx2 = row1 * levelSampleCount + col0; const int idx3 = row1 * levelSampleCount + col1; assert( idx0 < 65536 ); assert( idx1 < 65536 ); assert( idx2 < 65536 ); assert( idx3 < 65536 ); indices.push_back( idx0 ); indices.push_back( idx2 ); indices.push_back( idx1 ); indices.push_back( idx1 ); indices.push_back( idx2 ); indices.push_back( idx3 ); } } }
void ImageHeightDataSource::fillHeightData( GeometryClipmapLevel& level, const Rectangle2i& targetRect ) const { // check validity of the input data: assert( targetRect.getWidth() > 0 ); assert( targetRect.getHeight() > 0 ); // todo: clip data to the existing data: (saves per pixel clipping) // iterate over the target pixels and fill them with the image height data: Pnt2i samplePos; for( int y = targetRect.y0; y < targetRect.y1; ++y ) { float* targetPtr = &level.heightmap.samples[ y * level.heightmap.size ]; for( int x = targetRect.x0; x < targetRect.x1; ++x ) { // todo: make this incremental: samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) ); targetPtr[ x ] = heightScale_ * getHeightSample( samplePos ); } } }
bool CpuClipmapRenderer::buildIndices( GeometryClipmapLevel& level, const GeometryClipmapLevel* finerLevel ) { // todo: speedup?! (insert optimal tristrips here..) // and frustum culling... TerrainLevelRenderData& renderData = levels_[ level.index ]; const int levelSampleCount = level.heightmap.size; if( !renderData.rebuildIndices ) { return true; } renderData.rebuildIndices = false; renderData.indices.resize( 0 ); const int levelSampleCoverage = level.getSampleCoverage(); // we need the world extend in sample space for clipping: const Pnt2i worldSampleCount = geoClipmaps_->getWorldSampleCount(); // check if this level is at least partly visible: if( level.sampleOrigin[ 0 ] >= worldSampleCount[ 0 ] - level.sampleSpacing || level.sampleOrigin[ 1 ] >= worldSampleCount[ 1 ] - level.sampleSpacing || level.sampleOrigin[ 0 ] + levelSampleCoverage < level.sampleSpacing || level.sampleOrigin[ 1 ] + levelSampleCoverage < level.sampleSpacing ) { // completely invisible: return false; } // decision: // either it is only one large or 8 small primitives: if( !finerLevel ) { // ok: no finer level: only one large primitive // todo: possible speedup: frustum culling by dividing the level in smaller parts.. Rectangle2i blockRect; blockRect.setBounds( 0, 0, levelSampleCount - 1, levelSampleCount - 1 ); // clip rectangle to the world bounds: if( level.sampleOrigin[ 0 ] < 0 ) { blockRect._x0 = -level.sampleOrigin[ 0 ] / level.sampleSpacing; } if( level.sampleOrigin[ 1 ] < 0 ) { blockRect._y0 = -level.sampleOrigin[ 1 ] / level.sampleSpacing; } if( level.sampleOrigin[ 0 ] + levelSampleCoverage >= worldSampleCount[ 0 ] ) { blockRect._x1 = ( worldSampleCount[ 0 ] - level.sampleOrigin[ 0 ] - 1 ) / level.sampleSpacing; } if( level.sampleOrigin[ 1 ] + levelSampleCoverage >= worldSampleCount[ 1 ] ) { blockRect._y1 = ( worldSampleCount[ 1 ] - level.sampleOrigin[ 1 ] - 1 ) / level.sampleSpacing; } createBlockIndices( level, blockRect, levelSampleCount, renderData.indices ); } else { // build 8 primitives for the level: // 0 1 2 // 3 4 // 5 6 7 // todo: beautify this code int colEnd[ 3 ]; int rowEnd[ 3 ]; int colEndSample[ 3 ]; int rowEndSample[ 3 ]; colEndSample[ 0 ] = clamp( finerLevel->sampleOrigin[ 0 ], 0, worldSampleCount[ 0 ] - 1 ); colEndSample[ 1 ] = clamp( finerLevel->sampleOrigin[ 0 ] + finerLevel->getSampleCoverage(), 0, worldSampleCount[ 0 ] - 1 ); colEndSample[ 2 ] = clamp( level.sampleOrigin[ 0 ] + levelSampleCoverage, 0, worldSampleCount[ 0 ] - 1 ); // convert to block space: colEnd[ 0 ] = clamp( ( colEndSample[ 0 ] - level.sampleOrigin[ 0 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); colEnd[ 1 ] = clamp( ( colEndSample[ 1 ] - level.sampleOrigin[ 0 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); colEnd[ 2 ] = clamp( ( colEndSample[ 2 ] - level.sampleOrigin[ 0 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); rowEndSample[ 0 ] = clamp( finerLevel->sampleOrigin[ 1 ], 0, worldSampleCount[ 1 ] - 1 ); rowEndSample[ 1 ] = clamp( finerLevel->sampleOrigin[ 1 ] + finerLevel->getSampleCoverage(), 0, worldSampleCount[ 1 ] - 1 ); rowEndSample[ 2 ] = clamp( level.sampleOrigin[ 1 ] + levelSampleCoverage, 0, worldSampleCount[ 1 ] - 1 ); // convert to block space: rowEnd[ 0 ] = clamp( ( rowEndSample[ 0 ] - level.sampleOrigin[ 1 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); rowEnd[ 1 ] = clamp( ( rowEndSample[ 1 ] - level.sampleOrigin[ 1 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); rowEnd[ 2 ] = clamp( ( rowEndSample[ 2 ] - level.sampleOrigin[ 1 ] ) / level.sampleSpacing, 0, levelSampleCount - 1 ); // clip the row start: int rowStart = 0; if( level.sampleOrigin[ 1 ] < 0 ) { rowStart = -level.sampleOrigin[ 1 ] / level.sampleSpacing; } Rectangle2i blockRect; for( int row = 0; row < 3; ++row ) { //int rowStart = 0; if( row > 0 ) { rowStart = rowEnd[ row - 1 ]; } int colStart = 0; if( level.sampleOrigin[ 0 ] < 0 ) { colStart = -level.sampleOrigin[ 0 ] / level.sampleSpacing; } for( int col = 0; col < 3; ++col ) { if( row == 1 && col == 1 ) { // this place is taken by the finer level continue; } if( col > 0 ) { colStart = colEnd[ col - 1 ]; } blockRect.setValues( colStart, rowStart, colEnd[ col ], rowEnd[ row ] ); stats_.drawnBlockCount++; // create the indices: createBlockIndices( level, blockRect, levelSampleCount, renderData.indices ); } } } // return false if this contains no indices -> no need to render return renderData.indices.size() > 0; }
void CpuClipmapRenderer::onBuildVertices( GeometryClipmapLevel& level, const GeometryClipmapLevel* coarserLevel, const Rectangle2i& blockRect ) { const int levelSampleCount = level.heightmap.size; TerrainLevelRenderData& levelRenderData = levels_[ level.index ]; // loop over the changed vertices: Pnt2i samplePos; Pnt2f worldPos; const Pnt2i worldSampleCount = geoClipmaps_->getWorldSampleCount(); for( int y = blockRect._y0; y < blockRect._y1; ++y ) { const int index = y * levelSampleCount + blockRect._x0; const float* samplePtr = &level.heightmap.samples[ index ]; OpenGLTerrainVertex* vertexPtr = &levelRenderData.vertices[ index ]; for( int x = blockRect._x0; x < blockRect._x1; ++x ) { // todo: make this incremental samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) ); vertexPtr->pos[ 0 ] = samplePos[ 0 ]; vertexPtr->pos[ 1 ] = *samplePtr; vertexPtr->pos[ 2 ] = samplePos[ 1 ]; vertexPtr->pos[ 3 ] = *samplePtr; // todo: correct uv coordinates: vertexPtr->uv.setValues( float(samplePos[ 0 ]) / float(worldSampleCount[ 0 ]), float(samplePos[ 1 ]) / float(worldSampleCount[ 1 ]) ); //vertexPtr->uv.setValues( float( x ) / float( levelSampleCount - 1 ), float( y ) / float( levelSampleCount - 1 ) ); samplePtr++; vertexPtr++; } } if( coarserLevel ) { // todo: fill in the interpolated positions of the coarser level: const int coarserLevelSpacing = coarserLevel->sampleSpacing; #ifdef OSG_DEBUG const int coarserLevelSampleCount = coarserLevel->heightmap.size; #endif // todo: make this faster for( int y = blockRect._y0; y < blockRect._y1; ++y ) { const int index = y * levelSampleCount + blockRect._x0; OpenGLTerrainVertex* vertexPtr = &levelRenderData.vertices[ index ]; for( int x = blockRect._x0; x < blockRect._x1; ++x, ++vertexPtr ) { // todo: make this incremental samplePos = level.blockPosToSamplePos( Pnt2i( x, y ) ); Pnt2i blendWeights = componentModulo( samplePos, coarserLevelSpacing ); if( blendWeights[ 0 ] == 0 && blendWeights[ 1 ] == 0 ) { continue; } Pnt2i coarserSamplePos = samplePos - blendWeights; Pnt2i coarserSamplePos10 = componentAdd( coarserSamplePos, Pnt2i( coarserLevelSpacing, 0 ) ); Pnt2i coarserSamplePos01 = componentAdd( coarserSamplePos, Pnt2i( 0, coarserLevelSpacing ) ); // todo: clip on a higher level (not per pixel) if( !coarserLevel->containsSample( coarserSamplePos ) || !coarserLevel->containsSample( coarserSamplePos10 ) || !coarserLevel->containsSample( coarserSamplePos01 ) ) { // no data} continue; } Pnt2i coarserBlockPos = coarserLevel->samplePosToBlockPos( coarserSamplePos ); #ifdef OSG_DEBUG assert( coarserBlockPos[ 0 ] < coarserLevelSampleCount && coarserBlockPos[ 1 ] < coarserLevelSampleCount ); #endif Pnt2i coarserBlockPos10 = coarserLevel->samplePosToBlockPos( coarserSamplePos10 ); Pnt2i coarserBlockPos01 = coarserLevel->samplePosToBlockPos( coarserSamplePos01 ); if( ( blendWeights[ 0 ] != 0 ) && ( blendWeights[ 1 ] != 0 ) ) { // todo: is this case important? } else if( blendWeights[ 0 ] != 0 ) { const float coarserSample00 = coarserLevel->getBlockSample( coarserBlockPos ); const float coarserSample10 = coarserLevel->getBlockSample( coarserBlockPos10 ); vertexPtr->pos[ 3 ] = ( coarserSample00 + coarserSample10 ) / 2.0f; } else if( blendWeights[ 1 ] != 0 ) { const float coarserSample00 = coarserLevel->getBlockSample( coarserBlockPos ); const float coarserSample01 = coarserLevel->getBlockSample( coarserBlockPos01 ); vertexPtr->pos[ 3 ] = ( coarserSample00 + coarserSample01 ) / 2.0f; } } } } if( levelRenderData.vertexBuffer.isValid() ) { // todo: upload only the changed data rect: // const int vertexCount = levelRenderData.vertices.size(); //std::cerr << "Uploading Vbo Data " << blockRect << std::endl; const int vboLineLength = sizeof( OpenGLTerrainVertex ) * blockRect.getWidth(); for( int y = blockRect._y0; y < blockRect._y1; ++y ) { const int index = y * levelSampleCount + blockRect._x0; const int vboOffset = index * sizeof( OpenGLTerrainVertex ); levelRenderData.vertexBuffer.uploadData( &levelRenderData.vertices[ index ], vboOffset, vboLineLength ); } // testing: upload the complete vbo: //std::cerr << "Uploading VBO Data" << std::endl; //levelRenderData.vertexBuffer.uploadData( &levelRenderData.vertices[ 0 ], 0, sizeof( OpenGLTerrainVertex ) * vertexCount ); //checkVboConsistency( levelRenderData, levelSampleCount ); levelRenderData.vertexBuffer.deactivate(); } }
int GeometryClipmaps::updateBigLevelBlock( GeometryClipmapLevel& level, const GeometryClipmapLevel* parentLevel, const Rectangle2i& targetRect ) { if( targetRect.isEmpty() ) { return 0; } const int levelSampleCount = level.heightmap.size; // split the rect into up to 4 subrects and update them individually: Rectangle2i subRects[ 4 ]; int subRectCount = 0; #if 0 int midY = 0; int maxY = 0; int midX = 0; int maxX = 0; #endif subRects[ 0 ] = targetRect; subRectCount++; if( targetRect._x1 < targetRect._x0 ) { // split in X: subRects[ 0 ]._x1 = levelSampleCount; subRects[ 1 ] = targetRect; subRects[ 1 ]._x0 = 0; subRectCount++; } else if( targetRect._x1 > levelSampleCount ) { // split in X: subRects[ 0 ]._x1 = levelSampleCount; subRects[ 1 ] = targetRect; subRects[ 1 ]._x0 = 0; subRects[ 1 ]._x1 -= levelSampleCount; subRectCount++; } // ok.. we have 1 or 2 subRects already: split them on the Y axis: if( targetRect._y1 < targetRect._y0 ) { // patch the existing ones: for( int i = 0; i < subRectCount; ++i ) { subRects[ subRectCount + i ] = subRects[ i ]; subRects[ subRectCount + i ]._y0 = 0; subRects[ i ]._y1 = levelSampleCount; } subRectCount += subRectCount; } else if( targetRect._y1 > levelSampleCount ) { // patch the existing ones: for( int i = 0; i < subRectCount; ++i ) { subRects[ subRectCount + i ] = subRects[ i ]; subRects[ subRectCount + i ]._y0 = 0; subRects[ subRectCount + i ]._y1 -= levelSampleCount; subRects[ i ]._y1 = levelSampleCount; } subRectCount += subRectCount; } int sampleUpdateCount = 0; for( int i = 0; i < subRectCount; ++i ) { sampleUpdateCount += updateLevelBlock( level, parentLevel, subRects[ i ] ); } return sampleUpdateCount; }