Now we are ready for more complex models, consisting of several elements with individual behavior. For example - car, where body will be a parent (root) element, and wheels - child elements. For both, body and wheel, we'll need separate model descriptors and separate classes, which will handle their behaviors.
1. In Windows File Explorer open our root (CPP) folder, make a copy of a997modeler and rename it (copy) to a996car
2. Under C:\CPP\a996car\dt\models add sub-folder
C:\CPP\a996car\dt\models\cars\01
For simplicity, in this example, our models (car body and wheels) will simply be square.
3. Copy following code to a Text Editor and save it to/as
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" />
Now root model - car body plus attached wheels.
4. Copy following code to a Text Editor and save it to/as
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
Now - SW part.
5. Start VS. Open C:\CPP\a996car\p_windows\p_windows.sln solution.
Hierarchical concept will require a few changes in GameSubj class:
6. Replace GameSubj.h code by:
#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);
};
Please note:
- New virtual function clone().
- A set of new parent/child variables
7. Replace GameSubj.cpp code by:
#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;
}
Please note:
- buildModelMatrix(..) now counts parent Coords too.
- applySpeeds() is modified as well.
Coords class is seriously revised (simplified).
8. Replace Coords.h code by:
#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. Replace Coords.cpp code by:
#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;
}
One of affected classes is Camera.
10. Replace Camera.cpp code by:
#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);
}
New class CarWheel will be part of the Project, not of the engine. Let's add it.
11. Under xTheGame filter add New Filter, name - car
12. Under xTheGame/car add new item - header file CarWheel.h
Make sure to change location to C:\CPP\a996car\car
Code:
#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);
};
Please note:
- It inherits GameSubj class
- It has own implementation of virtual clone() function, which returns new CarWheel object
- and own implementation of virtual moveSubj() function
13. Under xTheGame/car add new C++ file CarWheel.cpp
Location: C:\CPP\a996car\car
Code:
#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() function simply rotates wheel with a constant radial speed around X axis.
14. Replace TheGame.cpp code by:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
#include "ModelLoader.h"
#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;
}
Please note:
- New #include: car/CarWheel.h
- Function newGameSubj(..) handles new classes
Now - reading/processing element tag:
15. Replace ModelLoader.h by:
#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. Replace ModelLoader.cpp by:
#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;
}
Please note:
- When creating GameSubj, we assigning pGS->pSubjsSet = pSubjsVector0;
- In ModelLoader::loadModel(): if such model was already loaded, we are making a copy, reusing it's DrawJobs.
For handling loading multiple models in ModelBuilder we have new vector/stack std::vector usingSubjsStack;
17. Replace ModelBuilder1base.h by:
#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. Build and run. Result:
Android
19. Restart VS. Open C:\CPP\a996car\p_android\p_android.sln
20. Under xTheGame filter add New Filter, name - car
21. Under xTheGame/car add existing item
from C:\CPP\a996car\car
- CarWheel.cpp
- CarWheel.h
Add
22. In order not to overwrite Marlboro APK on the device,
open AndroidManifest.xml (it's under p_android.Packaging project)
and change package name (line 4) to "com.OurProjectCar":
package="com.OurProjectCar"
and change android:label to "OurProjectCar":
android:label="OurProjectCar"
23. Switch on, unlock, plug in, allow.
Build and run. Good.