PlayStation?Mobile Development Cookbook
上QQ阅读APP看书,第一时间看更新

Loading, displaying, and translating a textured image

This recipe is going to create an application that loads a texture from an image file and displays it centered on the screen. This example is actually rather daunting, throwing quite a bit of information at a new developer. Don't worry if it seems overly complex for now; by the end of the book it will make more sense. If you feel overwhelmed, I recommend you continue the book and revisit this recipe later.

Getting ready

Following the instructions presented in the Creating a simple game loop recipe, create a new solution. I have named mine as Ch1_Example2.

How to do it...

  1. First, we need to add an image file to our project to use as a texture. This can be done by right-clicking our project in the Solution panel and selecting Add | Add Files... from the menu, as shown in the following screenshot:
    How to do it...
  2. Now, we need to tell PSM Studio what to do with this file. Select our newly added image, right-click on it, and select Build Action | Content.
  3. Now, enter the following code:
    using System;
    using System.Collections.Generic;
    
    using Sce.PlayStation.Core;
    using Sce.PlayStation.Core.Environment;
    using Sce.PlayStation.Core.Graphics;
    using Sce.PlayStation.Core.Input;
    
    namespace Ch1_Example2
    {
      public class AppMain
      {
        private static GraphicsContext _graphics;
        private static Texture2D _texture;
        private static VertexBuffer _vertexBuffer;
        private static ShaderProgram _textureShaderProgram;
        private static Matrix4 _localMatrix;
        private static Matrix4 _projectionMatrix;
        private static Matrix4 _viewMatrix;
        private static float _viewportWidth;
        private static float _viewportHeight;
    
        public static void Main (string[] args){
          Initialize ();
    
          while (true) {
            SystemEvents.CheckEvents ();
            Update ();
            Render ();
          }
        }
    
        public static void Initialize (){
          _graphics = new GraphicsContext ();
          _viewportWidth = _graphics.GetFrameBuffer().Width;
          _viewportHeight = _graphics.GetFrameBuffer().Height;
    
          _texture = new Texture2D("/Application/FA-18H.png",false);
          _vertexBuffer = new VertexBuffer(4,VertexFormat.Float3,VertexFormat.Float2);
          _vertexBuffer.SetVertices(0,new float[] {
            1,0,0,
            _texture.Width,0,0,
            _texture.Width,_texture.Height,0,
            0,_texture.Height,0});
    
          _vertexBuffer.SetVertices(1,new float[]{
            0.0f,0.0f,
            1.0f,0.0f,
            1.0f,1.0f,
            0.0f,1.0f});
    
          _textureShaderProgram = new ShaderProgram("/Application/shaders/Texture.cgx");
    
          _projectionMatrix = Matrix4.Ortho(
            0,_viewportWidth,
            0,_viewportHeight,
            0.0f,32768.0f);
    
          _viewMatrix = Matrix4.LookAt(
            new Vector3(0,_viewportHeight,0),
            new Vector3(0,_viewportHeight,1),
            new Vector3(0,-1,0));
    
          _localMatrix = Matrix4.Translation(new Vector3(-_texture.Width/2,-_texture.Height/2,0.0f))
          * Matrix4.Translation(new Vector3(_viewportWidth/2,_viewportHeight/2,0.0f));
    }
    
        public static void Update (){
          var gamePadData = GamePad.GetData (0);
        }
    
        public static void Render (){
          _graphics.SetClearColor (0.0f, 0.0f, 0.0f, 0.0f);
          _graphics.Clear ();
    
          var worldViewProjection = _projectionMatrix * _viewMatrix * _localMatrix;
          _textureShaderProgram.SetUniformValue(0,ref worldViewProjection);
    
          _graphics.SetShaderProgram(_textureShaderProgram);
          _graphics.SetVertexBuffer(0,_vertexBuffer);
          _graphics.SetTexture(0,_texture);
    
          _graphics.DrawArrays(DrawMode.TriangleFan,0,4);
    
          _graphics.SwapBuffers ();
        }
      }
    }

How it works...

Phew! That sure seemed like a lot of code to simply display a single image on screen, didn't it? The truth is, you did a lot more than just load and draw a texture. Let's jump in and look at exactly what we just created.

First, we declared the following variables, in addition to our existing GraphicsContext variable:

  • _texture is our Texture2D object that is going to hold our textured image.
  • _vertexBuffer is a VertexBuffer object that holds the 3D quad geometry we are going to map our texture on.
  • _shaderProgram is a ShaderProgram variable, the texture shader needed to render our texture. The GraphicsContext variable requires at least one. Fortunately, a simple one with the extension .cgx was created for you already by PSM Studio when you created the project.
  • _localMatrix, _projectionMatrix, and _viewMatrix are Matrix4 objects, representing the textured object's position.
  • _viewportWidth and _viewportHeight contain the dimensions of our window.

The bulk of our activity is in the Initialize() method. Once again, we create a GraphicsContext variable, and then store the dimensions of the frame buffer in the _viewportHeight and _viewportWidth variables. Next, we create our Texture2D object, passing the constructor the filename and whether or not we want a mipmap generated.

Next, we create a _vertexBuffer object, which is going to be a fullscreen quad we can draw our texture on. We make two calls to SetVertices(). The first call is defining the x, y, and z float variables that make up the four vertices of the fullscreen quad. The second SetVertices function call is four x and y texture coordinates. Texture coordinates are represented with a value from 0 to 1.

Next, we create our _textureShaderProgram function using the default shader PSM Studio created for us. We will cover shaders in more detail later in this chapter.

Finally, we set up the _projectionMatrix, _viewMatrix, and _localMatrix objects. The projection matrix is an orthographical matrix that represents our screen. The view matrix represents the camera within the world, using Matrix4.LookAt. LookAt(), which requires 3 vectors, the first representing your eye's location in 3D space, the second, the 3D point you are looking at, and the third, the direction where "UP" is, in this case in the Y direction. Finally, the local matrix represents the position of texture, which we want to be centered in the middle of the screen.

Now, let's take a look at the Render() function, where our texture is going to be displayed to the screen. As before, we set the clear color to black and clear the buffer. Next, we generate our worldViewProjection matrix by multiplying our projection, view and local matrices together. We then bind our worldViewProjection matrix to our shader program and then set our shader program to the GraphicsContext variable. We also set our VertexBuffer object and Texture2D object to the GraphicsContext variable. The DrawArrays() call is what ties it all together, using our worldViewMatrix to transform our vertices from our VertexBuffer object and applying our texture map, rendering it all to the active buffer. Finally, we make that buffer visible, which draws it on screen.

Here is our program in action, rendering our sprite centered to the screen:

How it works...

Again, if that seemed overly complex, don't panic! Most of this code only needs to be written once, and you have the option of not working at this low a level if you should choose!

There's more...

Build actions will be executed when your project is compiled, copying the content to the appropriate folder, performing whatever conversions are required. If you are used to XNA, this is similar to the functionality of the content pipeline, but not programmable.

Note

Why is there 3D in my 2D?

The bulk of this example was actually going through the process of faking a 2D environment using 3D. The reason is modern GPUs are optimized to work in 3D. If you look at the code to most modern 2D libraries, they are actually working in 3D. If you were to work with native 2D graphics libraries, your performance would be abysmal.

An explanation of 3D mathematics is beyond the scope of this book, but the Kahn Academy (see http://www.khanacademy.org/) is an excellent free resource with thousands of video tutorials.

Tip

The sprite I used for this example and throughout this book is from a wonderful free sprite library made available by GameDev.net user Prince Eugn. You can find more information and download the sprite pack at http://bit.ly/N7CPtE.