{"id":1205,"date":"2022-01-10T04:19:52","date_gmt":"2022-01-10T04:19:52","guid":{"rendered":"https:\/\/writingagame.com\/?p=1205"},"modified":"2023-05-13T21:09:31","modified_gmt":"2023-05-13T21:09:31","slug":"chapter-36-glass-effect","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2022\/01\/10\/chapter-36-glass-effect\/","title":{"rendered":"Chapter 36. &#8220;Glass&#8221; effect"},"content":{"rendered":"\n<p>The model is almost ready. Two remaining missing pieces are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Clear-film<\/li>\n\n\n\n<li>Excise stamp<\/li>\n<\/ul>\n\n\n\n<p>For clear-film we don&#8217;t even need to write a new shader. We will use our &#8220;mirror&#8221; shader (as for gilded prints) with white noise as a main texture (<em>uTex0)<\/em> and will &#8220;translate&#8221; it to semi-transparent 8&#215;1 texture <em>glass01.bmp<\/em> (<em>uTex3<\/em>), imitating glass reflection. It&#8217;s a plain white transparent texture with one (last) non-transparent pixel (generated programmatically). <\/p>\n\n\n\n1. Download <a href=\"https:\/\/writingagame.com\/img\/b01\/c36\/glass01.bmp\" download=\"\"><b>glass01.bmp<\/b> file here<\/a>\n\n\n\n<p>and save it to&nbsp;<em>C:\\CPP\\engine\\dt\\common\\img<strong>\\materials\\glass01.bmp<\/strong><\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>Revised model descriptor:<\/p>\n\n\n\n<p>2. Copy following code to a&nbsp;<strong>Text Editor<\/strong>&nbsp;and save it (overwrite) to\/as<\/p>\n\n\n\n<p><em>C:\\CPP\\<strong>a997modeler<\/strong>\\dt\\models\\misc\\marlboro01\\<strong>root01.txt<\/strong><\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; highlight: [57]; title: ; notranslate\" title=\"\">\n&lt;texture_as=&quot;tx0&quot; src=&quot;marlboro03small.png&quot; ckey=&quot;#00ff00&quot;\/&gt;\n&lt;mt_type=&quot;phong&quot; uTex0_use=&quot;tx0&quot; \/&gt;\n&lt;vs=&quot;box_tank&quot; whl=&quot;53,83,21&quot; ext=1 sectR=1 \/&gt;\n&lt;a=&quot;front v&quot; xywh=&quot;2,1,323,495&quot; mark=&quot;box_front&quot;\/&gt;\n&lt;a=&quot;back v&quot;  xywh=&quot;2,1,323,495&quot; mark=&quot;box_back&quot;\/&gt;\n&lt;a=&quot;right all&quot; xywh=&quot;327,1,128,495&quot; mark=&quot;box_right&quot;\/&gt;\n&lt;a=&quot;left all&quot; xywh=&quot;457,1,128,495&quot; mark=&quot;box_left&quot;\/&gt;\n&lt;a=&quot;top&quot; xywh=&quot;588,1,323,133&quot;\/&gt;\n&lt;a=&quot;bottom&quot; xywh=&quot;587,136,324,134&quot;\/&gt;\n\/\/golden prints\n&lt;vs=&quot;box&quot; whl=&quot;55.1,85.1,23.1&quot; \/&gt;\n&lt;texture_as=&quot;whitenoise&quot; src=&quot;\/dt\/common\/img\/whitenoise\/wn64_blur3.bmp&quot;\/&gt;\n&lt;texture_as=&quot;gold&quot; src=&quot;\/dt\/common\/img\/materials\/gold02roman.bmp&quot; \/&gt;\n&lt;mt_type=&quot;mirror&quot; uAlphaBlending uTex1mask_use=&quot;tx0&quot; uTex1alphaChannelN=1 uTex0_use=&quot;whitenoise&quot; uTex0translateChannelN=0 uTex3_use=&quot;gold&quot; \/&gt;\n\/\/side golden prints\n&lt;a=&quot;right&quot; xywh=&quot;342,12,101,10&quot; whl=&quot;x,1.8,18.1&quot; pxyz=&quot;x,39.8, -0.3&quot; \/&gt; \/\/Please do not litter\n&lt;a=&quot;right&quot; xywh=&quot;339,144,105,89&quot; whl=&quot;x,15.35,18.9&quot; pxyz=&quot;x,10.3,-0.12&quot; \/&gt; \/\/For special offers...\n&lt;a=&quot;left&quot; xywh=&quot;475,15,95,48&quot; whl=&quot;x,8.4,17&quot; pxyz=&quot;x,36, 0.3&quot; \/&gt; \/\/Underage sale...\n\/\/front prints\n&lt;group&gt;\n\t\/\/bottom golden print &quot;20 class a...&quot;\n\t&lt;a=&quot;front&quot; xywh=&quot;20,498,289,13&quot; whl=&quot;47.5,2,x&quot; pxyz=&quot;1,-36,x&quot; \/&gt;\n\t\/\/blazon\/emblem\n\t&lt;mt_type=&quot;mirror&quot; uAlphaBlending uTex2nm_use=&quot;tx0&quot; uTex0_use=&quot;whitenoise&quot; uTex0translateChannelN=0 uTex3_use=&quot;gold&quot; \/&gt;\n\t&lt;a=&quot;front&quot; xywh2nm=&quot;589,415,128,94&quot; whl=&quot;20.7,16,x&quot; pxyz=&quot;0.3,6.1,x&quot; \/&gt; \/\/emblem\n\t\/\/&quot;Marlboro\n\t&lt;mt_type=&quot;phong&quot; uAlphaBlending uTex2nm_use=&quot;tx0&quot; uColor=&quot;#1E211E&quot; \/&gt;\n\t&lt;a=&quot;front&quot; xywh2nm=&quot;590,275,301,136&quot; whl=&quot;49.2,23.3,x&quot; pxyz=&quot;0.21,-18,x&quot; \/&gt; \/\/marlboro\n&lt;\/group&gt; \n&lt;clone ay=180 \/&gt;\n\/\/joint (slit) between the pack and the lid\n&lt;group&gt;\n\t&lt;mt_adjust uTex2nm_use=&quot;tx0&quot; &gt;\n\t\t&lt;a2mesh wh=&quot;50,1&quot; xywh2nm=&quot;582,497,1,4&quot; all markedAs=&quot;box_right&quot; onThe=&quot;right&quot; py=24.6 az=31 \/&gt;\n\t\t&lt;a2mesh wh=&quot;50,1&quot; xywh2nm=&quot;582,497,1,4&quot; all markedAs=&quot;box_left&quot;  onThe=&quot;left&quot;  py=24.6 az=-31 \/&gt;\n\t\t&lt;a2mesh wh=&quot;53,1&quot; xywh2nm=&quot;582,497,1,4&quot; all markedAs=&quot;box_front&quot;               py=17.8 \/&gt;\n\t\t&lt;a2mesh wh=&quot;6 ,1&quot; xywh2nm=&quot;582,497,1,4&quot; all markedAs=&quot;box_back&quot;  onThe=&quot;back&quot;  py=31.5 px=23.5 \/&gt;\n\t\t&lt;a2mesh wh=&quot;6 ,1&quot; xywh2nm=&quot;582,497,1,4&quot; all markedAs=&quot;box_back&quot;  onThe=&quot;back&quot;  py=31.5 px=-23.5 \/&gt;\n\t&lt;\/mt_adjust&gt; \n&lt;\/group sizeD=&quot;0.1,0,0.1&quot;&gt; \n\/\/sealing ribbon\n&lt;mt_type=&quot;wire&quot; lineWidth=1.5 uColor=&quot;130,90,0&quot; zBufferUpdate=no \/&gt;\n&lt;line&gt;\n\t&lt;p pxyz=&quot;-27.6,16.5 ,0&quot; \/&gt;\n\t&lt;p dz=10.5 \/&gt; \/\/left side half\n\t&lt;p dxyz=&quot;1.1,0,1.1&quot; \/&gt; \/\/front left rib\n\t&lt;p dx=53 \/&gt; \/\/front side\n\t&lt;p dxyz=&quot;1.1,0,-1.1&quot; \/&gt; \/\/front right rib\n\t&lt;p dz=-21 \/&gt; \/\/right side\n\t&lt;p dxyz=&quot;-1.1,0,-1.1&quot; \/&gt; \/\/back right rib\n\t&lt;p dx=-53 \/&gt; \/\/back side\n\t&lt;p dxyz=&quot;-1.1,0,1.1&quot; \/&gt; \/\/back left rib\n\t&lt;p dz=16 \/&gt; \/\/left half\n\t&lt;p dxyz=&quot;-1,0,5&quot; \/&gt; \/\/ribbon &quot;tail&quot;\n\t&lt;p dz=1 \/&gt;\n&lt;\/line &gt;\n\/\/clear-film\n&lt;texture_as=&quot;glass&quot; src=&quot;\/dt\/common\/img\/materials\/glass01.bmp&quot; \/&gt;\n&lt;texture_as=&quot;whitenoise2&quot; src=&quot;\/dt\/common\/img\/whitenoise\/wn64_blur1.bmp&quot;\/&gt;\n&lt;vs=&quot;box_tank&quot; whl=&quot;53,83,21&quot; ext=1 sectR=1 \/&gt;\n&lt;group&gt;\n\t&lt;mt_type=&quot;mirror&quot; uAlphaFactor=0.9 uTex0_use=&quot;whitenoise2&quot; uTex0translateChannelN=1 uTex3_use=&quot;glass&quot; \/&gt;\n\t\t&lt;a=&quot;front h,back h,right,left&quot; \/&gt;\n\t&lt;mt_type=&quot;mirror&quot; uAlphaFactor=0.9 uTex0_use=&quot;whitenoise2&quot; uTex0translateChannelN=1 uTex3_use=&quot;glass&quot; uTex2nm_use=&quot;tx0&quot; \/&gt;\n\t\t&lt;a=&quot;top all&quot; xywh2nm=&quot;724,420,223,90&quot;\/&gt;\n\t\t&lt;a=&quot;bottom all&quot; xywh2nm=&quot;724,420,223,90&quot; mark=&quot;film_bottom&quot;\/&gt;\n&lt;\/group sizeD=&quot;0.2,0.1,0.2&quot; &gt;\n\/\/Excise stamp\n&lt;mt_type=&quot;phong&quot; uTex0_use=&quot;tx0&quot; \/&gt;\n\t&lt;a2mesh wh=&quot;22,13&quot; xywh=&quot;916,3,100,57&quot; flip=180 all markedAs=&quot;film_bottom&quot; onThe=&quot;bottom&quot; pxyz=&quot;-15,4,0&quot; az=-5 detachBy=0.05 \/&gt;\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>New code starts at line 57.<\/p>\n\n\n\n<p>Please note:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For top and bottom projections we also use a <em>normal map<\/em> (line 64), a rectangular bluish image on bottom right.<\/li>\n\n\n\n<li>For excise stamp we are using not &#8220;mt_adjust&#8221; as before, but entirely new material (line 69).<\/li>\n\n\n\n<li>In line 70 we have a new <em>a2mesh<\/em> property &#8211; <em>detachBy=0.05<\/em>. It instructs <em>ModelLoadere <\/em>to pull cutted fragment a bit out of &#8220;parent&#8221; mesh in order to avoid overlapping with clear-film surface.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>Since now we are using semi-transparent meshes, it arises a new problem &#8211; transparent surfaces shouldn&#8217;t overwrite (update) z-buffer in case we need to render something <em>under <\/em>(behind) such surface. In order to handle this, in <em>Material <\/em>class we will add 2 new properties:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>zBuffer <\/em>&#8211; to use z-buffer testing on not. Default &#8211; use.<\/li>\n\n\n\n<li><em>zBufferUpdate <\/em>&#8211; update z-buffer or not. Default &#8211; update.<\/li>\n<\/ul>\n\n\n\n<p>3. Open&nbsp;<em>Material.h<\/em>&nbsp;and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [29,30]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;MyColor.h&quot;\n#include &lt;string&gt;\n\nclass Material\n{\npublic:\n\tchar shaderType&#91;20] = &quot;&quot;;\n\tint shaderN = -1;\n\tint primitiveType = GL_TRIANGLES;\n\tMyColor uColor;\n\tint uTex0 = -1;\n\tint uTex1mask = -1;\n\tint uTex2nm = -1;\n\tint uTex3 = -1;\n\tint uTex1alphaChannelN = 3; \/\/default - alpha channel for mask\n\tint uTex1alphaNegative = 0; \/\/default - alpha channel not negative\n\tint uTex0translateChannelN = -1; \/\/translate tex0 to tex3 by channelN. Default -1 - don&#039;t translate\n\n\tint uAlphaBlending = 0; \/\/for semi-transparency\n\tfloat uAlphaFactor = 1; \/\/for semi-transparency\n\tfloat uAmbient = 0.4f; \/\/ambient light\n\t\/\/specular light parameters\n\tfloat uSpecularIntencity = 0.8f;\n\tfloat uSpecularMinDot = 0.8f;\n\tfloat uSpecularPowerOf = 20.0f;\n\n\tfloat lineWidth = 1;\n\tint zBuffer = 1;\n\tint zBufferUpdate = 1;\npublic:\n\tint pickShaderNumber() { return pickShaderNumber(this); };\n\tstatic int pickShaderNumber(Material* pMT);\n\tvoid setShaderType(std::string needType) { setShaderType(this, needType); };\n\tstatic void setShaderType(Material* pMT, std::string needType) { myStrcpy_s(pMT-&gt;shaderType, 20, (char*)needType.c_str()); };\n\tvoid clear() { clear(this); };\n\tstatic void clear(Material* pMT);\n\tint assignShader(std::string needType) { return assignShader(this, needType); };\n\tstatic int assignShader(Material* pMT, std::string needType);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>We will use these properties in <strong>DrawJob <\/strong>class to adjust rendering settings. Function &#8211; <em>executeDrawJob(..)<\/em>.<\/p>\n\n\n\n<p>4.  Open&nbsp;<em>DrawJob.cpp <\/em>and replace code by:  <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [152,153,154,155,156,157,158,159,160,161,162]; title: ; notranslate\" title=\"\">\n#include &quot;DrawJob.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;Shader.h&quot;\n#include &quot;Texture.h&quot;\n\n\/\/static arrays (vectors) of all loaded DrawJobs, VBO ids\nstd::vector&lt;DrawJob*&gt; DrawJob::drawJobs;\nstd::vector&lt;unsigned int&gt; DrawJob::buffersIds;\n\nDrawJob::DrawJob() {\n\tdrawJobs.push_back(this);\n}\nDrawJob::~DrawJob() {\n\tglBindBuffer(GL_ARRAY_BUFFER, 0);\n\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);\n\tif (glVAOid &gt; 0)\n\t\tglDeleteVertexArrays(1, &amp;glVAOid);\n}\nint DrawJob::newBufferId() {\n\tunsigned int bufferId;\n\tglGenBuffers(1, &amp;bufferId);\n\tbuffersIds.push_back(bufferId);\n\treturn (int)bufferId;\n}\nunsigned int activeVBOid;\nint DrawJob::buildVAOforShader(DrawJob* pDJ, int shaderN) {\n\t\/\/delete VAO if exists already\n\tif (pDJ-&gt;glVAOid &gt; 0) {\n\t\tglBindBuffer(GL_ARRAY_BUFFER, 0);\n\t\tglDeleteVertexArrays(1, &amp;(pDJ-&gt;glVAOid));\n\t}\n\tglGenVertexArrays(1, &amp;pDJ-&gt;glVAOid);\n\tglBindVertexArray(pDJ-&gt;glVAOid);\n\n\t\/\/open shader descriptor to access variables locations\n\tShader* pShader = Shader::shaders.at(pDJ-&gt;mt.shaderN);\n\n\tactiveVBOid = 0;\n\tattachAttribute(pShader-&gt;l_aPos, 3, &amp;pDJ-&gt;aPos);\n\tattachAttribute(pShader-&gt;l_aNormal, 3, &amp;pDJ-&gt;aNormal);\n\tattachAttribute(pShader-&gt;l_aTuv, 2, &amp;pDJ-&gt;aTuv);\n\tattachAttribute(pShader-&gt;l_aTuv2, 2, &amp;pDJ-&gt;aTuv2); \/\/for normal map\n\tattachAttribute(pShader-&gt;l_aTangent, 3, &amp;pDJ-&gt;aTangent); \/\/for normal map\n\tattachAttribute(pShader-&gt;l_aBinormal, 3, &amp;pDJ-&gt;aBinormal); \/\/for normal map\n\n\tif (pDJ-&gt;glEBOid &gt; 0)\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ-&gt;glEBOid);\n\n\tglBindVertexArray(0);\n\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);\n\tglBindBuffer(GL_ARRAY_BUFFER, 0);\n\treturn 1;\n}\n\nint DrawJob::attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAR) {\n\tif (varLocationInShader &lt; 0)\n\t\treturn 0; \/\/not used in this shader\n\tif (pAR-&gt;glVBOid == 0) {\n\t\tmylog(&quot;ERROR in DrawJob::attachAttribute, nk such attribute\/VBO\\n&quot;);\n\t\treturn -1;\n\t}\n\tglEnableVertexAttribArray(varLocationInShader);\n\tif (activeVBOid != pAR-&gt;glVBOid) {\n\t\tactiveVBOid = pAR-&gt;glVBOid;\n\t\t\/\/attach input stream data\n\t\tglBindBuffer(GL_ARRAY_BUFFER, activeVBOid);\n\t}\n\tglVertexAttribPointer(varLocationInShader, attributeSizeInFloats, GL_FLOAT, GL_FALSE, pAR-&gt;stride, (void*)(long)pAR-&gt;offset);\n\treturn 1;\n}\n\nint DrawJob::executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV3x3, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize, Material* pMt) {\n\tif (pMt == NULL)\n\t\tpMt = &amp;(pDJ-&gt;mt);\n\tglBindVertexArray(pDJ-&gt;glVAOid);\n\tShader* pShader = Shader::shaders.at(pMt-&gt;shaderN);\n\tglUseProgram(pShader-&gt;GLid);\n\t\/\/input uniforms\n\tglUniformMatrix4fv(pShader-&gt;l_uMVP, 1, GL_FALSE, (const GLfloat*)uMVP);\n\tif (pShader-&gt;l_uMV3x3 &gt;= 0)\n\t\tglUniformMatrix3fv(pShader-&gt;l_uMV3x3, 1, GL_FALSE, (const GLfloat*)uMV3x3);\n\tif (pShader-&gt;l_uMM &gt;= 0)\n\t\tglUniformMatrix4fv(pShader-&gt;l_uMM, 1, GL_FALSE, (const GLfloat*)uMM);\n\tif (pShader-&gt;l_uVectorToLight &gt;= 0)\n\t\tglUniform3fv(pShader-&gt;l_uVectorToLight, 1, (const GLfloat*)uVectorToLight);\n\tif (pShader-&gt;l_uCameraPosition &gt;= 0)\n\t\tglUniform3fv(pShader-&gt;l_uCameraPosition, 1, (const GLfloat*)uCameraPosition);\n\n\t\/\/attach textures\n\tif (pShader-&gt;l_uTex0 &gt;= 0) {\n\t\tint textureId = Texture::getGLid(pMt-&gt;uTex0);\n\t\t\/\/pass textureId to shader program\n\t\tglActiveTexture(GL_TEXTURE0); \/\/ activate the texture unit first before binding texture\n\t\tglBindTexture(GL_TEXTURE_2D, textureId);\n\t\t\/\/ Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.    \n\t\tglUniform1i(pShader-&gt;l_uTex0, 0);\n\t}\n\tif (pShader-&gt;l_uTex1mask &gt;= 0) {\n\t\tint textureId = Texture::getGLid(pMt-&gt;uTex1mask);\n\t\t\/\/pass textureId to shader program\n\t\tglActiveTexture(GL_TEXTURE1); \/\/ activate the texture unit first before binding texture\n\t\tglBindTexture(GL_TEXTURE_2D, textureId);\n\t\t\/\/ Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1.    \n\t\tglUniform1i(pShader-&gt;l_uTex1mask, 1);\n\t}\n\tif (pShader-&gt;l_uTex2nm &gt;= 0) {\n\t\tint textureId = Texture::getGLid(pMt-&gt;uTex2nm);\n\t\t\/\/pass textureId to shader program\n\t\tglActiveTexture(GL_TEXTURE2); \/\/ activate the texture unit first before binding texture\n\t\tglBindTexture(GL_TEXTURE_2D, textureId);\n\t\t\/\/ Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 2.    \n\t\tglUniform1i(pShader-&gt;l_uTex2nm, 2);\n\t}\n\tif (pShader-&gt;l_uTex0translateChannelN &gt;= 0) {\n\t\tglUniform1i(pShader-&gt;l_uTex0translateChannelN, pMt-&gt;uTex0translateChannelN);\n\t\tif (pShader-&gt;l_uTex3 &gt;= 0 &amp;&amp; pMt-&gt;uTex3 &gt;= 0) {\n\t\t\tint textureId = Texture::getGLid(pMt-&gt;uTex3);\n\t\t\t\/\/pass textureId to shader program\n\t\t\tglActiveTexture(GL_TEXTURE3); \/\/ activate the texture unit first before binding texture\n\t\t\tglBindTexture(GL_TEXTURE_2D, textureId);\n\t\t\t\/\/ Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 3.    \n\t\t\tglUniform1i(pShader-&gt;l_uTex3, 3);\n\t\t}\n\t}\n\t\/\/material uniforms\n\tif (pShader-&gt;l_uTex1alphaChannelN &gt;= 0)\n\t\tglUniform1i(pShader-&gt;l_uTex1alphaChannelN, pMt-&gt;uTex1alphaChannelN);\n\tif (pShader-&gt;l_uTex1alphaNegative &gt;= 0)\n\t\tglUniform1i(pShader-&gt;l_uTex1alphaNegative, pMt-&gt;uTex1alphaNegative);\n\tif (pShader-&gt;l_uColor &gt;= 0)\n\t\tglUniform4fv(pShader-&gt;l_uColor, 1, pMt-&gt;uColor.forGL());\n\tif (pShader-&gt;l_uAlphaFactor &gt;= 0)\n\t\tglUniform1f(pShader-&gt;l_uAlphaFactor, pMt-&gt;uAlphaFactor);\n\tif (pShader-&gt;l_uAlphaBlending &gt;= 0)\n\t\tglUniform1i(pShader-&gt;l_uAlphaBlending, pMt-&gt;uAlphaBlending);\n\tif (pShader-&gt;l_uAmbient &gt;= 0)\n\t\tglUniform1f(pShader-&gt;l_uAmbient, pMt-&gt;uAmbient);\n\tif (pShader-&gt;l_uSpecularIntencity &gt;= 0)\n\t\tglUniform1f(pShader-&gt;l_uSpecularIntencity, pMt-&gt;uSpecularIntencity);\n\tif (pShader-&gt;l_uSpecularMinDot &gt;= 0)\n\t\tglUniform1f(pShader-&gt;l_uSpecularMinDot, pMt-&gt;uSpecularMinDot);\n\tif (pShader-&gt;l_uSpecularPowerOf &gt;= 0)\n\t\tglUniform1f(pShader-&gt;l_uSpecularPowerOf, pMt-&gt;uSpecularPowerOf);\n\n\t\/\/adjust render settings\n\tif (lineWidthIsImportant(pMt-&gt;primitiveType)) {\n\t\tfloat lw = sizeUnitPixelsSize * pMt-&gt;lineWidth;\n\t\tglLineWidth(lw);\n\t}\n\n\tif (pMt-&gt;zBuffer &gt; 0) {\n\t\tglEnable(GL_DEPTH_TEST);\n\t\tglDepthFunc(GL_LEQUAL);\n\t}\n\telse\n\t\tglDisable(GL_DEPTH_TEST);\n\n\tif (pMt-&gt;zBufferUpdate &gt; 0)\n\t\tglDepthMask(GL_TRUE);\n\telse\n\t\tglDepthMask(GL_FALSE);\n\n\tif (pShader-&gt;l_uAlphaBlending &gt;= 0 &amp;&amp; pMt-&gt;uAlphaBlending &gt; 0) {\n\t\tglEnable(GL_BLEND);\n\t\tglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n\t}\n\telse\n\t\tglDisable(GL_BLEND);\n\n\t\/\/execute\n\tif (pDJ-&gt;glEBOid == 0) {\n\t\tglDrawArrays(pMt-&gt;primitiveType, 0, pDJ-&gt;pointsN);\n\t}\n\telse { \/\/use EBO\n\t\tglDrawElements(pMt-&gt;primitiveType, pDJ-&gt;pointsN, GL_UNSIGNED_SHORT, 0);\n\t}\n\tglBindVertexArray(0);\n\treturn 1;\n}\nint DrawJob::cleanUp() {\n\tint itemsN = drawJobs.size();\n\t\/\/delete all drawJobs\n\tfor (int i = 0; i &lt; itemsN; i++) {\n\t\tDrawJob* pDJ = drawJobs.at(i);\n\t\tdelete pDJ;\n\t}\n\tdrawJobs.clear();\n\t\/\/delete Buffers\n\titemsN = buffersIds.size();\n\t\/\/delete all buffers\n\tfor (int i = 0; i &lt; itemsN; i++) {\n\t\tunsigned int id = buffersIds.at(i);\n\t\tglDeleteBuffers(1, &amp;id);\n\t}\n\tbuffersIds.clear();\n\n\treturn 1;\n}\nint DrawJob::setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid) {\n\t\/\/sets desirable offsets and stride according to given shader needs\n\t\/\/assuming that we have 1 single VBO\n\tShader* pSh = Shader::shaders.at(shaderN);\n\tint stride = 0;\n\tpDJ-&gt;aPos.offset = 0; \/\/attribute o_aPos, always 0\n\tstride += sizeof(float) * 3; \/\/aPos size - 3 floats (x,y,z)\n\tif (pSh-&gt;l_aNormal &gt;= 0) { \/\/attribute normal\n\t\tpDJ-&gt;aNormal.offset = stride;\n\t\tstride += sizeof(float) * 3;\n\t}\n\tif (pSh-&gt;l_aTuv &gt;= 0) { \/\/attribute TUV (texture coordinates)\n\t\tpDJ-&gt;aTuv.offset = stride; \/\/attribute TUV (texture coordinates)\n\t\tstride += sizeof(float) * 2;\n\t}\n\tif (pSh-&gt;l_aTuv2 &gt;= 0) { \/\/for normal map\n\t\tpDJ-&gt;aTuv2.offset = stride;\n\t\tstride += sizeof(float) * 2;\n\t}\n\tif (pSh-&gt;l_aTangent &gt;= 0) { \/\/for normal map\n\t\tpDJ-&gt;aTangent.offset = stride;\n\t\tstride += sizeof(float) * 3;\n\t}\n\tif (pSh-&gt;l_aBinormal &gt;= 0) { \/\/for normal map\n\t\tpDJ-&gt;aBinormal.offset = stride;\n\t\tstride += sizeof(float) * 3;\n\t}\n\t*pStride = stride;\n\t\/\/add stride and VBOid to all attributes\n\tAttribRef* pAR = NULL;\n\tpAR = &amp;pDJ-&gt;aPos; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\tpAR = &amp;pDJ-&gt;aNormal; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\tpAR = &amp;pDJ-&gt;aTuv; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\tpAR = &amp;pDJ-&gt;aTuv2; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\tpAR = &amp;pDJ-&gt;aTangent; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\tpAR = &amp;pDJ-&gt;aBinormal; pAR-&gt;glVBOid = VBOid; pAR-&gt;stride = stride;\n\n\treturn 1;\n}\nbool DrawJob::lineWidthIsImportant(int primitiveType) {\n\tif (primitiveType == GL_TRIANGLES) return false;\n\tif (primitiveType == GL_TRIANGLE_STRIP) return false;\n\tif (primitiveType == GL_TRIANGLE_FAN) return false;\n\treturn true;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>Reading\/setting &#8211; in  <em>ModelLoader<\/em>, in  <em>fillProps_mt(..)<\/em> function.<\/p>\n\n\n\n<p>5. Open<em>&nbsp;ModelLoader.cpp<\/em>&nbsp;and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [96,97,104,105,106,107,562,563,564,565,566,567,568]; 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\tint subjN = pSubjsVector0-&gt;size();\n\tGameSubj* pGS = theGame.newGameSubj(subjClass);\n\tpSubjsVector0-&gt;push_back(pGS);\n\t\/\/pGS-&gt;djStartN = DrawJob::drawJobs.size();\n\tModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);\n\tprocessSource(pML);\n\tdelete pML;\n\t\/\/pGS-&gt;djTotalN = DrawJob::drawJobs.size() - pGS-&gt;djStartN;\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;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\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}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Another change here &#8211; handling <em>detachBy<\/em> property for <em>a2mesh <\/em>tag (lines 562-568).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p><em>TheGame.cpp <\/em>was affected too. Since now we are disabling updating z-buffer for some <em>DrawJobs<\/em>, we have to make sure it is <strong>enabled <\/strong>before clearing z-buffer (line 50).<\/p>\n\n\n\n<p> 6. Open<em>&nbsp;TheGame.cpp<\/em>&nbsp;and replace code by: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [29,34,50]; 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\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\/misc\/marlboro01\/root01.txt&quot;, &quot;&quot;);\n    GameSubj* pGS = gameSubjs.at(subjN);\n    pGS-&gt;name.assign(&quot;box1&quot;);\n    pGS-&gt;ownSpeed.setDegrees(1.5, 1, 0.5);\n    pGS-&gt;ownCoords.setDegrees(0,30, 0);\n\n    \/\/===== set up camera\n    mainCamera.ownCoords.setDegrees(15, 180, 0); \/\/set camera angles\/orientation\n    mainCamera.viewRangeDg = 20;\n    mainCamera.stageSize&#91;0] = 80;\n    mainCamera.stageSize&#91;1] = 120;\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;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        \/\/subj&#039;s distance from camera\n        float cameraSpacePos&#91;4];\n        mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS-&gt;ownCoords.pos, 1);\n        float zDistance = abs(cameraSpacePos&#91;2]);\n        float cotangentA = 1.0f \/ tanf(degrees2radians * mainCamera.viewRangeDg \/ 2.0);\n        float halfScreenVertSizeInUnits = zDistance \/ cotangentA;\n        float sizeUnitPixelsSize = screenSize&#91;1] \/ 2.0 \/ 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;ownModelMatrix, 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}\nGameSubj* TheGame::newGameSubj(std::string subjClass) {\n    return (new GameSubj());\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<ul class=\"wp-block-list\">\n<li>Another change here &#8211; <em>mainCamera.viewRangeDg = 20;<\/em>  30 was a bit too much, too strong perspective, 20 looks better.<\/li>\n\n\n\n<li>New rotation speeds (line 29).<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p>7. Build and run. Result:<\/p>\n\n\n\n<p><strong>Before:<\/strong><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c28\/01.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>(Plain projections)<\/p>\n\n\n\n<p><strong>After:<\/strong><\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c36\/01.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>With gilded and embossed prints, with pack-top joint as a normal map, with golden sealing ribbon as a line.<\/p>\n\n\n\n<p>And now &#8211; with clear-film and excise mark.<\/p>\n\n\n\n<iframe loading=\"lazy\" width=\"100%\" height=\"400\" src=\"https:\/\/www.youtube.com\/embed\/6xY-nfuW2Do?controls=0&amp;autoplay=1&amp;loop=1&amp;playlist=6xY-nfuW2Do\" title=\"Glass effect\" 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<ul class=\"wp-block-list\">\n<li>Verified on <strong>Android <\/strong>as well.<\/li>\n<\/ul>\n\n\n\n<p>Now model is completely ready. Finally, I can safely smoke this pack out. Or maybe it&#8217;s better to leave it as a keepsake of this Project?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">The model is almost ready. Two remaining missing pieces are: For clear-film we don&#8217;t even need to write a new shader. We will use our &#8220;mirror&#8221; shader (as for gilded prints) with white noise as a main texture (uTex0) and will &#8220;translate&#8221; it to semi-transparent 8&#215;1 texture glass01.bmp (uTex3), imitating glass reflection. It&#8217;s a plain [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1205","post","type-post","status-publish","format-standard","hentry","category-cross-platform-3d"],"_links":{"self":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1205","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=1205"}],"version-history":[{"count":35,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1205\/revisions"}],"predecessor-version":[{"id":1929,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/1205\/revisions\/1929"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=1205"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=1205"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=1205"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}