Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the antispam-bee domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/ruwritingagame/public_html/wp-includes/functions.php on line 6121
Глава 38. Иерархичные модели – Игра в Написание Игры

Глава 38. Иерархичные модели

Теперь можно подумать и о более сложных моделях, состоящих из нескольких элементов с индивидуальным поведением. Например – машина, где корпус был бы корневым элементом, а колеса – дочерними. Для обоих (корпус и колесо) понадобятся собственные отдельные дескрипторы и классы, обсуживающие их поведение.

1. В Windows File Explorer-е откроем наш корневой (CPP) каталог, сделаем копию a997modeler каталога и переименуем ее (копию) в a996car


2. Под C:\CPP\a996car\dt\models добавим новый sub-folder

C:\CPP\a996car\dt\models\cars\01


Для простоты в этом примере пусть наши модели (корпус и колеса) будут просто квадратными.

3. Копируем нижеследующий код в Text Editor и сохраняем его в/как

C:\CPP\a996car\dt\models\cars\01\wheel01.txt

//wheel
<mt_type="phong" uColor="#0000ff" />
<vs="box" whl="4,20,20" />
<a="front,back,right,top,bottom" />
<mt_type="phong" uColor="#000000" /> //inner side
<a="left" />


Теперь корневая модель – корпус с приделанными колесами.

4. Копируем нижеследующий код в Text Editor и сохраняем в/как

C:\CPP\a996car\dt\models\cars\01\root01.txt

//body
<mt_type="phong" uColor="#ff0000" />
<vs="box" whl="10,20,70" />
<a="front,back,left,right,top" py=5  />
//wheels
<element="wheel01.txt" class="CarWheel" pxyz="-10,0,30" ay=180 /> //front passenger
<element="wheel01.txt" class="CarWheel" pxyz="10,0,30" /> //front driver
<element="wheel01.txt" class="CarWheel" pxyz="-10,0,-20" ay=180 /> //rear passenger
<element="wheel01.txt" class="CarWheel" pxyz="10,0,-20" /> //rear driver
 

Теперь – SW часть.

5. Запускаем VS. Открываем C:\CPP\a996car\p_windows\p_windows.sln solution.


Иерархическая концепция потребует изменений в классе GameSubj:

6. Заменим GameSubj.h код этим:

#pragma once
#include "Coords.h"
#include "Material.h"
#include <string>
#include <vector>

class GameSubj
{
public:
	std::vector<GameSubj*>* pSubjsSet = NULL; //which vector/set this subj belongs to
	int nInSubjsSet = 0; //subj's number in pSubjsSet
	int rootN = 0; //model's root N
	int d2parent = 0; //shift to parent object
	int d2headTo = 0; //shift to headTo object
	int totalElements = 0; //elements N in the model
	int totalNativeElements = 0; //elements N in the model when initially loaded
	char source[256] = "";
	char className[32] = "";
	Coords absCoords;
	mat4x4 absModelMatrixUnscaled = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
	mat4x4 absModelMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };

	char name[32]="";
	Coords ownCoords;
	Coords ownSpeed;
	float scale[3] = { 1,1,1 };
	int djStartN = 0; //first DJ N in DJs array (DrawJob::drawJobs)
	int djTotalN = 0; //number of DJs
	mat4x4 ownModelMatrixUnscaled = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };

	Material* pAltMaterial = NULL;
public:
	virtual GameSubj* clone() { return new GameSubj(*this); };
	virtual ~GameSubj();
	void buildModelMatrix() { buildModelMatrix(this); };
	static void buildModelMatrix(GameSubj* pGS);
	virtual int moveSubj() { return applySpeeds(this); };
	static int applySpeeds(GameSubj* pGS);
};

Обратите внимание на:

  • Новую виртуальную функцию clone().
  • Набор новых parent/child переменных

7. Заменим GameSubj.cpp код на:

#include "GameSubj.h"
#include "platform.h"
#include "utils.h"

GameSubj::~GameSubj() {
    if (pAltMaterial != NULL)
        delete pAltMaterial;
}
void GameSubj::buildModelMatrix(GameSubj* pGS) {
    mat4x4_translate(pGS->ownModelMatrixUnscaled, pGS->ownCoords.pos[0], pGS->ownCoords.pos[1], pGS->ownCoords.pos[2]);
    //rotation order: Z-X-Y
    mat4x4_mul(pGS->ownModelMatrixUnscaled, pGS->ownModelMatrixUnscaled, pGS->ownCoords.rotationMatrix);

    if(pGS->d2parent == 0)
        memcpy(pGS->absModelMatrixUnscaled, pGS->ownModelMatrixUnscaled, sizeof(mat4x4));
    else {
        GameSubj* pParent = pGS->pSubjsSet->at(pGS->nInSubjsSet - pGS->d2parent);
        mat4x4_mul(pGS->absModelMatrixUnscaled, pParent->absModelMatrixUnscaled, pGS->ownModelMatrixUnscaled);
    }
    if (v3equals(pGS->scale, 1))
        memcpy(pGS->absModelMatrix, pGS->absModelMatrixUnscaled, sizeof(mat4x4));
    else
        mat4x4_scale_aniso(pGS->absModelMatrix, pGS->absModelMatrixUnscaled, pGS->scale[0], pGS->scale[1], pGS->scale[2]);

    //update absCoords
    if (pGS->d2parent == 0)
        memcpy(&pGS->absCoords, &pGS->ownCoords, sizeof(Coords));
    else {
        Coords::getPositionFromMatrix(pGS->absCoords.pos, pGS->absModelMatrixUnscaled);
    }
}
int GameSubj::applySpeeds(GameSubj* pGS) {
    bool angleChanged = false;
    for(int i=0;i<3;i++)
        if (pGS->ownSpeed.eulerDg[i] != 0) {
            angleChanged = true;
            pGS->ownCoords.eulerDg[i] += pGS->ownSpeed.eulerDg[i];
            if (pGS->ownCoords.eulerDg[i] > 180.0)
                pGS->ownCoords.eulerDg[i] -= 360.0;
            else if (pGS->ownCoords.eulerDg[i] <= -180.0)
                pGS->ownCoords.eulerDg[i] += 360.0;
        }
    if(angleChanged)
        Coords::eulerDgToMatrix(pGS->ownCoords.rotationMatrix, pGS->ownCoords.eulerDg);
    return 1;
}

Обратите внимание, что:

  • buildModelMatrix(..) теперь учитывет еще и родительские координаты.
  • applySpeeds() тоже изменен.

Класс Coords серьезно пересмотрен (упрощен).

8. Заменим Coords.h код на:

#pragma once
#include "linmath.h"

class Coords
{
public:
	float pos[4] = { 0,0,0,0 }; //x,y,z position + 4-th element for compatibility with 3D 4x4 matrices math
	float eulerDg[3] = { 0,0,0 }; //Euler angles (pitch, yaw, roll) in degrees
	mat4x4 rotationMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
public:
	static void getPositionFromMatrix(float* pos, mat4x4 m);
	static void eulerDgToMatrix(mat4x4 rotationMatrix, float* eulerDg);
	static void matrixToEulerDg(float* eulerDg, mat4x4 m);
};


9. Заменим Coords.cpp код на:

#include "Coords.h"
#include "platform.h"
#include <string>

float PI = 3.141592f;
float degrees2radians = PI / 180.0f;
float radians2degrees = 180.0f / PI;

void Coords::getPositionFromMatrix(float* pos, mat4x4 m) {
	//extracts position from matrix
	for (int i = 0; i < 3; i++)
		pos[i] = m[i][3];
}
void Coords::eulerDgToMatrix(mat4x4 rotationMatrix, float* eulerDg) {
	//builds rotation matrix from Euler angles (in degreed)
	mat4x4_identity(rotationMatrix);
	//rotation order: Z-X-Y
	float a = eulerDg[1];
	if (a != 0)
		mat4x4_rotate_Y(rotationMatrix, rotationMatrix, a * degrees2radians);
	a = eulerDg[0];
	if (a != 0)
		mat4x4_rotate_X(rotationMatrix, rotationMatrix, a * degrees2radians);
	a = eulerDg[2];
	if (a != 0)
		mat4x4_rotate_Z(rotationMatrix, rotationMatrix, a * degrees2radians);
}
void Coords::matrixToEulerDg(float* eulerDg, mat4x4 m) {
	//calculates Euler angles (in degrees) from matrix
	float yaw, pitch, roll; //in redians

	if (m[1][2] > 0.998 || m[1][2] < -0.998) { // singularity at south or north pole
		yaw = atan2f(-m[2][0], m[0][0]);
		roll = 0;
	}
	else {
		yaw = atan2f(-m[0][2], m[2][2]);
		roll = atan2f(-m[1][0], m[1][1]);
	}
	pitch = asinf(m[1][2]);

	eulerDg[0] = pitch * radians2degrees;
	eulerDg[1] = yaw * radians2degrees;
	eulerDg[2] = roll * radians2degrees;
}


Эти изменения также повлияли на класс Camera.

10. Заменим Camera.cpp код на:

#include "Camera.h"
#include "TheGame.h"
#include "utils.h"

extern TheGame theGame;
extern float degrees2radians;

float Camera::pickDistance(Camera* pCam) {
	float cotangentA = 1.0f / tanf(degrees2radians * pCam->viewRangeDg / 2);
	float cameraDistanceV = pCam->stageSize[1] / 2 * cotangentA;
	float cameraDistanceH = pCam->stageSize[0] / 2 * cotangentA / theGame.screenAspectRatio;
	pCam->focusDistance = fmax(cameraDistanceV, cameraDistanceH);
	return pCam->focusDistance;
}
void Camera::setCameraPosition(Camera* pCam) {
	v3set(pCam->ownCoords.pos, 0, 0, -pCam->focusDistance);
	mat4x4_mul_vec4plus(pCam->ownCoords.pos, pCam->ownCoords.rotationMatrix, pCam->ownCoords.pos, 1);
	for (int i = 0; i < 3; i++)
		pCam->ownCoords.pos[i] += pCam->lookAtPoint[i];
}
void Camera::buildLookAtMatrix(Camera* pCam) {
	float cameraUp[4] = { 0,1,0,0 }; //y - up
	mat4x4_mul_vec4plus(cameraUp, pCam->ownCoords.rotationMatrix, cameraUp, 0);
	mat4x4_look_at(pCam->lookAtMatrix, pCam->ownCoords.pos, pCam->lookAtPoint, cameraUp);
}
void Camera::onScreenResize(Camera* pCam) {
	pickDistance(pCam);
	setCameraPosition(pCam);
	buildLookAtMatrix(pCam);
}


Новый класс CarWheel будет частью Проекта, а не движка. Давайте добавим его.

11. Под xTheGame добавим New Filter,имя – car


12. Под xTheGame/car добавим new item – header file CarWheel.h

Меняем location на C:\CPP\a996car\car

Код:

#pragma once
#include "GameSubj.h"

class CarWheel : public GameSubj
{
public:
	float wheelDiameter = 20;
public:
	virtual GameSubj* clone() { return new CarWheel(*this); };
	virtual int moveSubj() { return moveSubj(this); };
	static int moveSubj(CarWheel* pGS);
};

Заметьте:

  • Он наследует класс GameSubj
  • У него собственная имплементация виртуальной функции clone(), которая возвращает ново-созданный объект CarWheel
  • И собственная имплементация виртуальной функции moveSubj()

13. Под xTheGame/car добавим новый C++ file CarWheel.cpp

Location: C:\CPP\a996car\car

Код:

#include "CarWheel.h"

int CarWheel::moveSubj(CarWheel* pGS) {
    float wheelRotationSpeedDg = 3;
    if (abs(pGS->ownCoords.eulerDg[1]) > 90)
        wheelRotationSpeedDg = -wheelRotationSpeedDg;
    pGS->ownSpeed.eulerDg[0] = wheelRotationSpeedDg;
    applySpeeds(pGS);
    return 1;
}

  • Функция moveSubj() просто проворачивает колесо с постоянной радиальной скоростью вокруг оси X.

14. Заменим 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"
#include "car/CarWheel.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/cars/01/root01.txt", "");
    GameSubj* pGS = gameSubjs.at(subjN);
    pGS->ownSpeed.eulerDg[1] = 1;

    //===== set up camera
     v3set(mainCamera.ownCoords.eulerDg, 15, 180, 0); //set camera angles/orientation
    Coords::eulerDgToMatrix(mainCamera.ownCoords.rotationMatrix, mainCamera.ownCoords.eulerDg);

    mainCamera.viewRangeDg = 20;
    mainCamera.stageSize[0] = 90;
    mainCamera.stageSize[1] = 60;
    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);
    glDepthMask(GL_TRUE);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    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 - 55;
    float farClip = mainCamera.focusDistance + 55;
    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->absModelMatrix);
        //build Model-View (rotation) matrix for normals
        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, pGS->absModelMatrixUnscaled);
        //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];
        //subj's distance from camera
        float cameraSpacePos[4];
        mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS->absCoords.pos, 1);
        float zDistance = abs(cameraSpacePos[2]);
        float cotangentA = 1.0f / tanf(degrees2radians * mainCamera.viewRangeDg / 2.0f);
        float halfScreenVertSizeInUnits = zDistance / cotangentA;
        float sizeUnitPixelsSize = (float)screenSize[1] / 2.0f / halfScreenVertSizeInUnits;
        //render subject
        for (int i = 0; i < pGS->djTotalN; i++) {
            DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
            pDJ->execute((float*)mMVP, *mMV3x3, (float*)pGS->absModelMatrix, dirToMainLight, mainCamera.ownCoords.pos, sizeUnitPixelsSize, 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) {
    GameSubj* pGS = NULL;
    if (subjClass.compare("") == 0)
        pGS = (new GameSubj());
    else if (subjClass.find("Car") == 0) {
        if (subjClass.compare("CarWheel") == 0)
            pGS = (new CarWheel());
    }
    if(pGS == NULL) {
        mylog("ERROR in TheGame::newGameSubj. %s class not found\n", subjClass.c_str());
        return NULL;
    }
    myStrcpy_s(pGS->className, 32, subjClass.c_str());
    return pGS;
}

Заметим:

  • Новый #include: car/CarWheel.h
  • Функция newGameSubj(..) теперь создает новые классы

Теперь – чтение/обработка тагаelement:

15. Заменим ModelLoader.h код на:

#pragma once
#include "XMLparser.h"
#include "ModelBuilder.h"
#include "GroupTransform.h"
#include "MaterialAdjust.h"

class ModelLoader : public XMLparser
{
public:
	ModelBuilder* pModelBuilder = NULL;
	bool ownModelBuilder = false;
	std::vector<GameSubj*>* pSubjsVector = NULL;
	MaterialAdjust* pMaterialAdjust = NULL;
	int lineStartsAt = -1;
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(pModelBuilder,subjN);
	};
	virtual ~ModelLoader() {
		if (!ownModelBuilder)
			return;
		pModelBuilder->buildDrawJobs(pModelBuilder, 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);
	virtual 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);
	static int processTag_a2mesh(ModelLoader* pML);
	static int processTag_element(ModelLoader* pML);
};


16. Заменим ModelLoader.cpp код на:

#include "ModelLoader.h"
#include "platform.h"
#include "TheGame.h"
#include "DrawJob.h"
#include "Texture.h"
#include "utils.h"
#include "Polygon.h"

extern TheGame theGame; 

int ModelLoader::loadModel(std::vector<GameSubj*>* pSubjsVector0, std::string sourceFile, std::string subjClass) {
	//returns element's (Subj) number or -1
	//first - check if already loaded
	int totalSubjs = pSubjsVector0->size();
	for (int subjN0 = totalSubjs - 1; subjN0 >= 0; subjN0--) {
		GameSubj* pGS0 = pSubjsVector0->at(subjN0);
		if (pGS0 == NULL)
			continue;
		if (strcmp(pGS0->source, sourceFile.c_str()) != 0)
			continue;
		//if here - model was already loaded - copy
		int subjN = pSubjsVector0->size();
		for (int i = 0; i < pGS0->totalNativeElements; i++) {
			GameSubj* pGS = pSubjsVector0->at(subjN0 + i)->clone();
			pGS->nInSubjsSet = pSubjsVector0->size();
			pSubjsVector0->push_back(pGS);
		}
		GameSubj* pGS = pSubjsVector0->at(subjN);
		pGS->totalElements = pGS->totalNativeElements;
		pGS->d2parent = 0;
		pGS->d2headTo = 0;
		//restore original 1st DrawJob
		return subjN;
	}
	//if here - model wasn't loaded before - load
	int subjN = pSubjsVector0->size();
	GameSubj* pGS = theGame.newGameSubj(subjClass);
	pGS->pSubjsSet = pSubjsVector0;
	pGS->nInSubjsSet = subjN;
	myStrcpy_s(pGS->source, 256, sourceFile.c_str());
	pSubjsVector0->push_back(pGS);
	ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
	processSource(pML);
	delete pML;
	pGS->totalNativeElements = pSubjsVector0->size() - subjN;
	pGS->totalElements = pGS->totalNativeElements;
	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;
	bool resetTexture = false;
	std::string varName = txName + "_use";
	if (varExists(varName, pML->currentTag)) {
		if (setValueFromIntHashMap(pInt, pMB->texturesHashMap, varName, pML->currentTag) == 0) {
			mylog("ERROR in ModelLoader::setTexture: texture not in hashMap: %s\n", pML->currentTag.c_str());
			return -1;
		}
		resetTexture = true;
	}
	else{
		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);
			resetTexture = true;
		}
	}
	if(resetTexture)
		return 1;
	return 0; //texture wasn't reset
}
int ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {
	if (setTexture(pML, &pMT->uTex0, "uTex0") > 0)
		pMT->uColor.clear();
	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);
		pMT->uTex0 = -1;
	}
	//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);
	setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
	if (pMT->uAlphaFactor < 1)
		pMT->uAlphaBlending = 1;
	setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
	if (pMT->uAlphaBlending > 0)
		pMT->zBufferUpdate = 0;
	setFloatValue(&pMT->uAmbient, "uAmbient", tagStr);
	setFloatValue(&pMT->uSpecularIntencity, "uSpecularIntencity", tagStr);
	setFloatValue(&pMT->uSpecularMinDot, "uSpecularMinDot", tagStr);
	setFloatValue(&pMT->uSpecularPowerOf, "uSpecularPowerOf", tagStr);

	setFloatValue(&pMT->lineWidth, "lineWidth", tagStr);
	setIntBoolValue(&pMT->zBuffer, "zBuffer", tagStr);
	if (pMT->zBuffer < 1)
		pMT->zBufferUpdate = 0;
	setIntBoolValue(&pMT->zBufferUpdate, "zBufferUpdate", tagStr);

	return 1;
}
int ModelLoader::processTag(ModelLoader* pML) {
	ModelBuilder* pMB = pML->pModelBuilder;
	if (pML->tagName.compare("element") == 0)
		return processTag_element(pML);
	if (pML->tagName.compare("/element") == 0) {
		//restore previous useSubjN from stack
		int subjN = pMB->usingSubjsStack.back();
		pMB->usingSubjsStack.pop_back();
		pMB->useSubjN(pMB, subjN);
		return 1;
	}
	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.compare("mt_type") == 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;
		fillProps_mt(&mt, pML->currentTag, pML);
		pMB->usingMaterialN = pMB->getMaterialN(pMB, &mt);
		return 1;
	}
	if (pML->tagName.compare("/mt_type") == 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);
	if (pML->tagName.compare("a2mesh") == 0)
		return processTag_a2mesh(pML);
	if (pML->tagName.compare("mt_adjust") == 0) {
		if (pML->pMaterialAdjust != NULL)
			mylog("ERROR in ModelLoader::processTag %s, pMaterialAdjust is still busy. File: %s\n", pML->currentTag.c_str(), pML->fullPath.c_str());
		pML->pMaterialAdjust = new (MaterialAdjust);
		fillProps_mt(pML->pMaterialAdjust, pML->currentTag, pML);
		pML->pMaterialAdjust->setWhat2adjust(pML->pMaterialAdjust, pML->currentTag);
		return 1;
	}
	if (pML->tagName.compare("/mt_adjust") == 0) {
		if (pML->pMaterialAdjust != NULL) {
			delete pML->pMaterialAdjust;
			pML->pMaterialAdjust = NULL;
		}
		return 1;
	}
	if (pML->tagName.compare("line") == 0) {
		Material mt;
		//save previous material in stack
		if (pMB->usingMaterialN >= 0){
			pMB->materialsStack.push_back(pMB->usingMaterialN);
			memcpy(&mt, pMB->materialsList.at(pMB->usingMaterialN),sizeof(Material));
		}
		mt.primitiveType = GL_LINE_STRIP;
		fillProps_mt(&mt, pML->currentTag, pML);
		pMB->usingMaterialN = pMB->getMaterialN(pMB, &mt);
		//line starts
		pML->lineStartsAt = pMB->vertices.size();
		return 1;
	}
	if (pML->tagName.compare("/line") == 0) {
		pMB->vertices.back()->endOfSequence = 1;
		pML->lineStartsAt = -1;
		//restore previous material
		if (pMB->materialsStack.size() > 0) {
			pMB->usingMaterialN = pMB->materialsStack.back();
			pMB->materialsStack.pop_back();
		}
		return 1;
	}
	if (pML->tagName.compare("p") == 0) {
		//line point
		Vertex01* pV = new Vertex01();
		if (pMB->vertices.size() > pML->lineStartsAt)
			memcpy(pV, pMB->vertices.back(), sizeof(Vertex01));
		pV->subjN = pMB->usingSubjN;
		pV->materialN = pMB->usingMaterialN;
		setFloatArray(pV->aPos, 3, "pxyz", pML->currentTag);
		setFloatValue(&pV->aPos[0], "px", pML->currentTag);
		setFloatValue(&pV->aPos[1], "py", pML->currentTag);
		setFloatValue(&pV->aPos[2], "pz", pML->currentTag);
		float dPos[3] = { 0,0,0 };
		setFloatArray(dPos, 3, "dxyz", pML->currentTag);
		setFloatValue(&dPos[0], "dx", pML->currentTag);
		setFloatValue(&dPos[1], "dy", pML->currentTag);
		setFloatValue(&dPos[2], "dz", pML->currentTag);
		if (!v3equals(dPos, 0))
			for (int i = 0; i < 3; i++)
				pV->aPos[i] += dPos[i];
		pMB->vertices.push_back(pV);
		return 1;
	}
	mylog("ERROR in ModelLoader::processTag, unhandled tag %s, file %s\n", pML->currentTag.c_str(), pML->fullPath.c_str());
	//mylog("======File:\n%s----------\n", pML->pData);
	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 };
	TexCoords* pTC = NULL;
	if (varExists("xywh", tagStr)) {
		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);
		pTC = &tc;
	}
	TexCoords* pTC2nm = NULL;
	if (varExists("xywh2nm", tagStr)) {
		setFloatArray(xywh, 4, "xywh2nm", tagStr);
		std::string flipStr = getStringValue("flip2nm", tagStr);
		TexCoords tc2nm;
		tc2nm.set(pMT->uTex2nm, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
		pTC2nm = &tc2nm;
	}
	//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), pVS_a, pTC, pTC2nm);
	}
	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);

	if (varExists("sizeD", tagStr)) { //re-size
		float sizeD[3];
		setFloatArray(sizeD, 3, "sizeD", tagStr);
		//bounding box
		pGT->flagSelection(pGT, &pMB->vertices, NULL);
		float bbMin[3];
		float bbMax[3];
		pGT->buildBoundingBoxFlagged(bbMin, bbMax, &pMB->vertices);
		for (int i = 0; i < 3; i++) {
			float size = bbMax[i] - bbMin[i];
			pGT->scale[i] = (size + sizeD[i]) / size;
		}
	}
	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;
}
int ModelLoader::processTag_a2mesh(ModelLoader* pML) {
	ModelBuilder* pMB = pML->pModelBuilder;
	std::string tagStr = pML->currentTag;
	GroupTransform gt;
	fillProps_gt(&gt, pMB, pML->currentTag);
	gt.flagSelection(&gt, &pMB->vertices, &pMB->triangles);
	//clone a copy
	std::vector<Vertex01*> vx1;
	std::vector<Triangle01*> tr1;
	gt.cloneFlagged(NULL, &vx1, &tr1, &pMB->vertices, &pMB->triangles);
	// build transform and inverted martrices
	mat4x4 transformMatrix;
	gt.buildTransformMatrix(&gt, &transformMatrix);
	mat4x4 transformMatrixInverted;
	mat4x4_invert(transformMatrixInverted, transformMatrix);
	//move/rotate cloned
	gt.flagAll(&vx1, &tr1);
	//gt.transformFlagged(&pMB->vertices, &transformMatrixInverted);
	gt.transformFlaggedMx(&vx1, &transformMatrixInverted);

	//gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &vx1, &tr1);

	float wh[2];
	setFloatArray(wh, 2, "wh", tagStr);
	Polygon frame;
	frame.setRectangle(&frame, wh[0], wh[1]);
	//destination arrays
	std::vector<Vertex01*> vx2;
	std::vector<Triangle01*> tr2;
	Polygon triangle;
	for (int i = tr1.size() - 1; i >= 0; i--) {
		triangle.setTriangle(&triangle, tr1.at(i), &vx1);
		Polygon intersection;
		int pointsN = Polygon::xyIntersection(&intersection, &frame, &triangle);
		if (pointsN > 2) {
			Polygon::buildTriangles(&intersection);
			GroupTransform::flagAll(&intersection.vertices, &intersection.triangles);
			GroupTransform::cloneFlagged(NULL, &vx2, &tr2, &intersection.vertices, &intersection.triangles);
		}
	}
	gt.flagAll(&vx2, &tr2);
	//at this point we have cutted fragment facing us
	int vxTotal = vx2.size();
	int trTotal = tr2.size();
	//apply adjusted material ?
	if (pML->pMaterialAdjust != NULL) {
		//scan vertices to find new (unupdated) material
		int materialNsrc = -1; //which N to replace
		int materialNdst = -1; //replace by N 
		for (int vN = 0; vN < vxTotal; vN++) {
			Vertex01* pV = vx2.at(vN);
			if (pV->flag < 0)
				continue;
			if (materialNsrc == pV->materialN)
				continue;
			//have new material
			materialNsrc = pV->materialN;
			Material mt;
			Material* pMt0 = pMB->materialsList.at(materialNsrc);
			memcpy(&mt, pMt0, sizeof(Material));
			//modify material
			MaterialAdjust::adjust(&mt, pML->pMaterialAdjust);
			materialNdst = pMB->getMaterialN(pMB, &mt);
			if (materialNsrc != materialNdst) {
				//replace mtN in vx and tr arrays
				for (int vN2 = vN; vN2 < vxTotal; vN2++) {
					Vertex01* pV2 = vx2.at(vN2);
					if (pV2->flag < 0)
						continue;
					if (materialNsrc == pV2->materialN)
						pV2->materialN = materialNdst;
				}
				for (int tN2 = 0; tN2 < trTotal; tN2++) {
					Triangle01* pT2 = tr2.at(tN2);
					if (pT2->flag < 0)
						continue;
					if (materialNsrc == pT2->materialN)
						pT2->materialN = materialNdst;
				}
				materialNsrc = materialNdst;
			}
		}
	}
	else { // pML->pMaterialAdjust == NULL, use pMB->usingMaterialN
		for (int vN2 = 0; vN2 < vxTotal; vN2++) {
			Vertex01* pV2 = vx2.at(vN2);
			if (pV2->flag < 0)
				continue;
			pV2->materialN = pMB->usingMaterialN;
		}
		for (int tN2 = 0; tN2 < trTotal; tN2++) {
			Triangle01* pT2 = tr2.at(tN2);
			if (pT2->flag < 0)
				continue;
			pT2->materialN = pMB->usingMaterialN;
		}
	}
	//apply xywh/2nm ?
	if (varExists("xywh", tagStr) || varExists("xywh2nm", tagStr)) {
		Material* pMT = pMB->materialsList.at(vx2.at(0)->materialN);
		float xywh[4] = { 0,0,1,1 };
		TexCoords* pTC = NULL;
		if (varExists("xywh", tagStr)) {
			setFloatArray(xywh, 4, "xywh", tagStr);
			std::string flipStr = getStringValue("flip", tagStr);
			int texN = pMT->uTex1mask;
			if (texN < 0)
				texN = pMT->uTex0;
			TexCoords tc;
			tc.set(texN, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
			pTC = &tc;
		}
		TexCoords* pTC2nm = NULL;
		if (varExists("xywh2nm", tagStr)) {
			setFloatArray(xywh, 4, "xywh2nm", tagStr);
			std::string flipStr = getStringValue("flip2nm", tagStr);
			TexCoords tc2nm;
			tc2nm.set(pMT->uTex2nm, xywh[0], xywh[1], xywh[2], xywh[3], flipStr);
			pTC2nm = &tc2nm;
		}
		pMB->applyTexture2flagged(&vx2, "front", pTC, false);
		pMB->applyTexture2flagged(&vx2, "front", pTC2nm, true);
	}

	float detachBy =0;
	setFloatValue(&detachBy, "detachBy", tagStr);
	if (detachBy != 0) {
		mat4x4 mx;
		mat4x4_translate(mx, 0, 0, detachBy);
		gt.transformFlaggedMx(&vx2, &mx);
	}
	//move/rotate back
	gt.transformFlaggedMx(&vx2, &transformMatrix);
	//clone back to modelBuilder arrays
	gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &vx2, &tr2);

	//clear memory
	for (int i = vx1.size() - 1; i >= 0; i--)
		delete vx1.at(i);
	vx1.clear();
	for (int i = tr1.size() - 1; i >= 0; i--)
		delete tr1.at(i);
	tr1.clear();
	for (int i = vx2.size() - 1; i >= 0; i--)
		delete vx2.at(i);
	vx2.clear();
	for (int i = tr2.size() - 1; i >= 0; i--)
		delete tr2.at(i);
	tr2.clear();

	return 1;
}
int ModelLoader::processTag_element(ModelLoader* pML) {
	ModelBuilder* pMB = pML->pModelBuilder;
	std::string tagStr = pML->currentTag;
	std::string sourceFile = getStringValue("element", tagStr);
	std::string subjClass = getStringValue("class", tagStr);
	std::vector<GameSubj*>* pSubjsVector0 = pML->pSubjsVector;
	int subjN = -1;
	if (!sourceFile.empty()) {
		sourceFile = buildFullPath(pML, sourceFile);
		subjN = loadModel(pSubjsVector0, sourceFile, subjClass);
	}
	else { //sourceFile not specified
		subjN = pML->pSubjsVector->size();
		GameSubj* pGS = theGame.newGameSubj(subjClass);
		pGS->pSubjsSet = pSubjsVector0;
		pGS->nInSubjsSet = subjN;
		pML->pSubjsVector->push_back(pGS);
		pGS->totalNativeElements = 1;
		pGS->totalElements = 1;
		if (!pML->closedTag) { //DrawJobs will follow
			pMB->usingSubjsStack.push_back(pMB->usingSubjN);
			pMB->useSubjN(pMB, subjN);
		}
	}
	//keep reading tag
	GameSubj* pGS = pSubjsVector0->at(subjN);
	int rootN = pMB->subjNumbersList.at(0);
	std::string attachTo = getStringValue("attachTo", tagStr);
	if (attachTo.empty()) //attach to root
		pGS->d2parent = subjN - rootN;
	else {
		//find parent by name
		for (int sN = subjN - 1; sN >= rootN; sN--) {
			if (strcmp(pSubjsVector0->at(sN)->name, attachTo.c_str()) == 0) {
				pGS->d2parent = subjN - sN;
				break;
			}
		}
	}
	std::string headTo = getStringValue("headTo", tagStr);
	if (!headTo.empty()) { //find headTo by name
		for (int sN = subjN - 1; sN >= rootN; sN--) {
			if (strcmp(pSubjsVector0->at(sN)->name, headTo.c_str()) == 0) {
				pGS->d2headTo = subjN - sN;
				break;
			}
		}
	}
	float xyz[3]={0,0,0};
	//position
	setFloatArray(xyz, 3, "pxyz", tagStr);
	setFloatValue(&xyz[0], "px", tagStr);
	setFloatValue(&xyz[1], "py", tagStr);
	setFloatValue(&xyz[2], "pz", tagStr);
	v3copy(pGS->ownCoords.pos, xyz);
	//angles
	v3set(xyz, 0,0,0);
	setFloatArray(xyz, 3, "axyz", tagStr);
	setFloatValue(&xyz[0], "ax", tagStr);
	setFloatValue(&xyz[1], "ay", tagStr);
	setFloatValue(&xyz[2], "az", tagStr);
	v3set(pGS->ownCoords.eulerDg, xyz[0], xyz[1], xyz[2]);
	return 1;
}

Заметим:

  • При создании нового GameSubj-а, мы запоминаем, в какой вектор его записали – pGS->pSubjsSet = pSubjsVector0;
  • В ModelLoader::loadModel(): если такая модель уже была загружена, то просто делаем копию, чтобы не создавать новых DrawJobs.

Для отработки нескольких моделей в ModelBuilder у нас новый vector/stack std::vector usingSubjsStack;

17. Заменим ModelBuilder1base.h код на:

#pragma once
#include <string>
#include <vector>
#include "Vertex01.h"
#include "Triangle01.h"
#include "VirtualShape.h"
#include "Group01.h"
#include "Material.h"
#include "GameSubj.h"
#include <map>

class ModelBuilder1base
{
public:
	std::vector<Vertex01*> vertices;
	std::vector<Triangle01*> triangles;
	std::vector<int> subjNumbersList;
	int usingSubjN = -1;
	std::vector<int> usingSubjsStack;

	std::vector<Group01*> groupsStack;
	Group01* pCurrentGroup = NULL;
	Group01* pLastClosedGroup = NULL;
	
	std::vector<VirtualShape*> vShapesStack;
	VirtualShape* pCurrentVShape = NULL;

	std::vector<Material*> materialsList;
	int usingMaterialN = -1;
	std::vector<int> materialsStack;

	std::map<std::string, int> texturesHashMap;
public:
	virtual ~ModelBuilder1base();
	static int useSubjN(ModelBuilder1base* pMB, int subjN);
	static int getMaterialN(ModelBuilder1base* pMB, Material* pMT);
	static void lockGroup(ModelBuilder1base* pMB);
	static void releaseGroup(ModelBuilder1base* pMB);
	static int addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz);
	static int add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n);
	static int addTriangle(ModelBuilder1base* pMB, int n0, int n1, int n2);
	static int buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*>* pGameSubjs);
	static int rearrangeArraysForDrawJob(std::vector<Vertex01*>* pAllVertices, std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles);
	static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles);
	static int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);
	static int calculateTangentSpace(std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles);
	static int finalizeLine(std::vector<Vertex01*>* pVerts, int lineStartsAt = 0, int lineEndsAt = 0);
	static int optimizeMesh(std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles);
};


18. Компиляция и запуск:


Android

19. Перезапускаем VS. Открываем C:\CPP\a996car\p_android\p_android.sln


20. Под xTheGame фильтром добавляем New Filter, имя – car


21. Под xTheGame/car добавляем existing item

из C:\CPP\a996car\car

  • CarWheel.cpp
  • CarWheel.h

Add


22. Чтобы не переписать Marlboro APK на устройстве,

откроем AndroidManifest.xml (он под проектом p_android.Packaging )

и изменим package name (line 4) на “com.OurProjectCar:

package="com.OurProjectCar"

и поменяем android:label на “OurProjectCar:

android:label="OurProjectCar"

23. Включаем, разблокируем, подключаем, разрешаем.

Компиляция и запуск. Прекрасно.


Leave a Reply

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