{"id":1880,"date":"2023-05-12T00:07:20","date_gmt":"2023-05-12T00:07:20","guid":{"rendered":"https:\/\/writingagame.com\/?p=1880"},"modified":"2026-04-08T17:00:41","modified_gmt":"2026-04-08T17:00:41","slug":"chapter-4-cross-platform-project-structure","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2023\/05\/12\/chapter-4-cross-platform-project-structure\/","title":{"rendered":"Chapter 4. Platform independence"},"content":{"rendered":"\n<p><strong>Tags:<\/strong> <em>Software Architecture, C++ Classes, Cross-Platform Design, Code Decoupling, Platform Abstraction, Reusable Core<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>In the previous 2 chapters, we have successfully &#8220;married&#8221; Android and C++, Windows and OpenGL ES. Now, having 2 C++\/GLES projects for 2 different platforms, let&#8217;s see what is common and what is not.<\/p>\n\n\n\n<p>The difference actually looks frightening, seems like <strong>nothing <\/strong>matches&#8230; Except C++ and GL syntax, which leaves us some hope.<\/p>\n\n\n\n<p>Well, then let\u2019s try to split the code to platform-specific part and to potentially reusable core.<\/p>\n\n\n\n<p>In this chapter we will dissect our GLFW spinning triangle sample. We will move \u201cgame\u201d implementation into a separate class, which we will reuse later in Android version. It will be triangle rendering related code. In order to make it platform-independent we will separate it from platform-specific calls. All environment related code, such as window creation, GL initialization and so on, we will leave in <em>main.cpp<\/em> as is. <\/p>\n\n\n\n<p>1. Start Visual Studio, open <em>C:\\CPP\\a999hello\\pw\\pw.sln<\/em> solution.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>2. Under  <em style=\"font-size: revert;\">pw<\/em>  project add new <strong>filter<\/strong>.<\/p>\n\n\n\n<p>Right-click on <em style=\"font-size: revert;\">pw<\/em>  project -&gt; <em>Add -&gt; New <strong>Filter<\/strong><\/em>. Name &#8211; <strong>xTheApp<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>3. Under <em>xTheApp <\/em>we&#8217;ll add a new class. We won\u2019t use \u201cadd Class\u201d option since it will place files at it\u2019s own discretion, not where we want. We\u2019ll better add it file-by-file.<\/p>\n\n\n\n<p>Right-click on <em>xTheApp <\/em>-&gt; <em>Add -&gt; New <strong>Item<\/strong><\/em>, <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Header File (.h)<\/li>\n\n\n\n<li>Name \u2013 <strong>TheApp.h<\/strong><\/li>\n\n\n\n<li>Change location to <em>C:\\CPP\\<em>a999hello<\/em>\\<\/em><\/li>\n<\/ul>\n\n\n\n<p><strong>Add<\/strong>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Please note: we are saving it in <em><em>a999hello<\/em><\/em> <strong>root <\/strong>folder, accessible for both <em>pw <\/em>and <em>pa <\/em>projects.<\/li>\n<\/ul>\n\n\n\n<p><em>TheApp <\/em>class will consist of 5 methods\/functions and 3 variables, summarized in <em>TheApp.h<\/em>.<\/p>\n\n\n\n<p>Copy and paste following code to <em>TheApp.h<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#pragma once\nclass TheApp\n{\npublic:\n\tint screenSize&#91;2];\n\tfloat screenRatio;\n\tbool bExitApp;\npublic:\n\tint run();\n\tint getReady();\n\tint drawFrame();\n\tint cleanUp();\n\tint onScreenResize(int width, int height);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>4. Now &#8211; implementation:<\/p>\n\n\n\n<p>Right-click on <em>xTheApp <\/em>-&gt; <em>Add -&gt; New <strong>Item<\/strong><\/em>, <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>C++ File (.cpp)<\/li>\n\n\n\n<li>Name \u2013 <strong>TheApp.cpp<\/strong><\/li>\n\n\n\n<li>Location &#8211; <em>C:\\CPP\\<em>a999hello<\/em>\\<\/em><\/li>\n<\/ul>\n\n\n\n<p>Add.<\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;TheApp.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;linmath.h&quot;\nstatic const struct\n{\n    float x, y;\n    float r, g, b;\n} vertices&#91;3] =\n{\n    { -0.6f, -0.4f, 1.f, 0.f, 0.f },\n    {  0.6f, -0.4f, 0.f, 1.f, 0.f },\n    {   0.f,  0.6f, 0.f, 0.f, 1.f }\n};\nstatic const char* vertex_shader_text =\n&quot;#version 320 es\\n&quot;\n&quot;precision lowp float;\\n&quot;\n&quot;uniform mat4 MVP;\\n&quot;\n&quot;in vec3 vCol;\\n&quot;\n&quot;in vec2 vPos;\\n&quot;\n&quot;out vec3 color;\\n&quot;\n&quot;void main()\\n&quot;\n&quot;{\\n&quot;\n&quot;    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\\n&quot;\n&quot;    color = vCol;\\n&quot;\n&quot;}\\n&quot;;\nstatic const char* fragment_shader_text =\n&quot;#version 320 es\\n&quot;\n&quot;precision lowp float;\\n&quot;\n&quot;in vec3 color;\\n&quot;\n&quot;out vec4 FragColor;\\n&quot;\n&quot;void main()\\n&quot;\n&quot;{\\n&quot;\n&quot;    FragColor = vec4(color, 1.0);\\n&quot;\n&quot;}\\n&quot;;\nunsigned int vao_buffer, vertex_buffer, vertex_shader, fragment_shader, program;\nint mvp_location, vpos_location, vcol_location;\nfloat angle_z = 0;\nint TheApp::getReady() {\n    bExitApp = false;\n    glGenVertexArrays(1, &amp;vao_buffer);\n    glBindVertexArray(vao_buffer);\n    glGenBuffers(1, &amp;vertex_buffer);\n    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);\n    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);\n    vertex_shader = glCreateShader(GL_VERTEX_SHADER);\n    glShaderSource(vertex_shader, 1, &amp;vertex_shader_text, NULL);\n    glCompileShader(vertex_shader);\n    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);\n    glShaderSource(fragment_shader, 1, &amp;fragment_shader_text, NULL);\n    glCompileShader(fragment_shader);\n    program = glCreateProgram();\n    glAttachShader(program, vertex_shader);\n    glAttachShader(program, fragment_shader);\n    glLinkProgram(program);\n    mvp_location = glGetUniformLocation(program, &quot;MVP&quot;);\n    vpos_location = glGetAttribLocation(program, &quot;vPos&quot;);\n    vcol_location = glGetAttribLocation(program, &quot;vCol&quot;);\n    glEnableVertexAttribArray(vpos_location);\n    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,\n        sizeof(vertices&#91;0]), (void*)0);\n    glEnableVertexAttribArray(vcol_location);\n    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,\n        sizeof(vertices&#91;0]), (void*)(sizeof(float) * 2));\n    return 1;\n}\nint TheApp::drawFrame() {\n    myPollEvents();\n    mat4x4 m, p, mvp;\n    glClear(GL_COLOR_BUFFER_BIT);\n    angle_z += 0.01f;\n    mat4x4_identity(m);\n    mat4x4_rotate_Z(m, m, angle_z);\n    mat4x4_ortho(p, -screenRatio, screenRatio, -1.f, 1.f, 1.f, -1.f);\n    mat4x4_mul(mvp, p, m);\n    glUseProgram(program);\n    glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*)mvp);\n    glDrawArrays(GL_TRIANGLES, 0, 3);\n    mySwapBuffers();\n    return 1;\n}\nint TheApp::cleanUp() {\n    return 1;\n}\nint TheApp::onScreenResize(int width, int height) {\n    if (screenSize&#91;0] == width &amp;&amp; screenSize&#91;1] == height)\n        return 0;\n    screenSize&#91;0] = width;\n    screenSize&#91;1] = height;\n    screenRatio = (float)width \/ (float)height;\n    glViewport(0, 0, width, height);\n    mylog(&quot; screen size %d x %d\\n&quot;, width, height);\n    return 1;\n}\nint TheApp::run() {\n    getReady();\n    while (!bExitApp) {\n        drawFrame();\n    }\n    cleanUp();\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Please note, that this code doesn\u2019t have platform-specific references (such as GLAD or GLFW), which means that it <strong>can <\/strong>be re-used on another platform, particularly on Android, which we\u2019ll definitely try in the next chapter.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>5. Also (as usual) we need to notify <em>pw <\/em>project where to look for <em>TheApp.h<\/em>.<\/p>\n\n\n\n<p>Right-click on  <em>pw <\/em> project -&gt; Properties, <strong>All Configurations<\/strong>, x64, <em>Configuration Properties -&gt; C\/C++ -&gt; General<\/em>, open <em>Additional Include Directories -&gt; Edit<\/em>, add new line: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>..<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Yes, it\u2019s just 2 dots, which means 1 folder level up (from <em>pw <\/em>application root folder).<\/p>\n\n\n\n<p>Ok, Apply, Ok.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>6. As planned, platform-specific code we&#8217;ll place <strong>outside <\/strong>of <em>TheApp <\/em>class. Particularly, GLAD and GLFW declarations, events handling and swap screen buffers call. Also I added printf-alike &#8220;mylog&#8221; function, which can print debug messages in console window.<\/p>\n\n\n\n<p>Under <em>pw <\/em>project add new filter <strong>xPlatform<\/strong>.<\/p>\n\n\n\n<p>Under <em>xPlatform <\/em>add new item<\/p>\n\n\n\n<p>Header File (.h)<\/p>\n\n\n\n<p>Name: <strong>platform.h<\/strong><\/p>\n\n\n\n<p>Location: <em>C:\\CPP\\p_windows\\<\/em><\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#pragma once\n#include &lt;glad\/glad.h&gt;\nvoid mylog(const char* _Format, ...);\nvoid mySwapBuffers();\nvoid myPollEvents();\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>7. Implementation:<\/p>\n\n\n\n<p>Right-click on <em>xPlatform <\/em>-&gt; <em>Add -&gt; New <strong>Item<\/strong><\/em>, <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>C++ File (.cpp)<\/li>\n\n\n\n<li>Name \u2013 <strong>platform.cpp<\/strong><\/li>\n\n\n\n<li>Location &#8211; <em>C:\\CPP\\<em>p_windows<\/em>\\<\/em><\/li>\n<\/ul>\n\n\n\n<p><strong>Add<\/strong>.<\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;stdarg.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;GLFW\/glfw3.h&gt;\n#include &quot;platform.h&quot;\n#include &quot;TheApp.h&quot;\nextern GLFWwindow* myMainWindow;\nextern TheApp theApp;\nvoid mylog(const char* _Format, ...) {\n#ifdef _DEBUG\n    va_list _ArgList;\n    va_start(_ArgList, _Format);\n    vprintf(_Format, _ArgList);\n    va_end(_ArgList);\n#endif\n};\nvoid mySwapBuffers() {\n    glfwSwapBuffers(myMainWindow);\n}\nvoid myPollEvents() {\n    glfwPollEvents();\n    \/\/check if closing the window\n    theApp.bExitApp = glfwWindowShouldClose(myMainWindow);\n    \/\/check screen size\n    int width, height;\n    glfwGetFramebufferSize(myMainWindow, &amp;width, &amp;height);\n    theApp.onScreenResize(width, height);\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>8. Add new path to <em>pw properties -&gt; C\/C++ -&gt; General -&gt; Additional Include Directories -&gt; Edit<\/em><\/p>\n\n\n\n<p>add new line<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>..\\..\\p_windows<\/em><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>9. <em>main.cpp<\/em>, accordingly, is much shorter now:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [6,7,20,46]; title: ; notranslate\" title=\"\">\n#define GLFW_INCLUDE_NONE\n#include &lt;GLFW\/glfw3.h&gt;\n#include &lt;stdlib.h&gt;\n#include &quot;TheApp.h&quot;\n#include &quot;platform.h&quot;\nstatic void error_callback(int error, const char* description)\n{\n    mylog(&quot;Error: %s\\n&quot;, description);\n}\nstatic void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)\n{\n    if (key == GLFW_KEY_ESCAPE &amp;&amp; action == GLFW_PRESS)\n        glfwSetWindowShouldClose(window, GLFW_TRUE);\n}\nTheApp theApp;\nGLFWwindow* myMainWindow;\nint main(void)\n{\n    glfwSetErrorCallback(error_callback);\n    if (!glfwInit())\n        exit(EXIT_FAILURE);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);\n    myMainWindow = glfwCreateWindow(640, 480, &quot;OurProject&quot;, NULL, NULL);\n    if (!myMainWindow)\n    {\n        glfwTerminate();\n        exit(EXIT_FAILURE);\n    }\n    glfwSetKeyCallback(myMainWindow, key_callback);\n    glfwMakeContextCurrent(myMainWindow);\n    gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress); \/\/gladLoadGL(glfwGetProcAddress);\n    glfwSwapInterval(1);\n    theApp.run();\n    glfwDestroyWindow(myMainWindow);\n    glfwTerminate();\n    exit(EXIT_SUCCESS);\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Replace all code in <em>main.cpp<\/em> by this one.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Please note, that this code does NOT contain any game-specific code, which means that we can re-use it \u201cas is\u201d in our ANY future Windows project.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p><\/p>\n\n\n\n<p>10. Now build and run (green arrow). Works!<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We also can run it in Release configuration, then it will work without Console window.<\/li>\n<\/ul>\n\n\n\n<p>Our next task \u2013 to make it work on Android.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">Tags: Software Architecture, C++ Classes, Cross-Platform Design, Code Decoupling, Platform Abstraction, Reusable Core In the previous 2 chapters, we have successfully &#8220;married&#8221; Android and C++, Windows and OpenGL ES. Now, having 2 C++\/GLES projects for 2 different platforms, let&#8217;s see what is common and what is not. The difference actually looks frightening, seems like nothing [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1880","post","type-post","status-publish","format-standard","hentry","category-cross-platform-3d"],"_links":{"self":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1880","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/comments?post=1880"}],"version-history":[{"count":30,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1880\/revisions"}],"predecessor-version":[{"id":4280,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1880\/revisions\/4280"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=1880"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=1880"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=1880"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}