COOKIES

This site may be using cookies to melk you with your own data. I, ben0bi, am not the owner of this web service and I also do not maintain their servers. But the EU and the owner of this service think, that the user (me) has the responsibility to inform the consumer (you), that this website uses cookies. Again: I, ben0bi, NEVER use cookies. I am not responsible for the setup of this web service. I just present some information here and do not intend to spy on you for whatever reason ever. But (also again), I do not host this website nor do I maintain any servers related to this website nor do I benefit from using the cookies maintained from this service. I hereby give the responsibility for using cookies on blogspot back to the owners of blogspot.

Dienstag, 10. März 2015

Part 2A: The Graphics Engine

This is Part 2A of my tutorial series about how to write an emulator.

In this part we will set up some graphics where the emulator can "draw to".

After reading Part 2 you should have created a basic WinPhone & MonoGame project, which we will extend now.

Note: If you downloaded the source from Part 2, you need to move some static methods to the new created GFXEngine.cs file (which is also there, but empty).

The Graphics-Engine needs to draw a texture on the screen, which is scaled to the screen size or an appropriate format (4:3 etc). There needs to be a method which can put all the graphics data from the emulator (intended to be a 1-dimensional Int32 array in the size consoleScreenWidth*consoleScreenHeight) onto this texture.

The texture itself will be the size of the console screen, but scaled to the size of the device (phone) screen. If the emulator changes, it should create a new texture in the appropriate size and reset the scaling factor.

There's more to that: A console can have more pixels than there will be seen. These must be covered or be outside of the device screen.

After this tutorial, you have a texture to draw on, which is scaled to the right size and in portrait mode, rotated about 90 degrees.

It will be filled with test pixels - you don't need to do that, it's only that you see something.

A little note: I am talking about UNSIGNED integers here. When I write Int32, you can assume it's meant UInt32. Unsigned means that there can be only positive numbers, not negative ones. This leaves one bit more for the value itself. Otherwise that bit would be used to determine if it's a positive or negative number.

Implementing

Ok then, first remove all the SpriteBatch-related code in Game1.cs.
We will implement that into our graphics-engine.

Now, lets create a new class called GFXEngine.cs.

public class GFXEngine
{
     // the graphics device from the engine.
     private GraphicsDevice graphicsDevice;

     // a SpriteBatch to draw sprites/textures.
     SpriteBatch spriteBatch;

     // the texture to draw on.
     Texture2D texture;

     // the device screen resolution is stored here.
     public int DeviceScreenWidth { get; private set; }
     public int DeviceScreenHeight { get; private set; }

     // Get device screen resolution
     // You need to call that before the game starts/outside of the game thread.
     public void ActualizeScreenResolution() {}

     // use this as constructor, it's called in a "safe region".
     public void LoadContent(GraphicsDevice gfxDevice) {}

     // the update call from the game engine.
     public void Update(GameTime gameTime) {}

    //the draw call from the game engine.
    public void Draw(GameTime gameTime) {}
}


That class will be extended later. First, we implement it into the engine.

Create an instance of the class in the Static.cs - class.

public class Static
{
      public static GFXEngine GFX = new GFXEngine();
      ...
}

And now call the methods in Game1.cs.
Call Static.GFX.ActualizeScreenResolution(); in the Constructor of Game1.cs.
Call Static.GFX.LoadContent(GraphicsDevice); in the LoadContent-method of Game1.cs.
Call Static.GFX.Update(gameTime); in the Update-method of Game1.cs.
Call Static.GFX.Draw(gameTime); in the Draw-method of Game1.cs.

We have  implemented our GFXEngine into the game and will now extend the class to our needs.

Extending

The MonoGame-screen is in portrait-mode. That means, we need to rotate our texture 90 degrees.
If you managed to run it in "real" landscape mode, you can skip the rotation part and just use 0 at the right position.

Add an enum, the PI-Constant and the RGBAToInt32-methods to your Static.cs-file:

public static class Static
{
    ...
    public enum EColorShift
    {
        RED = 0,
        GREEN = 8,
        BLUE = 16,
        ALPHA = 24
    }

    public const double PI = 3.14159265359;
    ....
    public static UInt32 RGBAToInt32i(int R, int G, int B, int A = 255)
    {
        return RGBAToInt32b((byte)R, (byte)G, (byte)B, (byte)A);
    }

    public static UInt32 RGBAToInt32b(byte R, byte G, byte B, byte A = 255)
    {
        UInt32 c= (UInt32)(R << (int)EColorShift.RED);
        c += (UInt32)(G << (int)EColorShift.GREEN);
        c += (UInt32)(B << (int)EColorShift.BLUE);
        c += (UInt32)(A << (int)EColorShift.ALPHA);
        return c;
    }

    // additional method to get color values back.
    public static byte ColorFromInt32(UInt32 value, EColorShift color)
    {
        // value (bit-)shift right by color, then get last 8 bits (0xFF = 255 = 8 bits all up)
        // e.g: 0xAABBCC & 0xFF = 0xAABBCC & 0x0000FF = 0xCC

        return (byte)((value >> (int)color) & 0xFF);
    }
}

The enum defines the right values for bitshifting later.
If you have another format than RGBA (e.g. AGBR) then you have to change only these values.

We need PI to get the correct radian angle. (I tried with 0.5 and 1.0 but it did'nt work.)

The RGBAToInt32-methods are the main functions to create the color-values for the console screen array. (And the ColorFromInt32-method does the exact opposite - getting color values back from Int32)

It simply pushes all values (ranging from 0-255 / 0x00-0xFF) into an Int32 variable by bitshifting.

Bitshifting works like that: You have x which is 4 bits "wide", the last one is up (1): 0001 => x=1
Shift Left 2 (x = x << 2) means shift all bits two to the left: 0001=>0100 (<--) => x=4
Shift Right 1 (x = x >> 1) means shift all bits one to the right: 0100=>0010 (-->) => x=2

E.g: R=0x11, G=0x22, B=0x33 and A=0xFF will give this hex value into the Int32-variable: 0xFF332211 because A is shifted 24 bits to the left, then B shifted 16 bits to the left and G shifted 8 bits to the left. R remained "where it was".

This methods are used by the emulators and have nothing to do with the graphics engine internals.
(But they are set up to correspond to the texture data format used by the graphics engine.)
The graphics engine must react to this functions, not otherwise.

That's why this methods are in the Static-class and not in the GFXEngine class.

Now, lets extend the GFXEngine-class. First we need some variables and properties:

public class GFXEngine
{
...
      private Vector2 textureOrigin;
      private Vector2 deviceHalfScreenSize;
      private Rectangle consoleRectangle;
      private float scaleFactor = 1.0f;
      // and the rotation for the portrait-to-landscape-hack:
      private const float consoleScreenRotation = (float)(90 * Static.PI / 180);

      public DeviceScreenWidthForConsole { get { return DeviceScreenHeight; }}
      public DeviceScreenHeightForConsole { get { return DeviceScreenWidth; }}
...
}

We could compute the most of them in each frame, but that's not necessary and like that, it saves some performance.

textureOrigin is the position on the texture from where it is accessed from outside (rotated / positioned). It's half the texture size.
deviceHalfScreenSize is the half of the device screen size. No need to compute that in each frame.
consoleRectangle is the rectangle on the texture which will actually be drawn. That is for the cutoff lines.
scaleFactor is used to scale the texture to device screen size. There is no independent scale factor for x and y. It's the same for both.
consoleScreenRotation is the rotation of the texture. It needs to be rotated 90 degrees because it's in portrait mode. If you managed to use real landscape mode, just use 0 here.
DeviceScreenWidthForConsole and DeviceScreenHeightForConsole return the "real" width and height for the console screen. Because it's in portrait mode, this values return the "other" value instead of the "right" one. (Height = DeviceWidth and vice versa.)

ActualizeScreenResolution

Extend the ActualizeScreenResolution()-method:
public void ActualizeScreenResolution()
{
      DeviceScreenWidth = (int)Application.Current.Host.Content.ActualWidth;
      DeviceScreenHeight = (int)Application.Current.Host.Content.ActualHeight;
      deviceHalfScreenSize = new Vector2(DeviceScreenWidth * 0.5, DeviceScreenHeight * 0.5);
}

(It's better to multiplicate instead of dividing (* 0.5 instead of / 2))

This method gets the device screen width and height. If you call that Application.xxx stuff inside the game, there will be an asyncrounous error. The method is called once at startup.

LoadContent

This is somewhat our constructor. Stuff will be created and initialized here for the first time.
public void LoadContent(GraphicsDevice gfxDev)
{
     graphicsDevice = gfxDev;
     spriteBatch = new SpriteBatch(graphicsDevice);

     // create a first texture so there is one. (...and it's used for testing.)
     ResizeTexture(140,100); // for the exact same test image like mine, use size 34x24

     // build the test image.
     BuildTestImage();
}

The methods ResizeTexture and BuildTestImage will follow later. You don't need BuildTestImage, it's only here to see something because otherwise, the texture would be transparent.

Draw

Lets extend the Draw-method.
public void Draw(GameTime gameTime)
{
     // we only need the samplerstate here.
     // needed for pixel-perfect scaling without blur and stuff.
      spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend,
                                    SamplerState.PointClamp,
                                    DephtStencilState.Default,
                                    RasterizerState.CullCounterClockwise);

     // now draw the texture with all given values.
     spriteBatch.Draw(texture,
                           deviceHalfScreenSize,
                           consoleRectangle,
                           Color.White,
                           consoleScreenRotation,
                           textureOrigin,
                           scaleFactor,       // bad that there is no scaling for x / y individually.
                           SpriteEffects.None,
                           0.0f);

     spriteBatch.End();
}

We need to modify the SamplerState in SpriteBatch.Begin so there is no blurry image. All other values are left default. Then we give all the values which are computed in ResizeTexture to SpriteBatch.Draw. SpriteBatch.End must be called to end the draw call.

ResizeTexture

Create the method ResizeTexture(int,int,int,int):
public void ResizeTexture(int width, int height, int cutOffTopLines = 0, int cutOffBottomLines = 0)
{
        // first, create the new texture with given width and height.
        texture = new Texture2D(graphicsDevice, width, height);

        // hcalc is the height minus the cutoff.
        // it's stored in consoleRectangle and used for scaling and origin.
        float hcalc = height - cutOffTopLines - cutOffBottomLines;
        // origin needs to be calculated using hcalc instead of height.
        textureOrigin = new Vector2(width * 0.5f, hcalc * 0.5f);
        // define the shown rectangle on the texture.
        consoleRectangle = new Rectangle(0, cutOffTopLines, width, (int)hcalc);
        // now calculate the scale factor.
        scaleFactor = (float)DeviceScreenHeightForConsole / hcalc;
        // maybe it's a widescreen. (not possible, but why not calculate it?)
        if(width * scaleFactor > DeviceScreenWidthForConsole)
               scaleFactor = (float)DeviceScreenWidthForConsole / width;
}

Everything is explained in the comments above.

This is the basic "graphics engine". All we need to do now, are some methods to put the given array from the console to the texture. For that, we set up some helper functions to modify textures.

"Interface" Methods

I call those "Interface Methods" because this are the methods which the emulators need to interface with the graphics engine.

Create these four methods in GFXEngine.cs:
public class GFXEngine
{
    // set the data for the main (onscreen) texture.
    public void SetMainTexData<T>(T[] data) where T:struct
    {
        ArrayToTexture(data, texture);
    }

    // get the data from the main (onscreen) texture.
    public T[] GetMainTexData<T>() where T:struct
    {
        return TextureToArray<T>(texture);
    }

    // retrieves all colors from a texture in an 1-dimensional {T} array.
    public static T TextureToArray<T>(Texture2D tex) where T:struct
    {
        T[] colors1D = new T[tex.Width * tex.Height];
        tex.GetData(colors1D);
        return colors1D;
    }

    // set an 1-dimensional {T} array to a texture.
    public static void ArrayToTexture<T>(T [] cols, Texture2D tex) where T:struct
    {
         tex.SetData(cols);
    }
}

The T stuff means simply that you can get/put any data type you wish, as long as it's accepted by the texture code itself. You can either use an Xna.Color-Array or an Int32-Array or something else there. ...where T:struct defines the T-variables as not-nullable.

To create an array with the right size, just use the TextureToArray-method.

Int32[] ar = TextureToArray<Int32>(myTex);



Testimage

Last but not least, the test image:

public void BuildTestImage()
{
   Microsoft.Xna.Framework.Color[] c = GetMainTexData<Microsoft.Xna.Framework.Color>();

  // go through all the pixels and color them.
  for(int i=0; i<c.Length; i++)
  {
       // color every second pixel different.
       if(i % 2 == 1)
             c[i] = Microsoft.Xna.Framework.Color.Red;
       else
             c[i] = Microsoft.Xna.Framework.Color.Green;

      // color the first four lines.
      if(i < texture.Width * 4)
         c[i] = Microsoft.Xna.Framework.Color.Yellow;
      // color the first 2 lines.
      if(i < texture.Width * 2)
         c[i] = Microsoft.Xna.Framework.Color.Magenta;

      // color the last two lines
      if(i > (texture.Height - 2) * texture.Width)
         c[i] = Microsoft.Xna.Framework.Color.Magenta;
  }

    // set the new data to the texture.
    SetMainTexData(c);
}

You will see an image like this:


On the left and right there is the background which is actually in the color "Cornflowerblue".
If you have a widescreen image (e.g. 200x100px), the background will be visible on the top and bottom.

The first two lines (magenta, top and bottom) are used to test the cutoff. You can use ResizeTexture(w, h, 2,2) to test that. The yellow line indicates the first two lines of the "real visible" screen. Then, every pixel is either red or green. In the bottom of the image, you should see a green or red pixel in the magenta lines. That one indicates if the image is drawn from left to right.


Download

Download the Source for this Part:
Emu_02_GraphicsEngine.zip from Mirror 1 (OneDrive)
Emu_02_GraphicsEngine.zip from Mirror 2 (homeserver)


That's it with this part. You should now have a "graphics engine" which your emulators can use.

In Part 2B [TBA] we will create the "Sound Engine"/Sound Interface for the emulators to use.

Part 2B: The Sound Engine [TBA]

You can skip the whole Part 2x stuff and just go to Part 3 if you do that on another Engine/Platform.

Montag, 9. März 2015

Part 2: Basic Winphone Setup

This is part 2 in my tutorial series about writing an emulator.
I am doing this with WinPhone 8.1 and MonoGame.

If you don't know what I'm talking about, here is Part 1.

In this section, we will install the right development environment and create a basic game-app.

I take this part first because you and I want to see at least something, not? You can skip that part if you use another engine/platform.

OK, I will use MonoGame, the (open source) successor of XNA.
If you want to use DirectX, you have to use C++ and that's none of my business anymore.

At least now, you should have downloaded and installed MS Visual Studio 2013 Express Update 4 or something similar.

After that, you should install MonoGame.

You can download MonoGame from here:
http://www.monogame.net/downloads/
Use at least V. 3.2 which has templates for MS VS 2013.

With the install also comes OpenAL, which I decided to install because, as mentioned, I don't know nothing about sound programming and if there is an "Open Source Audio Library", why not use that?

Start up Visual Studio and create a new Application using the C#->"MonoGame Windows Phone 8 Project"-Template.

There you have three "main" files:
GamePage.xaml, which is your WinPhone-Layout-file for that page.
GamePage.xaml.cs, which is the source-file corresponding to the layout (file) - where your button-clicks and other UI-stuff will be handled.
Game1.cs, which is the game itself. It runs in it's own thread - you cannot simply use WP-UI-button-clicks and stuff here because that will maybe throw an Asyncronous-Exception. More about that later, when I know how to handle that myself.

The project should compile without errors.

If you get a blank screen, don't worry - that's a common issue with the MonoGame v3.2 template.
Follow this discussion to solve it:
http://community.monogame.net/t/3-2-on-windows-phone-8-just-shows-black-screen/278/12

I just uncommented the alternate grid on the bottom of GamePage.xaml and commented out the main one. Even if it should run now in portrait mode, it seems to be in landscape mode, though.

Some Static Functions

There are some methods which will be used through the whole app. Create a new class called Static.cs and put this methods in it:

using System.Windows;
using System.Windows.Controls;
...

// shows a standard text message.
public static void ShowMsg(string txt)
{
     MessageBox.Show(txt);
}

// go back to the last page or to the main page.
public static void GoBackOrMain(Frame frm)
{
     if(frm.CanGoBack)
          frm.GoBack();
    else
         frm.Navigate(new Uri("/GamePage.xaml", UriKind.Relative);
}


The first method, ShowMsg, is just here to get some info on the screen. You can use that for infos like "Loading failed, please try another ROM" or such stuff which only needs an OK-button.

The second method GoBackOrMain, tries to go back to the last opened page. If it cannot go back, it tries to open the main page. This method is for use with the back button and/or cancel buttons.

In WP 8.1 that works a little bit other but on WP 8.0 that's all you have to do.
That code is not tested yet (navigating).

The Back Button

The back button does not work like in other apps. You cannot use HardwareButtons.BackbuttonPressed += myFunc (WP 8.1?).

Open GamePage.xaml. Click on the events button in the properties window. Search for the event BackbuttonPressed or OnBackbuttonPressed and double click it.

You have now a method for your back button, use it like you want. I want to open a CommandBar with it, but first you should just exit the app with _game.Exit();

Other Stuff

There are other problems which I could not solve until now. I will post it here when I find a solution.
+ Cannot switch tasks, the game just shows the cleared screen after resuming.
+ Cannot even press the switch-task-button and then resume game directly.

Download

Download the Source for this Part:
Emu_01_WP_Setup.zip from Mirror 1 (OneDrive)
Emu_01_WP_Setup.zip from Mirror 2 (homeserver) (direct download)

It has some more static methods and draws a texture to the screen for testing.



In Part 2A we will set up the graphics for our emulators to have a "render device".

Part 2A: The Graphics Engine

You can skip the whole Part 2x stuff and just go to Part 3 if you do that on another Platform/Engine.