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":647,"date":"2021-12-07T04:32:02","date_gmt":"2021-12-07T04:32:02","guid":{"rendered":"https:\/\/writingagame.com\/?p=647"},"modified":"2022-01-26T19:43:28","modified_gmt":"2022-01-26T19:43:28","slug":"chapter-20-modeler-round-shapes","status":"publish","type":"post","link":"https:\/\/writingagame.ru\/index.php\/2021\/12\/07\/chapter-20-modeler-round-shapes\/","title":{"rendered":"\u0413\u043b\u0430\u0432\u0430 20. Modeler. \u0421\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u043c \u0443\u0433\u043b\u044b"},"content":{"rendered":"\n

\u041d\u0430\u0448\u0430 \u043d\u044b\u043d\u0435\u0448\u043d\u044f\u044f \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0446\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u044e \u043f\u0440\u0435\u043b\u0435\u0441\u0442\u044c Phong \u0448\u0435\u0439\u0434\u0435\u0440\u0430, \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 – \u0431\u043b\u0438\u043a\u0438. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u0434\u043e\u0448\u043b\u043e \u0431\u044b \u0447\u0442\u043e-\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u043e\u043a\u0440\u0443\u0433\u043b\u043e\u0435. \u0417\u0430\u0447\u0438\u0442, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u043c Modeler.<\/p>\n\n\n\n

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

1. \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 – \u043d\u0430\u0434\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c VirtualShape.h<\/em>.<\/p>\n\n\n\n

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

\n#pragma once\n#include "platform.h"\n\nclass VirtualShape\n{\npublic:\n\tchar shapeType[20] = "box";\n\tfloat whl[3] = { 0 }; \/\/width\/height\/length (x,y,z sizes\/dimensions)\n\tint sections[3] = { 1,1,1 }; \/\/number of sections for each axis\n\tint sectionsR = 1; \/\/number of radial sections\n\t\/\/side extensions\n\tfloat extU = 0; \/\/up\n\tfloat extD = 0; \/\/down\n\tfloat extL = 0; \/\/left\n\tfloat extR = 0; \/\/right\n\tfloat extF = 0; \/\/front\n\tfloat extB = 0; \/\/back\n\npublic:\n\tvoid setShapeType(std::string needType) { setShapeType(this, needType); };\n\tstatic void setShapeType(VirtualShape* pVS, std::string needType) { myStrcpy_s(pVS->shapeType, 20, (char*)needType.c_str()); };\n\tvoid setExt(float v) { setExt(this, v); };\n\tvoid setExtX(float v) { setExtX(this, v); };\n\tvoid setExtY(float v) { setExtY(this, v); };\n\tvoid setExtZ(float v) { setExtZ(this, v); };\n\tstatic void setExt(VirtualShape* pVS, float v) { pVS->extU = v; pVS->extD = v; pVS->extL = v; pVS->extR = v; pVS->extF = v; pVS->extB = v; };\n\tstatic void setExtX(VirtualShape* pVS, float v) { pVS->extL = v; pVS->extR = v; };\n\tstatic void setExtY(VirtualShape* pVS, float v) { pVS->extU = v; pVS->extD = v; };\n\tstatic void setExtZ(VirtualShape* pVS, float v) { pVS->extF = v; pVS->extB = v; };\n};\n\n<\/pre><\/div>\n\n\n

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

\u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u0442\u0443\u0442 – “extensions” (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f). \u041e\u043d\u0438 \u043d\u0443\u0436\u043d\u044b \u0434\u043b\u044f \u0444\u043e\u0440\u043c \u0441 \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0443\u0433\u043b\u0430\u043c\u0438 \u0438 \u0440\u0435\u0431\u0440\u0430\u043c\u0438:<\/p>\n\n\n\n

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

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

\u0414\u0440\u0443\u0433\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0437\u0434\u0435\u0441\u044c – char* shapeType<\/em> \u0432\u043c\u0435\u0441\u0442\u043e std::string<\/em>.<\/p>\n\n\n\n


\n\n\n\n

\u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043d\u043e\u0432\u0430\u044f platform-specific<\/strong> \u0444\u0443\u043d\u043a\u0446\u0438\u044f – myStrcpy_s()<\/em>, \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 char. \u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u0442\u0430 \u0436\u0435 \u0447\u0442\u043e \u0438 \u0441 myFopen_s()<\/em>. \u0414\u0440\u0443\u0433\u0430\u044f platform-specific \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0430\u043c \u0441\u043a\u043e\u0440\u043e \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f – \u044d\u0442\u043e myMkDir()<\/em>, \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433.<\/p>\n\n\n\n

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

\n#pragma once\n#include <glad\/glad.h>\n#include <stdio.h>\n\ntypedef unsigned _int64 myUint64;\ntypedef unsigned _int32 myUint32;\ntypedef unsigned _int16 myUint16;\ntypedef unsigned _int8  myUint8;\n\nvoid mylog(const char* _Format, ...);\nvoid mySwapBuffers();\nvoid myPollEvents();\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode);\nint myMkDir(const char* outPath);\nvoid myStrcpy_s(char* dst, int maxSize, const char* src);\n\n<\/pre><\/div>\n\n\n

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


\n\n\n\n

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

\n#include <stdarg.h>\n#include <stdio.h>\n#include <GLFW\/glfw3.h>\n#include "platform.h"\n#include "TheGame.h"\n#include <direct.h> \/\/myMkDir: mkdir\n#include <sys\/stat.h> \/\/myMkDir: if file esists\n\nextern GLFWwindow* myMainWindow;\nextern TheGame theGame;\n\nvoid mylog(const char* _Format, ...) {\n#ifdef _DEBUG\n    va_list _ArgList;\n    va_start(_ArgList, _Format);\n    vprintf(_Format, _ArgList);\n    va_end(_ArgList);\n#endif\n};\nvoid mySwapBuffers() {\n    glfwSwapBuffers(myMainWindow);\n}\nvoid myPollEvents() {\n    glfwPollEvents();\n    \/\/check if closing the window\n    theGame.bExitGame = glfwWindowShouldClose(myMainWindow);\n    \/\/check screen size\n    int width, height;\n    glfwGetFramebufferSize(myMainWindow, &width, &height);\n    theGame.onScreenResize(width, height);\n}\nint myFopen_s(FILE** pFile, const char* filePath, const char* mode) {\n    return fopen_s(pFile, filePath, mode);\n}\nint myMkDir(const char* outPath) {\n    struct stat info;\n    if (stat(outPath, &info) == 0)\n        return 0; \/\/exists already\n    int status = _mkdir(outPath);\n    if (status == 0)\n        return 1; \/\/Successfully created\n    mylog("ERROR creating, status=%d\\n", status);\n    return -1;\n}\nvoid myStrcpy_s(char* dst, int maxSize, const char* src) {\n    strcpy_s(dst, maxSize, src);\n    \/\/fill tail by zeros\n    int strLen = strlen(dst);\n    if (strLen < maxSize)\n        for (int i = strLen; i < maxSize; i++)\n            dst[i] = 0;\n}\n\n<\/pre><\/div>\n\n\n

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


\n\n\n\n

\u041a\u043b\u0430\u0441\u0441 ModelBuilder <\/strong>\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u043e\u0440\u0430\u0436\u0438\u0432\u0430\u044e\u0449\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043c. \u041b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u043e\u0431\u044c\u0435\u043c \u0435\u0433\u043e \u043d\u0430 2 \u0443\u0440\u043e\u0432\u043d\u044f, \u043f\u0435\u0440\u0432\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c – \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e ModelBuilder1base<\/strong>. <\/p>\n\n\n\n

\u0422\u043e\u043b\u044c\u043a\u043e 1 \u043d\u043e\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f – moveGroupDg(\u2026)<\/em> – \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c \u0438 \u0441\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443 \u0432\u0435\u0440\u0442\u0435\u043a\u0441\u043e\u0432 (\u0447\u0442\u043e\u0431\u044b \u0441\u0442\u0430\u0432\u0438\u0442\u044c “\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f” \u043d\u0430 \u0438\u0445 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u0438)<\/p>\n\n\n\n

5. \u041f\u043e\u0434 modeler <\/em>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 Header File (.h) ModelBuilder1base.h<\/strong><\/p>\n\n\n\n

Location: C:\\CPP\\engine\\modeler\\<\/em><\/p>\n\n\n\n

\u041a\u043e\u0434:<\/p>\n\n\n

\n#pragma once\n#include <string>\n#include <vector>\n#include "Vertex01.h"\n#include "Triangle01.h"\n#include "VirtualShape.h"\n#include "Group01.h"\n#include "Material.h"\n#include "GameSubj.h"\n\nclass ModelBuilder1base\n{\npublic:\n\tstd::vector<Vertex01*> vertices;\n\tstd::vector<Triangle01*> triangles;\n\tstd::vector<VirtualShape*> vShapesStack;\n\tstd::vector<Group01*> groupsStack;\n\tstd::vector<Material*> materialsList;\n\tstd::vector<int> subjNumbersList;\n\tint usingSubjN = -1;\n\tint usingMaterialN = -1;\n\tGroup01* pCurrentGroup = NULL;\npublic:\n\tvirtual ~ModelBuilder1base();\n\tint useSubjN(int subjN) { return useSubjN(this, subjN); };\n\tstatic int useSubjN(ModelBuilder1base* pMB, int subjN);\n\tint useMaterial(Material* pMT) { return useMaterial(this, pMT); };\n\tstatic int useMaterial(ModelBuilder1base* pMB, Material* pMT);\n\tstatic void lockGroup(ModelBuilder1base* pMB);\n\tstatic void releaseGroup(ModelBuilder1base* pMB);\n\tstatic int addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz);\n\tstatic int add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n);\n\tstatic int addTriangle(ModelBuilder1base* pMB, int n0, int n1, int n2);\n\tint buildDrawJobs(std::vector<GameSubj*> gameSubjs) { return buildDrawJobs(this, gameSubjs); };\n\tstatic int buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs);\n\tstatic int rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);\n\tstatic int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);\n\tstatic int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);\n};\n\n<\/pre><\/div>\n\n\n

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


\n\n\n\n

6. \u041f\u043e\u0434 modeler <\/em>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0439 C++ File (.cpp) ModelBuilder1base.cpp<\/strong><\/p>\n\n\n\n

Location: C:\\CPP\\engine\\modeler\\<\/em><\/p>\n\n\n\n

\u041a\u043e\u0434:<\/p>\n\n\n

\n#include "ModelBuilder1base.h"\n#include "platform.h"\n#include "utils.h"\n#include "DrawJob.h"\n#include "Shader.h"\n\nextern float degrees2radians;\n\nModelBuilder1base::~ModelBuilder1base() {\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 ModelBuilder1base::useSubjN(ModelBuilder1base* 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 ModelBuilder1base::useMaterial(ModelBuilder1base* 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 ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {\n\t\/\/indexes: NorthWest, NorthEast, SouthWest,SouthEast\n\tif (n % 2 == 0) { \/\/even number\n\t\taddTriangle(pMB, nNW, nSW, nNE);\n\t\taddTriangle(pMB, nNE, nSW, nSE);\n\t}\n\telse { \/\/odd number\n\t\taddTriangle(pMB, nNW, nSE, nNE);\n\t\taddTriangle(pMB, nNW, nSW, nSE);\n\t}\n\treturn pMB->triangles.size() - 1;\n}\nint ModelBuilder1base::addTriangle(ModelBuilder1base* 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 ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {\n\tif (pMB->pCurrentGroup != NULL)\n\t\tpMB->groupsStack.push_back(pMB->pCurrentGroup);\n\tpMB->pCurrentGroup = new Group01();\n\tpMB->pCurrentGroup->fromVertexN = pMB->vertices.size();\n\tpMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();\n}\nvoid ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {\n\tdelete pMB->pCurrentGroup;\n\tif (pMB->groupsStack.size() > 0) {\n\t\tpMB->pCurrentGroup = pMB->groupsStack.back();\n\t\tpMB->groupsStack.pop_back();\n\t}\n\telse\n\t\tpMB->pCurrentGroup = NULL;\n}\nint ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {\n\tVertex01* pVX = new Vertex01();\n\tpMB->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 ModelBuilder1base::buildDrawJobs(ModelBuilder1base* 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 ModelBuilder1base::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 ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* 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\nint ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {\n\t\/\/moves and rotates vertex group\n\t\/\/rotation angles are set in degrees\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_translate(transformMatrix, kX, kY, kZ);\n\t\/\/rotation order: Z-X-Y\n\tif (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);\n\tif (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);\n\tif (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);\n\n\tint vertsN = pMB->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\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n

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


\n\n\n\n

\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 ModelBuilder<\/strong>:<\/p>\n\n\n\n

a) \u0422\u0435\u043f\u0435\u0440\u044c \u043e\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0442 ModelBuilder1base<\/em>. <\/p>\n\n\n\n

b) \u041d\u043e\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 ModelBuilder<\/em>-\u0430:<\/p>\n\n\n\n

  • buildFace(\u2026)<\/em> – \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0447\u0435\u043c \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u044c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u0438\u043f\u0430 VirtualShape<\/em><\/li>
  • buildBoxFace(\u2026)<\/em> – \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0430 \u0434\u043b\u044f \u043f\u0440\u0438\u0435\u043c\u0430\/\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 “\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439”<\/li>
  • boxFacePlain(\u2026)<\/em> – \u0440\u0438\u0441\u0443\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0435\u043a\u0446\u0438\u044e<\/li>
  • boxFaceTank(\u2026)<\/em> – \u0440\u0438\u0441\u0443\u0435\u0442 \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0435\u043a\u0446\u0438\u044e \u0441 \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u043a\u0440\u0430\u044f\u043c\u0438 \u0438 \u0443\u0433\u043b\u0430\u043c\u0438<\/li>
  • cylinderWrap(\u2026)<\/em> – \u0440\u0438\u0441\u0443\u0435\u0442 \u0441\u0435\u043a\u0442\u043e\u0440 \u0446\u0438\u043b\u0438\u043d\u0434\u0440\u0430 (\u0434\u043b\u044f \u0440\u0435\u0431\u0435\u0440 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439)<\/li>
  • capWrap(\u2026)<\/em> – \u0440\u0438\u0441\u0443\u0435\u0442 \u0441\u0435\u043a\u0442\u043e\u0440 “\u0447\u0430\u0448\u0438” (\u043f\u043e\u043b\u0443-\u0441\u0444\u0435\u0440\u044b) (\u0434\u043b\u044f \u0443\u0433\u043b\u043e\u0432 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0439)<\/li><\/ul>\n\n\n\n

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

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

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

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

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

    \n#pragma once\n#include "ModelBuilder1base.h"\n\nclass ModelBuilder : public ModelBuilder1base\n{\npublic:\n\tvirtual ~ModelBuilder();\n\tstatic int buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);\n\tstatic int cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);\n\tstatic int capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);\n};\n\n<\/pre><\/div>\n\n\n

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


    \n\n\n\n

    8. \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}\n\nint ModelBuilder::buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\tif (strstr(pVS->shapeType, "box") == pVS->shapeType)\n\t\treturn buildBoxFace(pMB, applyTo, pVS);\n\treturn -1;\n}\nint ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\t\/\/this code is for simple box\n\tVirtualShape vs; \/\/face VS, \n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tvs.sectionsR = pVS->sectionsR;\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\tvs.sections[0] = pVS->sections[0];\n\t\tvs.sections[1] = pVS->sections[1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extF;\n\t\tvs.extL = pVS->extL;\n\t\tvs.extR = pVS->extR;\n\t\tvs.extU = pVS->extU;\n\t\tvs.extD = pVS->extD;\n\t\t\/\/define how to move\/place generated face back to the VirtualShape\n\t\t\/\/just shift closer to us by length\/2\n\t\tmat4x4_translate(transformMatrix, 0, 0, pVS->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\tvs.sections[0] = pVS->sections[0];\n\t\tvs.sections[1] = pVS->sections[1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extB;\n\t\tvs.extL = pVS->extR;\n\t\tvs.extR = pVS->extL;\n\t\tvs.extU = pVS->extU;\n\t\tvs.extD = pVS->extD;\n\t\t\/\/rotate 180 degrees around Y and shift farther from us by half-length\n\t\tmat4x4_translate(transformMatrix, 0, 0, -pVS->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\tvs.sections[0] = pVS->sections[2];\n\t\tvs.sections[1] = pVS->sections[1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extL;\n\t\tvs.extL = pVS->extB;\n\t\tvs.extR = pVS->extF;\n\t\tvs.extU = pVS->extU;\n\t\tvs.extD = pVS->extD;\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\tvs.sections[0] = pVS->sections[2];\n\t\tvs.sections[1] = pVS->sections[1];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extR;\n\t\tvs.extL = pVS->extF;\n\t\tvs.extR = pVS->extB;\n\t\tvs.extU = pVS->extU;\n\t\tvs.extD = pVS->extD;\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\tvs.sections[0] = pVS->sections[0];\n\t\tvs.sections[1] = pVS->sections[2];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extU;\n\t\tvs.extL = pVS->extR;\n\t\tvs.extR = pVS->extL;\n\t\tvs.extU = pVS->extF;\n\t\tvs.extD = pVS->extB;\n\t\t\/\/rotate -90 degrees around X (CW) and 180 around Y, and shift half-height up\n\t\tmat4x4_translate(transformMatrix, 0, pVS->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\tvs.sections[0] = pVS->sections[0];\n\t\tvs.sections[1] = pVS->sections[2];\n\t\t\/\/extensions\n\t\tvs.extF = pVS->extD;\n\t\tvs.extL = pVS->extL;\n\t\tvs.extR = pVS->extR;\n\t\tvs.extU = pVS->extF;\n\t\tvs.extD = pVS->extB;\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\tlockGroup(pMB);\n\t\/\/create vertices\n\tif (strstr(pVS->shapeType, "tank") != nullptr)\n\t\tbuildBoxFaceTank(pMB, applyTo, &vs);\n\telse\n\t\tbuildBoxFacePlain(pMB, applyTo, &vs);\n\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\treleaseGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\tif (pVS->whl[0] == 0 || pVS->whl[1] == 0)\n\t\treturn 0;\n\t\/\/create vertices\n\tint sectionsX = pVS->sections[0];\n\tint sectionsY = pVS->sections[1];\n\tint pointsX = sectionsX + 1;\n\tint pointsY = sectionsY + 1;\n\tfloat stepX = pVS->whl[0] \/ sectionsX;\n\tfloat stepY = pVS->whl[1] \/ sectionsY;\n\tfloat kY = pVS->whl[1] \/ 2;\n\tfor (int iy = 0; iy < pointsY; iy++) {\n\t\tfloat kX = -pVS->whl[0] \/ 2;\n\t\tfor (int ix = 0; ix < pointsX; ix++) {\n\t\t\tint nSE = addVertex(pMB, kX, kY, pVS->extF, 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\treturn 1;\n}\nint ModelBuilder::buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {\n\t\/\/for diamond effect - sectionsRad=1, don't merge normals\n\tbool drawMiddle = true;\n\t\/\/edges\n\tbool drawTop = false;\n\tbool drawBottom = false;\n\tbool drawLeft = false;\n\tbool drawRight = false;\n\t\/\/corners\n\tbool drawTopLeft = false;\n\tbool drawTopRight = false;\n\tbool drawBottomLeft = false;\n\tbool drawBottomRight = false;\n\tif (pVS->extF == 0 || applyTo.find(" all") != std::string::npos) {\n\t\tdrawTop = true;\n\t\tdrawBottom = true;\n\t\tdrawLeft = true;\n\t\tdrawRight = true;\n\t\tdrawTopLeft = true;\n\t\tdrawTopRight = true;\n\t\tdrawBottomLeft = true;\n\t\tdrawBottomRight = true;\n\t}\n\telse if (applyTo.find(" h") != std::string::npos) {\n\t\tdrawLeft = true;\n\t\tdrawRight = true;\n\t}\n\telse if (applyTo.find(" v") != std::string::npos) {\n\t\tdrawTop = true;\n\t\tdrawBottom = true;\n\t}\n\tif (applyTo.find(" no") != std::string::npos) {\n\t\tif (applyTo.find(" noM") != std::string::npos) {\n\t\t\t\/\/middle\n\t\t\tif (applyTo.find(" noMrow") != std::string::npos) {\n\t\t\t\tdrawMiddle = false;\n\t\t\t\tdrawLeft = false;\n\t\t\t\tdrawRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noMcol") != std::string::npos) {\n\t\t\t\tdrawMiddle = false;\n\t\t\t\tdrawTop = false;\n\t\t\t\tdrawBottom = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noMid") != std::string::npos)\n\t\t\t\tdrawMiddle = false;\n\t\t}\n\t\tif (applyTo.find(" noN") != std::string::npos) {\n\t\t\t\/\/north\n\t\t\tif (applyTo.find(" noNrow") != std::string::npos) {\n\t\t\t\tdrawTop = false;\n\t\t\t\tdrawTopLeft = false;\n\t\t\t\tdrawTopRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noNedge") != std::string::npos)\n\t\t\t\tdrawTop = false;\n\t\t\tif (applyTo.find(" noNW") != std::string::npos)\n\t\t\t\tdrawTopLeft = false;\n\t\t\tif (applyTo.find(" noNE") != std::string::npos)\n\t\t\t\tdrawTopRight = false;\n\t\t}\n\t\tif (applyTo.find(" noS") != std::string::npos) {\n\t\t\t\/\/south\n\t\t\tif (applyTo.find(" noSrow") != std::string::npos) {\n\t\t\t\tdrawBottom = false;\n\t\t\t\tdrawBottomLeft = false;\n\t\t\t\tdrawBottomRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noSedge") != std::string::npos)\n\t\t\t\tdrawBottom = false;\n\t\t\tif (applyTo.find(" noSW") != std::string::npos)\n\t\t\t\tdrawBottomLeft = false;\n\t\t\tif (applyTo.find(" noSE") != std::string::npos)\n\t\t\t\tdrawBottomRight = false;\n\t\t}\n\t\tif (applyTo.find(" noW") != std::string::npos) {\n\t\t\t\/\/west\n\t\t\tif (applyTo.find(" noWcol") != std::string::npos) {\n\t\t\t\tdrawLeft = false;\n\t\t\t\tdrawTopLeft = false;\n\t\t\t\tdrawBottomLeft = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noWedge") != std::string::npos)\n\t\t\t\tdrawLeft = false;\n\t\t}\n\t\tif (applyTo.find(" noE") != std::string::npos) {\n\t\t\t\/\/east\n\t\t\tif (applyTo.find(" noEcol") != std::string::npos) {\n\t\t\t\tdrawRight = false;\n\t\t\t\tdrawTopRight = false;\n\t\t\t\tdrawBottomRight = false;\n\t\t\t}\n\t\t\tif (applyTo.find(" noEedge") != std::string::npos)\n\t\t\t\tdrawRight = false;\n\t\t}\n\t}\n\tlockGroup(pMB);\n\t\/\/middle\n\tif (pVS->whl[0] > 0 && pVS->whl[1] > 0 && drawMiddle) {\n\t\tbuildBoxFacePlain(pMB, applyTo, pVS);\n\t}\n\tVirtualShape vs;\n\t\/\/edges\n\t\/\/vs.type.assign("cylinder");\n\tvs.sectionsR = pVS->sectionsR;\n\tif (pVS->whl[0] > 0) {\n\t\tvs.sections[2] = pVS->sections[0]; \/\/cylinder Z sections n\n\t\tvs.whl[2] = pVS->whl[0]; \/\/cylinder length Z\n\t\tvs.whl[0] = pVS->extF * 2; \/\/cylinder diameter X\n\t\tif (pVS->extU > 0 && drawTop) {\n\t\t\tvs.whl[1] = pVS->extU * 2; \/\/cylinder diameter Y\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &vs, 0, 90);\n\t\t\t\/\/rotate -90 degrees around Y and shift up\n\t\t\tmoveGroupDg(pMB, 0, -90, 0, 0, pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS->extD > 0 && drawBottom) {\n\t\t\tvs.whl[1] = pVS->extD * 2; \/\/cylinder diameter Y\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &vs, -90, 0);\n\t\t\t\/\/rotate -90 degrees around Y and shift down\n\t\t\tmoveGroupDg(pMB, 0, -90, 0, 0, -pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\tif (pVS->whl[1] > 0) {\n\t\tvs.sections[2] = pVS->sections[1]; \/\/cylinder Z sections n\n\t\tvs.whl[2] = pVS->whl[1]; \/\/cylinder length Z\n\t\tvs.whl[1] = pVS->extF * 2; \/\/cylinder diameter Y\n\t\tif (pVS->extL > 0 && drawLeft) {\n\t\t\tvs.whl[0] = pVS->extL * 2; \/\/cylinder diameter X\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &vs, 90, 180);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 90, 0, 0, -pVS->whl[0] * 0.5f, 0, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS->extR > 0 && drawRight) {\n\t\t\tvs.whl[0] = pVS->extR * 2; \/\/cylinder diameter X\n\t\t\tlockGroup(pMB);\n\t\t\tcylinderWrap(pMB, &vs, 0, 90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 90, 0, 0, pVS->whl[0] * 0.5f, 0, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\t\/\/corners\n\t\/\/vs.type.assign("cap");\n\tvs.sectionsR = pVS->sectionsR;\n\tvs.sections[2] = pVS->sectionsR;\n\tvs.whl[2] = pVS->extF;\n\tif (pVS->extU > 0) {\n\t\t\/\/top corners\n\t\tvs.whl[1] = pVS->extU * 2;\n\t\tif (pVS->extL > 0 && drawTopLeft) {\n\t\t\tvs.whl[0] = pVS->extL * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &vs, 90, 180);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS->extR > 0 && drawTopRight) {\n\t\t\tvs.whl[0] = pVS->extR * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &vs, 0, 90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\n\t\t}\n\t}\n\tif (pVS->extD > 0) {\n\t\t\/\/bottom corners\n\t\tvs.whl[1] = pVS->extD * 2;\n\t\tif (pVS->extL > 0 && drawBottomLeft) {\n\t\t\tvs.whl[0] = pVS->extL * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &vs, -180, -90);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t\tif (pVS->extR > 0 && drawBottomRight) {\n\t\t\tvs.whl[0] = pVS->extR * 2;\n\t\t\tlockGroup(pMB);\n\t\t\tcapWrap(pMB, &vs, -90, 0);\n\t\t\t\/\/rotate 90 degrees around Y and shift left\n\t\t\tmoveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);\n\t\t\treleaseGroup(pMB);\n\t\t}\n\t}\n\tif (pVS->extF == 0) {\n\t\tint vertsN = pMB->vertices.size();\n\t\tfor (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {\n\t\t\tVertex01* pVX = pMB->vertices.at(i);\n\t\t\t\/\/normal\n\t\t\tv3set(pVX->aNormal, 0, 0, 1);\n\t\t}\n\t}\n\treleaseGroup(pMB);\n\treturn 1;\n}\n\nint ModelBuilder::cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {\n\t\/\/ angleFrom\/To - in degrees\n\tlockGroup(pMB);\n\tfloat stepZ = pVS->whl[2] \/ pVS->sections[2];\n\tfloat stepDg = (angleTo - angleFrom) \/ pVS->sectionsR; \/\/in degrees\n\tfor (int nz = 0; nz <= pVS->sections[2]; nz++) {\n\t\tfloat kz = stepZ * nz - pVS->whl[2] * 0.5f;\n\t\tfor (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {\n\t\t\t\/\/ rpn - radial point number\n\t\t\tfloat angleRd = (angleFrom + stepDg * rpn) * degrees2radians;\n\t\t\tfloat kx = cosf(angleRd);\n\t\t\tfloat ky = sinf(angleRd);\n\t\t\tint nSE = addVertex(pMB, kx, ky, kz, kx, ky, 0);\n\t\t\tif (nz > 0 && rpn > 0) {\n\t\t\t\tint nSW = nSE - 1;\n\t\t\t\tint nNW = nSW - pVS->sectionsR - 1;\n\t\t\t\tint nNE = nSE - pVS->sectionsR - 1;\n\t\t\t\tadd2triangles(pMB, nNE, nNW, nSE, nSW, nz + rpn);\n\t\t\t}\n\t\t}\n\t}\n\t\/\/scale to desirable diameters\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 1);\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}\n\treleaseGroup(pMB);\n\treturn 1;\n}\nint ModelBuilder::capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {\n\t\/\/ angleFrom\/To - in degrees\n\tlockGroup(pMB);\n\t\/\/center point\n\tint n0 = addVertex(pMB, 0, 0, 1, 0, 0, 1);\n\tfloat stepZdg = 90.0f \/ pVS->sections[2]; \/\/in degrees\n\tfloat stepRdg = (angleTo - angleFrom) \/ pVS->sectionsR; \/\/in degrees\n\tfor (int nz = 1; nz <= pVS->sections[2]; nz++) {\n\t\tfloat angleZrd = stepZdg * nz * degrees2radians;\n\t\tfloat kz = cosf(angleZrd);\n\t\tfloat R = sinf(angleZrd);\n\t\tfor (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {\n\t\t\t\/\/ rpn - radial point number\n\t\t\tfloat angleRd = (angleFrom + stepRdg * rpn) * degrees2radians;\n\t\t\tfloat kx = cosf(angleRd) * R;\n\t\t\tfloat ky = sinf(angleRd) * R;\n\t\t\tint nSE = addVertex(pMB, kx, ky, kz, kx, ky, kz);\n\t\t\tif (rpn > 0) {\n\t\t\t\tif (nz == 1) {\n\t\t\t\t\tint nSW = nSE - 1;\n\t\t\t\t\taddTriangle(pMB, n0, nSW, nSE);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tint nSW = nSE - 1;\n\t\t\t\t\tint nNW = nSW - pVS->sectionsR - 1;\n\t\t\t\t\tint nNE = nSE - pVS->sectionsR - 1;\n\t\t\t\t\tadd2triangles(pMB, nNW, nNE, nSW, nSE, nz + rpn);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\/\/scale to desirable diameters\n\tmat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };\n\tmat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, pVS->whl[2]);\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}\n\treleaseGroup(pMB);\n\treturn 1;\n}\n\n<\/pre><\/div>\n\n\n

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


    \n\n\n\n

    9. \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.setShapeType("box-tank");\n    vs.whl[0] = 60;\n    vs.whl[1] = 160;\n    vs.whl[2] = 390;\n    vs.setExt(20);\n    vs.extD = 0;\n    vs.extF = 0; \/\/to make front face "flat"\n    vs.sectionsR = 2;\n\n    Material mt;\n    \/\/define material - flat red\n    mt.shaderN = Shader::spN_phong_ucolor;\n    mt.primitiveType = GL_TRIANGLES;\n    mt.uColor.setRGBA(255, 0, 0,255); \/\/red\n    pMB->useMaterial(&mt);\n\n    pMB->buildBoxFace(pMB,"front v", &vs);\n    pMB->buildBoxFace(pMB, "back v", &vs);\n    pMB->buildBoxFace(pMB, "top", &vs);\n    pMB->buildBoxFace(pMB, "bottom", &vs);\n    pMB->buildBoxFace(pMB, "left all", &vs);\n    mt.uColor.setRGBA(0, 0, 255,255); pMB->useMaterial(&mt); \/\/blue\n    pMB->buildBoxFace(pMB, "right all", &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

    \u041e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043f\u0440\u0435\u0436\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438:<\/p>\n\n\n\n

    \u0424\u0443\u043d\u043a\u0446\u0438\u044f TheGame::getReady()<\/em> \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u0442\u0440\u043e\u0438\u0442 \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e “box”, \u0430 “box-tank” (\u043a\u0443\u0431\u0438\u043a \u0441 \u0437\u0430\u043a\u0440\u0443\u0433\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u043a\u0440\u0430\u044f\u043c\u0438 \u0438 \u0443\u0433\u043b\u0430\u043c\u0438), \u0441\u0442\u0440\u043e\u043a\u0438 \u0441 33 \u043f\u043e 55.<\/p>\n\n\n\n

    \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0445 “front”, “right”, \u0438 \u0442.\u0434., \u0443 \u043d\u0430\u0441 \u0442\u0435\u043f\u0435\u0440\u044c “front v”, “right all”, etc. \u042d\u0442\u043e \u0432\u0441\u0435 \u043f\u0440\u043e “extensions”:<\/p>\n\n\n\n

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

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


    \n\n\n\n

    10. \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

    <\/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