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 6131
Я выбрал именно эту модель за ее изысканно гнутые формы. Если чо, это 1935 Deusenberg SSJ Speedster, масштаб 1:64.
Для этого понадобится расширить функционал ModelBuilder-а и ModelLoader-а. Также понадобятся новые XML таги в дескрипторах чтобы этот функционал задействовать.
Здесь не будет никаких новых концепций, классов, и даже новых шейдеров. Все нужные классы мы уже обсудили в предыдущих главах. Правда, функциональность существенно расширена. Практически все классы/файлы modeler-а задействованы. Объяснять все пошагово наверное будет занудновато. Поэтому – вот просто новый репозиторий Проекта на GitHub-е:
https://github.com/bkantemir/_wg39
На всякий случай, напомню вкратце как сгрузить с GitHub-а:
Компиляция и запуск. Результат:

Что ж, похоже, мы СМОЖЕМ посоревноваться с профессиональными 3D художниками 
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);
};
Обратите внимание на:
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;
}
Обратите внимание, что:
Класс 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);
};
Заметьте:
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;
}
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;
}
Заметим:
Теперь – чтение/обработка тага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;
}
Заметим:
Для отработки нескольких моделей в 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. Компиляция и запуск:

19. Перезапускаем VS. Открываем C:\CPP\a996car\p_android\p_android.sln
20. Под xTheGame фильтром добавляем New Filter, имя – car
21. Под xTheGame/car добавляем existing item
из C:\CPP\a996car\car
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. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск. Прекрасно.
Полный текущий репозиторий Проекта можно сгрузить с
https://github.com/bkantemir/_wg37
Важные изменения:
1. Идем на https://github.com/bkantemir/_wg37
2. Жмем зеленую кнопку “Code”.
3.Обычно я выбираю “Download ZIP”.
4. Когда сгрузилось, идем в каталог Downloads и распаковываем файл _wg37-main.zip на наш жесткий диск (обычно C:).
5. Поскольку все пути проекта относительные, то можно смело переименовать развернутый каталог _wg37-main по своему усмотрению.
6. Запускаем Visual Studio,
7. Выбираем “Open a project or solution”.
8. Открываем _wg37-main\a997modeler\p_windows\p_windows.sln
9. ВАЖНО: Убедитесь, что выбранная конфигурация (в вернем VS меню) установлена в x86.
10. Компиляция и запуск (зеленая стрелка). Убедитесь, что работает.
11. Пере-запускаем Visual Studio,
12. Выбираем “Open a project or solution”.
13. Открываем _wg37-main\a997modeler\p_android\p_android.sln
14. ВАЖНО: Убедитесь, что выбранная конфигурация (в вернем VS меню) установлена в ARM64.
15. Включаем наш Android, разблокируем, подключаем к компу, разрешаем debugging.
16. Компиляция и запуск (зеленая стрелка). Убедитесь, что работает.
Всё.
Теперь наши с Вами среды не просто совместимые, а идентичные.
Ну и на случай, если Вам когда-то понадобится:
Скажем (как в моем случае) у Вас есть каталог/проект, который Вы хотите опубликовать на GitHub-е.
Во-первых, нужен GitHub account. Идем на https://github.com/ и делаем sign up.
Во-вторых, нужен репозиторий.
Жмем на “+” в верхнем правом углу и добавляем New repository.
Чтобы на этом этапе он был полностью пустым, не выбирайте readmi и gitignore опции. Лучше создать их локально и потом залить на GitHub вместе со всем остальным.
На всякий случай, мой README.md файл выглядит так:
# _wg37
<a href="https://writingagame.com/">WritingAGame.com</a> Chapter 37
<br />
<b>GameDev, 3D, cross-platform, C++, Visual Studio 2022, Android, Windows, OpenGL ES 3.2</b>
<br />
<a href="https://www.youtube.com/watch?v=R8ZICjQ5ABQ">Demo</a>
<br />
Windows solution, <b>x86</b>: <br />
<i>_wg37\a997modeler\p_windows\p_windows.sln</i>
<br />
Android solution, <b>ARM64</b>: <br />
<i>_wg37\a997modeler\p_android\p_android.sln</i>
<br />
Мой .gitignore файл – просто копия GitHub-овского шаблона для Visual Studio:
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
В-третьих, нужно установить сам git на Ваш комп.
Идем на https://git-scm.com/ -> Downloads -> Windows, и т.д.
Когда установлен, идем в Windows File Explorer, right-click на Ваш каталог и выбираем Git Bash Here.
Откроется черный экран с командной строкой.
Вы уже в Вашем каталоге. Набираем
git init
Это создаст под-каталог .git.
Ветка по умолчанию называется master, в то время как на GitHub-е ветка по умолчанию называется main.
Чтобы не морочиться потом с мержеванием (слиянием) веток, я создал ветку main локально, взяв историю из master-а. Команда:
git branch -m master main
Следующая команда добавит содержимое Вашего каталога в Ваш локальный git (за исключением того что упомянуто в файле .gitignore):
git add .
Потом надо сделать первый commit:
git commit -m "initial commit"
Теперь нам надо привязать удаленный GitHub репозиторий к нашему локальному каталогу:
git remote add origin https://github.com/your_git_account/your_repository_name
И залить локальный каталог в удаленный GitHub репозиторий:
git push -u origin main
И всё! Теперь он “в эфире”.
Сейчас модель состоит из 316 вертексов и 144 треугольников (432 индекса), что не так уж и много по сравнению с 3D редакторами. Но все-таки, откуда СТОЛЬКО-то??
Ну, сама коробка например:
Итого – 96 вертексов.
Так что, 316 вертексов – вполне оправдано.
Однако, многие из них на самом деле одинаковые. Например, углы, где каждая из 3-х вершин дублируется

То есть, не просто дублируется, а аж в 4-х экземплярах !
Наша следующая задача – выявить и убрать такие повторы.
Сделаем это в классе ModelBuilder1base, в функции buildSingleDrawJob(..).
1. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
2. Заменим 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<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);
};
3. Заменим ModelBuilder1base.cpp код на:
#include "ModelBuilder1base.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder1base::~ModelBuilder1base() {
releaseGroup(this);
//clear all vectors
int itemsN = vertices.size();
for (int i = 0; i < itemsN; i++)
delete vertices.at(i);
vertices.clear();
itemsN = triangles.size();
for (int i = 0; i < itemsN; i++)
delete triangles.at(i);
triangles.clear();
itemsN = vShapesStack.size();
for (int i = 0; i < itemsN; i++)
delete vShapesStack.at(i);
vShapesStack.clear();
itemsN = groupsStack.size();
for (int i = 0; i < itemsN; i++)
delete groupsStack.at(i);
groupsStack.clear();
if (pCurrentGroup != NULL)
delete pCurrentGroup;
if (pLastClosedGroup != NULL)
delete pLastClosedGroup;
itemsN = materialsList.size();
for (int i = 0; i < itemsN; i++)
delete materialsList.at(i);
materialsList.clear();
subjNumbersList.clear();
}
int ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {
pMB->usingSubjN = subjN;
int itemsN = pMB->subjNumbersList.size();
bool newN = true;
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (pMB->subjNumbersList.at(i) == subjN) {
newN = false;
break;
}
if (newN)
pMB->subjNumbersList.push_back(subjN);
return subjN;
}
int ModelBuilder1base::getMaterialN(ModelBuilder1base* pMB, Material* pMT) {
int itemsN = pMB->materialsList.size();
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
return i;
}
//if here - add new material to the list
Material* pMTnew = new Material(*pMT);
pMB->materialsList.push_back(pMTnew);
return itemsN;
}
int ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
//indexes: NorthWest, NorthEast, SouthWest,SouthEast
if (n % 2 == 0) { //even number
addTriangle(pMB, nNW, nSW, nNE);
addTriangle(pMB, nNE, nSW, nSE);
}
else { //odd number
addTriangle(pMB, nNW, nSE, nNE);
addTriangle(pMB, nNW, nSW, nSE);
}
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {
Triangle01* pTR = new Triangle01();
pMB->triangles.push_back(pTR);
pTR->idx[0] = i0;
pTR->idx[1] = i1;
pTR->idx[2] = i2;
pTR->subjN = pMB->usingSubjN;
pTR->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pTR->marks, 124, pMB->pCurrentGroup->marks);
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
Vertex01* pVX = new Vertex01();
pMB->vertices.push_back(pVX);
pVX->aPos[0] = kx;
pVX->aPos[1] = ky;
pVX->aPos[2] = kz;
//normal
pVX->aNormal[0] = nx;
pVX->aNormal[1] = ny;
pVX->aNormal[2] = nz;
pVX->subjN = pMB->usingSubjN;
pVX->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pVX->marks, 124, pMB->pCurrentGroup->marks);
return pMB->vertices.size() - 1;
}
int ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*>* pGameSubjs) {
int totalSubjsN = pMB->subjNumbersList.size();
if (totalSubjsN < 1) {
pMB->subjNumbersList.push_back(-1);
totalSubjsN = 1;
}
int totalMaterialsN = pMB->materialsList.size();
if (totalSubjsN < 2 && totalMaterialsN < 2) {
//simple single DrawJob
Material* pMT = pMB->materialsList.at(0);
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(0);
if (gsN >= 0)
pGS = pGameSubjs->at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
buildSingleDrawJob(pMT, &pMB->vertices, &pMB->triangles);
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return 1;
}
int totalVertsN = pMB->vertices.size();
int totalTrianglesN = pMB->triangles.size();
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
pVX->flag = 0;
}
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
pTR->flag = 0;
}
int addedDJs = 0;
for (int sN = 0; sN < totalSubjsN; sN++) {
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(sN);
if (gsN >= 0)
pGS = pGameSubjs->at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
Material* pMT = pMB->materialsList.at(mtN);
std::vector<Vertex01*> useVertices;
std::vector<Triangle01*> useTriangles;
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag != 0)
continue;
if (pVX->subjN != gsN)
continue;
if (pVX->materialN != mtN)
continue;
//if here - make a copy
pVX->altN = useVertices.size();
Vertex01* pVX2 = new Vertex01(*pVX);
useVertices.push_back(pVX2);
pVX->flag = 1;
if (pVX->endOfSequence > 0) {
//rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, &useVertices, &useTriangles);
addedDJs++;
//clear and proceed to next sequence
int useVerticesN = useVertices.size();
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
}
}
int useVerticesN = useVertices.size();
if (useVerticesN < 1)
continue; //to next material
//pick triangles
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
if (pTR->flag != 0)
continue;
if (pTR->subjN != gsN)
continue;
if (pTR->materialN != mtN)
continue;
//if here - make a copy
Triangle01* pTR2 = new Triangle01(*pTR);
useTriangles.push_back(pTR2);
pTR->flag = 1;
}
rearrangeArraysForDrawJob(&pMB->vertices, &useVertices, &useTriangles);
buildSingleDrawJob(pMT, &useVertices, &useTriangles);
useVerticesN = useVertices.size();
addedDJs++;
//clear all for next material
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
int useTrianglesN = useTriangles.size();
for (int i = 0; i < useTrianglesN; i++)
delete useTriangles.at(i);
useTriangles.clear();
}
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
}
return addedDJs;
}
int ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles) {
int totalVertsN = pVertices->size();
if (totalVertsN < 1)
return 0;
if (DrawJob::lineWidthIsImportant(pMT->primitiveType)) {
if (strcmp(pMT->shaderType, "wire") == 0)
finalizeLine(pVertices);
}
else {
optimizeMesh(pVertices, pTriangles);
totalVertsN = pVertices->size();
}
if (pMT->uTex2nm >= 0)
calculateTangentSpace(pVertices, pTriangles);
pMT->pickShaderNumber();
DrawJob* pDJ = new DrawJob();
//copy material to DJ
memcpy(&pDJ->mt, pMT, sizeof(Material));
//calculate VBO element size (stride) and variables offsets in VBO
int VBOid = DrawJob::newBufferId();
int stride = 0;
pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
//create an array for VBO
int bufferSize = totalVertsN * stride;
float* vertsBuffer = new float[bufferSize];
//fill vertsBuffer
Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
int floatSize = sizeof(float);
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pVertices->at(vN);
int idx = vN * stride / floatSize;
//pick data from vertex and move to the buffer
memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
if (pSh->l_aNormal >= 0) //normal
memcpy(&vertsBuffer[idx + pDJ->aNormal.offset / floatSize], pVX->aNormal, 3 * floatSize);
if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
if (pSh->l_aTuv2 >= 0) //attribute TUV2 (normal maps)
memcpy(&vertsBuffer[idx + pDJ->aTuv2.offset / floatSize], pVX->aTuv2, 2 * floatSize);
if (pSh->l_aTangent >= 0)
memcpy(&vertsBuffer[idx + pDJ->aTangent.offset / floatSize], pVX->aTangent, 3 * floatSize);
if (pSh->l_aBinormal >= 0)
memcpy(&vertsBuffer[idx + pDJ->aBinormal.offset / floatSize], pVX->aBinormal, 3 * floatSize);
}
//buffer is ready, create VBO
glBindBuffer(GL_ARRAY_BUFFER, VBOid);
glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
delete[] vertsBuffer;
pDJ->pointsN = totalVertsN;
int totalTrianglesN = pTriangles->size();
if (totalTrianglesN > 0) {
//create EBO
int totalIndexesN = totalTrianglesN * 3;
//create buffer
GLushort* indexBuffer = new GLushort[totalIndexesN];
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pTriangles->at(tN);
int idx = tN * 3;
indexBuffer[idx] = (GLushort)pTR->idx[0];
indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
}
//buffer is ready, create IBO
pDJ->glEBOid = DrawJob::newBufferId();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
delete[] indexBuffer;
pDJ->pointsN = totalIndexesN;
}
//create and fill vertex attributes array (VAO)
pDJ->buildVAO();
return 1;
}
int ModelBuilder1base::rearrangeArraysForDrawJob(std::vector<Vertex01*>* pAllVertices, std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles) {
int totalTrianglesN = pUseTriangles->size();
if (totalTrianglesN < 1)
return 0;
//replace triangle original indices by new numbers saved in original vertices altN
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pUseTriangles->at(tN);
for (int i = 0; i < 3; i++) {
Vertex01* pVX0 = pAllVertices->at(pTR->idx[i]);
pTR->idx[i] = pVX0->altN;
}
}
return 1;
}
int ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {
//moves and rotates vertex group
//rotation angles are set in degrees
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_translate(transformMatrix, kX, kY, kZ);
//rotation order: Z-X-Y
if (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);
if (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);
if (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
}
return 1;
}
int ModelBuilder1base::calculateTangentSpace(std::vector<Vertex01*>* pUseVertices, std::vector<Triangle01*>* pUseTriangles) {
int totalVertsN = pUseVertices->size();
if (totalVertsN < 1)
return 0;
int totalTrianglesN = pUseTriangles->size();
//assuming that GL_TRIANGLES
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = pUseVertices->at(vN);
pV->flag = 0;
}
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pUseVertices->at(vN);
if (pVX->flag != 0)
continue;
Triangle01* pT = NULL;
for (int tN = 0; tN < totalTrianglesN; tN++) {
pT = pUseTriangles->at(tN);
bool haveTriangle = false;
for (int i = 0; i < 3; i++)
if (pT->idx[i] == vN) {
haveTriangle = true;
break;
}
if (haveTriangle)
break;
}
Vertex01* pV[3];
for (int i = 0; i < 3; i++)
pV[i] = pUseVertices->at(pT->idx[i]);
float dPos1[3];
float dPos2[3];
float dUV1[2];
float dUV2[2];
for (int i = 0; i < 3; i++) {
dPos1[i] = pV[1]->aPos[i] - pV[0]->aPos[i];
dPos2[i] = pV[2]->aPos[i] - pV[0]->aPos[i];
}
for (int i = 0; i < 2; i++) {
dUV1[i] = pV[1]->aTuv2[i] - pV[0]->aTuv2[i];
dUV2[i] = pV[2]->aTuv2[i] - pV[0]->aTuv2[i];
}
float tangent[3];
float binormal[3];
float divider = dUV1[0] * dUV2[1] - dUV1[1] * dUV2[0];
if (divider == 0) {
v3set(tangent, 1, 0, 0);
v3set(binormal, 0, -1, 0);
}
else {
float r = 1.0f / divider;
for (int i = 0; i < 3; i++) {
tangent[i] = (dPos1[i] * dUV2[1] - dPos2[i] * dUV1[1]) * r;
binormal[i] = -(dPos2[i] * dUV1[0] - dPos1[i] * dUV2[0]) * r;
}
vec3_norm(tangent, tangent);
vec3_norm(binormal, binormal);
}
//add to all 3 vertices
for (int n = 0; n < 3; n++) {
if (pV[n]->flag > 0)
continue;
v3copy(pV[n]->aTangent, tangent);
v3copy(pV[n]->aBinormal, binormal);
pV[n]->flag = 1;
}
}
//normalize tangent and binormal around normal
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = pUseVertices->at(vN);
float v3out[3];
//tangent
vec3_mul_cross(v3out, pV->aNormal, pV->aBinormal);
if (v3dotProduct(pV->aTangent, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aTangent, v3out);
//binormal
vec3_mul_cross(v3out, pV->aNormal, pV->aTangent);
if (v3dotProduct(pV->aBinormal, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aBinormal, v3out);
}
return 1;
}
void ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {
Group01* pPrevGroup = pMB->pCurrentGroup;
if (pMB->pCurrentGroup != NULL)
pMB->groupsStack.push_back(pMB->pCurrentGroup);
pMB->pCurrentGroup = new Group01();
pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
//marks
if(pPrevGroup != NULL)
if (strcmp(pPrevGroup->marks, "") != 0)
myStrcpy_s(pMB->pCurrentGroup->marks, 124, pPrevGroup->marks);
}
void ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {
if (pMB->pLastClosedGroup != NULL)
delete pMB->pLastClosedGroup;
pMB->pLastClosedGroup = pMB->pCurrentGroup;
if (pMB->groupsStack.size() > 0) {
pMB->pCurrentGroup = pMB->groupsStack.back();
pMB->groupsStack.pop_back();
}
else
pMB->pCurrentGroup = NULL;
}
int ModelBuilder1base::finalizeLine(std::vector<Vertex01*>* pVerts, int lineStartsAt, int lineEndsAt) {
if (lineEndsAt <= 0)
lineEndsAt = pVerts->size() - 1;
Vertex01* pV0 = pVerts->at(lineStartsAt);
Vertex01* pV2 = pVerts->at(lineEndsAt);
bool closedLine = false;
if (v3match(pV0->aPos, pV2->aPos))
closedLine = true;
for (int vN = lineStartsAt; vN <= lineEndsAt; vN++) {
Vertex01* pV = pVerts->at(vN);
//prev point
if (vN == lineStartsAt) {
//first point
if (closedLine)
pV0 = pVerts->at(lineEndsAt);
else
pV0 = NULL;
}
else
pV0 = pVerts->at(vN - 1);
//next point
if (vN == lineEndsAt) {
//last point
if (closedLine)
pV2 = pVerts->at(lineStartsAt);
else
pV2 = NULL;
}
else
pV2 = pVerts->at(vN + 1);
//distances to neighbor points
float distFromPrev = 0;
float dirFromPrev[3] = { 0,0,0 };
if (pV0 != NULL) {
distFromPrev = v3lengthFromTo(pV0->aPos, pV->aPos);
v3dirFromTo(dirFromPrev, pV0->aPos, pV->aPos);
}
float distToNext = 0;
float dirToNext[3] = { 0,0,0 };
if (pV2 != NULL) {
distToNext = v3lengthFromTo(pV->aPos, pV2->aPos);
v3dirFromTo(dirToNext, pV->aPos, pV2->aPos);
}
float distTotal = distFromPrev + distToNext;
float kPrev = distFromPrev / distTotal;
float kNext = distToNext / distTotal;
if (kPrev > kNext * 3)
v3copy(pV->aNormal, dirFromPrev);
else if (kNext > kPrev * 3)
v3copy(pV->aNormal, dirToNext);
else
for (int i = 0; i < 3; i++)
pV->aNormal[i] = kPrev * dirFromPrev[i] + kNext * dirToNext[i];
vec3_norm(pV->aNormal, pV->aNormal);
}
return 1;
}
int ModelBuilder1base::optimizeMesh(std::vector<Vertex01*>* pVertices, std::vector<Triangle01*>* pTriangles) {
int trianglesN0 = pTriangles->size();
if (trianglesN0 == 0)
return 0;
int vertsN0 = pVertices->size();
//clear verts first for comparison
for (int vN = 0; vN < vertsN0; vN++) {
Vertex01* pV = pVertices->at(vN);
myStrcpy_s(pV->marks, 124, "");
pV->altN = -1;
pV->flag = 0;
pV->aTangent[0] = 0;
//round up
for (int i = 0; i < 3; i++) {
pV->aPos[i] = round(pV->aPos[i] * 1000.0) / 1000.0;
pV->aNormal[i] = round(pV->aNormal[i] * 1000.0) / 1000.0;
}
}
//find the same verts
int matchesN = 0;
for (int vN = 0; vN < vertsN0-1; vN++) {
Vertex01* pV = pVertices->at(vN);
if (pV->flag < 0)
continue;
for (int vN2 = vN+1; vN2 < vertsN0; vN2++) {
Vertex01* pV2 = pVertices->at(vN2);
if (pV2->flag < 0)
continue;
if (memcmp(pV, pV2, sizeof(Vertex01)) != 0)
continue;
//if here - verts are equal
pV2->flag = -1;
matchesN++;
//change refs in useTriangles from vN2 to vN
for (int tN = 0; tN < trianglesN0; tN++) {
Triangle01* pT = pTriangles->at(tN);
for (int i = 0; i < 3; i++)
if (pT->idx[i] == vN2)
pT->idx[i] = vN;
}
}
}
if (matchesN == 0)
return 0;
//unflag all verts
for (int vN = 0; vN < vertsN0 - 1; vN++) {
Vertex01* pV = pVertices->at(vN);
pV->flag = -1;
}
//flag verts in use
for (int tN = 0; tN < trianglesN0; tN++) {
Triangle01* pT = pTriangles->at(tN);
for (int i = 0; i < 3; i++) {
int vN = pT->idx[i];
Vertex01* pV = pVertices->at(vN);
pV->flag = 0;
}
}
//save original useVertices copy
std::vector<Vertex01*> oldVertices;
for (int vN = 0; vN < vertsN0; vN++) {
Vertex01* pV = pVertices->at(vN);
oldVertices.push_back(pV);
}
pVertices->clear();
//copy back only verts in use
for (int vN = 0; vN < vertsN0; vN++) {
Vertex01* pV = oldVertices.at(vN);
if (pV->flag < 0)
continue;
pV->altN = pVertices->size();
pVertices->push_back(pV);
}
//re-factor triangles
rearrangeArraysForDrawJob(&oldVertices, pVertices, pTriangles);
oldVertices.clear();
return pVertices->size();
}
4. Компиляция и запуск.
Картинка та же, но теперь вместо 316 вертексов у нас всего 219, на 31% меньше, даже лучше чем я ожидал.
Подробности:
Не по теме:
Пока трудился над этой главой, запустил одну пленку (без коробки, для отладки). Думаю, заслуживает отдельного видео:
Впечатляет, правда?
Тут нам даже не придется писать новых шейдеров. Для пленки мы возьмем наш “зеркальный” шейдер (как для позолоченных принтов) с белым шумом в качестве главной текстуры (uTex0) и “странслируем” ее в полупрозрачную 8×1 текстуру glass01.bmp (uTex3), имитирующую стеклянное отражение. Это просто белая прозрачная текстура с 1 НЕпрозрачным пикселем (сгенерирована программно).
1. Сгрузим glass01.bmp файл здесьи сохраним его в C:\CPP\engine\dt\common\img\materials\glass01.bmp
Обновленный model descriptor:
2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\a997modeler\dt\models\misc\marlboro01\root01.txt
<texture_as="tx0" src="marlboro03small.png" ckey="#00ff00"/>
<mt_type="phong" uTex0_use="tx0" />
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<a="front v" xywh="2,1,323,495" mark="box_front"/>
<a="back v" xywh="2,1,323,495" mark="box_back"/>
<a="right all" xywh="327,1,128,495" mark="box_right"/>
<a="left all" xywh="457,1,128,495" mark="box_left"/>
<a="top" xywh="588,1,323,133"/>
<a="bottom" xywh="587,136,324,134"/>
//golden prints
<vs="box" whl="55.1,85.1,23.1" />
<texture_as="whitenoise" src="/dt/common/img/whitenoise/wn64_blur3.bmp"/>
<texture_as="gold" src="/dt/common/img/materials/gold02roman.bmp" />
<mt_type="mirror" uAlphaBlending uTex1mask_use="tx0" uTex1alphaChannelN=1 uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
//side golden prints
<a="right" xywh="342,12,101,10" whl="x,1.8,18.1" pxyz="x,39.8, -0.3" /> //Please do not litter
<a="right" xywh="339,144,105,89" whl="x,15.35,18.9" pxyz="x,10.3,-0.12" /> //For special offers...
<a="left" xywh="475,15,95,48" whl="x,8.4,17" pxyz="x,36, 0.3" /> //Underage sale...
//front prints
<group>
//bottom golden print "20 class a..."
<a="front" xywh="20,498,289,13" whl="47.5,2,x" pxyz="1,-36,x" />
//blazon/emblem
<mt_type="mirror" uAlphaBlending uTex2nm_use="tx0" uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
<a="front" xywh2nm="589,415,128,94" whl="20.7,16,x" pxyz="0.3,6.1,x" /> //emblem
//"Marlboro
<mt_type="phong" uAlphaBlending uTex2nm_use="tx0" uColor="#1E211E" />
<a="front" xywh2nm="590,275,301,136" whl="49.2,23.3,x" pxyz="0.21,-18,x" /> //marlboro
</group>
<clone ay=180 />
//joint (slit) between the pack and the lid
<group>
<mt_adjust uTex2nm_use="tx0" >
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_right" onThe="right" py=24.6 az=31 />
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_left" onThe="left" py=24.6 az=-31 />
<a2mesh wh="53,1" xywh2nm="582,497,1,4" all markedAs="box_front" py=17.8 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=23.5 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=-23.5 />
</mt_adjust>
</group sizeD="0.1,0,0.1">
//sealing ribbon
<mt_type="wire" lineWidth=1.5 uColor="130,90,0" zBufferUpdate=no />
<line>
<p pxyz="-27.6,16.5 ,0" />
<p dz=10.5 /> //left side half
<p dxyz="1.1,0,1.1" /> //front left rib
<p dx=53 /> //front side
<p dxyz="1.1,0,-1.1" /> //front right rib
<p dz=-21 /> //right side
<p dxyz="-1.1,0,-1.1" /> //back right rib
<p dx=-53 /> //back side
<p dxyz="-1.1,0,1.1" /> //back left rib
<p dz=16 /> //left half
<p dxyz="-1,0,5" /> //ribbon "tail"
<p dz=1 />
</line >
//clear-film
<texture_as="glass" src="/dt/common/img/materials/glass01.bmp" />
<texture_as="whitenoise2" src="/dt/common/img/whitenoise/wn64_blur1.bmp"/>
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<group>
<mt_type="mirror" uAlphaFactor=0.9 uTex0_use="whitenoise2" uTex0translateChannelN=1 uTex3_use="glass" />
<a="front h,back h,right,left" />
<mt_type="mirror" uAlphaFactor=0.9 uTex0_use="whitenoise2" uTex0translateChannelN=1 uTex3_use="glass" uTex2nm_use="tx0" />
<a="top all" xywh2nm="724,420,223,90"/>
<a="bottom all" xywh2nm="724,420,223,90" mark="film_bottom"/>
</group sizeD="0.2,0.1,0.2" >
//Excise stamp
<mt_type="phong" uTex0_use="tx0" />
<a2mesh wh="22,13" xywh="916,3,100,57" flip=180 all markedAs="film_bottom" onThe="bottom" pxyz="-15,4,0" az=-5 detachBy=0.05 />
Новый код – с 57-й строки.
Обратите внимание:
Поскольку у нас появились полу-прозрачные поверхности, это поднимает новую проблему – прозрачные поверхности не должны переписывать (update) z-buffer на случай если придется что-то рендрить под (за) такой поверхностью. Для этого в классе Material добавлены 2 новые переменные:
3. Заменим Material.h код на:
#pragma once
#include "MyColor.h"
#include <string>
class Material
{
public:
char shaderType[20] = "";
int shaderN = -1;
int primitiveType = GL_TRIANGLES;
MyColor uColor;
int uTex0 = -1;
int uTex1mask = -1;
int uTex2nm = -1;
int uTex3 = -1;
int uTex1alphaChannelN = 3; //default - alpha channel for mask
int uTex1alphaNegative = 0; //default - alpha channel not negative
int uTex0translateChannelN = -1; //translate tex0 to tex3 by channelN. Default -1 - don't translate
int uAlphaBlending = 0; //for semi-transparency
float uAlphaFactor = 1; //for semi-transparency
float uAmbient = 0.4f; //ambient light
//specular light parameters
float uSpecularIntencity = 0.8f;
float uSpecularMinDot = 0.8f;
float uSpecularPowerOf = 20.0f;
float lineWidth = 1;
int zBuffer = 1;
int zBufferUpdate = 1;
public:
int pickShaderNumber() { return pickShaderNumber(this); };
static int pickShaderNumber(Material* pMT);
void setShaderType(std::string needType) { setShaderType(this, needType); };
static void setShaderType(Material* pMT, std::string needType) { myStrcpy_s(pMT->shaderType, 20, (char*)needType.c_str()); };
void clear() { clear(this); };
static void clear(Material* pMT);
int assignShader(std::string needType) { return assignShader(this, needType); };
static int assignShader(Material* pMT, std::string needType);
};
Эти новые переменные будут использоваться классом DrawJob для под-настройки рендринга. Функция – executeDrawJob(..).
4. Заменим DrawJob.cpp код на:
#include "DrawJob.h"
#include "platform.h"
#include "utils.h"
#include "Shader.h"
#include "Texture.h"
//static arrays (vectors) of all loaded DrawJobs, VBO ids
std::vector<DrawJob*> DrawJob::drawJobs;
std::vector<unsigned int> DrawJob::buffersIds;
DrawJob::DrawJob() {
drawJobs.push_back(this);
}
DrawJob::~DrawJob() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (glVAOid > 0)
glDeleteVertexArrays(1, &glVAOid);
}
int DrawJob::newBufferId() {
unsigned int bufferId;
glGenBuffers(1, &bufferId);
buffersIds.push_back(bufferId);
return (int)bufferId;
}
unsigned int activeVBOid;
int DrawJob::buildVAOforShader(DrawJob* pDJ, int shaderN) {
//delete VAO if exists already
if (pDJ->glVAOid > 0) {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteVertexArrays(1, &(pDJ->glVAOid));
}
glGenVertexArrays(1, &pDJ->glVAOid);
glBindVertexArray(pDJ->glVAOid);
//open shader descriptor to access variables locations
Shader* pShader = Shader::shaders.at(pDJ->mt.shaderN);
activeVBOid = 0;
attachAttribute(pShader->l_aPos, 3, &pDJ->aPos);
attachAttribute(pShader->l_aNormal, 3, &pDJ->aNormal);
attachAttribute(pShader->l_aTuv, 2, &pDJ->aTuv);
attachAttribute(pShader->l_aTuv2, 2, &pDJ->aTuv2); //for normal map
attachAttribute(pShader->l_aTangent, 3, &pDJ->aTangent); //for normal map
attachAttribute(pShader->l_aBinormal, 3, &pDJ->aBinormal); //for normal map
if (pDJ->glEBOid > 0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return 1;
}
int DrawJob::attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAR) {
if (varLocationInShader < 0)
return 0; //not used in this shader
if (pAR->glVBOid == 0) {
mylog("ERROR in DrawJob::attachAttribute, nk such attribute/VBO\n");
return -1;
}
glEnableVertexAttribArray(varLocationInShader);
if (activeVBOid != pAR->glVBOid) {
activeVBOid = pAR->glVBOid;
//attach input stream data
glBindBuffer(GL_ARRAY_BUFFER, activeVBOid);
}
glVertexAttribPointer(varLocationInShader, attributeSizeInFloats, GL_FLOAT, GL_FALSE, pAR->stride, (void*)(long)pAR->offset);
return 1;
}
int DrawJob::executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV3x3, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize, Material* pMt) {
if (pMt == NULL)
pMt = &(pDJ->mt);
glBindVertexArray(pDJ->glVAOid);
Shader* pShader = Shader::shaders.at(pMt->shaderN);
glUseProgram(pShader->GLid);
//input uniforms
glUniformMatrix4fv(pShader->l_uMVP, 1, GL_FALSE, (const GLfloat*)uMVP);
if (pShader->l_uMV3x3 >= 0)
glUniformMatrix3fv(pShader->l_uMV3x3, 1, GL_FALSE, (const GLfloat*)uMV3x3);
if (pShader->l_uMM >= 0)
glUniformMatrix4fv(pShader->l_uMM, 1, GL_FALSE, (const GLfloat*)uMM);
if (pShader->l_uVectorToLight >= 0)
glUniform3fv(pShader->l_uVectorToLight, 1, (const GLfloat*)uVectorToLight);
if (pShader->l_uCameraPosition >= 0)
glUniform3fv(pShader->l_uCameraPosition, 1, (const GLfloat*)uCameraPosition);
//attach textures
if (pShader->l_uTex0 >= 0) {
int textureId = Texture::getGLid(pMt->uTex0);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE0); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
glUniform1i(pShader->l_uTex0, 0);
}
if (pShader->l_uTex1mask >= 0) {
int textureId = Texture::getGLid(pMt->uTex1mask);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE1); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1.
glUniform1i(pShader->l_uTex1mask, 1);
}
if (pShader->l_uTex2nm >= 0) {
int textureId = Texture::getGLid(pMt->uTex2nm);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE2); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 2.
glUniform1i(pShader->l_uTex2nm, 2);
}
if (pShader->l_uTex0translateChannelN >= 0) {
glUniform1i(pShader->l_uTex0translateChannelN, pMt->uTex0translateChannelN);
if (pShader->l_uTex3 >= 0 && pMt->uTex3 >= 0) {
int textureId = Texture::getGLid(pMt->uTex3);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE3); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 3.
glUniform1i(pShader->l_uTex3, 3);
}
}
//material uniforms
if (pShader->l_uTex1alphaChannelN >= 0)
glUniform1i(pShader->l_uTex1alphaChannelN, pMt->uTex1alphaChannelN);
if (pShader->l_uTex1alphaNegative >= 0)
glUniform1i(pShader->l_uTex1alphaNegative, pMt->uTex1alphaNegative);
if (pShader->l_uColor >= 0)
glUniform4fv(pShader->l_uColor, 1, pMt->uColor.forGL());
if (pShader->l_uAlphaFactor >= 0)
glUniform1f(pShader->l_uAlphaFactor, pMt->uAlphaFactor);
if (pShader->l_uAlphaBlending >= 0)
glUniform1i(pShader->l_uAlphaBlending, pMt->uAlphaBlending);
if (pShader->l_uAmbient >= 0)
glUniform1f(pShader->l_uAmbient, pMt->uAmbient);
if (pShader->l_uSpecularIntencity >= 0)
glUniform1f(pShader->l_uSpecularIntencity, pMt->uSpecularIntencity);
if (pShader->l_uSpecularMinDot >= 0)
glUniform1f(pShader->l_uSpecularMinDot, pMt->uSpecularMinDot);
if (pShader->l_uSpecularPowerOf >= 0)
glUniform1f(pShader->l_uSpecularPowerOf, pMt->uSpecularPowerOf);
//adjust render settings
if (lineWidthIsImportant(pMt->primitiveType)) {
float lw = sizeUnitPixelsSize * pMt->lineWidth;
glLineWidth(lw);
}
if (pMt->zBuffer > 0) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}
else
glDisable(GL_DEPTH_TEST);
if (pMt->zBufferUpdate > 0)
glDepthMask(GL_TRUE);
else
glDepthMask(GL_FALSE);
if (pShader->l_uAlphaBlending >= 0 && pMt->uAlphaBlending > 0) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else
glDisable(GL_BLEND);
//execute
if (pDJ->glEBOid == 0) {
glDrawArrays(pMt->primitiveType, 0, pDJ->pointsN);
}
else { //use EBO
glDrawElements(pMt->primitiveType, pDJ->pointsN, GL_UNSIGNED_SHORT, 0);
}
glBindVertexArray(0);
return 1;
}
int DrawJob::cleanUp() {
int itemsN = drawJobs.size();
//delete all drawJobs
for (int i = 0; i < itemsN; i++) {
DrawJob* pDJ = drawJobs.at(i);
delete pDJ;
}
drawJobs.clear();
//delete Buffers
itemsN = buffersIds.size();
//delete all buffers
for (int i = 0; i < itemsN; i++) {
unsigned int id = buffersIds.at(i);
glDeleteBuffers(1, &id);
}
buffersIds.clear();
return 1;
}
int DrawJob::setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid) {
//sets desirable offsets and stride according to given shader needs
//assuming that we have 1 single VBO
Shader* pSh = Shader::shaders.at(shaderN);
int stride = 0;
pDJ->aPos.offset = 0; //attribute o_aPos, always 0
stride += sizeof(float) * 3; //aPos size - 3 floats (x,y,z)
if (pSh->l_aNormal >= 0) { //attribute normal
pDJ->aNormal.offset = stride;
stride += sizeof(float) * 3;
}
if (pSh->l_aTuv >= 0) { //attribute TUV (texture coordinates)
pDJ->aTuv.offset = stride; //attribute TUV (texture coordinates)
stride += sizeof(float) * 2;
}
if (pSh->l_aTuv2 >= 0) { //for normal map
pDJ->aTuv2.offset = stride;
stride += sizeof(float) * 2;
}
if (pSh->l_aTangent >= 0) { //for normal map
pDJ->aTangent.offset = stride;
stride += sizeof(float) * 3;
}
if (pSh->l_aBinormal >= 0) { //for normal map
pDJ->aBinormal.offset = stride;
stride += sizeof(float) * 3;
}
*pStride = stride;
//add stride and VBOid to all attributes
AttribRef* pAR = NULL;
pAR = &pDJ->aPos; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aNormal; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTuv; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTuv2; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTangent; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aBinormal; pAR->glVBOid = VBOid; pAR->stride = stride;
return 1;
}
bool DrawJob::lineWidthIsImportant(int primitiveType) {
if (primitiveType == GL_TRIANGLES) return false;
if (primitiveType == GL_TRIANGLE_STRIP) return false;
if (primitiveType == GL_TRIANGLE_FAN) return false;
return true;
}
Чтение/установки – в ModelLoader-е, в функции fillProps_mt(..).
5. Заменим 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
int subjN = pSubjsVector0->size();
GameSubj* pGS = theGame.newGameSubj(subjClass);
pSubjsVector0->push_back(pGS);
//pGS->djStartN = DrawJob::drawJobs.size();
ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
processSource(pML);
delete pML;
//pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return subjN;
}
int ModelLoader::setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr) {
if (!varExists(varName, tagStr))
return 0;
std::string str0 = getStringValue(varName, tagStr);
if (intHashMap.find(str0) == intHashMap.end()) {
mylog("ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\n", varName.c_str(), tagStr.c_str());
return -1;
}
*pInt = intHashMap[getStringValue(varName, tagStr)];
return 1;
}
int ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {
ModelBuilder* pMB = pML->pModelBuilder;
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("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());
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;
}
TheGame.cpp тоже задет. Поскольку мы теперь отключаем z-buffer для каких-то DrawJobs, теперь нам надо убедиться, что он включен прежде чем чистить z-buffer (строка 50).
6.Заменим TheGame.cpp код на:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
#include "ModelLoader.h"
extern std::string filesRoot;
extern float degrees2radians;
std::vector<GameSubj*> TheGame::gameSubjs;
int TheGame::getReady() {
bExitGame = false;
Shader::loadShaders();
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
int subjN = ModelLoader::loadModel(&gameSubjs, "/dt/models/misc/marlboro01/root01.txt", "");
GameSubj* pGS = gameSubjs.at(subjN);
pGS->name.assign("box1");
pGS->ownSpeed.setDegrees(1.5, 1, 0.5);
pGS->ownCoords.setDegrees(0,30, 0);
//===== set up camera
mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
mainCamera.viewRangeDg = 20;
mainCamera.stageSize[0] = 80;
mainCamera.stageSize[1] = 120;
memcpy(mainCamera.lookAtPoint, pGS->ownCoords.pos, sizeof(float) * 3);
mainCamera.onScreenResize();
//===== set up light
v3set(dirToMainLight, -1, 1, 1);
vec3_norm(dirToMainLight, dirToMainLight);
return 1;
}
int TheGame::drawFrame() {
myPollEvents();
//glClearColor(0.0, 0.0, 0.5, 1.0);
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->ownModelMatrix);
//build Model-View (rotation) matrix for normals
mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());
//convert to 3x3 matrix
float mMV3x3[3][3];
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
mMV3x3[y][x] = mMV4x4[y][x];
//subj's distance from camera
float cameraSpacePos[4];
mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS->ownCoords.pos, 1);
float zDistance = abs(cameraSpacePos[2]);
float cotangentA = 1.0f / tanf(degrees2radians * mainCamera.viewRangeDg / 2.0);
float halfScreenVertSizeInUnits = zDistance / cotangentA;
float sizeUnitPixelsSize = screenSize[1] / 2.0 / 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->ownModelMatrix, 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;
}
GameSubj* TheGame::newGameSubj(std::string subjClass) {
return (new GameSubj());
}
int TheGame::run() {
getReady();
while (!bExitGame) {
drawFrame();
}
cleanUp();
return 1;
}
7. Компиляция и запуск. Результат:
До:

(просто проекции)
После:

С золочеными и тиснеными принтами, со стыком пачка-крышка как normal map, с золотой лентой/пломбой/линией.
А теперь еще и с пленкой и с акцизной маркой.
Теперь модель полностью готова. Наконец, могу спокойно эту пачку выкурить. Или оставить ее на память о Проекте?
1. Скопируем нижеследующий код в Текстовый редактор и сохраним его (overwrite) to/as
C:\CPP\a997modeler\dt\models\misc\marlboro01\root01.txt
<texture_as="tx0" src="marlboro03small.png" ckey="#00ff00"/>
<mt_type="phong" uTex0_use="tx0" />
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<a="front v" xywh="2,1,323,495" mark="box_front"/>
<a="back v" xywh="2,1,323,495" mark="box_back"/>
<a="right all" xywh="327,1,128,495" mark="box_right"/>
<a="left all" xywh="457,1,128,495" mark="box_left"/>
<a="top" xywh="588,1,323,133"/>
<a="bottom" xywh="587,136,324,134"/>
//golden prints
<vs="box" whl="55.1,85.1,23.1" />
<texture_as="whitenoise" src="/dt/common/img/whitenoise/wn64_blur3.bmp"/>
<texture_as="gold" src="/dt/common/img/materials/gold02roman.bmp" />
<mt_type="mirror" uAlphaBlending uTex1mask_use="tx0" uTex1alphaChannelN=1 uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
//side golden prints
<a="right" xywh="342,12,101,10" whl="x,1.8,18.1" pxyz="x,39.8, -0.3" /> //Please do not litter
<a="right" xywh="339,144,105,89" whl="x,15.35,18.9" pxyz="x,10.3,-0.12" /> //For special offers...
<a="left" xywh="475,15,95,48" whl="x,8.4,17" pxyz="x,36, 0.3" /> //Underage sale...
//front prints
<group>
//bottom golden print "20 class a..."
<a="front" xywh="20,498,289,13" whl="47.5,2,x" pxyz="1,-36,x" />
//blazon/emblem
<mt_type="mirror" uAlphaBlending uTex2nm_use="tx0" uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
<a="front" xywh2nm="589,415,128,94" whl="20.7,16,x" pxyz="0.3,6.1,x" /> //emblem
//"Marlboro
<mt_type="phong" uAlphaBlending uTex2nm_use="tx0" uColor="#1E211E" />
<a="front" xywh2nm="590,275,301,136" whl="49.2,23.3,x" pxyz="0.21,-18,x" /> //marlboro
</group>
<clone ay=180 />
//joint (slit) between the pack and the lid
<group>
<mt_adjust uTex2nm_use="tx0" >
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_right" onThe="right" py=24.6 az=31 />
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_left" onThe="left" py=24.6 az=-31 />
<a2mesh wh="53,1" xywh2nm="582,497,1,4" all markedAs="box_front" py=17.8 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=23.5 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=-23.5 />
</mt_adjust>
</group sizeD="0.1,0,0.1">
//sealing ribbon
<mt_type="wire" lineWidth=1.5 uColor="130,90,0" >
<line>
<p pxyz="-27.6,16.5 ,0" />
<p dz=10.5 /> //left side half
<p dxyz="1.1,0,1.1" /> //front left rib
<p dx=53 /> //front side
<p dxyz="1.1,0,-1.1" /> //front right rib
<p dz=-21 /> //right side
<p dxyz="-1.1,0,-1.1" /> //back right rib
<p dx=-53 /> //back side
<p dxyz="-1.1,0,1.1" /> //back left rib
<p dz=16 /> //left half
<p dxyz="-1,0,5" /> //ribbon "tail"
<p dz=1 />
</line >
Обратите внимание на:
Еще понадобится дополнительный код в шейдерах. Также, пользуясь случаем, я решил перенести вычисления HalfVector-а изTheGame.cpp в vertex shader. Раньше мы вычисляли 1 HalfVector для всей модели. Теперь он будет считаться для каждого вертекса. Так будет реалистичнее.
Phong vertex shader:
2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\engine\dt\shaders\phong_v.txt
//#version 320 es
precision lowp float;
uniform mat4 uMVP; // transform matrix (Model-View-Projection)
uniform mat3 uMV3x3; // Model-View matrix (for calculating normals into eye space)
in vec3 aPos; // position attribute (x,y,z)
#if defined(USE_NORMALS)
in vec3 aNormal; // normal attribute (x,y,z)
out vec3 vNormal; // varying normal (to pass to fragment shader)
#endif
#if defined(USE_TUV0)
in vec2 aTuv; //attribute TUV (texture coordinates)
out vec2 vTuv; //varying TUV (pass to fragment shader)
#endif
#if defined(MIRROR)
out vec2 vTuvMirror; //varying TUV (pass to fragment shader)
#endif
#if defined(PHONG)
uniform mat4 uMM; // Model matrix (for vHalfVector for glares)
uniform vec3 uVectorToLight;
uniform vec3 uCameraPosition; //for calculating half vector for glares
uniform float uSpecularIntencity; //for calculating half vector for glares
out vec3 vHalfVector;
#endif
void main(void) {
gl_Position = uMVP * vec4(aPos, 1.0);
#if defined(USE_NORMALS)
// Transform the normal's orientation into eye space.
vNormal = uMV3x3 * aNormal;
#endif
#if defined(USE_TUV0)
vTuv = aTuv;
#endif
#if defined(MIRROR)
vTuvMirror[0] = (gl_Position[0]/gl_Position[3]*0.1+vNormal[0]*0.4)+0.5;
vTuvMirror[1] = -(gl_Position[1]/gl_Position[3]*0.1+vNormal[1]*0.4)+0.5;
#endif
#if defined(PHONG)
if(uSpecularIntencity > 0.0){ //for glares
vec4 vxPos = uMM * vec4(aPos, 1.0); //vertex position
vec3 dirToCamera = normalize(uCameraPosition - vec3(vxPos));
vHalfVector = normalize(dirToCamera + uVectorToLight);
}
#endif
}
Обратите внимание:
Phong fragment shader:
3. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\engine\dt\shaders\phong_f.txt
//#version 320 es
precision lowp float;
out vec4 FragColor; //output pixel color
uniform float uAlphaFactor; //for semi-transparency
uniform int uAlphaBlending; //for semi-transparency
#if defined(USE_NORMALS)
in vec3 vNormal; //normal passed from rasterizer
#endif
#if defined(USE_TEX0)
uniform sampler2D uTex0; //texture id
uniform sampler2D uTex3; //translate texture id
uniform int uTex0translateChannelN;
#else
uniform vec4 uColor;
#endif
#if defined(USE_TUV0)
in vec2 vTuv; //varying TUV (passed from vertex shader)
#endif
#if defined(MIRROR)
in vec2 vTuvMirror; //varying TUV (passed from vertex shader)
#endif
#if defined(OVERMASK)
uniform sampler2D uTex1mask; //texture id
uniform int uTex1alphaChannelN;
uniform int uTex1alphaNegative;
#endif
#if defined(PHONG)
uniform float uAmbient;
uniform float uSpecularIntencity;
uniform float uSpecularMinDot;
uniform float uSpecularPowerOf;
uniform vec3 uVectorToLight;
in vec3 vHalfVector;
#endif
void main(void) {
vec4 outColor;
float alpha = 1.0;
#if defined(OVERMASK)
outColor = texture(uTex1mask, vTuv);
alpha = outColor[uTex1alphaChannelN];
if(uTex1alphaNegative > 0)
alpha = 1.0 - alpha;
if(alpha < 0.5){
if(uAlphaBlending > 0){
if(alpha == 0.0){
discard;
return;
}
}
else{ //no AlphaBlending
discard;
return;
}
}
#endif
#if defined(USE_TEX0)
#if defined(MIRROR)
outColor = texture(uTex0, vTuvMirror);
#else
outColor = texture(uTex0, vTuv);
#endif
if(uTex0translateChannelN >= 0){ //translate channel
vec2 tuv3;
tuv3[0] = outColor[uTex0translateChannelN];
tuv3[1] = 0.0;
outColor = texture(uTex3, tuv3);
}
FragColor = outColor;
#else
FragColor = uColor;
#endif
if(FragColor.a != 1.0){
alpha *= FragColor.a;
if(alpha < 0.5){
if(uAlphaBlending > 0){
if(alpha == 0.0){
discard;
return;
}
}
else{ //no AlphaBlending
discard;
return;
}
}
}
#if defined(USE_NORMALS)
vec3 vNormalNormal = normalize(vNormal);
#endif
#if defined(PHONG)
if(uAmbient<1.0){
// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
// pointing in the same direction then it will get max illumination.
float dotProduct = dot(vNormalNormal, uVectorToLight);
#if defined(WIRE)
if(dotProduct < 0.0)
dotProduct = -dotProduct;
dotProduct = 1.0 - dotProduct;
#endif
// count ambient component
dotProduct += uAmbient;
if(dotProduct < uAmbient)
dotProduct = uAmbient;
// Multiply the color by the lightIntencity illumination level to get final output color.
FragColor *= dotProduct;
}
if(uSpecularIntencity>0.0){
//specular light
// INTENSITY OF THE SPECULAR LIGHT
// DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS
vec3 vNormalHalfVector = normalize(vHalfVector);
float dotProduct = dot(vNormalNormal, vNormalHalfVector);
#if defined(WIRE)
if(dotProduct < 0.0)
dotProduct = -dotProduct;
dotProduct = 1.0 - dotProduct;
#endif
if(dotProduct>uSpecularMinDot){
float specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;
if(specularIntencity > uSpecularIntencity)
specularIntencity = uSpecularIntencity;
FragColor += specularIntencity;
}
}
#endif
if(uAlphaFactor != 1.0)
alpha *= uAlphaFactor;
FragColor.a = alpha;
}
Normal map vertex shader:
4. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\engine\dt\shaders\nm_v.txt
//#version 320 es
precision lowp float;
uniform mat4 uMVP; // transform matrix (Model-View-Projection)
uniform mat3 uMV3x3; // Model-View matrix (for calculating normals into eye space)
uniform mat4 uMM; // Model matrix (for vHalfVector for glares)
in vec3 aPos; // position attribute (x,y,z)
in vec3 aNormal; // normal attribute (x,y,z)
//normal map
in vec3 aTangent;
in vec3 aBinormal;
in vec2 aTuv2; //attribute TUV2 (texture coordinates)
out vec2 vTuv2; //varying TUV2 (pass to fragment shader)
uniform vec3 uVectorToLight;
uniform vec3 uCameraPosition; //for calculating half vector for glares
uniform float uSpecularIntencity; //for calculating half vector for glares
out vec3 tbnVectorToLight;
out vec3 tbnHalfVector;
#if defined(MIRROR)
out vec2 vScreenPosition01;
out mat3 inversedTBN;
#endif
#if defined(USE_TUV0)
in vec2 aTuv; //attribute TUV (texture coordinates)
out vec2 vTuv; //varying TUV (pass to fragment shader)
#endif
void main(void) {
gl_Position = uMVP * vec4(aPos, 1.0);
#if defined(USE_TUV0)
vTuv = aTuv;
#endif
vTuv2 = aTuv2;
// Transform the normal's orientation into eye space.
vec3 N = uMV3x3 * aNormal;
vec3 T = uMV3x3 * aTangent;
vec3 B = uMV3x3 * aBinormal;
//build TBN matrix
mat3 TBN = mat3(
T[0],B[0],N[0],
T[1],B[1],N[1],
T[2],B[2],N[2]
);
tbnVectorToLight = TBN * uVectorToLight;
if(uSpecularIntencity > 0.0){ //for glares
vec4 vxPos = uMM * vec4(aPos, 1.0); //vertex position
vec3 dirToCamera = normalize(uCameraPosition - vec3(vxPos));
vec3 vHalfVector = normalize(dirToCamera + uVectorToLight);
tbnHalfVector = TBN * vHalfVector;
}
#if defined(MIRROR)
vScreenPosition01[0] = (gl_Position[0]/gl_Position[3])*0.1;
vScreenPosition01[1] = -(gl_Position[1]/gl_Position[3])*0.1;
inversedTBN = inverse(TBN);
#endif
}
Normal map fragment shader:
5. 2. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\engine\dt\shaders\nm_f.txt
//#version 320 es
precision lowp float;
out vec4 FragColor; //output pixel color
uniform float uAlphaFactor; //for semi-transparency
uniform int uAlphaBlending; //for semi-transparency
in vec2 vTuv2;
uniform sampler2D uTex2nm;
in vec3 tbnVectorToLight;
in vec3 tbnHalfVector;
#if defined(USE_TEX0)
uniform sampler2D uTex0; //texture id
uniform sampler2D uTex3; //translate texture id
uniform int uTex0translateChannelN;
#else
uniform vec4 uColor;
#endif
#if defined(USE_TUV0)
in vec2 vTuv; //varying TUV (passed from vertex shader)
#endif
#if defined(OVERMASK)
uniform sampler2D uTex1mask; //texture id
uniform int uTex1alphaChannelN;
uniform int uTex1alphaNegative;
#endif
#if defined(MIRROR)
in vec2 vScreenPosition01;
in mat3 inversedTBN;
#endif
uniform float uAmbient;
uniform float uSpecularIntencity;
uniform float uSpecularMinDot;
uniform float uSpecularPowerOf;
void main(void) {
vec4 tbnNormal4 = texture(uTex2nm, vTuv2);
float alpha = tbnNormal4.a;
if(alpha < 0.5){
if(uAlphaBlending > 0){
if(alpha == 0.0){
discard;
return;
}
}
else{ //no AlphaBlending
discard;
return;
}
}
//black?
if(tbnNormal4.b < 0.3){
FragColor = vec4(0.0,0.0,0.0,alpha);
return;
}
vec4 outColor;
#if defined(OVERMASK)
outColor = texture(uTex1mask, vTuv);
float alpha2 = outColor[uTex1alphaChannelN];
if(uTex1alphaNegative > 0)
alpha2 = 1.0 - alpha2;
if(alpha2 < 1.0){
alpha *= alpha2;
if(alpha < 0.5){
if(uAlphaBlending > 0){
if(alpha == 0.0){
discard;
return;
}
}
else{ //no AlphaBlending
discard;
return;
}
}
}
#endif
vec3 vNormalNormal = normalize(vec3(tbnNormal4) * 2.0 - 1.0);
#if defined(USE_TEX0)
#if defined(MIRROR)
vec3 inversedNormal = normalize(inversedTBN * vNormalNormal);
vec2 vTuvMirror;
vTuvMirror[0] = (vScreenPosition01[0]+inversedNormal[0]*0.4)+0.5;
vTuvMirror[1] = -(vScreenPosition01[1]+inversedNormal[1]*0.4)+0.5;
outColor = texture(uTex0, vTuvMirror);
#else
outColor = texture(uTex0, vTuv);
#endif
if(uTex0translateChannelN >= 0){ //translate channel
vec2 tuv3;
tuv3[0] = outColor[uTex0translateChannelN];
tuv3[1] = 0.0;
outColor = texture(uTex3, tuv3);
}
FragColor = outColor;
#else
FragColor = uColor;
#endif
if(FragColor.a != 1.0){
alpha *= FragColor.a;
if(alpha < 0.5){
if(uAlphaBlending > 0){
if(alpha == 0.0){
discard;
return;
}
}
else{ //no AlphaBlending
discard;
return;
}
}
}
if(uAmbient<1.0){
// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
// pointing in the same direction then it will get max illumination.
float dotProduct = dot(vNormalNormal, normalize(tbnVectorToLight));
#if defined(WIRE)
if(dotProduct < 0.0)
dotProduct = -dotProduct;
dotProduct = 1.0 - dotProduct;
#endif
// count ambient component
dotProduct += uAmbient;
if(dotProduct < uAmbient)
dotProduct = uAmbient;
// Multiply the color by the lightIntencity illumination level to get final output color.
FragColor *= dotProduct;
}
if(uSpecularIntencity>0.0){
//specular light
// INTENSITY OF THE SPECULAR LIGHT
// DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS
float dotProduct = dot(vNormalNormal, normalize(tbnHalfVector));
#if defined(WIRE)
if(dotProduct < 0.0)
dotProduct = -dotProduct;
dotProduct = 1.0 - dotProduct;
#endif
if(dotProduct>uSpecularMinDot){
float specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;
if(specularIntencity > uSpecularIntencity)
specularIntencity = uSpecularIntencity;
FragColor += specularIntencity;
}
}
if(uAlphaFactor != 1.0)
alpha *= uAlphaFactor;
FragColor.a = alpha;
}
Теперь – программная часть.
6. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
7. Заменим Material.h код на:
#pragma once
#include "MyColor.h"
#include <string>
class Material
{
public:
char shaderType[20] = "";
int shaderN = -1;
int primitiveType = GL_TRIANGLES;
MyColor uColor;
int uTex0 = -1;
int uTex1mask = -1;
int uTex2nm = -1;
int uTex3 = -1;
int uTex1alphaChannelN = 3; //default - alpha channel for mask
int uTex1alphaNegative = 0; //default - alpha channel not negative
int uTex0translateChannelN = -1; //translate tex0 to tex3 by channelN. Default -1 - don't translate
int uAlphaBlending = 0; //for semi-transparency
float uAlphaFactor = 1; //for semi-transparency
float uAmbient = 0.4f; //ambient light
//specular light parameters
float uSpecularIntencity = 0.8f;
float uSpecularMinDot = 0.8f;
float uSpecularPowerOf = 20.0f;
float lineWidth = 1;
public:
int pickShaderNumber() { return pickShaderNumber(this); };
static int pickShaderNumber(Material* pMT);
void setShaderType(std::string needType) { setShaderType(this, needType); };
static void setShaderType(Material* pMT, std::string needType) { myStrcpy_s(pMT->shaderType, 20, (char*)needType.c_str()); };
void clear() { clear(this); };
static void clear(Material* pMT);
int assignShader(std::string needType) { return assignShader(this, needType); };
static int assignShader(Material* pMT, std::string needType);
};
В классе ModelLoader – новая переменная и функционал (отмечены).
8. Заменим 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);
};
9. Заменим 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
int subjN = pSubjsVector0->size();
GameSubj* pGS = theGame.newGameSubj(subjClass);
pSubjsVector0->push_back(pGS);
//pGS->djStartN = DrawJob::drawJobs.size();
ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
processSource(pML);
delete pML;
//pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return subjN;
}
int ModelLoader::setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr) {
if (!varExists(varName, tagStr))
return 0;
std::string str0 = getStringValue(varName, tagStr);
if (intHashMap.find(str0) == intHashMap.end()) {
mylog("ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\n", varName.c_str(), tagStr.c_str());
return -1;
}
*pInt = intHashMap[getStringValue(varName, tagStr)];
return 1;
}
int ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {
ModelBuilder* pMB = pML->pModelBuilder;
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);
setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
setFloatValue(&pMT->uAmbient, "uAmbient", tagStr);
setFloatValue(&pMT->uSpecularIntencity, "uSpecularIntencity", tagStr);
setFloatValue(&pMT->uSpecularMinDot, "uSpecularMinDot", tagStr);
setFloatValue(&pMT->uSpecularPowerOf, "uSpecularPowerOf", tagStr);
setFloatValue(&pMT->lineWidth, "lineWidth", tagStr);
return 1;
}
int ModelLoader::processTag(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("texture_as") == 0) {
//saves texture N in texturesMap under given name
std::string keyName = getStringValue("texture_as", pML->currentTag);
if (pMB->texturesHashMap.find(keyName) != pMB->texturesHashMap.end())
return pMB->texturesHashMap[keyName];
else { //add new
std::string txFile = getStringValue("src", pML->currentTag);
unsigned int intCkey = 0;
setUintColorValue(&intCkey, "ckey", pML->currentTag);
int txN = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
pMB->texturesHashMap[keyName] = txN;
//mylog("%s=%d\n", keyName.c_str(), pMB->texturesMap[keyName]);
return txN;
}
}
if (pML->tagName.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());
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);
}
//move/rotate
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;
}
Когда линия загружена, надо б позаботиться о нормалях для вычисления освещения в шейдере. Только единственный вектор, который мы можем из нее вычислить – это направление линии, которое перпендикулярно любым нормалям.

Дополнительный код в шейдерах опирается на направления вместо нормалей. Направления мы будем вычислять в классе ModelBuilder.
10. Заменим 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<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*> gameSubjs);
static int rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
static int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);
static int calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
static int finalizeLine(std::vector<Vertex01*> verts, int lineStartsAt=0, int lineEndsAt=0);
};
11. Заменим ModelBuilder1base.cpp код на:
#include "ModelBuilder1base.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder1base::~ModelBuilder1base() {
releaseGroup(this);
//clear all vectors
int itemsN = vertices.size();
for (int i = 0; i < itemsN; i++)
delete vertices.at(i);
vertices.clear();
itemsN = triangles.size();
for (int i = 0; i < itemsN; i++)
delete triangles.at(i);
triangles.clear();
itemsN = vShapesStack.size();
for (int i = 0; i < itemsN; i++)
delete vShapesStack.at(i);
vShapesStack.clear();
itemsN = groupsStack.size();
for (int i = 0; i < itemsN; i++)
delete groupsStack.at(i);
groupsStack.clear();
if (pCurrentGroup != NULL)
delete pCurrentGroup;
if (pLastClosedGroup != NULL)
delete pLastClosedGroup;
itemsN = materialsList.size();
for (int i = 0; i < itemsN; i++)
delete materialsList.at(i);
materialsList.clear();
subjNumbersList.clear();
}
int ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {
pMB->usingSubjN = subjN;
int itemsN = pMB->subjNumbersList.size();
bool newN = true;
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (pMB->subjNumbersList.at(i) == subjN) {
newN = false;
break;
}
if (newN)
pMB->subjNumbersList.push_back(subjN);
return subjN;
}
int ModelBuilder1base::getMaterialN(ModelBuilder1base* pMB, Material* pMT) {
int itemsN = pMB->materialsList.size();
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
return i;
}
//if here - add new material to the list
Material* pMTnew = new Material(*pMT);
pMB->materialsList.push_back(pMTnew);
return itemsN;
}
int ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
//indexes: NorthWest, NorthEast, SouthWest,SouthEast
if (n % 2 == 0) { //even number
addTriangle(pMB, nNW, nSW, nNE);
addTriangle(pMB, nNE, nSW, nSE);
}
else { //odd number
addTriangle(pMB, nNW, nSE, nNE);
addTriangle(pMB, nNW, nSW, nSE);
}
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {
Triangle01* pTR = new Triangle01();
pMB->triangles.push_back(pTR);
pTR->idx[0] = i0;
pTR->idx[1] = i1;
pTR->idx[2] = i2;
pTR->subjN = pMB->usingSubjN;
pTR->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pTR->marks, 124, pMB->pCurrentGroup->marks);
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
Vertex01* pVX = new Vertex01();
pMB->vertices.push_back(pVX);
pVX->aPos[0] = kx;
pVX->aPos[1] = ky;
pVX->aPos[2] = kz;
//normal
pVX->aNormal[0] = nx;
pVX->aNormal[1] = ny;
pVX->aNormal[2] = nz;
pVX->subjN = pMB->usingSubjN;
pVX->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pVX->marks, 124, pMB->pCurrentGroup->marks);
return pMB->vertices.size() - 1;
}
int ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs) {
int totalSubjsN = pMB->subjNumbersList.size();
if (totalSubjsN < 1) {
pMB->subjNumbersList.push_back(-1);
totalSubjsN = 1;
}
int totalMaterialsN = pMB->materialsList.size();
if (totalSubjsN < 2 && totalMaterialsN < 2) {
//simple single DrawJob
Material* pMT = pMB->materialsList.at(0);
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(0);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
buildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return 1;
}
int totalVertsN = pMB->vertices.size();
int totalTrianglesN = pMB->triangles.size();
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
pVX->flag = 0;
}
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
pTR->flag = 0;
}
int addedDJs = 0;
for (int sN = 0; sN < totalSubjsN; sN++) {
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(sN);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
Material* pMT = pMB->materialsList.at(mtN);
std::vector<Vertex01*> useVertices;
std::vector<Triangle01*> useTriangles;
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag != 0)
continue;
if (pVX->subjN != gsN)
continue;
if (pVX->materialN != mtN)
continue;
//if here - make a copy
Vertex01* pVX2 = new Vertex01(*pVX);
useVertices.push_back(pVX2);
pVX2->altN = vN;
pVX->flag = 1;
if (pVX->endOfSequence > 0) {
//rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear and proceed to next sequence
int useVerticesN = useVertices.size();
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
}
}
int useVerticesN = useVertices.size();
if (useVerticesN < 1)
continue; //to next material
//pick triangles
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
if (pTR->flag != 0)
continue;
if (pTR->subjN != gsN)
continue;
if (pTR->materialN != mtN)
continue;
//if here - make a copy
Triangle01* pTR2 = new Triangle01(*pTR);
useTriangles.push_back(pTR2);
pTR->flag = 1;
}
rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear all for next material
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
int useTrianglesN = useTriangles.size();
for (int i = 0; i < useTrianglesN; i++)
delete useTriangles.at(i);
useTriangles.clear();
}
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
}
return addedDJs;
}
int ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
if (DrawJob::lineWidthIsImportant(pMT->primitiveType))
if(strcmp(pMT->shaderType,"wire")==0)
finalizeLine(useVertices);
if (pMT->uTex2nm >= 0)
calculateTangentSpace(useVertices, useTriangles);
pMT->pickShaderNumber();
DrawJob* pDJ = new DrawJob();
//copy material to DJ
memcpy(&pDJ->mt, pMT, sizeof(Material));
//calculate VBO element size (stride) and variables offsets in VBO
int VBOid = DrawJob::newBufferId();
int stride = 0;
pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
//create an array for VBO
int bufferSize = totalVertsN * stride;
float* vertsBuffer = new float[bufferSize];
//fill vertsBuffer
Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
int floatSize = sizeof(float);
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
int idx = vN * stride / floatSize;
//pick data from vertex and move to the buffer
memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
if (pSh->l_aNormal >= 0) //normal
memcpy(&vertsBuffer[idx + pDJ->aNormal.offset / floatSize], pVX->aNormal, 3 * floatSize);
if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
if (pSh->l_aTuv2 >= 0) //attribute TUV2 (normal maps)
memcpy(&vertsBuffer[idx + pDJ->aTuv2.offset / floatSize], pVX->aTuv2, 2 * floatSize);
if (pSh->l_aTangent >= 0)
memcpy(&vertsBuffer[idx + pDJ->aTangent.offset / floatSize], pVX->aTangent, 3 * floatSize);
if (pSh->l_aBinormal >= 0)
memcpy(&vertsBuffer[idx + pDJ->aBinormal.offset / floatSize], pVX->aBinormal, 3 * floatSize);
}
//buffer is ready, create VBO
glBindBuffer(GL_ARRAY_BUFFER, VBOid);
glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
delete[] vertsBuffer;
pDJ->pointsN = totalVertsN;
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN > 0) {
//create EBO
int totalIndexesN = totalTrianglesN * 3;
//create buffer
GLushort* indexBuffer = new GLushort[totalIndexesN];
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles[tN];
int idx = tN * 3;
indexBuffer[idx] = (GLushort)pTR->idx[0];
indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
}
//buffer is ready, create IBO
pDJ->glEBOid = DrawJob::newBufferId();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
delete[] indexBuffer;
pDJ->pointsN = totalIndexesN;
}
//create and fill vertex attributes array (VAO)
pDJ->buildVAO();
return 1;
}
int ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN < 1)
return 0;
int totalVerticesN = useVertices.size();
//save new vertices order in original vertices array
//since triangles indices refer to original vertices order
for (int i = 0; i < totalVerticesN; i++) {
Vertex01* pVX1 = useVertices.at(i);
Vertex01* pVX0 = allVertices.at(pVX1->altN);
pVX0->altN = i;
}
//replace triangle original indices by new numbers saved in original vertices altN
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles.at(tN);
for (int i = 0; i < 3; i++) {
Vertex01* pVX0 = allVertices.at(pTR->idx[i]);
pTR->idx[i] = pVX0->altN;
}
}
return 1;
}
int ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {
//moves and rotates vertex group
//rotation angles are set in degrees
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_translate(transformMatrix, kX, kY, kZ);
//rotation order: Z-X-Y
if (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);
if (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);
if (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
}
return 1;
}
int ModelBuilder1base::calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
int totalTrianglesN = useTriangles.size();
//assuming that GL_TRIANGLES
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
pV->flag = 0;
}
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
if (pVX->flag != 0)
continue;
Triangle01* pT = NULL;
for (int tN = 0; tN < totalTrianglesN; tN++) {
pT = useTriangles.at(tN);
bool haveTriangle = false;
for (int i = 0; i < 3; i++)
if (pT->idx[i] == vN) {
haveTriangle = true;
break;
}
if (haveTriangle)
break;
}
Vertex01* pV[3];
for (int i = 0; i < 3; i++)
pV[i] = useVertices.at(pT->idx[i]);
float dPos1[3];
float dPos2[3];
float dUV1[2];
float dUV2[2];
for (int i = 0; i < 3; i++) {
dPos1[i] = pV[1]->aPos[i] - pV[0]->aPos[i];
dPos2[i] = pV[2]->aPos[i] - pV[0]->aPos[i];
}
for (int i = 0; i < 2; i++) {
dUV1[i] = pV[1]->aTuv2[i] - pV[0]->aTuv2[i];
dUV2[i] = pV[2]->aTuv2[i] - pV[0]->aTuv2[i];
}
float tangent[3];
float binormal[3];
float divider = dUV1[0] * dUV2[1] - dUV1[1] * dUV2[0];
if (divider == 0) {
v3set(tangent, 1, 0, 0);
v3set(binormal, 0, -1, 0);
}
else {
float r = 1.0f / divider;
for (int i = 0; i < 3; i++) {
tangent[i] = (dPos1[i] * dUV2[1] - dPos2[i] * dUV1[1]) * r;
binormal[i] = -(dPos2[i] * dUV1[0] - dPos1[i] * dUV2[0]) * r;
}
vec3_norm(tangent, tangent);
vec3_norm(binormal, binormal);
}
//add to all 3 vertices
for (int n = 0; n < 3; n++) {
if (pV[n]->flag > 0)
continue;
v3copy(pV[n]->aTangent, tangent);
v3copy(pV[n]->aBinormal, binormal);
pV[n]->flag = 1;
}
}
//normalize tangent and binormal around normal
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
float v3out[3];
//tangent
vec3_mul_cross(v3out, pV->aNormal, pV->aBinormal);
if (v3dotProduct(pV->aTangent, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aTangent, v3out);
//binormal
vec3_mul_cross(v3out, pV->aNormal, pV->aTangent);
if (v3dotProduct(pV->aBinormal, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aBinormal, v3out);
}
return 1;
}
void ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {
Group01* pPrevGroup = pMB->pCurrentGroup;
if (pMB->pCurrentGroup != NULL)
pMB->groupsStack.push_back(pMB->pCurrentGroup);
pMB->pCurrentGroup = new Group01();
pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
//marks
if(pPrevGroup != NULL)
if (strcmp(pPrevGroup->marks, "") != 0)
myStrcpy_s(pMB->pCurrentGroup->marks, 124, pPrevGroup->marks);
}
void ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {
if (pMB->pLastClosedGroup != NULL)
delete pMB->pLastClosedGroup;
pMB->pLastClosedGroup = pMB->pCurrentGroup;
if (pMB->groupsStack.size() > 0) {
pMB->pCurrentGroup = pMB->groupsStack.back();
pMB->groupsStack.pop_back();
}
else
pMB->pCurrentGroup = NULL;
}
int ModelBuilder1base::finalizeLine(std::vector<Vertex01*> verts, int lineStartsAt, int lineEndsAt) {
if (lineEndsAt <= 0)
lineEndsAt = verts.size() - 1;
Vertex01* pV0 = verts.at(lineStartsAt);
Vertex01* pV2 = verts.at(lineEndsAt);
bool closedLine = false;
if (v3match(pV0->aPos, pV2->aPos))
closedLine = true;
for (int vN = lineStartsAt; vN <= lineEndsAt; vN++) {
Vertex01* pV = verts.at(vN);
//prev point
if (vN == lineStartsAt) {
//first point
if (closedLine)
pV0 = verts.at(lineEndsAt);
else
pV0 = NULL;
}
else
pV0 = verts.at(vN - 1);
//next point
if (vN == lineEndsAt) {
//last point
if (closedLine)
pV2 = verts.at(lineStartsAt);
else
pV2 = NULL;
}
else
pV2 = verts.at(vN + 1);
//distances to neighbor points
float distFromPrev = 0;
float dirFromPrev[3] = { 0,0,0 };
if (pV0 != NULL) {
distFromPrev = v3lengthFromTo(pV0->aPos, pV->aPos);
v3dirFromTo(dirFromPrev, pV0->aPos, pV->aPos);
}
float distToNext = 0;
float dirToNext[3] = { 0,0,0 };
if (pV2 != NULL) {
distToNext = v3lengthFromTo(pV->aPos, pV2->aPos);
v3dirFromTo(dirToNext, pV->aPos, pV2->aPos);
}
float distTotal = distFromPrev + distToNext;
float kPrev = distFromPrev / distTotal;
float kNext = distToNext / distTotal;
if (kPrev > kNext * 3)
v3copy(pV->aNormal, dirFromPrev);
else if (kNext > kPrev * 3)
v3copy(pV->aNormal, dirToNext);
else
for (int i = 0; i < 3; i++)
pV->aNormal[i] = kPrev * dirFromPrev[i] + kNext * dirToNext[i];
vec3_norm(pV->aNormal, pV->aNormal);
}
return 1;
}
В классе DrawJob новая функция lineWidthIsImportant() и новые параметры в executeDrawJob():
12. Заменим DrawJob.h код на:
#pragma once
#include "Material.h"
#include <vector>
struct AttribRef //attribute reference/description
{
unsigned int glVBOid = 0; //buffer object id
int offset = 0; //variable's offset inside of VBO's element
int stride = 0; //Buffer's element size in bytes
};
class DrawJob
{
public:
Material mt;
int pointsN = 0; //N of points to draw
unsigned int glVAOid = 0; //will hold data stream attributes mapping/positions
unsigned int glEBOid = 0; //Element Buffer Object (vertex indices)
//common attributes
AttribRef aPos;
AttribRef aNormal;
AttribRef aTuv;
AttribRef aTuv2; //for normal map
AttribRef aTangent; //for normal map
AttribRef aBinormal; //for normal map
//static arrays (vectors) of all loaded DrawJobs, VBO ids
static std::vector<DrawJob*> drawJobs;
static std::vector<unsigned int> buffersIds;
public:
DrawJob();
virtual ~DrawJob(); //destructor
static int cleanUp();
static int newBufferId();
int buildVAO() { return buildVAOforShader(this, mt.shaderN); };
static int buildVAOforShader(DrawJob* pDJ, int shaderN);
static int attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAttribRef);
virtual int setDesirableOffsets(int* pStride, int shaderN, int VBOid) { return setDesirableOffsetsForSingleVBO(this, pStride, shaderN, VBOid); };
static int setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid);
int execute(float* uMVP, float* uMV, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize = 0, Material* pMt=NULL) { return executeDrawJob(this, uMVP, uMV, uMM, uVectorToLight, uCameraPosition, sizeUnitPixelsSize, pMt); };
static int executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize = 0, Material* pMt=NULL);
static bool lineWidthIsImportant(int primitiveType);
};
13. Заменим DrawJob.cpp код на:
#include "DrawJob.h"
#include "platform.h"
#include "utils.h"
#include "Shader.h"
#include "Texture.h"
//static arrays (vectors) of all loaded DrawJobs, VBO ids
std::vector<DrawJob*> DrawJob::drawJobs;
std::vector<unsigned int> DrawJob::buffersIds;
DrawJob::DrawJob() {
drawJobs.push_back(this);
}
DrawJob::~DrawJob() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (glVAOid > 0)
glDeleteVertexArrays(1, &glVAOid);
}
int DrawJob::newBufferId() {
unsigned int bufferId;
glGenBuffers(1, &bufferId);
buffersIds.push_back(bufferId);
return (int)bufferId;
}
unsigned int activeVBOid;
int DrawJob::buildVAOforShader(DrawJob* pDJ, int shaderN) {
//delete VAO if exists already
if (pDJ->glVAOid > 0) {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteVertexArrays(1, &(pDJ->glVAOid));
}
glGenVertexArrays(1, &pDJ->glVAOid);
glBindVertexArray(pDJ->glVAOid);
//open shader descriptor to access variables locations
Shader* pShader = Shader::shaders.at(pDJ->mt.shaderN);
activeVBOid = 0;
attachAttribute(pShader->l_aPos, 3, &pDJ->aPos);
attachAttribute(pShader->l_aNormal, 3, &pDJ->aNormal);
attachAttribute(pShader->l_aTuv, 2, &pDJ->aTuv);
attachAttribute(pShader->l_aTuv2, 2, &pDJ->aTuv2); //for normal map
attachAttribute(pShader->l_aTangent, 3, &pDJ->aTangent); //for normal map
attachAttribute(pShader->l_aBinormal, 3, &pDJ->aBinormal); //for normal map
if (pDJ->glEBOid > 0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return 1;
}
int DrawJob::attachAttribute(int varLocationInShader, int attributeSizeInFloats, AttribRef* pAR) {
if (varLocationInShader < 0)
return 0; //not used in this shader
if (pAR->glVBOid == 0) {
mylog("ERROR in DrawJob::attachAttribute, nk such attribute/VBO\n");
return -1;
}
glEnableVertexAttribArray(varLocationInShader);
if (activeVBOid != pAR->glVBOid) {
activeVBOid = pAR->glVBOid;
//attach input stream data
glBindBuffer(GL_ARRAY_BUFFER, activeVBOid);
}
glVertexAttribPointer(varLocationInShader, attributeSizeInFloats, GL_FLOAT, GL_FALSE, pAR->stride, (void*)(long)pAR->offset);
return 1;
}
int DrawJob::executeDrawJob(DrawJob* pDJ, float* uMVP, float* uMV3x3, float* uMM, float* uVectorToLight, float* uCameraPosition, float sizeUnitPixelsSize, Material* pMt) {
if (pMt == NULL)
pMt = &(pDJ->mt);
glBindVertexArray(pDJ->glVAOid);
Shader* pShader = Shader::shaders.at(pMt->shaderN);
glUseProgram(pShader->GLid);
//input uniforms
glUniformMatrix4fv(pShader->l_uMVP, 1, GL_FALSE, (const GLfloat*)uMVP);
if (pShader->l_uMV3x3 >= 0)
glUniformMatrix3fv(pShader->l_uMV3x3, 1, GL_FALSE, (const GLfloat*)uMV3x3);
if (pShader->l_uMM >= 0)
glUniformMatrix4fv(pShader->l_uMM, 1, GL_FALSE, (const GLfloat*)uMM);
if (pShader->l_uVectorToLight >= 0)
glUniform3fv(pShader->l_uVectorToLight, 1, (const GLfloat*)uVectorToLight);
if (pShader->l_uCameraPosition >= 0)
glUniform3fv(pShader->l_uCameraPosition, 1, (const GLfloat*)uCameraPosition);
//attach textures
if (pShader->l_uTex0 >= 0) {
int textureId = Texture::getGLid(pMt->uTex0);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE0); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
glUniform1i(pShader->l_uTex0, 0);
}
if (pShader->l_uTex1mask >= 0) {
int textureId = Texture::getGLid(pMt->uTex1mask);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE1); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 1.
glUniform1i(pShader->l_uTex1mask, 1);
}
if (pShader->l_uTex2nm >= 0) {
int textureId = Texture::getGLid(pMt->uTex2nm);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE2); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 2.
glUniform1i(pShader->l_uTex2nm, 2);
}
if (pShader->l_uTex0translateChannelN >= 0) {
glUniform1i(pShader->l_uTex0translateChannelN, pMt->uTex0translateChannelN);
if (pShader->l_uTex3 >= 0 && pMt->uTex3 >= 0) {
int textureId = Texture::getGLid(pMt->uTex3);
//pass textureId to shader program
glActiveTexture(GL_TEXTURE3); // activate the texture unit first before binding texture
glBindTexture(GL_TEXTURE_2D, textureId);
// Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 3.
glUniform1i(pShader->l_uTex3, 3);
}
}
//material uniforms
if (pShader->l_uTex1alphaChannelN >= 0)
glUniform1i(pShader->l_uTex1alphaChannelN, pMt->uTex1alphaChannelN);
if (pShader->l_uTex1alphaNegative >= 0)
glUniform1i(pShader->l_uTex1alphaNegative, pMt->uTex1alphaNegative);
if (pShader->l_uColor >= 0)
glUniform4fv(pShader->l_uColor, 1, pMt->uColor.forGL());
if (pShader->l_uAlphaFactor >= 0)
glUniform1f(pShader->l_uAlphaFactor, pMt->uAlphaFactor);
if (pShader->l_uAlphaBlending >= 0)
glUniform1i(pShader->l_uAlphaBlending, pMt->uAlphaBlending);
if (pShader->l_uAmbient >= 0)
glUniform1f(pShader->l_uAmbient, pMt->uAmbient);
if (pShader->l_uSpecularIntencity >= 0)
glUniform1f(pShader->l_uSpecularIntencity, pMt->uSpecularIntencity);
if (pShader->l_uSpecularMinDot >= 0)
glUniform1f(pShader->l_uSpecularMinDot, pMt->uSpecularMinDot);
if (pShader->l_uSpecularPowerOf >= 0)
glUniform1f(pShader->l_uSpecularPowerOf, pMt->uSpecularPowerOf);
//adjust render settings
if (pShader->l_uAlphaBlending >= 0 && pMt->uAlphaBlending > 0) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else
glDisable(GL_BLEND);
if (lineWidthIsImportant(pMt->primitiveType)) {
float lw = sizeUnitPixelsSize * pMt->lineWidth;
glLineWidth(lw);
}
//execute
if (pDJ->glEBOid == 0) {
glDrawArrays(pMt->primitiveType, 0, pDJ->pointsN);
}
else { //use EBO
glDrawElements(pMt->primitiveType, pDJ->pointsN, GL_UNSIGNED_SHORT, 0);
}
glBindVertexArray(0);
return 1;
}
int DrawJob::cleanUp() {
int itemsN = drawJobs.size();
//delete all drawJobs
for (int i = 0; i < itemsN; i++) {
DrawJob* pDJ = drawJobs.at(i);
delete pDJ;
}
drawJobs.clear();
//delete Buffers
itemsN = buffersIds.size();
//delete all buffers
for (int i = 0; i < itemsN; i++) {
unsigned int id = buffersIds.at(i);
glDeleteBuffers(1, &id);
}
buffersIds.clear();
return 1;
}
int DrawJob::setDesirableOffsetsForSingleVBO(DrawJob* pDJ, int* pStride, int shaderN, int VBOid) {
//sets desirable offsets and stride according to given shader needs
//assuming that we have 1 single VBO
Shader* pSh = Shader::shaders.at(shaderN);
int stride = 0;
pDJ->aPos.offset = 0; //attribute o_aPos, always 0
stride += sizeof(float) * 3; //aPos size - 3 floats (x,y,z)
if (pSh->l_aNormal >= 0) { //attribute normal
pDJ->aNormal.offset = stride;
stride += sizeof(float) * 3;
}
if (pSh->l_aTuv >= 0) { //attribute TUV (texture coordinates)
pDJ->aTuv.offset = stride; //attribute TUV (texture coordinates)
stride += sizeof(float) * 2;
}
if (pSh->l_aTuv2 >= 0) { //for normal map
pDJ->aTuv2.offset = stride;
stride += sizeof(float) * 2;
}
if (pSh->l_aTangent >= 0) { //for normal map
pDJ->aTangent.offset = stride;
stride += sizeof(float) * 3;
}
if (pSh->l_aBinormal >= 0) { //for normal map
pDJ->aBinormal.offset = stride;
stride += sizeof(float) * 3;
}
*pStride = stride;
//add stride and VBOid to all attributes
AttribRef* pAR = NULL;
pAR = &pDJ->aPos; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aNormal; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTuv; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTuv2; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aTangent; pAR->glVBOid = VBOid; pAR->stride = stride;
pAR = &pDJ->aBinormal; pAR->glVBOid = VBOid; pAR->stride = stride;
return 1;
}
bool DrawJob::lineWidthIsImportant(int primitiveType) {
if (primitiveType == GL_TRIANGLES) return false;
if (primitiveType == GL_TRIANGLE_STRIP) return false;
if (primitiveType == GL_TRIANGLE_FAN) return false;
return true;
}
Вернемся к шейдерам. У нас новые переменные: uMM (model matrix) и uCameraPosition вместо uHalfVector. Плюс новый #define – WIRE
14. Заменим Shader.h код на:
#pragma once
#include "platform.h"
#include <string>
#include <vector>
class Shader
{
public:
//Shader program's individual descriptor:
unsigned int GLid = -1; // GL shader id
char shaderType[20] = "";
//common variables, "l_" for "location"
//attributes
int l_aPos; //attribute position (3D coordinates)
int l_aTuv; //attribute TUV (texture coordinates)
int l_aTuv2; //attribute TUV (texture coordinates for normal map)
int l_aNormal; //attribute normal (3D vector)
int l_aTangent; //for normal map
int l_aBinormal; //for normal map
//uniforms
int l_uMVP; // transform matrix (Model-View-Projection)
int l_uMV3x3; // Model-View matrix for normals
int l_uMM; // Model matrix for HalfVector
int l_uVectorToLight; //required for light
int l_uCameraPosition; //required for specular light
//material's properties
int l_uColor;
int l_uTex0; //texture id
int l_uTex1mask; //transparency map
int l_uTex2nm; //normal map
int l_uTex3; //texture id
int l_uTex1alphaChannelN; //alpha channel for mask
int l_uTex1alphaNegative; //alpha channel negative
int l_uTex0translateChannelN; //translate tex0 to tex3 by channelN.
int l_uAlphaFactor; //for semi-transparency
int l_uAlphaBlending; //for semi-transparency
//light:
int l_uAmbient; //ambient light
//specular light parameters
int l_uSpecularIntencity;
int l_uSpecularMinDot;
int l_uSpecularPowerOf;
//end of descriptor
//static array (vector) of all loaded shaders
static std::vector<Shader*> shaders;
public:
static int loadShaders();
static int cleanUp();
static unsigned int getGLid(int shN) { return shaders.at(shN)->GLid; };
static int shaderErrorCheck(int shaderId, std::string ref);
static int programErrorCheck(int programId, std::string ref);
static int fillLocations(Shader* pSh);
static int buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS);
static int linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS);
static int compileShaderFromFile(const char* filePath, GLenum shaderType);
static int buildShaderObjectWithDefines(std::string shaderType, std::string definesString, char* sourceVertex, char* sourceFragment);
static int linkShaderProgramWithDefines(std::string definesString, char* sourceVertex, char* sourceFragment);
static int compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType);
static int loadShadersGroup(std::string shaderType, std::string optionsString, char* sourceVertex, char* sourceFragment);
};
15. Заменим Shader.cpp код на:
#include "Shader.h"
#include "platform.h"
#include "utils.h"
#include "FileLoader.h"
extern std::string filesRoot;
//static array (vector) of all loaded shaders
std::vector<Shader*> Shader::shaders;
int Shader::loadShaders() {
FileLoader* pFLvertex = new FileLoader("/dt/shaders/phong_v.txt");
FileLoader* pFLfragment = new FileLoader("/dt/shaders/phong_f.txt");
loadShadersGroup("flat", "FLAT; COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
loadShadersGroup("phong", "PHONG; COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
loadShadersGroup("mirror", "PHONG;MIRROR; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
loadShadersGroup("wire", "WIRE;PHONG; COLOR | TEXTURE", pFLvertex->pData, pFLfragment->pData);
delete pFLvertex;
delete pFLfragment;
//Normal Maps
pFLvertex = new FileLoader("/dt/shaders/nm_v.txt");
pFLfragment = new FileLoader("/dt/shaders/nm_f.txt");
loadShadersGroup("phong", "COLOR | TEXTURE; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
loadShadersGroup("mirror", "MIRROR; NONE | OVERMASK", pFLvertex->pData, pFLfragment->pData);
delete pFLvertex;
delete pFLfragment;
return 1;
}
int Shader::buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS) {
//create shader object
Shader* pSh = new Shader();
shaders.push_back(pSh);
pSh->GLid = linkShaderProgramFromFiles((filesRoot + filePathVertexS).c_str(), (filesRoot + filePathFragmentS).c_str());
//common variables. If not presented, = -1;
fillLocations(pSh);
return (shaders.size() - 1);
}
int Shader::fillLocations(Shader* pSh) {
//common variables. If not presented, = -1;
//attributes
pSh->l_aPos = glGetAttribLocation(pSh->GLid, "aPos"); //attribute position (3D coordinates)
pSh->l_aNormal = glGetAttribLocation(pSh->GLid, "aNormal"); //attribute normal (3D vector)
pSh->l_aTangent = glGetAttribLocation(pSh->GLid, "aTangent"); //for normal map
pSh->l_aBinormal = glGetAttribLocation(pSh->GLid, "aBinormal"); //for normal map
pSh->l_aTuv = glGetAttribLocation(pSh->GLid, "aTuv"); //attribute TUV (texture coordinates)
pSh->l_aTuv2 = glGetAttribLocation(pSh->GLid, "aTuv2"); //attribute TUV (texture coordinates)
//uniforms
pSh->l_uMVP = glGetUniformLocation(pSh->GLid, "uMVP"); // transform matrix (Model-View-Projection)
pSh->l_uMV3x3 = glGetUniformLocation(pSh->GLid, "uMV3x3"); // Model-View matrix for normals
pSh->l_uMM = glGetUniformLocation(pSh->GLid, "uMM"); // Model matrix for HalfVector
pSh->l_uVectorToLight = glGetUniformLocation(pSh->GLid, "uVectorToLight"); //
pSh->l_uCameraPosition = glGetUniformLocation(pSh->GLid, "uCameraPosition"); // required for specular light
//material's properties
pSh->l_uColor = glGetUniformLocation(pSh->GLid, "uColor");
pSh->l_uTex0 = glGetUniformLocation(pSh->GLid, "uTex0"); //texture id
pSh->l_uTex1mask = glGetUniformLocation(pSh->GLid, "uTex1mask"); //texture id
pSh->l_uTex2nm = glGetUniformLocation(pSh->GLid, "uTex2nm"); //texture id
pSh->l_uTex3 = glGetUniformLocation(pSh->GLid, "uTex3"); //texture id
pSh->l_uTex1alphaChannelN = glGetUniformLocation(pSh->GLid, "uTex1alphaChannelN");
pSh->l_uTex1alphaNegative = glGetUniformLocation(pSh->GLid, "uTex1alphaNegative");
pSh->l_uTex0translateChannelN = glGetUniformLocation(pSh->GLid, "uTex0translateChannelN");
pSh->l_uAlphaFactor = glGetUniformLocation(pSh->GLid, "uAlphaFactor"); // for semi-transparency
pSh->l_uAlphaBlending = glGetUniformLocation(pSh->GLid, "uAlphaBlending"); // for semi-transparency
pSh->l_uAmbient = glGetUniformLocation(pSh->GLid, "uAmbient"); // ambient light
pSh->l_uSpecularIntencity = glGetUniformLocation(pSh->GLid, "uSpecularIntencity"); //
pSh->l_uSpecularMinDot = glGetUniformLocation(pSh->GLid, "uSpecularMinDot"); //
pSh->l_uSpecularPowerOf = glGetUniformLocation(pSh->GLid, "uSpecularPowerOf"); //
return 1;
}
int Shader::cleanUp() {
int shadersN = shaders.size();
if (shadersN < 1)
return -1;
glUseProgram(0);
for (int i = 0; i < shadersN; i++) {
Shader* pSh = shaders.at(i);
glDeleteProgram(pSh->GLid);
delete pSh;
}
shaders.clear();
return 1;
}
GLchar infoLog[1024];
int logLength;
int Shader::shaderErrorCheck(int shaderId, std::string ref) {
//use after glCompileShader()
if (checkGLerrors(ref) > 0)
return -1;
glGetShaderInfoLog(shaderId, 1024, &logLength, infoLog);
if (logLength == 0)
return 0;
mylog("%s shader infoLog:\n%s\n", ref.c_str(), infoLog);
return -1;
}
int Shader::programErrorCheck(int programId, std::string ref) {
//use after glLinkProgram()
if (checkGLerrors(ref) > 0)
return -1;
glGetProgramInfoLog(programId, 1024, &logLength, infoLog);
if (logLength == 0)
return 0;
mylog("%s program infoLog:\n%s\n", ref.c_str(), infoLog);
return -1;
}
int Shader::compileShaderFromFile(const char* filePath, GLenum shaderType) {
int shaderId = glCreateShader(shaderType);
FILE* pFile;
myFopen_s(&pFile, filePath, "rt");
if (pFile != NULL)
{
// obtain file size:
fseek(pFile, 0, SEEK_END);
int fSize = ftell(pFile);
rewind(pFile);
// size obtained, create buffer
char* shaderSource = new char[fSize + 1];
fSize = fread(shaderSource, 1, fSize, pFile);
shaderSource[fSize] = 0;
fclose(pFile);
// source code loaded, compile
glShaderSource(shaderId, 1, (const GLchar**)&shaderSource, NULL);
//myglErrorCheck("glShaderSource");
glCompileShader(shaderId);
if (shaderErrorCheck(shaderId, "glCompileShader") < 0)
return -1;
delete[] shaderSource;
}
else {
mylog("ERROR loading %s\n", filePath);
return -1;
}
return shaderId;
}
int Shader::linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS) {
int vertexShaderId = compileShaderFromFile(filePathVertexS, GL_VERTEX_SHADER);
int fragmentShaderId = compileShaderFromFile(filePathFragmentS, GL_FRAGMENT_SHADER);
int programId = glCreateProgram();
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId);
if (programErrorCheck(programId, "glLinkProgram") < 0)
return -1;
//don't need shaders any longer - detach and delete them
glDetachShader(programId, vertexShaderId);
glDetachShader(programId, fragmentShaderId);
glDeleteShader(vertexShaderId);
glDeleteShader(fragmentShaderId);
return programId;
}
int Shader::buildShaderObjectWithDefines(std::string shaderType, std::string definesString, char* sourceVertex, char* sourceFragment) {
//create shader object
Shader* pSh = new Shader();
shaders.push_back(pSh);
myStrcpy_s(pSh->shaderType, 20, shaderType.c_str());
pSh->GLid = linkShaderProgramWithDefines(definesString, sourceVertex, sourceFragment);
//common variables. If not presented, = -1;
fillLocations(pSh);
return (shaders.size() - 1);
}
int Shader::linkShaderProgramWithDefines(std::string definesString00, char* sourceVertex, char* sourceFragment) {
//build extended definesString
bool bUSE_NORMALS = false;
bool bUSE_TEX0 = false;
bool bUSE_TUV0 = false;
if (definesString00.find(" PHONG\n") != std::string::npos)
bUSE_NORMALS = true;
if (definesString00.find(" TEXTURE\n") != std::string::npos) {
bUSE_TEX0 = true;
bUSE_TUV0 = true;
}
if (definesString00.find(" MIRROR\n") != std::string::npos) {
bUSE_NORMALS = true;
bUSE_TEX0 = true;
}
if (definesString00.find(" OVERMASK\n") != std::string::npos) {
bUSE_TUV0 = true;
}
std::string definesString;
definesString.assign("#version 320 es\n");
definesString.append(definesString00);
if (bUSE_NORMALS)
definesString.append("#define USE_NORMALS\n");
if (bUSE_TEX0)
definesString.append("#define USE_TEX0\n");
if (bUSE_TUV0)
definesString.append("#define USE_TUV0\n");
int vertexShaderId = compileShaderWithDefines(definesString, sourceVertex, GL_VERTEX_SHADER);
int fragmentShaderId = compileShaderWithDefines(definesString, sourceFragment, GL_FRAGMENT_SHADER);
int programId = glCreateProgram();
glAttachShader(programId, vertexShaderId);
glAttachShader(programId, fragmentShaderId);
glLinkProgram(programId);
if (programErrorCheck(programId, "glLinkProgram") < 0)
return -1;
//don't need shaders any longer - detach and delete them
glDetachShader(programId, vertexShaderId);
glDetachShader(programId, fragmentShaderId);
glDeleteShader(vertexShaderId);
glDeleteShader(fragmentShaderId);
//mylog("linking program\n%s\n", definesString.c_str());
return programId;
}
int Shader::compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType) {
int shaderId = glCreateShader(shaderType);
if (definesString.empty())
glShaderSource(shaderId, 1, (const GLchar**)&shaderSource, NULL);
else { //2 strings
const char* sourceStrings[2];
sourceStrings[0] = definesString.c_str();
sourceStrings[1] = shaderSource;
// source code loaded, compile
glShaderSource(shaderId, 2, (const GLchar**)sourceStrings, NULL);
}
//myglErrorCheck("glShaderSource");
glCompileShader(shaderId);
if (shaderErrorCheck(shaderId, "glCompileShader") < 0) {
mylog("ERROR in compileShader,\n%s\n%s\n", definesString.c_str(), shaderSource);
return -1;
}
return shaderId;
}
int Shader::loadShadersGroup(std::string shaderType, std::string optionsString, char* sourceVertex, char* sourceFragment) {
struct Terms {
std::vector<std::string> terms;
int totalN = 0;
int currentN = 0;
};
std::vector<Terms*> terms;
std::vector<std::string> termGroups = splitString(optionsString, ";");
int groupsN = termGroups.size();
for (int groupN = 0; groupN < groupsN; groupN++) {
Terms* pTerms = new Terms();
terms.push_back(pTerms);
pTerms->terms = splitString(termGroups.at(groupN), "|");
pTerms->totalN = pTerms->terms.size();
}
while (1) {
std::string definesString = "";
for (int groupN = 0; groupN < groupsN; groupN++) {
Terms* pTerms = terms.at(groupN);
std::string term = pTerms->terms.at(pTerms->currentN);
if (term.compare("NONE") != 0) {
definesString.append("#define ");
definesString.append(term);
definesString.append("\n");
}
}
int shaderObjN = buildShaderObjectWithDefines(shaderType, definesString, sourceVertex, sourceFragment);
//go to next terms combo
bool noMoreOptions = false;
for (int groupN = groupsN - 1; groupN >= 0; groupN--) {
Terms* pTerms = terms.at(groupN);
if (pTerms->currentN < pTerms->totalN - 1) {
pTerms->currentN++;
break;
}
else { // the level exhausted
pTerms->currentN = 0;
//proceed to upper level
if (groupN == 0) {
noMoreOptions = true;
break;
}
}
}
if (noMoreOptions)
break;
}
return 1;
}
И наконец – TheGame. Теперь нам не надо здесь считать uHalfVector, но придется считать sizeUnitPixelsSize (для пересчета ширины линий при рендринге). Плюс – новые параметры в pDJ->execute(..)
16. Заменим TheGame.cpp код на:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
#include "ModelLoader.h"
extern std::string filesRoot;
extern float degrees2radians;
std::vector<GameSubj*> TheGame::gameSubjs;
int TheGame::getReady() {
bExitGame = false;
Shader::loadShaders();
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
int subjN = ModelLoader::loadModel(&gameSubjs, "/dt/models/misc/marlboro01/root01.txt", "");
GameSubj* pGS = gameSubjs.at(subjN);
pGS->name.assign("box1");
pGS->ownSpeed.setDegrees(0, 2, 0);
//pGS->ownCoords.setDegrees(0, -90, 0);
//===== set up camera
mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
mainCamera.viewRangeDg = 30;
mainCamera.stageSize[0] = 80;
mainCamera.stageSize[1] = 120;
memcpy(mainCamera.lookAtPoint, pGS->ownCoords.pos, sizeof(float) * 3);
mainCamera.onScreenResize();
//===== set up light
v3set(dirToMainLight, -1, 1, 1);
vec3_norm(dirToMainLight, dirToMainLight);
return 1;
}
int TheGame::drawFrame() {
myPollEvents();
//glClearColor(0.0, 0.0, 0.5, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;
//mat4x4_ortho(mProjection, -(float)screenSize[0] / 2, (float)screenSize[0] / 2, -(float)screenSize[1] / 2, (float)screenSize[1] / 2, 100.f, 500.f);
float nearClip = mainCamera.focusDistance - 50;
float farClip = mainCamera.focusDistance + 50;
if (nearClip < 0) nearClip = 0;
mat4x4_perspective(mProjection, mainCamera.viewRangeDg * degrees2radians, screenAspectRatio, nearClip, farClip);
mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);
mViewProjection[1][3] = 0; //keystone effect
//scan subjects
int subjsN = gameSubjs.size();
for (int subjN = 0; subjN < subjsN; subjN++) {
GameSubj* pGS = gameSubjs.at(subjN);
//behavior - apply rotation speed
pGS->moveSubj();
//prepare subject for rendering
pGS->buildModelMatrix(pGS);
//build MVP matrix for given subject
mat4x4_mul(mMVP, mViewProjection, pGS->ownModelMatrix);
//build Model-View (rotation) matrix for normals
mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS->ownCoords.getRotationMatrix());
//convert to 3x3 matrix
float mMV3x3[3][3];
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
mMV3x3[y][x] = mMV4x4[y][x];
//subj's distance from camera
float cameraSpacePos[4];
mat4x4_mul_vec4plus(cameraSpacePos, mainCamera.lookAtMatrix, pGS->ownCoords.pos, 1);
float zDistance = abs(cameraSpacePos[2]);
float cotangentA = 1.0f / tanf(degrees2radians * mainCamera.viewRangeDg / 2.0);
float halfScreenVertSizeInUnits = zDistance / cotangentA;
float sizeUnitPixelsSize = screenSize[1] / 2.0 / 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->ownModelMatrix, 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) {
return (new GameSubj());
}
17. Компиляция и запуск. Результат:

Теперь – с золотой ленточкой.
Кстати, без по-вертексного HalfVector-а не получилось бы такой натуральной скользящей “искры”.
Теперь у меня есть и ответы:

Не будем углубляться здесь насколько он хорош (а он и правда хорош):
Однако меня больше интересует насколько он кросс-платформенный. Оказывается, не совсем.
А главное, он даже НЕ кросс-GPU !! Он GPU-specific, так что Vulkan программы для AMD и для, скажем, Nvidia будут отличаться!
Вобщем, пожалуй, правильно, что выбрал не его…

Ситуация с OpenGL ES выглядит для меня гораздо более оптимистично и обнадеживающе. Почему? Потому что:

Гугловский проект Angle (Almost Native Graphics Layer Engine) – неплохая иллюстрация к моему предыдущему тезису. Я раньше не знал, но главная задача этого Проекта – это кросс-платформенная поддержка WebGL-а. Это драйвер, который переводит вызовы OpenGL ES в нативные для данной платформы (если это Apple – то в Metal).
Пока Apple поддерживает OpenGL и так, но скоро (при достаточной последовательности и настойчивости) может и перестать. Но для Angle это проблемой не станет, он все-равно примет OpenGL ES команды, и отработает их хоть на Metal-е, хоть на чем-то еще что окажется у него под рукой.
Кстати, Angle может отрабатывать их и на Vulkan-е (если надо).
Я безмерно рад и благодарен (себе же), что изначально выбрал OpenGL ES. Это был действительно мудрый (и наилучший) выбор.
1. Записать (захватить) видео
Допустим, у меня на экране анимация, которую я собираюсь записать (захватить).
Windows 10 предлагает Capture утилиту для записи дисплея. Это часть Xbox Game Bar-а. Для запуска – нажмите Windows+G. Или можно открыть Start menu и запустить Xbox Game Bar оттуда.
В меню выбираем Widgets под-меню, и выбираем инструмент Capture. Выглядит так:

Наверное, ясно что с ним делать (просто начать и остановить запись). После этого Ваше видео будет сохранено в каталогеVideo\Captures, который находится в C:\Users\???\Videos\Captures.
2. Редактирование видео
Для этого понадобится какой-то Видео редактор. По мне – чем проще – тем лучше. Мое второе требование (скорее даже первое) – должен быть бесплатным. Попробовав несколько, остановился на Davinci Resolve 17. Далеко не самый простой, но сопровожден внятными обучалками, что существенно все облегчает.
2.1. Скачать можно здесь.
Запускаем. Первый экран:

Очевидно, double-click на “Untitled Project”.
2.2. Главный экран:

Первое что нужно сделать – это загрузить Ваши исходные видео-материалы.
2.3. Загрузка видео: Top menu -> File -> Import -> Media:

Выберите Ваш видео файл. Может спросить: “Change Project Frame Rate?” – Change.
Теперь он в Media Pool секции (сверху слева).
2.4. Drag-and-drop его в Timeline секцию:

2.5. Выбрать стартовый кадр Вашего клипа:

2.6. Запомнить его: Trim -> Trim Start

То же с финальным кадром: выбрать последний кадр, потом Trim -> Trim End.
2.7. В моем случае этого было достаточно, можно сохранять результат, для чего нужно переключиться в экран “Deliver“. Кнопка – в нижней панели управления:

2.8. Задать имя и каталог выходного файла, выбрать видео-формат:

2.9. И добавить в очередь на исполнение – Add to Render Queue:

2.10. И – Render All:

Когда отрендрилось – всё, Ваш клип – в Вашем каталоге.
3. Видео хостинг
Выходной файл (в моем случае mp4) довольно большой, так что лучше держать его не на своем сервере. Youtube представляется достойной альтернативой. Значит, идем на youtube.com, кликаем на Вашу иконку в верхнем правом углу, добавляем канал, заливаем Ваш клип, отвечаем на пару вопросов и публикуем!
Вам будет выдан линк, который можно врезать в Вашу страницу.
4. Linking to your page
Youtube также предоставляет HTML код для врезки в Веб-страницу. Мне пришлось его маленько подрихтовать, чтобы помещался получше и чтобы проигрывался циклично:
<iframe width="100%" height="400"
src="https://www.youtube.com/embed/eZ5UKOL3plY?controls=0&autoplay=1&loop=1&playlist=eZ5UKOL3plY"
title="Shader groups" frameborder="0" allow="accelerometer;
autoplay; clipboard-write; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen=""></iframe>
Вставляем код в Веб-страницу, публикуем. Результат:

Microsoft Visual Studio: Unable to start program ‘C:\CPP\…\w.exe’. Operation did not complete successfully because the file contains a virus or potentially unwanted software.
Типа, не могу запустить программу, потому что она содержит вирус или потенциально нежелательный код.
По-началу меня это не сильно расстраивало, потому что перезапуск обычно проблему решал.
Но в итоге Defender “продвинул” меня (вместе с моим w.exe) в какой-то свой ну ОЧЕНЬ черный список и запретил запускать ее вообще. Плюс начал удалять ее с диска.
Well, иногда АнтиВирусы ведут себя злее некоторых вирусов. Черт знает, что может привлечь их внимание.
Оказалось, что это достаточно распространенная проблема, знакомая многим разработчикам. Если Вы один из них (вернее из нас), то вот решение:
Надо просто исключить Ваш екзешник из рассмотрения АнтиВируса.
Эта инструкция – для Windows Defender-а. Если Вы пользуете что-то другое, то конкретные шаги вероятно будут отличаться, но идея – та же самая.
Открываем панель управления АнтиВируса:
Находим секцию исключений (Exclusions section):
Исключаем Ваш рабочий каталог из чек-листа АнтиВируса:
И всё. Больше он Вас беспокоить не будет.
На данный момент у нас есть вырезанные фрагменты с их “родными” собственными Материалами. В предыдущей главе мы просто заменили их на зеленый Phong. На самом же деле нам нужны родные Material-ы, только с добавленной normal map. Вобщем, нам нужно средство поднастроить существующий материал.
Реализация:
1. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln.
2. Под modeler добавим новый header file MaterialAdjust.h
Location: C:\CPP\engine\modeler
Код:
#pragma once
#include "Material.h"
class MaterialAdjust : public Material
{
public:
bool b_shaderType = false;
bool b_primitiveType = false;
bool b_uColor = false;
bool b_uTex0 = false;
bool b_uTex1mask = false;
bool b_uTex2nm = false;
bool b_uTex3 = false;
bool b_uTex1alphaChannelN = false;
bool b_uTex1alphaNegative = false;
bool b_uTex0translateChannelN = false;
bool b_uAlphaBlending = false;
bool b_uAlphaFactor = false;
bool b_uAmbient = false;
//specular light parameters
bool b_uSpecularIntencity = false;
bool b_uSpecularMinDot = false;
bool b_uSpecularPowerOf = false;
public:
static int adjust(Material* pMT, MaterialAdjust* pMA);
static int setWhat2adjust(MaterialAdjust* pMA, std::string tagStr);
};
3. Под modeler добавим новый C++ file MaterialAdjust.cpp
Location: C:\CPP\engine\modeler
Код:
#include "MaterialAdjust.h"
int MaterialAdjust::adjust(Material* pMT, MaterialAdjust* pMA) {
if (pMA->b_shaderType)
pMT->setShaderType(pMA->shaderType);
if (pMA->b_primitiveType)
pMT->primitiveType = pMA->primitiveType;
if (pMA->b_uColor) {
memcpy(&pMT->uColor, &pMA->uColor, sizeof(MyColor));
pMT->uTex0 = -1;
}
if (pMA->b_uTex0) {
pMT->uTex0 = pMA->uTex0;
pMT->uColor.setUint32(0);
}
if (pMA->b_uTex1mask)
pMT->uTex1mask = pMA->uTex1mask;
if (pMA->b_uTex2nm)
pMT->uTex2nm = pMA->uTex2nm;
if (pMA->b_uTex3)
pMT->uTex3 = pMA->uTex3;
if (pMA->b_uTex1alphaChannelN)
pMT->uTex1alphaChannelN = pMA->uTex1alphaChannelN;
if (pMA->b_uTex1alphaNegative)
pMT->uTex1alphaNegative = pMA->uTex1alphaNegative;
if (pMA->b_uTex0translateChannelN)
pMT->uTex0translateChannelN = pMA->uTex0translateChannelN;
if (pMA->b_uAlphaBlending)
pMT->uAlphaBlending = pMA->uAlphaBlending;
if (pMA->b_uAlphaFactor)
pMT->uAlphaFactor = pMA->uAlphaFactor;
if (pMA->b_uAmbient)
pMT->uAmbient = pMA->uAmbient;
if (pMA->b_uSpecularIntencity)
pMT->uSpecularIntencity = pMA->uSpecularIntencity;
if (pMA->b_uSpecularMinDot)
pMT->uSpecularMinDot = pMA->uSpecularMinDot;
if (pMA->b_uSpecularPowerOf)
pMT->uSpecularPowerOf = pMA->uSpecularPowerOf;
return 1;
}
int MaterialAdjust::setWhat2adjust(MaterialAdjust* pMA, std::string tagStr) {
if (tagStr.find("uTex0") != std::string::npos)
if(pMA->uTex0 >= 0)
pMA->b_uTex0 = true;
if (tagStr.find("uTex1mask") != std::string::npos)
pMA->b_uTex1mask = true;
if (tagStr.find("uTex2nm") != std::string::npos)
pMA->b_uTex2nm = true;
if (tagStr.find("uTex3") != std::string::npos)
pMA->b_uTex3 = true;
if (tagStr.find("mt_type") != std::string::npos)
pMA->b_shaderType = true;
if (tagStr.find("uColor") != std::string::npos)
pMA->b_uColor = true;
if (tagStr.find("primitiveType") != std::string::npos)
pMA->b_primitiveType = true;
if (tagStr.find("uTex1alphaChannelN") != std::string::npos)
pMA->b_uTex1alphaChannelN = true;
if (tagStr.find("uTex0translateChannelN") != std::string::npos)
pMA->b_uTex0translateChannelN = true;
if (tagStr.find("uAlphaBlending") != std::string::npos)
pMA->b_uAlphaBlending = true;
if (tagStr.find("uAlphaFactor") != std::string::npos)
pMA->b_uAlphaFactor = true;
if (tagStr.find("uAmbient") != std::string::npos)
pMA->b_uAmbient = true;
if (tagStr.find("uSpecularIntencity") != std::string::npos)
pMA->b_uSpecularIntencity = true;
if (tagStr.find("uSpecularMinDot") != std::string::npos)
pMA->b_uSpecularMinDot = true;
if (tagStr.find("uSpecularPowerOf") != std::string::npos)
pMA->b_uSpecularPowerOf = true;
return 1;
}
Обратите внимание: этот класс включает весь функционал из родительского класса Material плюс 2 новые функции:
В root01.txt у нас теперь новый таг:
<mt_adjust uTex2nm_use="tx0" >
4. Копируем нижеследующий код в Текстовый редактор и сохраняем его (overwrite) to/as
C:\CPP\a997modeler\dt\models\misc\marlboro01\root01.txt
<texture_as="tx0" src="marlboro03small.png" ckey="#00ff00"/>
<mt_type="phong" uTex0_use="tx0" />
<vs="box_tank" whl="53,83,21" ext=1 sectR=1 />
<a="front v" xywh="2,1,323,495" mark="box_front"/>
<a="back v" xywh="2,1,323,495" mark="box_back"/>
<a="right all" xywh="327,1,128,495" mark="box_right"/>
<a="left all" xywh="457,1,128,495" mark="box_left"/>
<a="top" xywh="588,1,323,133"/>
<a="bottom" xywh="587,136,324,134"/>
//golden prints
<vs="box" whl="55.1,85.1,23.1" />
<texture_as="whitenoise" src="/dt/common/img/whitenoise/wn64_blur3.bmp"/>
<texture_as="gold" src="/dt/common/img/materials/gold02roman.bmp" />
<mt_type="mirror" uAlphaBlending uTex1mask_use="tx0" uTex1alphaChannelN=1 uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
//side golden prints
<a="right" xywh="342,12,101,10" whl="x,1.8,18.1" pxyz="x,39.8, -0.3" /> //Please do not litter
<a="right" xywh="339,144,105,89" whl="x,15.35,18.9" pxyz="x,10.3,-0.12" /> //For special offers...
<a="left" xywh="475,15,95,48" whl="x,8.4,17" pxyz="x,36, 0.3" /> //Underage sale...
//front prints
<group>
//bottom golden print "20 class a..."
<a="front" xywh="20,498,289,13" whl="47.5,2,x" pxyz="1,-36,x" />
//blazon/emblem
<mt_type="mirror" uAlphaBlending uTex2nm_use="tx0" uTex0_use="whitenoise" uTex0translateChannelN=0 uTex3_use="gold" />
<a="front" xywh2nm="589,415,128,94" whl="20.7,16,x" pxyz="0.3,6.1,x" /> //emblem
//"Marlboro
<mt_type="phong" uAlphaBlending uTex2nm_use="tx0" uColor="#1E211E" />
<a="front" xywh2nm="590,275,301,136" whl="49.2,23.3,x" pxyz="0.21,-18,x" /> //marlboro
</group>
<clone ay=180 />
//joint (slit) between the pack and the lid
<group>
<mt_adjust uTex2nm_use="tx0" >
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_right" onThe="right" py=24.6 az=31 />
<a2mesh wh="50,1" xywh2nm="582,497,1,4" all markedAs="box_left" onThe="left" py=24.6 az=-31 />
<a2mesh wh="53,1" xywh2nm="582,497,1,4" all markedAs="box_front" py=17.8 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=23.5 />
<a2mesh wh="6 ,1" xywh2nm="582,497,1,4" all markedAs="box_back" onThe="back" py=31.5 px=-23.5 />
</mt_adjust>
</group sizeD="0.1,0,0.1">
Теперь – чтение и исполнение новых тагов и параметров:
5. Заменим 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;
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);
};
6. Заменим 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
int subjN = pSubjsVector0->size();
GameSubj* pGS = theGame.newGameSubj(subjClass);
pSubjsVector0->push_back(pGS);
//pGS->djStartN = DrawJob::drawJobs.size();
ModelLoader* pML = new ModelLoader(pSubjsVector0, subjN, NULL, sourceFile);
processSource(pML);
delete pML;
//pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return subjN;
}
int ModelLoader::setValueFromIntHashMap(int* pInt, std::map<std::string, int> intHashMap, std::string varName, std::string tagStr) {
if (!varExists(varName, tagStr))
return 0;
std::string str0 = getStringValue(varName, tagStr);
if (intHashMap.find(str0) == intHashMap.end()) {
mylog("ERROR in ModelLoader::setValueFromIntMap, %s not found, %s\n", varName.c_str(), tagStr.c_str());
return -1;
}
*pInt = intHashMap[getStringValue(varName, tagStr)];
return 1;
}
int ModelLoader::setTexture(ModelLoader* pML, int* pInt, std::string txName) {
ModelBuilder* pMB = pML->pModelBuilder;
std::string varName = txName + "_use";
if (setValueFromIntHashMap(pInt, pMB->texturesHashMap, varName, pML->currentTag) == 0) {
//the texture is not in hash table
varName = txName + "_src";
if (varExists(varName, pML->currentTag)) {
std::string txFile = getStringValue(varName, pML->currentTag);
varName = txName + "_ckey";
unsigned int intCkey = 0;
setUintColorValue(&intCkey, varName, pML->currentTag);
*pInt = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
}
}
return 1;
}
int ModelLoader::setMaterialTextures(ModelLoader* pML, Material* pMT) {
setTexture(pML, &pMT->uTex0, "uTex0");
setTexture(pML, &pMT->uTex1mask, "uTex1mask");
setTexture(pML, &pMT->uTex2nm, "uTex2nm");
setTexture(pML, &pMT->uTex3, "uTex3");
return 1;
}
int ModelLoader::fillProps_mt(Material* pMT, std::string tagStr, ModelLoader* pML) {
setCharsValue(pMT->shaderType, 20, "mt_type", tagStr);
setMaterialTextures(pML, pMT);
//color
if (varExists("uColor", tagStr)) {
unsigned int uintColor = 0;
setUintColorValue(&uintColor, "uColor", tagStr);
pMT->uColor.setUint32(uintColor);
}
//mylog("mt.uTex0=%d, mt.uTex1mask=%d\n", mt.uTex0, mt.uTex1mask);
if (varExists("primitiveType", tagStr)) {
std::string str0 = getStringValue("primitiveType", tagStr);
if (str0.compare("GL_POINTS") == 0) pMT->primitiveType = GL_POINTS;
else if (str0.compare("GL_LINES") == 0) pMT->primitiveType = GL_LINES;
else if (str0.compare("GL_LINE_STRIP") == 0) pMT->primitiveType = GL_LINE_STRIP;
else if (str0.compare("GL_LINE_LOOP") == 0) pMT->primitiveType = GL_LINE_LOOP;
else if (str0.compare("GL_TRIANGLE_STRIP") == 0) pMT->primitiveType = GL_TRIANGLE_STRIP;
else if (str0.compare("GL_TRIANGLE_FAN") == 0) pMT->primitiveType = GL_TRIANGLE_FAN;
else pMT->primitiveType = GL_TRIANGLES;
}
setIntValue(&pMT->uTex1alphaChannelN, "uTex1alphaChannelN", tagStr);
setIntValue(&pMT->uTex0translateChannelN, "uTex0translateChannelN", tagStr);
setIntBoolValue(&pMT->uAlphaBlending, "uAlphaBlending", tagStr);
setFloatValue(&pMT->uAlphaFactor, "uAlphaFactor", tagStr);
setFloatValue(&pMT->uAmbient, "uAmbient", tagStr);
setFloatValue(&pMT->uSpecularIntencity, "uSpecularIntencity", tagStr);
setFloatValue(&pMT->uSpecularMinDot, "uSpecularMinDot", tagStr);
setFloatValue(&pMT->uSpecularPowerOf, "uSpecularPowerOf", tagStr);
return 1;
}
int ModelLoader::processTag(ModelLoader* pML) {
ModelBuilder* pMB = pML->pModelBuilder;
if (pML->tagName.compare("texture_as") == 0) {
//saves texture N in texturesMap under given name
std::string keyName = getStringValue("texture_as", pML->currentTag);
if (pMB->texturesHashMap.find(keyName) != pMB->texturesHashMap.end())
return pMB->texturesHashMap[keyName];
else { //add new
std::string txFile = getStringValue("src", pML->currentTag);
unsigned int intCkey = 0;
setUintColorValue(&intCkey, "ckey", pML->currentTag);
int txN = Texture::loadTexture(buildFullPath(pML, txFile), intCkey);
pMB->texturesHashMap[keyName] = txN;
//mylog("%s=%d\n", keyName.c_str(), pMB->texturesMap[keyName]);
return txN;
}
}
if (pML->tagName.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;
}
//mylog("%s, %s /group?=%d\n",pML->currentTag.c_str(), pML->tagName.c_str(), (pML->tagName.compare("/group") == 0));
mylog("ERROR in ModelLoader::processTag, unhandled tag %s, file %s\n", pML->currentTag.c_str(), pML->fullPath.c_str());
return -1;
}
int ModelLoader::fillProps_vs(VirtualShape* pVS, std::string tagStr) {
//sets virtual shape
setCharsValue(pVS->shapeType, 20, "vs", tagStr);
setFloatArray(pVS->whl, 3, "whl", tagStr);
//extensions
float ext;
if (varExists("ext", tagStr)) {
setFloatValue(&ext, "ext", tagStr);
pVS->setExt(ext);
}
if (varExists("extX", tagStr)) {
setFloatValue(&ext, "extX", tagStr);
pVS->setExtX(ext);
}
if (varExists("extY", tagStr)) {
setFloatValue(&ext, "extY", tagStr);
pVS->setExtY(ext);
}
if (varExists("extZ", tagStr)) {
setFloatValue(&ext, "extZ", tagStr);
pVS->setExtZ(ext);
}
setFloatValue(&pVS->extU, "extU", tagStr);
setFloatValue(&pVS->extD, "extD", tagStr);
setFloatValue(&pVS->extL, "extL", tagStr);
setFloatValue(&pVS->extR, "extR", tagStr);
setFloatValue(&pVS->extF, "extF", tagStr);
setFloatValue(&pVS->extB, "extB", tagStr);
//sections
setIntValue(&pVS->sectionsR, "sectR", tagStr);
setIntValue(&pVS->sections[0], "sectX", tagStr);
setIntValue(&pVS->sections[1], "sectY", tagStr);
setIntValue(&pVS->sections[2], "sectZ", tagStr);
//mylog("pVS->shapeType=%s whl=%fx%fx%f\n", pVS->shapeType, pVS->whl[0], pVS->whl[1], pVS->whl[2]);
return 1;
}
int ModelLoader::processTag_a(ModelLoader* pML) {
//apply
ModelBuilder* pMB = pML->pModelBuilder;
std::string tagStr = pML->currentTag;
pMB->lockGroup(pMB);
//mark
if (varExists("mark", tagStr))
addMark(pMB->pCurrentGroup->marks, getStringValue("mark", tagStr));
std::vector<std::string> applyTosVector = splitString(pML->getStringValue("a", tagStr), ",");
Material* pMT = pMB->materialsList.at(pMB->usingMaterialN);
int texN = pMT->uTex1mask;
if (texN < 0)
texN = pMT->uTex0;
float xywh[4] = { 0,0,1,1 };
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);
}
//move/rotate
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;
}
Обновленный ModelLoader требует дополнительного функционала от класса ModelBuilder.
7. Заменим 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<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*> gameSubjs);
static int rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
static int buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
static int moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ);
static int calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles);
};
8. Заменим ModelBuilder1base.cpp код на:
#include "ModelBuilder1base.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder1base::~ModelBuilder1base() {
releaseGroup(this);
//clear all vectors
int itemsN = vertices.size();
for (int i = 0; i < itemsN; i++)
delete vertices.at(i);
vertices.clear();
itemsN = triangles.size();
for (int i = 0; i < itemsN; i++)
delete triangles.at(i);
triangles.clear();
itemsN = vShapesStack.size();
for (int i = 0; i < itemsN; i++)
delete vShapesStack.at(i);
vShapesStack.clear();
itemsN = groupsStack.size();
for (int i = 0; i < itemsN; i++)
delete groupsStack.at(i);
groupsStack.clear();
if (pCurrentGroup != NULL)
delete pCurrentGroup;
if (pLastClosedGroup != NULL)
delete pLastClosedGroup;
itemsN = materialsList.size();
for (int i = 0; i < itemsN; i++)
delete materialsList.at(i);
materialsList.clear();
subjNumbersList.clear();
}
int ModelBuilder1base::useSubjN(ModelBuilder1base* pMB, int subjN) {
pMB->usingSubjN = subjN;
int itemsN = pMB->subjNumbersList.size();
bool newN = true;
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (pMB->subjNumbersList.at(i) == subjN) {
newN = false;
break;
}
if (newN)
pMB->subjNumbersList.push_back(subjN);
return subjN;
}
int ModelBuilder1base::getMaterialN(ModelBuilder1base* pMB, Material* pMT) {
int itemsN = pMB->materialsList.size();
if (itemsN > 0)
for (int i = 0; i < itemsN; i++)
if (memcmp(pMB->materialsList.at(i), pMT, sizeof(Material)) == 0) {
return i;
}
//if here - add new material to the list
Material* pMTnew = new Material(*pMT);
pMB->materialsList.push_back(pMTnew);
return itemsN;
}
int ModelBuilder1base::add2triangles(ModelBuilder1base* pMB, int nNW, int nNE, int nSW, int nSE, int n) {
//indexes: NorthWest, NorthEast, SouthWest,SouthEast
if (n % 2 == 0) { //even number
addTriangle(pMB, nNW, nSW, nNE);
addTriangle(pMB, nNE, nSW, nSE);
}
else { //odd number
addTriangle(pMB, nNW, nSE, nNE);
addTriangle(pMB, nNW, nSW, nSE);
}
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addTriangle(ModelBuilder1base* pMB, int i0, int i1, int i2) {
Triangle01* pTR = new Triangle01();
pMB->triangles.push_back(pTR);
pTR->idx[0] = i0;
pTR->idx[1] = i1;
pTR->idx[2] = i2;
pTR->subjN = pMB->usingSubjN;
pTR->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pTR->marks, 124, pMB->pCurrentGroup->marks);
return pMB->triangles.size() - 1;
}
int ModelBuilder1base::addVertex(ModelBuilder1base* pMB, float kx, float ky, float kz, float nx, float ny, float nz) {
Vertex01* pVX = new Vertex01();
pMB->vertices.push_back(pVX);
pVX->aPos[0] = kx;
pVX->aPos[1] = ky;
pVX->aPos[2] = kz;
//normal
pVX->aNormal[0] = nx;
pVX->aNormal[1] = ny;
pVX->aNormal[2] = nz;
pVX->subjN = pMB->usingSubjN;
pVX->materialN = pMB->usingMaterialN;
//mark
if (pMB->pCurrentGroup != NULL)
if (strcmp(pMB->pCurrentGroup->marks, "") != 0)
myStrcpy_s(pVX->marks, 124, pMB->pCurrentGroup->marks);
return pMB->vertices.size() - 1;
}
int ModelBuilder1base::buildDrawJobs(ModelBuilder1base* pMB, std::vector<GameSubj*> gameSubjs) {
int totalSubjsN = pMB->subjNumbersList.size();
if (totalSubjsN < 1) {
pMB->subjNumbersList.push_back(-1);
totalSubjsN = 1;
}
int totalMaterialsN = pMB->materialsList.size();
if (totalSubjsN < 2 && totalMaterialsN < 2) {
//simple single DrawJob
Material* pMT = pMB->materialsList.at(0);
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(0);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
buildSingleDrawJob(pMT, pMB->vertices, pMB->triangles);
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
return 1;
}
int totalVertsN = pMB->vertices.size();
int totalTrianglesN = pMB->triangles.size();
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
pVX->flag = 0;
}
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
pTR->flag = 0;
}
int addedDJs = 0;
for (int sN = 0; sN < totalSubjsN; sN++) {
GameSubj* pGS = NULL;
int gsN = pMB->subjNumbersList.at(sN);
if (gsN >= 0)
pGS = gameSubjs.at(gsN);
if (pGS != NULL)
pGS->djStartN = DrawJob::drawJobs.size();
for (int mtN = 0; mtN < totalMaterialsN; mtN++) {
Material* pMT = pMB->materialsList.at(mtN);
std::vector<Vertex01*> useVertices;
std::vector<Triangle01*> useTriangles;
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if (pVX->flag != 0)
continue;
if (pVX->subjN != gsN)
continue;
if (pVX->materialN != mtN)
continue;
//if here - make a copy
Vertex01* pVX2 = new Vertex01(*pVX);
useVertices.push_back(pVX2);
pVX2->altN = vN;
pVX->flag = 1;
if (pVX->endOfSequence > 0) {
rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear and proceed to next sequence
int useVerticesN = useVertices.size();
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
}
}
int useVerticesN = useVertices.size();
if (useVerticesN < 1)
continue; //to next material
//pick triangles
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = pMB->triangles.at(tN);
if (pTR->flag != 0)
continue;
if (pTR->subjN != gsN)
continue;
if (pTR->materialN != mtN)
continue;
//if here - make a copy
Triangle01* pTR2 = new Triangle01(*pTR);
useTriangles.push_back(pTR2);
pTR->flag = 1;
}
rearrangeArraysForDrawJob(pMB, pMB->vertices, useVertices, useTriangles);
buildSingleDrawJob(pMT, useVertices, useTriangles);
addedDJs++;
//clear all for next material
for (int i = 0; i < useVerticesN; i++)
delete useVertices.at(i);
useVertices.clear();
int useTrianglesN = useTriangles.size();
for (int i = 0; i < useTrianglesN; i++)
delete useTriangles.at(i);
useTriangles.clear();
}
if (pGS != NULL)
pGS->djTotalN = DrawJob::drawJobs.size() - pGS->djStartN;
}
return addedDJs;
}
int ModelBuilder1base::buildSingleDrawJob(Material* pMT, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
if (pMT->uTex2nm >= 0)
calculateTangentSpace(useVertices, useTriangles);
pMT->pickShaderNumber();
DrawJob* pDJ = new DrawJob();
//copy material to DJ
memcpy(&pDJ->mt, pMT, sizeof(Material));
//calculate VBO element size (stride) and variables offsets in VBO
int VBOid = DrawJob::newBufferId();
int stride = 0;
pDJ->setDesirableOffsets(&stride, pDJ->mt.shaderN, VBOid);
//create an array for VBO
int bufferSize = totalVertsN * stride;
float* vertsBuffer = new float[bufferSize];
//fill vertsBuffer
Shader* pSh = Shader::shaders.at(pDJ->mt.shaderN);
int floatSize = sizeof(float);
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
int idx = vN * stride / floatSize;
//pick data from vertex and move to the buffer
memcpy(&vertsBuffer[idx + pDJ->aPos.offset / floatSize], pVX->aPos, 3 * floatSize);
if (pSh->l_aNormal >= 0) //normal
memcpy(&vertsBuffer[idx + pDJ->aNormal.offset / floatSize], pVX->aNormal, 3 * floatSize);
if (pSh->l_aTuv >= 0) //attribute TUV (texture coordinates)
memcpy(&vertsBuffer[idx + pDJ->aTuv.offset / floatSize], pVX->aTuv, 2 * floatSize);
if (pSh->l_aTuv2 >= 0) //attribute TUV2 (normal maps)
memcpy(&vertsBuffer[idx + pDJ->aTuv2.offset / floatSize], pVX->aTuv2, 2 * floatSize);
if (pSh->l_aTangent >= 0)
memcpy(&vertsBuffer[idx + pDJ->aTangent.offset / floatSize], pVX->aTangent, 3 * floatSize);
if (pSh->l_aBinormal >= 0)
memcpy(&vertsBuffer[idx + pDJ->aBinormal.offset / floatSize], pVX->aBinormal, 3 * floatSize);
}
//buffer is ready, create VBO
glBindBuffer(GL_ARRAY_BUFFER, VBOid);
glBufferData(GL_ARRAY_BUFFER, bufferSize * floatSize, vertsBuffer, GL_STATIC_DRAW);
delete[] vertsBuffer;
pDJ->pointsN = totalVertsN;
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN > 0) {
//create EBO
int totalIndexesN = totalTrianglesN * 3;
//create buffer
GLushort* indexBuffer = new GLushort[totalIndexesN];
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles[tN];
int idx = tN * 3;
indexBuffer[idx] = (GLushort)pTR->idx[0];
indexBuffer[idx + 1] = (GLushort)pTR->idx[1];
indexBuffer[idx + 2] = (GLushort)pTR->idx[2];
}
//buffer is ready, create IBO
pDJ->glEBOid = DrawJob::newBufferId();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pDJ->glEBOid);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexesN * sizeof(GLushort), indexBuffer, GL_STATIC_DRAW);
delete[] indexBuffer;
pDJ->pointsN = totalIndexesN;
}
//create and fill vertex attributes array (VAO)
pDJ->buildVAO();
return 1;
}
int ModelBuilder1base::rearrangeArraysForDrawJob(ModelBuilder1base* pMB, std::vector<Vertex01*> allVertices, std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalTrianglesN = useTriangles.size();
if (totalTrianglesN < 1)
return 0;
int totalVerticesN = useVertices.size();
//save new vertices order in original vertices array
//since triangles indices refer to original vertices order
for (int i = 0; i < totalVerticesN; i++) {
Vertex01* pVX1 = useVertices.at(i);
Vertex01* pVX0 = allVertices.at(pVX1->altN);
pVX0->altN = i;
}
//replace triangle original indices by new numbers saved in original vertices altN
for (int tN = 0; tN < totalTrianglesN; tN++) {
Triangle01* pTR = useTriangles.at(tN);
for (int i = 0; i < 3; i++) {
Vertex01* pVX0 = allVertices.at(pTR->idx[i]);
pTR->idx[i] = pVX0->altN;
}
}
return 1;
}
int ModelBuilder1base::moveGroupDg(ModelBuilder1base* pMB, float aX, float aY, float aZ, float kX, float kY, float kZ) {
//moves and rotates vertex group
//rotation angles are set in degrees
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_translate(transformMatrix, kX, kY, kZ);
//rotation order: Z-X-Y
if (aY != 0) mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * aY);
if (aX != 0) mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * aX);
if (aZ != 0) mat4x4_rotate_Z(transformMatrix, transformMatrix, degrees2radians * aZ);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
}
return 1;
}
int ModelBuilder1base::calculateTangentSpace(std::vector<Vertex01*> useVertices, std::vector<Triangle01*> useTriangles) {
int totalVertsN = useVertices.size();
if (totalVertsN < 1)
return 0;
int totalTrianglesN = useTriangles.size();
//assuming that GL_TRIANGLES
//clear flags
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
pV->flag = 0;
}
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pVX = useVertices.at(vN);
if (pVX->flag != 0)
continue;
Triangle01* pT = NULL;
for (int tN = 0; tN < totalTrianglesN; tN++) {
pT = useTriangles.at(tN);
bool haveTriangle = false;
for (int i = 0; i < 3; i++)
if (pT->idx[i] == vN) {
haveTriangle = true;
break;
}
if (haveTriangle)
break;
}
Vertex01* pV[3];
for (int i = 0; i < 3; i++)
pV[i] = useVertices.at(pT->idx[i]);
float dPos1[3];
float dPos2[3];
float dUV1[2];
float dUV2[2];
for (int i = 0; i < 3; i++) {
dPos1[i] = pV[1]->aPos[i] - pV[0]->aPos[i];
dPos2[i] = pV[2]->aPos[i] - pV[0]->aPos[i];
}
for (int i = 0; i < 2; i++) {
dUV1[i] = pV[1]->aTuv2[i] - pV[0]->aTuv2[i];
dUV2[i] = pV[2]->aTuv2[i] - pV[0]->aTuv2[i];
}
float tangent[3];
float binormal[3];
float divider = dUV1[0] * dUV2[1] - dUV1[1] * dUV2[0];
if (divider == 0) {
v3set(tangent, 1, 0, 0);
v3set(binormal, 0, -1, 0);
}
else {
float r = 1.0f / divider;
for (int i = 0; i < 3; i++) {
tangent[i] = (dPos1[i] * dUV2[1] - dPos2[i] * dUV1[1]) * r;
binormal[i] = -(dPos2[i] * dUV1[0] - dPos1[i] * dUV2[0]) * r;
}
vec3_norm(tangent, tangent);
vec3_norm(binormal, binormal);
}
//add to all 3 vertices
for (int n = 0; n < 3; n++) {
if (pV[n]->flag > 0)
continue;
v3copy(pV[n]->aTangent, tangent);
v3copy(pV[n]->aBinormal, binormal);
pV[n]->flag = 1;
}
}
//normalize tangent and binormal around normal
for (int vN = 0; vN < totalVertsN; vN++) {
Vertex01* pV = useVertices.at(vN);
float v3out[3];
//tangent
vec3_mul_cross(v3out, pV->aNormal, pV->aBinormal);
if (v3dotProduct(pV->aTangent, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aTangent, v3out);
//binormal
vec3_mul_cross(v3out, pV->aNormal, pV->aTangent);
if (v3dotProduct(pV->aBinormal, v3out) < 0)
v3inverse(v3out);
v3copy(pV->aBinormal, v3out);
}
return 1;
}
void ModelBuilder1base::lockGroup(ModelBuilder1base* pMB) {
Group01* pPrevGroup = pMB->pCurrentGroup;
if (pMB->pCurrentGroup != NULL)
pMB->groupsStack.push_back(pMB->pCurrentGroup);
pMB->pCurrentGroup = new Group01();
pMB->pCurrentGroup->fromVertexN = pMB->vertices.size();
pMB->pCurrentGroup->fromTriangleN = pMB->triangles.size();
//marks
if(pPrevGroup != NULL)
if (strcmp(pPrevGroup->marks, "") != 0)
myStrcpy_s(pMB->pCurrentGroup->marks, 124, pPrevGroup->marks);
}
void ModelBuilder1base::releaseGroup(ModelBuilder1base* pMB) {
if (pMB->pLastClosedGroup != NULL)
delete pMB->pLastClosedGroup;
pMB->pLastClosedGroup = pMB->pCurrentGroup;
if (pMB->groupsStack.size() > 0) {
pMB->pCurrentGroup = pMB->groupsStack.back();
pMB->groupsStack.pop_back();
}
else
pMB->pCurrentGroup = NULL;
}
9. Заменим ModelBuilder.h код на:
#pragma once
#include "ModelBuilder1base.h"
#include "TexCoords.h"
class ModelBuilder : public ModelBuilder1base
{
public:
virtual ~ModelBuilder();
static int buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC = NULL, TexCoords* pTC2nm = NULL);
static int buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC = NULL, TexCoords* pTC2nm = NULL);
static int buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);
static int buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS);
static int cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);
static int capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo);
static int groupApplyTexture(ModelBuilder* pMB, std::string applyTo, TexCoords* pTC, TexCoords* pTC2nm = NULL);
static int applyTexture2flagged(std::vector<Vertex01*>* pVX, std::string applyTo, TexCoords* pTC, bool isNormalMap);
};
10. Заменим ModelBuilder.cpp код на:
#include "ModelBuilder.h"
#include "platform.h"
#include "utils.h"
#include "DrawJob.h"
#include "Shader.h"
extern float degrees2radians;
ModelBuilder::~ModelBuilder() {
}
int ModelBuilder::buildFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC, TexCoords* pTC2nm) {
if (strstr(pVS->shapeType, "box") == pVS->shapeType)
return buildBoxFace(pMB, applyTo, pVS, pTC, pTC2nm);
return -1;
}
int ModelBuilder::buildBoxFace(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS, TexCoords* pTC, TexCoords* pTC2nm) {
//this code is for simple box
VirtualShape vs; //face VS,
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
vs.sectionsR = pVS->sectionsR;
//rotate desirable side to face us.
if (applyTo.find("front") == 0) {
//Side <front> is facing us as is.
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extF;
vs.extL = pVS->extL;
vs.extR = pVS->extR;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//define how to move/place generated face back to the VirtualShape
//just shift closer to us by length/2
mat4x4_translate(transformMatrix, 0, 0, pVS->whl[2] / 2);
}
else if (applyTo.find("back") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extB;
vs.extL = pVS->extR;
vs.extR = pVS->extL;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate 180 degrees around Y and shift farther from us by half-length
mat4x4_translate(transformMatrix, 0, 0, -pVS->whl[2] / 2);
mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 180);
}
else if (applyTo.find("left") == 0) {
vs.whl[0] = pVS->whl[2]; //width = original length
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[2];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extL;
vs.extL = pVS->extB;
vs.extR = pVS->extF;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate -90 degrees around Y (CW) and shift half-width to the left
mat4x4_translate(transformMatrix, -pVS->whl[0] / 2, 0, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, -degrees2radians * 90);
}
else if (applyTo.find("right") == 0) {
vs.whl[0] = pVS->whl[2]; //width = original length
vs.whl[1] = pVS->whl[1];
vs.sections[0] = pVS->sections[2];
vs.sections[1] = pVS->sections[1];
//extensions
vs.extF = pVS->extR;
vs.extL = pVS->extF;
vs.extR = pVS->extB;
vs.extU = pVS->extU;
vs.extD = pVS->extD;
//rotate +90 degrees around Y (CCW) and shift half-width to the right
mat4x4_translate(transformMatrix, pVS->whl[0] / 2, 0, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 90);
}
else if (applyTo.find("top") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[2]; //height = original length
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[2];
//extensions
vs.extF = pVS->extU;
vs.extL = pVS->extR;
vs.extR = pVS->extL;
vs.extU = pVS->extF;
vs.extD = pVS->extB;
//rotate -90 degrees around X (CW) and 180 around Y, and shift half-height up
mat4x4_translate(transformMatrix, 0, pVS->whl[1] / 2, 0);
mat4x4_rotate_Y(transformMatrix, transformMatrix, degrees2radians * 180);
mat4x4_rotate_X(transformMatrix, transformMatrix, -degrees2radians * 90);
}
else if (applyTo.find("bottom") == 0) {
vs.whl[0] = pVS->whl[0];
vs.whl[1] = pVS->whl[2]; //height = original length
vs.sections[0] = pVS->sections[0];
vs.sections[1] = pVS->sections[2];
//extensions
vs.extF = pVS->extD;
vs.extL = pVS->extL;
vs.extR = pVS->extR;
vs.extU = pVS->extF;
vs.extD = pVS->extB;
//rotate 90 around X (CCW) and shift half-height down
mat4x4_translate(transformMatrix, 0, -pVS->whl[1] / 2, 0);
mat4x4_rotate_X(transformMatrix, transformMatrix, degrees2radians * 90);
}
lockGroup(pMB);
//create vertices
if (strstr(pVS->shapeType, "tank") != nullptr)
buildBoxFaceTank(pMB, applyTo, &vs);
else
buildBoxFacePlain(pMB, applyTo, &vs);
groupApplyTexture(pMB, "front", pTC, pTC2nm);
//move face to it's place (apply transform matrix)
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
mat4x4_mul_vec4plus(pVX->aNormal, transformMatrix, pVX->aNormal, 0);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::buildBoxFacePlain(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {
if (pVS->whl[0] == 0 || pVS->whl[1] == 0)
return 0;
//create vertices
int sectionsX = pVS->sections[0];
int sectionsY = pVS->sections[1];
int pointsX = sectionsX + 1;
int pointsY = sectionsY + 1;
float stepX = pVS->whl[0] / sectionsX;
float stepY = pVS->whl[1] / sectionsY;
float kY = pVS->whl[1] / 2;
for (int iy = 0; iy < pointsY; iy++) {
float kX = -pVS->whl[0] / 2;
for (int ix = 0; ix < pointsX; ix++) {
int nSE = addVertex(pMB, kX, kY, pVS->extF, 0, 0, 1); //vertex number on south-east
if (iy > 0 && ix > 0) {
//add 2 triangles
int nSW = nSE - 1; //vertex number south-west
int nNE = nSE - pointsX; //north-east
int nNW = nSW - pointsX; //north-west
add2triangles(pMB, nNW, nNE, nSW, nSE, iy + ix);
}
kX += stepX;
}
kY -= stepY;
}
return 1;
}
int ModelBuilder::buildBoxFaceTank(ModelBuilder* pMB, std::string applyTo, VirtualShape* pVS) {
//for diamond effect - sectionsRad=1, don't merge normals
bool drawMiddle = true;
//edges
bool drawTop = false;
bool drawBottom = false;
bool drawLeft = false;
bool drawRight = false;
//corners
bool drawTopLeft = false;
bool drawTopRight = false;
bool drawBottomLeft = false;
bool drawBottomRight = false;
if (pVS->extF == 0 || applyTo.find(" all") != std::string::npos) {
drawTop = true;
drawBottom = true;
drawLeft = true;
drawRight = true;
drawTopLeft = true;
drawTopRight = true;
drawBottomLeft = true;
drawBottomRight = true;
}
else if (applyTo.find(" h") != std::string::npos) {
drawLeft = true;
drawRight = true;
}
else if (applyTo.find(" v") != std::string::npos) {
drawTop = true;
drawBottom = true;
}
if (applyTo.find(" no") != std::string::npos) {
if (applyTo.find(" noM") != std::string::npos) {
//middle
if (applyTo.find(" noMrow") != std::string::npos) {
drawMiddle = false;
drawLeft = false;
drawRight = false;
}
if (applyTo.find(" noMcol") != std::string::npos) {
drawMiddle = false;
drawTop = false;
drawBottom = false;
}
if (applyTo.find(" noMid") != std::string::npos)
drawMiddle = false;
}
if (applyTo.find(" noN") != std::string::npos) {
//north
if (applyTo.find(" noNrow") != std::string::npos) {
drawTop = false;
drawTopLeft = false;
drawTopRight = false;
}
if (applyTo.find(" noNedge") != std::string::npos)
drawTop = false;
if (applyTo.find(" noNW") != std::string::npos)
drawTopLeft = false;
if (applyTo.find(" noNE") != std::string::npos)
drawTopRight = false;
}
if (applyTo.find(" noS") != std::string::npos) {
//south
if (applyTo.find(" noSrow") != std::string::npos) {
drawBottom = false;
drawBottomLeft = false;
drawBottomRight = false;
}
if (applyTo.find(" noSedge") != std::string::npos)
drawBottom = false;
if (applyTo.find(" noSW") != std::string::npos)
drawBottomLeft = false;
if (applyTo.find(" noSE") != std::string::npos)
drawBottomRight = false;
}
if (applyTo.find(" noW") != std::string::npos) {
//west
if (applyTo.find(" noWcol") != std::string::npos) {
drawLeft = false;
drawTopLeft = false;
drawBottomLeft = false;
}
if (applyTo.find(" noWedge") != std::string::npos)
drawLeft = false;
}
if (applyTo.find(" noE") != std::string::npos) {
//east
if (applyTo.find(" noEcol") != std::string::npos) {
drawRight = false;
drawTopRight = false;
drawBottomRight = false;
}
if (applyTo.find(" noEedge") != std::string::npos)
drawRight = false;
}
}
lockGroup(pMB);
//middle
if (pVS->whl[0] > 0 && pVS->whl[1] > 0 && drawMiddle) {
buildBoxFacePlain(pMB, applyTo, pVS);
}
VirtualShape vs;
//edges
//vs.type.assign("cylinder");
vs.sectionsR = pVS->sectionsR;
if (pVS->whl[0] > 0) {
vs.sections[2] = pVS->sections[0]; //cylinder Z sections n
vs.whl[2] = pVS->whl[0]; //cylinder length Z
vs.whl[0] = pVS->extF * 2; //cylinder diameter X
if (pVS->extU > 0 && drawTop) {
vs.whl[1] = pVS->extU * 2; //cylinder diameter Y
lockGroup(pMB);
cylinderWrap(pMB, &vs, 0, 90);
//rotate -90 degrees around Y and shift up
moveGroupDg(pMB, 0, -90, 0, 0, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extD > 0 && drawBottom) {
vs.whl[1] = pVS->extD * 2; //cylinder diameter Y
lockGroup(pMB);
cylinderWrap(pMB, &vs, -90, 0);
//rotate -90 degrees around Y and shift down
moveGroupDg(pMB, 0, -90, 0, 0, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->whl[1] > 0) {
vs.sections[2] = pVS->sections[1]; //cylinder Z sections n
vs.whl[2] = pVS->whl[1]; //cylinder length Z
vs.whl[1] = pVS->extF * 2; //cylinder diameter Y
if (pVS->extL > 0 && drawLeft) {
vs.whl[0] = pVS->extL * 2; //cylinder diameter X
lockGroup(pMB);
cylinderWrap(pMB, &vs, 90, 180);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 90, 0, 0, -pVS->whl[0] * 0.5f, 0, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawRight) {
vs.whl[0] = pVS->extR * 2; //cylinder diameter X
lockGroup(pMB);
cylinderWrap(pMB, &vs, 0, 90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 90, 0, 0, pVS->whl[0] * 0.5f, 0, 0);
releaseGroup(pMB);
}
}
//corners
//vs.type.assign("cap");
vs.sectionsR = pVS->sectionsR;
vs.sections[2] = pVS->sectionsR;
vs.whl[2] = pVS->extF;
if (pVS->extU > 0) {
//top corners
vs.whl[1] = pVS->extU * 2;
if (pVS->extL > 0 && drawTopLeft) {
vs.whl[0] = pVS->extL * 2;
lockGroup(pMB);
capWrap(pMB, &vs, 90, 180);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawTopRight) {
vs.whl[0] = pVS->extR * 2;
lockGroup(pMB);
capWrap(pMB, &vs, 0, 90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->extD > 0) {
//bottom corners
vs.whl[1] = pVS->extD * 2;
if (pVS->extL > 0 && drawBottomLeft) {
vs.whl[0] = pVS->extL * 2;
lockGroup(pMB);
capWrap(pMB, &vs, -180, -90);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, -pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
if (pVS->extR > 0 && drawBottomRight) {
vs.whl[0] = pVS->extR * 2;
lockGroup(pMB);
capWrap(pMB, &vs, -90, 0);
//rotate 90 degrees around Y and shift left
moveGroupDg(pMB, 0, 0, 0, pVS->whl[0] * 0.5f, -pVS->whl[1] * 0.5f, 0);
releaseGroup(pMB);
}
}
if (pVS->extF == 0) {
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
//normal
v3set(pVX->aNormal, 0, 0, 1);
}
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::cylinderWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {
// angleFrom/To - in degrees
lockGroup(pMB);
float stepZ = pVS->whl[2] / pVS->sections[2];
float stepDg = (angleTo - angleFrom) / pVS->sectionsR; //in degrees
for (int nz = 0; nz <= pVS->sections[2]; nz++) {
float kz = stepZ * nz - pVS->whl[2] * 0.5f;
for (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {
// rpn - radial point number
float angleRd = (angleFrom + stepDg * rpn) * degrees2radians;
float kx = cosf(angleRd);
float ky = sinf(angleRd);
int nSE = addVertex(pMB, kx, ky, kz, kx, ky, 0);
if (nz > 0 && rpn > 0) {
int nSW = nSE - 1;
int nNW = nSW - pVS->sectionsR - 1;
int nNE = nSE - pVS->sectionsR - 1;
add2triangles(pMB, nNE, nNW, nSE, nSW, nz + rpn);
}
}
}
//scale to desirable diameters
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, 1);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::capWrap(ModelBuilder* pMB, VirtualShape* pVS, float angleFrom, float angleTo) {
// angleFrom/To - in degrees
lockGroup(pMB);
//center point
int n0 = addVertex(pMB, 0, 0, 1, 0, 0, 1);
float stepZdg = 90.0f / pVS->sections[2]; //in degrees
float stepRdg = (angleTo - angleFrom) / pVS->sectionsR; //in degrees
for (int nz = 1; nz <= pVS->sections[2]; nz++) {
float angleZrd = stepZdg * nz * degrees2radians;
float kz = cosf(angleZrd);
float R = sinf(angleZrd);
for (int rpn = 0; rpn <= pVS->sectionsR; rpn++) {
// rpn - radial point number
float angleRd = (angleFrom + stepRdg * rpn) * degrees2radians;
float kx = cosf(angleRd) * R;
float ky = sinf(angleRd) * R;
int nSE = addVertex(pMB, kx, ky, kz, kx, ky, kz);
if (rpn > 0) {
if (nz == 1) {
int nSW = nSE - 1;
addTriangle(pMB, n0, nSW, nSE);
}
else {
int nSW = nSE - 1;
int nNW = nSW - pVS->sectionsR - 1;
int nNE = nSE - pVS->sectionsR - 1;
add2triangles(pMB, nNW, nNE, nSW, nSE, nz + rpn);
}
}
}
}
//scale to desirable diameters
mat4x4 transformMatrix = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };
mat4x4_scale_aniso(transformMatrix, transformMatrix, pVS->whl[0] * 0.5f, pVS->whl[1] * 0.5f, pVS->whl[2]);
int vertsN = pMB->vertices.size();
for (int i = pMB->pCurrentGroup->fromVertexN; i < vertsN; i++) {
Vertex01* pVX = pMB->vertices.at(i);
mat4x4_mul_vec4plus(pVX->aPos, transformMatrix, pVX->aPos, 1);
}
releaseGroup(pMB);
return 1;
}
int ModelBuilder::groupApplyTexture(ModelBuilder* pMB, std::string applyTo, TexCoords* pTC, TexCoords* pTC2nm) {
int vertsN = pMB->vertices.size();
for (int vN = 0; vN < vertsN; vN++) {
Vertex01* pVX = pMB->vertices.at(vN);
if(vN < pMB->pCurrentGroup->fromVertexN)
pVX->flag = -1;
else
pVX->flag = 0;
}
applyTexture2flagged(&pMB->vertices, applyTo, pTC, false);
applyTexture2flagged(&pMB->vertices, applyTo, pTC2nm, true);
return 1;
}
int ModelBuilder::applyTexture2flagged(std::vector<Vertex01*>* pVerts, std::string applyTo, TexCoords* pTC, bool isNormalMap) {
if (pTC == NULL)
return 0;
float posMin[3];
float posMax[3];
float posRange[3];
for (int i = 0; i < 3; i++) {
posMin[i] = 1000000;
posMax[i] = -1000000;
}
int vertsN = pVerts->size();
for (int vN = 0; vN < vertsN; vN++) {
Vertex01* pVX = pVerts->at(vN);
if (pVX->flag < 0) //ignore
continue;
for (int i = 0; i < 3; i++) {
if (posMin[i] > pVX->aPos[i])
posMin[i] = pVX->aPos[i];
if (posMax[i] < pVX->aPos[i])
posMax[i] = pVX->aPos[i];
}
}
//here we have coordinates range
for (int i = 0; i < 3; i++)
posRange[i] = posMax[i] - posMin[i];
//for "front"
int xRateIndex = 0;
bool xRateInverse = false;
int yRateIndex = 1;
bool yRateInverse = true;
if (applyTo.find("front") == 0)
; //do nothing
else if (applyTo.find("back") == 0)
xRateInverse = true;
else if (applyTo.find("left") == 0)
xRateIndex = 2;
else if (applyTo.find("right") == 0) {
xRateIndex = 2;
xRateInverse = true;
}
else if (applyTo.find("top") == 0) {
xRateInverse = true;
yRateIndex = 2;
}
else if (applyTo.find("bottom") == 0)
yRateIndex = 2;
float xRate = 0;
float yRate = 0;
float tuvRange[2];
tuvRange[0] = pTC->tuvBottomRight[0] - pTC->tuvTopLeft[0];
tuvRange[1] = pTC->tuvBottomRight[1] - pTC->tuvTopLeft[1];
for (int vN = 0; vN < vertsN; vN++) {
Vertex01* pVX = pVerts->at(vN);
if (pVX->flag < 0) //ignore
continue;
if (posRange[xRateIndex] == 0)
xRate = 0;
else {
xRate = (pVX->aPos[xRateIndex] - posMin[xRateIndex]) / posRange[xRateIndex];
if (xRateInverse)
xRate = 1.0f - xRate;
}
if (posRange[yRateIndex] == 0)
yRate = 0;
else {
yRate = (pVX->aPos[yRateIndex] - posMin[yRateIndex]) / posRange[yRateIndex];
if (yRateInverse)
yRate = 1.0f - yRate;
}
float* pTuv = pVX->aTuv;
if(isNormalMap)
pTuv = pVX->aTuv2;
pTuv[0] = pTC->tuvTopLeft[0] + tuvRange[0] * xRate;
pTuv[1] = pTC->tuvTopLeft[1] + tuvRange[1] * yRate;
}
return 1;
}
11. Компиляция и запуск. Результат:
До:

После:

Прекрасно!
12. Пере-запускаем VS. Открываем C:\CPP\a997modeler\p_android\p_android.sln.
13. Под modeler добавим Existing Item
из C:\CPP\engine\modeler
Add
14. Включаем, разблокируем, подключаем, разрешаем.
Компиляция и запуск. Работает.
VS top menu -> Debug -> Stop Debugging