Here I want to add a golden sealing ribbon on the pack. It will be a line, a sequence of points instead of regular triangulars mesh. It will require:
- A set of new tags in ModelLoader
- New lineWidth property in Material class
- In DrawJob we'll need to adjust lineWidth depending on distance
- Additional shader's functionality
- And, of course, updated model descriptor
1. Copy following code to a Text Editor and save it (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 >
Please note:
- New mt_type "wire" (line 52)
- New Material property "lineWidth"
- New tag "line"
- New tags "p" (for "point")
- New properties "dxyz", "dx" and so on. This is "delta"(difference) from previous point
Also we'll need some extra code (related to lines) in shaders. Also, taking this opportunity, I decided to move HalfVector calculations from TheGame.cpp to vertex shaders. Before we calculated HalfVector just once for entire model. Now it will be calculated for each vertex. The result will be much more realistic.
Phong vertex shader:
2. Copy following code to a Text Editor and save it (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
}
Please note:
- We don't use uHalfVector any more
- Now we use uniform mat4 uMM; (Model matrix for vHalfVector for glares)
- and uniform vec3 uCameraPosition; (for calculating half vector for glares)
- We calculating varying vHalfVector for each vertex
Phong fragment shader:
3. Copy following code to a Text Editor and save it (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;
}
- Line (wire) related changes are highlighted
Normal map vertex shader:
4. Copy following code to a Text Editor and save it (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. Copy following code to a Text Editor and save it (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;
}
Now - SW part.
Windows
6. Start VS, open C:\CPP\a997modeler\p_windows\p_windows.sln.
7. Open Material.h and replace code by:
#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);
};
- New property lineWidth
- Another change - uSpecularMinDot = 0.8f; (will make glares softer)
In ModelLoader class - new variable and new functionality (highlighted).
8. Open ModelLoader.h and replace code by:
#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. Open ModelLoader.cpp and replace code by:
#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;
}
When we have a line loaded, we need to take care of normals to calculate light/shading in the shader. However, in case of line, the only vector we can calculate is line direction, which is perpendicular to any line's normal.
An extra code in shaders uses directions instead of normals. We'll calculate directions in ModelBuilder class.
10. Open ModelBuilder1base.h and replace code by:
#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. Open ModelBuilder1base.cpp and replace code by:
#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;
}
- Another change here (line 260) - points number for DrawJob when we don't have triangles (indices).
In DrawJob class we have new function lineWidthIsImportant() and new parameters in executeDrawJob():
- uMM - Model's transform matrix (for HalfVector calculations)
- uCameraPosition
- sizeUnitPixelsSize - for setting line width for rendering
12. Open DrawJob.h and replace code by:
#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. Open DrawJob.cpp and replace code by:
#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;
}
Now - back to shaders. We have new variables: uMM (model matrix) and uCameraPosition instead of uHalfVector. Plus now we have a new #define - WIRE
14. 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
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. 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;
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;
}
And finally - TheGame. We don't have to calculate uHalfVector there any more, but have to calculate sizeUnitPixelsSize (for setting line width for rendering). Plus - new parameters in pDJ->execute(..)
16. 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"
#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. Build and run. Result:
Now - with golden sealing ribbon.
BTW, without per-vertex HalfVector calculation, we wouldn't have such a nice sliding sparkle.
- Verified on Android too, works fine.