{"id":647,"date":"2021-12-07T04:32:02","date_gmt":"2021-12-07T04:32:02","guid":{"rendered":"https:\/\/writingagame.com\/?p=647"},"modified":"2026-03-20T23:12:24","modified_gmt":"2026-03-20T23:12:24","slug":"chapter-20-modeler-round-shapes","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2021\/12\/07\/chapter-20-modeler-round-shapes\/","title":{"rendered":"Chapter 20. Modeler. Curved surfaces"},"content":{"rendered":"\n<p>Our current rectangular model does not allow us to appreciate all the beauty of the Phong shader, in particular &#8211; the glares. For that we&#8217;ll need something more round-shaped. Let&#8217;s extend our Modeler accordingly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Windows<\/h2>\n\n\n\n<p>1. Start VS. Open <em>C:\\CPP\\<em>a997modeler<\/em>\\p_windows\\p_windows.sln<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>First &#8211; we&#8217;ll need to extend <em>VirtualShape.h<\/em>.<\/p>\n\n\n\n<p>2. Open <em>VirtualShape.h<\/em> and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [12,13,14,15,16,17]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;platform.h&quot;\n\nclass VirtualShape\n{\npublic:\n\tchar shapeType&#91;20] = &quot;box&quot;;\n\tfloat whl&#91;3] = { 0 }; \/\/width\/height\/length (x,y,z sizes\/dimensions)\n\tint sections&#91;3] = { 1,1,1 }; \/\/number of sections for each axis\n\tint sectionsR = 1; \/\/number of radial sections\n\t\/\/side extensions\n\tfloat extU = 0; \/\/up\n\tfloat extD = 0; \/\/down\n\tfloat extL = 0; \/\/left\n\tfloat extR = 0; \/\/right\n\tfloat extF = 0; \/\/front\n\tfloat extB = 0; \/\/back\n\npublic:\n\tvoid setShapeType(std::string needType) { setShapeType(this, needType); };\n\tstatic void setShapeType(VirtualShape* pVS, std::string needType) { myStrcpy_s(pVS-&gt;shapeType, 20, (char*)needType.c_str()); };\n\tvoid setExt(float v) { setExt(this, v); };\n\tvoid setExtX(float v) { setExtX(this, v); };\n\tvoid setExtY(float v) { setExtY(this, v); };\n\tvoid setExtZ(float v) { setExtZ(this, v); };\n\tstatic void setExt(VirtualShape* pVS, float v) { pVS-&gt;extU = v; pVS-&gt;extD = v; pVS-&gt;extL = v; pVS-&gt;extR = v; pVS-&gt;extF = v; pVS-&gt;extB = v; };\n\tstatic void setExtX(VirtualShape* pVS, float v) { pVS-&gt;extL = v; pVS-&gt;extR = v; };\n\tstatic void setExtY(VirtualShape* pVS, float v) { pVS-&gt;extU = v; pVS-&gt;extD = v; };\n\tstatic void setExtZ(VirtualShape* pVS, float v) { pVS-&gt;extF = v; pVS-&gt;extB = v; };\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>New concept here is &#8211; &#8220;extensions&#8221;. They are needed for shapes with rounded corners and edges:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c20\/01.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Another change here &#8211; <em>char* shapeType<\/em> instead of <em>std::string<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Have new <strong>platform-specific<\/strong> function here &#8211; <em>myStrcpy_s()<\/em>, char array copy. Same situation as with <em>myFopen_s()<\/em>. Another platform-specific function we will need soon is <em>myMkDir()<\/em>, making a directory.<\/p>\n\n\n\n<p>3. Open <em>platform.h<\/em> and replace code by<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [14,15]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &lt;glad\/glad.h&gt;\n#include &lt;stdio.h&gt;\n\ntypedef unsigned _int64 myUint64;\ntypedef unsigned _int32 myUint32;\ntypedef unsigned _int16 myUint16;\ntypedef unsigned _int8  myUint8;\n\nvoid mylog(const char* _Format, ...);\nvoid mySwapBuffers();\nvoid myPollEvents();\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode);\nint myMkDir(const char* outPath);\nvoid myStrcpy_s(char* dst, int maxSize, const char* src);\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>4. Open <em>platform.cpp<\/em> and replace code by <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [6,7,35,45]; 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;TheGame.h&quot;\n#include &lt;direct.h&gt; \/\/myMkDir: mkdir\n#include &lt;sys\/stat.h&gt; \/\/myMkDir: if file esists\n\nextern GLFWwindow* myMainWindow;\nextern TheGame theGame;\n\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    theGame.bExitGame = glfwWindowShouldClose(myMainWindow);\n    \/\/check screen size\n    int width, height;\n    glfwGetFramebufferSize(myMainWindow, &amp;width, &amp;height);\n    theGame.onScreenResize(width, height);\n}\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode) {\n    return fopen_s(pFile, filePath, mode);\n}\nint myMkDir(const char* outPath) {\n    struct stat info;\n    if (stat(outPath, &amp;info) == 0)\n        return 0; \/\/exists already\n    int status = _mkdir(outPath);\n    if (status == 0)\n        return 1; \/\/Successfully created\n    mylog(&quot;ERROR creating, status=%d\\n&quot;, status);\n    return -1;\n}\nvoid myStrcpy_s(char* dst, int maxSize, const char* src) {\n    strcpy_s(dst, maxSize, src);\n    \/\/fill tail by zeros\n    int strLen = strlen(dst);\n    if (strLen &lt; maxSize)\n        for (int i = strLen; i &lt; maxSize; i++)\n            dst&#91;i] = 0;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><strong>ModelBuilder <\/strong>class becomes dangerously big. We will split it in 2 layers. 1-st layer &#8211; basic functionality, we&#8217;ll call it  <strong>ModelBuilder1base<\/strong>. <\/p>\n\n\n\n<p>Only 1 new function &#8211; <em>moveGroupDg(\u2026)<\/em> &#8211; shifts and rotates vertex group (added to place extensions to their places)<\/p>\n\n\n\n<p>5. Under <em>modeler <\/em>add new Header File (.h)  <strong>ModelBuilder1base.h<\/strong><\/p>\n\n\n\n<p>Location: <em>C:\\CPP\\engine\\modeler\\<\/em><\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [38]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &lt;string&gt;\n#include &lt;vector&gt;\n#include &quot;Vertex01.h&quot;\n#include &quot;Triangle01.h&quot;\n#include &quot;VirtualShape.h&quot;\n#include &quot;Group01.h&quot;\n#include &quot;Material.h&quot;\n#include &quot;GameSubj.h&quot;\n\nclass ModelBuilder1base\n{\npublic:\n\tstd::vector&lt;Vertex01*&gt; vertices;\n\tstd::vector&lt;Triangle01*&gt; triangles;\n\tstd::vector&lt;VirtualShape*&gt; vShapesStack;\n\tstd::vector&lt;Group01*&gt; groupsStack;\n\tstd::vector&lt;Material*&gt; materialsList;\n\tstd::vector&lt;int&gt; subjNumbersList;\n\tint usingSubjN = -1;\n\tint usingMaterialN = -1;\n\tGroup01* pCurrentGroup = NULL;\npublic:\n\tvirtual ~ModelBuilder1base();\n\tint useSubjN(int subjN) { return useSubjN(this, subjN); };\n\tstatic int useSubjN(ModelBuilder1base* pMB, int subjN);\n\tint useMaterial(Material* pMT) { return useMaterial(this, pMT); };\n\tstatic int useMaterial(ModelBuilder1base* pMB, Material* pMT);\n\tstatic void lockGroup(ModelBuilder1base* pMB);\n\tstatic void releaseGroup(ModelBuilder1base* pMB);\n\tstatic int addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz);\n\tstatic int add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n);\n\tstatic int addTriangle(ModelBuilder1base* pMB, int n0, int n1, int n2);\n\tint buildDrawJobs(std::vector&lt;GameSubj*&gt; gameSubjs) { return buildDrawJobs(this, gameSubjs); };\n\tstatic int buildDrawJobs(ModelBuilder1base* pMB, std::vector&lt;GameSubj*&gt; gameSubjs);\n\tstatic int rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector&lt;Vertex01*&gt; allVertices, std::vector&lt;Vertex01*&gt; useVertices, std::vector&lt;Triangle01*&gt; useTriangles);\n\tstatic int buildSingleDrawJob(Material* pMT, std::vector&lt;Vertex01*&gt; useVertices, std::vector&lt;Triangle01*&gt; useTriangles);\n\tstatic int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>6. Under <em>modeler <\/em>add new C++ File (.cpp)  <strong>ModelBuilder1base.cpp<\/strong><\/p>\n\n\n\n<p>Location: <em>C:\\CPP\\engine\\modeler\\<\/em><\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [307]; title: ; notranslate\" title=\"\">\n#include &quot;ModelBuilder1base.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;DrawJob.h&quot;\n#include &quot;Shader.h&quot;\n\nextern float degrees2radians;\n\nModelBuilder1base::~ModelBuilder1base() {\n\t\/\/clear all vectors\n\tint itemsN = vertices.size();\n\tfor (int i = 0; i &lt; itemsN; i++)\n\t\tdelete vertices.at(i);\n\tvertices.clear();\n\n\titemsN = triangles.size();\n\tfor (int i = 0; i &lt; itemsN; i++)\n\t\tdelete triangles.at(i);\n\ttriangles.clear();\n\n\titemsN = vShapesStack.size();\n\tfor (int i = 0; i &lt; itemsN; i++)\n\t\tdelete vShapesStack.at(i);\n\tvShapesStack.clear();\n\n\titemsN = groupsStack.size();\n\tfor (int i = 0; i &lt; itemsN; i++)\n\t\tdelete groupsStack.at(i);\n\tgroupsStack.clear();\n\n\titemsN = materialsList.size();\n\tfor (int i = 0; i &lt; itemsN; i++)\n\t\tdelete materialsList.at(i);\n\tmaterialsList.clear();\n\n\tsubjNumbersList.clear();\n}\nint ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {\n\tpMB-&gt;usingSubjN = subjN;\n\tint itemsN = pMB-&gt;subjNumbersList.size();\n\tbool newN = true;\n\tif (itemsN &gt; 0)\n\t\tfor (int i = 0; i &lt; itemsN; i++)\n\t\t\tif (pMB-&gt;subjNumbersList.at(i) == subjN) {\n\t\t\t\tnewN = false;\n\t\t\t\tbreak;\n\t\t\t}\n\tif (newN)\n\t\tpMB-&gt;subjNumbersList.push_back(subjN);\n\treturn subjN;\n}\nint ModelBuilder1base::useMaterial(ModelBuilder1base* pMB, Material* pMT) {\n\tint itemsN = pMB-&gt;materialsList.size();\n\tif (itemsN &gt; 0)\n\t\tfor (int i = 0; i &lt; itemsN; i++)\n\t\t\tif (memcmp(pMB-&gt;materialsList.at(i), pMT, sizeof(Material)) == 0) {\n\t\t\t\tpMB-&gt;usingMaterialN = i;\n\t\t\t\treturn i;\n\t\t\t}\n\t\/\/if here - add new material to the list\n\tpMB-&gt;usingMaterialN = itemsN;\n\t\/\/create a copy of new Material and add to the list\n\tMaterial* pMTnew = new Material(*pMT);\n\tpMB-&gt;materialsList.push_back(pMTnew);\n\treturn itemsN;\n}\nint ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {\n\t\/\/indexes: NorthWest, NorthEast, SouthWest,SouthEast\n\tif (n % 2 == 0) { \/\/even number\n\t\taddTriangle(pMB, nNW, nSW, nNE);\n\t\taddTriangle(pMB, nNE, nSW, nSE);\n\t}\n\telse { \/\/odd number\n\t\taddTriangle(pMB, nNW, nSE, nNE);\n\t\taddTriangle(pMB, nNW, nSW, nSE);\n\t}\n\treturn pMB-&gt;triangles.size() - 1;\n}\nint ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {\n\tTriangle01* pTR = new Triangle01();\n\tpMB-&gt;triangles.push_back(pTR);\n\tpTR-&gt;idx&#91;0] = i0;\n\tpTR-&gt;idx&#91;1] = i1;\n\tpTR-&gt;idx&#91;2] = i2;\n\tpTR-&gt;subjN = pMB-&gt;usingSubjN;\n\tpTR-&gt;materialN = pMB-&gt;usingMaterialN;\n\treturn pMB-&gt;triangles.size() - 1;\n}\nvoid ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {\n\tif (pMB-&gt;pCurrentGroup != NULL)\n\t\tpMB-&gt;groupsStack.push_back(pMB-&gt;pCurrentGroup);\n\tpMB-&gt;pCurrentGroup = new Group01();\n\tpMB-&gt;pCurrentGroup-&gt;fromVertexN = pMB-&gt;vertices.size();\n\tpMB-&gt;pCurrentGroup-&gt;fromTriangleN = pMB-&gt;triangles.size();\n}\nvoid ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {\n\tdelete pMB-&gt;pCurrentGroup;\n\tif (pMB-&gt;groupsStack.size() &gt; 0) {\n\t\tpMB-&gt;pCurrentGroup = pMB-&gt;groupsStack.back();\n\t\tpMB-&gt;groupsStack.pop_back();\n\t}\n\telse\n\t\tpMB-&gt;pCurrentGroup = NULL;\n}\nint ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {\n\tVertex01* pVX = new Vertex01();\n\tpMB-&gt;vertices.push_back(pVX);\n\tpVX-&gt;aPos&#91;0] = kx;\n\tpVX-&gt;aPos&#91;1] = ky;\n\tpVX-&gt;aPos&#91;2] = kz;\n\t\/\/normal\n\tpVX-&gt;aNormal&#91;0] = nx;\n\tpVX-&gt;aNormal&#91;1] = ny;\n\tpVX-&gt;aNormal&#91;2] = nz;\n\tpVX-&gt;subjN = pMB-&gt;usingSubjN;\n\tpVX-&gt;materialN = pMB-&gt;usingMaterialN;\n\treturn pMB-&gt;vertices.size() - 1;\n}\nint ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector&lt;GameSubj*&gt; gameSubjs) {\n\tint totalSubjsN = pMB-&gt;subjNumbersList.size();\n\tif (totalSubjsN &lt; 1) {\n\t\tpMB-&gt;subjNumbersList.push_back(-1);\n\t\ttotalSubjsN = 1;\n\t}\n\tint totalMaterialsN = pMB-&gt;materialsList.size();\n\tif (totalSubjsN &lt; 2 &amp;&amp; totalMaterialsN &lt; 2) {\n\t\t\/\/simple single DrawJob\n\t\tMaterial* pMT = pMB-&gt;materialsList.at(0);\n\t\tGameSubj* pGS = NULL;\n\t\tint gsN = pMB-&gt;subjNumbersList.at(0);\n\t\tif (gsN &gt;= 0)\n\t\t\tpGS = gameSubjs.at(gsN);\n\t\tif (pGS != NULL)\n\t\t\tpGS-&gt;djStartN = DrawJob::drawJobs.size();\n\t\tbuildSingleDrawJob(pMT, pMB-&gt;vertices, pMB-&gt;triangles);\n\t\tif (pGS != NULL)\n\t\t\tpGS-&gt;djTotalN = DrawJob::drawJobs.size() - pGS-&gt;djStartN;\n\t\treturn 1;\n\t}\n\tint totalVertsN = pMB-&gt;vertices.size();\n\tint totalTrianglesN = pMB-&gt;triangles.size();\n\t\/\/clear flags\n\tfor (int vN = 0; vN &lt; totalVertsN; vN++) {\n\t\tVertex01* pVX = pMB-&gt;vertices.at(vN);\n\t\tpVX-&gt;flag = 0;\n\t}\n\tfor (int tN = 0; tN &lt; totalTrianglesN; tN++) {\n\t\tTriangle01* pTR = pMB-&gt;triangles.at(tN);\n\t\tpTR-&gt;flag = 0;\n\t}\n\tint addedDJs = 0;\n\tfor (int sN = 0; sN &lt; totalSubjsN; sN++) {\n\t\tGameSubj* pGS = NULL;\n\t\tint gsN = pMB-&gt;subjNumbersList.at(sN);\n\t\tif (gsN &gt;= 0)\n\t\t\tpGS = gameSubjs.at(gsN);\n\t\tif (pGS != NULL)\n\t\t\tpGS-&gt;djStartN = DrawJob::drawJobs.size();\n\t\tfor (int mtN = 0; mtN &lt; totalMaterialsN; mtN++) {\n\t\t\tMaterial* pMT = pMB-&gt;materialsList.at(mtN);\n\t\t\tstd::vector&lt;Vertex01*&gt; useVertices;\n\t\t\tstd::vector&lt;Triangle01*&gt; useTriangles;\n\t\t\tfor (int vN = 0; vN &lt; totalVertsN; vN++) {\n\t\t\t\tVertex01* pVX = pMB-&gt;vertices.at(vN);\n\t\t\t\tif (pVX-&gt;flag != 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pVX-&gt;subjN != gsN)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pVX-&gt;materialN != mtN)\n\t\t\t\t\tcontinue;\n\t\t\t\t\/\/if here - make a copy\n\t\t\t\tVertex01* pVX2 = new Vertex01(*pVX);\n\t\t\t\tuseVertices.push_back(pVX2);\n\t\t\t\tpVX2-&gt;altN = vN;\n\t\t\t\tpVX-&gt;flag = 1;\n\t\t\t\tif (pVX-&gt;endOfSequence &gt; 0) {\n\t\t\t\t\trearrangeArraysForDrawJob(pMB, pMB-&gt;vertices, useVertices, useTriangles);\n\t\t\t\t\tbuildSingleDrawJob(pMT, useVertices, useTriangles);\n\t\t\t\t\taddedDJs++;\n\t\t\t\t\t\/\/clear and proceed to next sequence\n\t\t\t\t\tint useVerticesN = useVertices.size();\n\t\t\t\t\tfor (int i = 0; i &lt; useVerticesN; i++)\n\t\t\t\t\t\tdelete useVertices.at(i);\n\t\t\t\t\tuseVertices.clear();\n\t\t\t\t}\n\t\t\t}\n\t\t\tint useVerticesN = useVertices.size();\n\t\t\tif (useVerticesN &lt; 1)\n\t\t\t\tcontinue; \/\/to next material\n\t\t\t\/\/pick triangles\n\t\t\tfor (int tN = 0; tN &lt; totalTrianglesN; tN++) {\n\t\t\t\tTriangle01* pTR = pMB-&gt;triangles.at(tN);\n\t\t\t\tif (pTR-&gt;flag != 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pTR-&gt;subjN != gsN)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pTR-&gt;materialN != mtN)\n\t\t\t\t\tcontinue;\n\t\t\t\t\/\/if here - make a copy\n\t\t\t\tTriangle01* pTR2 = new Triangle01(*pTR);\n\t\t\t\tuseTriangles.push_back(pTR2);\n\t\t\t\tpTR-&gt;flag = 1;\n\t\t\t}\n\t\t\trearrangeArraysForDrawJob(pMB, pMB-&gt;vertices, useVertices, useTriangles);\n\t\t\tbuildSingleDrawJob(pMT, useVertices, useTriangles);\n\t\t\taddedDJs++;\n\t\t\t\/\/clear all for next material\n\t\t\tfor (int i = 0; i &lt; useVerticesN; i++)\n\t\t\t\tdelete useVertices.at(i);\n\t\t\tuseVertices.clear();\n\t\t\tint useTrianglesN = useTriangles.size();\n\t\t\tfor (int i = 0; i &lt; useTrianglesN; i++)\n\t\t\t\tdelete useTriangles.at(i);\n\t\t\tuseTriangles.clear();\n\t\t}\n\t\tif (pGS != NULL)\n\t\t\tpGS-&gt;djTotalN = DrawJob::drawJobs.size() - pGS-&gt;djStartN;\n\t}\n\treturn addedDJs;\n}\nint ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector&lt;Vertex01*&gt; useVertices, std::vector&lt;Triangle01*&gt; useTriangles) {\n\tint totalVertsN = useVertices.size();\n\tif (totalVertsN &lt; 1)\n\t\treturn 1;\n\tDrawJob* pDJ = new DrawJob();\n\t\/\/copy material to DJ\n\tmemcpy(&amp;pDJ-&gt;mt, pMT, sizeof(Material));\n\t\/\/calculate VBO element size (stride) and variables offsets in VBO\n\tint VBOid = DrawJob::newBufferId();\n\tint stride = 0;\n\tpDJ-&gt;setDesirableOffsets(&amp;stride, pDJ-&gt;mt.shaderN, VBOid);\n\t\/\/create an array for VBO\n\tint bufferSize = totalVertsN * stride;\n\tfloat* vertsBuffer = new float&#91;bufferSize];\n\t\/\/fill vertsBuffer\n\tShader* pSh = Shader::shaders.at(pDJ-&gt;mt.shaderN);\n\tint floatSize = sizeof(float);\n\tfor (int vN = 0; vN &lt; totalVertsN; vN++) {\n\t\tVertex01* pVX = useVertices.at(vN);\n\t\tint idx = vN * stride \/ floatSize;\n\t\t\/\/pick data from vertex and move to the buffer\n\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aPos.offset \/ floatSize], pVX-&gt;aPos, 3 * floatSize);\n\t\tif (pSh-&gt;l_aNormal &gt;= 0) \/\/normal\n\t\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aNormal.offset \/ floatSize], pVX-&gt;aNormal, 3 * floatSize);\n\t\tif (pSh-&gt;l_aTuv &gt;= 0) \/\/attribute TUV (texture coordinates)\n\t\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aTuv.offset \/ floatSize], pVX-&gt;aTuv, 2 * floatSize);\n\t\tif (pSh-&gt;l_aTuv2 &gt;= 0) \/\/attribute TUV2 (normal maps)\n\t\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aTuv2.offset \/ floatSize], pVX-&gt;aTuv2, 2 * floatSize);\n\t\tif (pSh-&gt;l_aTangent &gt;= 0)\n\t\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aTangent.offset \/ floatSize], pVX-&gt;aTangent, 3 * floatSize);\n\t\tif (pSh-&gt;l_aBinormal &gt;= 0)\n\t\t\tmemcpy(&amp;vertsBuffer&#91;idx + pDJ-&gt;aBinormal.offset \/ floatSize], pVX-&gt;aBinormal, 3 * floatSize);\n\t}\n\t\/\/buffer is ready, create VBO\n\tglBindBuffer(GL_ARRAY_BUFFER, VBOid);\n\tglBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);\n\tdelete&#91;] vertsBuffer;\n\tpDJ-&gt;pointsN = totalVertsN;\n\n\tint totalTrianglesN = useTriangles.size();\n\tif (totalTrianglesN &gt; 0) {\n\t\t\/\/create EBO\n\t\tint totalIndexesN = totalTrianglesN * 3;\n\t\t\/\/create buffer\n\t\tGLushort* indexBuffer = new GLushort&#91;totalIndexesN];\n\t\tfor (int tN = 0; tN &lt; totalTrianglesN; tN++) {\n\t\t\tTriangle01* pTR = useTriangles&#91;tN];\n\t\t\tint idx = tN * 3;\n\t\t\tindexBuffer&#91;idx + 0] = (GLushort)pTR-&gt;idx&#91;0];\n\t\t\tindexBuffer&#91;idx + 1] = (GLushort)pTR-&gt;idx&#91;1];\n\t\t\tindexBuffer&#91;idx + 2] = (GLushort)pTR-&gt;idx&#91;2];\n\t\t}\n\t\t\/\/buffer is ready, create IBO\n\t\tpDJ-&gt;glEBOid = DrawJob::newBufferId();\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ-&gt;glEBOid);\n\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);\n\t\tdelete&#91;] indexBuffer;\n\t\tpDJ-&gt;pointsN = totalIndexesN;\n\t}\n\t\/\/create and fill vertex attributes array (VAO)\n\tpDJ-&gt;buildVAO();\n\treturn 1;\n}\nint ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector&lt;Vertex01*&gt; allVertices, std::vector&lt;Vertex01*&gt; useVertices, std::vector&lt;Triangle01*&gt; useTriangles) {\n\tint totalTrianglesN = useTriangles.size();\n\tif (totalTrianglesN &lt; 1)\n\t\treturn 0;\n\tint totalVerticesN = useVertices.size();\n\t\/\/save new vertices order in original vertices array\n\t\/\/since triangles indices refer to original vertices order\n\tfor (int i = 0; i &lt; totalVerticesN; i++) {\n\t\tVertex01* pVX1 = useVertices.at(i);\n\t\tVertex01* pVX0 = allVertices.at(pVX1-&gt;altN);\n\t\tpVX0-&gt;altN = i;\n\t}\n\t\/\/replace triangle original indices by new numbers saved in original vertices altN\n\tfor (int tN = 0; tN &lt; totalTrianglesN; tN++) {\n\t\tTriangle01* pTR = useTriangles.at(tN);\n\t\tfor (int i = 0; i &lt; 3; i++) {\n\t\t\tVertex01* pVX0 = allVertices.at(pTR-&gt;idx&#91;i]);\n\t\t\tpTR-&gt;idx&#91;i] = pVX0-&gt;altN;\n\t\t}\n\t}\n\treturn 1;\n}\n\nint ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {\n\t\/\/moves and rotates vertex group\n\t\/\/rotation angles are set in degrees\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_translate(transformMatrix, kX, kY, kZ);\n\t\/\/rotation order: Z-X-Y\n\tif (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);\n\tif (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);\n\tif (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);\n\n\tint vertsN = pMB-&gt;vertices.size();\n\tfor (int i = pMB-&gt;pCurrentGroup-&gt;fromVertexN; i &lt; vertsN; i++) {\n\t\tVertex01* pVX = pMB-&gt;vertices.at(i);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aPos, transformMatrix, pVX-&gt;aPos, 1);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aNormal, transformMatrix, pVX-&gt;aNormal, 0);\n\t}\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Changes in <strong>ModelBuilder <\/strong>class:<\/p>\n\n\n\n<p>a) Now it inherits  <em>ModelBuilder1base<\/em>. <\/p>\n\n\n\n<p>b) New  <em>ModelBuilder<\/em>&#8216;s functions:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>buildFace(\u2026)<\/em> &#8211; dispatches call depending on <em>VirtualShape <\/em>type<\/li><li><em>buildBoxFace(\u2026)<\/em> &#8211; extended to handle &#8220;extensions&#8221;<\/li><li><em>boxFacePlain(\u2026)<\/em> &#8211; draws simple rectangular box face<\/li><li><em>boxFaceTank(\u2026)<\/em> &#8211; draws box face with rounded edges and corners<\/li><li><em>cylinderWrap(\u2026)<\/em> &#8211; draws cylinder sector (for box edges extensions)<\/li><li><em>capWrap(\u2026)<\/em> &#8211; draws &#8220;cap&#8221; (hemi-sphere) sector (for box corners extensions)<\/li><\/ul>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c20\/02.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c20\/03.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>7. Open <em>ModelBuilder.h<\/em> and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [2,4]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;ModelBuilder1base.h&quot;\n\nclass ModelBuilder : public ModelBuilder1base\n{\npublic:\n\tvirtual ~ModelBuilder();\n\tstatic int buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);\n\tstatic int capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p> 8. Open <em>ModelBuilder.cpp<\/em> and replace code by: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;ModelBuilder.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;DrawJob.h&quot;\n#include &quot;Shader.h&quot;\n\nextern float degrees2radians;\n\nModelBuilder::~ModelBuilder() {\n}\n\nint ModelBuilder::buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\tif (strstr(pVS-&gt;shapeType, &quot;box&quot;) == pVS-&gt;shapeType)\n\t\treturn buildBoxFace(pMB, applyTo, pVS);\n\treturn -1;\n}\nint ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\t\/\/this code is for simple box\n\tVirtualShape vs; \/\/face VS, \n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tvs.sectionsR = pVS-&gt;sectionsR;\n\t\/\/rotate desirable side to face us. \n\tif (applyTo.find(&quot;front&quot;) == 0) {\n\t\t\/\/Side &lt;front&gt; is facing us as is.\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;0];\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;1];\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;0];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extF;\n\t\tvs.extL = pVS-&gt;extL;\n\t\tvs.extR = pVS-&gt;extR;\n\t\tvs.extU = pVS-&gt;extU;\n\t\tvs.extD = pVS-&gt;extD;\n\t\t\/\/define how to move\/place generated face back to the VirtualShape\n\t\t\/\/just shift closer to us by length\/2\n\t\tmat4x4_translate(transformMatrix, 0, 0, pVS-&gt;whl&#91;2] \/ 2);\n\t}\n\telse if (applyTo.find(&quot;back&quot;) == 0) {\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;0];\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;1];\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;0];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extB;\n\t\tvs.extL = pVS-&gt;extR;\n\t\tvs.extR = pVS-&gt;extL;\n\t\tvs.extU = pVS-&gt;extU;\n\t\tvs.extD = pVS-&gt;extD;\n\t\t\/\/rotate 180 degrees around Y and shift farther from us by half-length\n\t\tmat4x4_translate(transformMatrix, 0, 0, -pVS-&gt;whl&#91;2] \/ 2);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 180);\n\t}\n\telse if (applyTo.find(&quot;left&quot;) == 0) {\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;2]; \/\/width = original length\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;1];\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;2];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extL;\n\t\tvs.extL = pVS-&gt;extB;\n\t\tvs.extR = pVS-&gt;extF;\n\t\tvs.extU = pVS-&gt;extU;\n\t\tvs.extD = pVS-&gt;extD;\n\t\t\/\/rotate -90 degrees around Y (CW) and shift half-width to the left\n\t\tmat4x4_translate(transformMatrix, -pVS-&gt;whl&#91;0] \/ 2, 0, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 90);\n\t}\n\telse if (applyTo.find(&quot;right&quot;) == 0) {\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;2]; \/\/width = original length\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;1];\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;2];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extR;\n\t\tvs.extL = pVS-&gt;extF;\n\t\tvs.extR = pVS-&gt;extB;\n\t\tvs.extU = pVS-&gt;extU;\n\t\tvs.extD = pVS-&gt;extD;\n\t\t\/\/rotate +90 degrees around Y (CCW) and shift half-width to the right\n\t\tmat4x4_translate(transformMatrix, pVS-&gt;whl&#91;0] \/ 2, 0, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 90);\n\t}\n\telse if (applyTo.find(&quot;top&quot;) == 0) {\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;0];\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;2]; \/\/height = original length\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;0];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;2];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extU;\n\t\tvs.extL = pVS-&gt;extR;\n\t\tvs.extR = pVS-&gt;extL;\n\t\tvs.extU = pVS-&gt;extF;\n\t\tvs.extD = pVS-&gt;extB;\n\t\t\/\/rotate -90 degrees around X (CW) and 180 around Y, and shift half-height up\n\t\tmat4x4_translate(transformMatrix, 0, pVS-&gt;whl&#91;1] \/ 2, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 180);\n\t\tmat4x4_rotate_X(transformMatrix, transformMatrix, -degrees2radians * 90);\n\t}\n\telse if (applyTo.find(&quot;bottom&quot;) == 0) {\n\t\tvs.whl&#91;0] = pVS-&gt;whl&#91;0];\n\t\tvs.whl&#91;1] = pVS-&gt;whl&#91;2]; \/\/height = original length\n\t\tvs.sections&#91;0] = pVS-&gt;sections&#91;0];\n\t\tvs.sections&#91;1] = pVS-&gt;sections&#91;2];\n\t\t\/\/extensions\n\t\tvs.extF = pVS-&gt;extD;\n\t\tvs.extL = pVS-&gt;extL;\n\t\tvs.extR = pVS-&gt;extR;\n\t\tvs.extU = pVS-&gt;extF;\n\t\tvs.extD = pVS-&gt;extB;\n\t\t\/\/rotate 90 around X (CCW) and shift half-height down\n\t\tmat4x4_translate(transformMatrix, 0, -pVS-&gt;whl&#91;1] \/ 2, 0);\n\t\tmat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * 90);\n\t}\n\tlockGroup(pMB);\n\t\/\/create vertices\n\tif (strstr(pVS-&gt;shapeType, &quot;tank&quot;) != nullptr)\n\t\tbuildBoxFaceTank(pMB, applyTo, &amp;vs);\n\telse\n\t\tbuildBoxFacePlain(pMB, applyTo, &amp;vs);\n\n\t\/\/move face to it&#039;s place (apply transform matrix)\n\tint vertsN = pMB-&gt;vertices.size();\n\tfor (int i = pMB-&gt;pCurrentGroup-&gt;fromVertexN; i &lt; vertsN; i++) {\n\t\tVertex01* pVX = pMB-&gt;vertices.at(i);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aPos, transformMatrix, pVX-&gt;aPos, 1);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aNormal, transformMatrix, pVX-&gt;aNormal, 0);\n\t}\n\treleaseGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\tif (pVS-&gt;whl&#91;0] == 0 || pVS-&gt;whl&#91;1] == 0)\n\t\treturn 0;\n\t\/\/create vertices\n\tint sectionsX = pVS-&gt;sections&#91;0];\n\tint sectionsY = pVS-&gt;sections&#91;1];\n\tint pointsX = sectionsX + 1;\n\tint pointsY = sectionsY + 1;\n\tfloat stepX = pVS-&gt;whl&#91;0] \/ sectionsX;\n\tfloat stepY = pVS-&gt;whl&#91;1] \/ sectionsY;\n\tfloat kY = pVS-&gt;whl&#91;1] \/ 2;\n\tfor (int iy = 0; iy &lt; pointsY; iy++) {\n\t\tfloat kX = -pVS-&gt;whl&#91;0] \/ 2;\n\t\tfor (int ix = 0; ix &lt; pointsX; ix++) {\n\t\t\tint nSE = addVertex(pMB, kX, kY, pVS-&gt;extF, 0, 0, 1); \/\/vertex number on south-east\n\t\t\tif (iy &gt; 0 &amp;&amp; ix &gt; 0) {\n\t\t\t\t\/\/add 2 triangles\n\t\t\t\tint nSW = nSE - 1; \/\/vertex number south-west\n\t\t\t\tint nNE = nSE - pointsX; \/\/north-east\n\t\t\t\tint nNW = nSW - pointsX; \/\/north-west\n\t\t\t\tadd2triangles(pMB, nNW, nNE, nSW, nSE, iy + ix);\n\t\t\t}\n\t\t\tkX += stepX;\n\t\t}\n\t\tkY -= stepY;\n\t}\n\treturn 1;\n}\nint ModelBuilder::buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\t\/\/for diamond effect - sectionsRad=1, don&#039;t merge normals\n\tbool drawMiddle = true;\n\t\/\/edges\n\tbool drawTop = false;\n\tbool drawBottom = false;\n\tbool drawLeft = false;\n\tbool drawRight = false;\n\t\/\/corners\n\tbool drawTopLeft = false;\n\tbool drawTopRight = false;\n\tbool drawBottomLeft = false;\n\tbool drawBottomRight = false;\n\tif (pVS-&gt;extF == 0 || applyTo.find(&quot; all&quot;) != std::string::npos) {\n\t\tdrawTop = true;\n\t\tdrawBottom = true;\n\t\tdrawLeft = true;\n\t\tdrawRight = true;\n\t\tdrawTopLeft = true;\n\t\tdrawTopRight = true;\n\t\tdrawBottomLeft = true;\n\t\tdrawBottomRight = true;\n\t}\n\telse if (applyTo.find(&quot; h&quot;) != std::string::npos) {\n\t\tdrawLeft = true;\n\t\tdrawRight = true;\n\t}\n\telse if (applyTo.find(&quot; v&quot;) != std::string::npos) {\n\t\tdrawTop = true;\n\t\tdrawBottom = true;\n\t}\n\tif (applyTo.find(&quot; no&quot;) != std::string::npos) {\n\t\tif (applyTo.find(&quot; noM&quot;) != std::string::npos) {\n\t\t\t\/\/middle\n\t\t\tif (applyTo.find(&quot; noMrow&quot;) != std::string::npos) {\n\t\t\t\tdrawMiddle = false;\n\t\t\t\tdrawLeft = false;\n\t\t\t\tdrawRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noMcol&quot;) != std::string::npos) {\n\t\t\t\tdrawMiddle = false;\n\t\t\t\tdrawTop = false;\n\t\t\t\tdrawBottom = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noMid&quot;) != std::string::npos)\n\t\t\t\tdrawMiddle = false;\n\t\t}\n\t\tif (applyTo.find(&quot; noN&quot;) != std::string::npos) {\n\t\t\t\/\/north\n\t\t\tif (applyTo.find(&quot; noNrow&quot;) != std::string::npos) {\n\t\t\t\tdrawTop = false;\n\t\t\t\tdrawTopLeft = false;\n\t\t\t\tdrawTopRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noNedge&quot;) != std::string::npos)\n\t\t\t\tdrawTop = false;\n\t\t\tif (applyTo.find(&quot; noNW&quot;) != std::string::npos)\n\t\t\t\tdrawTopLeft = false;\n\t\t\tif (applyTo.find(&quot; noNE&quot;) != std::string::npos)\n\t\t\t\tdrawTopRight = false;\n\t\t}\n\t\tif (applyTo.find(&quot; noS&quot;) != std::string::npos) {\n\t\t\t\/\/south\n\t\t\tif (applyTo.find(&quot; noSrow&quot;) != std::string::npos) {\n\t\t\t\tdrawBottom = false;\n\t\t\t\tdrawBottomLeft = false;\n\t\t\t\tdrawBottomRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noSedge&quot;) != std::string::npos)\n\t\t\t\tdrawBottom = false;\n\t\t\tif (applyTo.find(&quot; noSW&quot;) != std::string::npos)\n\t\t\t\tdrawBottomLeft = false;\n\t\t\tif (applyTo.find(&quot; noSE&quot;) != std::string::npos)\n\t\t\t\tdrawBottomRight = false;\n\t\t}\n\t\tif (applyTo.find(&quot; noW&quot;) != std::string::npos) {\n\t\t\t\/\/west\n\t\t\tif (applyTo.find(&quot; noWcol&quot;) != std::string::npos) {\n\t\t\t\tdrawLeft = false;\n\t\t\t\tdrawTopLeft = false;\n\t\t\t\tdrawBottomLeft = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noWedge&quot;) != std::string::npos)\n\t\t\t\tdrawLeft = false;\n\t\t}\n\t\tif (applyTo.find(&quot; noE&quot;) != std::string::npos) {\n\t\t\t\/\/east\n\t\t\tif (applyTo.find(&quot; noEcol&quot;) != std::string::npos) {\n\t\t\t\tdrawRight = false;\n\t\t\t\tdrawTopRight = false;\n\t\t\t\tdrawBottomRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(&quot; noEedge&quot;) != std::string::npos)\n\t\t\t\tdrawRight = false;\n\t\t}\n\t}\n\tlockGroup(pMB);\n\t\/\/middle\n\tif (pVS-&gt;whl&#91;0] &gt; 0 &amp;&amp; pVS-&gt;whl&#91;1] &gt; 0 &amp;&amp; drawMiddle) {\n\t\tbuildBoxFacePlain(pMB, applyTo, pVS);\n\t}\n\tVirtualShape vs;\n\t\/\/edges\n\t\/\/vs.type.assign(&quot;cylinder&quot;);\n\tvs.sectionsR = pVS-&gt;sectionsR;\n\tif (pVS-&gt;whl&#91;0] &gt; 0) {\n\t\tvs.sections&#91;2] = pVS-&gt;sections&#91;0]; \/\/cylinder Z sections n\n\t\tvs.whl&#91;2] = pVS-&gt;whl&#91;0]; \/\/cylinder length Z\n\t\tvs.whl&#91;0] = pVS-&gt;extF * 2; \/\/cylinder diameter X\n\t\tif (pVS-&gt;extU &gt; 0 &amp;&amp; drawTop) {\n\t\t\tvs.whl&#91;1] = pVS-&gt;extU * 2; \/\/cylinder diameter Y\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &amp;vs, 0, 90);\n\t\t\t\/\/rotate -90 degrees around Y and shift up\n\t\t\tmoveGroupDg(pMB, 0, -90, 0, 0, pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS-&gt;extD &gt; 0 &amp;&amp; drawBottom) {\n\t\t\tvs.whl&#91;1] = pVS-&gt;extD * 2; \/\/cylinder diameter Y\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &amp;vs, -90, 0);\n\t\t\t\/\/rotate -90 degrees around Y and shift down\n\t\t\tmoveGroupDg(pMB, 0, -90, 0, 0, -pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\tif (pVS-&gt;whl&#91;1] &gt; 0) {\n\t\tvs.sections&#91;2] = pVS-&gt;sections&#91;1]; \/\/cylinder Z sections n\n\t\tvs.whl&#91;2] = pVS-&gt;whl&#91;1]; \/\/cylinder length Z\n\t\tvs.whl&#91;1] = pVS-&gt;extF * 2; \/\/cylinder diameter Y\n\t\tif (pVS-&gt;extL &gt; 0 &amp;&amp; drawLeft) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extL * 2; \/\/cylinder diameter X\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &amp;vs, 90, 180);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 90, 0, 0, -pVS-&gt;whl&#91;0] * 0.5f, 0, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS-&gt;extR &gt; 0 &amp;&amp; drawRight) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extR * 2; \/\/cylinder diameter X\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &amp;vs, 0, 90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 90, 0, 0, pVS-&gt;whl&#91;0] * 0.5f, 0, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\t\/\/corners\n\t\/\/vs.type.assign(&quot;cap&quot;);\n\tvs.sectionsR = pVS-&gt;sectionsR;\n\tvs.sections&#91;2] = pVS-&gt;sectionsR;\n\tvs.whl&#91;2] = pVS-&gt;extF;\n\tif (pVS-&gt;extU &gt; 0) {\n\t\t\/\/top corners\n\t\tvs.whl&#91;1] = pVS-&gt;extU * 2;\n\t\tif (pVS-&gt;extL &gt; 0 &amp;&amp; drawTopLeft) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extL * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &amp;vs, 90, 180);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, -pVS-&gt;whl&#91;0] * 0.5f, pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS-&gt;extR &gt; 0 &amp;&amp; drawTopRight) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extR * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &amp;vs, 0, 90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, pVS-&gt;whl&#91;0] * 0.5f, pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\n\t\t}\n\t}\n\tif (pVS-&gt;extD &gt; 0) {\n\t\t\/\/bottom corners\n\t\tvs.whl&#91;1] = pVS-&gt;extD * 2;\n\t\tif (pVS-&gt;extL &gt; 0 &amp;&amp; drawBottomLeft) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extL * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &amp;vs, -180, -90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, -pVS-&gt;whl&#91;0] * 0.5f, -pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS-&gt;extR &gt; 0 &amp;&amp; drawBottomRight) {\n\t\t\tvs.whl&#91;0] = pVS-&gt;extR * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &amp;vs, -90, 0);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, pVS-&gt;whl&#91;0] * 0.5f, -pVS-&gt;whl&#91;1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\tif (pVS-&gt;extF == 0) {\n\t\tint vertsN = pMB-&gt;vertices.size();\n\t\tfor (int i = pMB-&gt;pCurrentGroup-&gt;fromVertexN; i &lt; vertsN; i++) {\n\t\t\tVertex01* pVX = pMB-&gt;vertices.at(i);\n\t\t\t\/\/normal\n\t\t\tv3set(pVX-&gt;aNormal, 0, 0, 1);\n\t\t}\n\t}\n\treleaseGroup(pMB);\n\treturn 1;\n}\n\nint ModelBuilder::cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {\n\t\/\/ angleFrom\/To - in degrees\n\tlockGroup(pMB);\n\tfloat stepZ = pVS-&gt;whl&#91;2] \/ pVS-&gt;sections&#91;2];\n\tfloat stepDg = (angleTo - angleFrom) \/ pVS-&gt;sectionsR; \/\/in degrees\n\tfor (int nz = 0; nz &lt;= pVS-&gt;sections&#91;2]; nz++) {\n\t\tfloat kz = stepZ * nz - pVS-&gt;whl&#91;2] * 0.5f;\n\t\tfor (int rpn = 0; rpn &lt;= pVS-&gt;sectionsR; rpn++) {\n\t\t\t\/\/ rpn - radial point number\n\t\t\tfloat angleRd = (angleFrom + stepDg * rpn) * degrees2radians;\n\t\t\tfloat kx = cosf(angleRd);\n\t\t\tfloat ky = sinf(angleRd);\n\t\t\tint nSE = addVertex(pMB, kx, ky, kz, kx, ky, 0);\n\t\t\tif (nz &gt; 0 &amp;&amp; rpn &gt; 0) {\n\t\t\t\tint nSW = nSE - 1;\n\t\t\t\tint nNW = nSW - pVS-&gt;sectionsR - 1;\n\t\t\t\tint nNE = nSE - pVS-&gt;sectionsR - 1;\n\t\t\t\tadd2triangles(pMB, nNE, nNW, nSE, nSW, nz + rpn);\n\t\t\t}\n\t\t}\n\t}\n\t\/\/scale to desirable diameters\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_scale_aniso(transformMatrix, transformMatrix, pVS-&gt;whl&#91;0] * 0.5f, pVS-&gt;whl&#91;1] * 0.5f, 1);\n\tint vertsN = pMB-&gt;vertices.size();\n\tfor (int i = pMB-&gt;pCurrentGroup-&gt;fromVertexN; i &lt; vertsN; i++) {\n\t\tVertex01* pVX = pMB-&gt;vertices.at(i);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aPos, transformMatrix, pVX-&gt;aPos, 1);\n\t}\n\treleaseGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {\n\t\/\/ angleFrom\/To - in degrees\n\tlockGroup(pMB);\n\t\/\/center point\n\tint n0 = addVertex(pMB, 0, 0, 1, 0, 0, 1);\n\tfloat stepZdg = 90.0f \/ pVS-&gt;sections&#91;2]; \/\/in degrees\n\tfloat stepRdg = (angleTo - angleFrom) \/ pVS-&gt;sectionsR; \/\/in degrees\n\tfor (int nz = 1; nz &lt;= pVS-&gt;sections&#91;2]; nz++) {\n\t\tfloat angleZrd = stepZdg * nz * degrees2radians;\n\t\tfloat kz = cosf(angleZrd);\n\t\tfloat R = sinf(angleZrd);\n\t\tfor (int rpn = 0; rpn &lt;= pVS-&gt;sectionsR; rpn++) {\n\t\t\t\/\/ rpn - radial point number\n\t\t\tfloat angleRd = (angleFrom + stepRdg * rpn) * degrees2radians;\n\t\t\tfloat kx = cosf(angleRd) * R;\n\t\t\tfloat ky = sinf(angleRd) * R;\n\t\t\tint nSE = addVertex(pMB, kx, ky, kz, kx, ky, kz);\n\t\t\tif (rpn &gt; 0) {\n\t\t\t\tif (nz == 1) {\n\t\t\t\t\tint nSW = nSE - 1;\n\t\t\t\t\taddTriangle(pMB, n0, nSW, nSE);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tint nSW = nSE - 1;\n\t\t\t\t\tint nNW = nSW - pVS-&gt;sectionsR - 1;\n\t\t\t\t\tint nNE = nSE - pVS-&gt;sectionsR - 1;\n\t\t\t\t\tadd2triangles(pMB, nNW, nNE, nSW, nSE, nz + rpn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\/\/scale to desirable diameters\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_scale_aniso(transformMatrix, transformMatrix, pVS-&gt;whl&#91;0] * 0.5f, pVS-&gt;whl&#91;1] * 0.5f, pVS-&gt;whl&#91;2]);\n\tint vertsN = pMB-&gt;vertices.size();\n\tfor (int i = pMB-&gt;pCurrentGroup-&gt;fromVertexN; i &lt; vertsN; i++) {\n\t\tVertex01* pVX = pMB-&gt;vertices.at(i);\n\t\tmat4x4_mul_vec4plus(pVX-&gt;aPos, transformMatrix, pVX-&gt;aPos, 1);\n\t}\n\treleaseGroup(pMB);\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>9. Open <em>TheGame.cpp<\/em> and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55]; title: ; notranslate\" title=\"\">\n#include &quot;TheGame.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;linmath.h&quot;\n#include &quot;Texture.h&quot;\n#include &quot;Shader.h&quot;\n#include &quot;DrawJob.h&quot;\n#include &quot;ModelBuilder.h&quot;\n\nextern std::string filesRoot;\n\nstd::vector&lt;GameSubj*&gt; TheGame::gameSubjs;\n\nint TheGame::getReady() {\n    bExitGame = false;\n    Shader::loadShaders();\n    glEnable(GL_CULL_FACE);\n\n    \/\/=== create box ========================\n    GameSubj* pGS = new GameSubj();\n    gameSubjs.push_back(pGS);\n\n    pGS-&gt;name.assign(&quot;box1&quot;);\n    pGS-&gt;ownCoords.setPosition(0, 0, 0);\n    pGS-&gt;ownCoords.setDegrees(0, 0, 0);\n    pGS-&gt;ownSpeed.setDegrees(0,1,0);\n\n    ModelBuilder* pMB = new ModelBuilder();\n    pMB-&gt;useSubjN(gameSubjs.size() - 1);\n\n    \/\/define VirtualShape\n    VirtualShape vs;\n    vs.setShapeType(&quot;box-tank&quot;);\n    vs.whl&#91;0] = 60;\n    vs.whl&#91;1] = 160;\n    vs.whl&#91;2] = 390;\n    vs.setExt(20);\n    vs.extD = 0;\n    vs.extF = 0; \/\/to make front face &quot;flat&quot;\n    vs.sectionsR = 2;\n\n    Material mt;\n    \/\/define material - flat red\n    mt.shaderN = Shader::spN_phong_ucolor;\n    mt.primitiveType = GL_TRIANGLES;\n    mt.uColor.setRGBA(255, 0, 0,255); \/\/red\n    pMB-&gt;useMaterial(&amp;mt);\n\n    pMB-&gt;buildBoxFace(pMB,&quot;front v&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;back v&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;top&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;bottom&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;left all&quot;, &amp;vs);\n    mt.uColor.setRGBA(0, 0, 255,255); pMB-&gt;useMaterial(&amp;mt); \/\/blue\n    pMB-&gt;buildBoxFace(pMB, &quot;right all&quot;, &amp;vs);\n\n    pMB-&gt;buildDrawJobs(gameSubjs);\n\n    delete pMB;\n\n    \/\/===== set up camera\n    v3set(mainCamera.ownCoords.pos, 0, 200, 1000); \/\/set position\n    float cameraDir&#91;3];\n    v3set(cameraDir, 0, -200, -1000); \/\/set direction vector\n    float cameraYawDg = v3yawDg(cameraDir);\n    float cameraPitchDg = v3pitchDg(cameraDir);\n    \/\/mylog(&quot;cameraYaw=%f, cameraPitch=%f\\n&quot;, cameraYawDg, cameraPitchDg);\n\n    mainCamera.ownCoords.setDegrees(cameraPitchDg, cameraYawDg, 0);\n    float cameraUp&#91;4] = { 0,1,0,0 }; \/\/y - up\n    mat4x4_mul_vec4plus(cameraUp, *mainCamera.ownCoords.getRotationMatrix(), cameraUp, 0);\n\n    mat4x4_look_at(mainCamera.lookAtMatrix, mainCamera.ownCoords.pos, pGS-&gt;ownCoords.pos, cameraUp);\n\n    \/\/===== set up light\n    v3set(dirToMainLight, -1, 1, 1);\n    vec3_norm(dirToMainLight, dirToMainLight);\n\n    return 1;\n}\nint TheGame::drawFrame() {\n    myPollEvents();\n\n    \/\/glClearColor(0.0, 0.0, 0.5, 1.0);\n    glClear(GL_COLOR_BUFFER_BIT);\n\n    \/\/calculate halfVector\n    float dirToCamera&#91;4] = { 0,0,-1,0 }; \/\/-z\n    mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);\n\n    float uHalfVector&#91;4] = { 0,0,0,0 };\n    for (int i = 0; i &lt; 3; i++)\n        uHalfVector&#91;i] = (dirToCamera&#91;i] + dirToMainLight&#91;i]) \/ 2;\n    vec3_norm(uHalfVector, uHalfVector);\n\n    mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;\n    \/\/mat4x4_ortho(mProjection, -(float)screenSize&#91;0] \/ 2, (float)screenSize&#91;0] \/ 2, -(float)screenSize&#91;1] \/ 2, (float)screenSize&#91;1] \/ 2, 100.f, 500.f);\n    mat4x4_perspective(mProjection, 3.14f \/ 6.0f, (float)screenSize&#91;0] \/ screenSize&#91;1], 700.f, 1300.f);\n    mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);\n    \/\/mViewProjection&#91;1]&#91;3] = 0; \/\/keystone effect\n\n    \/\/scan subjects\n    int subjsN = gameSubjs.size();\n    for (int subjN = 0; subjN &lt; subjsN; subjN++) {\n        GameSubj* pGS = gameSubjs.at(subjN);\n        \/\/behavior - apply rotation speed\n        pGS-&gt;moveSubj();\n        \/\/prepare subject for rendering\n        pGS-&gt;buildModelMatrix(pGS);\n        \/\/build MVP matrix for given subject\n        mat4x4_mul(mMVP, mViewProjection, pGS-&gt;ownModelMatrix);\n        \/\/build Model-View (rotation) matrix for normals\n        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS-&gt;ownCoords.getRotationMatrix());\n        \/\/convert to 3x3 matrix\n        float mMV3x3&#91;3]&#91;3];\n        for (int y = 0; y &lt; 3; y++)\n            for (int x = 0; x &lt; 3; x++)\n                mMV3x3&#91;y]&#91;x] = mMV4x4&#91;y]&#91;x];\n        \/\/render subject\n        for (int i = 0; i &lt; pGS-&gt;djTotalN; i++) {\n            DrawJob* pDJ = DrawJob::drawJobs.at(pGS-&gt;djStartN + i);\n            pDJ-&gt;execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);\n        }\n    }\n    mySwapBuffers();\n    return 1;\n}\n\nint TheGame::cleanUp() {\n    int itemsN = gameSubjs.size();\n    \/\/delete all UISubjs\n    for (int i = 0; i &lt; itemsN; i++) {\n        GameSubj* pGS = gameSubjs.at(i);\n        delete pGS;\n    }\n    gameSubjs.clear();\n    \/\/clear all other classes\n    Texture::cleanUp();\n    Shader::cleanUp();\n    DrawJob::cleanUp();\n    return 1;\n}\nint TheGame::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 TheGame::run() {\n    getReady();\n    while (!bExitGame) {\n        drawFrame();\n    }\n    cleanUp();\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>The difference with previous version is:<\/p>\n\n\n\n<p><em>TheGame::getReady()<\/em> now builds not just a &#8220;box&#8221;, but &#8220;box-tank&#8221; (box with rounded edges and corners), lines from 33 to 55.<\/p>\n\n\n\n<p>Please note, that now instead of plain &#8220;front&#8221;, &#8220;right&#8221;, etc. we&#8217;re also using &#8220;front v&#8221;, &#8220;right all&#8221;, etc. This is about &#8220;extensions&#8221;:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c20\/04.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>10. Build and run:<\/p>\n\n\n\n<p><strong>Before:<\/strong><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c19\/00.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>After:<\/strong><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c20\/00.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<iframe loading=\"lazy\" width=\"100%\" height=\"400\" src=\"https:\/\/www.youtube.com\/embed\/ilg6uknO_2Y?controls=0&amp;autoplay=1&amp;loop=1&amp;playlist=ilg6uknO_2Y\" title=\"Rounded shape\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"\"><\/iframe>\n\n\n\n<p><\/p>\n\n\n\n<p>Now &#8211; with glares.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Android<\/h2>\n\n\n\n<p>Here we will need to add Android&#8217;s <em>myMkDir()<\/em> and <em>myStrcpy_s()<\/em> implementations.<\/p>\n\n\n\n<p>11. Re-start VS. Open&nbsp;<em>C:\\CPP\\a997modeler\\p_android\\p_android.sln<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>12. Open&nbsp;<em>platform.h<\/em>&nbsp;and replace code by<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [12,13]; title: ; notranslate\" title=\"\">\n#pragma once\n\ntypedef uint32_t myUint64;\ntypedef uint32_t myUint32;\ntypedef uint8_t  myUint16;\ntypedef uint8_t  myUint8;\n\nvoid mylog(const char* _Format, ...);\nvoid mySwapBuffers();\nvoid myPollEvents();\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode);\nint myMkDir(const char* outPath);\nvoid myStrcpy_s(char* dst, int maxSize, const char* src);\n\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>13. Open&nbsp;<em>platform.cpp<\/em>&nbsp;and replace code by<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [4,73,83]; title: ; notranslate\" title=\"\">\n#include &lt;android\/log.h&gt;\n#include &quot;stdio.h&quot;\n#include &quot;TheGame.h&quot;\n#include &lt;sys\/stat.h&gt;\t\/\/mkdir for Android\n\nextern struct android_app* androidApp;\nextern const ASensor* accelerometerSensor;\nextern ASensorEventQueue* sensorEventQueue;\n\nextern EGLDisplay androidDisplay;\nextern EGLSurface androidSurface;\nextern TheGame theGame;\n\nvoid mylog(const char* _Format, ...) {\n#ifdef _DEBUG\n    char outStr&#91;1024];\n    va_list _ArgList;\n    va_start(_ArgList, _Format);\n    vsprintf(outStr, _Format, _ArgList);\n    __android_log_print(ANDROID_LOG_INFO, &quot;mylog&quot;, outStr, NULL);\n    va_end(_ArgList);\n#endif\n};\n\nvoid mySwapBuffers() {\n\teglSwapBuffers(androidDisplay, androidSurface);\n}\nvoid myPollEvents() {\n\t\/\/ Read all pending events.\n\tint ident;\n\tint events;\n\tstruct android_poll_source* source;\n\n\t\/\/ If not animating, we will block forever waiting for events.\n\t\/\/ If animating, we loop until all events are read, then continue\n\t\/\/ to draw the next frame of animation.\n\twhile ((ident = ALooper_pollAll(0, NULL, &amp;events,\n\t\t(void**)&amp;source)) &gt;= 0) {\n\n\t\t\/\/ Process this event.\n\t\tif (source != NULL) {\n\t\t\tsource-&gt;process(androidApp, source);\n\t\t}\n\n\t\t\/\/ If a sensor has data, process it now.\n\t\tif (ident == LOOPER_ID_USER) {\n\t\t\tif (accelerometerSensor != NULL) {\n\t\t\t\tASensorEvent event;\n\t\t\t\twhile (ASensorEventQueue_getEvents(sensorEventQueue,\n\t\t\t\t\t&amp;event, 1) &gt; 0) {\n\t\t\t\t\t\/\/LOGI(&quot;accelerometer: x=%f y=%f z=%f&quot;,\n\t\t\t\t\t\/\/\tevent.acceleration.x, event.acceleration.y,\n\t\t\t\t\t\/\/\tevent.acceleration.z);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t\/\/ Check if we are exiting.\n\t\tif (androidApp-&gt;destroyRequested != 0) {\n\t\t\ttheGame.bExitGame = true;\n\t\t\tbreak;\n\t\t}\n\t}\n}\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode) {\n\t*pFile = fopen(filePath, mode);\n\tif (*pFile == NULL) {\n\t\tmylog(&quot;ERROR: can&#039;t open file %s\\n&quot;, filePath);\n\t\treturn -1;\n\t}\n\treturn 1;\n}\nint myMkDir(const char* outPath) {\n\tstruct stat info;\n\tif (stat(outPath, &amp;info) == 0)\n\t\treturn 0; \/\/exists already\n\tint status = mkdir(outPath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);\n\tif (status == 0)\n\t\treturn 1; \/\/Successfully created\n\tmylog(&quot;ERROR creating, status=%d, errno: %s.\\n&quot;, status, std::strerror(errno));\n\treturn -1;\n}\nvoid myStrcpy_s(char* dst, int maxSize, const char* src) {\n\tstrcpy(dst, src);\n\t\/\/fill tail by zeros\n\tint strLen = strlen(dst);\n\tif (strLen &lt; maxSize)\n\t\tfor (int i = strLen; i &lt; maxSize; i++)\n\t\t\tdst&#91;i] = 0;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>14. Under <em>modeler <\/em>add <strong>Existing<\/strong> Item<\/p>\n\n\n\n<p>from <em>C:\\CPP\\engine\\modeler<\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>ModelBuilder1base.cpp<\/li><li>ModelBuilder1base.h<\/li><\/ul>\n\n\n\n<p><strong>Add<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>15. Switch on, unlock, plug in, allow.<\/p>\n\n\n\n<p>Rebuild solution. Run.<\/p>\n\n\n\n<p>Good.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">Our current rectangular model does not allow us to appreciate all the beauty of the Phong shader, in particular &#8211; the glares. For that we&#8217;ll need something more round-shaped. Let&#8217;s extend our Modeler accordingly. Windows 1. Start VS. Open C:\\CPP\\a997modeler\\p_windows\\p_windows.sln. First &#8211; we&#8217;ll need to extend VirtualShape.h. 2. Open VirtualShape.h and replace code by: New [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":650,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-647","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cross-platform-3d"],"_links":{"self":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/647","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=647"}],"version-history":[{"count":20,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/647\/revisions"}],"predecessor-version":[{"id":3514,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/647\/revisions\/3514"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media\/650"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=647"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=647"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=647"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}