Глава 32. “Помеченные” группы вертексов

Теперь я хочу “нарисовать” стык (щель) между пачкой и крышкой, как 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(&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(&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(&gt, &pMB->vertices, &pMB->triangles);

		//cloning
		pMB->lockGroup(pMB);
		gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &pMB->vertices, &pMB->triangles);
	}
	GroupTransform gt;
	fillProps_gt(&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(&gt, pMB, pML->currentTag);
	gt.flagSelection(&gt, &pMB->vertices, &pMB->triangles);
	gt.transformFlagged(&gt, &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


Leave a Reply

Your email address will not be published. Required fields are marked *