{"id":1398,"date":"2022-03-01T00:12:31","date_gmt":"2022-03-01T00:12:31","guid":{"rendered":"https:\/\/writingagame.com\/?p=1398"},"modified":"2022-03-05T21:45:24","modified_gmt":"2022-03-05T21:45:24","slug":"chapter-38-hierarchical-models","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2022\/03\/01\/chapter-38-hierarchical-models\/","title":{"rendered":"Chapter 38. Hierarchical models"},"content":{"rendered":"\n<p>Now we are ready for more complex models, consisting of several elements with individual behavior. For example &#8211; car, where body will be a parent (root) element, and wheels &#8211; child elements. For both, body and wheel, we&#8217;ll need separate model descriptors and separate <strong>classes<\/strong>, which will handle their behaviors.<\/p>\n\n\n\n<p>1. In <strong>Windows File Explorer<\/strong> open our root (CPP) folder, make a copy of <em>a997modeler <\/em>and rename it (copy) to <strong>a996car<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>2. Under <em>C:\\CPP\\a996car\\<strong>dt\\models<\/strong><\/em> add sub-folder <\/p>\n\n\n\n<p><em>C:\\CPP\\a996car\\dt\\models<strong>\\cars\\01<\/strong><\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>For simplicity, in this example, our models (car body and wheels) will simply be square.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c38\/01.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>3. Copy following code to a <strong>Text Editor<\/strong> and save it to\/as<\/p>\n\n\n\n<p><em>C:\\CPP\\a996car\\dt\\models\\cars\\01\\<strong>wheel01.txt<\/strong><\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n\/\/wheel\n&lt;mt_type=&quot;phong&quot; uColor=&quot;#0000ff&quot; \/&gt;\n&lt;vs=&quot;box&quot; whl=&quot;4,20,20&quot; \/&gt;\n&lt;a=&quot;front,back,right,top,bottom&quot; \/&gt;\n&lt;mt_type=&quot;phong&quot; uColor=&quot;#000000&quot; \/&gt; \/\/inner side\n&lt;a=&quot;left&quot; \/&gt;\n\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now root model &#8211; car body plus attached wheels.<\/p>\n\n\n\n<p>4. Copy following code to a <strong>Text Editor<\/strong> and save it to\/as<\/p>\n\n\n\n<p><em>C:\\CPP\\a996car\\dt\\models\\cars\\01\\<strong>root01.txt<\/strong><\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n\/\/body\n&lt;mt_type=&quot;phong&quot; uColor=&quot;#ff0000&quot; \/&gt;\n&lt;vs=&quot;box&quot; whl=&quot;10,20,70&quot; \/&gt;\n&lt;a=&quot;front,back,left,right,top&quot; py=5  \/&gt;\n\/\/wheels\n&lt;element=&quot;wheel01.txt&quot; class=&quot;CarWheel&quot; pxyz=&quot;-10,0,30&quot; ay=180 \/&gt; \/\/front passenger\n&lt;element=&quot;wheel01.txt&quot; class=&quot;CarWheel&quot; pxyz=&quot;10,0,30&quot; \/&gt; \/\/front driver\n&lt;element=&quot;wheel01.txt&quot; class=&quot;CarWheel&quot; pxyz=&quot;-10,0,-20&quot; ay=180 \/&gt; \/\/rear passenger\n&lt;element=&quot;wheel01.txt&quot; class=&quot;CarWheel&quot; pxyz=&quot;10,0,-20&quot; \/&gt; \/\/rear driver\n \n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now &#8211; SW part.<\/p>\n\n\n\n<p>5. Start VS. Open <em>C:\\CPP\\<strong>a996car<\/strong>\\p_windows\\<strong>p_windows.sln<\/strong><\/em> solution.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Hierarchical concept will require a few changes in <strong>GameSubj <\/strong>class:<\/p>\n\n\n\n<p>6. Replace <em>GameSubj.h<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [10,11,12,13,14,15,16,17,18,19,20,21,22,23,33]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;Coords.h&quot;\n#include &quot;Material.h&quot;\n#include &lt;string&gt;\n#include &lt;vector&gt;\n\nclass GameSubj\n{\npublic:\n\tstd::vector&lt;GameSubj*&gt;* pSubjsSet = NULL; \/\/which vector\/set this subj belongs to\n\tint nInSubjsSet = 0; \/\/subj&#039;s number in pSubjsSet\n\tint rootN = 0; \/\/model&#039;s root N\n\tint d2parent = 0; \/\/shift to parent object\n\tint d2headTo = 0; \/\/shift to headTo object\n\tint totalElements = 0; \/\/elements N in the model\n\tint totalNativeElements = 0; \/\/elements N in the model when initially loaded\n\tchar source&#91;256] = &quot;&quot;;\n\tchar className&#91;32] = &quot;&quot;;\n\tCoords absCoords;\n\tmat4x4 absModelMatrixUnscaled = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4 absModelMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\n\tchar name&#91;32]=&quot;&quot;;\n\tCoords ownCoords;\n\tCoords ownSpeed;\n\tfloat scale&#91;3] = { 1,1,1 };\n\tint djStartN = 0; \/\/first DJ N in DJs array (DrawJob::drawJobs)\n\tint djTotalN = 0; \/\/number of DJs\n\tmat4x4 ownModelMatrixUnscaled = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\n\tMaterial* pAltMaterial = NULL;\npublic:\n\tvirtual GameSubj* clone() { return new GameSubj(*this); };\n\tvirtual ~GameSubj();\n\tvoid buildModelMatrix() { buildModelMatrix(this); };\n\tstatic void buildModelMatrix(GameSubj* pGS);\n\tvirtual int moveSubj() { return applySpeeds(this); };\n\tstatic int applySpeeds(GameSubj* pGS);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>New <em>virtual <\/em>function <em>clone()<\/em>.<\/li><li>A set of new parent\/child variables<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>7. Replace <em>GameSubj.cpp<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [16,17,18,19]; title: ; notranslate\" title=\"\">\n#include &quot;GameSubj.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n\nGameSubj::~GameSubj() {\n    if (pAltMaterial != NULL)\n        delete pAltMaterial;\n}\nvoid GameSubj::buildModelMatrix(GameSubj* pGS) {\n    mat4x4_translate(pGS-&gt;ownModelMatrixUnscaled, pGS-&gt;ownCoords.pos&#91;0], pGS-&gt;ownCoords.pos&#91;1], pGS-&gt;ownCoords.pos&#91;2]);\n    \/\/rotation order: Z-X-Y\n    mat4x4_mul(pGS-&gt;ownModelMatrixUnscaled, pGS-&gt;ownModelMatrixUnscaled, pGS-&gt;ownCoords.rotationMatrix);\n\n    if(pGS-&gt;d2parent == 0)\n        memcpy(pGS-&gt;absModelMatrixUnscaled, pGS-&gt;ownModelMatrixUnscaled, sizeof(mat4x4));\n    else {\n        GameSubj* pParent = pGS-&gt;pSubjsSet-&gt;at(pGS-&gt;nInSubjsSet - pGS-&gt;d2parent);\n        mat4x4_mul(pGS-&gt;absModelMatrixUnscaled, pParent-&gt;absModelMatrixUnscaled, pGS-&gt;ownModelMatrixUnscaled);\n    }\n    if (v3equals(pGS-&gt;scale, 1))\n        memcpy(pGS-&gt;absModelMatrix, pGS-&gt;absModelMatrixUnscaled, sizeof(mat4x4));\n    else\n        mat4x4_scale_aniso(pGS-&gt;absModelMatrix, pGS-&gt;absModelMatrixUnscaled, pGS-&gt;scale&#91;0], pGS-&gt;scale&#91;1], pGS-&gt;scale&#91;2]);\n\n    \/\/update absCoords\n    if (pGS-&gt;d2parent == 0)\n        memcpy(&amp;pGS-&gt;absCoords, &amp;pGS-&gt;ownCoords, sizeof(Coords));\n    else {\n        Coords::getPositionFromMatrix(pGS-&gt;absCoords.pos, pGS-&gt;absModelMatrixUnscaled);\n    }\n}\nint GameSubj::applySpeeds(GameSubj* pGS) {\n    bool angleChanged = false;\n    for(int i=0;i&lt;3;i++)\n        if (pGS-&gt;ownSpeed.eulerDg&#91;i] != 0) {\n            angleChanged = true;\n            pGS-&gt;ownCoords.eulerDg&#91;i] += pGS-&gt;ownSpeed.eulerDg&#91;i];\n            if (pGS-&gt;ownCoords.eulerDg&#91;i] &gt; 180.0)\n                pGS-&gt;ownCoords.eulerDg&#91;i] -= 360.0;\n            else if (pGS-&gt;ownCoords.eulerDg&#91;i] &lt;= -180.0)\n                pGS-&gt;ownCoords.eulerDg&#91;i] += 360.0;\n        }\n    if(angleChanged)\n        Coords::eulerDgToMatrix(pGS-&gt;ownCoords.rotationMatrix, pGS-&gt;ownCoords.eulerDg);\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>buildModelMatrix(..)<\/em> now counts parent Coords too.<\/li><li><em>applySpeeds()<\/em> is modified as well.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><strong>Coords <\/strong>class is seriously revised (simplified).<\/p>\n\n\n\n<p>8. Replace <em>Coords.h<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;linmath.h&quot;\n\nclass Coords\n{\npublic:\n\tfloat pos&#91;4] = { 0,0,0,0 }; \/\/x,y,z position + 4-th element for compatibility with 3D 4x4 matrices math\n\tfloat eulerDg&#91;3] = { 0,0,0 }; \/\/Euler angles (pitch, yaw, roll) in degrees\n\tmat4x4 rotationMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\npublic:\n\tstatic void getPositionFromMatrix(float* pos, mat4x4 m);\n\tstatic void eulerDgToMatrix(mat4x4 rotationMatrix, float* eulerDg);\n\tstatic void matrixToEulerDg(float* eulerDg, mat4x4 m);\n};\n\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>9. Replace <em>Coords.cpp<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;Coords.h&quot;\n#include &quot;platform.h&quot;\n#include &lt;string&gt;\n\nfloat PI = 3.141592f;\nfloat degrees2radians = PI \/ 180.0f;\nfloat radians2degrees = 180.0f \/ PI;\n\nvoid Coords::getPositionFromMatrix(float* pos, mat4x4 m) {\n\t\/\/extracts position from matrix\n\tfor (int i = 0; i &lt; 3; i++)\n\t\tpos&#91;i] = m&#91;i]&#91;3];\n}\nvoid Coords::eulerDgToMatrix(mat4x4 rotationMatrix, float* eulerDg) {\n\t\/\/builds rotation matrix from Euler angles (in degreed)\n\tmat4x4_identity(rotationMatrix);\n\t\/\/rotation order: Z-X-Y\n\tfloat a = eulerDg&#91;1];\n\tif (a != 0)\n\t\tmat4x4_rotate_Y(rotationMatrix, rotationMatrix, a * degrees2radians);\n\ta = eulerDg&#91;0];\n\tif (a != 0)\n\t\tmat4x4_rotate_X(rotationMatrix, rotationMatrix, a * degrees2radians);\n\ta = eulerDg&#91;2];\n\tif (a != 0)\n\t\tmat4x4_rotate_Z(rotationMatrix, rotationMatrix, a * degrees2radians);\n}\nvoid Coords::matrixToEulerDg(float* eulerDg, mat4x4 m) {\n\t\/\/calculates Euler angles (in degrees) from matrix\n\tfloat yaw, pitch, roll; \/\/in redians\n\n\tif (m&#91;1]&#91;2] &gt; 0.998 || m&#91;1]&#91;2] &lt; -0.998) { \/\/ singularity at south or north pole\n\t\tyaw = atan2f(-m&#91;2]&#91;0], m&#91;0]&#91;0]);\n\t\troll = 0;\n\t}\n\telse {\n\t\tyaw = atan2f(-m&#91;0]&#91;2], m&#91;2]&#91;2]);\n\t\troll = atan2f(-m&#91;1]&#91;0], m&#91;1]&#91;1]);\n\t}\n\tpitch = asinf(m&#91;1]&#91;2]);\n\n\teulerDg&#91;0] = pitch * radians2degrees;\n\teulerDg&#91;1] = yaw * radians2degrees;\n\teulerDg&#91;2] = roll * radians2degrees;\n}\n\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>One of affected classes is <strong>Camera<\/strong>.<\/p>\n\n\n\n<p>10. Replace <em>Camera.cpp<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;Camera.h&quot;\n#include &quot;TheGame.h&quot;\n#include &quot;utils.h&quot;\n\nextern TheGame theGame;\nextern float degrees2radians;\n\nfloat Camera::pickDistance(Camera* pCam) {\n\tfloat cotangentA = 1.0f \/ tanf(degrees2radians * pCam-&gt;viewRangeDg \/ 2);\n\tfloat cameraDistanceV = pCam-&gt;stageSize&#91;1] \/ 2 * cotangentA;\n\tfloat cameraDistanceH = pCam-&gt;stageSize&#91;0] \/ 2 * cotangentA \/ theGame.screenAspectRatio;\n\tpCam-&gt;focusDistance = fmax(cameraDistanceV, cameraDistanceH);\n\treturn pCam-&gt;focusDistance;\n}\nvoid Camera::setCameraPosition(Camera* pCam) {\n\tv3set(pCam-&gt;ownCoords.pos, 0, 0, -pCam-&gt;focusDistance);\n\tmat4x4_mul_vec4plus(pCam-&gt;ownCoords.pos, pCam-&gt;ownCoords.rotationMatrix, pCam-&gt;ownCoords.pos, 1);\n\tfor (int i = 0; i &lt; 3; i++)\n\t\tpCam-&gt;ownCoords.pos&#91;i] += pCam-&gt;lookAtPoint&#91;i];\n}\nvoid Camera::buildLookAtMatrix(Camera* pCam) {\n\tfloat cameraUp&#91;4] = { 0,1,0,0 }; \/\/y - up\n\tmat4x4_mul_vec4plus(cameraUp, pCam-&gt;ownCoords.rotationMatrix, cameraUp, 0);\n\tmat4x4_look_at(pCam-&gt;lookAtMatrix, pCam-&gt;ownCoords.pos, pCam-&gt;lookAtPoint, cameraUp);\n}\nvoid Camera::onScreenResize(Camera* pCam) {\n\tpickDistance(pCam);\n\tsetCameraPosition(pCam);\n\tbuildLookAtMatrix(pCam);\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>New class <strong>CarWheel <\/strong>will be part of the <strong>Project<\/strong>, not of the engine. Let&#8217;s add it.<\/p>\n\n\n\n<p>11. Under <em>xTheGame<\/em> filter add <strong>New Filter<\/strong>, name &#8211; <strong>car<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>12. Under <em>xTheGame\/car<\/em> add new item &#8211; <em>header<\/em> file <strong>CarWheel.h<\/strong><\/p>\n\n\n\n<p>Make sure to change location to <em>C:\\CPP\\a996car<strong>\\car<\/strong><\/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 &quot;GameSubj.h&quot;\n\nclass CarWheel : public GameSubj\n{\npublic:\n\tfloat wheelDiameter = 20;\npublic:\n\tvirtual GameSubj* clone() { return new CarWheel(*this); };\n\tvirtual int moveSubj() { return moveSubj(this); };\n\tstatic int moveSubj(CarWheel* pGS);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>It inherits <em>GameSubj <\/em>class<\/li><li>It has own implementation of <em>virtual clone()<\/em> function, which returns new <em>CarWheel <\/em>object<\/li><li>and own implementation of <em>virtual moveSubj()<\/em> function<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>13. Under <em>xTheGame\/car<\/em> add new <em>C++<\/em> file <strong>CarWheel.cpp<\/strong><\/p>\n\n\n\n<p>Location: <em>C:\\CPP\\a996car<strong>\\car<\/strong><\/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;CarWheel.h&quot;\n\nint CarWheel::moveSubj(CarWheel* pGS) {\n    float wheelRotationSpeedDg = 3;\n    if (abs(pGS-&gt;ownCoords.eulerDg&#91;1]) &gt; 90)\n        wheelRotationSpeedDg = -wheelRotationSpeedDg;\n    pGS-&gt;ownSpeed.eulerDg&#91;0] = wheelRotationSpeedDg;\n    applySpeeds(pGS);\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>moveSubj()<\/em> function simply rotates wheel with a constant radial speed around X axis.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>14. Replace <em>TheGame.cpp<\/em> code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [11,27,36,37,68,142,143,144,145]; 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#include &quot;TexCoords.h&quot;\n#include &quot;ModelLoader.h&quot;\n#include &quot;car\/CarWheel.h&quot;\n\nextern std::string filesRoot;\nextern float degrees2radians;\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    glEnable(GL_DEPTH_TEST);\n    glDepthFunc(GL_LEQUAL);\n    glDepthMask(GL_TRUE);\n\n    int subjN = ModelLoader::loadModel(&amp;gameSubjs, &quot;\/dt\/models\/cars\/01\/root01.txt&quot;, &quot;&quot;);\n    GameSubj* pGS = gameSubjs.at(subjN);\n    pGS-&gt;ownSpeed.eulerDg&#91;1] = 1;\n\n    \/\/===== set up camera\n     v3set(mainCamera.ownCoords.eulerDg, 15, 180, 0); \/\/set camera angles\/orientation\n    Coords::eulerDgToMatrix(mainCamera.ownCoords.rotationMatrix, mainCamera.ownCoords.eulerDg);\n\n    mainCamera.viewRangeDg = 20;\n    mainCamera.stageSize&#91;0] = 90;\n    mainCamera.stageSize&#91;1] = 60;\n    memcpy(mainCamera.lookAtPoint, pGS-&gt;ownCoords.pos, sizeof(float) * 3);\n    mainCamera.onScreenResize();\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    glDepthMask(GL_TRUE);\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\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    float nearClip = mainCamera.focusDistance - 55;\n    float farClip = mainCamera.focusDistance + 55;\n    if (nearClip &lt; 0) nearClip = 0;\n    mat4x4_perspective(mProjection, mainCamera.viewRangeDg * degrees2radians, screenAspectRatio, nearClip, farClip);\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;absModelMatrix);\n        \/\/build Model-View (rotation) matrix for normals\n        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, pGS-&gt;absModelMatrixUnscaled);\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        \/\/subj&#039;s distance from camera\n        float cameraSpacePos&#91;4];\n        mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS-&gt;absCoords.pos, 1);\n        float zDistance = abs(cameraSpacePos&#91;2]);\n        float cotangentA = 1.0f \/ tanf(degrees2radians * mainCamera.viewRangeDg \/ 2.0f);\n        float halfScreenVertSizeInUnits = zDistance \/ cotangentA;\n        float sizeUnitPixelsSize = (float)screenSize&#91;1] \/ 2.0f \/ halfScreenVertSizeInUnits;\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, (float*)pGS-&gt;absModelMatrix, dirToMainLight, mainCamera.ownCoords.pos, sizeUnitPixelsSize, NULL);\n        }\n    }\n    \/\/synchronization\n    while (1) {\n        long long int currentMillis = getSystemMillis();\n        long long int millisSinceLastFrame = currentMillis - lastFrameMillis;\n        if (millisSinceLastFrame &gt;= millisPerFrame) {\n            lastFrameMillis = currentMillis;\n            break;\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    screenAspectRatio = (float)width \/ height;\n    glViewport(0, 0, width, height);\n    mainCamera.onScreenResize();\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}\nGameSubj* TheGame::newGameSubj(std::string subjClass) {\n    GameSubj* pGS = NULL;\n    if (subjClass.compare(&quot;&quot;) == 0)\n        pGS = (new GameSubj());\n    else if (subjClass.find(&quot;Car&quot;) == 0) {\n        if (subjClass.compare(&quot;CarWheel&quot;) == 0)\n            pGS = (new CarWheel());\n    }\n    if(pGS == NULL) {\n        mylog(&quot;ERROR in TheGame::newGameSubj. %s class not found\\n&quot;, subjClass.c_str());\n        return NULL;\n    }\n    myStrcpy_s(pGS-&gt;className, 32, subjClass.c_str());\n    return pGS;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>New #include: <em>car\/CarWheel.h<\/em><\/li><li>Function <em>newGameSubj(..)<\/em> handles new classes <\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now &#8211; reading\/processing <em>element <\/em>tag:<\/p>\n\n\n\n<p>15. Replace <em>ModelLoader.h<\/em> by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [49]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;XMLparser.h&quot;\n#include &quot;ModelBuilder.h&quot;\n#include &quot;GroupTransform.h&quot;\n#include &quot;MaterialAdjust.h&quot;\n\nclass ModelLoader : public XMLparser\n{\npublic:\n\tModelBuilder* pModelBuilder = NULL;\n\tbool ownModelBuilder = false;\n\tstd::vector&lt;GameSubj*&gt;* pSubjsVector = NULL;\n\tMaterialAdjust* pMaterialAdjust = NULL;\n\tint lineStartsAt = -1;\npublic:\n\tModelLoader(std::vector&lt;GameSubj*&gt;* pSubjsVector0, int subjN, ModelBuilder* pMB, std::string filePath) : XMLparser(filePath) {\n\t\tpSubjsVector = pSubjsVector0;\n\t\tif (pMB != NULL) {\n\t\t\townModelBuilder = false;\n\t\t\tpModelBuilder = pMB;\n\t\t}\n\t\telse {\n\t\t\townModelBuilder = true;\n\t\t\tpModelBuilder = new ModelBuilder();\n\t\t\tpModelBuilder-&gt;lockGroup(pModelBuilder);\n\t\t}\n\t\tpModelBuilder-&gt;useSubjN(pModelBuilder,subjN);\n\t};\n\tvirtual ~ModelLoader() {\n\t\tif (!ownModelBuilder)\n\t\t\treturn;\n\t\tpModelBuilder-&gt;buildDrawJobs(pModelBuilder, pSubjsVector);\n\t\tdelete pModelBuilder;\n\t};\n\tstatic int processTag_a(ModelLoader* pML); \/\/apply\n\tstatic int setValueFromIntHashMap(int* pInt, std::map&lt;std::string, int&gt; intHashMap, std::string varName, std::string tagStr);\n\tstatic int setTexture(ModelLoader* pML, int* pInt, std::string txName);\n\tstatic int setMaterialTextures(ModelLoader* pML, Material* pMT);\n\tstatic int fillProps_vs(VirtualShape* pVS, std::string tagStr); \/\/virtual shape\n\tstatic int fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML); \/\/Material\n\tstatic int fillProps_gt(GroupTransform* pGS, ModelBuilder* pMB, std::string tagStr);\n\tvirtual int processTag() { return processTag(this); };\n\tstatic int processTag(ModelLoader* pML);\n\tstatic int loadModel(std::vector&lt;GameSubj*&gt;* pSubjsVector0, std::string sourceFile, std::string subjClass);\n\tstatic int processTag_clone(ModelLoader* pML);\n\tstatic int addMark(char* marks, std::string newMark);\n\tstatic int processTag_do(ModelLoader* pML);\n\tstatic int processTag_a2mesh(ModelLoader* pML);\n\tstatic int processTag_element(ModelLoader* pML);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>16. Replace <em>ModelLoader.cpp<\/em> by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [139,140,141,142,143,144,145,146,147,626]; title: ; notranslate\" title=\"\">\n#include &quot;ModelLoader.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;TheGame.h&quot;\n#include &quot;DrawJob.h&quot;\n#include &quot;Texture.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;Polygon.h&quot;\n\nextern TheGame theGame; \n\nint ModelLoader::loadModel(std::vector&lt;GameSubj*&gt;* pSubjsVector0, std::string sourceFile, std::string subjClass) {\n\t\/\/returns element&#039;s (Subj) number or -1\n\t\/\/first - check if already loaded\n\tint totalSubjs = pSubjsVector0-&gt;size();\n\tfor (int subjN0 = totalSubjs - 1; subjN0 &gt;= 0; subjN0--) {\n\t\tGameSubj* pGS0 = pSubjsVector0-&gt;at(subjN0);\n\t\tif (pGS0 == NULL)\n\t\t\tcontinue;\n\t\tif (strcmp(pGS0-&gt;source, sourceFile.c_str()) != 0)\n\t\t\tcontinue;\n\t\t\/\/if here - model was already loaded - copy\n\t\tint subjN = pSubjsVector0-&gt;size();\n\t\tfor (int i = 0; i &lt; pGS0-&gt;totalNativeElements; i++) {\n\t\t\tGameSubj* pGS = pSubjsVector0-&gt;at(subjN0 + i)-&gt;clone();\n\t\t\tpGS-&gt;nInSubjsSet = pSubjsVector0-&gt;size();\n\t\t\tpSubjsVector0-&gt;push_back(pGS);\n\t\t}\n\t\tGameSubj* pGS = pSubjsVector0-&gt;at(subjN);\n\t\tpGS-&gt;totalElements = pGS-&gt;totalNativeElements;\n\t\tpGS-&gt;d2parent = 0;\n\t\tpGS-&gt;d2headTo = 0;\n\t\t\/\/restore original 1st DrawJob\n\t\treturn subjN;\n\t}\n\t\/\/if here - model wasn&#039;t loaded before - load\n\tint subjN = pSubjsVector0-&gt;size();\n\tGameSubj* pGS = theGame.newGameSubj(subjClass);\n\tpGS-&gt;pSubjsSet = pSubjsVector0;\n\tpGS-&gt;nInSubjsSet = subjN;\n\tmyStrcpy_s(pGS-&gt;source, 256, sourceFile.c_str());\n\tpSubjsVector0-&gt;push_back(pGS);\n\tModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);\n\tprocessSource(pML);\n\tdelete pML;\n\tpGS-&gt;totalNativeElements = pSubjsVector0-&gt;size() - subjN;\n\tpGS-&gt;totalElements = pGS-&gt;totalNativeElements;\n\treturn subjN;\n}\n\nint ModelLoader::setValueFromIntHashMap(int* pInt, std::map&lt;std::string, int&gt; intHashMap, std::string varName, std::string tagStr) {\n\tif (!varExists(varName, tagStr))\n\t\treturn 0;\n\tstd::string str0 = getStringValue(varName, tagStr);\n\tif (intHashMap.find(str0) == intHashMap.end()) {\n\t\tmylog(&quot;ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\\n&quot;, varName.c_str(), tagStr.c_str());\n\t\treturn -1;\n\t}\n\t*pInt = intHashMap&#91;getStringValue(varName, tagStr)];\n\treturn 1;\n}\nint ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tbool resetTexture = false;\n\tstd::string varName = txName + &quot;_use&quot;;\n\tif (varExists(varName, pML-&gt;currentTag)) {\n\t\tif (setValueFromIntHashMap(pInt, pMB-&gt;texturesHashMap, varName, pML-&gt;currentTag) == 0) {\n\t\t\tmylog(&quot;ERROR in ModelLoader::setTexture: texture not in hashMap: %s\\n&quot;, pML-&gt;currentTag.c_str());\n\t\t\treturn -1;\n\t\t}\n\t\tresetTexture = true;\n\t}\n\telse{\n\t\tvarName = txName + &quot;_src&quot;;\n\t\tif (varExists(varName, pML-&gt;currentTag)) {\n\t\t\tstd::string txFile = getStringValue(varName, pML-&gt;currentTag);\n\t\t\tvarName = txName + &quot;_ckey&quot;;\n\t\t\tunsigned int intCkey = 0;\n\t\t\tsetUintColorValue(&amp;intCkey, varName, pML-&gt;currentTag);\n\t\t\t*pInt = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);\n\t\t\tresetTexture = true;\n\t\t}\n\t}\n\tif(resetTexture)\n\t\treturn 1;\n\treturn 0; \/\/texture wasn&#039;t reset\n}\nint ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {\n\tif (setTexture(pML, &amp;pMT-&gt;uTex0, &quot;uTex0&quot;) &gt; 0)\n\t\tpMT-&gt;uColor.clear();\n\tsetTexture(pML, &amp;pMT-&gt;uTex1mask, &quot;uTex1mask&quot;);\n\tsetTexture(pML, &amp;pMT-&gt;uTex2nm, &quot;uTex2nm&quot;);\n\tsetTexture(pML, &amp;pMT-&gt;uTex3, &quot;uTex3&quot;);\n\treturn 1;\n}\nint ModelLoader::fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML) {\n\tsetCharsValue(pMT-&gt;shaderType, 20, &quot;mt_type&quot;, tagStr);\n\tsetMaterialTextures(pML, pMT);\n\t\/\/color\n\tif (varExists(&quot;uColor&quot;, tagStr)) {\n\t\tunsigned int uintColor = 0;\n\t\tsetUintColorValue(&amp;uintColor, &quot;uColor&quot;, tagStr);\n\t\tpMT-&gt;uColor.setUint32(uintColor);\n\t\tpMT-&gt;uTex0 = -1;\n\t}\n\t\/\/mylog(&quot;mt.uTex0=%d, mt.uTex1mask=%d\\n&quot;, mt.uTex0, mt.uTex1mask);\n\tif (varExists(&quot;primitiveType&quot;, tagStr)) {\n\t\tstd::string str0 = getStringValue(&quot;primitiveType&quot;, tagStr);\n\t\tif (str0.compare(&quot;GL_POINTS&quot;) == 0) pMT-&gt;primitiveType = GL_POINTS;\n\t\telse if (str0.compare(&quot;GL_LINES&quot;) == 0) pMT-&gt;primitiveType = GL_LINES;\n\t\telse if (str0.compare(&quot;GL_LINE_STRIP&quot;) == 0) pMT-&gt;primitiveType = GL_LINE_STRIP;\n\t\telse if (str0.compare(&quot;GL_LINE_LOOP&quot;) == 0) pMT-&gt;primitiveType = GL_LINE_LOOP;\n\t\telse if (str0.compare(&quot;GL_TRIANGLE_STRIP&quot;) == 0) pMT-&gt;primitiveType = GL_TRIANGLE_STRIP;\n\t\telse if (str0.compare(&quot;GL_TRIANGLE_FAN&quot;) == 0) pMT-&gt;primitiveType = GL_TRIANGLE_FAN;\n\t\telse pMT-&gt;primitiveType = GL_TRIANGLES;\n\t}\n\tsetIntValue(&amp;pMT-&gt;uTex1alphaChannelN, &quot;uTex1alphaChannelN&quot;, tagStr);\n\tsetIntValue(&amp;pMT-&gt;uTex0translateChannelN, &quot;uTex0translateChannelN&quot;, tagStr);\n\tsetFloatValue(&amp;pMT-&gt;uAlphaFactor, &quot;uAlphaFactor&quot;, tagStr);\n\tif (pMT-&gt;uAlphaFactor &lt; 1)\n\t\tpMT-&gt;uAlphaBlending = 1;\n\tsetIntBoolValue(&amp;pMT-&gt;uAlphaBlending, &quot;uAlphaBlending&quot;, tagStr);\n\tif (pMT-&gt;uAlphaBlending &gt; 0)\n\t\tpMT-&gt;zBufferUpdate = 0;\n\tsetFloatValue(&amp;pMT-&gt;uAmbient, &quot;uAmbient&quot;, tagStr);\n\tsetFloatValue(&amp;pMT-&gt;uSpecularIntencity, &quot;uSpecularIntencity&quot;, tagStr);\n\tsetFloatValue(&amp;pMT-&gt;uSpecularMinDot, &quot;uSpecularMinDot&quot;, tagStr);\n\tsetFloatValue(&amp;pMT-&gt;uSpecularPowerOf, &quot;uSpecularPowerOf&quot;, tagStr);\n\n\tsetFloatValue(&amp;pMT-&gt;lineWidth, &quot;lineWidth&quot;, tagStr);\n\tsetIntBoolValue(&amp;pMT-&gt;zBuffer, &quot;zBuffer&quot;, tagStr);\n\tif (pMT-&gt;zBuffer &lt; 1)\n\t\tpMT-&gt;zBufferUpdate = 0;\n\tsetIntBoolValue(&amp;pMT-&gt;zBufferUpdate, &quot;zBufferUpdate&quot;, tagStr);\n\n\treturn 1;\n}\nint ModelLoader::processTag(ModelLoader* pML) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tif (pML-&gt;tagName.compare(&quot;element&quot;) == 0)\n\t\treturn processTag_element(pML);\n\tif (pML-&gt;tagName.compare(&quot;\/element&quot;) == 0) {\n\t\t\/\/restore previous useSubjN from stack\n\t\tint subjN = pMB-&gt;usingSubjsStack.back();\n\t\tpMB-&gt;usingSubjsStack.pop_back();\n\t\tpMB-&gt;useSubjN(pMB, subjN);\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;texture_as&quot;) == 0) {\n\t\t\/\/saves texture N in texturesMap under given name\n\t\tstd::string keyName = getStringValue(&quot;texture_as&quot;, pML-&gt;currentTag);\n\t\tif (pMB-&gt;texturesHashMap.find(keyName) != pMB-&gt;texturesHashMap.end())\n\t\t\treturn pMB-&gt;texturesHashMap&#91;keyName];\n\t\telse { \/\/add new\n\t\t\tstd::string txFile = getStringValue(&quot;src&quot;, pML-&gt;currentTag);\n\t\t\tunsigned int intCkey = 0;\n\t\t\tsetUintColorValue(&amp;intCkey, &quot;ckey&quot;, pML-&gt;currentTag);\n\t\t\tint txN = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);\n\t\t\tpMB-&gt;texturesHashMap&#91;keyName] = txN;\n\t\t\t\/\/mylog(&quot;%s=%d\\n&quot;, keyName.c_str(), pMB-&gt;texturesMap&#91;keyName]);\n\t\t\treturn txN;\n\t\t}\n\t}\n\tif (pML-&gt;tagName.compare(&quot;mt_type&quot;) == 0) {\n\t\t\/\/sets current material\n\t\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\t\tif (!pML-&gt;closedTag) {\n\t\t\t\/\/save previous material in stack\n\t\t\tif (pMB-&gt;usingMaterialN &gt;= 0)\n\t\t\t\tpMB-&gt;materialsStack.push_back(pMB-&gt;usingMaterialN);\n\t\t}\n\t\tMaterial mt;\n\t\tfillProps_mt(&amp;mt, pML-&gt;currentTag, pML);\n\t\tpMB-&gt;usingMaterialN = pMB-&gt;getMaterialN(pMB, &amp;mt);\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;\/mt_type&quot;) == 0) {\n\t\t\/\/restore previous material\n\t\tif (pMB-&gt;materialsStack.size() &gt; 0) {\n\t\t\tpMB-&gt;usingMaterialN = pMB-&gt;materialsStack.back();\n\t\t\tpMB-&gt;materialsStack.pop_back();\n\t\t}\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;vs&quot;) == 0) {\n\t\t\/\/sets virtual shape\n\t\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\t\tif (pML-&gt;closedTag) {\n\t\t\tif (pMB-&gt;pCurrentVShape != NULL)\n\t\t\t\tdelete pMB-&gt;pCurrentVShape;\n\t\t}\n\t\telse { \/\/open tag\n\t\t\t\/\/save previous vshape in stack\n\t\t\tif (pMB-&gt;pCurrentVShape != NULL)\n\t\t\t\tpMB-&gt;vShapesStack.push_back(pMB-&gt;pCurrentVShape);\n\t\t}\n\t\tpMB-&gt;pCurrentVShape = new VirtualShape();\n\t\tfillProps_vs(pMB-&gt;pCurrentVShape, pML-&gt;currentTag);\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;\/vs&quot;) == 0) {\n\t\t\/\/restore previous virtual shape\n\t\tif (pMB-&gt;vShapesStack.size() &gt; 0) {\n\t\t\tif (pMB-&gt;pCurrentVShape != NULL)\n\t\t\t\tdelete(pMB-&gt;pCurrentVShape);\n\t\t\tpMB-&gt;pCurrentVShape = pMB-&gt;vShapesStack.back();\n\t\t\tpMB-&gt;vShapesStack.pop_back();\n\t\t}\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;group&quot;) == 0) {\n\t\tstd::string notAllowed&#91;] = { &quot;pxyz&quot;,&quot;axyz&quot;,&quot;align&quot;,&quot;headTo&quot; };\n\t\tint notAllowedLn = sizeof(notAllowed) \/ sizeof(notAllowed&#91;0]);\n\t\tfor (int i = 0; i &lt; notAllowedLn; i++)\n\t\t\tif (varExists(notAllowed&#91;i], pML-&gt;currentTag)) {\n\t\t\t\tmylog(&quot;ERROR in ModelLoader::processTag: use %s in &lt;\/group&gt;: %s\\n&quot;, notAllowed&#91;i].c_str(), pML-&gt;currentTag.c_str());\n\t\t\t\treturn -1;\n\t\t\t}\n\t\tpMB-&gt;lockGroup(pMB);\n\t\t\/\/mark\n\t\tif (varExists(&quot;mark&quot;, pML-&gt;currentTag))\n\t\t\taddMark(pMB-&gt;pCurrentGroup-&gt;marks, getStringValue(&quot;mark&quot;, pML-&gt;currentTag));\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;\/group&quot;) == 0) {\n\t\tGroupTransform gt;\n\t\tfillProps_gt(&amp;gt, pMB, pML-&gt;currentTag);\n\t\tgt.executeGroupTransform(pMB);\n\n\t\tpMB-&gt;releaseGroup(pMB);\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;a&quot;) == 0)\n\t\treturn processTag_a(pML); \/\/apply \n\tif (pML-&gt;tagName.compare(&quot;clone&quot;) == 0)\n\t\treturn processTag_clone(pML);\n\tif (pML-&gt;tagName.compare(&quot;\/clone&quot;) == 0)\n\t\treturn processTag_clone(pML);\n\tif (pML-&gt;tagName.compare(&quot;do&quot;) == 0)\n\t\treturn processTag_do(pML);\n\tif (pML-&gt;tagName.compare(&quot;a2mesh&quot;) == 0)\n\t\treturn processTag_a2mesh(pML);\n\tif (pML-&gt;tagName.compare(&quot;mt_adjust&quot;) == 0) {\n\t\tif (pML-&gt;pMaterialAdjust != NULL)\n\t\t\tmylog(&quot;ERROR in ModelLoader::processTag %s, pMaterialAdjust is still busy. File: %s\\n&quot;, pML-&gt;currentTag.c_str(), pML-&gt;fullPath.c_str());\n\t\tpML-&gt;pMaterialAdjust = new (MaterialAdjust);\n\t\tfillProps_mt(pML-&gt;pMaterialAdjust, pML-&gt;currentTag, pML);\n\t\tpML-&gt;pMaterialAdjust-&gt;setWhat2adjust(pML-&gt;pMaterialAdjust, pML-&gt;currentTag);\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;\/mt_adjust&quot;) == 0) {\n\t\tif (pML-&gt;pMaterialAdjust != NULL) {\n\t\t\tdelete pML-&gt;pMaterialAdjust;\n\t\t\tpML-&gt;pMaterialAdjust = NULL;\n\t\t}\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;line&quot;) == 0) {\n\t\tMaterial mt;\n\t\t\/\/save previous material in stack\n\t\tif (pMB-&gt;usingMaterialN &gt;= 0){\n\t\t\tpMB-&gt;materialsStack.push_back(pMB-&gt;usingMaterialN);\n\t\t\tmemcpy(&amp;mt, pMB-&gt;materialsList.at(pMB-&gt;usingMaterialN),sizeof(Material));\n\t\t}\n\t\tmt.primitiveType = GL_LINE_STRIP;\n\t\tfillProps_mt(&amp;mt, pML-&gt;currentTag, pML);\n\t\tpMB-&gt;usingMaterialN = pMB-&gt;getMaterialN(pMB, &amp;mt);\n\t\t\/\/line starts\n\t\tpML-&gt;lineStartsAt = pMB-&gt;vertices.size();\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;\/line&quot;) == 0) {\n\t\tpMB-&gt;vertices.back()-&gt;endOfSequence = 1;\n\t\tpML-&gt;lineStartsAt = -1;\n\t\t\/\/restore previous material\n\t\tif (pMB-&gt;materialsStack.size() &gt; 0) {\n\t\t\tpMB-&gt;usingMaterialN = pMB-&gt;materialsStack.back();\n\t\t\tpMB-&gt;materialsStack.pop_back();\n\t\t}\n\t\treturn 1;\n\t}\n\tif (pML-&gt;tagName.compare(&quot;p&quot;) == 0) {\n\t\t\/\/line point\n\t\tVertex01* pV = new Vertex01();\n\t\tif (pMB-&gt;vertices.size() &gt; pML-&gt;lineStartsAt)\n\t\t\tmemcpy(pV, pMB-&gt;vertices.back(), sizeof(Vertex01));\n\t\tpV-&gt;subjN = pMB-&gt;usingSubjN;\n\t\tpV-&gt;materialN = pMB-&gt;usingMaterialN;\n\t\tsetFloatArray(pV-&gt;aPos, 3, &quot;pxyz&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;pV-&gt;aPos&#91;0], &quot;px&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;pV-&gt;aPos&#91;1], &quot;py&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;pV-&gt;aPos&#91;2], &quot;pz&quot;, pML-&gt;currentTag);\n\t\tfloat dPos&#91;3] = { 0,0,0 };\n\t\tsetFloatArray(dPos, 3, &quot;dxyz&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;dPos&#91;0], &quot;dx&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;dPos&#91;1], &quot;dy&quot;, pML-&gt;currentTag);\n\t\tsetFloatValue(&amp;dPos&#91;2], &quot;dz&quot;, pML-&gt;currentTag);\n\t\tif (!v3equals(dPos, 0))\n\t\t\tfor (int i = 0; i &lt; 3; i++)\n\t\t\t\tpV-&gt;aPos&#91;i] += dPos&#91;i];\n\t\tpMB-&gt;vertices.push_back(pV);\n\t\treturn 1;\n\t}\n\tmylog(&quot;ERROR in ModelLoader::processTag, unhandled tag %s, file %s\\n&quot;, pML-&gt;currentTag.c_str(), pML-&gt;fullPath.c_str());\n\t\/\/mylog(&quot;======File:\\n%s----------\\n&quot;, pML-&gt;pData);\n\treturn -1;\n}\n\nint ModelLoader::fillProps_vs(VirtualShape* pVS, std::string tagStr) {\n\t\/\/sets virtual shape\n\tsetCharsValue(pVS-&gt;shapeType, 20, &quot;vs&quot;, tagStr);\n\tsetFloatArray(pVS-&gt;whl, 3, &quot;whl&quot;, tagStr);\n\t\/\/extensions\n\tfloat ext;\n\tif (varExists(&quot;ext&quot;, tagStr)) {\n\t\tsetFloatValue(&amp;ext, &quot;ext&quot;, tagStr);\n\t\tpVS-&gt;setExt(ext);\n\t}\n\tif (varExists(&quot;extX&quot;, tagStr)) {\n\t\tsetFloatValue(&amp;ext, &quot;extX&quot;, tagStr);\n\t\tpVS-&gt;setExtX(ext);\n\t}\n\tif (varExists(&quot;extY&quot;, tagStr)) {\n\t\tsetFloatValue(&amp;ext, &quot;extY&quot;, tagStr);\n\t\tpVS-&gt;setExtY(ext);\n\t}\n\tif (varExists(&quot;extZ&quot;, tagStr)) {\n\t\tsetFloatValue(&amp;ext, &quot;extZ&quot;, tagStr);\n\t\tpVS-&gt;setExtZ(ext);\n\t}\n\tsetFloatValue(&amp;pVS-&gt;extU, &quot;extU&quot;, tagStr);\n\tsetFloatValue(&amp;pVS-&gt;extD, &quot;extD&quot;, tagStr);\n\tsetFloatValue(&amp;pVS-&gt;extL, &quot;extL&quot;, tagStr);\n\tsetFloatValue(&amp;pVS-&gt;extR, &quot;extR&quot;, tagStr);\n\tsetFloatValue(&amp;pVS-&gt;extF, &quot;extF&quot;, tagStr);\n\tsetFloatValue(&amp;pVS-&gt;extB, &quot;extB&quot;, tagStr);\n\t\/\/sections\n\tsetIntValue(&amp;pVS-&gt;sectionsR, &quot;sectR&quot;, tagStr);\n\tsetIntValue(&amp;pVS-&gt;sections&#91;0], &quot;sectX&quot;, tagStr);\n\tsetIntValue(&amp;pVS-&gt;sections&#91;1], &quot;sectY&quot;, tagStr);\n\tsetIntValue(&amp;pVS-&gt;sections&#91;2], &quot;sectZ&quot;, tagStr);\n\n\t\/\/mylog(&quot;pVS-&gt;shapeType=%s whl=%fx%fx%f\\n&quot;, pVS-&gt;shapeType, pVS-&gt;whl&#91;0], pVS-&gt;whl&#91;1], pVS-&gt;whl&#91;2]);\n\treturn 1;\n}\nint ModelLoader::processTag_a(ModelLoader* pML) {\n\t\/\/apply\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tstd::string tagStr = pML-&gt;currentTag;\n\tpMB-&gt;lockGroup(pMB);\n\t\/\/mark\n\tif (varExists(&quot;mark&quot;, tagStr))\n\t\taddMark(pMB-&gt;pCurrentGroup-&gt;marks, getStringValue(&quot;mark&quot;, tagStr));\n\n\tstd::vector&lt;std::string&gt; applyTosVector = splitString(pML-&gt;getStringValue(&quot;a&quot;, tagStr), &quot;,&quot;);\n\tMaterial* pMT = pMB-&gt;materialsList.at(pMB-&gt;usingMaterialN);\n\tint texN = pMT-&gt;uTex1mask;\n\tif (texN &lt; 0)\n\t\ttexN = pMT-&gt;uTex0;\n\tfloat xywh&#91;4] = { 0,0,1,1 };\n\tTexCoords* pTC = NULL;\n\tif (varExists(&quot;xywh&quot;, tagStr)) {\n\t\tsetFloatArray(xywh, 4, &quot;xywh&quot;, tagStr);\n\t\tstd::string flipStr = getStringValue(&quot;flip&quot;, tagStr);\n\t\tTexCoords tc;\n\t\ttc.set(texN, xywh&#91;0], xywh&#91;1], xywh&#91;2], xywh&#91;3], flipStr);\n\t\tpTC = &amp;tc;\n\t}\n\tTexCoords* pTC2nm = NULL;\n\tif (varExists(&quot;xywh2nm&quot;, tagStr)) {\n\t\tsetFloatArray(xywh, 4, &quot;xywh2nm&quot;, tagStr);\n\t\tstd::string flipStr = getStringValue(&quot;flip2nm&quot;, tagStr);\n\t\tTexCoords tc2nm;\n\t\ttc2nm.set(pMT-&gt;uTex2nm, xywh&#91;0], xywh&#91;1], xywh&#91;2], xywh&#91;3], flipStr);\n\t\tpTC2nm = &amp;tc2nm;\n\t}\n\t\/\/adjusted VirtualShape\n\tVirtualShape* pVS_a = new VirtualShape(*pMB-&gt;pCurrentVShape);\n\tfillProps_vs(pVS_a, tagStr);\n\n\tfor (int aN = 0; aN &lt; (int)applyTosVector.size(); aN++) {\n\t\tpMB-&gt;buildFace(pMB, applyTosVector.at(aN), pVS_a, pTC, pTC2nm);\n\t}\n\tdelete pVS_a;\n\t\/\/mylog(&quot;vertsN=%d\\n&quot;,pMB-&gt;vertices.size());\n\n\tGroupTransform GT_a;\n\tfillProps_gt(&amp;GT_a, pMB, tagStr);\n\tGT_a.executeGroupTransform(pMB);\n\n\tpMB-&gt;releaseGroup(pMB);\n\treturn 1;\n}\nint ModelLoader::processTag_clone(ModelLoader* pML) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tif (pML-&gt;tagName.compare(&quot;clone&quot;) == 0) {\n\t\t\/\/mark what to clone\n\t\tGroupTransform gt;\n\t\tgt.pGroup = pMB-&gt;pLastClosedGroup;\n\t\tgt.flagSelection(&amp;gt, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles);\n\n\t\t\/\/cloning\n\t\tpMB-&gt;lockGroup(pMB);\n\t\tgt.cloneFlagged(pMB, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles);\n\t}\n\tGroupTransform gt;\n\tfillProps_gt(&amp;gt, pMB, pML-&gt;currentTag);\n\tgt.executeGroupTransform(pMB);\n\n\tif (pML-&gt;tagName.compare(&quot;\/clone&quot;) == 0 || pML-&gt;closedTag) {\n\t\tpMB-&gt;releaseGroup(pMB);\n\t}\n\treturn 1;\n}\nint ModelLoader::addMark(char* marks, std::string newMark) {\n\tif (newMark.empty())\n\t\treturn 0;\n\tstd::string allMarks;\n\tallMarks.assign(marks);\n\tallMarks.append(&quot;&lt;&quot; + newMark + &quot;&gt;&quot;);\n\tmyStrcpy_s(marks, 124, allMarks.c_str());\n\treturn 1;\n}\nint ModelLoader::fillProps_gt(GroupTransform* pGT, ModelBuilder* pMB, std::string tagStr) {\n\tpGT-&gt;pGroup = pMB-&gt;pCurrentGroup;\n\t\/\/position\n\tsetFloatArray(pGT-&gt;shift, 3, &quot;pxyz&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;shift&#91;0], &quot;px&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;shift&#91;1], &quot;py&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;shift&#91;2], &quot;pz&quot;, tagStr);\n\t\/\/angles\n\tsetFloatArray(pGT-&gt;spin, 3, &quot;axyz&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;spin&#91;0], &quot;ax&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;spin&#91;1], &quot;ay&quot;, tagStr);\n\tsetFloatValue(&amp;pGT-&gt;spin&#91;2], &quot;az&quot;, tagStr);\n\t\/\/scale\n\tsetFloatArray(pGT-&gt;scale, 3, &quot;scale&quot;, tagStr);\n\n\tpGT-&gt;onThe = getStringValue(&quot;onThe&quot;, tagStr);\n\tpGT-&gt;allign = getStringValue(&quot;allign&quot;, tagStr);\n\tpGT-&gt;headZto = getStringValue(&quot;headZto&quot;, tagStr);\n\t\/\/limit to\n\tif (varExists(&quot;all&quot;, tagStr))\n\t\tpGT-&gt;pGroup = NULL;\n\tif (varExists(&quot;lastClosedGroup&quot;, tagStr))\n\t\tpGT-&gt;pGroup = pMB-&gt;pLastClosedGroup;\n\tif (varExists(&quot;markedAs&quot;, tagStr))\n\t\tpGT-&gt;limit2mark(pGT, getStringValue(&quot;markedAs&quot;, tagStr));\n\tsetFloatArray(pGT-&gt;pMin, 3, &quot;xyzMin&quot;, tagStr);\n\tsetFloatArray(pGT-&gt;pMax, 3, &quot;xyzMax&quot;, tagStr);\n\n\tif (varExists(&quot;sizeD&quot;, tagStr)) { \/\/re-size\n\t\tfloat sizeD&#91;3];\n\t\tsetFloatArray(sizeD, 3, &quot;sizeD&quot;, tagStr);\n\t\t\/\/bounding box\n\t\tpGT-&gt;flagSelection(pGT, &amp;pMB-&gt;vertices, NULL);\n\t\tfloat bbMin&#91;3];\n\t\tfloat bbMax&#91;3];\n\t\tpGT-&gt;buildBoundingBoxFlagged(bbMin, bbMax, &amp;pMB-&gt;vertices);\n\t\tfor (int i = 0; i &lt; 3; i++) {\n\t\t\tfloat size = bbMax&#91;i] - bbMin&#91;i];\n\t\t\tpGT-&gt;scale&#91;i] = (size + sizeD&#91;i]) \/ size;\n\t\t}\n\t}\n\treturn 1;\n}\nint ModelLoader::processTag_do(ModelLoader* pML) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tGroupTransform gt;\n\tfillProps_gt(&amp;gt, pMB, pML-&gt;currentTag);\n\tgt.flagSelection(&amp;gt, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles);\n\tgt.transformFlagged(&amp;gt, &amp;pMB-&gt;vertices);\n\treturn 1;\n}\nint ModelLoader::processTag_a2mesh(ModelLoader* pML) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tstd::string tagStr = pML-&gt;currentTag;\n\tGroupTransform gt;\n\tfillProps_gt(&amp;gt, pMB, pML-&gt;currentTag);\n\tgt.flagSelection(&amp;gt, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles);\n\t\/\/clone a copy\n\tstd::vector&lt;Vertex01*&gt; vx1;\n\tstd::vector&lt;Triangle01*&gt; tr1;\n\tgt.cloneFlagged(NULL, &amp;vx1, &amp;tr1, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles);\n\t\/\/ build transform and inverted martrices\n\tmat4x4 transformMatrix;\n\tgt.buildTransformMatrix(&amp;gt, &amp;transformMatrix);\n\tmat4x4 transformMatrixInverted;\n\tmat4x4_invert(transformMatrixInverted, transformMatrix);\n\t\/\/move\/rotate cloned\n\tgt.flagAll(&amp;vx1, &amp;tr1);\n\t\/\/gt.transformFlagged(&amp;pMB-&gt;vertices, &amp;transformMatrixInverted);\n\tgt.transformFlaggedMx(&amp;vx1, &amp;transformMatrixInverted);\n\n\t\/\/gt.cloneFlagged(pMB, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles, &amp;vx1, &amp;tr1);\n\n\tfloat wh&#91;2];\n\tsetFloatArray(wh, 2, &quot;wh&quot;, tagStr);\n\tPolygon frame;\n\tframe.setRectangle(&amp;frame, wh&#91;0], wh&#91;1]);\n\t\/\/destination arrays\n\tstd::vector&lt;Vertex01*&gt; vx2;\n\tstd::vector&lt;Triangle01*&gt; tr2;\n\tPolygon triangle;\n\tfor (int i = tr1.size() - 1; i &gt;= 0; i--) {\n\t\ttriangle.setTriangle(&amp;triangle, tr1.at(i), &amp;vx1);\n\t\tPolygon intersection;\n\t\tint pointsN = Polygon::xyIntersection(&amp;intersection, &amp;frame, &amp;triangle);\n\t\tif (pointsN &gt; 2) {\n\t\t\tPolygon::buildTriangles(&amp;intersection);\n\t\t\tGroupTransform::flagAll(&amp;intersection.vertices, &amp;intersection.triangles);\n\t\t\tGroupTransform::cloneFlagged(NULL, &amp;vx2, &amp;tr2, &amp;intersection.vertices, &amp;intersection.triangles);\n\t\t}\n\t}\n\tgt.flagAll(&amp;vx2, &amp;tr2);\n\t\/\/at this point we have cutted fragment facing us\n\tint vxTotal = vx2.size();\n\tint trTotal = tr2.size();\n\t\/\/apply adjusted material ?\n\tif (pML-&gt;pMaterialAdjust != NULL) {\n\t\t\/\/scan vertices to find new (unupdated) material\n\t\tint materialNsrc = -1; \/\/which N to replace\n\t\tint materialNdst = -1; \/\/replace by N \n\t\tfor (int vN = 0; vN &lt; vxTotal; vN++) {\n\t\t\tVertex01* pV = vx2.at(vN);\n\t\t\tif (pV-&gt;flag &lt; 0)\n\t\t\t\tcontinue;\n\t\t\tif (materialNsrc == pV-&gt;materialN)\n\t\t\t\tcontinue;\n\t\t\t\/\/have new material\n\t\t\tmaterialNsrc = pV-&gt;materialN;\n\t\t\tMaterial mt;\n\t\t\tMaterial* pMt0 = pMB-&gt;materialsList.at(materialNsrc);\n\t\t\tmemcpy(&amp;mt, pMt0, sizeof(Material));\n\t\t\t\/\/modify material\n\t\t\tMaterialAdjust::adjust(&amp;mt, pML-&gt;pMaterialAdjust);\n\t\t\tmaterialNdst = pMB-&gt;getMaterialN(pMB, &amp;mt);\n\t\t\tif (materialNsrc != materialNdst) {\n\t\t\t\t\/\/replace mtN in vx and tr arrays\n\t\t\t\tfor (int vN2 = vN; vN2 &lt; vxTotal; vN2++) {\n\t\t\t\t\tVertex01* pV2 = vx2.at(vN2);\n\t\t\t\t\tif (pV2-&gt;flag &lt; 0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (materialNsrc == pV2-&gt;materialN)\n\t\t\t\t\t\tpV2-&gt;materialN = materialNdst;\n\t\t\t\t}\n\t\t\t\tfor (int tN2 = 0; tN2 &lt; trTotal; tN2++) {\n\t\t\t\t\tTriangle01* pT2 = tr2.at(tN2);\n\t\t\t\t\tif (pT2-&gt;flag &lt; 0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (materialNsrc == pT2-&gt;materialN)\n\t\t\t\t\t\tpT2-&gt;materialN = materialNdst;\n\t\t\t\t}\n\t\t\t\tmaterialNsrc = materialNdst;\n\t\t\t}\n\t\t}\n\t}\n\telse { \/\/ pML-&gt;pMaterialAdjust == NULL, use pMB-&gt;usingMaterialN\n\t\tfor (int vN2 = 0; vN2 &lt; vxTotal; vN2++) {\n\t\t\tVertex01* pV2 = vx2.at(vN2);\n\t\t\tif (pV2-&gt;flag &lt; 0)\n\t\t\t\tcontinue;\n\t\t\tpV2-&gt;materialN = pMB-&gt;usingMaterialN;\n\t\t}\n\t\tfor (int tN2 = 0; tN2 &lt; trTotal; tN2++) {\n\t\t\tTriangle01* pT2 = tr2.at(tN2);\n\t\t\tif (pT2-&gt;flag &lt; 0)\n\t\t\t\tcontinue;\n\t\t\tpT2-&gt;materialN = pMB-&gt;usingMaterialN;\n\t\t}\n\t}\n\t\/\/apply xywh\/2nm ?\n\tif (varExists(&quot;xywh&quot;, tagStr) || varExists(&quot;xywh2nm&quot;, tagStr)) {\n\t\tMaterial* pMT = pMB-&gt;materialsList.at(vx2.at(0)-&gt;materialN);\n\t\tfloat xywh&#91;4] = { 0,0,1,1 };\n\t\tTexCoords* pTC = NULL;\n\t\tif (varExists(&quot;xywh&quot;, tagStr)) {\n\t\t\tsetFloatArray(xywh, 4, &quot;xywh&quot;, tagStr);\n\t\t\tstd::string flipStr = getStringValue(&quot;flip&quot;, tagStr);\n\t\t\tint texN = pMT-&gt;uTex1mask;\n\t\t\tif (texN &lt; 0)\n\t\t\t\ttexN = pMT-&gt;uTex0;\n\t\t\tTexCoords tc;\n\t\t\ttc.set(texN, xywh&#91;0], xywh&#91;1], xywh&#91;2], xywh&#91;3], flipStr);\n\t\t\tpTC = &amp;tc;\n\t\t}\n\t\tTexCoords* pTC2nm = NULL;\n\t\tif (varExists(&quot;xywh2nm&quot;, tagStr)) {\n\t\t\tsetFloatArray(xywh, 4, &quot;xywh2nm&quot;, tagStr);\n\t\t\tstd::string flipStr = getStringValue(&quot;flip2nm&quot;, tagStr);\n\t\t\tTexCoords tc2nm;\n\t\t\ttc2nm.set(pMT-&gt;uTex2nm, xywh&#91;0], xywh&#91;1], xywh&#91;2], xywh&#91;3], flipStr);\n\t\t\tpTC2nm = &amp;tc2nm;\n\t\t}\n\t\tpMB-&gt;applyTexture2flagged(&amp;vx2, &quot;front&quot;, pTC, false);\n\t\tpMB-&gt;applyTexture2flagged(&amp;vx2, &quot;front&quot;, pTC2nm, true);\n\t}\n\n\tfloat detachBy =0;\n\tsetFloatValue(&amp;detachBy, &quot;detachBy&quot;, tagStr);\n\tif (detachBy != 0) {\n\t\tmat4x4 mx;\n\t\tmat4x4_translate(mx, 0, 0, detachBy);\n\t\tgt.transformFlaggedMx(&amp;vx2, &amp;mx);\n\t}\n\t\/\/move\/rotate back\n\tgt.transformFlaggedMx(&amp;vx2, &amp;transformMatrix);\n\t\/\/clone back to modelBuilder arrays\n\tgt.cloneFlagged(pMB, &amp;pMB-&gt;vertices, &amp;pMB-&gt;triangles, &amp;vx2, &amp;tr2);\n\n\t\/\/clear memory\n\tfor (int i = vx1.size() - 1; i &gt;= 0; i--)\n\t\tdelete vx1.at(i);\n\tvx1.clear();\n\tfor (int i = tr1.size() - 1; i &gt;= 0; i--)\n\t\tdelete tr1.at(i);\n\ttr1.clear();\n\tfor (int i = vx2.size() - 1; i &gt;= 0; i--)\n\t\tdelete vx2.at(i);\n\tvx2.clear();\n\tfor (int i = tr2.size() - 1; i &gt;= 0; i--)\n\t\tdelete tr2.at(i);\n\ttr2.clear();\n\n\treturn 1;\n}\nint ModelLoader::processTag_element(ModelLoader* pML) {\n\tModelBuilder* pMB = pML-&gt;pModelBuilder;\n\tstd::string tagStr = pML-&gt;currentTag;\n\tstd::string sourceFile = getStringValue(&quot;element&quot;, tagStr);\n\tstd::string subjClass = getStringValue(&quot;class&quot;, tagStr);\n\tstd::vector&lt;GameSubj*&gt;* pSubjsVector0 = pML-&gt;pSubjsVector;\n\tint subjN = -1;\n\tif (!sourceFile.empty()) {\n\t\tsourceFile = buildFullPath(pML, sourceFile);\n\t\tsubjN = loadModel(pSubjsVector0, sourceFile, subjClass);\n\t}\n\telse { \/\/sourceFile not specified\n\t\tsubjN = pML-&gt;pSubjsVector-&gt;size();\n\t\tGameSubj* pGS = theGame.newGameSubj(subjClass);\n\t\tpGS-&gt;pSubjsSet = pSubjsVector0;\n\t\tpGS-&gt;nInSubjsSet = subjN;\n\t\tpML-&gt;pSubjsVector-&gt;push_back(pGS);\n\t\tpGS-&gt;totalNativeElements = 1;\n\t\tpGS-&gt;totalElements = 1;\n\t\tif (!pML-&gt;closedTag) { \/\/DrawJobs will follow\n\t\t\tpMB-&gt;usingSubjsStack.push_back(pMB-&gt;usingSubjN);\n\t\t\tpMB-&gt;useSubjN(pMB, subjN);\n\t\t}\n\t}\n\t\/\/keep reading tag\n\tGameSubj* pGS = pSubjsVector0-&gt;at(subjN);\n\tint rootN = pMB-&gt;subjNumbersList.at(0);\n\tstd::string attachTo = getStringValue(&quot;attachTo&quot;, tagStr);\n\tif (attachTo.empty()) \/\/attach to root\n\t\tpGS-&gt;d2parent = subjN - rootN;\n\telse {\n\t\t\/\/find parent by name\n\t\tfor (int sN = subjN - 1; sN &gt;= rootN; sN--) {\n\t\t\tif (strcmp(pSubjsVector0-&gt;at(sN)-&gt;name, attachTo.c_str()) == 0) {\n\t\t\t\tpGS-&gt;d2parent = subjN - sN;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tstd::string headTo = getStringValue(&quot;headTo&quot;, tagStr);\n\tif (!headTo.empty()) { \/\/find headTo by name\n\t\tfor (int sN = subjN - 1; sN &gt;= rootN; sN--) {\n\t\t\tif (strcmp(pSubjsVector0-&gt;at(sN)-&gt;name, headTo.c_str()) == 0) {\n\t\t\t\tpGS-&gt;d2headTo = subjN - sN;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tfloat xyz&#91;3]={0,0,0};\n\t\/\/position\n\tsetFloatArray(xyz, 3, &quot;pxyz&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;0], &quot;px&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;1], &quot;py&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;2], &quot;pz&quot;, tagStr);\n\tv3copy(pGS-&gt;ownCoords.pos, xyz);\n\t\/\/angles\n\tv3set(xyz, 0,0,0);\n\tsetFloatArray(xyz, 3, &quot;axyz&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;0], &quot;ax&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;1], &quot;ay&quot;, tagStr);\n\tsetFloatValue(&amp;xyz&#91;2], &quot;az&quot;, tagStr);\n\tv3set(pGS-&gt;ownCoords.eulerDg, xyz&#91;0], xyz&#91;1], xyz&#91;2]);\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>When creating <em>GameSubj<\/em>, we assigning <em>pGS-&gt;pSubjsSet = pSubjsVector0;<\/em><\/li><li>In ModelLoader::loadModel(): if such model was already loaded, we are making a copy, reusing it&#8217;s DrawJobs.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>For handling loading multiple models in <em>ModelBuilder <\/em>we have new vector\/stack <em>std::vector usingSubjsStack;<\/em><\/p>\n\n\n\n<p>17. Replace <em>ModelBuilder1base.h<\/em> by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [19]; 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#include &lt;map&gt;\n\nclass ModelBuilder1base\n{\npublic:\n\tstd::vector&lt;Vertex01*&gt; vertices;\n\tstd::vector&lt;Triangle01*&gt; triangles;\n\tstd::vector&lt;int&gt; subjNumbersList;\n\tint usingSubjN = -1;\n\tstd::vector&lt;int&gt; usingSubjsStack;\n\n\tstd::vector&lt;Group01*&gt; groupsStack;\n\tGroup01* pCurrentGroup = NULL;\n\tGroup01* pLastClosedGroup = NULL;\n\t\n\tstd::vector&lt;VirtualShape*&gt; vShapesStack;\n\tVirtualShape* pCurrentVShape = NULL;\n\n\tstd::vector&lt;Material*&gt; materialsList;\n\tint usingMaterialN = -1;\n\tstd::vector&lt;int&gt; materialsStack;\n\n\tstd::map&lt;std::string, int&gt; texturesHashMap;\npublic:\n\tvirtual ~ModelBuilder1base();\n\tstatic int useSubjN(ModelBuilder1base* pMB, int subjN);\n\tstatic int getMaterialN(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\tstatic int buildDrawJobs(ModelBuilder1base* pMB, std::vector&lt;GameSubj*&gt;* pGameSubjs);\n\tstatic int rearrangeArraysForDrawJob(std::vector&lt;Vertex01*&gt;* pAllVertices, std::vector&lt;Vertex01*&gt;* pUseVertices, std::vector&lt;Triangle01*&gt;* pUseTriangles);\n\tstatic int buildSingleDrawJob(Material* pMT, std::vector&lt;Vertex01*&gt;* pVertices, std::vector&lt;Triangle01*&gt;* pTriangles);\n\tstatic int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);\n\tstatic int calculateTangentSpace(std::vector&lt;Vertex01*&gt;* pUseVertices, std::vector&lt;Triangle01*&gt;* pUseTriangles);\n\tstatic int finalizeLine(std::vector&lt;Vertex01*&gt;* pVerts, int lineStartsAt = 0, int lineEndsAt = 0);\n\tstatic int optimizeMesh(std::vector&lt;Vertex01*&gt;* pVertices, std::vector&lt;Triangle01*&gt;* pTriangles);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>18. Build and run. Result:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c38\/01.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\/7O5idmmKFJg?controls=0&amp;autoplay=1&amp;loop=1&amp;playlist=7O5idmmKFJg\" title=\"Hierarchical model\" 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<h2 class=\"wp-block-heading\">Android<\/h2>\n\n\n\n<p>19. Restart VS. Open\u00a0<em><em>C:\\CPP\\<strong>a996car<\/strong>\\p_android\\p_android.sln<\/em><\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>20. Under <em>xTheGame<\/em> filter add <strong>New Filter<\/strong>, name &#8211; <strong>car<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>21. Under <em>xTheGame\/car<\/em> add <strong>existing <\/strong>item <\/p>\n\n\n\n<p>from <em>C:\\CPP\\a996car<strong>\\car<\/strong><\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>CarWheel.cpp<\/li><li>CarWheel.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>22. In order <strong>not <\/strong>to overwrite Marlboro APK on the device,<\/p>\n\n\n\n<p>open <em>AndroidManifest.xml<\/em> (it&#8217;s under <strong>p_android.Packaging<\/strong> project)<\/p>\n\n\n\n<p>and change <em>package <\/em>name (line 4) to <em>&#8220;com.OurProject<strong>Car<\/strong>&#8220;<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package=\"com.OurProjectCar\"<\/code><\/pre>\n\n\n\n<p>and change <em>android:label<\/em> to <em>&#8220;OurProject<strong>Car<\/strong>&#8220;<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>android:label=\"OurProjectCar\"<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>23. Switch on, unlock, plug in, allow.<\/p>\n\n\n\n<p>Build and run. Good.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">Now we are ready for more complex models, consisting of several elements with individual behavior. For example &#8211; car, where body will be a parent (root) element, and wheels &#8211; child elements. For both, body and wheel, we&#8217;ll need separate model descriptors and separate classes, which will handle their behaviors. 1. In Windows File Explorer [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1444,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1398","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\/1398","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=1398"}],"version-history":[{"count":54,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1398\/revisions"}],"predecessor-version":[{"id":1460,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1398\/revisions\/1460"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media\/1444"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=1398"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=1398"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=1398"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}