Right now we have 6 shaders (3 vertex+fragment pairs), which generate 3 shader-programs. Now we need to add textured Phong shader, then we'll need color or texture over transparency mask for both flat and phong cases, which will double number of shaders, and so on, and soon it will be hard to manage them all. Besides, in most cases it will be just different combinations of the same code fragments.
However, turned out that GLSL (shaders language) understands #define and #ifdef instructions, which allows to cover ALL mentioned cases by ONE single shaders pair.
We can generate #define part programmatically, and then concatenate it with shader's source code, which will let us generate multiple shader programs for different data sources (such as uColor or texture or else).
1. In Windows File Explorer open C:\CPP\engine\dt\shaders folder.
Delete all 6 files from there
and replace them by:
2. Vertex shader:
Copy following code in a Text Editor and save it as a txt file in
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)
#endif
#if defined(USE_TEX0)
out vec2 vTuv; //varying TUV (pass to fragment shader)
#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
}
3. Fragment shader:
Copy following code in a Text Editor and save it as a txt file in
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
#if defined(USE_NORMALS)
in vec3 vNormal; //normal passed from rasterizer
#endif
#if defined(USE_TEX0)
uniform sampler2D uTex0; //texture id
in vec2 vTuv; //varying TUV (passed from vertex shader)
#else
uniform vec4 uColor;
#endif
#if defined(PHONG)
uniform float uAmbient;
uniform float uSpecularIntencity;
uniform float uSpecularMinDot;
uniform float uSpecularPowerOf;
uniform vec3 uVectorToLight;
uniform vec3 uHalfVector;
#endif
void main(void) {
#if defined(USE_TEX0)
FragColor = texture(uTex0, vTuv);
#else
FragColor = uColor;
#endif
#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 directionalLightIntencity = dot(vNormalNormal, uVectorToLight);
// count ambient component
directionalLightIntencity += uAmbient;
if(directionalLightIntencity < uAmbient)
directionalLightIntencity = uAmbient;
// Multiply the color by the lightIntencity illumination level to get final output color.
FragColor *= directionalLightIntencity;
}
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, uHalfVector);
if(dotProduct>uSpecularMinDot){
float specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;
if(specularIntencity > uSpecularIntencity)
specularIntencity = uSpecularIntencity;
FragColor += specularIntencity;
}
}
#endif
if(uAlphaFactor != 1.0)
FragColor.a *= uAlphaFactor;
}
Now we need to load them in the memory, concatenate with #deines string, translate and link into executable shader programs.
File Loader
Reading/loading files into the memory for subsequent manipulations/processing is a quite common task. We did it before and will a lot in the future. Makes sense to wrap it in a separate class. We'll call it FileLoader.
4. Start VS, open C:\CPP\a997modeler\p_windows\p_windows.sln
5. Under xEngine add new header file FileLoader.h
Location: C:\CPP\engine
Code:
#pragma once
#include <string>
class FileLoader
{
public:
std::string fullPath;
std::string inAppFolder;
char* pData = NULL;
int dataSize = 0;
public:
FileLoader(std::string filePath, std::string readMode="rt");
virtual ~FileLoader();
static int translatePath(FileLoader* pFL, std::string filePath);
static int loadFile(FileLoader* pFL, std::string filePath, std::string readMode="rb");
};
- translatePath() function will check filePath parameter and will concatenate it with filesRoot when necessary. Also it will extract inAppFolder for future use.
- loadFile() will retrieve file's size, allocate memory and load file into the memory.
Implementation:
6. Under xEngine add new C++ file FileLoader.cpp
Location: C:\CPP\engine
Code:
#include "FileLoader.h"
#include "platform.h"
extern std::string filesRoot;
FileLoader::FileLoader(std::string filePath, std::string readMode) {
loadFile(this, filePath, readMode);
}
FileLoader::~FileLoader() {
if (pData != NULL) {
delete[] pData;
pData = NULL;
}
}
int FileLoader::translatePath(FileLoader* pFL, std::string filePath) {
if (filePath.find(filesRoot) == 0)
pFL->fullPath = filePath;
else
pFL->fullPath = filesRoot + filePath;
int startPos = filesRoot.size();
int lastSlashPos = pFL->fullPath.find_last_of('/');
pFL->inAppFolder = pFL->fullPath.substr(startPos, lastSlashPos - startPos + 1);
return 1;
}
int FileLoader::loadFile(FileLoader* pFL, std::string filePath, std::string readMode) {
translatePath(pFL, filePath);
FILE* pFile;
myFopen_s(&pFile, pFL->fullPath.c_str(), readMode.c_str());
if (pFile != NULL)
{
// obtain file size:
fseek(pFile, 0, SEEK_END);
pFL->dataSize = ftell(pFile);
rewind(pFile);
// size obtained, create buffer
pFL->pData = new char[pFL->dataSize + 1];
pFL->dataSize = fread(pFL->pData, 1, pFL->dataSize, pFile);
pFL->pData[pFL->dataSize] = 0;
fclose(pFile);
}
else {
mylog("ERROR loading %s\n", pFL->fullPath.c_str());
return -1;
}
return 1;
}
When shaders are loaded into the memory, we need to set "defines" part. Now, for shaders compilation we will use strings ARRAY, consisting of 2 strings:
- "defines" string, which we will set programmatically
- and shader's source code, which actually is a "string" too.
We will do it in Shader class.
7. Open Shader.h and replace code by:
#pragma once
#include "platform.h"
#include <string>
#include <vector>
class Shader
{
public:
//Shader program's individual descriptor:
unsigned int GLid = -1; // GL shader id
//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_uVectorToLight; //required for light
int l_uHalfVector; //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;
//common shader programs ("spN" - shader program number)
static int spN_flat_ucolor;
static int spN_flat_tex;
static int spN_phong_ucolor;
static int spN_phong_tex;
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 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);
};
We have 3 new functions here:
- compileShaderWithDefines, which accepts "def"-string and 1 shader source code as parameters, and then compiles vertex or fragment shader, which will be linked together in
- linkShaderProgramWithDefines, which calls 2 compilations and links 2 compiled shaders into a Shader Program, which will be returned to
- buildShaderObjectWithDefines, which gets a "def"-string and 2 source codes as parameters, extends defString, calls linkShaderProgramWithDefines, analyses linked shader program, fills out variables locations and saves this info, including GL Program's ID in a new Shader object.
8. Open Shader.cpp and replace code by:
#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;
//common shader programs ("spN" - shader program number)
int Shader::spN_flat_ucolor = -1;
int Shader::spN_flat_tex = -1;
int Shader::spN_phong_ucolor = -1;
int Shader::spN_phong_tex = -1;
int Shader::loadShaders() {
FileLoader* pFLvertex = new FileLoader("/dt/shaders/phong_v.txt");
FileLoader* pFLfragment = new FileLoader("/dt/shaders/phong_f.txt");
spN_flat_ucolor = buildShaderObjectWithDefines("#define FLAT\n#define COLOR\n", pFLvertex->pData, pFLfragment->pData);
spN_flat_tex = buildShaderObjectWithDefines("#define FLAT\n#define TEXTURE\n", pFLvertex->pData, pFLfragment->pData);
spN_phong_ucolor = buildShaderObjectWithDefines("#define PHONG\n#define COLOR\n", pFLvertex->pData, pFLfragment->pData);
spN_phong_tex = buildShaderObjectWithDefines("#define PHONG\n#define TEXTURE\n", 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_uVectorToLight = glGetUniformLocation(pSh->GLid, "uVectorToLight"); //
pSh->l_uHalfVector = glGetUniformLocation(pSh->GLid, "uHalfVector"); // 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 definesString, char* sourceVertex, char* sourceFragment) {
//create shader object
Shader* pSh = new Shader();
shaders.push_back(pSh);
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;
}
- Now Shader::loadShaders() has a different format. Plus now we are building not 3, but 4 shaders, including new Phong textured shader.
Now in TheGame.cpp we can set Phong shader for textured surface. Don't have to flip it upside down any more.
9. Open TheGame.cpp and replace code by:
#include "TheGame.h"
#include "platform.h"
#include "utils.h"
#include "linmath.h"
#include "Texture.h"
#include "Shader.h"
#include "DrawJob.h"
#include "ModelBuilder.h"
#include "TexCoords.h"
extern std::string filesRoot;
extern float degrees2radians;
std::vector<GameSubj*> TheGame::gameSubjs;
int TheGame::getReady() {
bExitGame = false;
Shader::loadShaders();
glEnable(GL_CULL_FACE);
//=== create box ========================
GameSubj* pGS = new GameSubj();
gameSubjs.push_back(pGS);
pGS->name.assign("box1");
pGS->ownCoords.setPosition(0, 0, 0);
pGS->ownCoords.setDegrees(0, 0, 0);
pGS->ownSpeed.setDegrees(0,1,0);
ModelBuilder* pMB = new ModelBuilder();
pMB->useSubjN(gameSubjs.size() - 1);
//define VirtualShape
VirtualShape vs;
vs.setShapeType("box-tank");
vs.whl[0] = 60;
vs.whl[1] = 160;
vs.whl[2] = 390;
vs.setExt(20);
vs.extD = 0;
vs.extF = 0; //to make front face "flat"
vs.sectionsR = 2;
Material mt;
//define material - flat red
mt.shaderN = Shader::spN_phong_ucolor;
mt.primitiveType = GL_TRIANGLES;
mt.uColor.setRGBA(255, 0, 0,255); //red
pMB->useMaterial(&mt);
pMB->buildBoxFace(pMB,"front v", &vs);
pMB->buildBoxFace(pMB, "back v", &vs);
pMB->buildBoxFace(pMB, "top", &vs);
pMB->buildBoxFace(pMB, "bottom", &vs);
pMB->buildBoxFace(pMB, "left all", &vs);
mt.uColor.clear(); // set to zero;
mt.uTex0 = Texture::loadTexture(filesRoot + "/dt/sample_img.png");
mt.shaderN = Shader::spN_phong_tex;
pMB->useMaterial(&mt);
TexCoords tc;
tc.set(mt.uTex0, 11, 12, 256, 128, "h"); //flip horizontally
pMB->buildBoxFace(pMB, "right all", &vs, &tc);
pMB->buildDrawJobs(gameSubjs);
delete pMB;
//===== set up camera
mainCamera.ownCoords.setDegrees(15, 180, 0); //set camera angles/orientation
mainCamera.viewRangeDg = 30;
mainCamera.stageSize[0] = 500;
mainCamera.stageSize[1] = 375;
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);
//calculate halfVector
float dirToCamera[4] = { 0,0,-1,0 }; //-z
mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);
float uHalfVector[4] = { 0,0,0,0 };
for (int i = 0; i < 3; i++)
uHalfVector[i] = (dirToCamera[i] + dirToMainLight[i]) / 2;
vec3_norm(uHalfVector, uHalfVector);
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 - 250;
float farClip = mainCamera.focusDistance + 250;
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];
//render subject
for (int i = 0; i < pGS->djTotalN; i++) {
DrawJob* pDJ = DrawJob::drawJobs.at(pGS->djStartN + i);
pDJ->execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);
}
}
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;
}
10. Build and run.
Before:
After:
Now
Android
11. Close and re-open VS. Open C:\CPP\a997modeler\p_android\p_android.sln.
12. Under xEngine add Existing Item
from C:\CPP\engine:
- FileLoader.cpp
- FileLoader.h
Add.
13. Switch on, unlock, plug in, allow.
Build and run.
Good.