Здесь я собираюсь добавить золотую ленточку (пломбу). Это будет линия, последовательность точек вместо обычного набора треугольников. Для этого потребуются:
- Набор новых тагов в ModelLoader
- Новая переменная lineWidth в классе Material
- В DrawJob будет нужно перекалибровывать lineWidth в зависимости от дистанции
- Дополнительный функционал в шейдерах
- И, конечно, новый model descriptor
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 >
Обратите внимание на:
- Новый mt_type “wire” (строка 52)
- Новая переменная “lineWidth” в Material-е
- Новый таг “line”
- Новые таги “p” (for “point”)
- Новые переменные “dxyz”, “dx” и т.д. Это “delta”(разница) от предыдущей точки
Еще понадобится дополнительный код в шейдерах. Также, пользуясь случаем, я решил перенести вычисления 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
}
Обратите внимание:
- Мы больше не используем юниформу uHalfVector
- Теперь у нас uniform mat4 uMM; (Model matrix для вычисления vHalfVector для бликов)
- и uniform vec3 uCameraPosition; (для того же)
- Мы вычисляем varying vHalfVector для каждого вертекса
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;
}
Теперь – программная часть.
Windows
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);
};
- Новая переменная lineWidth
- Другое измененме – uSpecularMinDot = 0.8f; (сделает блики помягче)
В классе 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;
}
- Другое изменение тут (строка 260) – количество точек в DrawJob когда нет треугольников (индексов).
В классе DrawJob новая функция lineWidthIsImportant() и новые параметры в executeDrawJob():
- uMM – матрица Model’s transform (для вычислений HalfVector-а)
- uCameraPosition
- sizeUnitPixelsSize – для пересчета ширины линии при рендринге
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-а не получилось бы такой натуральной скользящей “искры”.
- На Андроиде тоже проверено, все работает.