{"id":556,"date":"2021-12-06T04:59:46","date_gmt":"2021-12-06T04:59:46","guid":{"rendered":"https:\/\/writingagame.com\/?p=556"},"modified":"2021-12-07T17:28:47","modified_gmt":"2021-12-07T17:28:47","slug":"chapter-17-modeler-part-2","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2021\/12\/06\/chapter-17-modeler-part-2\/","title":{"rendered":"Chapter 17. Modeler. Part 2"},"content":{"rendered":"\n<p>Now back to the modeler. Time to put all these new classes together.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Windows<\/h2>\n\n\n\n<p>1. Start VS. Open&nbsp;<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>2. Under <em>modeler <\/em>add new header file <strong>ModelBuilder.h<\/strong><\/p>\n\n\n\n<p>Location &#8211; <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; 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 ModelBuilder\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 ~ModelBuilder();\n\tint useSubjN(int subjN) { return useSubjN(this, subjN); };\n\tstatic int useSubjN(ModelBuilder* pMB, int subjN);\n\tint useMaterial(Material* pMT) { return useMaterial(this, pMT); };\n\tstatic int useMaterial(ModelBuilder* pMB, Material* pMT);\n\tint buildBoxFace(std::string applyTo, VirtualShape* pVS) { return buildBoxFace(this, applyTo, pVS); };\n\tstatic int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic void startGroup(ModelBuilder* pMB);\n\tstatic void endGroup(ModelBuilder* pMB);\n\tstatic int addVertex(ModelBuilder* pMB, float kx, float ky, float kz, float nx, float ny, float nz);\n\tstatic int add2triangles(ModelBuilder* pMB, int nNW, int nNE, int nSW, int nSE, int n);\n\tstatic int addTriangle(ModelBuilder* pMB, int n0, int n1, int n2);\n\tint buildDrawJobs(std::vector&lt;GameSubj*&gt; gameSubjs) { return buildDrawJobs(this, gameSubjs); };\n\tstatic int buildDrawJobs(ModelBuilder* pMB, std::vector&lt;GameSubj*&gt; gameSubjs);\n\tstatic int rearrangeArraysForDrawJob(ModelBuilder* 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};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>In this sample we will create a red box with 1 blue side (the right one). For this we will need to create 1 <em>GameSubject<\/em>, 2 <em>materials<\/em>, a &#8220;box&#8221; <em>virtual shape<\/em>, then add 6 &#8220;faces&#8221; to it. Then we&#8217;ll need to convert created vertices and triangles arrays into <em>DrawJobs<\/em>. The set of commands\/functions above is defined by these needs.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now &#8211; implementations:<\/p>\n\n\n\n<p>3. Under <em>modeler <\/em>add new C++ file <strong>ModelBuilder.cpp<\/strong><\/p>\n\n\n\n<p>Location &#8211; <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; 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\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 ModelBuilder::useSubjN(ModelBuilder* 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 ModelBuilder::useMaterial(ModelBuilder* 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 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\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\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\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\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\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\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\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\tstartGroup(pMB);\n\t\/\/create vertices\n\tint sectionsX = 1;\n\tint sectionsY = 1;\n\tint pointsX = sectionsX + 1;\n\tint pointsY = sectionsY + 1;\n\tfloat stepX = vs.whl&#91;0] \/ sectionsX;\n\tfloat stepY = vs.whl&#91;1] \/ sectionsY;\n\tfloat kY = vs.whl&#91;1] \/ 2;\n\tfor (int iy = 0; iy &lt; pointsY; iy++) {\n\t\tfloat kX = -vs.whl&#91;0] \/ 2;\n\t\tfor (int ix = 0; ix &lt; pointsX; ix++) {\n\t\t\tint nSE = addVertex(pMB, kX, kY, 0, 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\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\tendGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::add2triangles(ModelBuilder* 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 ModelBuilder::addTriangle(ModelBuilder* 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 ModelBuilder::startGroup(ModelBuilder* pMB) {\n\tpMB-&gt;pCurrentGroup = new Group01();\n\tpMB-&gt;groupsStack.push_back(pMB-&gt;pCurrentGroup);\n\tpMB-&gt;pCurrentGroup-&gt;fromVertexN = pMB-&gt;vertices.size();\n\tpMB-&gt;pCurrentGroup-&gt;fromTriangleN = pMB-&gt;triangles.size();\n}\nvoid ModelBuilder::endGroup(ModelBuilder* pMB) {\n\tdelete pMB-&gt;pCurrentGroup;\n\tpMB-&gt;pCurrentGroup = pMB-&gt;groupsStack.back();\n\tpMB-&gt;groupsStack.pop_back();\n}\nint ModelBuilder::addVertex(ModelBuilder* 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 ModelBuilder::buildDrawJobs(ModelBuilder* 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 ModelBuilder::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_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}\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 ModelBuilder::rearrangeArraysForDrawJob(ModelBuilder* 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\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now we have everything we need to create our first 3D model. In this sample it will be a red box with 1 blue side.<\/p>\n\n\n\n<p>In <em>TheGame.cpp<\/em> we won&#8217;t need hard-coded vertex arrays any longer. Instead, we will create them <em>programmatically<\/em>.<\/p>\n\n\n\n<p>4. Open <em>TheGame.cpp<\/em> and replace the code by following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; 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(1.5, -2, 1);\n\n    ModelBuilder* pMB = new ModelBuilder();\n    pMB-&gt;useSubjN(gameSubjs.size() - 1);\n\n    \/\/define VirtualShape\n    VirtualShape vs;\n    vs.type.assign(&quot;box&quot;);\n    vs.whl&#91;0] = 100;\n    vs.whl&#91;1] = 200;\n    vs.whl&#91;2] = 400;\n\n    Material mt;\n    \/\/define material - flat red\n    mt.shaderN = Shader::spN_flat_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(&quot;front&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(&quot;back&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(&quot;top&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(&quot;bottom&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(&quot;left&quot;, &amp;vs);\n\n    mt.uColor.setRGBA(0, 0, 255,255); pMB-&gt;useMaterial(&amp;mt); \/\/blue\n    pMB-&gt;buildBoxFace(&quot;right&quot;, &amp;vs);\n\n    pMB-&gt;buildDrawJobs(gameSubjs);\n\n    delete pMB;\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    mat4x4 mProjection, mMVP;\n    mat4x4_ortho(mProjection, -(float)screenSize&#91;0] \/ 2, (float)screenSize&#91;0] \/ 2, -(float)screenSize&#91;1] \/ 2, (float)screenSize&#91;1] \/ 2, 400.f, -400.f);\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, mProjection, pGS-&gt;ownModelMatrix);\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, NULL);\n        }\n    }\n    mySwapBuffers();\n    return 1;\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>Let&#8217;s take a closer look. Everything happens in the <em>TheGame::getReady()<\/em> (from line 20).<\/p>\n\n\n\n<p>First, we are creating a <em>GameSubj <\/em>and setting initial position and rotation speeds (lines 19 to 26).<\/p>\n\n\n\n<p>In line 28 we&#8217;re creating <em>ModelBuilder&#8217;s<\/em> instance, which we will use for building vertex arrays and <em>DrawJobs<\/em>.<\/p>\n\n\n\n<p>The next line (29), <em>pMB-&gt;useSubjN<\/em>, instructs <em>ModelBuilder <\/em>which <em>GameSubj <\/em>model number is it for (which makes possible to build a few sub-models simultaneously in the future).<\/p>\n\n\n\n<p>Lines 32 to 36 are setting a <em>VirtualShape<\/em>, so all following commands know what we are building,<\/p>\n\n\n\n<p>Lines 40 to 43 are defining the <em>Material<\/em>. <\/p>\n\n\n\n<p><em>pMB-&gt;useMaterial(&amp;mt)<\/em> command (line 43) tells <em>ModelBuilder <\/em>which material number we will use for all newly created vertices and triangles. Open <em>Vertex01.h<\/em> or <em>Triangle01.h<\/em>, you will see <em>subjN <\/em>and <em>materialN <\/em>references.<\/p>\n\n\n\n<p>In line 45 we&#8217;re starting to fill vertices and triangles arrays, the command <em>buildBoxFace()<\/em>. Let&#8217;s take a closer look.<\/p>\n\n\n\n<p>Open <em>ModelBuilder.cpp<\/em>, line 67. The idea is to create a simple 2D rectangle which faces us, and then rotate and shift it to it&#8217;s final place. We are getting this rectangle sizes (width and height) from given <em>VirtualShape <\/em>(parameter <em>pVS<\/em>) and saving them into local <em>VirtualShape vs<\/em>. It&#8217;s lines 72 to 115. Also in this code we are building <em>transformMatrix <\/em>which will move created rectangle to it&#8217;s place.<\/p>\n\n\n\n<p>For example, &#8220;front&#8221; side case, lines 72 to 79. This side faces us as is, so we&#8217;ll need just to shift it along Z axis closer to us, in +Z direction. &#8220;+Z&#8221; is because OpenGL has <strong>right-handed coordinate system<\/strong>:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c17\/01.jpg\"><\/p>\n\n\n\n<p>Line 116, <em>startGroup()<\/em>. We are about to create a set of actual vertices and triangles, then we will need to move them. The concept of &#8220;group&#8221; will allow us to move only what we need, without touching previously created vertices. <em>startGroup()<\/em> saves starting array positions of our future vertices and triangles.<\/p>\n\n\n\n<p>Lines 118 to 139 &#8211; were we&#8217;re actually creating vertices and triangles. This code allows to build not only simple 4 vertices rectangle, but also a &#8220;net&#8221;.<\/p>\n\n\n\n<p>Then, lines 141 to 146 &#8211; moving vertices to their final positions.<\/p>\n\n\n\n<p>Now back to <em>TheGame.cpp<\/em>.<\/p>\n\n\n\n<p>Please note, line 51: we are adjusting current <em>Material <\/em>(setting color to Blue), then calling <em>pMB-&gt;useMaterial(&amp;mt)<\/em>. This command checks if we have such material in <em>ModelBuilder&#8217;s materialsList<\/em> already, if not &#8211; creates a new one and adds it to the list. And sets <em>usingMaterialN <\/em>variable to corresponding value. So all following vertices will have new <em>materialN<\/em>.<\/p>\n\n\n\n<p>So, after line 52 we have 24 vertices and 12 triangles (6 sides, 36 indices), all with subjN 0 and with 2 different material numbers (first 5 sides with material #0, and the last one &#8211; with material #1).<\/p>\n\n\n\n<p>Now the key function, <em>pMB-&gt;buildDrawJobs()<\/em>, line 54. This function takes these vertices and triangles arrays, extracts from them sub-arrays for each subj\/material and builds corresponding <em>DrawJobs<\/em>. You can see it in <em>ModelBuilder.cpp<\/em>, line 197.<\/p>\n\n\n\n<p>After extracting sub-arrays for a single <em>DrawJob <\/em>it rearranges vertices order and builds corresponding VBOs, EBOs and VAOs.<\/p>\n\n\n\n<p>Line 56 &#8211; don&#8217;t forget to delete created objects to avoid memory leaks.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Hope now everything is more-or-less clear. Let&#8217;s try it.<\/p>\n\n\n\n<p>5. Build an run. Result:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c17\/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\/jbjPcdmAlXY?controls=0&amp;autoplay=1&amp;loop=1&amp;playlist=jbjPcdmAlXY\" title=\"Modeler\" 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<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Android<\/h2>\n\n\n\n<p>6. Re-start VS. Open <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>7. Under <em>xEngine <\/em>add New Filter, name &#8211; <strong>modeler<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>8. Under <em>modeler <\/em>add <strong>Existing Item<\/strong><\/p>\n\n\n\n<p>Browse to <em>C:\\CPP\\engine\\modeler<\/em><\/p>\n\n\n\n<p>Pick all 6 files over there,<\/p>\n\n\n\n<p><strong>Add<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>9. Add new path to project Properties:<\/p>\n\n\n\n<p>Right-click on  <em>p_android.NativeActivity<\/em> project, open project Properties, All Configurations \/ Active (ARM64), <em>C\/C++ -&gt; General -&gt; Additional Include Directories -&gt; Edit<\/em>, <\/p>\n\n\n\n<p>add new line, browse to <em>C:\\CPP\\engine\\modeler<\/em>,<\/p>\n\n\n\n<p>Select Folder, Ok, Apply, Ok.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>10. Switch on, unlock, plug in Android, allow debugging.<\/p>\n\n\n\n<p>Re-build solution, run. 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\">Now back to the modeler. Time to put all these new classes together. Windows 1. Start VS. Open&nbsp;C:\\CPP\\a997modeler\\p_windows\\p_windows.sln. 2. Under modeler add new header file ModelBuilder.h Location &#8211; C:\\CPP\\engine\\modeler Code: In this sample we will create a red box with 1 blue side (the right one). For this we will need to create 1 GameSubject, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":570,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-556","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\/556","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=556"}],"version-history":[{"count":14,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/556\/revisions"}],"predecessor-version":[{"id":672,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/556\/revisions\/672"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media\/570"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=556"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=556"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=556"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}