Глава 23. #ifdef в шейдерах

Сейчас у нас 6 шейдеров (3 vertex+fragment пары), которые генерируют 3 шейдер-программы. Теперь нам надо добавить текстурированный Фонг шейдер, потом понадобятся цвет или текстура поверх маски прозрачности (трафарет) для обоих flat и phong случаев, что удвоит число шейдеров, и так далее, и скоро будет сложно их всех отслеживать. Причем, в основном это будут просто разные комбинации одних и тех же кусков кода.

Однако оказалось, что GLSL (shaders language) понимает #define и #ifdef инструкции, что позволяет закрыть ВСЕ упомянутые случаи всего ОДНОЙ парой шейдеров.

Мы можем сгенерировать #define часть программно, и соединить ее с исходным кодом шейдера, что позволит нам генерировать разные шейдер-программы для разных входных данных (как например uColor или texture или еще что-то).

1. В Windows File Explorer-е откроем каталог C:\CPP\engine\dt\shaders.

Удалим все 6 файлов оттуда

и заменим их на:


2. Vertex shader:

Скопируем нижеприведенный код в Text Editor и сохраним его как txt файл в

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:

Скопируем нижеприведенный код в Text Editor и сохраним его как txt файл в

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;		
}

Теперь их надо залить в память, соединить с #deines строкой, и странслировать в исполняемую шейдер-программу.


Загрузчик файлов / File Loader

Чтение/загрузка файлов в память для последующего использования – достаточно частая задача. Мы делали это и раньше, и еще не раз предстоит. Имеет смысл оформить это в отдельный класс. Назовем его FileLoader.

4. Запускаем VS, открываем C:\CPP\a997modeler\p_windows\p_windows.sln


5. Под xEngine добавим новый header файл FileLoader.h

Location: C:\CPP\engine

Код:

#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() будет проверять параметр filePath и дополнять его filesRoot-ом если надо. И еще будет извлекать каталог inAppFolder для последующего использования.
  • Функция loadFile() будет извлекать размер файла, резервировать память и загружать туда сам файл.

Имплементация:

6. Под xEngine добавим новый C++ файл FileLoader.cpp

Location: C:\CPP\engine

Код:

#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;
}


Когда исходники шейдеров загружены в память, нам надо будет задать “defines” часть. Теперь для компиляции шейдеров мы будем использовать МАССИВ строк, состоящий из двух строк:

  • Строка с “defines”, которую мы зададим программно
  • и исходный код шейдера, который вобщем-то тоже “строка”.

Сделаем это в классе Shader.

7. Заменим 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
    //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);

};

У нас тут 3 новые функции:

  • compileShaderWithDefines, которая принимает “def”-строку и исходник 1-ого шейдера как параметры, и затем транслирует vertex или fragment шейдер, которые потом будут залинкованы вместе в
  • linkShaderProgramWithDefines, который вызывает 2 компиляции и линкует 2 скомпилированных шейдера в Shader Program, которая будет возвращена в
  • buildShaderObjectWithDefines, которая принимает “def”-строку и 2 исходника как параметры, дополняет defString если надо, вызывает linkShaderProgramWithDefines, анализирует слинкованную шейдер-программу, заполняет переменные локаций и сохраняет всю эту инфу, включая GL Program’s ID в новом объекте Shader.

8. Заменим 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;
//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;
}

  • Теперь у функции Shader::loadShaders() новый формат. Плюс теперь мы строим не 3, а 4 шейдера, включая новый текстурированный Phong шейдер.

Теперь в TheGame.cpp мы можем задать Phong шейдер для текстурированной поверхности. И можно ее больше не переворачивать.

9. Заменим 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"

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. Компиляция и запуск.

До:

После:


Теперь

Android

11. Пере-запускаем VS. Открываем C:\CPP\a997modeler\p_android\p_android.sln.


12. Под xEngine добавим Existing Item

из C:\CPP\engine:

  • FileLoader.cpp
  • FileLoader.h

Add.


13. Включаем, разблокируем, подключаем, разрешаем.

Компиляция и запуск.

Прекрасно.


Leave a Reply

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