This project is read-only.

11. Windows Forms Integration - Tutorial

Probably it's most important tutorial. Usage .NET based game engine in 2D game world is very limited. In other hand windows forms applications requires fast and powerful 2D graphics library much more often. In this tutorial I'll show you how to plug Vortex2D into windows application correctly. Also bitmap-to-texture data transfer will be covered.

windowsFormsIntergration_50x50.jpg

Initialization

In win forms application you need to create Vortex2D drawing device manually. For games this process is encapsulated in Vortex.Game class. The best place to create device is Load event of form.

This time we will target Panel. With the same success you can target self window.

//create drawing device and target it to some window (in this sample - Panel Surface)
_DrawingDevice = new DrawingDevice(TargetDrawingPanel.Handle);

Vortex.Drawing.DrawingDevice class encapsulates Direct3D 9 Device. It should be targeted to some window (specified with handle). You can specify other parameters: bufferWidth, bufferHeight, fullScreen, verticalSync.
  • bufferWidth, bufferHeight - size of back-buffer used by device. If these params are not specified backbuffer size will match window size specified with handle;
  • fullScreen - defines fullscreen mode, for windowed applications it should be to false;
  • verticalSync - enables waiting of vertical blank signal before showing new frame;

Next step - create canvas object which contains all drawing functions.

//create canvas for newly created device
_Canvas = new Canvas2D(_DrawingDevice);
After drawing device is created we can create/load all other resources: textures, fonts, shaders... Tutorial shows a basic interoperability between textures and bitmaps. Currently you can use bitmaps as pixel source on texture creation or texture surface update. One more useful thing is creation of built-in Vortex2D console font. In WinForms application you need to do it manually.

//creation of default console font embedded in Vortex2D
_DefaultConsoleFont = SpriteFont.CreateDefaultConsoleFont();

//load texture background through bitmap proxy, it just works
using (Bitmap bitmap = new Bitmap(@"graphics\\tornado.jpg")) {
    _Background = new Texture(bitmap);
}

//create bitmap on which we will draw with System.Drawing methods
_DynamicBitmap = new Bitmap(DYNAMIC_BITMAP_SIZE.Width, DYNAMIC_BITMAP_SIZE.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

//create texture which will reflect dynamic bitmap content
_BitmapReflection = new Texture(DYNAMIC_BITMAP_SIZE.Width, DYNAMIC_BITMAP_SIZE.Height, PixelFormat.A8R8G8B8);

Control Paint Event

We can call UpdateTarget each time when we change something in frame. But sometimes windows need to update control surface. It is unnecessary to redraw frame with UpdateTarget, much easily just present content from frame buffer.

private void TargetDrawingPanel_Paint(object sender, PaintEventArgs e) {
    //in paint event for target we need just present currently available frame
    _DrawingDevice.Present();
}

Control Resize Handling

We are planning to support control resizing. Next code sample will handle this fine.

private void TargetDrawingPanel_Resize(object sender, EventArgs e) {
    //we need manually setup new device buffer parameters
    _DrawingDevice.ResizeBuffer(TargetDrawingPanel.Width, TargetDrawingPanel.Height);           

    //after device buffer is resized
    UpdateTarget();
}

Drawing

Having everything initialized we can start drawing. Lets create UpdateTarget core method where whole frame will be repainted. Encapsulate all user drawing code into DrawFrameContent

private void UpdateTarget() {
    //start new scene (device automatically begins new frame)
    if (_Canvas.BeginScene()) {
        //now we can draw frame content
        DrawFrameContent();
        //finalize canvas drawing
        _Canvas.EndScene();
        //present frame from back-buffer to target window (Panel)
        _DrawingDevice.Present();
    } else {
        //reset device (try until it will be successfully reset)
        _DrawingDevice.Reset();
    }
}
Canvas2D.BeginScene turns on device drawing mode. It can return false if device is lost and need to be reset. Otherwise we can start drawing. After drawing is complete we need to call Canvas2D.EndScene which flushes all buffers. After that backbuffer is ready and need to be presented. DrawingDevice.Present brings content of backbuffer to target window surface.

BTW DrawingDevice.Present can copy backbuffer content not only into window specified on DrawingDevice creation. You can freely specify another target, source or target region.

private void DrawFrameContent() {
    //clear background
    _Canvas.DrawSprite(_Canvas.Region, _Background.ToSprite(), ColorU.White);

    //draw reflection bitmap
    Rect rect = Rect.FromPoint(DYNAMIC_BITMAP_OFFSET.X, DYNAMIC_BITMAP_OFFSET.Y, DYNAMIC_BITMAP_SIZE.Width, DYNAMIC_BITMAP_SIZE.Height);
    _Canvas.DrawSprite(rect, _BitmapReflection.ToSprite(), ColorU.White);
    _Canvas.DrawRect(rect.Inflate(1, 1), ColorU.LightBlue);

    //draw text information
    _Canvas.DrawTextLayout(_DefaultConsoleFont, _Canvas.Region.Deflate(12, 12), ComposeInfoString(), TextLayout.Default, ColorU.White);

    //draw red background
    _Canvas.DrawRect(_Canvas.Region.Deflate(5, 5), ColorU.White);
    _Canvas.DrawRect(_Canvas.Region.Deflate(7, 7), ColorU.White);
}

Copy Data from Bitmap to Texture

Copying of bitmap data is very simple. Just use SetData method. You can specify additional parameters: source and target region. It could be useful for partial/optimized update of texture.

//each 1/Nth of second add new random semitransparent shape
private void RateTimer_Tick(object sender, EventArgs e) {
    using (Graphics graphics = Graphics.FromImage(_DynamicBitmap)) {
        Rectangle rect = new Rectangle(RandomUtils.NextInt(DYNAMIC_BITMAP_SIZE.Width), RandomUtils.NextInt(DYNAMIC_BITMAP_SIZE.Height), RandomUtils.NextInt(50), RandomUtils.NextInt(50));
        Color color = Color.FromArgb(128, RandomUtils.NextInt(255), RandomUtils.NextInt(255), RandomUtils.NextInt(255));
        Brush brush = new SolidBrush(color);

        switch(RandomUtils.NextInt(2)) {
            case 0:
                graphics.FillEllipse(brush, rect);
                break;
            default:
                graphics.FillRectangle(brush, rect);
                break;
        }
    }
    //update texture from bitmap cache
    _BitmapReflection.SetData(_DynamicBitmap);

    //redraw frame
    UpdateTarget();
}

Freeing Resources

Do not forget to free resources on application form is disposed.

private void WindowsFormsIntegrationForm_Disposed(object sender, EventArgs e) {
    _DefaultConsoleFont.Dispose();
    _BitmapReflection.Dispose();
    _Background.Dispose();
    _Canvas.Dispose();
    _DrawingDevice.Dispose();
}

Last edited Aug 31, 2010 at 6:30 PM by AlexKhomich, version 10

Comments

No comments yet.