Теперь я хочу “нарисовать” стык (щель) между пачкой и крышкой, как normal map приложенную к части существующей поверхности, НЕ ко всей поверхности, и НЕ к VirtualShape как мы делали раньше. Это потребует нескольких новых концепций.
Windows
1. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
Первая из новых концепций – помеченные группы вертексов/треугольников, чтобы потом мы могли обратиться к какой-то из поверхностей по имени (метке).
2. Заменим Group01.h код на:
#pragma once
class Group01
{
public:
char marks[124] = "";
int fromVertexN = 0;
int fromTriangleN = 0;
};
3. Заменим Vertex01.h код на:
#pragma once
class Vertex01
{
public:
char marks[124] = "";
int subjN = -1; //game subject number
int materialN = -1; //material number
int flag = 0;
int endOfSequence = 0; //for sequentional (unindexed) primitives (like GL_LINE_STRIP for example)
int altN = -1; //vertex' position in alternative array
//atributes
float aPos[4] = { 0,0,0,0 }; //position x,y,z + 4-th float for matrix operations
float aNormal[4] = { 0,0,0,0 }; //normal (surface reflection vector) x,y,z + 4-th float for matrix operations
float aTuv[2] = { 0,0 }; //2D texture coordinates
float aTuv2[2] = { 0,0 }; //for normal maps
//tangent space (for normal maps)
float aTangent[3] = { 0,0,0 };
float aBinormal[3] = { 0,0,0 };
};
4. Заменим Triangle01.h код на:
#pragma once
class Triangle01
{
public:
char marks[124] = "";
int subjN = -1; //game subject number
int materialN = -1; //material number
int flag = 0;
int idx[3] = { 0,0,0 }; //3 vertex indices
};
Класс GroupTransform теперь будет содержать информацию что мы выбрали, ну и что собираемся делать с этой выборкой.
5. Заменим GroupTransform.h код на:
#pragma once
#include <stdlib.h>
#include "ModelBuilder1base.h"
class GroupTransform
{
public:
float shift[4] = { 0,0,0,0 };
float spin[3] = { 0,0,0 }; //in degrees
float scale[3] = { 1,1,1 };
std::string allign = "";
std::string headZto = "";
std::string onThe = ""; //left/right/etc.
//limit to
Group01* pGroup = NULL; //NULL-all, can also be pCurrentGroup or pLastClosedGroup
char mark[32] = ""; //must be in <>
float pMin[3]{ -1000000 ,-1000000 ,-1000000 };
float pMax[3]{ 1000000 , 1000000 , 1000000 };
public:
int executeGroupTransform(ModelBuilder1base* pMB) { return executeGroupTransform(pMB, this); };
static int executeGroupTransform(ModelBuilder1base* pMB, GroupTransform* pGT);
//set limits
static void limit2mark(GroupTransform* pGT, std::string mark0);
static int flagSelection(GroupTransform* pGT, std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles);
static int cloneFlagged(ModelBuilder1base* pMB,
std::vector<Vertex01*>* pVxDst, std::vector<Triangle01*>* pTrDst,
std::vector<Vertex01*>* pVxSrc, std::vector<Triangle01*>* pTrSrc);
static int refactorTriangles(std::vector<Triangle01*>* pTrDst, int trianglesN0dst, std::vector<Vertex01*>* pVxSrc);
static int transformFlagged(GroupTransform* pGT, std::vector<Vertex01*>* pVxDst);
static int transformFlaggedMx(std::vector<Vertex01*>* pVx, mat4x4* pTransformMatrix);
static int buildTransformMatrix(GroupTransform* pGT, mat4x4* pTransformMatrix);
static void flagAll(std::vector<Vertex01*>* pVx, std::vector<Triangle01*>* pTr);
static int buildBoundingBoxFlagged(float* bbMin, float* bbMax, std::vector<Vertex01*>* pVx);
};
6. Заменим GroupTransform.cpp код на:
#include "GroupTransform.h"
#include "utils.h"
extern float degrees2radians;
int GroupTransform::executeGroupTransform(ModelBuilder1base* pMB, GroupTransform* pGT) {
//pMB->moveGroupDg(pMB, pGT->spin[0], pGT->spin[1], pGT->spin[2], pGT->shift[0], pGT->shift[1], pGT->shift[2]);
flagSelection(pGT, &pMB->vertices, &pMB->triangles);
transformFlagged(pGT, &pMB->vertices);
return 1;
}
void GroupTransform::limit2mark(GroupTransform* pGT, std::string mark0) {
std::string outStr;
outStr.append("<" + mark0 + ">");
myStrcpy_s(pGT->mark, 32, outStr.c_str());
}
int GroupTransform::flagSelection(GroupTransform* pGT, std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles) {
if (pVertices != NULL) {
bool checkSize = false;
for (int i = 0; i < 3; i++) {
if (pGT->pMin[i] > -1000000) {
checkSize = true;
break;
}
if (pGT->pMax[i] < 1000000) {
checkSize = true;
break;
}
}
int totalN = pVertices->size();
for (int vN = 0; vN < totalN; vN++) {
Vertex01* pV = pVertices->at(vN);
if (pGT->pGroup != NULL) {
if (vN < pGT->pGroup->fromVertexN) {
pV->flag = -1;
continue;
}
}
if (strcmp(pGT->mark, "") != 0) {
if (strstr(pV->marks, pGT->mark) == nullptr) {
pV->flag = -1;
continue;
}
}
if (checkSize) {
bool fits = true;
for (int i = 0; i < 3; i++) {
if (pV->aPos[i] < pGT->pMin[i]) {
fits = false;
break;
}
if (pV->aPos[i] > pGT->pMax[i]) {
fits = false;
break;
}
}
if (!fits) {
pV->flag = -1;
continue;
}
}
pV->flag = 0;
}
}
if (pTriangles != NULL) {
int totalN = pTriangles->size();
for (int tN = 0; tN < totalN; tN++) {
Triangle01* pT = pTriangles->at(tN);
if (pGT->pGroup != NULL) {
if (tN < pGT->pGroup->fromTriangleN) {
pT->flag = -1;
continue;
}
}
if (strcmp(pGT->mark, "") != 0) {
if (strstr(pT->marks, pGT->mark) == nullptr) {
pT->flag = -1;
continue;
}
}
pT->flag = 0;
}
}
return 1;
}
int GroupTransform::cloneFlagged(ModelBuilder1base* pMB,
std::vector<Vertex01*>* pVxDst, std::vector<Triangle01*>* pTrDst,
std::vector<Vertex01*>* pVxSrc, std::vector<Triangle01*>* pTrSrc) {
int vertsNsrc = pVxSrc->size();
int trianglesNsrc = pTrSrc->size();
int vertsN0dst = pVxDst->size();
int trianglesN0dst = pTrDst->size();
for (int i = 0; i < vertsNsrc; i++) {
Vertex01* pV0 = pVxSrc->at(i);
if (pV0->flag < 0)
continue;
pV0->altN = pVxDst->size();
Vertex01* pV = new Vertex01(*pV0);
pVxDst->push_back(pV);
//overwrite marks
if (pMB != NULL) {
myStrcpy_s(pV->marks, 124, pMB->pCurrentGroup->marks);
pV->subjN = pMB->usingSubjN;
}
pV->flag = -1;
}
for (int i = 0; i < trianglesNsrc; i++) {
Triangle01* pT0 = pTrSrc->at(i);
if (pT0->flag < 0)
continue;
Triangle01* pT = new Triangle01(*pT0);
pTrDst->push_back(pT);
//overwrite marks
if (pMB != NULL) {
myStrcpy_s(pT->marks, 124, pMB->pCurrentGroup->marks);
pT->subjN = pMB->usingSubjN;
}
pT->flag = -1;
}
refactorTriangles(pTrDst, trianglesN0dst, pVxSrc);
return 1;
}
int GroupTransform::refactorTriangles(std::vector<Triangle01*>* pTrDst, int trianglesN0dst, std::vector<Vertex01*>* pVxSrc){
//re-factor triangles idx, adjusting triangles verts #s
int trianglesNdst = pTrDst->size();
for (int tN = trianglesN0dst; tN < trianglesNdst; tN++) {
Triangle01* pT = pTrDst->at(tN);
for (int i = 0; i < 3; i++) {
int vN = pT->idx[i];
Vertex01* pV = pVxSrc->at(vN);
pT->idx[i] = pV->altN;
}
}
return 1;
}
int GroupTransform::buildTransformMatrix(GroupTransform* pGT, mat4x4* pTransformMatrix) {
mat4x4_identity(*pTransformMatrix);
if (!pGT->onThe.empty()) {
if (pGT->onThe.compare("back") == 0)
mat4x4_rotate_Y(*pTransformMatrix, *pTransformMatrix, degrees2radians * 180);
else if (pGT->onThe.compare("left") == 0)
mat4x4_rotate_Y(*pTransformMatrix, *pTransformMatrix, -degrees2radians * 90);
else if (pGT->onThe.compare("right") == 0)
mat4x4_rotate_Y(*pTransformMatrix, *pTransformMatrix, degrees2radians * 90);
else if (pGT->onThe.compare("top") == 0) {
mat4x4_rotate_Y(*pTransformMatrix, *pTransformMatrix, degrees2radians * 180);
mat4x4_rotate_X(*pTransformMatrix, *pTransformMatrix, -degrees2radians * 90);
}
else if (pGT->onThe.compare("bottom") == 0)
mat4x4_rotate_X(*pTransformMatrix, *pTransformMatrix, degrees2radians * 90);
}
if(!v3equals(pGT->shift, 0))
mat4x4_translate_in_place(*pTransformMatrix, pGT->shift[0], pGT->shift[1], pGT->shift[2]);
//rotation order: Z-X-Y
if (pGT->spin[1] != 0)
mat4x4_rotate_Y(*pTransformMatrix, *pTransformMatrix, degrees2radians * pGT->spin[1]);
if (pGT->spin[0] != 0)
mat4x4_rotate_X(*pTransformMatrix, *pTransformMatrix, degrees2radians * pGT->spin[0]);
if (pGT->spin[2] != 0)
mat4x4_rotate_Z(*pTransformMatrix, *pTransformMatrix, degrees2radians * pGT->spin[2]);
if (!v3equals(pGT->scale, 1))
mat4x4_scale_aniso(*pTransformMatrix, *pTransformMatrix, pGT->scale[0], pGT->scale[1], pGT->scale[2]);
return 1;
}
int GroupTransform::transformFlagged(GroupTransform* pGT, std::vector<Vertex01*>* pVx) {
//moves and rotates vertex group
//rotation angles are set in degrees
mat4x4 transformMatrix;
buildTransformMatrix(pGT, &transformMatrix);
transformFlaggedMx(pVx, &transformMatrix);
return 1;
}
int GroupTransform::transformFlaggedMx(std::vector<Vertex01*>* pVx, mat4x4* pTransformMatrix) {
//moves and rotates vertex group
int vertsN = pVx->size();
for (int i = 0; i < vertsN; i++) {
Vertex01* pV = pVx->at(i);
if (pV->flag < 0)
continue;
mat4x4_mul_vec4plus(pV->aPos, *pTransformMatrix, pV->aPos, 1);
//normal
mat4x4_mul_vec4plus(pV->aNormal, *pTransformMatrix, pV->aNormal, 0);
vec3_norm(pV->aNormal, pV->aNormal);
}
return 1;
}
void GroupTransform::flagAll(std::vector<Vertex01*>* pVx, std::vector<Triangle01*>* pTr) {
//set flags
for (int i = pVx->size() - 1; i >= 0; i--)
pVx->at(i)->flag = 0;
for (int i = pTr->size() - 1; i >= 0; i--)
pTr->at(i)->flag = 0;
}
int GroupTransform::buildBoundingBoxFlagged(float* bbMin, float* bbMax, std::vector<Vertex01*>* pVx) {
v3copy(bbMin, pVx->at(0)->aPos);
v3copy(bbMax, pVx->at(0)->aPos);
//scan all flagged except #0
for (int vN = pVx->size() - 1; vN > 0; vN--) {
Vertex01* pV = pVx->at(vN);
if (pV->flag < 0)
continue;
for(int i=0;i<3;i++){
if (bbMin[i] > pV->aPos[i])
bbMin[i] = pV->aPos[i];
if (bbMax[i] < pV->aPos[i])
bbMax[i] = pV->aPos[i];
}
}
return 1;
}
Чтобы это протестировать, добавим в root01.txt пару команд.
7. Копируем нижеприведенный код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\a997modeler\dt\models\misc\marlboro01\root01.txt
<texture_as="tx0" src="marlboro03small.png" ckey="#00ff00"/>
<mt_type="phong" uTex0_use="tx0" />
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<a="front v" xywh="2,1,323,495"/>
<a="back v" xywh="2,1,323,495"/>
<a="right all" xywh="327,1,128,495" mark="box_right"/>
<a="left all" xywh="457,1,128,495"/>
<a="top" xywh="588,1,323,133"/>
<a="bottom" xywh="587,136,324,134"/>
//golden prints
<vs="box" whl="55.1,85.1,23.1" />
<texture_as="whitenoise" src="/dt/common/img/whitenoise/wn64_blur3.bmp"/>
<texture_as="gold" src="/dt/common/img/materials/gold02roman.bmp" />
<mt_type="mirror" uAlphaBlending uTex1mask_use="tx0" uTex1alphaChannelN=1 uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
//side golden prints
<a="right" xywh="342,12,101,10" whl="x,1.8,18.1" pxyz="x,39.8, -0.3" /> //Please do not litter
<a="right" xywh="339,144,105,89" whl="x,15.35,18.9" pxyz="x,10.3,-0.12" /> //For special offers...
<a="left" xywh="475,15,95,48" whl="x,8.4,17" pxyz="x,36, 0.3" /> //Underage sale...
//front prints
<group>
//bottom golden print "20 class a..."
<a="front" xywh="20,498,289,13" whl="47.5,2,x" pxyz="1,-36,x" />
//blazon/emblem
<mt_type="mirror" uAlphaBlending uTex2nm_use="tx0" uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
<a="front" xywh2nm="589,415,128,94" whl="20.7,16,x" pxyz="0.3,6.1,x" /> //emblem
//"Marlboro
<mt_type="phong" uAlphaBlending uTex2nm_use="tx0" uColor="#1E211E" />
<a="front" xywh2nm="590,275,301,136" whl="49.2,23.3,x" pxyz="0.21,-18,x" /> //marlboro
</group>
<clone ay=180 />
<do px=5 all markedAs="box_right" />
- В строке 6 мы помечаем правую сторону как “box_right”.
- Все вовлеченные вертексы и треугольники для этой стороны будут иметь эту метку.
- В строке 32 мы инструктируем сдвинуть все помеченное как “box_right” вправо (x coordinate) на 5 единиц (units).
Для приема новых параметров, классы XMLParser, ModelBuilder1base и ModelLoader слегка изменены.
8. Заменим XMLParser.cpp код на:
#include "XMLparser.h"
#include "platform.h"
#include "utils.h"
#include "MyColor.h"
#include <vector>
extern std::string filesRoot;
int XMLparser::removeComments(XMLparser* pXP) {
//find all occurances of "/*"
std::vector<char*> commentsStarts;
// /* comments */
char* scanFrom = pXP->pData;
while (1) {
char* commentStarts = strstr(scanFrom, "/*");
if (commentStarts == NULL)
break;
commentsStarts.push_back(commentStarts);
scanFrom = commentStarts + 2;
}
//here we have a list of /* comments */
while(commentsStarts.size() > 0){
//get last comment
char* commentStarts = commentsStarts.back();
commentsStarts.pop_back();
char* commentEnds = strstr(commentStarts, "*/");
int commentLength = (int)(commentEnds - commentStarts) + 2;
//shift text left
myStrcpy_s(commentStarts, pXP->dataSize - (int)(commentStarts - pXP->pData), commentStarts + commentLength);
pXP->dataSize -= commentLength;
//mylog("======\n%s----------\n", pXP->pData);
}
// //line comments
scanFrom = pXP->pData;
while (1) {
char* commentStarts = strstr(scanFrom, "//");
if (commentStarts == NULL)
break;
char* commentEnds = strstr(commentStarts, "\n");
int commentLength = (int)(commentEnds - commentStarts) + 1;
//shift text left
myStrcpy_s(commentStarts, pXP->dataSize - (int)(commentStarts - pXP->pData), commentStarts + commentLength);
pXP->dataSize -= commentLength;
scanFrom = commentStarts;
//mylog("======\n%s----------\n", pXP->pData);
}
// <!-- comments -->
scanFrom = pXP->pData;
while (1) {
char* commentStarts = strstr(scanFrom, "<!--");
if (commentStarts == NULL)
break;
commentsStarts.push_back(commentStarts);
scanFrom = commentStarts + 4;
}
//here we have a list of <!-- comments -->
while (commentsStarts.size() > 0) {
//get last comment
char* commentStarts = commentsStarts.back();
commentsStarts.pop_back();
char* commentEnds = strstr(commentStarts, "-->");
int commentLength = (int)(commentEnds - commentStarts) + 3;
//shift text left
myStrcpy_s(commentStarts, pXP->dataSize - (int)(commentStarts - pXP->pData), commentStarts + commentLength);
pXP->dataSize -= commentLength;
//mylog("======\n%s----------\n", pXP->pData);
}
return 1;
}
bool XMLparser::nextTag(XMLparser* pXP) {
//returns 0 if no more tags, 1 - tag extracted
char* tagStarts = strstr(pXP->readFrom, "<");
if (tagStarts == NULL)
return false;
pXP->readFrom++;
char* tagEnds = strstr(pXP->readFrom, ">");
if (tagEnds == NULL)
return false;
pXP->readFrom = tagEnds + 1;
pXP->tagLength = (int)(tagEnds - tagStarts) + 1;
pXP->currentTag.assign(tagStarts, pXP->tagLength);
return true;
}
int XMLparser::nameEndsAt(std::string varName, std::string tag) {
int scanFrom = 0;
int nameLength = varName.length();
std::string optsBefore = "< ";
std::string optsAfter = " =/>\n";
while (1) {
int varStartsAt = tag.find(varName, scanFrom);
if (varStartsAt == std::string::npos)
return -1;
scanFrom = varStartsAt + nameLength;
char charBefore = tag.at(varStartsAt - 1);
if (optsBefore.find(charBefore) == std::string::npos)
continue;
char charAfter = tag.at(scanFrom);
if (optsAfter.find(charAfter) == std::string::npos)
continue;
return scanFrom;
}
}
int XMLparser::processSource(XMLparser* pXP) {
while (pXP->nextTag()) {
//extract tagName
int nameStart = pXP->currentTag.find_first_not_of(" <");
int nameEnd = pXP->currentTag.find_first_of(" =/>\n", nameStart+1);
pXP->tagName = pXP->currentTag.substr(nameStart, (nameEnd - nameStart));
//open/closed tag
char lastChar = pXP->currentTag.at(pXP->tagLength - 2);
pXP->closedTag = (lastChar == '/');
//mylog("[%s] [%s] closed=%d nameStart=%d nameEnd=%d\n", pXP->currentTag.c_str(), pXP->tagName.c_str(), pXP->closedTag, nameStart, nameEnd);
pXP->processTag();
}
return 1;
}
std::string XMLparser::buildFullPath(XMLparser* pXP, std::string filePath) {
if (filePath.at(0) != '/')
filePath = pXP->inAppFolder + filePath;
return (filesRoot + filePath);
}
std::string XMLparser::getStringValue(std::string varName, std::string tag) {
//returns std::string
int valueStartsAt = nameEndsAt(varName, tag);
if (valueStartsAt < 0)
return ""; //var not found
valueStartsAt = tag.find_first_not_of(" ", valueStartsAt);
char c = tag.at(valueStartsAt);
if (c != '=')
return ""; //var exists, but value not set
valueStartsAt = tag.find_first_not_of(" ", valueStartsAt + 1);
c = tag.at(valueStartsAt);
std::string optsQuote = "\"'";
int valueEndsAt = 0;
if (optsQuote.find(c) != std::string::npos) {
//the value is in quotes
valueStartsAt++;
valueEndsAt = tag.find(c, valueStartsAt);
}
else { //value not quoted
valueEndsAt = tag.find_first_of(" />\n", valueStartsAt);
}
return tag.substr(valueStartsAt, valueEndsAt - valueStartsAt);
}
bool XMLparser::varExists(std::string varName, std::string tag) {
int valueStartsAt = nameEndsAt(varName, tag);
if (valueStartsAt < 0)
return false; //var not found
return true;
}
int XMLparser::setCharsValue(char* pChars, int charsLength, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string val = getStringValue(varName, tag);
myStrcpy_s(pChars, charsLength, (char*)val.c_str());
return 1;
}
int XMLparser::setIntValue(int* pInt, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string val = getStringValue(varName, tag);
*pInt = stoi(val);
return 1;
}
int XMLparser::setFloatValue(float* pFloat, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string val = getStringValue(varName, tag);
*pFloat = stof(val);
return 1;
}
int XMLparser::setFloatArray(float* pFloats, int arrayLength, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string valuesString = getStringValue(varName, tag);
std::vector<std::string> valuesVector = splitString(valuesString, ",");
int valsN = valuesVector.size();
if (valsN == 1) {
float val = stof(valuesVector.at(0));
for (int i = 0; i < arrayLength; i++)
pFloats[i] = val;
return 1;
}
if (valsN != arrayLength) {
mylog("ERROR in XMLparser::getFloatArray, %s, %s\n", varName.c_str(), tag.c_str());
return -1;
}
for (int i = 0; i < valsN; i++) {
if (valuesVector.at(i).compare("x") != 0)
pFloats[i] = stof(valuesVector.at(i));
}
return 1;
}int XMLparser::setIntArray(int* pInts, int arrayLength, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string valuesString = getStringValue(varName, tag);
std::vector<std::string> valuesVector = splitString(valuesString, ",");
int valsN = valuesVector.size();
if (valsN == 1) {
int val = stoi(valuesVector.at(0));
for (int i = 0; i < arrayLength; i++)
pInts[i] = val;
return 1;
}
if (valsN != arrayLength) {
mylog("ERROR in XMLparser::getIntArray, %s, %s\n", varName.c_str(), tag.c_str());
return -1;
}
for (int i = 0; i < valsN; i++) {
if(valuesVector.at(i).compare("x") != 0)
pInts[i] = stoi(valuesVector.at(i));
}
return 1;
}
int XMLparser::setUintColorValue(unsigned int* pInt, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
MyColor clr;
std::string val = getStringValue(varName, tag);
if (val.at(0) == '#') {
//the value is in HTML HEX format (like #ff0000)
int r = std::stoi(val.substr(1, 2), nullptr, 16);
int g = std::stoi(val.substr(3, 2), nullptr, 16);
int b = std::stoi(val.substr(5, 2), nullptr, 16);
int a = 255;
if (val.size() > 7)
a = std::stoi(val.substr(7, 2), nullptr, 16);
clr.setRGBA(r, g, b, a);
}
else if (val.find(",") > 0) {
//the value is an array of ints (?)
std::vector<std::string> valuesVector = splitString(val, ",");
int r = std::stoi(valuesVector[0]);
int g = std::stoi(valuesVector[1]);
int b = std::stoi(valuesVector[2]);
int a = 255;
if (valuesVector.size() > 3)
a = std::stoi(valuesVector[3]);
clr.setRGBA(r, g, b, a);
}
else {
mylog("ERROR in XMLparser::setUintColorValue: unhandled Color format %s\n",tag.c_str());
return -1;
}
*pInt = clr.getUint32();
return 1;
}
int XMLparser::setIntBoolValue(int* pInt, std::string varName, std::string tag) {
if (!varExists(varName, tag))
return 0; //var not found
std::string val = getStringValue(varName, tag);
if (val.compare("") == 0) *pInt = 1;
else if (val.compare("1") == 0) *pInt = 1;
else if (val.compare("0") == 0) *pInt = 0;
else if (val.compare("yes") == 0) *pInt = 1;
else if (val.compare("no") == 0) *pInt = 0;
else if (val.compare("true") == 0) *pInt = 1;
else if (val.compare("false") == 0) *pInt = 0;
else {
mylog("ERROR in XMLparser::setIntBoolValue, %s=%s in %s\n",varName.c_str(),val.c_str(),tag.c_str());
return -1;
}
return 1;
}
Измененные функции:
- setFloatArray(..)
- setIntArray(..)
9. Заменим ModelBuilder1base.cpp код на:
#include "ModelBuilder1base.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder1base::~ModelBuilder1base() {
releaseGroup(this);
//clear all vectors
int itemsN = vertices.size();
for (int i = 0; i < itemsN; i++)
delete vertices.at(i);
vertices.clear();
itemsN = triangles.size();
for (int i = 0; i < itemsN; i++)
delete triangles.at(i);
triangles.clear();
itemsN = vShapesStack.size();
for (int i = 0; i < itemsN; i++)
delete vShapesStack.at(i);
vShapesStack.clear();
itemsN = groupsStack.size();
for (int i = 0; i < itemsN; i++)
delete groupsStack.at(i);
groupsStack.clear();
if (pCurrentGroup != NULL)
delete pCurrentGroup;
if (pLastClosedGroup != NULL)
delete pLastClosedGroup;
itemsN = materialsList.size();
for (int i = 0; i < itemsN; i++)
delete materialsList.at(i);
materialsList.clear();
subjNumbersList.clear();
}
int ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {
pMB->usingSubjN = subjN;
int itemsN = pMB->subjNumbersList.size();
bool newN = true;
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (pMB->subjNumbersList.at(i) == subjN) {
newN = false;
break;
}
if (newN)
pMB->subjNumbersList.push_back(subjN);
return subjN;
}
int ModelBuilder1base::useMaterial(ModelBuilder1base* pMB, Material* pMT) {
int itemsN = pMB->materialsList.size();
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
pMB->usingMaterialN = i;
return i;
}
//if here - add new material to the list
pMB->usingMaterialN = itemsN;
//create a copy of new Material and add to the list
Material* pMTnew = new Material(*pMT);
pMB->materialsList.push_back(pMTnew);
return itemsN;
}
int ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
//indexes: NorthWest, NorthEast, SouthWest,SouthEast
if (n % 2 == 0) { //even number
addTriangle(pMB, nNW, nSW, nNE);
addTriangle(pMB, nNE, nSW, nSE);
}
else { //odd number
addTriangle(pMB, nNW, nSE, nNE);
addTriangle(pMB, nNW, nSW, nSE);
}
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {
Triangle01* pTR = new Triangle01();
pMB->triangles.push_back(pTR);
pTR->idx[0] = i0;
pTR->idx[1] = i1;
pTR->idx[2] = i2;
pTR->subjN = pMB->usingSubjN;
pTR->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pTR->marks, 124, pMB->pCurrentGroup->marks);
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
Vertex01* pVX = new Vertex01();
pMB->vertices.push_back(pVX);
pVX->aPos[0] = kx;
pVX->aPos[1] = ky;
pVX->aPos[2] = kz;
//normal
pVX->aNormal[0] = nx;
pVX->aNormal[1] = ny;
pVX->aNormal[2] = nz;
pVX->subjN = pMB->usingSubjN;
pVX->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pVX->marks, 124, pMB->pCurrentGroup->marks);
return pMB->vertices.size() - 1;
}
int ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs) {
int totalSubjsN = pMB->subjNumbersList.size();
if (totalSubjsN < 1) {
pMB->subjNumbersList.push_back(-1);
totalSubjsN = 1;
}
int totalMaterialsN = pMB->materialsList.size();
if (totalSubjsN < 2 && totalMaterialsN < 2) {
//simple single DrawJob
Material* pMT = pMB->materialsList.at(0);
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(0);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
buildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return 1;
}
int totalVertsN = pMB->vertices.size();
int totalTrianglesN = pMB->triangles.size();
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
pVX->flag = 0;
}
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
pTR->flag = 0;
}
int addedDJs = 0;
for (int sN = 0; sN < totalSubjsN; sN++) {
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(sN);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
Material* pMT = pMB->materialsList.at(mtN);
std::vector<Vertex01*> useVertices;
std::vector<Triangle01*> useTriangles;
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag != 0)
continue;
if (pVX->subjN != gsN)
continue;
if (pVX->materialN != mtN)
continue;
//if here - make a copy
Vertex01* pVX2 = new Vertex01(*pVX);
useVertices.push_back(pVX2);
pVX2->altN = vN;
pVX->flag = 1;
if (pVX->endOfSequence > 0) {
rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear and proceed to next sequence
int useVerticesN = useVertices.size();
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
}
}
int useVerticesN = useVertices.size();
if (useVerticesN < 1)
continue; //to next material
//pick triangles
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
if (pTR->flag != 0)
continue;
if (pTR->subjN != gsN)
continue;
if (pTR->materialN != mtN)
continue;
//if here - make a copy
Triangle01* pTR2 = new Triangle01(*pTR);
useTriangles.push_back(pTR2);
pTR->flag = 1;
}
rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear all for next material
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
int useTrianglesN = useTriangles.size();
for (int i = 0; i < useTrianglesN; i++)
delete useTriangles.at(i);
useTriangles.clear();
}
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
}
return addedDJs;
}
int ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
if (pMT->uTex2nm >= 0)
calculateTangentSpace(useVertices, useTriangles);
pMT->pickShaderNumber();
DrawJob* pDJ = new DrawJob();
//copy material to DJ
memcpy(&pDJ->mt, pMT, sizeof(Material));
//calculate VBO element size (stride) and variables offsets in VBO
int VBOid = DrawJob::newBufferId();
int stride = 0;
pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
//create an array for VBO
int bufferSize = totalVertsN * stride;
float* vertsBuffer = new float[bufferSize];
//fill vertsBuffer
Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
int floatSize = sizeof(float);
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
int idx = vN * stride / floatSize;
//pick data from vertex and move to the buffer
memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
if (pSh->l_aNormal >= 0) //normal
memcpy(&vertsBuffer[idx + pDJ->aNormal.offset / floatSize], pVX->aNormal, 3 * floatSize);
if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
if (pSh->l_aTuv2 >= 0) //attribute TUV2 (normal maps)
memcpy(&vertsBuffer[idx + pDJ->aTuv2.offset / floatSize], pVX->aTuv2, 2 * floatSize);
if (pSh->l_aTangent >= 0)
memcpy(&vertsBuffer[idx + pDJ->aTangent.offset / floatSize], pVX->aTangent, 3 * floatSize);
if (pSh->l_aBinormal >= 0)
memcpy(&vertsBuffer[idx + pDJ->aBinormal.offset / floatSize], pVX->aBinormal, 3 * floatSize);
}
//buffer is ready, create VBO
glBindBuffer(GL_ARRAY_BUFFER, VBOid);
glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
delete[] vertsBuffer;
pDJ->pointsN = totalVertsN;
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN > 0) {
//create EBO
int totalIndexesN = totalTrianglesN * 3;
//create buffer
GLushort* indexBuffer = new GLushort[totalIndexesN];
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles[tN];
int idx = tN * 3;
indexBuffer[idx] = (GLushort)pTR->idx[0];
indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
}
//buffer is ready, create IBO
pDJ->glEBOid = DrawJob::newBufferId();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
delete[] indexBuffer;
pDJ->pointsN = totalIndexesN;
}
//create and fill vertex attributes array (VAO)
pDJ->buildVAO();
return 1;
}
int ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN < 1)
return 0;
int totalVerticesN = useVertices.size();
//save new vertices order in original vertices array
//since triangles indices refer to original vertices order
for (int i = 0; i < totalVerticesN; i++) {
Vertex01* pVX1 = useVertices.at(i);
Vertex01* pVX0 = allVertices.at(pVX1->altN);
pVX0->altN = i;
}
//replace triangle original indices by new numbers saved in original vertices altN
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles.at(tN);
for (int i = 0; i < 3; i++) {
Vertex01* pVX0 = allVertices.at(pTR->idx[i]);
pTR->idx[i] = pVX0->altN;
}
}
return 1;
}
int ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {
//moves and rotates vertex group
//rotation angles are set in degrees
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_translate(transformMatrix, kX, kY, kZ);
//rotation order: Z-X-Y
if (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);
if (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);
if (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
}
return 1;
}
int ModelBuilder1base::calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
int totalTrianglesN = useTriangles.size();
//assuming that GL_TRIANGLES
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
pV->flag = 0;
}
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
if (pVX->flag != 0)
continue;
Triangle01* pT = NULL;
for (int tN = 0; tN < totalTrianglesN; tN++) {
pT = useTriangles.at(tN);
bool haveTriangle = false;
for (int i = 0; i < 3; i++)
if (pT->idx[i] == vN) {
haveTriangle = true;
break;
}
if (haveTriangle)
break;
}
Vertex01* pV[3];
for (int i = 0; i < 3; i++)
pV[i] = useVertices.at(pT->idx[i]);
float dPos1[3];
float dPos2[3];
float dUV1[2];
float dUV2[2];
for (int i = 0; i < 3; i++) {
dPos1[i] = pV[1]->aPos[i] - pV[0]->aPos[i];
dPos2[i] = pV[2]->aPos[i] - pV[0]->aPos[i];
}
for (int i = 0; i < 2; i++) {
dUV1[i] = pV[1]->aTuv2[i] - pV[0]->aTuv2[i];
dUV2[i] = pV[2]->aTuv2[i] - pV[0]->aTuv2[i];
}
float tangent[3];
float binormal[3];
float divider = dUV1[0] * dUV2[1] - dUV1[1] * dUV2[0];
if (divider == 0) {
v3set(tangent, 1, 0, 0);
v3set(binormal, 0, -1, 0);
}
else {
float r = 1.0f / divider;
for (int i = 0; i < 3; i++) {
tangent[i] = (dPos1[i] * dUV2[1] - dPos2[i] * dUV1[1]) * r;
binormal[i] = -(dPos2[i] * dUV1[0] - dPos1[i] * dUV2[0]) * r;
}
vec3_norm(tangent, tangent);
vec3_norm(binormal, binormal);
}
//add to all 3 vertices
for (int n = 0; n < 3; n++) {
if (pV[n]->flag > 0)
continue;
v3copy(pV[n]->aTangent, tangent);
v3copy(pV[n]->aBinormal, binormal);
pV[n]->flag = 1;
}
}
//normalize tangent and binormal around normal
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
float v3out[3];
//tangent
vec3_mul_cross(v3out, pV->aNormal, pV->aBinormal);
if (v3dotProduct(pV->aTangent, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aTangent, v3out);
//binormal
vec3_mul_cross(v3out, pV->aNormal, pV->aTangent);
if (v3dotProduct(pV->aBinormal, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aBinormal, v3out);
}
return 1;
}
void ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {
Group01* pPrevGroup = pMB->pCurrentGroup;
if (pMB->pCurrentGroup != NULL)
pMB->groupsStack.push_back(pMB->pCurrentGroup);
pMB->pCurrentGroup = new Group01();
pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
//marks
if(pPrevGroup != NULL)
if (strcmp(pPrevGroup->marks, "") != 0)
myStrcpy_s(pMB->pCurrentGroup->marks, 124, pPrevGroup->marks);
}
void ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {
if (pMB->pLastClosedGroup != NULL)
delete pMB->pLastClosedGroup;
pMB->pLastClosedGroup = pMB->pCurrentGroup;
if (pMB->groupsStack.size() > 0) {
pMB->pCurrentGroup = pMB->groupsStack.back();
pMB->groupsStack.pop_back();
}
else
pMB->pCurrentGroup = NULL;
}
Измененные функции:
- lockGroup(..)
- addVertex(..)
- addTriangle(..)
10. Заменим ModelLoader.h код на:
#pragma once
#include "XMLparser.h"
#include "ModelBuilder.h"
#include "GroupTransform.h"
class ModelLoader : public XMLparser
{
public:
ModelBuilder* pModelBuilder = NULL;
bool ownModelBuilder = false;
std::vector<GameSubj*>* pSubjsVector = NULL;
public:
ModelLoader(std::vector<GameSubj*>* pSubjsVector0, int subjN, ModelBuilder* pMB, std::string filePath) : XMLparser(filePath) {
pSubjsVector = pSubjsVector0;
if (pMB != NULL) {
ownModelBuilder = false;
pModelBuilder = pMB;
}
else {
ownModelBuilder = true;
pModelBuilder = new ModelBuilder();
pModelBuilder->lockGroup(pModelBuilder);
}
pModelBuilder->useSubjN(subjN);
};
virtual ~ModelLoader() {
if (!ownModelBuilder)
return;
pModelBuilder->buildDrawJobs(*pSubjsVector);
delete pModelBuilder;
};
static int processTag_a(ModelLoader* pML); //apply
static int setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr);
static int setTexture(ModelLoader* pML, int* pInt, std::string txName);
static int setMaterialTextures(ModelLoader* pML, Material* pMT);
static int fillProps_vs(VirtualShape* pVS, std::string tagStr); //virtual shape
static int fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML); //Material
static int fillProps_gt(GroupTransform* pGS, ModelBuilder* pMB, std::string tagStr);
int processTag() { return processTag(this); };
static int processTag(ModelLoader* pML);
static int loadModel(std::vector<GameSubj*>* pSubjsVector0, std::string sourceFile, std::string subjClass);
static int processTag_clone(ModelLoader* pML);
static int addMark(char* marks, std::string newMark);
static int processTag_do(ModelLoader* pML);
};
Новые функции:
- addMark(..)
- processTag_do(..)
11. Заменим ModelLoader.cpp код на:
#include "ModelLoader.h"
#include "platform.h"
#include "TheGame.h"
#include "DrawJob.h"
#include "Texture.h"
#include "utils.h"
extern TheGame theGame;
int ModelLoader::loadModel(std::vector<GameSubj*>* pSubjsVector0, std::string sourceFile, std::string subjClass) {
//returns element's (Subj) number or -1
int subjN = pSubjsVector0->size();
GameSubj* pGS = theGame.newGameSubj(subjClass);
pSubjsVector0->push_back(pGS);
//pGS->djStartN = DrawJob::drawJobs.size();
ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
processSource(pML);
delete pML;
//pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return subjN;
}
int ModelLoader::setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr) {
if (!varExists(varName, tagStr))
return 0;
std::string str0 = getStringValue(varName, tagStr);
if (intHashMap.find(str0) == intHashMap.end()) {
mylog("ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\n", varName.c_str(), tagStr.c_str());
return -1;
}
*pInt = intHashMap[getStringValue(varName, tagStr)];
return 1;
}
int ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {
ModelBuilder* pMB = pML->pModelBuilder;
std::string varName = txName + "_use";
if (setValueFromIntHashMap(pInt, pMB->texturesHashMap, varName, pML->currentTag) == 0) {
//the texture is not in hash table
varName = txName + "_src";
if (varExists(varName, pML->currentTag)) {
std::string txFile = getStringValue(varName, pML->currentTag);
varName = txName + "_ckey";
unsigned int intCkey = 0;
setUintColorValue(&intCkey, varName, pML->currentTag);
*pInt = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
}
}
return 1;
}
int ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {
setTexture(pML, &pMT->uTex0, "uTex0");
setTexture(pML, &pMT->uTex1mask, "uTex1mask");
setTexture(pML, &pMT->uTex2nm, "uTex2nm");
setTexture(pML, &pMT->uTex3, "uTex3");
return 1;
}
int ModelLoader::fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML) {
setCharsValue(pMT->shaderType, 20, "mt_type", tagStr);
setMaterialTextures(pML, pMT);
//color
if (varExists("uColor", tagStr)) {
unsigned int uintColor = 0;
setUintColorValue(&uintColor, "uColor", tagStr);
pMT->uColor.setUint32(uintColor);
}
//mylog("mt.uTex0=%d, mt.uTex1mask=%d\n", mt.uTex0, mt.uTex1mask);
if (varExists("primitiveType", tagStr)) {
std::string str0 = getStringValue("primitiveType", tagStr);
if (str0.compare("GL_POINTS") == 0) pMT->primitiveType = GL_POINTS;
else if (str0.compare("GL_LINES") == 0) pMT->primitiveType = GL_LINES;
else if (str0.compare("GL_LINE_STRIP") == 0) pMT->primitiveType = GL_LINE_STRIP;
else if (str0.compare("GL_LINE_LOOP") == 0) pMT->primitiveType = GL_LINE_LOOP;
else if (str0.compare("GL_TRIANGLE_STRIP") == 0) pMT->primitiveType = GL_TRIANGLE_STRIP;
else if (str0.compare("GL_TRIANGLE_FAN") == 0) pMT->primitiveType = GL_TRIANGLE_FAN;
else pMT->primitiveType = GL_TRIANGLES;
}
setIntValue(&pMT->uTex1alphaChannelN, "uTex1alphaChannelN", tagStr);
setIntValue(&pMT->uTex0translateChannelN, "uTex0translateChannelN", tagStr);
setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
setFloatValue(&pMT->uAmbient, "uAmbient", tagStr);
setFloatValue(&pMT->uSpecularIntencity, "uSpecularIntencity", tagStr);
setFloatValue(&pMT->uSpecularMinDot, "uSpecularMinDot", tagStr);
setFloatValue(&pMT->uSpecularPowerOf, "uSpecularPowerOf", tagStr);
pML->pModelBuilder->useMaterial(pMT);
return 1;
}
int ModelLoader::processTag(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("texture_as") == 0) {
//saves texture N in texturesMap under given name
std::string keyName = getStringValue("texture_as", pML->currentTag);
if (pMB->texturesHashMap.find(keyName) != pMB->texturesHashMap.end())
return pMB->texturesHashMap[keyName];
else { //add new
std::string txFile = getStringValue("src", pML->currentTag);
unsigned int intCkey = 0;
setUintColorValue(&intCkey, "ckey", pML->currentTag);
int txN = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
pMB->texturesHashMap[keyName] = txN;
//mylog("%s=%d\n", keyName.c_str(), pMB->texturesMap[keyName]);
return txN;
}
}
if (pML->tagName.find("mt_") == 0) {
//sets current material
ModelBuilder* pMB = pML->pModelBuilder;
if (!pML->closedTag) {
//save previous material in stack
if (pMB->usingMaterialN >= 0)
pMB->materialsStack.push_back(pMB->usingMaterialN);
}
Material mt;
return fillProps_mt(&mt, pML->currentTag, pML);
}
if (pML->tagName.find("/mt_") == 0) {
//restore previous material
if (pMB->materialsStack.size() > 0) {
pMB->usingMaterialN = pMB->materialsStack.back();
pMB->materialsStack.pop_back();
}
return 1;
}
if (pML->tagName.compare("vs") == 0) {
//sets virtual shape
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->closedTag) {
if (pMB->pCurrentVShape != NULL)
delete pMB->pCurrentVShape;
}
else { //open tag
//save previous vshape in stack
if (pMB->pCurrentVShape != NULL)
pMB->vShapesStack.push_back(pMB->pCurrentVShape);
}
pMB->pCurrentVShape = new VirtualShape();
fillProps_vs(pMB->pCurrentVShape, pML->currentTag);
return 1;
}
if (pML->tagName.compare("/vs") == 0) {
//restore previous virtual shape
if (pMB->vShapesStack.size() > 0) {
if (pMB->pCurrentVShape != NULL)
delete(pMB->pCurrentVShape);
pMB->pCurrentVShape = pMB->vShapesStack.back();
pMB->vShapesStack.pop_back();
}
return 1;
}
if (pML->tagName.compare("group") == 0) {
std::string notAllowed[] = { "pxyz","axyz","align","headTo" };
int notAllowedLn = sizeof(notAllowed) / sizeof(notAllowed[0]);
for (int i = 0; i < notAllowedLn; i++)
if (varExists(notAllowed[i], pML->currentTag)) {
mylog("ERROR in ModelLoader::processTag: use %s in </group>: %s\n", notAllowed[i].c_str(), pML->currentTag.c_str());
return -1;
}
pMB->lockGroup(pMB);
//mark
if (varExists("mark", pML->currentTag))
addMark(pMB->pCurrentGroup->marks, getStringValue("mark", pML->currentTag));
return 1;
}
if (pML->tagName.compare("/group") == 0) {
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.executeGroupTransform(pMB);
pMB->releaseGroup(pMB);
return 1;
}
if (pML->tagName.compare("a") == 0)
return processTag_a(pML); //apply
if (pML->tagName.compare("clone") == 0)
return processTag_clone(pML);
if (pML->tagName.compare("/clone") == 0)
return processTag_clone(pML);
if (pML->tagName.compare("do") == 0)
return processTag_do(pML);
//mylog("%s, %s /group?=%d\n",pML->currentTag.c_str(), pML->tagName.c_str(), (pML->tagName.compare("/group") == 0));
mylog("ERROR in ModelLoader::processTag, unhandled tag %s, file %s\n", pML->currentTag.c_str(), pML->fullPath.c_str());
return -1;
}
int ModelLoader::fillProps_vs(VirtualShape* pVS, std::string tagStr) {
//sets virtual shape
setCharsValue(pVS->shapeType, 20, "vs", tagStr);
setFloatArray(pVS->whl, 3, "whl", tagStr);
//extensions
float ext;
if (varExists("ext", tagStr)) {
setFloatValue(&ext, "ext", tagStr);
pVS->setExt(ext);
}
if (varExists("extX", tagStr)) {
setFloatValue(&ext, "extX", tagStr);
pVS->setExtX(ext);
}
if (varExists("extY", tagStr)) {
setFloatValue(&ext, "extY", tagStr);
pVS->setExtY(ext);
}
if (varExists("extZ", tagStr)) {
setFloatValue(&ext, "extZ", tagStr);
pVS->setExtZ(ext);
}
setFloatValue(&pVS->extU, "extU", tagStr);
setFloatValue(&pVS->extD, "extD", tagStr);
setFloatValue(&pVS->extL, "extL", tagStr);
setFloatValue(&pVS->extR, "extR", tagStr);
setFloatValue(&pVS->extF, "extF", tagStr);
setFloatValue(&pVS->extB, "extB", tagStr);
//sections
setIntValue(&pVS->sectionsR, "sectR", tagStr);
setIntValue(&pVS->sections[0], "sectX", tagStr);
setIntValue(&pVS->sections[1], "sectY", tagStr);
setIntValue(&pVS->sections[2], "sectZ", tagStr);
//mylog("pVS->shapeType=%s whl=%fx%fx%f\n", pVS->shapeType, pVS->whl[0], pVS->whl[1], pVS->whl[2]);
return 1;
}
int ModelLoader::processTag_a(ModelLoader* pML) {
//apply
ModelBuilder* pMB = pML->pModelBuilder;
std::string tagStr = pML->currentTag;
pMB->lockGroup(pMB);
//mark
if (varExists("mark", tagStr))
addMark(pMB->pCurrentGroup->marks, getStringValue("mark", tagStr));
std::vector<std::string> applyTosVector = splitString(pML->getStringValue("a", tagStr), ",");
Material* pMT = pMB->materialsList.at(pMB->usingMaterialN);
int texN = pMT->uTex1mask;
if (texN < 0)
texN = pMT->uTex0;
float xywh[4] = { 0,0,1,1 };
setFloatArray(xywh, 4, "xywh", tagStr);
std::string flipStr = getStringValue("flip", tagStr);
TexCoords tc;
tc.set(texN, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
setFloatArray(xywh, 4, "xywh2nm", tagStr);
flipStr = getStringValue("flip2nm", tagStr);
TexCoords tc2nm;
tc2nm.set(pMT->uTex2nm, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
//adjusted VirtualShape
VirtualShape* pVS_a = new VirtualShape(*pMB->pCurrentVShape);
fillProps_vs(pVS_a, tagStr);
for (int aN = 0; aN < (int)applyTosVector.size(); aN++) {
//pMB->buildFace(pMB, applyTosVector.at(aN), pMB->pCurrentVShape, &tc, &tc2nm);
pMB->buildFace(pMB, applyTosVector.at(aN), pVS_a, &tc, &tc2nm);
}
delete pVS_a;
//mylog("vertsN=%d\n",pMB->vertices.size());
GroupTransform GT_a;
fillProps_gt(>_a, pMB, tagStr);
GT_a.executeGroupTransform(pMB);
pMB->releaseGroup(pMB);
return 1;
}
int ModelLoader::processTag_clone(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("clone") == 0) {
//mark what to clone
GroupTransform gt;
gt.pGroup = pMB->pLastClosedGroup;
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
//cloning
pMB->lockGroup(pMB);
gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &pMB->vertices, &pMB->triangles);
}
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.executeGroupTransform(pMB);
if (pML->tagName.compare("/clone") == 0 || pML->closedTag) {
pMB->releaseGroup(pMB);
}
return 1;
}
int ModelLoader::addMark(char* marks, std::string newMark) {
if (newMark.empty())
return 0;
std::string allMarks;
allMarks.assign(marks);
allMarks.append("<"+ newMark+">");
myStrcpy_s(marks,124, allMarks.c_str());
return 1;
}
int ModelLoader::fillProps_gt(GroupTransform* pGT, ModelBuilder* pMB, std::string tagStr) {
pGT->pGroup = pMB->pCurrentGroup;
//position
setFloatArray(pGT->shift, 3, "pxyz", tagStr);
setFloatValue(&pGT->shift[0], "px", tagStr);
setFloatValue(&pGT->shift[1], "py", tagStr);
setFloatValue(&pGT->shift[2], "pz", tagStr);
//angles
setFloatArray(pGT->spin, 3, "axyz", tagStr);
setFloatValue(&pGT->spin[0], "ax", tagStr);
setFloatValue(&pGT->spin[1], "ay", tagStr);
setFloatValue(&pGT->spin[2], "az", tagStr);
//scale
setFloatArray(pGT->scale, 3, "scale", tagStr);
pGT->onThe = getStringValue("onThe", tagStr);
pGT->allign = getStringValue("allign", tagStr);
pGT->headZto = getStringValue("headZto", tagStr);
//limit to
if(varExists("all",tagStr))
pGT->pGroup = NULL;
if (varExists("lastClosedGroup", tagStr))
pGT->pGroup = pMB->pLastClosedGroup;
if (varExists("markedAs", tagStr))
pGT->limit2mark(pGT, getStringValue("markedAs", tagStr));
setFloatArray(pGT->pMin, 3, "xyzMin", tagStr);
setFloatArray(pGT->pMax, 3, "xyzMax", tagStr);
return 1;
}
int ModelLoader::processTag_do(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
gt.transformFlagged(>, &pMB->vertices);
return 1;
}
Также изменены:
- fillProps_gt(..)
- processTag_clone(..)
Итак, мы собирались пометить правую сторону, и потом сдвинуть ее вправо на 5 units.
12. Компиляция и запуск. Результат:
Сдвинули. Однако, золоченые буквы, которые мы оставили позади, перекрывают сдвинутую правую сторону. Так получилось потому что мы до сих пор не озаботились depth buffer-ом (z-buffer). Более близкие объекты все-таки должны закрывать дальние. А для этого надо “включить проверку буфера глубины” (enable depth buffer testing).
13. Заменим TheGame.cpp код на:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
#include "ModelLoader.h"
extern std::string filesRoot;
extern float degrees2radians;
std::vector<GameSubj*> TheGame::gameSubjs;
int TheGame::getReady() {
bExitGame = false;
Shader::loadShaders();
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
int subjN = ModelLoader::loadModel(&gameSubjs, "/dt/models/misc/marlboro01/root01.txt", "");
GameSubj* pGS = gameSubjs.at(subjN);
pGS->name.assign("box1");
pGS->ownSpeed.setDegrees(0, 2, 0);
//pGS->ownCoords.setDegrees(0, 90, 0);
//===== set up camera
mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
mainCamera.viewRangeDg = 30;
mainCamera.stageSize[0] = 80;
mainCamera.stageSize[1] = 120;
memcpy(mainCamera.lookAtPoint, pGS->ownCoords.pos, sizeof(float) * 3);
mainCamera.onScreenResize();
//===== set up light
v3set(dirToMainLight, -1, 1, 1);
vec3_norm(dirToMainLight, dirToMainLight);
return 1;
}
int TheGame::drawFrame() {
myPollEvents();
//glClearColor(0.0, 0.0, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//calculate halfVector
float dirToCamera[4] = { 0,0,-1,0 }; //-z
mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);
float uHalfVector[4] = { 0,0,0,0 };
for (int i = 0; i < 3; i++)
uHalfVector[i] = (dirToCamera[i] + dirToMainLight[i]) / 2;
vec3_norm(uHalfVector, uHalfVector);
mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;
//mat4x4_ortho(mProjection, -(float)screenSize[0] / 2, (float)screenSize[0] / 2, -(float)screenSize[1] / 2, (float)screenSize[1] / 2, 100.f, 500.f);
float nearClip = mainCamera.focusDistance - 50;
float farClip = mainCamera.focusDistance + 50;
if (nearClip < 0) nearClip = 0;
mat4x4_perspective(mProjection, mainCamera.viewRangeDg * degrees2radians, screenAspectRatio, nearClip, farClip);
mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);
mViewProjection[1][3] = 0; //keystone effect
//scan subjects
int subjsN = gameSubjs.size();
for (int subjN = 0; subjN < subjsN; subjN++) {
GameSubj* pGS = gameSubjs.at(subjN);
//behavior - apply rotation speed
pGS->moveSubj();
//prepare subject for rendering
pGS->buildModelMatrix(pGS);
//build MVP matrix for given subject
mat4x4_mul(mMVP, mViewProjection, pGS->ownModelMatrix);
//build Model-View (rotation) matrix for normals
mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());
//convert to 3x3 matrix
float mMV3x3[3][3];
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
mMV3x3[y][x] = mMV4x4[y][x];
//render subject
for (int i = 0; i < pGS->djTotalN; i++) {
DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
pDJ->execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);
}
}
//synchronization
while (1) {
long long int currentMillis = getSystemMillis();
long long int millisSinceLastFrame = currentMillis - lastFrameMillis;
if (millisSinceLastFrame >= millisPerFrame) {
lastFrameMillis = currentMillis;
break;
}
}
mySwapBuffers();
return 1;
}
int TheGame::cleanUp() {
int itemsN = gameSubjs.size();
//delete all UISubjs
for (int i = 0; i < itemsN; i++) {
GameSubj* pGS = gameSubjs.at(i);
delete pGS;
}
gameSubjs.clear();
//clear all other classes
Texture::cleanUp();
Shader::cleanUp();
DrawJob::cleanUp();
return 1;
}
int TheGame::onScreenResize(int width, int height) {
if (screenSize[0] == width && screenSize[1] == height)
return 0;
screenSize[0] = width;
screenSize[1] = height;
screenAspectRatio = (float)width / height;
glViewport(0, 0, width, height);
mainCamera.onScreenResize();
mylog(" screen size %d x %d\n", width, height);
return 1;
}
int TheGame::run() {
getReady();
while (!bExitGame) {
drawFrame();
}
cleanUp();
return 1;
}
GameSubj* TheGame::newGameSubj(std::string subjClass) {
return (new GameSubj());
}
Обратите внимание на отмеченные строки. Добавлено:
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LEQUAL);
- glDepthMask(GL_TRUE);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
14. Компиляция и запуск. Результат:
Теперь правильно, depth buffer работает.
Итак, мы можем пометить sub-mesh и модифицировать его, заявленная цель достигнута.
Теперь
Android
К моему большому удивлению depth buffer на Андроиде не сработал! Оказалось, что он просто нЕ был включен в инициализацию GL-а в main.cpp.
15. Пере-запускаем VS. Открываем C:\CPP\a997modeler\p_android\p_android.sln.
16. Заменим main.cpp код на:
#include "platform.h"
#include "TheGame.h"
#include <string>
#include <vector>
#include <sys/stat.h> //mkdir for Android
std::string filesRoot;
TheGame theGame;
struct android_app* androidApp;
ASensorManager* sensorManager;
const ASensor* accelerometerSensor;
ASensorEventQueue* sensorEventQueue;
EGLDisplay androidDisplay;
EGLSurface androidSurface;
EGLContext androidContext;
/**
* Initialize an EGL context for the current display.
*/
static int engine_init_display(struct engine* engine) {
// initialize OpenGL ES and EGL
/*
* Here specify the attributes of the desired configuration.
* Below, we select an EGLConfig with at least 8 bits per color
* component compatible with on-screen windows
*/
const EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_NONE
};
EGLint format;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
/* Here, the application chooses the configuration it desires. In this
* sample, we have a very simplified selection process, where we pick
* the first EGLConfig that matches our criteria */
eglChooseConfig(display, attribs, &config, 1, &numConfigs);
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
* guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
* As soon as we picked a EGLConfig, we can safely reconfigure the
* ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, format);
surface = eglCreateWindowSurface(display, config, androidApp->window, NULL);
EGLint contextAttribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
context = eglCreateContext(display, config, NULL, contextAttribs);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
mylog("ERROR: Unable to eglMakeCurrent");
return -1;
}
androidDisplay = display;
androidContext = context;
androidSurface = surface;
return 0;
}
/**
* Tear down the EGL context currently associated with the display.
*/
static void engine_term_display() {
if (androidDisplay != EGL_NO_DISPLAY) {
eglMakeCurrent(androidDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (androidContext != EGL_NO_CONTEXT) {
eglDestroyContext(androidDisplay, androidContext);
}
if (androidSurface != EGL_NO_SURFACE) {
eglDestroySurface(androidDisplay, androidSurface);
}
eglTerminate(androidDisplay);
}
androidDisplay = EGL_NO_DISPLAY;
androidContext = EGL_NO_CONTEXT;
androidSurface = EGL_NO_SURFACE;
}
/**
* Process the next input event.
*/
static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
//engine->state.x = AMotionEvent_getX(event, 0);
//engine->state.y = AMotionEvent_getY(event, 0);
return 1;
}
return 0;
}
/**
* Process the next main command.
*/
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
struct engine* engine = (struct engine*)app->userData;
switch (cmd) {
case APP_CMD_INIT_WINDOW:
// The window is being shown, get it ready.
if (androidApp->window != NULL) {
engine_init_display(engine);
//engine_draw_frame(engine);
}
break;
case APP_CMD_TERM_WINDOW:
// The window is being hidden or closed, clean it up.
engine_term_display();
break;
case APP_CMD_GAINED_FOCUS:
// When our app gains focus, we start monitoring the accelerometer.
if (accelerometerSensor != NULL) {
ASensorEventQueue_enableSensor(sensorEventQueue,
accelerometerSensor);
// We'd like to get 60 events per second (in microseconds).
ASensorEventQueue_setEventRate(sensorEventQueue,
accelerometerSensor, (1000L / 60) * 1000);
}
break;
case APP_CMD_LOST_FOCUS:
// When our app loses focus, we stop monitoring the accelerometer.
// This is to avoid consuming battery while not being used.
if (accelerometerSensor != NULL) {
ASensorEventQueue_disableSensor(sensorEventQueue,
accelerometerSensor);
}
// Also stop animating.
//engine_draw_frame(engine);
break;
}
}
static std::vector<std::string> list_assets(android_app* app, const char* asset_path)
{ //by Marcel Smit, stolen from https://github.com/android/ndk-samples/issues/603
std::vector<std::string> result;
JNIEnv* env = nullptr;
app->activity->vm->AttachCurrentThread(&env, nullptr);
auto context_object = app->activity->clazz;
auto getAssets_method = env->GetMethodID(env->GetObjectClass(context_object), "getAssets", "()Landroid/content/res/AssetManager;");
auto assetManager_object = env->CallObjectMethod(context_object, getAssets_method);
auto list_method = env->GetMethodID(env->GetObjectClass(assetManager_object), "list", "(Ljava/lang/String;)[Ljava/lang/String;");
jstring path_object = env->NewStringUTF(asset_path);
auto files_object = (jobjectArray)env->CallObjectMethod(assetManager_object, list_method, path_object);
env->DeleteLocalRef(path_object);
auto length = env->GetArrayLength(files_object);
for (int i = 0; i < length; i++)
{
jstring jstr = (jstring)env->GetObjectArrayElement(files_object, i);
const char* filename = env->GetStringUTFChars(jstr, nullptr);
if (filename != nullptr)
{
result.push_back(filename);
env->ReleaseStringUTFChars(jstr, filename);
}
env->DeleteLocalRef(jstr);
}
app->activity->vm->DetachCurrentThread();
return result;
}
int updateAssets() {
//get APK apkLastUpdateTime timestamp
JNIEnv* env = nullptr;
androidApp->activity->vm->AttachCurrentThread(&env, nullptr);
jobject context_object = androidApp->activity->clazz;
jmethodID getPackageNameMid_method = env->GetMethodID(env->GetObjectClass(context_object), "getPackageName", "()Ljava/lang/String;");
jstring packageName = (jstring)env->CallObjectMethod(context_object, getPackageNameMid_method);
jmethodID getPackageManager_method = env->GetMethodID(env->GetObjectClass(context_object), "getPackageManager", "()Landroid/content/pm/PackageManager;");
jobject packageManager_object = env->CallObjectMethod(context_object, getPackageManager_method);
jmethodID getPackageInfo_method = env->GetMethodID(env->GetObjectClass(packageManager_object), "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
jobject packageInfo_object = env->CallObjectMethod(packageManager_object, getPackageInfo_method, packageName, 0x0);
jfieldID updateTimeFid = env->GetFieldID(env->GetObjectClass(packageInfo_object), "lastUpdateTime", "J");
long int apkLastUpdateTime = env->GetLongField(packageInfo_object, updateTimeFid);
// APK updateTime timestamp retrieved
// compare with saved timestamp
std::string updateTimeFilePath = filesRoot + "/dt/apk_update_time.bin";
FILE* inFile = fopen(updateTimeFilePath.c_str(), "r");
if (inFile != NULL)
{
long int savedUpdateTime;
fread(&savedUpdateTime, 1, sizeof(savedUpdateTime), inFile);
fclose(inFile);
if (savedUpdateTime == apkLastUpdateTime) {
mylog("Assets are up to date.\n");
return 0;
}
}
// if here - need to update assets
AAssetManager* am = androidApp->activity->assetManager;
int buffSize = 1000000; //guess, should be enough?
char* buff = new char[buffSize];
std::vector<std::string> dirsToCheck; //list of assets folders to check
dirsToCheck.push_back("dt"); //root folder
while (dirsToCheck.size() > 0) {
//open last element from directories vector
std::string dirPath = dirsToCheck.back();
dirsToCheck.pop_back(); //delete last element
//mylog("Scanning directory <%s>\n", dirPath.c_str());
//make sure folder exists on local drive
std::string outPath = filesRoot + "/" + dirPath; // .c_str();
struct stat info;
int statRC = stat(outPath.c_str(), &info);
if (statRC == 0)
mylog("%s folder exists.\n", outPath.c_str());
else {
// mylog("Try to create %s\n", outPath.c_str());
int status = mkdir(outPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status == 0)
mylog("%s folder added.\n", outPath.c_str());
else {
mylog("ERROR creating, status=%d, errno: %s.\n", status, std::strerror(errno));
}
}
//get folder's content
std::vector<std::string> dirItems = list_assets(androidApp, dirPath.c_str());
int itemsN = dirItems.size();
//scan directory items
for (int i = 0; i < itemsN; i++) {
std::string itemPath = dirPath + "/" + dirItems.at(i).c_str();
//mylog("New item: <%s> - ", itemPath.c_str());
//try to open it to see if it's a file
AAsset* asset = AAssetManager_open(am, itemPath.c_str(), AASSET_MODE_UNKNOWN);
if (asset != NULL) {
long size = AAsset_getLength(asset);
//mylog("It's a file, size = %d - ", size);
if (size > buffSize) {
mylog("ERROR in main.cpp->updateAssets(): File %s is too big, skipped.\n", itemPath.c_str());
}
else {
AAsset_read(asset, buff, size);
outPath = filesRoot + "/" + itemPath;
FILE* outFile = fopen(outPath.c_str(), "w+");
if (outFile != NULL)
{
fwrite(buff, 1, size, outFile);
fflush(outFile);
fclose(outFile);
mylog("%s saved\n", outPath.c_str());
}
else
mylog("ERROR in main.cpp->updateAssets(): Can't create file %s\n", itemPath.c_str());
}
AAsset_close(asset);
}
else {
dirsToCheck.push_back(itemPath);
//mylog("It's a folder, add to folders list to check.\n");
}
}
dirItems.clear();
}
delete[] buff;
// save updateTime
FILE* outFile = fopen(updateTimeFilePath.c_str(), "w+");
if (outFile != NULL)
{
fwrite(&apkLastUpdateTime, 1, sizeof(apkLastUpdateTime), outFile);
fflush(outFile);
fclose(outFile);
}
else
mylog("ERROR creating %s\n", updateTimeFilePath.c_str());
return 1;
}
/**
* This is the main entry point of a native application that is using
* android_native_app_glue. It runs in its own thread, with its own
* event loop for receiving input events and doing other things.
*/
void android_main(struct android_app* state) {
//state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
androidApp = state;
// Prepare to monitor accelerometer
sensorManager = ASensorManager_getInstance();
accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager,
ASENSOR_TYPE_ACCELEROMETER);
sensorEventQueue = ASensorManager_createEventQueue(sensorManager,
state->looper, LOOPER_ID_USER, NULL, NULL);
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
//wait for display
while (androidDisplay == NULL) {
// No display yet.
//std::this_thread::sleep_for(std::chrono::seconds(1));
//mylog("No display yet\n");
//wait for event
while ((ident = ALooper_pollAll(0, NULL, &events,
(void**)&source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
}
}
EGLint w, h;
eglQuerySurface(androidDisplay, androidSurface, EGL_WIDTH, &w);
eglQuerySurface(androidDisplay, androidSurface, EGL_HEIGHT, &h);
theGame.onScreenResize(w, h);
//retrieving files root
filesRoot.assign(androidApp->activity->internalDataPath);
mylog("filesRoot = %s\n", filesRoot.c_str());
updateAssets();
theGame.run();
engine_term_display();
}
Всего лишь 1 пропущенная строка… (строка 39).
17. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск. Теперь – работает.
VS top menu -> Debug -> Stop Debugging