Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the antispam-bee domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/ruwritingagame/public_html/wp-includes/functions.php on line 6131

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902

Warning: Cannot modify header information - headers already sent by (output started at /home/ruwritingagame/public_html/wp-includes/functions.php:6131) in /home/ruwritingagame/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1902
{"id":607,"date":"2021-12-07T01:47:18","date_gmt":"2021-12-07T01:47:18","guid":{"rendered":"https:\/\/writingagame.com\/?p=607"},"modified":"2022-02-09T20:30:40","modified_gmt":"2022-02-09T20:30:40","slug":"chapter-19-light-and-phong-shading","status":"publish","type":"post","link":"https:\/\/writingagame.ru\/index.php\/2021\/12\/07\/chapter-19-light-and-phong-shading\/","title":{"rendered":"\u0413\u043b\u0430\u0432\u0430 19. \u041e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u0438 Phong Shading"},"content":{"rendered":"\n

\u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u044b \u0441\u0432\u0435\u0442 \u0438 \u0442\u0435\u043d\u0438, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 – \u043d\u043e\u0432\u044b\u0435 \u0448\u0435\u0439\u0434\u0435\u0440\u044b<\/em>. \u041c\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c Phong shading model<\/strong>.<\/p>\n\n\n\n

\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e, \u0437\u0430\u043b\u0438\u0432\u043a\u0430 \u0441\u043f\u043b\u043e\u0448\u043d\u044b\u0439 \u0446\u0432\u0435\u0442\u043e\u043c.<\/p>\n\n\n\n

1. Vertex shader<\/strong>. <\/p>\n\n\n\n

\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u043d\u0438\u0436\u0435\u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432 \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440<\/strong> \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u043a\u0430\u043a txt \u0444\u0430\u0439\u043b \u0432<\/p>\n\n\n\n

C:\\CPP\\engine\\dt\\shaders\\phong_ucolor_v.txt<\/strong><\/em><\/p>\n\n\n

\n#version 320 es\nprecision lowp float;\nuniform mat4 uMVP; \/\/ transform matrix (Model-View-Projection)\nuniform mat3 uMV3x3; \/\/ Model-View matrix (for calculating normals into eye space)\nin vec3 aPos; \/\/ position attribute (x,y,z)\nin vec3 aNormal; \/\/ normal attribute (x,y,z)\nout vec3 vNormal; \/\/ varying normal (to pass to fragment shader)\n\nvoid main(void) { \n\tgl_Position = uMVP * vec4(aPos, 1.0);\t\n\t\/\/ Transform the normal's orientation into eye space.    \n\tvNormal = uMV3x3 * aNormal;\t\n}\n\n<\/pre><\/div>\n\n\n

<\/p>\n\n\n\n


\n\n\n\n

2. Fragment shader<\/strong>. <\/p>\n\n\n\n

\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u043d\u0438\u0436\u0435\u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432 \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440<\/strong> \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u043a\u0430\u043a txt \u0444\u0430\u0439\u043b \u0432 <\/p>\n\n\n\n

C:\\CPP\\engine\\dt\\shaders\\phong_ucolor_f<\/strong>.txt<\/em><\/p>\n\n\n

\n#version 320 es\nprecision lowp float;\nout vec4 FragColor; \/\/output pixel color\n\nin vec3 vNormal; \/\/normal passed from rasterizer\nuniform vec4 uColor;\n\nuniform float uAlphaFactor; \/\/for semi-transparency\n\nuniform float uAmbient;\nuniform float uSpecularIntencity;\nuniform float uSpecularMinDot;\nuniform float uSpecularPowerOf;\n\nuniform vec3 uVectorToLight;\nuniform vec3 uHalfVector;\n\nvoid main(void) {\n\n\tvec4 outColor = uColor;\n\n\tvec3 vNormalNormal = normalize(vNormal);\n\n\tif(uAmbient<1.0){\n\t\t \/\/ Calculate the dot product of the light vector and vertex normal. If the normal and light vector are\n\t\t \/\/ pointing in the same direction then it will get max illumination.\n\t\t float directionalLightIntencity = dot(vNormalNormal, uVectorToLight);\n\t\t\n\t\t \/\/ count ambient component\n\t\t directionalLightIntencity += uAmbient;\n\t\t if(directionalLightIntencity < uAmbient)\n\t\t\tdirectionalLightIntencity = uAmbient;\n\n\t\t \/\/ Multiply the color by the lightIntencity illumination level to get final output color.\n\t\t outColor = outColor * directionalLightIntencity;\n\t}\n\tif(uSpecularIntencity>0.0){\n\t\t\/\/specular light\n\t\t\/\/ INTENSITY OF THE SPECULAR LIGHT\n\t\t\/\/ DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS\n\t\tfloat dotProduct = dot(vNormalNormal, uHalfVector);\n\n\t\tif(dotProduct>uSpecularMinDot){\n\t\t\tfloat specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;\t\t\n\t\t\tif(specularIntencity > uSpecularIntencity)\n\t\t\t\tspecularIntencity = uSpecularIntencity;\n\t\t\toutColor += specularIntencity;\n\t\t}\n\t}\n\toutColor.a = uAlphaFactor;\t\t\n\tFragColor = outColor;\n}\n\n<\/pre><\/div>\n\n\n

<\/p>\n\n\n\n

\u041a\u043e\u0434 \u0441\u043e\u043f\u0440\u043e\u0432\u043e\u0436\u0434\u0435\u043d \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u043d\u044b\u043c\u0438.<\/p>\n\n\n\n

\u041a\u0443\u0447\u0430 \u043d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0437\u0430\u0431\u043e\u0442\u0438\u043c\u0441\u044f \u043e \u043d\u0438\u0445.<\/p>\n\n\n\n


\n\n\n\n

Windows<\/h2>\n\n\n\n

3. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c VS. \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c C:\\CPP\\a997modeler<\/em>\\p_windows\\p_windows.sln<\/em>.<\/p>\n\n\n\n


\n\n\n\n

\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043a\u043b\u0430\u0441\u0441 Shader<\/em>. \u041d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c locations <\/em>\u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0448\u0435\u0439\u0434\u0435\u0440\u0430, \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f:<\/p>\n\n\n\n

  • l_aNormal – vertex normal attribute (\u043d\u043e\u0440\u043c\u0430\u043b\u0438)<\/li>
  • l_uVectorToLight – \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u0432\u0435\u0442, \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u0438<\/li>
  • l_uHalfVector – \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430 \u0431\u043b\u0438\u043a\u043e\u0432 (specular light)<\/li><\/ul>\n\n\n\n

    \u041d\u043e\u0432\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 Material<\/em>-\u0430, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0441\u0432\u0435\u0442\u0443:<\/p>\n\n\n\n

    • l_uAmbient<\/li>
    • l_uSpecularIntencity<\/li>
    • l_uSpecularMinDot<\/li>
    • l_uSpecularPowerOf<\/li><\/ul>\n\n\n\n
      • \u041f\u043b\u044e\u0441 l_uMV3x3<\/em> – \u0434\u043b\u044f Model-View \u043c\u0430\u0442\u0440\u0438\u0446\u044b \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430 \u043d\u043e\u0440\u043c\u0430\u043b\u0435\u0439<\/li>
      • \u041f\u043b\u044e\u0441 \u043d\u043e\u043c\u0435\u0440 \u043d\u043e\u0432\u043e\u0439 \u0448\u0435\u0439\u0434\u0435\u0440-\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b spN_phong<\/em><\/em>_ucolor<\/em>.<\/li>
      • \u041f\u043b\u044e\u0441 \u043d\u043e\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f fillLocations()<\/em> .<\/li><\/ul>\n\n\n\n
        • \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043d\u043e\u0432\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0434\u043b\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u0436\u0443\u044e\u0449\u0438\u0445 \u0433\u043b\u0430\u0432.<\/li><\/ul>\n\n\n\n

          4. \u0417\u0430\u043c\u0435\u043d\u0438\u043c Shader.h<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#pragma once\n#include "platform.h"\n#include <string>\n#include <vector>\n\nclass Shader\n{\npublic:\n    \/\/Shader program's individual descriptor:\n    unsigned int GLid = -1; \/\/ GL shader id\n    \/\/common variables, "l_" for "location"\n    \/\/attributes\n    int l_aPos; \/\/attribute position (3D coordinates)\n    int l_aTuv; \/\/attribute TUV (texture coordinates)\n    int l_aTuv2; \/\/attribute TUV (texture coordinates for normal map)\n    int l_aNormal; \/\/attribute normal (3D vector)\n    int l_aTangent; \/\/for normal map\n    int l_aBinormal; \/\/for normal map\n    \/\/uniforms\n    int l_uMVP; \/\/ transform matrix (Model-View-Projection)\n    int l_uMV3x3; \/\/ Model-View matrix for normals\n    int l_uVectorToLight; \/\/required for light\n    int l_uHalfVector; \/\/required for specular light\n    \/\/material's properties\n    int l_uColor;\n    int l_uTex0; \/\/texture id\n    int l_uTex1mask; \/\/transparency map\n    int l_uTex2nm; \/\/normal map\n    int l_uTex3; \/\/texture id\n    int l_uTex1alphaChannelN; \/\/alpha channel for mask\n    int l_uTex1alphaNegative; \/\/alpha channel negative\n    int l_uTex0translateChannelN; \/\/translate tex0 to tex3 by channelN.\n    int l_uAlphaFactor; \/\/for semi-transparency\n    int l_uAlphaBlending; \/\/for semi-transparency\n    \/\/light:\n    int l_uAmbient; \/\/ambient light\n    \/\/specular light parameters\n    int l_uSpecularIntencity;\n    int l_uSpecularMinDot;\n    int l_uSpecularPowerOf;\n    \/\/end of descriptor\n\n    \/\/static array (vector) of all loaded shaders\n    static std::vector<Shader*> shaders;\n    \/\/common shader programs ("spN" - shader program number)\n    static int spN_flat_ucolor;\n    static int spN_flat_tex;\n    static int spN_phong_ucolor;\n\npublic:\n    static int loadShaders();\n    static int loadShader(std::string filePathVertexS, std::string filePathFragmentS);\n    static int cleanUp();\n    static unsigned int getGLid(int shN) { return shaders.at(shN)->GLid; };\n    \n    static int linkShaderProgram(const char* filePathVertexS, const char* filePathFragmentS);\n\tstatic int compileShader(const char* filePath, GLenum shaderType);\n\tstatic int shaderErrorCheck(int shaderId, std::string ref);\n\tstatic int programErrorCheck(int programId, std::string ref);\n    static int fillLocations(Shader* pSh);\n};\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u043e\u0432\u043e\u0439 \u0448\u0435\u0439\u0434\u0435\u0440-\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0432 Shader::loadShaders<\/strong>()<\/em>. \u0410 \u0432 Shader::loadShader()->fillLocations()<\/em> – \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043b\u043e\u043a\u0430\u0446\u0438\u0439 \u043d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445.<\/p>\n\n\n\n

          5. \u0417\u0430\u043c\u0435\u043d\u0438\u043c Shader.cpp<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#include "Shader.h"\n#include "platform.h"\n#include "utils.h"\n\nextern std::string filesRoot;\n\n\/\/static array (vector) of all loaded shaders\nstd::vector<Shader*> Shader::shaders;\n\/\/common shader programs ("spN" - shader program number)\nint Shader::spN_flat_ucolor = -1;\nint Shader::spN_flat_tex = -1;\nint Shader::spN_phong_ucolor = -1;\n\nint Shader::loadShaders() {\n    spN_flat_ucolor = loadShader("\/dt\/shaders\/flat_ucolor_v.txt", "\/dt\/shaders\/flat_ucolor_f.txt");\n    spN_flat_tex = loadShader("\/dt\/shaders\/flat_tex_v.txt", "\/dt\/shaders\/flat_tex_f.txt");\n    spN_phong_ucolor = loadShader("\/dt\/shaders\/phong_ucolor_v.txt", "\/dt\/shaders\/phong_ucolor_f.txt");\n    return 1;\n}\nint Shader::loadShader(std::string filePathVertexS, std::string filePathFragmentS) {\n    \/\/create shader object\n    Shader* pSh = new Shader();\n    shaders.push_back(pSh);\n    pSh->GLid = linkShaderProgram((filesRoot + filePathVertexS).c_str(), (filesRoot + filePathFragmentS).c_str());\n    \/\/common variables. If not presented, = -1;\n    fillLocations(pSh);\n\n    return (shaders.size() - 1);\n}\nint Shader::fillLocations(Shader* pSh) {\n    \/\/common variables. If not presented, = -1;\n    \/\/attributes\n    pSh->l_aPos = glGetAttribLocation(pSh->GLid, "aPos"); \/\/attribute position (3D coordinates)\n    pSh->l_aNormal = glGetAttribLocation(pSh->GLid, "aNormal"); \/\/attribute normal (3D vector)\n    pSh->l_aTangent = glGetAttribLocation(pSh->GLid, "aTangent"); \/\/for normal map\n    pSh->l_aBinormal = glGetAttribLocation(pSh->GLid, "aBinormal"); \/\/for normal map\n    pSh->l_aTuv = glGetAttribLocation(pSh->GLid, "aTuv"); \/\/attribute TUV (texture coordinates)\n    pSh->l_aTuv2 = glGetAttribLocation(pSh->GLid, "aTuv2"); \/\/attribute TUV (texture coordinates)\n    \/\/uniforms\n    pSh->l_uMVP = glGetUniformLocation(pSh->GLid, "uMVP"); \/\/ transform matrix (Model-View-Projection)\n    pSh->l_uMV3x3 = glGetUniformLocation(pSh->GLid, "uMV3x3"); \/\/ Model-View matrix for normals\n    pSh->l_uVectorToLight = glGetUniformLocation(pSh->GLid, "uVectorToLight"); \/\/ \n    pSh->l_uHalfVector = glGetUniformLocation(pSh->GLid, "uHalfVector"); \/\/ required for specular light\n    \/\/material's properties\n    pSh->l_uColor = glGetUniformLocation(pSh->GLid, "uColor");\n    pSh->l_uTex0 = glGetUniformLocation(pSh->GLid, "uTex0"); \/\/texture id\n    pSh->l_uTex1mask = glGetUniformLocation(pSh->GLid, "uTex1mask"); \/\/texture id\n    pSh->l_uTex2nm = glGetUniformLocation(pSh->GLid, "uTex2nm"); \/\/texture id\n    pSh->l_uTex3 = glGetUniformLocation(pSh->GLid, "uTex3"); \/\/texture id\n    pSh->l_uTex1alphaChannelN = glGetUniformLocation(pSh->GLid, "uTex1alphaChannelN");\n    pSh->l_uTex1alphaNegative = glGetUniformLocation(pSh->GLid, "uTex1alphaNegative");\n    pSh->l_uTex0translateChannelN = glGetUniformLocation(pSh->GLid, "uTex0translateChannelN");\n    pSh->l_uAlphaFactor = glGetUniformLocation(pSh->GLid, "uAlphaFactor"); \/\/ for semi-transparency\n    pSh->l_uAlphaBlending = glGetUniformLocation(pSh->GLid, "uAlphaBlending"); \/\/ for semi-transparency\n    pSh->l_uAmbient = glGetUniformLocation(pSh->GLid, "uAmbient"); \/\/ ambient light\n    pSh->l_uSpecularIntencity = glGetUniformLocation(pSh->GLid, "uSpecularIntencity"); \/\/ \n    pSh->l_uSpecularMinDot = glGetUniformLocation(pSh->GLid, "uSpecularMinDot"); \/\/ \n    pSh->l_uSpecularPowerOf = glGetUniformLocation(pSh->GLid, "uSpecularPowerOf"); \/\/ \n    return 1;\n}\nint Shader::cleanUp() {\n    int shadersN = shaders.size();\n    if (shadersN < 1)\n        return -1;\n    glUseProgram(0);\n    for (int i = 0; i < shadersN; i++) {\n        Shader* pSh = shaders.at(i);\n        glDeleteProgram(pSh->GLid);\n        delete pSh;\n    }\n    shaders.clear();\n    return 1;\n}\n\nGLchar infoLog[1024];\nint logLength;\nint Shader::shaderErrorCheck(int shaderId, std::string ref) {\n    \/\/use after glCompileShader()\n    if (checkGLerrors(ref) > 0)\n        return -1;\n    glGetShaderInfoLog(shaderId, 1024, &logLength, infoLog);\n    if (logLength == 0)\n        return 0;\n    mylog("%s shader infoLog:\\n%s\\n", ref.c_str(), infoLog);\n    return -1;\n}\nint Shader::programErrorCheck(int programId, std::string ref) {\n    \/\/use after glLinkProgram()\n    if (checkGLerrors(ref) > 0)\n        return -1;\n    glGetProgramInfoLog(programId, 1024, &logLength, infoLog);\n    if (logLength == 0)\n        return 0;\n    mylog("%s program infoLog:\\n%s\\n", ref.c_str(), infoLog);\n    return -1;\n}\n\nint Shader::compileShader(const char* filePath, GLenum shaderType) {\n    int shaderId = glCreateShader(shaderType);\n    FILE* pFile;\n    myFopen_s(&pFile, filePath, "rt");\n    if (pFile != NULL)\n    {\n        \/\/ obtain file size:\n        fseek(pFile, 0, SEEK_END);\n        int fSize = ftell(pFile);\n        rewind(pFile);\n        \/\/ size obtained, create buffer\n        char* shaderSource = new char[fSize + 1];\n        fSize = fread(shaderSource, 1, fSize, pFile);\n        shaderSource[fSize] = 0;\n        fclose(pFile);\n        \/\/ source code loaded, compile\n        glShaderSource(shaderId, 1, (const GLchar**)&shaderSource, NULL);\n        \/\/myglErrorCheck("glShaderSource");\n        glCompileShader(shaderId);\n        if (shaderErrorCheck(shaderId, "glCompileShader") < 0)\n            return -1;\n        delete[] shaderSource;\n    }\n    else {\n        mylog("ERROR loading %s\\n", filePath);\n        return -1;\n    }\n    return shaderId;\n}\nint Shader::linkShaderProgram(const char* filePathVertexS, const char* filePathFragmentS) {\n    int vertexShaderId = compileShader(filePathVertexS, GL_VERTEX_SHADER);\n    int fragmentShaderId = compileShader(filePathFragmentS, GL_FRAGMENT_SHADER);\n    int programId = glCreateProgram();\n    glAttachShader(programId, vertexShaderId);\n    glAttachShader(programId, fragmentShaderId);\n    glLinkProgram(programId);\n    if (programErrorCheck(programId, "glLinkProgram") < 0)\n        return -1;\n    \/\/don't need shaders any longer - detach and delete them\n    glDetachShader(programId, vertexShaderId);\n    glDetachShader(programId, fragmentShaderId);\n    glDeleteShader(vertexShaderId);\n    glDeleteShader(fragmentShaderId);\n    return programId;\n}\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u0412 DrawJob <\/em>\u0443 \u043d\u0430\u0441 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u043e\u0432\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b.<\/p>\n\n\n\n

          \u0415\u0449\u0435 \u0432 executeDrawJob()<\/em> – \u043d\u043e\u0432\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u044e\u043d\u0438\u0444\u043e\u0440\u043c\u044b, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f\u043c \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u0438.<\/p>\n\n\n\n

          6. \u0417\u0430\u043c\u0435\u043d\u0438\u043c DrawJob.h<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#pragma once\n#include "Material.h"\n#include <vector>\n\nstruct AttribRef \/\/attribute reference\/description\n{\n\tunsigned int glVBOid = 0; \/\/buffer object id\n\tint offset = 0; \/\/variable's offset inside of VBO's element\n\tint stride = 0; \/\/Buffer's element size in bytes\n};\n\nclass DrawJob\n{\npublic:\n\tMaterial mt;\n\tint pointsN = 0; \/\/N of points to draw\n\tunsigned int glVAOid = 0; \/\/will hold data stream attributes mapping\/positions\n\tunsigned int glEBOid = 0; \/\/Element Buffer Object (vertex indices)\n\n\t\/\/common attributes\n\tAttribRef aPos;\n\tAttribRef aNormal;\n\tAttribRef aTuv;\n\tAttribRef aTuv2; \/\/for normal map\n\tAttribRef aTangent; \/\/for normal map\n\tAttribRef aBinormal; \/\/for normal map\n\n\t\/\/static arrays (vectors) of all loaded DrawJobs, VBO ids\n\tstatic std::vector<DrawJob*> drawJobs;\n\tstatic std::vector<unsigned int> buffersIds;\npublic:\n\tDrawJob();\n\tvirtual ~DrawJob(); \/\/destructor\n\tstatic int cleanUp();\n\tstatic int newBufferId();\n\tint buildVAO() { return buildVAOforShader(this, mt.shaderN); };\n\tstatic int buildVAOforShader(DrawJob* pDJ, int shaderN);\n\tstatic int attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAttribRef);\n\n\tvirtual int setDesirableOffsets(int* pStride, int shaderN, int VBOid) { return setDesirableOffsetsForSingleVBO(this, pStride, shaderN, VBOid); };\n\tstatic int setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid);\n\n\tint execute(float* uMVP, float* uMV, float* dir2light, float* halfVector, Material* pMt) { return executeDrawJob(this, uMVP, uMV, dir2light, halfVector, pMt); };\n\tstatic int executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV, float* gir2light, float* halfVector, Material* pMt);\n};\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u041d\u0430\u0434\u043e \u043e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0432 DrawJob::setDesirableOffsetsForSingleVBO(), DrawJob::buildVAOforShader()<\/em> \u0438 \u0432 DrawJob::executeDrawJob()<\/em>.<\/p>\n\n\n\n

          7. \u0417\u0430\u043c\u0435\u043d\u0438\u043c DrawJob.cpp<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#include "DrawJob.h"\n#include "platform.h"\n#include "utils.h"\n#include "Shader.h"\n#include "Texture.h"\n\n\/\/static arrays (vectors) of all loaded DrawJobs, VBO ids\nstd::vector<DrawJob*> DrawJob::drawJobs;\nstd::vector<unsigned int> 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 > 0)\n\t\tglDeleteVertexArrays(1, &glVAOid);\n}\nint DrawJob::newBufferId() {\n\tunsigned int bufferId;\n\tglGenBuffers(1, &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->glVAOid > 0) {\n\t\tglBindBuffer(GL_ARRAY_BUFFER, 0);\n\t\tglDeleteVertexArrays(1, &(pDJ->glVAOid));\n\t}\n\tglGenVertexArrays(1, &pDJ->glVAOid);\n\tglBindVertexArray(pDJ->glVAOid);\n\n\t\/\/open shader descriptor to access variables locations\n\tShader* pShader = Shader::shaders.at(pDJ->mt.shaderN);\n\n\tactiveVBOid = 0;\n\tattachAttribute(pShader->l_aPos, 3, &pDJ->aPos);\n\tattachAttribute(pShader->l_aNormal, 3, &pDJ->aNormal);\n\tattachAttribute(pShader->l_aTuv, 2, &pDJ->aTuv);\n\tattachAttribute(pShader->l_aTuv2, 2, &pDJ->aTuv2); \/\/for normal map\n\tattachAttribute(pShader->l_aTangent, 3, &pDJ->aTangent); \/\/for normal map\n\tattachAttribute(pShader->l_aBinormal, 3, &pDJ->aBinormal); \/\/for normal map\n\n\tif (pDJ->glEBOid > 0)\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->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 < 0)\n\t\treturn 0; \/\/not used in this shader\n\tif (pAR->glVBOid == 0) {\n\t\tmylog("ERROR in DrawJob::attachAttribute, nk such attribute\/VBO\\n");\n\t\treturn -1;\n\t}\n\tglEnableVertexAttribArray(varLocationInShader);\n\tif (activeVBOid != pAR->glVBOid) {\n\t\tactiveVBOid = pAR->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->stride, (void*)(long)pAR->offset);\n\treturn 1;\n}\nint DrawJob::executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV3x3, float* dir2light, float* halfVector, Material* pMt) {\n\tif (pMt == NULL)\n\t\tpMt = &(pDJ->mt);\n\tglBindVertexArray(pDJ->glVAOid);\n\tShader* pShader = Shader::shaders.at(pMt->shaderN);\n\tglUseProgram(pShader->GLid);\n\t\/\/input uniforms\n\tglUniformMatrix4fv(pShader->l_uMVP, 1, GL_FALSE, (const GLfloat*)uMVP);\n\tglUniformMatrix3fv(pShader->l_uMV3x3, 1, GL_FALSE, (const GLfloat*)uMV3x3);\n\tif (pShader->l_uVectorToLight >= 0)\n\t\tglUniform3fv(pShader->l_uVectorToLight, 1, (const GLfloat*)dir2light);\n\tif (pShader->l_uHalfVector >= 0)\n\t\tglUniform3fv(pShader->l_uHalfVector, 1, (const GLfloat*)halfVector);\n\n\t\/\/attach textures\n\tif (pShader->l_uTex0 >= 0) {\n\t\tint textureId = Texture::getGLid(pMt->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->l_uTex0, 0);\n\t}\n\tif (pShader->l_uTex1mask >= 0) {\n\t\tint textureId = Texture::getGLid(pMt->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->l_uTex1mask, 1);\n\t}\n\tif (pShader->l_uTex2nm >= 0) {\n\t\tint textureId = Texture::getGLid(pMt->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->l_uTex2nm, 2);\n\t}\n\tif (pShader->l_uTex0translateChannelN >= 0) {\n\t\tglUniform1i(pShader->l_uTex0translateChannelN, pMt->uTex0translateChannelN);\n\t\tif (pShader->l_uTex3 >= 0 && pMt->uTex3 >= 0) {\n\t\t\tint textureId = Texture::getGLid(pMt->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->l_uTex3, 3);\n\t\t}\n\t}\n\t\/\/material uniforms\n\tif (pShader->l_uTex1alphaChannelN >= 0)\n\t\tglUniform1i(pShader->l_uTex1alphaChannelN, pMt->uTex1alphaChannelN);\n\tif (pShader->l_uTex1alphaNegative >= 0)\n\t\tglUniform1i(pShader->l_uTex1alphaNegative, pMt->uTex1alphaNegative);\n\tif (pShader->l_uColor >= 0)\n\t\tglUniform4fv(pShader->l_uColor, 1, pMt->uColor.forGL());\n\tif (pShader->l_uAlphaFactor >= 0)\n\t\tglUniform1f(pShader->l_uAlphaFactor, pMt->uAlphaFactor);\n\tif (pShader->l_uAlphaBlending >= 0)\n\t\tglUniform1i(pShader->l_uAlphaBlending, pMt->uAlphaBlending);\n\tif (pShader->l_uAmbient >= 0)\n\t\tglUniform1f(pShader->l_uAmbient, pMt->uAmbient);\n\tif (pShader->l_uSpecularIntencity >= 0)\n\t\tglUniform1f(pShader->l_uSpecularIntencity, pMt->uSpecularIntencity);\n\tif (pShader->l_uSpecularMinDot >= 0)\n\t\tglUniform1f(pShader->l_uSpecularMinDot, pMt->uSpecularMinDot);\n\tif (pShader->l_uSpecularPowerOf >= 0)\n\t\tglUniform1f(pShader->l_uSpecularPowerOf, pMt->uSpecularPowerOf);\n\n\t\/\/adjust render settings\n\tif (pShader->l_uAlphaBlending >= 0 && pMt->uAlphaBlending > 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->glEBOid == 0) {\n\t\tglDrawArrays(pMt->primitiveType, 0, pDJ->pointsN);\n\t}\n\telse { \/\/use EBO\n\t\tglDrawElements(pMt->primitiveType, pDJ->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 < 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 < itemsN; i++) {\n\t\tunsigned int id = buffersIds.at(i);\n\t\tglDeleteBuffers(1, &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->aPos.offset = 0; \/\/attribute o_aPos, always 0\n\tstride += sizeof(float) * 3; \/\/aPos size - 3 floats (x,y,z)\n\tif (pSh->l_aNormal >= 0) { \/\/attribute normal\n\t\tpDJ->aNormal.offset = stride;\n\t\tstride += sizeof(float) * 3;\n\t}\n\tif (pSh->l_aTuv >= 0) { \/\/attribute TUV (texture coordinates)\n\t\tpDJ->aTuv.offset = stride; \/\/attribute TUV (texture coordinates)\n\t\tstride += sizeof(float) * 2;\n\t}\n\tif (pSh->l_aTuv2 >= 0) { \/\/for normal map\n\t\tpDJ->aTuv2.offset = stride;\n\t\tstride += sizeof(float) * 2;\n\t}\n\tif (pSh->l_aTangent >= 0) { \/\/for normal map\n\t\tpDJ->aTangent.offset = stride;\n\t\tstride += sizeof(float) * 3;\n\t}\n\tif (pSh->l_aBinormal >= 0) { \/\/for normal map\n\t\tpDJ->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 = &pDJ->aPos; pAR->glVBOid = VBOid; pAR->stride = stride;\n\tpAR = &pDJ->aNormal; pAR->glVBOid = VBOid; pAR->stride = stride;\n\tpAR = &pDJ->aTuv; pAR->glVBOid = VBOid; pAR->stride = stride;\n\tpAR = &pDJ->aTuv2; pAR->glVBOid = VBOid; pAR->stride = stride;\n\tpAR = &pDJ->aTangent; pAR->glVBOid = VBOid; pAR->stride = stride;\n\tpAR = &pDJ->aBinormal; pAR->glVBOid = VBOid; pAR->stride = stride;\n\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u044e\u043d\u0438\u0444\u043e\u0440\u043c\u044b \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0443 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0432 Material.h<\/em>.<\/p>\n\n\n\n

          8. \u0417\u0430\u043c\u0435\u043d\u0438\u043c Material.h<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n #pragma once\n#include "MyColor.h"\n\nclass Material\n{\npublic:\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'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.95f;\n\tfloat uSpecularPowerOf = 20.0f;\n};\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0432 ModelBuilder::buildSingleDrawJob()<\/em>.<\/p>\n\n\n\n

          9. \u0417\u0430\u043c\u0435\u043d\u0438\u043c ModelBuilder.cpp<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#include "ModelBuilder.h"\n#include "platform.h"\n#include "utils.h"\n#include "DrawJob.h"\n#include "Shader.h"\n\nextern float degrees2radians;\n\nModelBuilder::~ModelBuilder() {\n\t\/\/clear all vectors\n\tint itemsN = vertices.size();\n\tfor (int i = 0; i < itemsN; i++)\n\t\tdelete vertices.at(i);\n\tvertices.clear();\n\n\titemsN = triangles.size();\n\tfor (int i = 0; i < itemsN; i++)\n\t\tdelete triangles.at(i);\n\ttriangles.clear();\n\n\titemsN = vShapesStack.size();\n\tfor (int i = 0; i < itemsN; i++)\n\t\tdelete vShapesStack.at(i);\n\tvShapesStack.clear();\n\n\titemsN = groupsStack.size();\n\tfor (int i = 0; i < itemsN; i++)\n\t\tdelete groupsStack.at(i);\n\tgroupsStack.clear();\n\n\titemsN = materialsList.size();\n\tfor (int i = 0; i < itemsN; i++)\n\t\tdelete materialsList.at(i);\n\tmaterialsList.clear();\n\n\tsubjNumbersList.clear();\n}\nint ModelBuilder::useSubjN(ModelBuilder* pMB, int subjN) {\n\tpMB->usingSubjN = subjN;\n\tint itemsN = pMB->subjNumbersList.size();\n\tbool newN = true;\n\tif (itemsN > 0)\n\t\tfor (int i = 0; i < itemsN; i++)\n\t\t\tif (pMB->subjNumbersList.at(i) == subjN) {\n\t\t\t\tnewN = false;\n\t\t\t\tbreak;\n\t\t\t}\n\tif (newN)\n\t\tpMB->subjNumbersList.push_back(subjN);\n\treturn subjN;\n}\nint ModelBuilder::useMaterial(ModelBuilder* pMB, Material* pMT) {\n\tint itemsN = pMB->materialsList.size();\n\tif (itemsN > 0)\n\t\tfor (int i = 0; i < itemsN; i++)\n\t\t\tif (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {\n\t\t\t\tpMB->usingMaterialN = i;\n\t\t\t\treturn i;\n\t\t\t}\n\t\/\/if here - add new material to the list\n\tpMB->usingMaterialN = itemsN;\n\t\/\/create a copy of new Material and add to the list\n\tMaterial* pMTnew = new Material(*pMT);\n\tpMB->materialsList.push_back(pMTnew);\n\treturn itemsN;\n}\nint ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\t\/\/this code is for simple box\n\tVirtualShape vs; \/\/face VS, \n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\t\/\/rotate desirable side to face us. \n\tif (applyTo.find("front") == 0) {\n\t\t\/\/Side <front> is facing us as is.\n\t\tvs.whl[0] = pVS->whl[0];\n\t\tvs.whl[1] = pVS->whl[1];\n\t\t\/\/define how to move\/place generated face back to the VirtualShape\n\t\t\/\/just shift closer to us by length\/2\n\t\tmat4x4_translate(transformMatrix, 0, 0, pVS->whl[2] \/ 2);\n\t}\n\telse if (applyTo.find("back") == 0) {\n\t\tvs.whl[0] = pVS->whl[0];\n\t\tvs.whl[1] = pVS->whl[1];\n\t\t\/\/rotate 180 degrees around Y and shift farther from us by half-length\n\t\tmat4x4_translate(transformMatrix, 0, 0, -pVS->whl[2] \/ 2);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 180);\n\t}\n\telse if (applyTo.find("left") == 0) {\n\t\tvs.whl[0] = pVS->whl[2]; \/\/width = original length\n\t\tvs.whl[1] = pVS->whl[1];\n\t\t\/\/rotate -90 degrees around Y (CW) and shift half-width to the left\n\t\tmat4x4_translate(transformMatrix, -pVS->whl[0] \/ 2, 0, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 90);\n\t}\n\telse if (applyTo.find("right") == 0) {\n\t\tvs.whl[0] = pVS->whl[2]; \/\/width = original length\n\t\tvs.whl[1] = pVS->whl[1];\n\t\t\/\/rotate +90 degrees around Y (CCW) and shift half-width to the right\n\t\tmat4x4_translate(transformMatrix, pVS->whl[0] \/ 2, 0, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 90);\n\t}\n\telse if (applyTo.find("top") == 0) {\n\t\tvs.whl[0] = pVS->whl[0];\n\t\tvs.whl[1] = pVS->whl[2]; \/\/height = original length\n\t\t\/\/rotate -90 degrees around X (CW) and 180 around Y, and shift half-height up\n\t\tmat4x4_translate(transformMatrix, 0, pVS->whl[1] \/ 2, 0);\n\t\tmat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 180);\n\t\tmat4x4_rotate_X(transformMatrix, transformMatrix, -degrees2radians * 90);\n\t}\n\telse if (applyTo.find("bottom") == 0) {\n\t\tvs.whl[0] = pVS->whl[0];\n\t\tvs.whl[1] = pVS->whl[2]; \/\/height = original length\n\t\t\/\/rotate 90 around X (CCW) and shift half-height down\n\t\tmat4x4_translate(transformMatrix, 0, -pVS->whl[1] \/ 2, 0);\n\t\tmat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * 90);\n\t}\n\tstartGroup(pMB);\n\t\/\/create vertices\n\tint sectionsX = 1;\n\tint sectionsY = 1;\n\tint pointsX = sectionsX + 1;\n\tint pointsY = sectionsY + 1;\n\tfloat stepX = vs.whl[0] \/ sectionsX;\n\tfloat stepY = vs.whl[1] \/ sectionsY;\n\tfloat kY = vs.whl[1] \/ 2;\n\tfor (int iy = 0; iy < pointsY; iy++) {\n\t\tfloat kX = -vs.whl[0] \/ 2;\n\t\tfor (int ix = 0; ix < pointsX; ix++) {\n\t\t\tint nSE = addVertex(pMB, kX, kY, 0, 0, 0, 1); \/\/vertex number on south-east\n\t\t\tif (iy > 0 && ix > 0) {\n\t\t\t\t\/\/add 2 triangles\n\t\t\t\tint nSW = nSE - 1; \/\/vertex number south-west\n\t\t\t\tint nNE = nSE - pointsX; \/\/north-east\n\t\t\t\tint nNW = nSW - pointsX; \/\/north-west\n\t\t\t\tadd2triangles(pMB, nNW, nNE, nSW, nSE, iy + ix);\n\t\t\t}\n\t\t\tkX += stepX;\n\t\t}\n\t\tkY -= stepY;\n\t}\n\t\/\/move face to it's place (apply transform matrix)\n\tint vertsN = pMB->vertices.size();\n\tfor (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {\n\t\tVertex01* pVX = pMB->vertices.at(i);\n\t\tmat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);\n\t\tmat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);\n\t}\n\tendGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::add2triangles(ModelBuilder* pMB, int nNW, int nNE, int nSW, int nSE, int n) {\n\t\/\/indexes: NorthWest, NorthEast, SouthWest,SouthEast\n\tif (n % 2 == 0) { \/\/even number\n\t\taddTriangle(pMB, nNW, nSW, nNE);\n\t\taddTriangle(pMB, nNE, nSW, nSE);\n\t}\n\telse { \/\/odd number\n\t\taddTriangle(pMB, nNW, nSE, nNE);\n\t\taddTriangle(pMB, nNW, nSW, nSE);\n\t}\n\treturn pMB->triangles.size() - 1;\n}\nint ModelBuilder::addTriangle(ModelBuilder* pMB, int i0, int i1, int i2) {\n\tTriangle01* pTR = new Triangle01();\n\tpMB->triangles.push_back(pTR);\n\tpTR->idx[0] = i0;\n\tpTR->idx[1] = i1;\n\tpTR->idx[2] = i2;\n\tpTR->subjN = pMB->usingSubjN;\n\tpTR->materialN = pMB->usingMaterialN;\n\treturn pMB->triangles.size() - 1;\n}\nvoid ModelBuilder::startGroup(ModelBuilder* pMB) {\n\tpMB->pCurrentGroup = new Group01();\n\tpMB->groupsStack.push_back(pMB->pCurrentGroup);\n\tpMB->pCurrentGroup->fromVertexN = pMB->vertices.size();\n\tpMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();\n}\nvoid ModelBuilder::endGroup(ModelBuilder* pMB) {\n\tdelete pMB->pCurrentGroup;\n\tpMB->pCurrentGroup = pMB->groupsStack.back();\n\tpMB->groupsStack.pop_back();\n}\nint ModelBuilder::addVertex(ModelBuilder* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {\n\tVertex01* pVX = new Vertex01();\n\tpMB->vertices.push_back(pVX);\n\tpVX->aPos[0] = kx;\n\tpVX->aPos[1] = ky;\n\tpVX->aPos[2] = kz;\n\t\/\/normal\n\tpVX->aNormal[0] = nx;\n\tpVX->aNormal[1] = ny;\n\tpVX->aNormal[2] = nz;\n\tpVX->subjN = pMB->usingSubjN;\n\tpVX->materialN = pMB->usingMaterialN;\n\treturn pMB->vertices.size() - 1;\n}\nint ModelBuilder::buildDrawJobs(ModelBuilder* pMB, std::vector<GameSubj*> gameSubjs) {\n\tint totalSubjsN = pMB->subjNumbersList.size();\n\tif (totalSubjsN < 1) {\n\t\tpMB->subjNumbersList.push_back(-1);\n\t\ttotalSubjsN = 1;\n\t}\n\tint totalMaterialsN = pMB->materialsList.size();\n\tif (totalSubjsN < 2 && totalMaterialsN < 2) {\n\t\t\/\/simple single DrawJob\n\t\tMaterial* pMT = pMB->materialsList.at(0);\n\t\tGameSubj* pGS = NULL;\n\t\tint gsN = pMB->subjNumbersList.at(0);\n\t\tif (gsN >= 0)\n\t\t\tpGS = gameSubjs.at(gsN);\n\t\tif (pGS != NULL)\n\t\t\tpGS->djStartN = DrawJob::drawJobs.size();\n\t\tbuildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);\n\t\tif (pGS != NULL)\n\t\t\tpGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;\n\t\treturn 1;\n\t}\n\tint totalVertsN = pMB->vertices.size();\n\tint totalTrianglesN = pMB->triangles.size();\n\t\/\/clear flags\n\tfor (int vN = 0; vN < totalVertsN; vN++) {\n\t\tVertex01* pVX = pMB->vertices.at(vN);\n\t\tpVX->flag = 0;\n\t}\n\tfor (int tN = 0; tN < totalTrianglesN; tN++) {\n\t\tTriangle01* pTR = pMB->triangles.at(tN);\n\t\tpTR->flag = 0;\n\t}\n\tint addedDJs = 0;\n\tfor (int sN = 0; sN < totalSubjsN; sN++) {\n\t\tGameSubj* pGS = NULL;\n\t\tint gsN = pMB->subjNumbersList.at(sN);\n\t\tif (gsN >= 0)\n\t\t\tpGS = gameSubjs.at(gsN);\n\t\tif (pGS != NULL)\n\t\t\tpGS->djStartN = DrawJob::drawJobs.size();\n\t\tfor (int mtN = 0; mtN < totalMaterialsN; mtN++) {\n\t\t\tMaterial* pMT = pMB->materialsList.at(mtN);\n\t\t\tstd::vector<Vertex01*> useVertices;\n\t\t\tstd::vector<Triangle01*> useTriangles;\n\t\t\tfor (int vN = 0; vN < totalVertsN; vN++) {\n\t\t\t\tVertex01* pVX = pMB->vertices.at(vN);\n\t\t\t\tif (pVX->flag != 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pVX->subjN != gsN)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pVX->materialN != mtN)\n\t\t\t\t\tcontinue;\n\t\t\t\t\/\/if here - make a copy\n\t\t\t\tVertex01* pVX2 = new Vertex01(*pVX);\n\t\t\t\tuseVertices.push_back(pVX2);\n\t\t\t\tpVX2->altN = vN;\n\t\t\t\tpVX->flag = 1;\n\t\t\t\tif (pVX->endOfSequence > 0) {\n\t\t\t\t\trearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);\n\t\t\t\t\tbuildSingleDrawJob(pMT, useVertices, useTriangles);\n\t\t\t\t\taddedDJs++;\n\t\t\t\t\t\/\/clear and proceed to next sequence\n\t\t\t\t\tint useVerticesN = useVertices.size();\n\t\t\t\t\tfor (int i = 0; i < useVerticesN; i++)\n\t\t\t\t\t\tdelete useVertices.at(i);\n\t\t\t\t\tuseVertices.clear();\n\t\t\t\t}\n\t\t\t}\n\t\t\tint useVerticesN = useVertices.size();\n\t\t\tif (useVerticesN < 1)\n\t\t\t\tcontinue; \/\/to next material\n\t\t\t\/\/pick triangles\n\t\t\tfor (int tN = 0; tN < totalTrianglesN; tN++) {\n\t\t\t\tTriangle01* pTR = pMB->triangles.at(tN);\n\t\t\t\tif (pTR->flag != 0)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pTR->subjN != gsN)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (pTR->materialN != mtN)\n\t\t\t\t\tcontinue;\n\t\t\t\t\/\/if here - make a copy\n\t\t\t\tTriangle01* pTR2 = new Triangle01(*pTR);\n\t\t\t\tuseTriangles.push_back(pTR2);\n\t\t\t\tpTR->flag = 1;\n\t\t\t}\n\t\t\trearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);\n\t\t\tbuildSingleDrawJob(pMT, useVertices, useTriangles);\n\t\t\taddedDJs++;\n\t\t\t\/\/clear all for next material\n\t\t\tfor (int i = 0; i < useVerticesN; i++)\n\t\t\t\tdelete useVertices.at(i);\n\t\t\tuseVertices.clear();\n\t\t\tint useTrianglesN = useTriangles.size();\n\t\t\tfor (int i = 0; i < useTrianglesN; i++)\n\t\t\t\tdelete useTriangles.at(i);\n\t\t\tuseTriangles.clear();\n\t\t}\n\t\tif (pGS != NULL)\n\t\t\tpGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;\n\t}\n\treturn addedDJs;\n}\nint ModelBuilder::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {\n\tint totalVertsN = useVertices.size();\n\tif (totalVertsN < 1)\n\t\treturn 1;\n\tDrawJob* pDJ = new DrawJob();\n\t\/\/copy material to DJ\n\tmemcpy(&pDJ->mt, pMT, sizeof(Material));\n\t\/\/calculate VBO element size (stride) and variables offsets in VBO\n\tint VBOid = DrawJob::newBufferId();\n\tint stride = 0;\n\tpDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);\n\t\/\/create an array for VBO\n\tint bufferSize = totalVertsN * stride;\n\tfloat* vertsBuffer = new float[bufferSize];\n\t\/\/fill vertsBuffer\n\tShader* pSh = Shader::shaders.at(pDJ->mt.shaderN);\n\tint floatSize = sizeof(float);\n\tfor (int vN = 0; vN < totalVertsN; vN++) {\n\t\tVertex01* pVX = useVertices.at(vN);\n\t\tint idx = vN * stride \/ floatSize;\n\t\t\/\/pick data from vertex and move to the buffer\n\t\tmemcpy(&vertsBuffer[idx + pDJ->aPos.offset \/ floatSize], pVX->aPos, 3 * floatSize);\n\t\tif (pSh->l_aNormal >= 0) \/\/normal\n\t\t\tmemcpy(&vertsBuffer[idx + pDJ->aNormal.offset \/ floatSize], pVX->aNormal, 3 * floatSize);\n\t\tif (pSh->l_aTuv >= 0) \/\/attribute TUV (texture coordinates)\n\t\t\tmemcpy(&vertsBuffer[idx + pDJ->aTuv.offset \/ floatSize], pVX->aTuv, 2 * floatSize);\n\t\tif (pSh->l_aTuv2 >= 0) \/\/attribute TUV2 (normal maps)\n\t\t\tmemcpy(&vertsBuffer[idx + pDJ->aTuv2.offset \/ floatSize], pVX->aTuv2, 2 * floatSize);\n\t\tif (pSh->l_aTangent >= 0)\n\t\t\tmemcpy(&vertsBuffer[idx + pDJ->aTangent.offset \/ floatSize], pVX->aTangent, 3 * floatSize);\n\t\tif (pSh->l_aBinormal >= 0)\n\t\t\tmemcpy(&vertsBuffer[idx + pDJ->aBinormal.offset \/ floatSize], pVX->aBinormal, 3 * floatSize);\n\t}\n\t\/\/buffer is ready, create VBO\n\tglBindBuffer(GL_ARRAY_BUFFER, VBOid);\n\tglBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);\n\tdelete[] vertsBuffer;\n\tpDJ->pointsN = totalVertsN;\n\n\tint totalTrianglesN = useTriangles.size();\n\tif (totalTrianglesN > 0) {\n\t\t\/\/create EBO\n\t\tint totalIndexesN = totalTrianglesN * 3;\n\t\t\/\/create buffer\n\t\tGLushort* indexBuffer = new GLushort[totalIndexesN];\n\t\tfor (int tN = 0; tN < totalTrianglesN; tN++) {\n\t\t\tTriangle01* pTR = useTriangles[tN];\n\t\t\tint idx = tN * 3;\n\t\t\tindexBuffer[idx + 0] = (GLushort)pTR->idx[0];\n\t\t\tindexBuffer[idx + 1] = (GLushort)pTR->idx[1];\n\t\t\tindexBuffer[idx + 2] = (GLushort)pTR->idx[2];\n\t\t}\n\t\t\/\/buffer is ready, create IBO\n\t\tpDJ->glEBOid = DrawJob::newBufferId();\n\t\tglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);\n\t\tglBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);\n\t\tdelete[] indexBuffer;\n\t\tpDJ->pointsN = totalIndexesN;\n\t}\n\t\/\/create and fill vertex attributes array (VAO)\n\tpDJ->buildVAO();\n\treturn 1;\n}\nint ModelBuilder::rearrangeArraysForDrawJob(ModelBuilder* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {\n\tint totalTrianglesN = useTriangles.size();\n\tif (totalTrianglesN < 1)\n\t\treturn 0;\n\tint totalVerticesN = useVertices.size();\n\t\/\/save new vertices order in original vertices array\n\t\/\/since triangles indices refer to original vertices order\n\tfor (int i = 0; i < totalVerticesN; i++) {\n\t\tVertex01* pVX1 = useVertices.at(i);\n\t\tVertex01* pVX0 = allVertices.at(pVX1->altN);\n\t\tpVX0->altN = i;\n\t}\n\t\/\/replace triangle original indices by new numbers saved in original vertices altN\n\tfor (int tN = 0; tN < totalTrianglesN; tN++) {\n\t\tTriangle01* pTR = useTriangles.at(tN);\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tVertex01* pVX0 = allVertices.at(pTR->idx[i]);\n\t\t\tpTR->idx[i] = pVX0->altN;\n\t\t}\n\t}\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u0412 TheGame.h<\/em> – \u043d\u043e\u0432\u0430\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f dirToMainLight<\/em>.<\/p>\n\n\n\n

          10. \u0417\u0430\u043c\u0435\u043d\u0438\u043c TheGame.h<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#pragma once\n#include <vector>\n#include "GameSubj.h"\n#include "Camera.h"\n\nclass TheGame\n{\npublic:\n\tint screenSize[2];\n\tfloat screenRatio;\n\tbool bExitGame;\n\tCamera mainCamera;\n\tfloat dirToMainLight[4] = { 1,1,1,0 };\n\n\t\/\/static arrays (vectors) of active GameSubjs\n\tstatic std::vector<GameSubj*> gameSubjs;\npublic:\n\tint run();\n\tint getReady();\n\tint drawFrame();\n\tint cleanUp();\n\tint onScreenResize(int width, int height);\n};\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 TheGame::getReady()<\/em> \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c Main Light. \u041e\u0434\u0438\u043d\u043e\u0447\u043d\u044b\u0439 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0441\u0432\u0435\u0442\u0430 (Directional single-source light) – \u0432\u043f\u043e\u043b\u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. \u0420\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0435\u0433\u043e (\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a) \u0437\u0430 \u043d\u0430\u0448\u0438\u043c \u043b\u0435\u0432\u044b\u043c \u043f\u043b\u0435\u0447\u043e\u043c. \u0422\u043e\u0433\u0434\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a \u0441\u0432\u0435\u0442\u0443, dirToMainLight<\/em>, \u0431\u0443\u0434\u0435\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u043c {-1,1,1}.<\/p>\n\n\n\n

          \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 TheGame::drawFrame()<\/em> \u043c\u044b \u0440\u0430\u0441\u0447\u0438\u0442\u0430\u0435\u043c uHalfVector <\/em>\u043a\u0430\u043a \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 dirToMainLight <\/em>\u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043d\u0430 \u043a\u0430\u043c\u0435\u0440\u0443.<\/p>\n\n\n\n

          \u0412 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0446\u0438\u043a\u043b\u0435 \u043c\u044b \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e 4\u04454 \u043c\u0430\u0442\u0440\u0438\u0446\u0443 mMVP <\/em>(Model-View-Projection) \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430 3D \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442, \u043d\u043e \u0422\u0410\u041a\u0416\u0415 \u0438 3\u04453 \u043c\u0430\u0442\u0440\u0438\u0446\u0443 mMV3x3 <\/em>(3×3 Model-View) \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u0430 \u043d\u043e\u0440\u043c\u0430\u043b\u0435\u0439.<\/p>\n\n\n\n

          \u041d\u0443 \u0438 \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043a\u0443\u0431\u0438\u043a\u0430 \u043c\u044b \u0432\u044b\u0431\u0435\u0440\u0435\u043c \u043d\u043e\u0432\u044b\u0439 spN_phong<\/strong>_ucolor<\/em> \u0448\u0435\u0439\u0434\u0435\u0440.<\/p>\n\n\n\n

          11. \u0417\u0430\u043c\u0435\u043d\u0438\u043c TheGame.cpp<\/em> \u043a\u043e\u0434 \u043d\u0430:<\/p>\n\n\n

          \n#include "TheGame.h"\n#include "platform.h"\n#include "utils.h"\n#include "linmath.h"\n#include "Texture.h"\n#include "Shader.h"\n#include "DrawJob.h"\n#include "ModelBuilder.h"\n\nextern std::string filesRoot;\n\nstd::vector<GameSubj*> TheGame::gameSubjs;\n\nint TheGame::getReady() {\n    bExitGame = false;\n    Shader::loadShaders();\n    glEnable(GL_CULL_FACE);\n\n    \/\/=== create box ========================\n    GameSubj* pGS = new GameSubj();\n    gameSubjs.push_back(pGS);\n\n    pGS->name.assign("box1");\n    pGS->ownCoords.setPosition(0, 0, 0);\n    pGS->ownCoords.setDegrees(0, 0, 0);\n    pGS->ownSpeed.setDegrees(0,1,0);\n\n    ModelBuilder* pMB = new ModelBuilder();\n    pMB->useSubjN(gameSubjs.size() - 1);\n\n    \/\/define VirtualShape\n    VirtualShape vs;\n    vs.type.assign("box");\n    vs.whl[0] = 100;\n    vs.whl[1] = 200;\n    vs.whl[2] = 400;\n\n    Material mt;\n    \/\/define material - flat red\n    mt.shaderN = Shader::spN_phong_ucolor;\n    mt.primitiveType = GL_TRIANGLES;\n    mt.uColor.setRGBA(255, 0, 0,255); \/\/red\n    pMB->useMaterial(&mt);\n\n    pMB->buildBoxFace("front", &vs);\n    pMB->buildBoxFace("back", &vs);\n    pMB->buildBoxFace("top", &vs);\n    pMB->buildBoxFace("bottom", &vs);\n    pMB->buildBoxFace("left", &vs);\n\n    mt.uColor.setRGBA(0, 0, 255,255); pMB->useMaterial(&mt); \/\/blue\n    pMB->buildBoxFace("right", &vs);\n\n    pMB->buildDrawJobs(gameSubjs);\n\n    delete pMB;\n\n    \/\/===== set up camera\n    v3set(mainCamera.ownCoords.pos, 0, 200, 1000); \/\/set position\n    float cameraDir[3];\n    v3set(cameraDir, 0, -200, -1000); \/\/set direction vector\n    float cameraYawDg = v3yawDg(cameraDir);\n    float cameraPitchDg = v3pitchDg(cameraDir);\n    \/\/mylog("cameraYaw=%f, cameraPitch=%f\\n", cameraYawDg, cameraPitchDg);\n\n    mainCamera.ownCoords.setDegrees(cameraPitchDg, cameraYawDg, 0);\n    float cameraUp[4] = { 0,1,0,0 }; \/\/y - up\n    mat4x4_mul_vec4plus(cameraUp, *mainCamera.ownCoords.getRotationMatrix(), cameraUp, 0);\n\n    mat4x4_look_at(mainCamera.lookAtMatrix, mainCamera.ownCoords.pos, pGS->ownCoords.pos, cameraUp);\n\n    \/\/===== set up light\n    v3set(dirToMainLight, -1, 1, 1);\n    vec3_norm(dirToMainLight, dirToMainLight);\n\n    return 1;\n}\nint TheGame::drawFrame() {\n    myPollEvents();\n\n    \/\/glClearColor(0.0, 0.0, 0.5, 1.0);\n    glClear(GL_COLOR_BUFFER_BIT);\n\n    \/\/calculate halfVector\n    float dirToCamera[4] = { 0,0,-1,0 }; \/\/-z\n    mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);\n\n    float uHalfVector[4] = { 0,0,0,0 };\n    for (int i = 0; i < 3; i++)\n        uHalfVector[i] = (dirToCamera[i] + dirToMainLight[i]) \/ 2;\n    vec3_norm(uHalfVector, uHalfVector);\n\n    mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;\n    \/\/mat4x4_ortho(mProjection, -(float)screenSize[0] \/ 2, (float)screenSize[0] \/ 2, -(float)screenSize[1] \/ 2, (float)screenSize[1] \/ 2, 100.f, 500.f);\n    mat4x4_perspective(mProjection, 3.14f \/ 6.0f, (float)screenSize[0] \/ screenSize[1], 700.f, 1300.f);\n    mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);\n    \/\/mViewProjection[1][3] = 0; \/\/keystone effect\n\n    \/\/scan subjects\n    int subjsN = gameSubjs.size();\n    for (int subjN = 0; subjN < subjsN; subjN++) {\n        GameSubj* pGS = gameSubjs.at(subjN);\n        \/\/behavior - apply rotation speed\n        pGS->moveSubj();\n        \/\/prepare subject for rendering\n        pGS->buildModelMatrix(pGS);\n        \/\/build MVP matrix for given subject\n        mat4x4_mul(mMVP, mViewProjection, pGS->ownModelMatrix);\n        \/\/build Model-View (rotation) matrix for normals\n        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());\n        \/\/convert to 3x3 matrix\n        float mMV3x3[3][3];\n        for (int y = 0; y < 3; y++)\n            for (int x = 0; x < 3; x++)\n                mMV3x3[y][x] = mMV4x4[y][x];\n        \/\/render subject\n        for (int i = 0; i < pGS->djTotalN; i++) {\n            DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);\n            pDJ->execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);\n        }\n    }\n    mySwapBuffers();\n    return 1;\n}\n\nint TheGame::cleanUp() {\n    int itemsN = gameSubjs.size();\n    \/\/delete all UISubjs\n    for (int i = 0; i < 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[0] == width && screenSize[1] == height)\n        return 0;\n    screenSize[0] = width;\n    screenSize[1] = height;\n    screenRatio = (float)width \/ (float)height;\n    glViewport(0, 0, width, height);\n    mylog(" screen size %d x %d\\n", width, height);\n    return 1;\n}\nint TheGame::run() {\n    getReady();\n    while (!bExitGame) {\n        drawFrame();\n    }\n    cleanUp();\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n

          <\/p>\n\n\n\n


          \n\n\n\n

          12. \u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u044f \u0438 \u0437\u0430\u043f\u0443\u0441\u043a.<\/p>\n\n\n\n

          \u0414\u043e:<\/strong><\/p>\n\n\n\n

          <\/p>\n\n\n\n


          \u041f\u043e\u0441\u043b\u0435:<\/strong><\/p>\n\n\n\n

          <\/p>\n\n\n\n

          <\/p>\n\n\n\n