Chapter 23. #ifdef in shaders

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.


Leave a Reply

Your email address will not be published. Required fields are marked *