Next I want to "draw" the joint (slit) between the pack and the lid, like a small normal map applied to existing textured mesh's fragment, not to entire mesh and not to VirtualShape as we did before. This will require a few new concepts.
Windows
1. Start VS, open C:\CPP\a997modeler\p_windows\p_windows.sln.
The first new concept - marked vertices/triangles groups, so later we can address certain mesh by the name (mark).
2. Open Group01.h and replace code by:
#pragma once
class Group01
{
public:
char marks[124] = "";
int fromVertexN = 0;
int fromTriangleN = 0;
};
3. Open Vertex01.h and replace code by:
#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. Open Triangle01.h and replace code by:
#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 class will hold all info about what we are selecting and what we want to do with this selection.
5. Open GroupTransform.h and replace code by:
#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. Open GroupTransform.cpp and replace code by:
#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;
}
To test it we will add to root01.txt couple commands.
7. Copy following code to a Text Editor and save it (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" />
- In line 6 we are marking right side as "box_right".
- All vertices and triangles for this side will have this mark.
- In line 32 we are saying to move everything marked as "box_right" to the right (x coordinate) by 5 units.
In order to accept new parameters, XMLParser, ModelBuilder1base and ModelLoader are modified a bit.
8. Open XMLParser.cpp and replace code by:
#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;
}
Modified functions:
- setFloatArray(..)
- setIntArray(..)
9. Open ModelBuilder1base.cpp and replace code by:
#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;
}
Modified functions::
- lockGroup(..)
- addVertex(..)
- addTriangle(..)
10. Open ModelLoader.h and replace code by:
#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);
};
New functions:
- addMark(..)
- processTag_do(..)
11. Open ModelLoader.cpp and replace code by:
#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;
}
Also modified:
- fillProps_gt(..)
- processTag_clone(..)
So, we wanted to mark right side and shift it out to the right by 5 units.
12. Build and run. Result:
Shifted. However, gilded letters which were left behind are overlapping shifted right side. That's because we still didn't care about depth buffer. Closer objects should cover more distant ones. For that we need to enable depth buffer testing.
13. Open TheGame.cpp and replace code by:
#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());
}
See highlighted lines. Added:
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LEQUAL);
- glDepthMask(GL_TRUE);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
14. Build and run. Result:
Now - correct, depth buffer works.
So, we are able to mark sub-meshes and to modify them, the goal is achieved.
Now - on
Android
To my big surprise depth buffer on Android didn't work! Turned out that it just wasn't included in GL initialization in main.cpp.
15. Close and re-start VS. Open C:\CPP\a997modeler\p_android\p_android.sln.
16. Open main.cpp and replace code by:
#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();
}
Just 1 missed line... (line 39).
17. Switch on, unlock, plug in, allow.
Build and run. Now - works.
VS top menu -> Debug -> Stop Debugging