Теперь можно подумать и о более сложных моделях, состоящих из нескольких элементов с индивидуальным поведением. Например – машина, где корпус был бы корневым элементом, а колеса – дочерними. Для обоих (корпус и колесо) понадобятся собственные отдельные дескрипторы и классы, обсуживающие их поведение.
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(>, 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(>_a, pMB, tagStr);
GT_a.executeGroupTransform(pMB);
pMB->releaseGroup(pMB);
return 1;
}
int ModelLoader::processTag_clone(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("clone") == 0) {
//mark what to clone
GroupTransform gt;
gt.pGroup = pMB->pLastClosedGroup;
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
//cloning
pMB->lockGroup(pMB);
gt.cloneFlagged(pMB, &pMB->vertices, &pMB->triangles, &pMB->vertices, &pMB->triangles);
}
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.executeGroupTransform(pMB);
if (pML->tagName.compare("/clone") == 0 || pML->closedTag) {
pMB->releaseGroup(pMB);
}
return 1;
}
int ModelLoader::addMark(char* marks, std::string newMark) {
if (newMark.empty())
return 0;
std::string allMarks;
allMarks.assign(marks);
allMarks.append("<" + newMark + ">");
myStrcpy_s(marks, 124, allMarks.c_str());
return 1;
}
int ModelLoader::fillProps_gt(GroupTransform* pGT, ModelBuilder* pMB, std::string tagStr) {
pGT->pGroup = pMB->pCurrentGroup;
//position
setFloatArray(pGT->shift, 3, "pxyz", tagStr);
setFloatValue(&pGT->shift[0], "px", tagStr);
setFloatValue(&pGT->shift[1], "py", tagStr);
setFloatValue(&pGT->shift[2], "pz", tagStr);
//angles
setFloatArray(pGT->spin, 3, "axyz", tagStr);
setFloatValue(&pGT->spin[0], "ax", tagStr);
setFloatValue(&pGT->spin[1], "ay", tagStr);
setFloatValue(&pGT->spin[2], "az", tagStr);
//scale
setFloatArray(pGT->scale, 3, "scale", tagStr);
pGT->onThe = getStringValue("onThe", tagStr);
pGT->allign = getStringValue("allign", tagStr);
pGT->headZto = getStringValue("headZto", tagStr);
//limit to
if (varExists("all", tagStr))
pGT->pGroup = NULL;
if (varExists("lastClosedGroup", tagStr))
pGT->pGroup = pMB->pLastClosedGroup;
if (varExists("markedAs", tagStr))
pGT->limit2mark(pGT, getStringValue("markedAs", tagStr));
setFloatArray(pGT->pMin, 3, "xyzMin", tagStr);
setFloatArray(pGT->pMax, 3, "xyzMax", tagStr);
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(>, pMB, pML->currentTag);
gt.flagSelection(>, &pMB->vertices, &pMB->triangles);
gt.transformFlagged(>, &pMB->vertices);
return 1;
}
int ModelLoader::processTag_a2mesh(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
std::string tagStr = pML->currentTag;
GroupTransform gt;
fillProps_gt(>, pMB, pML->currentTag);
gt.flagSelection(>, &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(>, &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. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск. Прекрасно.