OpenGL ES 1.1 on Windows 8 and Windows Phone 8.1

    Back in 1998, I tried to make my game with OpenGL. The development hardly reached alpha and was abandoned, but what I especially remembered was how convenient it was to do GL interfaces - orthogonal projection, a couple of transformations, several vertex binders with GL_TRIANGLE_STRIP and we already have a button. And now, after sixteen years and doing mobile game building, I came across the same approach in OpenGL ES 1. *, unless 2D textures without rotations can now be drawn through glDrawTexfOES.
    I supported several projects made on this principle and a little insidious plan was lined up in my head: to make a cross-platform 2D game on mobile with OpenGL ES and C #, and on desktops with regular OpenGL. I did not achieve the goals the first time and there were a lot of problems with it, but as a result, the next project works for me without changing the business logic on iOS, Android, BlackBerry, Windows XP / 7, Mac OS X, Linux, ReactOS, Windows 8, Windows Phone 8.1. I gathered a lot of material on many articles, but this time I’ll talk about the support of Windows Runtime.

    Opentk


    You can argue a lot about the convenience of OpenGL specifically for 2D, to hoax in your throat to convince yourself that shaders and multi-pass rendering are necessary for a full-fledged game, and at the same time find confirmation that the outdated OpenGL ES 1.1 is often implemented precisely at the level of emulation through shaders. This I will leave for Don Quixote and theorists. I was worried that this is the easiest way to write 2D rendering code once and run it on different platforms, without using the monstrous Unity, MonoGame and other engines.
    On iOS and Android, under Xamarin, everything went smoothly, working with GL is done through the OpenTK library with the OpenGL.Graphics.GL11 namespace, the constants and methods are the same on both platforms. On desktops, I decided to use OpenTK.Graphics.OpenGL, i.e. regular desktop OpenGL with C # wrapper. There, in principle, there is no glDrawTexfOES, but without problems you can make a replacement for it and draw two triangles via GL_TRIANGLE_STIP / GL_TRIANGLES and glDrawElements - compared to mobile, performance is more than enough and VBO is not needed here.
    Wrapper example with GL_TRIANGLES
            private static readonly int[] s_textureCropOesTiv = new int[4];
            private static readonly short[] s_indexValues = new short[] { 0, 1, 2, 1, 2, 3 };
            private static readonly float[] s_vertexValues = new float[] { -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f };
            public void glDrawTexfOES(float x, float y, float z, float w, float h)
            {
                glPushMatrix();
                glLoadIdentity();
                glTranslatef(w / 2.0f + x, h / 2.0f + y, 0.0f);
                glScalef(w, -h, 1.0f);
                int[] tiv = s_textureCropOesTiv; // NOTE: clip rectangle, should be set before call
                int[] texW = new int[1];
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, texW);
                int[] texH = new int[1];
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, texH);
                float[] texCoordValues = new float[8];
                float left = 1.0f - (tiv[0] + tiv[2]) / (float)texW[0];
                float bottom = 1.0f - tiv[1] / (float)texH[0];
                float right = 1.0f - tiv[0] / (float)texW[0];
                float top = 1.0f - (tiv[1] + tiv[3]) / (float)texH[0];
                texCoordValues[0] = right;
                texCoordValues[2] = left;
                texCoordValues[4] = right;
                texCoordValues[6] = left;
                texCoordValues[1] = bottom;
                texCoordValues[3] = bottom;
                texCoordValues[5] = top;
                texCoordValues[7] = top;
                glEnableClientState(GL_VERTEX_ARRAY);
                glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                glVertexPointer(2, GL_FLOAT, 0, s_vertexValues);
                glTexCoordPointer(2, GL_FLOAT, 0, texCoordValues);
                glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, s_indexValues);
                glPopMatrix();
            }
    

    Note that copying this code yourself is not worth it - it will not work where there are no GL_TEXTURE_WIDTH / GL_TEXTURE_HEIGHT constants. At the same time, the s_textureCropOesTiv variable must be filled before the call, and the code itself does not flip the viewport along the ordinate.


    Xaml


    A certain amount of magic was needed for the project to run on the current versions of Mono, .Net 2.0-4.5, Wine, and at the same time under ReactOS, but in general, there were no special problems besides the zoo with textures. But the problems started on Windows 8 and Windows Phone, where OpenGL is absent in principle. From the beginning, I tried to solve it with little blood, literally adding my version of glDrawTexfOES, which inside would call something specific for these systems. During the experiments, I used the XAML Canvas element, and in it I drew a Rectangle, which in Brush used the necessary transformation to display only part of the texture.
    Transformation Code in XAML
                    TransformGroup group = new TransformGroup();
                    ScaleTransform scale = new ScaleTransform();
                    scale.ScaleX = (double)texture.PixelWidth / (double)clipRect.Width;
                    scale.ScaleY = (double)texture.PixelHeight / (double)clipRect.Height;
                    group.Children.Add(scale);
                    TranslateTransform translate = new TranslateTransform();
                    translate.X = -scale.ScaleX * (double)clipRect.X / (double)texture.PixelWidth;
                    translate.Y = -scale.ScaleY * (double)clipRect.Y / (double)texture.PixelHeight;
                    group.Children.Add(translate);
                    imageBrush.RelativeTransform = group;
    

    clipRect - a rectangle with cropping parameters, an analog of s_textureCropOesTiv from the example above
    texture - BitmapSource with the texture itself

    This method seems strange, but keep in mind that XAML is often hardware accelerated and fairly fast. I have ported several OpenGL ES mobile games to Windows 8 with this approach and they work reasonably well, but there is no way to change the texture color, as in GL via glColor. Those. in principle, XAML allows you to change the transparency of an element, but you can’t change its Color Tint in any way. For example, if you use white fonts and then are painted in different colors, then with this approach they will remain white.

    In general, the version with XAML is rather doubtful and did not quite correspond to the original plan, and even without the color differentiation of the pantsmodulation, because when the game was 80% ready and was already working on mobile and stationary .Net / Mono, I started looking for more acceptable options for Windows 8. There were a lot of rumors and enthusiasm around the port of the Angle library , but at that time it was already very raw and without C # support. Directly, it was also not possible to work with DirectX directly from C #, and Microsoft itself offers the developer several “simple” ways: remake all C # code in C ++, use a third-party SharpDX library(C # binding over DirectX), or switch to MonoGame. The MonoGame library is an XNA descendant that uses the same SharpDX to display graphics on Windows 8, it is pretty good, but quite specific, and it was too late to switch to it in my project. SharpDX looked no less monstrous, because it draws all the existing DirectX capabilities , although it is pretty close to what I needed. I already began to conduct serious conversations with him with a soldering iron and a manual when I came across the gl2dx project.

    GL2DX


    This library was uploaded by average on CodePlex.several years ago and was no longer updated. This is a C ++ library that declares the same functions as in OpenGL, and internally translates them into D3D11 calls. There was an example in C ++ / CX to the library, which created a XAML page with SwapChainBackgroundPanel and initialized it through D3D11CreateDevice to work with the C ++ part. The project would be good if at least a little out of the prototype stage. Technically, only a few percent of OpenGL methods work in it, and the rest are assertions. On the other hand, it handles 2D texture output, transformation, and simple geometry. At this stage, I took on the library and brought it to the state of the product, which connects to the C # project as a Visual Studio Extension and allows you to write similar code:
    The code
     GL.Enable(All.ColorMaterial);
                GL.Enable(All.Texture2D);
                GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);
                GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 0, 0, 1024, 1024 });
                GL.BindTexture(All.Texture2D, m_textureId1);
                GL.DrawTex(0, - (m_width - m_height) / 2, 0, m_width, m_width);
                for (int i = 0; i < 10; i++)
                {
                    if (i % 2 == 0)
                    {
                        GL.BindTexture(All.Texture2D, m_textureId2);
                        GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 0, 0, 256, 256 });
                    }
                    else
                    {
                        GL.BindTexture(All.Texture2D, m_textureId2);
                        GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 256, 0, 256, 256 });
                    }
                    int aqPadding = 20;
                    int fishSize = 128;
                    int aqWidth = (int)m_width - aqPadding * 2 - fishSize;
                    float x = (Environment.TickCount / (i + 10)) % aqWidth;
                    float alpha = 1.0f;
                    if (x < fishSize)
                        alpha = x / fishSize;
                    else
                        if (x > aqWidth - fishSize)
                            alpha = (aqWidth - x - fishSize) / 128.0f;
                    GL.Color4(1.0f, 1.0f, 1.0f, alpha);
                    GL.DrawTex(x + aqPadding, m_height / 20 * (i + 5), 0, fishSize, fishSize);
                }
    

    PS The code is in the OpenTK call format, which is a little confusing for those who are used to writing glColor4f instead of GL.Color4.
    This thing got the proud name of MetroGL from me.

    Metrogl


    An example in C ++ / CX was transformed into a library on the same birdmodern language, overgrown with a large number of additional functions, and C ++ received the implementation of many OpenGL methods, blending, optimization of the internal VertexBuilder, loading arbitrary images and DDS textures, and most importantly - an exact simulation of glDrawTexfOES, giving 1v1 the same picture as on OpenGL ES, but at the same time connecting sequential operations with one texture into a single DrawCall. Something had to be brought up with a file, the code itself is dirty in some places (both before and after me), and to create a VSIX extension, you need to manually rebuild the project for each architecture (x86, x64, ARM) and only then build the VSIX project. The main thing is that if you have OpenGL ES 1. * code with a 2D interface or not complex 3D, then with this library you can use it directly from C # without thinking about the internals, C ++ code, D3D11 contexts and other nasty thingsjoys. At the same time, an example was made with the fish on the right and the code from under the kata. Of course, if you have OpenGL 2.0+ code with shaders and extensions, then there will be no talk about any porting.
    Another unpleasant moment is that I have no mood and desire to bring the library to the level of 50-100% compatibility with OpenGL, which means that it will have to be sharpened on your own for your specific tasks. Fortunately, all the code has been posted on github, and so far I have not disappeared anywhere and will be happy to commit or generally want to shoulder this burden. The library is built under Windows 8.0 and Windows Phone 8.1, for VSIX you may need a non-Express version of Visual Studio.

    Epilogue


    Well, in the end, a little about games. I have 100% finished my project, and it was the combination of C # and OpenGL that made it possible to make high-level code completely unchangeable - it is a library without a single define that does not use any system calls. Then comes the mid-level code: drawing through OpenGL in 2D, with rotation, transformation and color modulation, here the code is slightly different on different platforms - different textures, data is stored differently. The low-level part is already different for each platform, this is creating a window, initializing the context, outputting sound. In any case, the nine platforms listed at the beginning of the article really work, and while C # in conjunction with OpenGL cannot yet be used on the web or on Firefox OS, but is it not a glimpse of a cross-platform future, gentlemen?



    Also popular now: